summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--arch/sparc64/kernel/smp.c40
1 files changed, 24 insertions, 16 deletions
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index 6bc7fd47e443..c4548a88953c 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -563,7 +563,7 @@ static void hypervisor_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t
 	u64 *mondo;
 	cpumask_t error_mask;
 	unsigned long flags, status;
-	int cnt, retries, this_cpu, i;
+	int cnt, retries, this_cpu, prev_sent, i;
 
 	/* We have to do this whole thing with interrupts fully disabled.
 	 * Otherwise if we send an xcall from interrupt context it will
@@ -595,8 +595,9 @@ static void hypervisor_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t
 
 	cpus_clear(error_mask);
 	retries = 0;
+	prev_sent = 0;
 	do {
-		int forward_progress;
+		int forward_progress, n_sent;
 
 		status = sun4v_cpu_mondo_send(cnt,
 					      tb->cpu_list_pa,
@@ -606,18 +607,23 @@ static void hypervisor_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t
 		if (likely(status == HV_EOK))
 			break;
 
-		/* First, clear out all the cpus in the mask that were
-		 * successfully sent to.  The hypervisor indicates this
-		 * by setting the cpu list entry of such cpus to 0xffff.
+		/* First, see if we made any forward progress.
+		 *
+		 * The hypervisor indicates successful sends by setting
+		 * cpu list entries to the value 0xffff.
 		 */
-		forward_progress = 0;
+		n_sent = 0;
 		for (i = 0; i < cnt; i++) {
-			if (cpu_list[i] == 0xffff) {
-				cpu_clear(i, mask);
-				forward_progress = 1;
-			}
+			if (likely(cpu_list[i] == 0xffff))
+				n_sent++;
 		}
 
+		forward_progress = 0;
+		if (n_sent > prev_sent)
+			forward_progress = 1;
+
+		prev_sent = n_sent;
+
 		/* If we get a HV_ECPUERROR, then one or more of the cpus
 		 * in the list are in error state.  Use the cpu_state()
 		 * hypervisor call to find out which cpus are in error state.
@@ -634,18 +640,20 @@ static void hypervisor_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t
 				err = sun4v_cpu_state(cpu);
 				if (err >= 0 &&
 				    err == HV_CPU_STATE_ERROR) {
-					cpu_clear(cpu, mask);
+					cpu_list[i] = 0xffff;
 					cpu_set(cpu, error_mask);
 				}
 			}
 		} else if (unlikely(status != HV_EWOULDBLOCK))
 			goto fatal_mondo_error;
 
-		/* Rebuild the cpu_list[] array and try again.  */
-		cnt = 0;
-		for_each_cpu_mask(i, mask)
-			cpu_list[cnt++] = i;
-
+		/* Don't bother rewriting the CPU list, just leave the
+		 * 0xffff and non-0xffff entries in there and the
+		 * hypervisor will do the right thing.
+		 *
+		 * Only advance timeout state if we didn't make any
+		 * forward progress.
+		 */
 		if (unlikely(!forward_progress)) {
 			if (unlikely(++retries > 10000))
 				goto fatal_mondo_timeout;