Skip to contentSkip to conversation

x86: add workaround monitor bug

PATCH Superseded Jacob Pan 3 participants 6 messages Jul 6, 2016 – Jul 18, 2016 x86

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Jacob Pan

author
#0 lore raw

PATCH 1/1 x86: add workaround monitor bug

From: Peter Zijlstra <[email protected]>

Monitored cached line may not wake up from mwait on certain Goldmont based CPUs. This patch will avoid calling current_set_polling_and_test() and thereby not set the TIF_ flag. The result is that we'll always send IPIs for wakeups.

---
 arch/x86/include/asm/cpufeatures.h | 1 +
 arch/x86/include/asm/mwait.h       | 2 +-
 arch/x86/kernel/cpu/intel.c        | 5 +++++
 arch/x86/kernel/process.c          | 2 +-
 4 files changed, 8 insertions(+), 2 deletions(-)
arch/x86/include/asm/cpufeatures.h +1 -0
index 78dbd28..197a3f4 100644
@@ -304,6 +304,7 @@
 #define X86_BUG_SYSRET_SS_ATTRS	X86_BUG(8) /* SYSRET doesn't fix up SS attrs */
 #define X86_BUG_NULL_SEG	X86_BUG(9) /* Nulling a selector preserves the base */
 #define X86_BUG_SWAPGS_FENCE	X86_BUG(10) /* SWAPGS without input dep on GS */
+#define X86_BUG_MONITOR		X86_BUG(11) /* IPI required to wake up remote cpu */
 
 
 #ifdef CONFIG_X86_32
arch/x86/include/asm/mwait.h +1 -1
index 0deeb2d..f37f2d8 100644
@@ -97,7 +97,7 @@ static inline void __sti_mwait(unsigned long eax, unsigned long ecx)
  */
 static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
 {
-	if (!current_set_polling_and_test()) {
+	if (static_cpu_has_bug(X86_BUG_MONITOR) || !current_set_polling_and_test()) {
 		if (static_cpu_has_bug(X86_BUG_CLFLUSH_MONITOR)) {
 			mb();
 			clflush((void *)&current_thread_info()->flags);
arch/x86/kernel/cpu/intel.c +5 -0
index 6e2ffbe..77c6b3e 100644
@@ -13,6 +13,7 @@
 #include <asm/msr.h>
 #include <asm/bugs.h>
 #include <asm/cpu.h>
+#include <asm/intel-family.h>
 
 #ifdef CONFIG_X86_64
 #include <linux/topology.h>
@@ -509,6 +510,10 @@ static void init_intel(struct cpuinfo_x86 *c)
 	    (c->x86_model == 29 || c->x86_model == 46 || c->x86_model == 47))
 		set_cpu_bug(c, X86_BUG_CLFLUSH_MONITOR);
 
+	if (c->x86 == 6 && boot_cpu_has(X86_FEATURE_MWAIT) &&
+		((c->x86_model == INTEL_FAM6_ATOM_GOLDMONT)))
+		set_cpu_bug(c, X86_BUG_MONITOR);
+
 #ifdef CONFIG_X86_64
 	if (c->x86 == 15)
 		c->x86_cache_alignment = c->x86_clflush_size * 2;
arch/x86/kernel/process.c +1 -1
index 96becbb..59f68f1 100644
@@ -404,7 +404,7 @@ static int prefer_mwait_c1_over_halt(const struct cpuinfo_x86 *c)
 	if (c->x86_vendor != X86_VENDOR_INTEL)
 		return 0;
 
-	if (!cpu_has(c, X86_FEATURE_MWAIT))
+	if (!cpu_has(c, X86_FEATURE_MWAIT) || static_cpu_has_bug(X86_BUG_MONITOR))
 		return 0;
 
 	return 1;
Signature · 1 line
1.9.1
#1 lore raw
Quoted 33 lines · Jacob Pan

From: Peter Zijlstra <[email protected]>

Monitored cached line may not wake up from mwait on certain Goldmont based CPUs. This patch will avoid calling current_set_polling_and_test() and thereby not set the TIF_ flag. The result is that we'll always send IPIs for wakeups.

---
 arch/x86/include/asm/cpufeatures.h | 1 +
 arch/x86/include/asm/mwait.h       | 2 +-
 arch/x86/kernel/cpu/intel.c        | 5 +++++
 arch/x86/kernel/process.c          | 2 +-
 4 files changed, 8 insertions(+), 2 deletions(-)
arch/x86/include/asm/cpufeatures.h +1 -0
index 78dbd28..197a3f4 100644
@@ -304,6 +304,7 @@
 #define X86_BUG_SYSRET_SS_ATTRS	X86_BUG(8) /* SYSRET doesn't fix up SS attrs */
 #define X86_BUG_NULL_SEG	X86_BUG(9) /* Nulling a selector preserves the base */
 #define X86_BUG_SWAPGS_FENCE	X86_BUG(10) /* SWAPGS without input dep on GS */
+#define X86_BUG_MONITOR		X86_BUG(11) /* IPI required to wake up remote cpu */
 
 
 #ifdef CONFIG_X86_32
arch/x86/include/asm/mwait.h +0 -0
index 0deeb2d..f37f2d8 100644

arch/x86/include/asm/mwait.h static inline void __sti_mwait(unsigned long eax, unsigned long ecx) · line 100

Quoted 5 lines
arch/x86/include/asm/mwait.h +1 -1
@@ -97,7 +97,7 @@ static inline void __sti_mwait(unsigned long eax, unsigned long ecx)
  */
 static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
 {
-	if (!current_set_polling_and_test()) {
+	if (static_cpu_has_bug(X86_BUG_MONITOR) || !current_set_polling_and_test()) {

Hm, this might be suboptimal: if MONITOR/MWAIT is implemented by setting the exclusive flag for the monitored memory address and then snooping for cache invalidation requests for that cache line, then not modifying the ->flags value with TIF_POLLING_NRFLAG makes MWAIT not wake up - only the IPI would wake it up.

I think a better approach would be to still optimistically modify the ->flags value _AND_ to also send an IPI, to make sure the wakeup is not lost. This means that the woken CPU will wake up much faster (no IPI latency).

(The system will still bear the ovehread of sending and receiving the IPI, but that cost is unavoidable if there's no other workaround for this erratum.)

Thanks,

Ingo

#2 lore raw
Quoted 9 lines · Ingo Molnar

arch/x86/include/asm/mwait.h static inline void __sti_mwait(unsigned long eax, unsigned long ecx) · line 100

Quoted 4 lines

static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) {

arch/x86/include/asm/mwait.h +1 -1
-	if (!current_set_polling_and_test()) {
+	if (static_cpu_has_bug(X86_BUG_MONITOR) || !current_set_polling_and_test()) {

Hm, this might be suboptimal: if MONITOR/MWAIT is implemented by setting the exclusive flag for the monitored memory address and then snooping for cache invalidation requests for that cache line, then not modifying the ->flags value with TIF_POLLING_NRFLAG makes MWAIT not wake up - only the IPI would wake it up.

Confused.. POLLING_NRFLAGS is not used to wake up ever. It is only used to determine if we want to send IPIs or not.

And since we _must_ send an IPI in this case, because the monitor is busted, we cannot set this.

Quoted 3 lines

I think a better approach would be to still optimistically modify the ->flags value _AND_ to also send an IPI, to make sure the wakeup is not lost. This means that the woken CPU will wake up much faster (no IPI latency).

This is exactly what is done. See resched_curr()'s use of set_nr_and_not_polling(). That does:

	if (!(fetch_or(&flags, NEED_RESCHED) & POLLING_NRFLAG))
		smp_send_reschedule(cpu);

So we unconditionally set NEED_RESCHED, if, when we set that, POLLING was set, we skip the IPI.

So again, since monitor is busted, simply setting NEED_RESCHED will not wake us, we must send the IPI, this is achieved by not setting POLLING_NRFLAG.

#3 lore raw
Quoted 12 lines · Peter Zijlstra
Quoted 9 lines · Ingo Molnar

arch/x86/include/asm/mwait.h static inline void __sti_mwait(unsigned long eax, unsigned long ecx) · line 100

Quoted 4 lines

static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) {

arch/x86/include/asm/mwait.h +1 -1
-	if (!current_set_polling_and_test()) {
+	if (static_cpu_has_bug(X86_BUG_MONITOR) || !current_set_polling_and_test()) {

Hm, this might be suboptimal: if MONITOR/MWAIT is implemented by setting the exclusive flag for the monitored memory address and then snooping for cache invalidation requests for that cache line, then not modifying the ->flags value with TIF_POLLING_NRFLAG makes MWAIT not wake up - only the IPI would wake it up.

Quoted 2 lines

Confused.. POLLING_NRFLAGS is not used to wake up ever. It is only used to determine if we want to send IPIs or not.

I called the IPI the 'wakeup' - it's the 'CPU wakeup' :-)

Quoted 7 lines

And since we _must_ send an IPI in this case, because the monitor is busted, we cannot set this.

Quoted 3 lines

I think a better approach would be to still optimistically modify the ->flags value _AND_ to also send an IPI, to make sure the wakeup is not lost. This means that the woken CPU will wake up much faster (no IPI latency).

Quoted 8 lines

This is exactly what is done. See resched_curr()'s use of set_nr_and_not_polling(). That does:

	if (!(fetch_or(&flags, NEED_RESCHED) & POLLING_NRFLAG))
		smp_send_reschedule(cpu);

So we unconditionally set NEED_RESCHED, if, when we set that, POLLING was set, we skip the IPI.

Ah, indeed, we set NEED_RESCHED in the same memory address that __monitor() is watching so all is good.

Quoted 3 lines

So again, since monitor is busted, simply setting NEED_RESCHED will not wake us, we must send the IPI, this is achieved by not setting POLLING_NRFLAG.

Yeah, so I got the impression that it might be broken in only certain circumstances, or is it completely busted?

Thanks,

Ingo

author
#4 lore raw
Quoted 43 lines · On Fri, 8 Jul 2016 14:07:12 +0200 Ingo Molnar <[email protected]> wrote:
Quoted 15 lines · Peter Zijlstra
Quoted 4 lines · Ingo Molnar
Quoted 4 lines

static inline void mwait_idle_with_hints(unsigned long eax,
unsigned long ecx) {

(quoted diff) +1 -1
-	if (!current_set_polling_and_test()) {
+	if (static_cpu_has_bug(X86_BUG_MONITOR)
Quoted 8 lines
Quoted 1 line

|| !current_set_polling_and_test()) {

Hm, this might be suboptimal: if MONITOR/MWAIT is implemented by setting the exclusive flag for the monitored memory address and then snooping for cache invalidation requests for that cache line, then not modifying the ->flags value with TIF_POLLING_NRFLAG makes MWAIT not wake up - only the IPI would wake it up.

Quoted 2 lines

Confused.. POLLING_NRFLAGS is not used to wake up ever. It is only used to determine if we want to send IPIs or not.

I called the IPI the 'wakeup' - it's the 'CPU wakeup' :-)

Quoted 8 lines

And since we _must_ send an IPI in this case, because the monitor is busted, we cannot set this.

Quoted 4 lines

I think a better approach would be to still optimistically modify the ->flags value _AND_ to also send an IPI, to make sure the wakeup is not lost. This means that the woken CPU will wake up much faster (no IPI latency).

Quoted 8 lines

This is exactly what is done. See resched_curr()'s use of set_nr_and_not_polling(). That does:

	if (!(fetch_or(&flags, NEED_RESCHED) & POLLING_NRFLAG))
		smp_send_reschedule(cpu);

So we unconditionally set NEED_RESCHED, if, when we set that, POLLING was set, we skip the IPI.

Ah, indeed, we set NEED_RESCHED in the same memory address that __monitor() is watching so all is good.

Quoted 7 lines
Quoted 3 lines

So again, since monitor is busted, simply setting NEED_RESCHED will not wake us, we must send the IPI, this is achieved by not setting POLLING_NRFLAG.

Yeah, so I got the impression that it might be broken in only certain circumstances, or is it completely busted?

That is right, monitor is only partially broken not completely busted. I don't have the statistics but it is not rare to miss wakeup. The typical symptom is random slowness and fails to boot, without this patch.

So doing both can speed up wake up in some cases.

Jacob

Quoted 3 lines

Thanks,

Ingo

[Jacob Pan]

author
#5 lore raw
Quoted 53 lines · On Fri, 8 Jul 2016 12:14:33 -0700 Jacob Pan <[email protected]>…
Quoted 43 lines · On Fri, 8 Jul 2016 14:07:12 +0200 Ingo Molnar <[email protected]> wrote:
Quoted 15 lines · Peter Zijlstra
Quoted 4 lines · Ingo Molnar
Quoted 4 lines

static inline void mwait_idle_with_hints(unsigned long eax,
unsigned long ecx) {

(quoted diff) +1 -1
-	if (!current_set_polling_and_test()) {
+	if (static_cpu_has_bug(X86_BUG_MONITOR)
Quoted 8 lines
Quoted 1 line

|| !current_set_polling_and_test()) {

Hm, this might be suboptimal: if MONITOR/MWAIT is implemented by setting the exclusive flag for the monitored memory address and then snooping for cache invalidation requests for that cache line, then not modifying the ->flags value with TIF_POLLING_NRFLAG makes MWAIT not wake up - only the IPI would wake it up.

Quoted 2 lines

Confused.. POLLING_NRFLAGS is not used to wake up ever. It is only used to determine if we want to send IPIs or not.

I called the IPI the 'wakeup' - it's the 'CPU wakeup' :-)

Quoted 8 lines

And since we _must_ send an IPI in this case, because the monitor is busted, we cannot set this.

Quoted 4 lines

I think a better approach would be to still optimistically modify the ->flags value _AND_ to also send an IPI, to make sure the wakeup is not lost. This means that the woken CPU will wake up much faster (no IPI latency).

Quoted 8 lines

This is exactly what is done. See resched_curr()'s use of set_nr_and_not_polling(). That does:

	if (!(fetch_or(&flags, NEED_RESCHED) & POLLING_NRFLAG))
		smp_send_reschedule(cpu);

So we unconditionally set NEED_RESCHED, if, when we set that, POLLING was set, we skip the IPI.

Ah, indeed, we set NEED_RESCHED in the same memory address that __monitor() is watching so all is good.

Quoted 7 lines
Quoted 3 lines

So again, since monitor is busted, simply setting NEED_RESCHED will not wake us, we must send the IPI, this is achieved by not setting POLLING_NRFLAG.

Yeah, so I got the impression that it might be broken in only certain circumstances, or is it completely busted?

Quoted 12 lines

That is right, monitor is only partially broken not completely busted. I don't have the statistics but it is not rare to miss wakeup. The typical symptom is random slowness and fails to boot, without this patch.

So doing both can speed up wake up in some cases.

Jacob

Quoted 3 lines

Thanks,

Ingo

BTW, I just rebased and sent out v2 to avoid conflict with the commit below. Could you take this patch for v4.8? it is a critical fix for affected CPUs.

commit 8709ed4d4b0eab04561c1ec9e6ea50fd1e3897ff

Author: Dave Hansen <[email protected]>
Date:   Fri Jun 17 17:15:03 2016 -0700

x86/cpu: Fix duplicated X86_BUG(9) macro

Thanks,

Jacob