From: "Huang, Ying" <ying.huang@linux.alibaba.com>
To: Zhiteng Qiu <qiuzhiteng@hygon.cn>
Cc: Andrew Morton <akpm@linux-foundation.org>,
David Hildenbrand <david@kernel.org>, Zi Yan <ziy@nvidia.com>,
Matthew Brost <matthew.brost@intel.com>,
Joshua Hahn <joshua.hahnjy@gmail.com>,
Byungchul Park <byungchul@sk.com>,
Gregory Price <gourry@gourry.net>,
Alistair Popple <apopple@nvidia.com>,
Xin Hao <xhao@linux.alibaba.com>,
Dave Hansen <dave.hansen@linux.intel.com>, <linux-mm@kvack.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH] mm/migrate: don't batch the TLB flush when there is nothing to batch
Date: Sat, 07 Nov 2026 20:26:07 +0800 [thread overview]
Message-ID: <87h5hsx1y8.fsf@DESKTOP-5N7EMDA> (raw)
In-Reply-To: <20261106041628.1177963-1-qiuzhiteng@hygon.cn> (Zhiteng Qiu's message of "Fri, 6 Nov 2026 12:16:28 +0800")
Zhiteng Qiu <qiuzhiteng@hygon.cn> writes:
> Since commit 7e12beb8ca2a ("migrate_pages: batch flushing TLB"), every
> MIGRATE_ASYNC migration unmaps with TTU_BATCH_FLUSH. That includes NUMA
> balancing and memory-tiering promotion, which migrate one folio per call
> through migrate_misplaced_folio(). For an order-0 folio with a single
> mapping the batch holds one PTE and there is nothing to merge, but the
> flush still changes: on x86, arch_tlbbatch_flush() sends a full TLB
> flush to every CPU in mm_cpumask() instead of invalidating that one page.
>
> On a 2-socket, 384-CPU x86 server without INVLPGB, a 4 KiB-page,
> memory-heavy multithreaded workload takes about 200 TLB shootdown
> interrupts per page migrated by NUMA balancing (v5.15 on the same machine
> takes about 11), and balancing costs it 13-16% of its throughput.
>
> Skip the batched flush only when the batch cannot merge anything: the
> folio is the only one in this migrate_pages_batch() call, it is not a
> large folio, and it is mapped only once. The rmap walk then meets at
> most one PTE, and ptep_clear_flush() invalidates just that page.
> Everything else keeps the batched flush, including PTE-mapped large
> folios, folios mapped more than once, and the batch of sub-folios
> produced when a partially mapped large folio is split during migration.
>
> PMD-mapped THPs already invalidate their mappings through
> set_pmd_migration_entry(), independently of TTU_BATCH_FLUSH, so their
> flush behaviour is unchanged. On arm64 the batched path only defers the
> final DSB, so a one-PTE batch should cost about the same either way.
>
> migrate_folio_unmap() cannot see how many folios its caller is
> migrating, so migrate_pages_batch() passes that down.
>
> Measured on v7.3: 120 GiB anonymous working set, 384 threads, 180 s per
> run, three A/B pairs (numa_balancing on vs. off, same boot) per kernel.
>
> v7.3 patched
> throughput loss from balancing 13.0-16.5% 0.5-6.3%
> kernel cycles per hinting PTE 1.18-1.22M 0.28-0.31M
> TLB interrupts / migrated page 202-210 12.7-13.6
>
> Signed-off-by: Zhiteng Qiu <qiuzhiteng@hygon.cn>
> ---
> Notes:
>
> An earlier version decided on reason == MR_NUMA_MISPLACED. That also
> dropped batching for the split sub-folio batch mentioned above. In a
> test that splits partially mapped 2 MiB THPs during NUMA migration, the
> earlier version raised TLB interrupts per migrated page from 0.13 to
> 16.1; this version keeps them at 0.11. For 64 KiB mTHP and for a
> MAP_SHARED mapping used by two processes, this version behaves like the
> base kernel (TLB interrupts per migrated page: mTHP 2.2-2.7 patched vs.
> 2.6-3.3 base; shared 31.3-31.5 vs. 31.2-31.7).
>
> The earlier version behaves the same as this one for order-0, private,
> single-mapped memory. These results were measured with it:
> - 192 CPUs (SMT off, 192 threads, 4 pairs): balancing loss
> 3.1-14.7% patched vs. 7.4-54.3% base.
> - Memory first-touched on the remote socket, threads on the local
> socket (6 runs per kernel): no regression in time to convergence or
> in throughput after convergence.
> - 2 MiB THP (2 pairs): no difference, as expected for PMD-mapped
> folios.
> - A 30-minute run with compaction forced every 30 s: no warnings.
>
> With this version, tools/testing/selftests/mm/migration passes 6/6
> (including the THP and hugetlb cases), the LTP move_pages and
> migrate_pages cases pass 14/14, and the scenario tests above saw no data
> mismatches.
>
> There is no Fixes: tag. This changes a performance trade-off in code
> that has been there since v6.4, and nobody has reported it as a
> regression. Please add one if you see it that way.
> ---
> mm/migrate.c | 19 ++++++++++++++++---
> 1 file changed, 16 insertions(+), 3 deletions(-)
>
> diff --git a/mm/migrate.c b/mm/migrate.c
> index 953af633ec..0f3404c3d7 100644
> --- a/mm/migrate.c
> +++ b/mm/migrate.c
> @@ -1222,7 +1222,7 @@ static void migrate_folio_done(struct folio *src,
> static int migrate_folio_unmap(new_folio_t get_new_folio,
> free_folio_t put_new_folio, unsigned long private,
> struct folio *src, struct folio **dstp, enum migrate_mode mode,
> - struct list_head *ret)
> + bool single_folio, struct list_head *ret)
> {
> struct folio *dst;
> int rc = -EAGAIN;
> @@ -1344,7 +1344,18 @@ static int migrate_folio_unmap(new_folio_t get_new_folio,
> /* Establish migration ptes */
> VM_BUG_ON_FOLIO(folio_test_anon(src) &&
> !folio_test_ksm(src) && !anon_vma, src);
> - try_to_migrate(src, mode == MIGRATE_ASYNC ? TTU_BATCH_FLUSH : 0);
> + /*
> + * Batching only pays off when the rmap walk can meet more than
> + * one PTE. A lone order-0 folio with a single mapping has
> + * exactly one, and on x86 the batched flush turns that one-page
> + * invalidation into a full flush of mm_cpumask().
> + */
> + bool batch_flush = mode == MIGRATE_ASYNC;
Better to set try_to_migrate() flags directly?
> +
> + if (single_folio && !folio_test_large(src) &&
> + !folio_maybe_mapped_shared(src))
> + batch_flush = false;
> + try_to_migrate(src, batch_flush ? TTU_BATCH_FLUSH : 0);
> old_folio_state |= FOLIO_WAS_MAPPED;
> }
>
> @@ -1835,6 +1846,7 @@ static int migrate_pages_batch(struct list_head *from,
> LIST_HEAD(unmap_folios);
> LIST_HEAD(dst_folios);
> bool nosplit = (reason == MR_NUMA_MISPLACED);
> + bool single_folio = list_is_singular(from);
>
> VM_WARN_ON_ONCE(mode != MIGRATE_ASYNC &&
> !list_empty(from) && !list_is_singular(from));
s/list_is_singular(from)/single_folio/
?
> @@ -1925,7 +1937,8 @@ static int migrate_pages_batch(struct list_head *from,
> }
>
> rc = migrate_folio_unmap(get_new_folio, put_new_folio,
> - private, folio, &dst, mode, ret_folios);
> + private, folio, &dst, mode, single_folio,
> + ret_folios);
> /*
> * The rules are:
> * 0: folio will be put on unmap_folios list,
>
> base-commit: dba8aba0a9a00ff781c03d82ae534fc6f8c2e263
---
Best Regards,
Huang, Ying
prev parent reply other threads:[~2026-11-07 12:26 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 4:16 Zhiteng Qiu
2026-11-06 9:37 ` Huang, Ying
2026-11-06 10:09 ` Zhiteng Qiu
2026-11-06 17:50 ` Zhiteng Qiu
2026-11-07 6:54 ` Huang, Ying
2026-11-07 11:07 ` Zhiteng Qiu
2026-11-07 12:22 ` Huang, Ying
2026-11-07 12:26 ` Huang, Ying [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=87h5hsx1y8.fsf@DESKTOP-5N7EMDA \
--to=ying.huang@linux.alibaba.com \
--cc=akpm@linux-foundation.org \
--cc=apopple@nvidia.com \
--cc=byungchul@sk.com \
--cc=dave.hansen@linux.intel.com \
--cc=david@kernel.org \
--cc=gourry@gourry.net \
--cc=joshua.hahnjy@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=matthew.brost@intel.com \
--cc=qiuzhiteng@hygon.cn \
--cc=xhao@linux.alibaba.com \
--cc=ziy@nvidia.com \
/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®