* [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks
@ 2026-10-27 9:40 Xuewen Wang
2026-10-27 9:40 ` [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks Xuewen Wang
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Xuewen Wang @ 2026-10-27 9:40 UTC (permalink / raw)
To: hughd, baolin.wang, rostedt, mhiramat, mathieu.desnoyers, corbet,
skhan, rdunlap, akpm
Cc: linux-mm, linux-kernel, linux-trace-kernel, linux-doc, Xuewen Wang
The tmpfs swapin and allocation paths are currently a blind spot for
tracing: the swap layer has no tracepoints at all, the existing
counters cannot attribute events to a file, and an mTHP request can be
silently downgraded at two layers - or fail outright - with only one
of those having any accounting.
On the swapin side:
- PGMAJFAULT only accounts the page fault path; swapins triggered by
read()/write()/fallocate()/truncate/symlink go through
shmem_get_folio() with vmf/fault_type NULL and are not counted
- PSWPIN/ZSWPIN are counted at the I/O and zswap layers with global
and memcg granularity, but cannot be attributed to an inode or file
- the mTHP order degradations in shmem_swap_alloc_folio() (uffd/zswap
forced order-0, order-0 retry after sync allocation failure) have
no counters at all, unlike the swapout side which counts
THP_SWPOUT_FALLBACK and MTHP_STAT_SWPOUT_FALLBACK
On the allocation side:
- the order mask from shmem_suitable_orders() (vma filtering, page
cache layout conflicts) can drop the order with no counter at all;
the code comment notes that -EEXIST "would be the right code, but
not here"
- the allocation fallback loop is counted by MTHP_STAT_SHMEM_FALLBACK
without attribution
- a fully masked-out request fails with -ENOMEM while the caller
silently retries at order 0
- when mem_cgroup_charge() fails with the target range already
occupied, the error is rewritten to -EEXIST, making a cgroup limit
event indistinguishable from a benign insertion race
Patch 1 adds a single shmem_swapin_folio event at every return from
shmem_swapin_folio(): the original caller index and XArray entry, the
requested (entry) and actual (folio) orders, the branch taken -
validation failure, initial swap cache hit, synchronous helper,
readahead helper, or poison marker - and the return value. For the
synchronous helper it also records why the request became order 0: a
fallback bitmask reporting the userfaultfd and zswap restrictions
together when both apply, sync_fail plus the initial errno when a
higher-order attempt failed and was retried, or none.
The fallback field is a bitmask rather than an enum because the two
policy restrictions are independent: a VMA can have userfaultfd armed
while the boot-wide zswap latch is also set. Reporting only one value
would hide that the other restriction must also go away before large
folios come back. sync_fail never combines with them - a policy
downgrade forces order 0 before the first attempt, so the retry that
sets sync_fail can only run when no policy bit was set.
Patch 2 adds a shmem_alloc_folio_finish event with the full order
chain (requested/suitable/actual) separating VMA and page-cache
filtering from allocator fallback, a stage value (filter, alloc,
charge, cache, account, done) identifying where the attempt ended,
and both the stage errno and the returned errno: a memcg charge
failure stays visible as error=-ENOMEM even when a page-cache
conflict rewrites the return to -EEXIST. A fully filtered-out request
reports stage=filter with error=0 and suitable_order=-1 because no
allocator call occurred. Successes at the requested order - including
ordinary base-page allocations - are omitted, so order-0 traffic
never floods the ring buffer.
shmem_swapin_folio does fire on every swapin attempt, and that is
deliberate: a swapin is a major-fault or I/O slow path whose rate is
bounded by actual swap traffic, and each event is precisely what
PSWPIN cannot attribute. Folio allocation is the opposite - it runs
for every page ever touched, "succeeded at the requested order"
carries no information, and it is already counted by
MTHP_STAT_SHMEM_ALLOC and THP_FILE_ALLOC. Hence the asymmetric
gating between the two patches. Both events, their fields and the
attempt semantics are documented in Documentation/trace/events-shmem.rst.
Three questions answered up front:
Why not vmstat counters? Counters show trends, tracepoints do
per-inode attribution; the two are complementary, and the trend side
already has its counters.
Why not eBPF/kprobes? BPF programs attach to tracepoints, so this
series is an enabler for BPF rather than competition. The interesting
data is mid-function state (suitable_order is a local consumed by the
fallback loop, the fallback bitmask is an internal policy decision,
and the charge error is rewritten in place before the return) that is
not visible at any function boundary; the functions involved are
static and may be inlined, so kprobe scripts silently break across
kernel versions, while a tracepoint's fields are documented in its
format file and any change is visible in review.
Why not extend trace_mm_filemap_add_to_page_cache()? tmpfs never
emits it. Its only call site is in mm/filemap.c, while
shmem_add_to_page_cache() runs its own XArray insertion loop with no
trace calls at all, so tmpfs folios entering the page cache produce
no filemap event. Even if they did, it reports only the final order -
no requested/suitable/actual chain, no stage, no errno - and nothing
whatsoever for the swapin side.
Reproduced on x86_64 (8 CPUs), kernel v7.3.0-rc3 + this series, tmpfs
on disk-backed swap with cgroup v1 memory limits forcing swapout
deterministically; zram provides the SWP_SYNCHRONOUS_IO device for
the synchronous path.
Swapin attribution - three tmpfs files (400M + 500M + 150M) charged
to one cgroup, 965M forced out with a 100M limit, then the 400M and
150M files read back with read():
PSWPIN +116219 pages, PGMAJFAULT +0
ino=2 (db_shm, 400M, read back): 102400 events - every one of
its 102400 pages
ino=3 (app_cache, 500M, untouched): 0 events
ino=4 (misc, 150M, read back): 16661 events, exactly the
pages that had left memory
104528 events took the cache branch and 14533 the readahead branch;
all carry error=0 and fallback=none. PGMAJFAULT never moved while
119061 read()-triggered swapin events were recorded.
mTHP order degradation on the readahead path - huge=always mount,
512M faulted in as 256 2M folios, 414M swapped out to disk swap and
read back:
102672 events requested_order=0 actual_order=0
1656 events requested_order=5 actual_order=0
1449 events requested_order=6 actual_order=0
207 events requested_order=9 actual_order=0
The 3312 events with requested_order > 0 all came back order 0 while
fallback stays none: the readahead path returning a smaller folio
without any policy downgrade, exactly the case the documentation
describes.
Synchronous whole-folio swapin - the same workload with zram as the
swap device and the zswap latch unset:
207 events, all path=sync requested_order=9 actual_order=9
207 2M folios account for the 414M that went out: with no policy
restriction the synchronous helper preserves the entry order end to
end.
zswap policy downgrade - zswap enabled once then disabled (arming the
one-way latch), 128M fault-written with zram swap, 90M forced out:
23040 events, one per page swapped out
720 events with requested_order > 0, every one fallback=zswap
360 order=5 / 315 order=6 / 45 order=9, all actual_order=0
fallback_error=0 and error=0 throughout: policy downgrades, not
failed attempts. The remaining 22320 events are order-0 entries that
had nothing to downgrade (fallback=none).
Combined policy restrictions - a userfaultfd registered over the
faulting VMA plus the armed zswap latch, 158M forced out and read
back through the armed VMA. This is the case a single-value fallback
field cannot report:
1264 events fallback=userfaultfd|zswap
632 order=5 / 553 order=6 / 79 order=9, all actual_order=0
The 39184 order-0 events in the same run report fallback=none: an
order-0 request has nothing to downgrade.
Allocation filtering - MADV_NOHUGEPAGE 64K stripes every 2M on a
huge=always mount, 512M fault-written:
1280 events, all stage=done error=0 ret=0
256 each for requested_order=9 with suitable_order and
actual_order of 4, 5, 6, 7 and 8
Not one 2M folio is allocated across the whole 512M file
(shmem_alloc 2M stat delta 0), yet nothing today reports why.
Allocation failures under a cgroup limit - 300M fault-written inside
a 100M memory limit, 8365 events in one run:
1976 stage=charge error=-12 ret=-12 memcg charge failure, the
errno preserved
1976 stage=filter error=0 ret=-12 suitable_order=-1
actual_order=-1, no
allocator call occurred
4413 stage=done error=0 ret=0 cascade fallback,
suitable_order 1 to 9
Filesystem accounting - a 16m tmpfs mount written with 64M of 4K
pwrites: 24576 stage=account events, all error=-28 ret=-28, firing
at the block where accounting ran out; 12288 carry requested_order=9
(the large-folio write attempt) and 12288 requested_order=0 (its
order-0 retry), two events per failed write.
Negative controls: 0 events for re-reading a file already in cache,
0 with the events disabled, and 0 for a 64M plain order-0 write
(patch 2's volume filter).
Not exercised at runtime: path=lookup and path=poison with nonzero
error, fallback=sync_fail with fallback_error (these need a failing
higher-order synchronous attempt or fault injection), userfaultfd
without zswap (the latch is one-way per boot and was already armed
for the combined test), stage=alloc with a real allocator failure,
and stage=cache (a concurrent insertion race). Everything quoted
above was observed on the target machine.
Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
Xuewen Wang (2):
mm/shmem: trace swapin results and order fallbacks
mm/shmem: trace folio allocation failures and fallbacks
Documentation/trace/events-shmem.rst | 118 ++++++++++++++++++++++
Documentation/trace/index.rst | 1 +
MAINTAINERS | 2 +
include/trace/events/shmem.h | 141 ++++++++++++++++++++++++++++
mm/shmem.c | 98 ++++++++++++++----
5 files changed, 340 insertions(+), 20 deletions(-)
create mode 100644 Documentation/trace/events-shmem.rst
create mode 100644 include/trace/events/shmem.h
--
2.25.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks
2026-10-27 9:40 [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks Xuewen Wang
@ 2026-10-27 9:40 ` Xuewen Wang
2026-10-27 23:55 ` Randy Dunlap
2026-10-27 9:40 ` [PATCH 2/2] mm/shmem: trace folio allocation failures and fallbacks Xuewen Wang
2026-10-28 7:56 ` [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks Baolin Wang
2 siblings, 1 reply; 6+ messages in thread
From: Xuewen Wang @ 2026-10-27 9:40 UTC (permalink / raw)
To: hughd, baolin.wang, rostedt, mhiramat, mathieu.desnoyers, corbet,
skhan, rdunlap, akpm
Cc: linux-mm, linux-kernel, linux-trace-kernel, linux-doc, Xuewen Wang
PSWPIN and ZSWPIN describe swap traffic but cannot identify which tmpfs
file caused it. Shmem swapins initiated through read() do not increment
PGMAJFAULT. This makes it difficult to identify which shared-memory file
is being brought back under memory pressure.
Add an event at each return from shmem_swapin_folio(), with the
filesystem device, inode, original caller index and XArray entry, entry
and folio orders, path and return value. Include entry validation
failures and poison markers so failed attempts are visible too.
Record explicit downgrades from a higher order to order 0 when the
faulting VMA has userfaultfd armed or zswap_never_enabled() returns false.
Record both reasons when both restrictions apply. The zswap restriction
follows a static key, not the current enable setting: it persists after
zswap is disabled and also applies with CONFIG_ZSWAP_DEFAULT_ON=y when
booting with zswap.enabled=0. Initial order-0 requests do not set policy
reason bits.
Also record the initial error when a higher-order synchronous attempt
fails and is retried at order 0. Use sync_fail for this case: the swap
layer can reject a range with -EBUSY before allocating memory, and
-ENOMEM can also come from memcg charging. A racing swap-cache insertion
may supply a folio of a different order, so the fallback fields describe
the request, not its outcome.
Existing swpin_fallback and swpin_fallback_charge counters report
failures on the swap allocation path, but lack inode attribution and do
not describe these policy restrictions. Carry the fallback decision and
final result in the same event so users do not need to correlate
separate records.
Document attempt semantics, swap-cache hits and order sentinels. A
successful event represents a folio installed in the file page cache;
it need not involve disk I/O or a major fault. Keep the existing return
and retry behavior.
Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
---
Documentation/trace/events-shmem.rst | 73 +++++++++++++++++++++++
Documentation/trace/index.rst | 1 +
MAINTAINERS | 2 +
include/trace/events/shmem.h | 86 ++++++++++++++++++++++++++++
mm/shmem.c | 64 +++++++++++++++------
5 files changed, 207 insertions(+), 19 deletions(-)
create mode 100644 Documentation/trace/events-shmem.rst
create mode 100644 include/trace/events/shmem.h
diff --git a/Documentation/trace/events-shmem.rst b/Documentation/trace/events-shmem.rst
new file mode 100644
index 000000000000..45da82761360
--- /dev/null
+++ b/Documentation/trace/events-shmem.rst
@@ -0,0 +1,73 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Tmpfs and shmem events
+=====================
+
+The events in ``events/shmem/`` attribute swapin and allocation activity to
+``dev`` (the filesystem device) and ``ino`` (the inode number). They also
+cover anonymous shared memory and memfd files. ``index`` is the page offset
+requested by the caller, before any alignment for a large folio.
+
+Swapin
+------
+
+``shmem_swapin_folio`` records each return from ``shmem_swapin_folio()``.
+``error`` is the return value: zero means a folio was installed in the file
+page cache; ``-EEXIST`` normally asks the caller to retry after a race.
+Entry validation failures and poison markers also produce events.
+
+``entry`` contains the original XArray entry's type and offset. For a large
+entry, this is its base swap slot even when ``index`` lies in its middle.
+For ``path=poison``, it encodes a marker, not a real swap slot.
+
+``requested_order`` is the confirmed XArray entry order, or -1 when unknown.
+``actual_order`` is the order of the folio obtained, or -1 if none was
+obtained. A nonnegative actual order does not imply successful insertion:
+always check ``error``. One successful event may cover multiple pages.
+
+``path`` identifies the branch taken: ``lookup`` for entry validation
+failure, ``cache`` for the initial swap cache hit, ``sync`` for synchronous
+swapin, ``readahead`` for the readahead helper, or ``poison`` for a marker.
+The helpers may themselves find a cached folio, so path does not prove I/O
+occurred. Event counts are not disk-I/O or major-fault counts.
+
+``fallback`` is a bitmask recording explicit downgrades from a higher order
+to order 0 in the synchronous helper. An initial order-0 request does not
+set policy reason bits, even when the policy conditions hold. Set reasons
+are printed separated by ``|``:
+
+* ``none``: no explicit downgrade in the synchronous helper.
+* ``userfaultfd``: the VMA has userfaultfd armed.
+* ``zswap``: ``zswap_never_enabled()`` is false, restricting the request
+ to order 0.
+* ``sync_fail``: the initial higher-order synchronous attempt failed.
+
+The zswap restriction follows a static key, not the current enable setting.
+It persists after zswap is disabled and also applies with
+``CONFIG_ZSWAP_DEFAULT_ON=y`` even when booting with ``zswap.enabled=0``.
+The policy reasons can occur together, so both ``userfaultfd`` and ``zswap``
+are recorded when both restrictions apply to a higher-order request.
+``fallback_error`` preserves the initial synchronous error, including
+range conflicts such as ``-EBUSY``; it is zero for policy downgrades and
+when fallback is ``none``. ``-ENOMEM`` can indicate allocation or memcg
+charging failure.
+
+These fields describe a decision to request order 0, not a guarantee about
+the returned folio: a racing swap-cache insertion may provide a different
+order. Swap-cache and readahead paths can also return a smaller folio
+without setting ``fallback``.
+
+For example, after mounting tracefs at ``/sys/kernel/tracing``, use a private
+instance to capture a workload's swapins::
+
+ mkdir /sys/kernel/tracing/instances/shmem
+ cd /sys/kernel/tracing/instances/shmem
+ echo 1 > events/shmem/shmem_swapin_folio/enable
+ # Run the workload, then stop recording before reading the buffer.
+ echo 0 > tracing_on
+ cat trace
+
+Filter or aggregate by both ``dev`` and ``ino``; inode numbers alone are
+not unique across mounts. Trace filters use the numeric values shown by
+the event's ``format`` file. Check ``per_cpu/cpu*/stats`` for dropped events
+before interpreting counts.
diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst
index f4058e8e92e3..dbf407744430 100644
--- a/Documentation/trace/index.rst
+++ b/Documentation/trace/index.rst
@@ -51,6 +51,7 @@ applications.
events
events-kmem
+ events-shmem
events-power
events-nmi
events-msr
diff --git a/MAINTAINERS b/MAINTAINERS
index d2fc3d8364b3..8f6a02dcf9c7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27602,8 +27602,10 @@ M: Hugh Dickins <hughd@google.com>
R: Baolin Wang <baolin.wang@linux.alibaba.com>
L: linux-mm@kvack.org
S: Maintained
+F: Documentation/trace/events-shmem.rst
F: include/linux/memfd.h
F: include/linux/shmem_fs.h
+F: include/trace/events/shmem.h
F: mm/memfd.c
F: mm/shmem.c
F: mm/shmem_quota.c
diff --git a/include/trace/events/shmem.h b/include/trace/events/shmem.h
new file mode 100644
index 000000000000..7a0050a1686f
--- /dev/null
+++ b/include/trace/events/shmem.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM shmem
+
+#if !defined(_TRACE_SHMEM_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SHMEM_H
+
+#include <linux/bits.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/tracepoint.h>
+
+#define SHMEM_SWAPIN_LOOKUP 0
+#define SHMEM_SWAPIN_CACHE 1
+#define SHMEM_SWAPIN_SYNC 2
+#define SHMEM_SWAPIN_READAHEAD 3
+#define SHMEM_SWAPIN_POISON 4
+
+#define SHMEM_SWAPIN_FALLBACK_NONE 0
+#define SHMEM_SWAPIN_FALLBACK_USERFAULTFD BIT(0)
+#define SHMEM_SWAPIN_FALLBACK_ZSWAP BIT(1)
+#define SHMEM_SWAPIN_FALLBACK_SYNC BIT(2)
+
+#define show_shmem_swapin_fallback(flags) \
+ (flags) ? __print_flags(flags, "|", \
+ { SHMEM_SWAPIN_FALLBACK_USERFAULTFD, "userfaultfd" },\
+ { SHMEM_SWAPIN_FALLBACK_ZSWAP, "zswap" }, \
+ { SHMEM_SWAPIN_FALLBACK_SYNC, "sync_fail" }) : "none"
+
+TRACE_EVENT(shmem_swapin_folio,
+
+ TP_PROTO(struct inode *inode, pgoff_t index,
+ unsigned int entry_type, pgoff_t entry_offset,
+ int requested_order, int actual_order, unsigned int path,
+ unsigned int fallback, int fallback_error, int error),
+
+ TP_ARGS(inode, index, entry_type, entry_offset, requested_order,
+ actual_order, path, fallback, fallback_error, error),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(unsigned long, ino)
+ __field(unsigned long, index)
+ __field(unsigned int, entry_type)
+ __field(unsigned long, entry_offset)
+ __field(int, requested_order)
+ __field(int, actual_order)
+ __field(unsigned int, path)
+ __field(unsigned int, fallback)
+ __field(int, fallback_error)
+ __field(int, error)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->index = index;
+ __entry->entry_type = entry_type;
+ __entry->entry_offset = entry_offset;
+ __entry->requested_order = requested_order;
+ __entry->actual_order = actual_order;
+ __entry->path = path;
+ __entry->fallback = fallback;
+ __entry->fallback_error = fallback_error;
+ __entry->error = error;
+ ),
+
+ TP_printk("dev=%d:%d ino=%lu index=%lu entry=%u:%lu requested_order=%d actual_order=%d path=%s fallback=%s fallback_error=%d error=%d",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino, __entry->index,
+ __entry->entry_type, __entry->entry_offset,
+ __entry->requested_order, __entry->actual_order,
+ __print_symbolic(__entry->path,
+ { SHMEM_SWAPIN_LOOKUP, "lookup" },
+ { SHMEM_SWAPIN_CACHE, "cache" },
+ { SHMEM_SWAPIN_SYNC, "sync" },
+ { SHMEM_SWAPIN_READAHEAD, "readahead" },
+ { SHMEM_SWAPIN_POISON, "poison" }),
+ show_shmem_swapin_fallback(__entry->fallback),
+ __entry->fallback_error, __entry->error)
+);
+
+#endif /* _TRACE_SHMEM_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/mm/shmem.c b/mm/shmem.c
index 9f0965b8b59c..3b910aec5bcf 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -87,6 +87,9 @@ static struct vfsmount *shm_mnt __ro_after_init;
#include "internal.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/shmem.h>
+
#define VM_ACCT(size) (PAGE_ALIGN(size) >> PAGE_SHIFT)
/* Pretend that each entry is of this size in directory's i_size */
@@ -2249,16 +2252,22 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
static struct folio *shmem_swap_alloc_folio(struct inode *inode,
struct vm_fault *vmf, pgoff_t index,
- swp_entry_t entry, int order, gfp_t gfp)
+ swp_entry_t entry, int order, gfp_t gfp,
+ unsigned int *fallback, int *fallback_error)
{
pgoff_t ilx;
struct folio *folio;
struct mempolicy *mpol;
struct shmem_inode_info *info = SHMEM_I(inode);
- if ((vmf && unlikely(userfaultfd_armed(vmf->vma))) ||
- !zswap_never_enabled())
- order = 0;
+ if (order) {
+ if (vmf && unlikely(userfaultfd_armed(vmf->vma)))
+ *fallback |= SHMEM_SWAPIN_FALLBACK_USERFAULTFD;
+ if (!zswap_never_enabled())
+ *fallback |= SHMEM_SWAPIN_FALLBACK_ZSWAP;
+ if (*fallback)
+ order = 0;
+ }
again:
mpol = shmem_get_pgoff_policy(info, index, order, &ilx);
@@ -2269,6 +2278,8 @@ static struct folio *shmem_swap_alloc_folio(struct inode *inode,
return folio;
if (order) {
+ *fallback |= SHMEM_SWAPIN_FALLBACK_SYNC;
+ *fallback_error = PTR_ERR(folio);
order = 0;
goto again;
}
@@ -2471,7 +2482,12 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
softleaf_t index_entry;
struct swap_info_struct *si;
struct folio *folio = NULL;
- int error, nr_pages, order;
+ int error, nr_pages, order = -1;
+ int actual_order = -1;
+ unsigned int path = SHMEM_SWAPIN_LOOKUP;
+ unsigned int fallback = SHMEM_SWAPIN_FALLBACK_NONE;
+ int fallback_error = 0;
+ pgoff_t orig_index = index;
pgoff_t offset;
VM_BUG_ON(!*foliop || !xa_is_value(*foliop));
@@ -2479,20 +2495,21 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
swap = index_entry;
*foliop = NULL;
- if (softleaf_is_poison_marker(index_entry))
- return -EIO;
+ if (softleaf_is_poison_marker(index_entry)) {
+ path = SHMEM_SWAPIN_POISON;
+ error = -EIO;
+ goto out;
+ }
si = get_swap_device(index_entry);
order = shmem_confirm_swap(mapping, index, index_entry);
if (IS_ERR_OR_NULL(si)) {
- if (order < 0)
- return -EEXIST;
- else
- return -EINVAL;
+ error = order < 0 ? -EEXIST : -EINVAL;
+ goto out;
}
if (unlikely(order < 0)) {
- put_swap_device(si);
- return -EEXIST;
+ error = -EEXIST;
+ goto put_swap;
}
/* index may point to the middle of a large entry, get the sub entry */
@@ -2506,10 +2523,13 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
if (!folio) {
if (data_race(si->flags & SWP_SYNCHRONOUS_IO)) {
/* Direct swapin skipping swap cache & readahead */
+ path = SHMEM_SWAPIN_SYNC;
folio = shmem_swap_alloc_folio(inode, vmf, index,
- swap, order, gfp);
+ swap, order, gfp, &fallback,
+ &fallback_error);
} else {
/* Cached swapin only supports order 0 folio */
+ path = SHMEM_SWAPIN_READAHEAD;
folio = shmem_swapin_cluster(swap, gfp, info, index);
}
if (IS_ERR_OR_NULL(folio)) {
@@ -2526,6 +2546,7 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
count_memcg_event_mm(fault_mm, PGMAJFAULT);
}
} else {
+ path = SHMEM_SWAPIN_CACHE;
swap_update_readahead(folio, NULL, 0);
}
@@ -2603,10 +2624,9 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
folio_put_swap(folio, NULL);
swap_cache_del_folio(folio);
folio_mark_dirty(folio);
- put_swap_device(si);
-
+ actual_order = folio_order(folio);
*foliop = folio;
- return 0;
+ goto put_swap;
failed:
if (shmem_confirm_swap(mapping, index, swap) < 0)
error = -EEXIST;
@@ -2616,10 +2636,16 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
if (folio)
folio_unlock(folio);
failed_nolock:
- if (folio)
+ if (folio) {
+ actual_order = folio_order(folio);
folio_put(folio);
+ }
+put_swap:
put_swap_device(si);
-
+out:
+ trace_shmem_swapin_folio(inode, orig_index, swp_type(index_entry),
+ swp_offset(index_entry), order, actual_order,
+ path, fallback, fallback_error, error);
return error;
}
--
2.25.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2] mm/shmem: trace folio allocation failures and fallbacks
2026-10-27 9:40 [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks Xuewen Wang
2026-10-27 9:40 ` [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks Xuewen Wang
@ 2026-10-27 9:40 ` Xuewen Wang
2026-10-28 7:56 ` [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks Baolin Wang
2 siblings, 0 replies; 6+ messages in thread
From: Xuewen Wang @ 2026-10-27 9:40 UTC (permalink / raw)
To: hughd, baolin.wang, rostedt, mhiramat, mathieu.desnoyers, corbet,
skhan, rdunlap, akpm
Cc: linux-mm, linux-kernel, linux-trace-kernel, linux-doc, Xuewen Wang
The shmem allocation counters cannot identify which tmpfs file is
affected by allocation fallback. They also do not describe order
filtering caused by VMA constraints or existing page-cache entries.
Trace failed calls to shmem_alloc_and_add_folio() and successful calls
that allocate below the highest order permitted by the supplied mask.
Record the original index and the highest permitted, highest suitable
and allocated orders to distinguish filtering from allocator fallback.
A suitable order of -1 identifies an empty filtered mask, where no folio
allocation is attempted; such records use a separate filter stage and keep
the stage error at zero while preserving the helper return value. Omit
successes at the requested order, including ordinary base-page allocations.
Identify failures by stage: allocation, memcg charging, cache insertion
or filesystem accounting. Preserve both the stage's errno and the
returned errno. In particular, a charge failure remains visible even
when a page-cache conflict makes the function return -EEXIST instead.
Stage and errno distinguish this case from a direct insertion race.
Document that each event describes one call to the helper, which may try
multiple candidate orders. The caller may subsequently retry at order 0
or restart a lookup, so an error in this event is not necessarily a failed
user operation. A successful order-0 retry is omitted as a success at that
call's requested order.
Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
---
Documentation/trace/events-shmem.rst | 45 +++++++++++++++++++++++
include/trace/events/shmem.h | 55 ++++++++++++++++++++++++++++
mm/shmem.c | 34 ++++++++++++++++-
3 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/Documentation/trace/events-shmem.rst b/Documentation/trace/events-shmem.rst
index 45da82761360..4a0e50bdbdf3 100644
--- a/Documentation/trace/events-shmem.rst
+++ b/Documentation/trace/events-shmem.rst
@@ -71,3 +71,48 @@ Filter or aggregate by both ``dev`` and ``ino``; inode numbers alone are
not unique across mounts. Trace filters use the numeric values shown by
the event's ``format`` file. Check ``per_cpu/cpu*/stats`` for dropped events
before interpreting counts.
+
+Allocation attempts
+-------------------
+
+``shmem_alloc_folio_finish`` records failed calls to
+``shmem_alloc_and_add_folio()`` and successful calls that allocate below
+their requested order. Successes at the requested order are omitted,
+including ordinary order-0 allocations.
+
+This is an attempt result, not the final result of a read, write or fault.
+After a higher-order failure the caller may retry at order 0, or restart
+its lookup on ``-EEXIST``. A successful order-0 retry is not recorded.
+Do not count all nonzero returns as failed user operations.
+
+``requested_order`` is the highest order allowed by this call's order mask,
+or 0 for a base-page request. ``suitable_order`` is the highest remaining
+order after VMA and page-cache filtering, or -1 if none remains.
+``actual_order`` is the allocated folio's order, or -1 if allocation did
+not produce a folio. Orders refer to base pages, not bytes.
+
+``stage`` identifies where the attempt ended:
+
+* ``filter``: filtering removed every candidate order, so no folio
+ allocation was attempted. ``suitable_order=-1`` identifies this case.
+* ``alloc``: a suitable order existed, but every allocation failed.
+* ``charge``: memcg charging failed.
+* ``cache``: page-cache insertion failed.
+* ``account``: filesystem block accounting failed after its retry.
+* ``done``: allocation succeeded at a smaller order.
+
+``error`` is the error at that stage; ``ret`` is the error returned to the
+caller. ``stage=filter`` has ``error=0`` because no allocator call occurred,
+while ``ret=-ENOMEM`` preserves the helper's return value. Both are zero for
+``done``. For a charge failure followed by a page-cache conflict, ``error``
+retains the charge error (such as ``-ENOMEM``), while ``ret=-EEXIST``
+requests a lookup retry. A direct insertion race instead has
+``stage=cache error=-EEXIST ret=-EEXIST``. Accounting errors retain
+``-ENOSPC`` or ``-EDQUOT`` as appropriate.
+
+Enable ``events/shmem/shmem_alloc_folio_finish/enable`` in the trace
+instance to collect these records. On successful fallbacks,
+``suitable_order < requested_order`` shows filtering, and
+``actual_order < suitable_order`` shows allocator fallback. Both may
+occur in one attempt. The existing shmem allocation and fallback
+counters remain useful for aggregate statistics.
diff --git a/include/trace/events/shmem.h b/include/trace/events/shmem.h
index 7a0050a1686f..72b20d6b3caf 100644
--- a/include/trace/events/shmem.h
+++ b/include/trace/events/shmem.h
@@ -80,6 +80,61 @@ TRACE_EVENT(shmem_swapin_folio,
__entry->fallback_error, __entry->error)
);
+#define SHMEM_ALLOC_ALLOC 0
+#define SHMEM_ALLOC_CHARGE 1
+#define SHMEM_ALLOC_CACHE 2
+#define SHMEM_ALLOC_ACCOUNT 3
+#define SHMEM_ALLOC_DONE 4
+#define SHMEM_ALLOC_FILTER 5
+
+TRACE_EVENT(shmem_alloc_folio_finish,
+
+ TP_PROTO(struct inode *inode, pgoff_t index,
+ int requested_order, int suitable_order, int actual_order,
+ unsigned int stage, int error, int ret),
+
+ TP_ARGS(inode, index, requested_order, suitable_order, actual_order,
+ stage, error, ret),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(unsigned long, ino)
+ __field(unsigned long, index)
+ __field(int, requested_order)
+ __field(int, suitable_order)
+ __field(int, actual_order)
+ __field(unsigned int, stage)
+ __field(int, error)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->index = index;
+ __entry->requested_order = requested_order;
+ __entry->suitable_order = suitable_order;
+ __entry->actual_order = actual_order;
+ __entry->stage = stage;
+ __entry->error = error;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("dev=%d:%d ino=%lu index=%lu requested_order=%d suitable_order=%d actual_order=%d stage=%s error=%d ret=%d",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino, __entry->index,
+ __entry->requested_order, __entry->suitable_order,
+ __entry->actual_order,
+ __print_symbolic(__entry->stage,
+ { SHMEM_ALLOC_ALLOC, "alloc" },
+ { SHMEM_ALLOC_CHARGE, "charge" },
+ { SHMEM_ALLOC_CACHE, "cache" },
+ { SHMEM_ALLOC_ACCOUNT, "account" },
+ { SHMEM_ALLOC_DONE, "done" },
+ { SHMEM_ALLOC_FILTER, "filter" }),
+ __entry->error, __entry->ret)
+);
+
#endif /* _TRACE_SHMEM_H */
/* This part must be outside protection */
diff --git a/mm/shmem.c b/mm/shmem.c
index 3b910aec5bcf..68321d61dfdb 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2155,17 +2155,23 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
unsigned long suitable_orders = 0;
struct folio *folio = NULL;
pgoff_t aligned_index;
+ pgoff_t orig_index = index;
long pages;
int error, order;
+ int requested_order, suitable_order;
+ int charge_error = 0;
+ unsigned int stage;
if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
orders = 0;
+ requested_order = orders ? highest_order(orders) : 0;
if (orders > 0) {
suitable_orders = shmem_suitable_orders(inode, vmf,
mapping, index, orders);
order = highest_order(suitable_orders);
+ suitable_order = order;
while (suitable_orders) {
pages = 1UL << order;
aligned_index = round_down(index, pages);
@@ -2181,19 +2187,35 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
order = next_order(&suitable_orders, order);
}
} else {
+ suitable_order = 0;
pages = 1;
folio = shmem_alloc_folio(gfp, 0, info, index);
}
- if (!folio)
+ if (!folio) {
+ if (suitable_order < 0)
+ trace_shmem_alloc_folio_finish(inode, orig_index,
+ requested_order,
+ suitable_order, -1,
+ SHMEM_ALLOC_FILTER, 0,
+ -ENOMEM);
+ else
+ trace_shmem_alloc_folio_finish(inode, orig_index,
+ requested_order,
+ suitable_order, -1,
+ SHMEM_ALLOC_ALLOC, -ENOMEM,
+ -ENOMEM);
return ERR_PTR(-ENOMEM);
+ }
allocated:
__folio_set_locked(folio);
__folio_set_swapbacked(folio);
gfp &= GFP_RECLAIM_MASK;
+ stage = SHMEM_ALLOC_CHARGE;
error = mem_cgroup_charge(folio, fault_mm, gfp);
if (error) {
+ charge_error = error;
if (xa_find(&mapping->i_pages, &index,
index + pages - 1, XA_PRESENT)) {
error = -EEXIST;
@@ -2208,10 +2230,12 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
goto unlock;
}
+ stage = SHMEM_ALLOC_CACHE;
error = shmem_add_to_page_cache(folio, mapping, index, NULL, gfp);
if (error)
goto unlock;
+ stage = SHMEM_ALLOC_ACCOUNT;
error = shmem_inode_acct_blocks(inode, pages);
if (error) {
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
@@ -2242,9 +2266,17 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
shmem_recalc_inode(inode, pages, 0);
folio_add_lru(folio);
+ if (folio_order(folio) < requested_order)
+ trace_shmem_alloc_folio_finish(inode, orig_index,
+ requested_order, suitable_order,
+ folio_order(folio),
+ SHMEM_ALLOC_DONE, 0, 0);
return folio;
unlock:
+ trace_shmem_alloc_folio_finish(inode, orig_index, requested_order,
+ suitable_order, folio_order(folio), stage,
+ charge_error ?: error, error);
folio_unlock(folio);
folio_put(folio);
return ERR_PTR(error);
--
2.25.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks
2026-10-27 9:40 ` [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks Xuewen Wang
@ 2026-10-27 23:55 ` Randy Dunlap
2026-10-28 3:27 ` wangxuewen
0 siblings, 1 reply; 6+ messages in thread
From: Randy Dunlap @ 2026-10-27 23:55 UTC (permalink / raw)
To: Xuewen Wang, hughd, baolin.wang, rostedt, mhiramat,
mathieu.desnoyers, corbet, skhan, akpm
Cc: linux-mm, linux-kernel, linux-trace-kernel, linux-doc
On 10/27/26 2:40 AM, Xuewen Wang wrote:
> diff --git a/Documentation/trace/events-shmem.rst b/Documentation/trace/events-shmem.rst
> new file mode 100644
> index 000000000000..45da82761360
> --- /dev/null
> +++ b/Documentation/trace/events-shmem.rst
> @@ -0,0 +1,73 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +Tmpfs and shmem events
> +=====================
Underline is one character too short:
Documentation/trace/events-shmem.rst:4: WARNING: Title underline too short.
Tmpfs and shmem events
===================== [docutils]
--
~Randy
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks
2026-10-27 23:55 ` Randy Dunlap
@ 2026-10-28 3:27 ` wangxuewen
0 siblings, 0 replies; 6+ messages in thread
From: wangxuewen @ 2026-10-28 3:27 UTC (permalink / raw)
To: Randy Dunlap, hughd, baolin.wang, rostedt, mhiramat,
mathieu.desnoyers, corbet, skhan, akpm
Cc: linux-mm, linux-kernel, linux-trace-kernel, linux-doc
Hi Randy,
Thanks for catching this. I will fix the title underline in v2.
Xuewen
在 2026/10/28 07:55, Randy Dunlap 写道:
>
>
> On 10/27/26 2:40 AM, Xuewen Wang wrote:
>> diff --git a/Documentation/trace/events-shmem.rst b/Documentation/trace/events-shmem.rst
>> new file mode 100644
>> index 000000000000..45da82761360
>> --- /dev/null
>> +++ b/Documentation/trace/events-shmem.rst
>> @@ -0,0 +1,73 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +Tmpfs and shmem events
>> +=====================
>
> Underline is one character too short:
>
> Documentation/trace/events-shmem.rst:4: WARNING: Title underline too short.
>
> Tmpfs and shmem events
> ===================== [docutils]
>
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks
2026-10-27 9:40 [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks Xuewen Wang
2026-10-27 9:40 ` [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks Xuewen Wang
2026-10-27 9:40 ` [PATCH 2/2] mm/shmem: trace folio allocation failures and fallbacks Xuewen Wang
@ 2026-10-28 7:56 ` Baolin Wang
2 siblings, 0 replies; 6+ messages in thread
From: Baolin Wang @ 2026-10-28 7:56 UTC (permalink / raw)
To: Xuewen Wang, hughd, rostedt, mhiramat, mathieu.desnoyers, corbet,
skhan, rdunlap, akpm
Cc: linux-mm, linux-kernel, linux-trace-kernel, linux-doc
On 10/27/26 5:40 PM, Xuewen Wang wrote:
> The tmpfs swapin and allocation paths are currently a blind spot for
> tracing: the swap layer has no tracepoints at all, the existing
> counters cannot attribute events to a file, and an mTHP request can be
> silently downgraded at two layers - or fail outright - with only one
> of those having any accounting.
>
> On the swapin side:
>
> - PGMAJFAULT only accounts the page fault path; swapins triggered by
> read()/write()/fallocate()/truncate/symlink go through
> shmem_get_folio() with vmf/fault_type NULL and are not counted
> - PSWPIN/ZSWPIN are counted at the I/O and zswap layers with global
> and memcg granularity, but cannot be attributed to an inode or file
> - the mTHP order degradations in shmem_swap_alloc_folio() (uffd/zswap
> forced order-0, order-0 retry after sync allocation failure) have
> no counters at all, unlike the swapout side which counts
> THP_SWPOUT_FALLBACK and MTHP_STAT_SWPOUT_FALLBACK
>
> On the allocation side:
>
> - the order mask from shmem_suitable_orders() (vma filtering, page
> cache layout conflicts) can drop the order with no counter at all;
> the code comment notes that -EEXIST "would be the right code, but
> not here"
> - the allocation fallback loop is counted by MTHP_STAT_SHMEM_FALLBACK
> without attribution
> - a fully masked-out request fails with -ENOMEM while the caller
> silently retries at order 0
> - when mem_cgroup_charge() fails with the target range already
> occupied, the error is rewritten to -EEXIST, making a cgroup limit
> event indistinguishable from a benign insertion race
>
> Patch 1 adds a single shmem_swapin_folio event at every return from
> shmem_swapin_folio(): the original caller index and XArray entry, the
> requested (entry) and actual (folio) orders, the branch taken -
> validation failure, initial swap cache hit, synchronous helper,
> readahead helper, or poison marker - and the return value. For the
> synchronous helper it also records why the request became order 0: a
> fallback bitmask reporting the userfaultfd and zswap restrictions
> together when both apply, sync_fail plus the initial errno when a
> higher-order attempt failed and was retried, or none.
>
> The fallback field is a bitmask rather than an enum because the two
> policy restrictions are independent: a VMA can have userfaultfd armed
> while the boot-wide zswap latch is also set. Reporting only one value
> would hide that the other restriction must also go away before large
> folios come back. sync_fail never combines with them - a policy
> downgrade forces order 0 before the first attempt, so the retry that
> sets sync_fail can only run when no policy bit was set.
>
> Patch 2 adds a shmem_alloc_folio_finish event with the full order
> chain (requested/suitable/actual) separating VMA and page-cache
> filtering from allocator fallback, a stage value (filter, alloc,
> charge, cache, account, done) identifying where the attempt ended,
> and both the stage errno and the returned errno: a memcg charge
> failure stays visible as error=-ENOMEM even when a page-cache
> conflict rewrites the return to -EEXIST. A fully filtered-out request
> reports stage=filter with error=0 and suitable_order=-1 because no
> allocator call occurred. Successes at the requested order - including
> ordinary base-page allocations - are omitted, so order-0 traffic
> never floods the ring buffer.
>
> shmem_swapin_folio does fire on every swapin attempt, and that is
> deliberate: a swapin is a major-fault or I/O slow path whose rate is
> bounded by actual swap traffic, and each event is precisely what
> PSWPIN cannot attribute. Folio allocation is the opposite - it runs
> for every page ever touched, "succeeded at the requested order"
> carries no information, and it is already counted by
> MTHP_STAT_SHMEM_ALLOC and THP_FILE_ALLOC. Hence the asymmetric
> gating between the two patches. Both events, their fields and the
> attempt semantics are documented in Documentation/trace/events-shmem.rst.
>
> Three questions answered up front:
>
> Why not vmstat counters? Counters show trends, tracepoints do
> per-inode attribution; the two are complementary, and the trend side
> already has its counters.
>
> Why not eBPF/kprobes? BPF programs attach to tracepoints, so this
> series is an enabler for BPF rather than competition. The interesting
> data is mid-function state (suitable_order is a local consumed by the
> fallback loop, the fallback bitmask is an internal policy decision,
> and the charge error is rewritten in place before the return) that is
> not visible at any function boundary; the functions involved are
> static and may be inlined, so kprobe scripts silently break across
> kernel versions, while a tracepoint's fields are documented in its
> format file and any change is visible in review.
>
> Why not extend trace_mm_filemap_add_to_page_cache()? tmpfs never
> emits it. Its only call site is in mm/filemap.c, while
> shmem_add_to_page_cache() runs its own XArray insertion loop with no
> trace calls at all, so tmpfs folios entering the page cache produce
> no filemap event. Even if they did, it reports only the final order -
> no requested/suitable/actual chain, no stage, no errno - and nothing
> whatsoever for the swapin side.
>
> Reproduced on x86_64 (8 CPUs), kernel v7.3.0-rc3 + this series, tmpfs
> on disk-backed swap with cgroup v1 memory limits forcing swapout
> deterministically; zram provides the SWP_SYNCHRONOUS_IO device for
> the synchronous path.
>
> Swapin attribution - three tmpfs files (400M + 500M + 150M) charged
> to one cgroup, 965M forced out with a 100M limit, then the 400M and
> 150M files read back with read():
>
> PSWPIN +116219 pages, PGMAJFAULT +0
>
> ino=2 (db_shm, 400M, read back): 102400 events - every one of
> its 102400 pages
> ino=3 (app_cache, 500M, untouched): 0 events
> ino=4 (misc, 150M, read back): 16661 events, exactly the
> pages that had left memory
>
> 104528 events took the cache branch and 14533 the readahead branch;
> all carry error=0 and fallback=none. PGMAJFAULT never moved while
> 119061 read()-triggered swapin events were recorded.
>
> mTHP order degradation on the readahead path - huge=always mount,
> 512M faulted in as 256 2M folios, 414M swapped out to disk swap and
> read back:
>
> 102672 events requested_order=0 actual_order=0
> 1656 events requested_order=5 actual_order=0
> 1449 events requested_order=6 actual_order=0
> 207 events requested_order=9 actual_order=0
>
> The 3312 events with requested_order > 0 all came back order 0 while
> fallback stays none: the readahead path returning a smaller folio
> without any policy downgrade, exactly the case the documentation
> describes.
>
> Synchronous whole-folio swapin - the same workload with zram as the
> swap device and the zswap latch unset:
>
> 207 events, all path=sync requested_order=9 actual_order=9
>
> 207 2M folios account for the 414M that went out: with no policy
> restriction the synchronous helper preserves the entry order end to
> end.
>
> zswap policy downgrade - zswap enabled once then disabled (arming the
> one-way latch), 128M fault-written with zram swap, 90M forced out:
>
> 23040 events, one per page swapped out
> 720 events with requested_order > 0, every one fallback=zswap
> 360 order=5 / 315 order=6 / 45 order=9, all actual_order=0
>
> fallback_error=0 and error=0 throughout: policy downgrades, not
> failed attempts. The remaining 22320 events are order-0 entries that
> had nothing to downgrade (fallback=none).
>
> Combined policy restrictions - a userfaultfd registered over the
> faulting VMA plus the armed zswap latch, 158M forced out and read
> back through the armed VMA. This is the case a single-value fallback
> field cannot report:
>
> 1264 events fallback=userfaultfd|zswap
> 632 order=5 / 553 order=6 / 79 order=9, all actual_order=0
>
> The 39184 order-0 events in the same run report fallback=none: an
> order-0 request has nothing to downgrade.
>
> Allocation filtering - MADV_NOHUGEPAGE 64K stripes every 2M on a
> huge=always mount, 512M fault-written:
>
> 1280 events, all stage=done error=0 ret=0
> 256 each for requested_order=9 with suitable_order and
> actual_order of 4, 5, 6, 7 and 8
>
> Not one 2M folio is allocated across the whole 512M file
> (shmem_alloc 2M stat delta 0), yet nothing today reports why.
>
> Allocation failures under a cgroup limit - 300M fault-written inside
> a 100M memory limit, 8365 events in one run:
>
> 1976 stage=charge error=-12 ret=-12 memcg charge failure, the
> errno preserved
> 1976 stage=filter error=0 ret=-12 suitable_order=-1
> actual_order=-1, no
> allocator call occurred
> 4413 stage=done error=0 ret=0 cascade fallback,
> suitable_order 1 to 9
>
> Filesystem accounting - a 16m tmpfs mount written with 64M of 4K
> pwrites: 24576 stage=account events, all error=-28 ret=-28, firing
> at the block where accounting ran out; 12288 carry requested_order=9
> (the large-folio write attempt) and 12288 requested_order=0 (its
> order-0 retry), two events per failed write.
>
> Negative controls: 0 events for re-reading a file already in cache,
> 0 with the events disabled, and 0 for a 64M plain order-0 write
> (patch 2's volume filter).
>
> Not exercised at runtime: path=lookup and path=poison with nonzero
> error, fallback=sync_fail with fallback_error (these need a failing
> higher-order synchronous attempt or fault injection), userfaultfd
> without zswap (the latch is one-way per boot and was already armed
> for the combined test), stage=alloc with a real allocator failure,
> and stage=cache (a concurrent insertion race). Everything quoted
> above was observed on the target machine.
Sorry, I'm not a fan of adding such tracepoints without any real use
cases in production to show which data is truly useful. And as you
mentioned, eBPF can do most of the data tracing.
Moreover, I don't think shmem is special. I don't want shmem to have its
own swapout/swapin tracepoints while anonymous memory has its own
swapout/swapin tracepoints. If there are swap related tracepoints that
are actually needed, they should be considered together as proper
interfaces.
Finally, tell your LLM to simplify the cover letter.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-28 7:56 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27 9:40 [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks Xuewen Wang
2026-10-27 9:40 ` [PATCH 1/2] mm/shmem: trace swapin results and order fallbacks Xuewen Wang
2026-10-27 23:55 ` Randy Dunlap
2026-10-28 3:27 ` wangxuewen
2026-10-27 9:40 ` [PATCH 2/2] mm/shmem: trace folio allocation failures and fallbacks Xuewen Wang
2026-10-28 7:56 ` [PATCH 0/2] mm/shmem: trace swapin results and allocation fallbacks Baolin Wang
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®