Linux-HyperV List
 help / color / mirror / Atom feed
* [PATCH 0/3] x86/mm: Cleanups
@ 2025-05-20 10:55 Peter Zijlstra
  2025-05-20 10:55 ` [PATCH 1/3] x86/mm: Unexport tlb_state_shared Peter Zijlstra
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Peter Zijlstra @ 2025-05-20 10:55 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, peterz, kys, haiyangz, wei.liu, decui, tglx, mingo,
	bp, dave.hansen, hpa, luto, linux-hyperv

Hi!

When reviewing fea4e317f9e7 ("x86/mm: Eliminate window where TLB flushes may be
inadvertently skipped") various other issues were raised. None of these are
critical, so here goes.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] x86/mm: Unexport tlb_state_shared
  2025-05-20 10:55 [PATCH 0/3] x86/mm: Cleanups Peter Zijlstra
@ 2025-05-20 10:55 ` Peter Zijlstra
  2025-05-20 15:27   ` Michael Kelley
  2025-05-23 17:20   ` Dave Hansen
  2025-05-20 10:55 ` [PATCH 2/3] x86/mm: Avoid repeated this_cpu_*() ops in switch_mm_irqs_off() Peter Zijlstra
  2025-05-20 10:55 ` [PATCH 3/3] x86/mm: Clarify should_flush_tlb() ordering Peter Zijlstra
  2 siblings, 2 replies; 8+ messages in thread
From: Peter Zijlstra @ 2025-05-20 10:55 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, peterz, kys, haiyangz, wei.liu, decui, tglx, mingo,
	bp, dave.hansen, hpa, luto, linux-hyperv

Never export data; modules have no business being able to change tlb
state.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 arch/x86/hyperv/mmu.c           |    9 ++-------
 arch/x86/include/asm/tlbflush.h |    2 ++
 arch/x86/mm/tlb.c               |    7 ++++++-
 3 files changed, 10 insertions(+), 8 deletions(-)

--- a/arch/x86/hyperv/mmu.c
+++ b/arch/x86/hyperv/mmu.c
@@ -51,11 +51,6 @@ static inline int fill_gva_list(u64 gva_
 	return gva_n - offset;
 }
 
