From: Dave Hansen <dave.hansen@intel.com>
To: Naman Jain <namjain@linux.microsoft.com>,
haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com,
tglx@kernel.org, mingo@redhat.com, bp@alien8.de,
dave.hansen@linux.intel.com, x86@kernel.org, luto@kernel.org,
peterz@infradead.org
Cc: hpa@zytor.com, riel@surriel.com, mhklinux@outlook.com,
linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] x86/hyperv: Account for lazy mm_cpumask maintenance
Date: Fri, 30 Oct 2026 09:09:49 -0700 [thread overview]
Message-ID: <cae10e61-ef31-4a66-9de9-7c39663026cd@intel.com> (raw)
In-Reply-To: <20261029064234.362385-1-namjain@linux.microsoft.com>
On 10/28/26 23:42, Naman Jain wrote:
> CPUs no longer running an mm may remain in mm_cpumask(). Hyper-V treats
> these CPUs as flush targets and bypasses the native callbacks that trim
> the mask. A single-threaded process can therefore keep issuing
> all-processor flushes after visiting all CPUs and returning to one CPU.
>
> Share the native target predicate and filter the mask before building
> either hypercall format or selecting HV_FLUSH_ALL_PROCESSORS. Preserve
> lazy-TLB exclusion and unfiltered page-table-freeing flushes, and use
> native callbacks for periodic trimming and ASID transitions.
>
> When no remote targets remain, request immediate trimming through native
> callbacks so stale memberships do not unnecessarily prevent later
> invalidations from using the local fast path. Leave lazy CPU-mask
> maintenance during context switches unchanged.
There's a lot of stuff in this patch not covered in the changelog. Like
the mm_in_asid_transition() addition.
This reads to me as being highly LLM-generated and not super readable or
relevant.
> Fixes: 209954cbc7d0 ("x86/mm/tlb: Update mm_cpumask lazily")
We usually don't put Fixes: tags on performance improvements.
I don't want this backported.
> - if (!hv_hypercall_pg)
> + /*
> + * Hypercalls bypass the callbacks that trim mm_cpumask() and complete
> + * ASID transitions.
> + */
> + if (!hv_hypercall_pg || info->trim_cpumask ||
> + mm_in_asid_transition(info->mm))
> goto do_native;
What do ASID transitions have to do with this?
> local_irq_save(flags);
> @@ -79,6 +82,30 @@ static void hyperv_flush_tlb_multi(const struct cpumask *cpus,
> goto do_native;
> }
>
> + flush_cpus = this_cpu_ptr(&hyperv_tlb_flush_mask);
> + if (info->freed_tables) {
> + cpumask_copy(flush_cpus, cpus);
> + } else {
> + cpumask_clear(flush_cpus);
> + for_each_cpu(cpu, cpus) {
> + if (cpu_needs_tlb_flush(cpu, info))
> + __cpumask_set_cpu(cpu, flush_cpus);
> + }
> + }
This needs some heavy commenting. Or a helper. Or both.
> + /*
> + * An effectively local mm should not keep taking the remote path until
> + * the next periodic trim. Let the native callbacks prune stale bits.
> + */
> + if (cpumask_any_but(flush_cpus, smp_processor_id()) >= nr_cpu_ids) {
Is this condition right? Doesn't ">= nr_cpu_ids" mean "no other CPUs found"?
I'm not following this.
> + struct flush_tlb_info local_info = *info;
What's wrong with reusing 'info'?
> + local_info.trim_cpumask = 1;
> + local_irq_restore(flags);
> + native_flush_tlb_multi(cpus, &local_info);
> + return;
> + }
Gah, so is this hacking "trim_cpumask = 1" to force should_flush_tlb()
or something?
Right now, we have quite clean code around info->trim_cpumask. It's
either 0 or set from:
info.trim_cpumask = should_trim_cpumask(mm);
It would be nice to keep it that way if possible.
Also, just looking at flush_tlb_mm_range(), there's a very familiar
check there:
} else if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids) {
remote_flush = true;
which feeds:
if (remote_flush) {
info.trim_cpumask = should_trim_cpumask(mm);
flush_tlb_multi(mm_cpumask(mm), &info);
consider_global_asid(mm);
}
So what if we just added a check to should_trim_cpumask(), like:
if (cpumask_weight(mm_cpumask(mm)) > atomic_read(&mm->mm_users))
return true;
Or mm_users*2, or _some_ other heuristic that will trip when
mm_cpumask(mm) gets out of whack from ->mm_users.
That's a pretty cheap check because mm_cpumask() is already hot or is
just about to be read anyway. It might actually be useful to INVLPGB
systems too. That still needs the info->trim_cpumask check at the top of
hyperv_flush_tlb_multi(), btw.
Also, this is *kinda* changing the meaning of ->trim_cpumask. It's
really starting to mean "send IPIs". A rename might be appropriate.
/*
> diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
> index 70098d448e999..e1e05cc867d5a 100644
> --- a/arch/x86/include/asm/tlbflush.h
> +++ b/arch/x86/include/asm/tlbflush.h
> @@ -264,6 +264,12 @@ void flush_tlb_one_kernel(unsigned long addr);
> void flush_tlb_multi(const struct cpumask *cpumask,
> const struct flush_tlb_info *info);
>
> +/*
> + * Do not use for filtering when freeing page tables or during global ASID
> + * transitions.
> + */
> +bool cpu_needs_tlb_flush(int cpu, const struct flush_tlb_info *info);
Please look around this header that's being patched. Are the functions
above this commented? Please match the style.
Comment the definition itself, not the declaration.
> static inline bool is_dyn_asid(u16 asid)
> {
> return asid < TLB_NR_DYN_ASIDS;
> diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
> index 0c4da320831cb..f2f40928d56a4 100644
> --- a/arch/x86/mm/tlb.c
> +++ b/arch/x86/mm/tlb.c
> @@ -1273,10 +1273,9 @@ static void flush_tlb_func(void *info)
> nr_invalidate);
> }
>
> -static bool should_flush_tlb(int cpu, void *data)
> +bool cpu_needs_tlb_flush(int cpu, const struct flush_tlb_info *info)
> {
> struct mm_struct *loaded_mm = per_cpu(cpu_tlbstate.loaded_mm, cpu);
> - struct flush_tlb_info *info = data;
>
> /*
> * Order the 'loaded_mm' and 'is_lazy' against their
> @@ -1311,6 +1310,11 @@ static bool should_flush_tlb(int cpu, void *data)
> return false;
> }
>
> +static bool should_flush_tlb(int cpu, void *data)
> +{
> + return cpu_needs_tlb_flush(cpu, data);
> +}
> +
> static bool should_trim_cpumask(struct mm_struct *mm)
> {
> if (time_after(jiffies, READ_ONCE(mm->context.next_trim_cpumask))) {
None of this movement is mentioned in the changelog. The naming change
here is also really silly. Now we have
should_flush_tlb(int cpu, void *data)
and
cpu_needs_tlb_flush(int cpu, const struct flush_tlb_info *info)
which are literally the same thing, except for the type. This is
precisely the kind of place we should keep the names _identical_. Either
use an __ or call the "void *" version "_wrapper" or something with a
comment:
/* Wrap foo() with a different type for use as an IPI callback: */
BTW, this "patch" probably needs to be broken up into a 4-5ish patch series.
prev parent reply other threads:[~2026-10-30 16:09 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 6:42 Naman Jain
2026-10-30 2:39 ` Michael Kelley
2026-10-30 4:00 ` Naman Jain
2026-10-30 4:09 ` Michael Kelley
2026-10-30 16:09 ` Dave Hansen [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cae10e61-ef31-4a66-9de9-7c39663026cd@intel.com \
--to=dave.hansen@intel.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=decui@microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=hpa@zytor.com \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@kernel.org \
--cc=mhklinux@outlook.com \
--cc=mingo@redhat.com \
--cc=namjain@linux.microsoft.com \
--cc=peterz@infradead.org \
--cc=riel@surriel.com \
--cc=tglx@kernel.org \
--cc=wei.liu@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®