diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 3aa699e9d4af..d06d59ac2f05 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6309,6 +6309,16 @@ static void update_idle_cpu(int cpu)
 	}
 }
 
+static inline bool should_scan_sibling(int cpu)
+{
+	return cmpxchg(&cpu_rq(cpu)->sis_scan_sibling, 1, 0);
+}
+
+static inline void set_scan_sibling(int cpu)
+{
+	WRITE_ONCE(cpu_rq(cpu)->sis_scan_sibling, 1);
+}
+
 /*
  * Scans the local SMT mask to see if the entire core is idle, and records this
  * information in sd_llc_shared->has_idle_cores.
@@ -6384,17 +6394,20 @@ static int select_idle_core(struct task_struct *p, int core, struct cpumask *cpu
 /*
  * Scan the local SMT mask for idle CPUs.
  */
-static int select_idle_smt(struct task_struct *p, int target)
+static int select_idle_smt(struct task_struct *p, int core, struct cpumask *cpus, int exclude)
 {
 	int cpu;
 
-	for_each_cpu_and(cpu, cpu_smt_mask(target), p->cpus_ptr) {
-		if (cpu == target)
+	for_each_cpu_and(cpu, cpu_smt_mask(core), p->cpus_ptr) {
+		if (exclude && cpu == core)
 			continue;
 		if (available_idle_cpu(cpu) || sched_idle_cpu(cpu))
 			return cpu;
 	}
 
+	if (cpus)
+		cpumask_clear_cpu(core, cpus);
+
 	return -1;
 }
 
@@ -6409,12 +6422,21 @@ static inline bool test_idle_cores(int cpu)
 	return false;
 }
 
+static inline bool should_scan_sibling(int cpu)
+{
+	return false;
+}
+
+static inline void set_scan_sibling(int cpu)
+{
+}
+
 static inline int select_idle_core(struct task_struct *p, int core, struct cpumask *cpus, int *idle_cpu)
 {
 	return __select_idle_cpu(core, p);
 }
 
-static inline int select_idle_smt(struct task_struct *p, int target)
+static inline int select_idle_smt(struct task_struct *p, int core, struct cpumask *cpus, int exclude)
 {
 	return -1;
 }
@@ -6434,6 +6456,7 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
 	struct rq *this_rq = this_rq();
 	int this = smp_processor_id();
 	struct sched_domain *this_sd = NULL;
+	bool scan_sibling = false;
 	u64 time = 0;
 
 	if (sched_feat(SIS_PROP) && !has_idle_core) {
@@ -6479,20 +6502,31 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
 		}
 	}
 
-	if (sched_feat(SIS_CORE) && sched_smt_active())
+	if (sched_feat(SIS_CORE) && sched_smt_active()) {
+		/*
+		 * Due to the nature of idle core scanning, has_idle_core
+		 * hint should also consume the scan_sibling flag even
+		 * though it doesn't use the flag when scanning.
+		 */
+		scan_sibling = should_scan_sibling(target);
 		icpus = sched_domain_icpus(sd);
+	}
 
 	cpumask_and(cpus, icpus ? icpus : sched_domain_span(sd), p->cpus_ptr);
 
 	for_each_cpu_wrap(cpu, cpus, target + 1) {
+		if (!--nr)
+			break;
+
 		if (has_idle_core) {
 			i = select_idle_core(p, cpu, cpus, &idle_cpu);
 			if ((unsigned int)i < nr_cpumask_bits)
 				return i;
-
+		} else if (scan_sibling) {
+			idle_cpu = select_idle_smt(p, cpu, icpus, 0);
+			if ((unsigned int)idle_cpu < nr_cpumask_bits)
+				break;
 		} else {
-			if (!--nr)
-				return -1;
 			idle_cpu = __select_idle_cpu(cpu, p);
 			if ((unsigned int)idle_cpu < nr_cpumask_bits)
 				break;
@@ -6503,9 +6537,25 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
 		set_idle_cores(target, false);
 
 	if (icpus && idle_cpu == -1) {
-		/* Reset the idle cpu mask if a full scan fails */
-		if (nr > 0)
-			cpumask_clear(icpus);
+		if (nr > 0 && (has_idle_core || scan_sibling)) {
+			/*
+			 * Reset the idle cpu mask if a full scan fails,
+			 * but ignore the !has_idle_core case which should
+			 * have already been fixed during scan.
+			 */
+			if (has_idle_core)
+				cpumask_clear(icpus);
+		} else {
+			/*
+			 * As for partial scan failures, it will probably
+			 * fail again next time scanning from the same cpu.
+			 * Due to the SIS_CORE rule of CORE granule update,
+			 * some idle cpus can be missed in the mask. So it
+			 * would be reasonable to scan SMT siblings as well
+			 * if the scan is fail-prone.
+			 */
+			set_scan_sibling(target);
+		}
 	}
 
 	if (sched_feat(SIS_PROP) && this_sd && !has_idle_core) {
@@ -6657,7 +6707,7 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target)
 		has_idle_core = test_idle_cores(target);
 
 		if (!has_idle_core && cpus_share_cache(prev, target)) {
-			i = select_idle_smt(p, prev);
+			i = select_idle_smt(p, prev, NULL, 1);
 			if ((unsigned int)i < nr_cpumask_bits)
 				return i;
 		}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 1fc198be1ffd..c7f8ed5021e6 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -971,6 +971,9 @@ struct rq {
 
 #ifdef CONFIG_SMP
 	unsigned int		ttwu_pending;
+#ifdef CONFIG_SCHED_SMT
+	int			sis_scan_sibling;
+#endif
 #endif
 	u64			nr_switches;
 