-static bool cpu_is_lazy(int cpu)
-{
-	return per_cpu(cpu_tlbstate_shared.is_lazy, cpu);
-}
-
 static void hyperv_flush_tlb_multi(const struct cpumask *cpus,
 				   const struct flush_tlb_info *info)
 {
@@ -113,7 +108,7 @@ static void hyperv_flush_tlb_multi(const
 			goto do_ex_hypercall;
 
 		for_each_cpu(cpu, cpus) {
-			if (do_lazy && cpu_is_lazy(cpu))
+			if (do_lazy && cpu_tlbstate_is_lazy(cpu))
 				continue;
 			vcpu = hv_cpu_number_to_vp_number(cpu);
 			if (vcpu == VP_INVAL) {
@@ -198,7 +193,7 @@ static u64 hyperv_flush_tlb_others_ex(co
 
 	flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
 	nr_bank = cpumask_to_vpset_skip(&flush->hv_vp_set, cpus,
-			info->freed_tables ? NULL : cpu_is_lazy);
+			info->freed_tables ? NULL : cpu_tlbstate_is_lazy);
 	if (nr_bank < 0)
 		return HV_STATUS_INVALID_PARAMETER;
 
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -172,6 +172,8 @@ struct tlb_state_shared {
 };
 DECLARE_PER_CPU_SHARED_ALIGNED(struct tlb_state_shared, cpu_tlbstate_shared);
 
+bool cpu_tlbstate_is_lazy(int cpu);
+
 bool nmi_uaccess_okay(void);
 #define nmi_uaccess_okay nmi_uaccess_okay
 
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -1322,7 +1322,12 @@ static bool should_trim_cpumask(struct m
 }
 
 DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state_shared, cpu_tlbstate_shared);
-EXPORT_PER_CPU_SYMBOL(cpu_tlbstate_shared);
+
+bool cpu_tlbstate_is_lazy(int cpu)
+{
+	return per_cpu(cpu_tlbstate_shared.is_lazy, cpu);
+}
+EXPORT_SYMBOL_GPL(cpu_tlbstate_is_lazy);
 
 STATIC_NOPV void native_flush_tlb_multi(const struct cpumask *cpumask,
 					 const struct flush_tlb_info *info)



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/3] x86/mm: Avoid repeated this_cpu_*() ops in switch_mm_irqs_off()
  2025-05-20 10:55 [PATCH 0/3] x86/mm: Cleanups Peter Zijlstra
  2025-05-20 10:55 ` [PATCH 1/3] x86/mm: Unexport tlb_state_shared Peter Zijlstra
@ 2025-05-20 10:55 ` Peter Zijlstra
  2025-05-23 17:21   ` Dave Hansen
  2025-05-20 10:55 ` [PATCH 3/3] x86/mm: Clarify should_flush_tlb() ordering Peter Zijlstra
  2 siblings, 1 reply; 8+ messages in thread
From: Peter Zijlstra @ 2025-05-20 10:55 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, peterz, kys, haiyangz, wei.liu, decui, tglx, mingo,
	bp, dave.hansen, hpa, luto, linux-hyperv

Aside from generating slightly better code for not having to use %fs
prefixed ops, the real purpose is to clarify code by switching some to
smp_store_release() later on.

Notably, this_cpu_{read,write}() imply {READ,WRITE}_ONCE().

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 arch/x86/mm/tlb.c |   21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -51,7 +51,7 @@
 
 /*
  * Bits to mangle the TIF_SPEC_* state into the mm pointer which is
- * stored in cpu_tlb_state.last_user_mm_spec.
+ * stored in cpu_tlbstate.last_user_mm_spec.
  */
 #define LAST_USER_MM_IBPB	0x1UL
 #define LAST_USER_MM_L1D_FLUSH	0x2UL
@@ -782,8 +782,9 @@ static inline void cr4_update_pce_mm(str
 void switch_mm_irqs_off(struct mm_struct *unused, struct mm_struct *next,
 			struct task_struct *tsk)
 {
-	struct mm_struct *prev = this_cpu_read(cpu_tlbstate.loaded_mm);
-	u16 prev_asid = this_cpu_read(cpu_tlbstate.loaded_mm_asid);
+	struct tlb_state *this_tlbstate = this_cpu_ptr(&cpu_tlbstate);
+	struct mm_struct *prev = READ_ONCE(this_tlbstate->loaded_mm);
+	u16 prev_asid = READ_ONCE(this_tlbstate->loaded_mm_asid);
 	bool was_lazy = this_cpu_read(cpu_tlbstate_shared.is_lazy);
 	unsigned cpu = smp_processor_id();
 	unsigned long new_lam;
@@ -840,7 +841,7 @@ void switch_mm_irqs_off(struct mm_struct
 	if (prev == next) {
 		/* Not actually switching mm's */
 		VM_WARN_ON(is_dyn_asid(prev_asid) &&
-			   this_cpu_read(cpu_tlbstate.ctxs[prev_asid].ctx_id) !=
+			   READ_ONCE(this_tlbstate->ctxs[prev_asid].ctx_id) !=
 			   next->context.ctx_id);
 
 		/*
@@ -888,7 +889,7 @@ void switch_mm_irqs_off(struct mm_struct
 		 */
 		smp_mb();
 		next_tlb_gen = atomic64_read(&next->context.tlb_gen);
-		if (this_cpu_read(cpu_tlbstate.ctxs[prev_asid].tlb_gen) ==
+		if (READ_ONCE(this_tlbstate->ctxs[prev_asid].tlb_gen) ==
 				next_tlb_gen)
 			return;
 
@@ -910,7 +911,7 @@ void switch_mm_irqs_off(struct mm_struct
 		 * and others are sensitive to the window where mm_cpumask(),
 		 * CR3 and cpu_tlbstate.loaded_mm are not all in sync.
 		 */
-		this_cpu_write(cpu_tlbstate.loaded_mm, LOADED_MM_SWITCHING);
+		WRITE_ONCE(this_tlbstate->loaded_mm, LOADED_MM_SWITCHING);
 		barrier();
 
 		/* Start receiving IPIs and then read tlb_gen (and LAM below) */
@@ -925,8 +926,8 @@ void switch_mm_irqs_off(struct mm_struct
 	new_lam = mm_lam_cr3_mask(next);
 	if (ns.need_flush) {
 		VM_WARN_ON_ONCE(is_global_asid(ns.asid));
-		this_cpu_write(cpu_tlbstate.ctxs[ns.asid].ctx_id, next->context.ctx_id);
-		this_cpu_write(cpu_tlbstate.ctxs[ns.asid].tlb_gen, next_tlb_gen);
+		WRITE_ONCE(this_tlbstate->ctxs[ns.asid].ctx_id, next->context.ctx_id);
+		WRITE_ONCE(this_tlbstate->ctxs[ns.asid].tlb_gen, next_tlb_gen);
 		load_new_mm_cr3(next->pgd, ns.asid, new_lam, true);
 
 		trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
@@ -940,8 +941,8 @@ void switch_mm_irqs_off(struct mm_struct
 	/* Make sure we write CR3 before loaded_mm. */
 	barrier();
 
-	this_cpu_write(cpu_tlbstate.loaded_mm, next);
-	this_cpu_write(cpu_tlbstate.loaded_mm_asid, ns.asid);
+	WRITE_ONCE(this_tlbstate->loaded_mm, next);
+	WRITE_ONCE(this_tlbstate->loaded_mm_asid, ns.asid);
 	cpu_tlbstate_update_lam(new_lam, mm_untag_mask(next));
 
 	if (next != prev) {



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 3/3] x86/mm: Clarify should_flush_tlb() ordering
  2025-05-20 10:55 [PATCH 0/3] x86/mm: Cleanups Peter Zijlstra
  2025-05-20 10:55 ` [PATCH 1/3] x86/mm: Unexport tlb_state_shared Peter Zijlstra
  2025-05-20 10:55 ` [PATCH 2/3] x86/mm: Avoid repeated this_cpu_*() ops in switch_mm_irqs_off() Peter Zijlstra
@ 2025-05-20 10:55 ` Peter Zijlstra
  2025-05-23 17:23   ` Dave Hansen
  2 siblings, 1 reply; 8+ messages in thread
From: Peter Zijlstra @ 2025-05-20 10:55 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, peterz, kys, haiyangz, wei.liu, decui, tglx, mingo,
	bp, dave.hansen, hpa, luto, linux-hyperv

The ordering in should_flush_tlb() is entirely non-obvious and is only
correct because x86 is TSO. Clarify the situation by replacing two
WRITE_ONCE()s with smp_store_release(), which on x86 is cosmetic.

Additionally, clarify the comment on should_flush_tlb().

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 arch/x86/mm/tlb.c |   30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -910,8 +910,10 @@ void switch_mm_irqs_off(struct mm_struct
 		 * Indicate that CR3 is about to change. nmi_uaccess_okay()
 		 * and others are sensitive to the window where mm_cpumask(),
 		 * CR3 and cpu_tlbstate.loaded_mm are not all in sync.
+		 *
+		 * Also, see should_flush_tlb().
 		 */
-		WRITE_ONCE(this_tlbstate->loaded_mm, LOADED_MM_SWITCHING);
+		smp_store_release(&this_tlbstate->loaded_mm, LOADED_MM_SWITCHING);
 		barrier();
 
 		/* Start receiving IPIs and then read tlb_gen (and LAM below) */
@@ -938,10 +940,11 @@ void switch_mm_irqs_off(struct mm_struct
 		trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, 0);
 	}
 
-	/* Make sure we write CR3 before loaded_mm. */
-	barrier();
-
-	WRITE_ONCE(this_tlbstate->loaded_mm, next);
+	/*
+	 * Make sure we write CR3 before loaded_mm.
+	 * See nmi_uaccess_okay() and should_flush_tlb().
+	 */
+	smp_store_release(&this_tlbstate->loaded_mm, next);
 	WRITE_ONCE(this_tlbstate->loaded_mm_asid, ns.asid);
 	cpu_tlbstate_update_lam(new_lam, mm_untag_mask(next));
 
@@ -1280,9 +1283,20 @@ static bool should_flush_tlb(int cpu, vo
 	struct flush_tlb_info *info = data;
 
 	/*
-	 * Order the 'loaded_mm' and 'is_lazy' against their
-	 * write ordering in switch_mm_irqs_off(). Ensure
-	 * 'is_lazy' is at least as new as 'loaded_mm'.
+	 * switch_mm_irqs_off()				should_flush_tlb()
+	 *   WRITE_ONCE(is_lazy, false);		  loaded_mm = READ_ONCE(loaded_mm);
+	 *   smp_store_release(loaded_mm, SWITCHING);     smp_rmb();
+	 *   mov-cr3
+	 *   smp_store_release(loaded_mm, next)
+	 *                                                if (READ_ONCE(is_lazy))
+	 *                                                  return false;
+	 *
+	 * Where smp_rmb() matches against either smp_store_release() to
+	 * ensure that if we observe loaded_mm to be either SWITCHING or next
+	 * we must also observe is_lazy == false.
+	 *
+	 * If this were not so, it would be possible to falsely return false
+	 * and miss sending an invalidation IPI.
 	 */
 	smp_rmb();
 



^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 1/3] x86/mm: Unexport tlb_state_shared
  2025-05-20 10:55 ` [PATCH 1/3] x86/mm: Unexport tlb_state_shared Peter Zijlstra
@ 2025-05-20 15:27   ` Michael Kelley
  2025-05-23 17:20   ` Dave Hansen
  1 sibling, 0 replies; 8+ messages in thread
From: Michael Kelley @ 2025-05-20 15:27 UTC (permalink / raw)
  To: Peter Zijlstra, x86@kernel.org
  Cc: linux-kernel@vger.kernel.org, kys@microsoft.com,
	haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com,
	tglx@linutronix.de, mingo@redhat.com, bp@alien8.de,
	dave.hansen@linux.intel.com, hpa@zytor.com, luto@kernel.org,
	linux-hyperv@vger.kernel.org

From: Peter Zijlstra <peterz@infradead.org> Sent: Tuesday, May 20, 2025 3:56 AM
> 
> Never export data; modules have no business being able to change tlb
> state.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  arch/x86/hyperv/mmu.c           |    9 ++-------
>  arch/x86/include/asm/tlbflush.h |    2 ++
>  arch/x86/mm/tlb.c               |    7 ++++++-
>  3 files changed, 10 insertions(+), 8 deletions(-)
> 
> --- a/arch/x86/hyperv/mmu.c
> +++ b/arch/x86/hyperv/mmu.c
> @@ -51,11 +51,6 @@ static inline int fill_gva_list(u64 gva_
>  	return gva_n - offset;
>  }
> 
> -static bool cpu_is_lazy(int cpu)
> -{
> -	return per_cpu(cpu_tlbstate_shared.is_lazy, cpu);
> -}
> -
>  static void hyperv_flush_tlb_multi(const struct cpumask *cpus,
>  				   const struct flush_tlb_info *info)
>  {
> @@ -113,7 +108,7 @@ static void hyperv_flush_tlb_multi(const
>  			goto do_ex_hypercall;
> 
>  		for_each_cpu(cpu, cpus) {
> -			if (do_lazy && cpu_is_lazy(cpu))
> +			if (do_lazy && cpu_tlbstate_is_lazy(cpu))
>  				continue;
>  			vcpu = hv_cpu_number_to_vp_number(cpu);
>  			if (vcpu == VP_INVAL) {
> @@ -198,7 +193,7 @@ static u64 hyperv_flush_tlb_others_ex(co
> 
>  	flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
>  	nr_bank = cpumask_to_vpset_skip(&flush->hv_vp_set, cpus,
> -			info->freed_tables ? NULL : cpu_is_lazy);
> +			info->freed_tables ? NULL : cpu_tlbstate_is_lazy);
>  	if (nr_bank < 0)
>  		return HV_STATUS_INVALID_PARAMETER;
> 
> --- a/arch/x86/include/asm/tlbflush.h
> +++ b/arch/x86/include/asm/tlbflush.h
> @@ -172,6 +172,8 @@ struct tlb_state_shared {
>  };
>  DECLARE_PER_CPU_SHARED_ALIGNED(struct tlb_state_shared, cpu_tlbstate_shared);
> 
> +bool cpu_tlbstate_is_lazy(int cpu);
> +
>  bool nmi_uaccess_okay(void);
>  #define nmi_uaccess_okay nmi_uaccess_okay
> 
> --- a/arch/x86/mm/tlb.c
> +++ b/arch/x86/mm/tlb.c
> @@ -1322,7 +1322,12 @@ static bool should_trim_cpumask(struct m
>  }
> 
>  DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state_shared, cpu_tlbstate_shared);
> -EXPORT_PER_CPU_SYMBOL(cpu_tlbstate_shared);

FWIW, this EXPORT wasn't there for Hyper-V code. The EXPORT was
added in commit 2f4305b19fe6a, and it's not clear why. That commit
was 2 years before the Hyper-V MMU code started checking the lazy
flag.

> +
> +bool cpu_tlbstate_is_lazy(int cpu)
> +{
> +	return per_cpu(cpu_tlbstate_shared.is_lazy, cpu);
> +}
> +EXPORT_SYMBOL_GPL(cpu_tlbstate_is_lazy);

This EXPORT isn't needed for Hyper-V. The Hyper-V MMU code is
never built as a module, even if CONFIG_HYPERV=m. And I can't
see any other reason the EXPORT would be needed.

In any case,

Reviewed-by: Michael Kelley <mhklinux@outlook.com>

> 
>  STATIC_NOPV void native_flush_tlb_multi(const struct cpumask *cpumask,
>  					 const struct flush_tlb_info *info)
> 
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] x86/mm: Unexport tlb_state_shared
  2025-05-20 10:55 ` [PATCH 1/3] x86/mm: Unexport tlb_state_shared Peter Zijlstra
  2025-05-20 15:27   ` Michael Kelley
@ 2025-05-23 17:20   ` Dave Hansen
  1 sibling, 0 replies; 8+ messages in thread
From: Dave Hansen @ 2025-05-23 17:20 UTC (permalink / raw)
  To: Peter Zijlstra, x86
  Cc: linux-kernel, kys, haiyangz, wei.liu, decui, tglx, mingo, bp,
	dave.hansen, hpa, luto, linux-hyperv

On 5/20/25 03:55, Peter Zijlstra wrote:
> Never export data; modules have no business being able to change tlb
> state.

Acked-by: Dave Hansen <dave.hansen@linux.intel.com>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] x86/mm: Avoid repeated this_cpu_*() ops in switch_mm_irqs_off()
  2025-05-20 10:55 ` [PATCH 2/3] x86/mm: Avoid repeated this_cpu_*() ops in switch_mm_irqs_off() Peter Zijlstra
@ 2025-05-23 17:21   ` Dave Hansen
  0 siblings, 0 replies; 8+ messages in thread
From: Dave Hansen @ 2025-05-23 17:21 UTC (permalink / raw)
  To: Peter Zijlstra, x86
  Cc: linux-kernel, kys, haiyangz, wei.liu, decui, tglx, mingo, bp,
	dave.hansen, hpa, luto, linux-hyperv

On 5/20/25 03:55, Peter Zijlstra wrote:
> Aside from generating slightly better code for not having to use %fs
> prefixed ops, the real purpose is to clarify code by switching some to
> smp_store_release() later on.
> 
> Notably, this_cpu_{read,write}() imply {READ,WRITE}_ONCE().

Acked-by: Dave Hansen <dave.hansen@linux.intel.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] x86/mm: Clarify should_flush_tlb() ordering
  2025-05-20 10:55 ` [PATCH 3/3] x86/mm: Clarify should_flush_tlb() ordering Peter Zijlstra
@ 2025-05-23 17:23   ` Dave Hansen
  0 siblings, 0 replies; 8+ messages in thread
From: Dave Hansen @ 2025-05-23 17:23 UTC (permalink / raw)
  To: Peter Zijlstra, x86
  Cc: linux-kernel, kys, haiyangz, wei.liu, decui, tglx, mingo, bp,
	dave.hansen, hpa, luto, linux-hyperv

On 5/20/25 03:55, Peter Zijlstra wrote:
> The ordering in should_flush_tlb() is entirely non-obvious and is only
> correct because x86 is TSO. Clarify the situation by replacing two
> WRITE_ONCE()s with smp_store_release(), which on x86 is cosmetic.
> 
> Additionally, clarify the comment on should_flush_tlb().

Thanks for clarifying the ordering in those comments. It's much
appreciated by us mere mortals!

Oh, and there are a few we's that snuck into the comments. But whoever
applies this can fix them up.

Acked-by: Dave Hansen <dave.hansen@linux.intel.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-05-23 17:23 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-20 10:55 [PATCH 0/3] x86/mm: Cleanups Peter Zijlstra
2025-05-20 10:55 ` [PATCH 1/3] x86/mm: Unexport tlb_state_shared Peter Zijlstra
2025-05-20 15:27   ` Michael Kelley
2025-05-23 17:20   ` Dave Hansen
2025-05-20 10:55 ` [PATCH 2/3] x86/mm: Avoid repeated this_cpu_*() ops in switch_mm_irqs_off() Peter Zijlstra
2025-05-23 17:21   ` Dave Hansen
2025-05-20 10:55 ` [PATCH 3/3] x86/mm: Clarify should_flush_tlb() ordering Peter Zijlstra
2025-05-23 17:23   ` Dave Hansen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox