* [PATCH 00/16] mm/shmem: various (urgent?) fixes
@ 2026-10-26 9:40 David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 01/16] mm/mempolicy: charge shared policy nodes David Hildenbrand (Arm)
` (17 more replies)
0 siblings, 18 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm),
Chris Mason, stable, Josef Bacik, Eva Kurchatova
Hi,
the fixing fun continues.
This is a handful of fixes for shmem/tmpfs. I sneaked in a related
hugetlb fix that stalled upstream.
Some of the stuff is more urgent (patch #1, F_SEAL_EXEC, truncation
hangs), others are less urgent but still rather unpleasant.
The majority are fixes from Anthropic that I cleaned up or reworked.
While doing that, I found a bunch of other issues and tried to fix them
somewhat cleanly.
In general, shmem + swap handling is a nightmare. shmem_undo_range() is
such utter garbage it makes me want to cry. I mean, really, WTF did we
end up creating there. Does any expert here have time to clean that up and
make it *actually* maintainable.
The following things are still broken in shmem_undo_range():
(1) Truncate/punch of a single page of a large swap-error marker
We don't split. Consequently, read access afterwards will still fail
instead of returning 0s.
I have a fix to try to split (which can fail), but I don't consider it
that urgent, really. So I'm not including it. Might send it later
separately.
Truncate/punch of a partial page can obviously never do the right thing,
as we cannot poison parts of pages ...
(2) Truncate/punch whole pages within part of a large swap entry
We try to read the entry, but if we are out of memory that might fail.
Consequently, we will not free up swap space and a re-read will not give
us 0s but the previous content.
AI proposed a nasty NOFAIL patch which has too many nasty side effects
IMHO (allowing users to force-read pages and retain them afterwards even
though memory.max would disallow it). This needs a proper thought. Likely,
support for splitting large swap entries (and large folios in the
swapcache, which I recall is under development by Kairui). But splitting
can fail ...
(3) Truncate/punch of a part of a base-page swap entry
We obviously have to read the page in, in order to 0-out the parts to
discard. That might obviously fail.
Maybe we should install swapin-error markers if swapping during
truncate/punchhole fails, to at least tell user space on later access
that something went wrong? We might affect unrelated data, outside the
requested range, though. Anyhow, these are long standing issues and
I don't consider them urgent.
They need a proper solution.
I did my best trying to test these fixes. I was able to reproduce some
of the things (including unkillable truncation hangs), and the fixes seemed
to help.
But again, shmem_undo_range() is just an absolute mess.
Please prioritize review of these fixes, I want to get fixes for the
issues in a timely manner upstream.
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
Chris Mason (5):
mm/mempolicy: charge shared policy nodes
mm/shmem: honour F_SEAL_EXEC when an ACL is set
mm/shmem: avoid moving the truncation start backwards
mm/shmem: stop retrying folios that cannot be split
mm/shmem: preserve data in clean folios when splitting fails
David Hildenbrand (Arm) (7):
mm/shmem: discard whole new large folios on fallocate rollback
mm/shmem: rescan after nonuniform folio splitting
mm/shmem: fix truncate hang in large swap entry
mm/shmem: preserve allocation errors when swapping in large entries
mm/shmem: fix truncate loop after boundary swapin failure
mm/shmem: fix accounting of swapin-error markers
mm/shmem: fix truncation of files containing only swapin-error markers
Eva Kurchatova (1):
hugetlbfs: enforce F_SEAL_EXEC in setattr
Josef Bacik (3):
tmpfs: keep the quota soft limits of an unused id
tmpfs: preserve explicitly disabled quota limits
mm/shmem: hold off eviction in shmem_writeout()
fs/hugetlbfs/inode.c | 8 +++
include/linux/memfd.h | 9 +++
mm/mempolicy.c | 4 +-
mm/shmem.c | 170 +++++++++++++++++++++++++++++++++++---------------
mm/shmem_quota.c | 28 +++++----
5 files changed, 154 insertions(+), 65 deletions(-)
---
base-commit: 21a7f44fd0765fcc5194130b28767786eba1d1e7
change-id: 20261025-fixes_shmem-2782719b130e
--
Cheers,
David
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 01/16] mm/mempolicy: charge shared policy nodes
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 13:38 ` Pedro Falcato
2026-10-26 15:44 ` Kiryl Shutsemau
2026-10-26 9:40 ` [PATCH 02/16] tmpfs: keep the quota soft limits of an unused id David Hildenbrand (Arm)
` (16 subsequent siblings)
17 siblings, 2 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Chris Mason, stable
From: Chris Mason <mason@kernel.org>
Any local user can bypass memory.max with mbind() on a tmpfs file, and
allocate unbounded kernel memory.
mbind() sets a NUMA memory policy for a range of a mapping. If the
mapping is of a shmem and friends, the kernel also stores the policy on
the inode, in a tree with one entry per range of the file.
Each mbind() on a single page of a mapping can add one shared policy node
(struct sp_node) and one struct mempolicy to that tree. They outlive the
mapping and stay around until the inode is destroyed. The tree is not
bounded by the file size, so the file can be empty.
Memory that user space can allocate must be charged to a memory cgroup. A
slab cache does that for all of its objects if it is created with
SLAB_ACCOUNT. numa_policy_init() creates the slab caches for both
structs without SLAB_ACCOUNT, so they are not charged to any cgroup.
Reproduced with mbind() on single pages of an empty tmpfs file, from a
cgroup with memory.max=8M: the global OOM killer killed tasks owned by
root, and the machine panicked.
Fix this by creating both caches with SLAB_ACCOUNT. This change implies
that mbind() can now fail when running into memcg limits. Further,
the policy_cache also serves task and VMA policies, so they are
similarly accounted now.
Note that per-file limits are not really helpful, as the user could
simply use many files; having limits per user is tricky as well.
Fix generated by an LLM and submitted by Chris Mason in private.
Fixes: 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
[ david: smaller description tweaks ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/mempolicy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index e9860fb9f73f8..ad49b7524064a 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -3372,11 +3372,11 @@ void __init numa_policy_init(void)
policy_cache = kmem_cache_create("numa_policy",
sizeof(struct mempolicy),
- 0, SLAB_PANIC, NULL);
+ 0, SLAB_PANIC | SLAB_ACCOUNT, NULL);
sn_cache = kmem_cache_create("shared_policy_node",
sizeof(struct sp_node),
- 0, SLAB_PANIC, NULL);
+ 0, SLAB_PANIC | SLAB_ACCOUNT, NULL);
for_each_node(nid) {
preferred_node_policy[nid] = (struct mempolicy) {
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 02/16] tmpfs: keep the quota soft limits of an unused id
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 01/16] mm/mempolicy: charge shared policy nodes David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 10:53 ` Jan Kara
2026-10-26 9:40 ` [PATCH 03/16] tmpfs: preserve explicitly disabled quota limits David Hildenbrand (Arm)
` (15 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Josef Bacik, stable
From: Josef Bacik <josef@toxicpanda.com>
When the last reference to a dquot goes away, shmem_release_dquot()
stores its limits in the rb tree of the mount, unless
shmem_is_empty_dquot() says that there is nothing worth storing. That
function looks at the usage and at the two hard limits only.
So if the administrator sets nothing but soft limits for an id that owns
no file yet, the entry is thrown away as soon as the dquot that
Q_SETQUOTA used is released, and the id never gets to see a limit.
To fix it, compare the soft limits as well: an entry can be empty only if
both soft limits are zero as well, which is exactly what
shmem_acquire_dquot() sets for a new entry.
Fix generated by an LLM and sent by Josef Bacik in private.
Fixes: de4c0e7ca8b5 ("shmem: Add default quota limit mount options")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
[ david: slightly adjusted description ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem_quota.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/mm/shmem_quota.c b/mm/shmem_quota.c
index a3fa4bd93e753..43d3b4fd6cb1b 100644
--- a/mm/shmem_quota.c
+++ b/mm/shmem_quota.c
@@ -256,6 +256,8 @@ static bool shmem_is_empty_dquot(struct dquot *dquot)
if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
(dquot->dq_dqb.dqb_curspace == 0 &&
dquot->dq_dqb.dqb_curinodes == 0 &&
+ dquot->dq_dqb.dqb_bsoftlimit == 0 &&
+ dquot->dq_dqb.dqb_isoftlimit == 0 &&
dquot->dq_dqb.dqb_bhardlimit == bhardlimit &&
dquot->dq_dqb.dqb_ihardlimit == ihardlimit))
return true;
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 03/16] tmpfs: preserve explicitly disabled quota limits
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 01/16] mm/mempolicy: charge shared policy nodes David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 02/16] tmpfs: keep the quota soft limits of an unused id David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 13:37 ` Jan Kara
2026-10-26 9:40 ` [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set David Hildenbrand (Arm)
` (14 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Josef Bacik, stable
From: Josef Bacik <josef@toxicpanda.com>
An administrator’s decision to disable a user’s or group’s quota
limits can be silently lost when tmpfs restores the mount defaults
after releasing and reacquiring the quota record. This can cause
later file creation or writes to fail with EDQUOT, even though the
administrator has explicitly disabled quota limits for the user or
group.
do_set_dqblk() sets DQ_FAKE_B when all block and inode limits are zero.
This means quota enforcement is disabled for that user or group.
shmem_is_empty_dquot() treats the flag as a reason to discard the entry
from the mount's quota tree. The next shmem_acquire_dquot() finds no
saved entry and recreates it with the mount's default hard limits.
If the cached dquot is reused, DQ_FAKE_B remains set: Q_GETQUOTA reports
the restored defaults, but enforcement is still disabled. Once that
dquot has been reclaimed, a new dquot enforces the restored defaults.
Remove the DQ_FAKE_B test. Only discard entries with zero usage, zero
soft limits and hard limits equal to the mount defaults. Explicitly
disabled limits are then preserved whenever the mount defaults are
nonzero.
Explicitly disabled limits now retain a quota-tree entry, just like other
limits that differ from the mount defaults.
Rename shmem_is_empty_dquot() to shmem_is_default_dquot() to describe
the check for default values.
Fix generated by an LLM and sent by Josef Bacik in private.
Fixes: de4c0e7ca8b5 ("shmem: Add default quota limit mount options")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
[ david: code cleanups; rewrite description ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem_quota.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/mm/shmem_quota.c b/mm/shmem_quota.c
index 43d3b4fd6cb1b..d191ebda3f00e 100644
--- a/mm/shmem_quota.c
+++ b/mm/shmem_quota.c
@@ -239,7 +239,7 @@ static int shmem_acquire_dquot(struct dquot *dquot)
return ret;
}
-static bool shmem_is_empty_dquot(struct dquot *dquot)
+static bool shmem_is_default_dquot(struct dquot *dquot)
{
struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;
qsize_t bhardlimit;
@@ -253,21 +253,21 @@ static bool shmem_is_empty_dquot(struct dquot *dquot)
ihardlimit = sbinfo->qlimits.grpquota_ihardlimit;
}
- if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
- (dquot->dq_dqb.dqb_curspace == 0 &&
- dquot->dq_dqb.dqb_curinodes == 0 &&
- dquot->dq_dqb.dqb_bsoftlimit == 0 &&
- dquot->dq_dqb.dqb_isoftlimit == 0 &&
- dquot->dq_dqb.dqb_bhardlimit == bhardlimit &&
- dquot->dq_dqb.dqb_ihardlimit == ihardlimit))
- return true;
-
- return false;
+ /*
+ * Defaults are zero usage and soft limits, and the mount's hard limits.
+ * DQ_FAKE_B only means that *all* limits are zero, which need not match
+ * the mount defaults.
+ */
+ return dquot->dq_dqb.dqb_curspace == 0 &&
+ dquot->dq_dqb.dqb_curinodes == 0 &&
+ dquot->dq_dqb.dqb_bsoftlimit == 0 &&
+ dquot->dq_dqb.dqb_isoftlimit == 0 &&
+ dquot->dq_dqb.dqb_bhardlimit == bhardlimit &&
+ dquot->dq_dqb.dqb_ihardlimit == ihardlimit;
}
/*
- * Store limits from dquot in the tree unless it's fake. If it is fake
- * remove the id from the tree since there is no useful information in
- * there.
+ * Remove the ID from the tree if it has no usage and its limits match
+ * the mount defaults. Otherwise, save its limits for the next acquire.
*/
static int shmem_release_dquot(struct dquot *dquot)
{
@@ -302,7 +302,7 @@ static int shmem_release_dquot(struct dquot *dquot)
return -ENOENT;
found:
- if (shmem_is_empty_dquot(dquot)) {
+ if (shmem_is_default_dquot(dquot)) {
/* Remove entry from the tree */
rb_erase(&entry->node, info->dqi_priv);
kfree(entry);
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (2 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 03/16] tmpfs: preserve explicitly disabled quota limits David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 13:48 ` Pedro Falcato
2026-10-26 15:52 ` Kiryl Shutsemau
2026-10-26 9:40 ` [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr David Hildenbrand (Arm)
` (13 subsequent siblings)
17 siblings, 2 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Chris Mason, stable
From: Chris Mason <mason@kernel.org>
The owner of a memfd sealed with F_SEAL_EXEC can still change its
execute bits by setting a POSIX access ACL. With CONFIG_TMPFS_POSIX_ACL,
this lets an unprivileged owner make a non-executable memfd executable,
bypassing vm.memfd_noexec=2.
simple_set_acl() updates i_mode directly, bypassing the seal check in
shmem_setattr(). Add shmem_set_acl() to check the resulting mode. Share
the F_SEAL_EXEC check with shmem_setattr() using memfd_check_seals_mode()
in include/linux/memfd.h.
Fix generated by an LLM and sent by Chris Mason in private.
Fixes: 6fd7353829ca ("mm/memfd: add F_SEAL_EXEC")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
[ david: use common helper; simplify description ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
include/linux/memfd.h | 9 +++++++++
mm/shmem.c | 35 ++++++++++++++++++++++++++++++-----
2 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/include/linux/memfd.h b/include/linux/memfd.h
index c159e40e3f34a..272479317cf10 100644
--- a/include/linux/memfd.h
+++ b/include/linux/memfd.h
@@ -2,10 +2,19 @@
#ifndef __LINUX_MEMFD_H
#define __LINUX_MEMFD_H
+#include <linux/fcntl.h>
#include <linux/file.h>
#define MEMFD_ANON_NAME "[memfd]"
+static inline int memfd_check_seals_mode(unsigned int seals, umode_t old_mode,
+ umode_t new_mode)
+{
+ if ((seals & F_SEAL_EXEC) && ((old_mode ^ new_mode) & 0111))
+ return -EPERM;
+ return 0;
+}
+
#ifdef CONFIG_MEMFD_CREATE
extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg);
struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);
diff --git a/mm/shmem.c b/mm/shmem.c
index d97fc32e7c942..86b9d19f26c7e 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -77,6 +77,7 @@ static struct vfsmount *shm_mnt __ro_after_init;
#include <linux/magic.h>
#include <linux/syscalls.h>
#include <linux/fcntl.h>
+#include <linux/memfd.h>
#include <uapi/linux/memfd.h>
#include <linux/rmap.h>
#include <linux/uuid.h>
@@ -1549,10 +1550,11 @@ static int shmem_setattr(const struct mnt_idmap *idmap,
if (error)
return error;
- if ((info->seals & F_SEAL_EXEC) && (attr->ia_valid & ATTR_MODE)) {
- if ((inode->i_mode ^ attr->ia_mode) & 0111) {
- return -EPERM;
- }
+ if (attr->ia_valid & ATTR_MODE) {
+ error = memfd_check_seals_mode(info->seals, inode->i_mode,
+ attr->ia_mode);
+ if (error)
+ return error;
}
if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
@@ -5400,12 +5402,35 @@ static const struct file_operations shmem_file_operations = {
#endif
};
+#ifdef CONFIG_TMPFS_POSIX_ACL
+static int shmem_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
+ struct posix_acl *acl, int type)
+{
+ struct inode *inode = d_inode(dentry);
+ umode_t mode = inode->i_mode;
+ int error;
+
+ if (type == ACL_TYPE_ACCESS) {
+ error = posix_acl_equiv_mode(acl, &mode);
+ if (error < 0)
+ return error;
+ error = memfd_check_seals_mode(SHMEM_I(inode)->seals,
+ inode->i_mode, mode);
+ if (error)
+ return error;
+ }
+ return simple_set_acl(idmap, dentry, acl, type);
+}
+#else
+#define shmem_set_acl NULL
+#endif
+
static const struct inode_operations shmem_inode_operations = {
.getattr = shmem_getattr,
.setattr = shmem_setattr,
#ifdef CONFIG_TMPFS_XATTR
.listxattr = shmem_listxattr,
- .set_acl = simple_set_acl,
+ .set_acl = shmem_set_acl,
.fileattr_get = shmem_fileattr_get,
.fileattr_set = shmem_fileattr_set,
#endif
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (3 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 13:49 ` Pedro Falcato
2026-10-26 15:55 ` Kiryl Shutsemau
2026-10-26 9:40 ` [PATCH 06/16] mm/shmem: hold off eviction in shmem_writeout() David Hildenbrand (Arm)
` (12 subsequent siblings)
17 siblings, 2 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Eva Kurchatova, stable
From: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
F_SEAL_EXEC is enforced by shmem_setattr(), but hugetlbfs_setattr() does
not check it. As a result, fchmod() can change the execute bits of a
sealed hugetlbfs memfd.
Use memfd_check_seals_mode() in hugetlbfs_setattr() to reject changes
to execute bits when F_SEAL_EXEC is set.
This also fixes the memfd_test hugetlbfs SEAL-EXEC subtest, where
fchmod() incorrectly succeeded after applying F_SEAL_EXEC.
LLM assistance was used to reuse the helper and revise the description.
Fixes: 6fd7353829ca ("mm/memfd: add F_SEAL_EXEC")
Cc: stable@vger.kernel.org
Signed-off-by: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
Link: https://patch.msgid.link/20260629180247.2343229-1-eva.kurchatova@virtuozzo.com
[ david: use common helper; fixup Fixes: ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
fs/hugetlbfs/inode.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 6656807ce4377..7f5c3962b5d56 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -28,6 +28,7 @@
#include <linux/folio_batch.h>
#include <linux/fs_parser.h>
#include <linux/mman.h>
+#include <linux/memfd.h>
#include <linux/slab.h>
#include <linux/dnotify.h>
#include <linux/statfs.h>
@@ -841,6 +842,13 @@ static int hugetlbfs_setattr(const struct mnt_idmap *idmap,
if (error)
return error;
+ if (ia_valid & ATTR_MODE) {
+ error = memfd_check_seals_mode(info->seals, inode->i_mode,
+ attr->ia_mode);
+ if (error)
+ return error;
+ }
+
trace_hugetlbfs_setattr(inode, dentry, attr);
if (ia_valid & ATTR_SIZE) {
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 06/16] mm/shmem: hold off eviction in shmem_writeout()
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (4 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 15:14 ` Pedro Falcato
2026-10-26 9:40 ` [PATCH 07/16] mm/shmem: avoid moving the truncation start backwards David Hildenbrand (Arm)
` (11 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Josef Bacik, stable
From: Josef Bacik <josef@toxicpanda.com>
Once shmem_writeout() has moved the folio from the page cache to the
swap cache, the folio lock no longer keeps the inode alive: truncation
and eviction find a swap entry, free it and do not wait for anybody.
That did not matter as long as swap_writeout() was the last thing the
function did. But since it puts the folio back when swap_writeout()
returns AOP_WRITEPAGE_ACTIVATE, it uses the mapping and the inode
afterwards:
reclaim last close of an unlinked file
shmem_writeout()
shmem_delete_from_page_cache()
swap_writeout()
zswap_store() fails shmem_evict_inode()
writeback is disabled shmem_truncate_range()
return AOP_WRITEPAGE_ACTIVATE shmem_free_swap()
destroy_inode()
... RCU grace period, freed
shmem_add_to_page_cache(mapping)
xas_lock_irq() <- use after free
swap_writeout() behaves that way if zswap is enabled, cannot take the
folio and the memcg or one of its ancestors has memory.zswap.writeback
set to 0.
shmem_unuse() already added infrastructure to deal with that.
Reuse info->stop_eviction to protect the inode during writeout. Increment
it before removing the folio from the page cache, and decrement it after
swap_writeout() and any attempted reinsertion and inode accounting.
In shmem_evict_inode(), wait for the counter to reach zero even if
swapoff has already removed the inode from the swap list. Add some helpers
to make the code easier to digest.
Fix generated by an LLM and sent by Josef Bacik in private.
Fixes: 6344a6d9ce13 ("mm/shmem: writeout free swap if swap_writeout() reactivates")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
[ david: code cleanups; improve description ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 36 ++++++++++++++++++++++++++++++------
1 file changed, 30 insertions(+), 6 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index 86b9d19f26c7e..c551e33c697fe 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1619,6 +1619,17 @@ static int shmem_setattr(const struct mnt_idmap *idmap,
return error;
}
+static void shmem_disallow_eviction(struct shmem_inode_info *info)
+{
+ atomic_inc(&info->stop_eviction);
+}
+
+static void shmem_allow_eviction(struct shmem_inode_info *info)
+{
+ if (atomic_dec_and_test(&info->stop_eviction))
+ wake_up_var(&info->stop_eviction);
+}
+
static void shmem_evict_inode(struct inode *inode)
{
struct shmem_inode_info *info = SHMEM_I(inode);
@@ -1631,16 +1642,21 @@ static void shmem_evict_inode(struct inode *inode)
mapping_set_exiting(inode->i_mapping);
shmem_truncate_range(inode, 0, (loff_t)-1);
shmem_unused_huge_del(inode);
- while (!list_empty(&info->swaplist)) {
- /* Wait while shmem_unuse() is scanning this inode... */
+ /*
+ * Writeout may still use the inode after swapoff has removed it
+ * from the swaplist, so always wait at least once.
+ */
+ do {
wait_var_event(&info->stop_eviction,
!atomic_read(&info->stop_eviction));
+ if (list_empty(&info->swaplist))
+ break;
spin_lock(&shmem_swaplist_lock);
/* ...but beware of the race if we peeked too early */
if (!atomic_read(&info->stop_eviction))
list_del_init(&info->swaplist);
spin_unlock(&shmem_swaplist_lock);
- }
+ } while (!list_empty(&info->swaplist));
}
simple_xattrs_free(&sbinfo->xa_cache, &info->xattrs, sbinfo->max_inodes ? &freed : NULL);
@@ -1779,15 +1795,14 @@ int shmem_unuse(unsigned int type)
* remove placeholder inode from swaplist, nor let it be freed
* (igrab() would protect from unlink, but not from unmount).
*/
- atomic_inc(&info->stop_eviction);
+ shmem_disallow_eviction(info);
spin_unlock(&shmem_swaplist_lock);
error = shmem_unuse_inode(&info->vfs_inode, type);
cond_resched();
spin_lock(&shmem_swaplist_lock);
- if (atomic_dec_and_test(&info->stop_eviction))
- wake_up_var(&info->stop_eviction);
+ shmem_allow_eviction(info);
if (error)
break;
if (list_empty(&info->swaplist))
@@ -1918,6 +1933,13 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
spin_unlock(&shmem_swaplist_lock);
}
+ /*
+ * Once the folio has left the page cache, its lock no longer
+ * keeps the inode from being evicted and freed: hold eviction
+ * off, in case the folio has to be put back below.
+ */
+ shmem_disallow_eviction(info);
+
folio_dup_swap(folio, NULL);
shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap));
@@ -1925,6 +1947,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
error = swap_writeout(ctx, folio);
if (error != AOP_WRITEPAGE_ACTIVATE) {
/* folio has been unlocked */
+ shmem_allow_eviction(info);
return error;
}
@@ -1941,6 +1964,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
shmem_recalc_inode(inode, 0, -nr_pages);
folio_put_swap(folio, NULL);
}
+ shmem_allow_eviction(info);
/*
* The swap_cache_del_folio() below could be left for
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 07/16] mm/shmem: avoid moving the truncation start backwards
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (5 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 06/16] mm/shmem: hold off eviction in shmem_writeout() David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 08/16] mm/shmem: stop retrying folios that cannot be split David Hildenbrand (Arm)
` (10 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Chris Mason, stable
From: Chris Mason <mason@kernel.org>
A nonuniform folio split can fail with -ENOMEM after splitting off some
smaller folios. shmem_undo_range() then sets start to the end of the
original folio, which may now lie before the truncation boundary.
The final scan can consequently discard data below the new EOF, or
keep retrying a large folio whose split point lies outside it.
Keep start at least at its original value when skipping a folio after
a failed split. The part in the truncated range has already been zeroed.
Fix generated by an LLM and sent by Chris Mason in private.
Fixes: 7460b470a131f ("mm/truncate: use folio_split() in truncate operation")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
[ david: split fixes, simplify descriptions, adjust comments ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index c551e33c697fe..2410ec46ee1d1 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1388,7 +1388,13 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
same_folio = lend < folio_next_pos(folio);
folio_mark_dirty(folio);
if (!truncate_inode_partial_folio(folio, lstart, lend)) {
- start = folio_next_index(folio);
+ /*
+ * Splitting failed, but the part in the range is
+ * already zeroed, so skip this folio. The failed split
+ * may have shrunk it; do not move start backwards and
+ * discard data before lstart.
+ */
+ start = max(start, folio_next_index(folio));
if (same_folio)
end = folio->index;
}
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 08/16] mm/shmem: stop retrying folios that cannot be split
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (6 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 07/16] mm/shmem: avoid moving the truncation start backwards David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 09/16] mm/shmem: preserve data in clean folios when splitting fails David Hildenbrand (Arm)
` (9 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Chris Mason, stable
From: Chris Mason <mason@kernel.org>
A fault or collapse can insert a large folio crossing EOF after
shmem_undo_range() has handled the boundary folios. If splitting that
folio fails, truncate_inode_partial_folio() retains it when it is dirty.
The final scan then repeatedly finds it because it restarts from an
unchanged start.
Truncation keeps the inode lock and ignores fatal signals while looping.
A concurrent unlink() can then block other directory operations while
holding the directory lock and waiting for the file's lock.
Advance start past the retained folio, whose truncated part has already
been zeroed. Do not move start backwards if the failed split has shrunk
the folio.
Fix generated by an LLM and sent by Chris Mason in private.
Fixes: 55ac8bbe358b ("mm/shmem: fix race in shmem_undo_range w/THP")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
[ david: split fixes, simplify descriptions, adjust comments ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/mm/shmem.c b/mm/shmem.c
index 2410ec46ee1d1..e2c3b04051833 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1490,6 +1490,14 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
index = start;
break;
}
+ } else {
+ /*
+ * Splitting failed, but the part in the
+ * range is already zeroed, so skip this
+ * folio when restarting the scan to
+ * avoid retrying it forever.
+ */
+ start = max(start, folio_next_index(folio));
}
}
folio_unlock(folio);
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 09/16] mm/shmem: preserve data in clean folios when splitting fails
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (7 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 08/16] mm/shmem: stop retrying folios that cannot be split David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 10/16] mm/shmem: discard whole new large folios on fallocate rollback David Hildenbrand (Arm)
` (8 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), Chris Mason, stable
From: Chris Mason <mason@kernel.org>
A shared writable mapping can have dirty PTEs while its shmem folio is
still clean. A read fault can install a writable PTE, and a later store
sets the PTE's dirty bit without marking the folio dirty.
After shmem_undo_range() has handled the boundary folios, a concurrent
read fault into a hole below the new EOF can allocate a large folio.
For example, on a tmpfs mount with huge=always, the fault's offset must
be within the file, but the allocated folio can extend beyond i_size.
The final scan then finds this folio and calls
truncate_inode_partial_folio() without marking it dirty.
Splitting normally collects the PTE dirty bits when unmapping the
folio. An extra reference, for example from vmsplice(), can make it
fail before that step. The fallback unmapping on split failure excludes
shmem, so the helper still sees a clean folio and drops it, losing data
outside the range.
Mark large folios dirty before partial truncation in the final scan,
as the boundary handling already does. Exclude failed-fallocate
rollback, which must discard the folios it added.
Fix generated by an LLM and sent by Chris Mason in private.
Fixes: b9a8a4195c7d ("truncate,shmem: Handle truncates that split large folios")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
[ david: simplify descriptions, adjust comments ]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/mm/shmem.c b/mm/shmem.c
index e2c3b04051833..938221c473f3f 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1474,6 +1474,15 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
VM_BUG_ON_FOLIO(folio_test_writeback(folio),
folio);
+ /*
+ * A shared mapping can have dirty PTEs while
+ * the folio is still clean. Mark the folio
+ * dirty so a failed split cannot discard data
+ * outside the range.
+ */
+ if (!unfalloc && folio_test_large(folio))
+ folio_mark_dirty(folio);
+
if (!folio_test_large(folio)) {
truncate_inode_folio(mapping, folio);
} else if (truncate_inode_partial_folio(folio, lstart, lend)) {
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 10/16] mm/shmem: discard whole new large folios on fallocate rollback
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (8 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 09/16] mm/shmem: preserve data in clean folios when splitting fails David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 11/16] mm/shmem: rescan after nonuniform folio splitting David Hildenbrand (Arm)
` (7 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), stable
When undoing a failed fallocate(), shmem_undo_range() must discard each
newly allocated folio. Its second scan instead passes large
folios to truncate_inode_partial_folio(), which does not necessarily
discard the entire large folio, but instead tries splitting it to
discard only the requested range.
For example, with huge=always, a failed allocation starting at offset
4 KiB can leave page 0 allocated in an otherwise empty file.
Discard the whole folio when unfalloc is set. Note that we enter this
path only for folios that are not uptodate, and can consequently be
dropped without losing any data.
Fixes: 55ac8bbe358b ("mm/shmem: fix race in shmem_undo_range w/THP")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index 938221c473f3f..c8608026e06ef 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1483,7 +1483,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
if (!unfalloc && folio_test_large(folio))
folio_mark_dirty(folio);
- if (!folio_test_large(folio)) {
+ if (unfalloc || !folio_test_large(folio)) {
truncate_inode_folio(mapping, folio);
} else if (truncate_inode_partial_folio(folio, lstart, lend)) {
/*
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 11/16] mm/shmem: rescan after nonuniform folio splitting
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (9 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 10/16] mm/shmem: discard whole new large folios on fallocate rollback David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 12/16] mm/shmem: fix truncate hang in large swap entry David Hildenbrand (Arm)
` (6 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), stable
The final scan in shmem_undo_range() restarts after a successful split
only if the original folio is no longer large. A nonuniform split can
leave that folio large, so the scan skips the newly created folios.
Hole punching then leaves pages within the hole allocated even though
they could be discarded.
For example, a fault below the hole can allocate a 2 MiB folio after
boundary handling. Punching a 1 MiB hole at offset 64 KiB splits the
folio successfully but leaves the original folio at order 4. The scan
continues past all 512 pages, leaving 2 MiB allocated instead of 1 MiB.
The hole reads as zero, but the pages and their block charges remain.
Restart when the original folio is still in the mapping after partial
truncation, identified by checking folio->mapping. If the helper
removed the whole folio, no rescan is needed.
Fixes: 7460b470a131f ("mm/truncate: use folio_split() in truncate operation")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index c8608026e06ef..fcec40a2dd707 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1487,14 +1487,11 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
truncate_inode_folio(mapping, folio);
} else if (truncate_inode_partial_folio(folio, lstart, lend)) {
/*
- * If we split a page, reset the loop so
- * that we pick up the new sub pages.
- * Otherwise the THP was entirely
- * dropped or the target range was
- * zeroed, so just continue the loop as
- * is.
+ * If the folio was not removed, it
+ * was split. Retry the scan to find
+ * the split folios.
*/
- if (!folio_test_large(folio)) {
+ if (folio->mapping) {
folio_unlock(folio);
index = start;
break;
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 12/16] mm/shmem: fix truncate hang in large swap entry
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (10 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 11/16] mm/shmem: rescan after nonuniform folio splitting David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 13/16] mm/shmem: preserve allocation errors when swapping in large entries David Hildenbrand (Arm)
` (5 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), stable
Truncating a tmpfs file or memfd to a page-aligned size inside a large
swap entry can loop indefinitely in shmem_undo_range(). The task holds
the inode lock and cannot be killed.
A large swap entry represents a whole swapped-out folio. For example,
truncate a file whose pages 0-7 share one swap entry to four pages:
[ 0 1 2 3 | 4 5 6 7 ]
keep | discard
^
new EOF, start = 4
i_size has already been updated when boundary handling asks
shmem_get_partial_folio() for page 4. Since it is swapped out, the helper
calls shmem_get_folio() with SGP_READ. That rejects page 4 with -EINVAL
because its starting offset equals i_size. Nothing is read back.
The final scan cannot free the entry without losing pages 0-3. It skips
it, restarts from start = 4, and finds it again indefinitely. An unaligned
truncate avoids this particular failure because the requested page starts
below i_size. Folios already in the page-cache cannot trigger it as the
direct lookup will return them beyond EOF.
Fix it by letting shmem_get_partial_folio() call shmem_swapin_folio()
directly for swap entries. This bypasses the read path's i_size check,
allowing the existing boundary handling to zero and split the folio. If
swap-in returns a smaller folio, it also splits the swap entry so the final
scan can free the entries beyond EOF. Retry the lookup if the entry
changes during swap-in.
Handling swap-in allocation failures remains broken and is unchanged by
this commit. Even when shmem_swapin_folio() correctly returns -ENOMEM
instead of -EEXIST, shmem_get_partial_folio() returns NULL and
shmem_undo_range() skips boundary handling. If the large swap entry
remains intact, truncation can still loop in the final scan, while hole
punching can return without zeroing the boundary.
This may read an entire large folio back from swap while holding
i_rwsem, only to discard part of it. Avoiding that I/O would require
splitting both the swap entry and any large folio still in the swap
cache. That, however, is much bigger work.
Original fix generated by LLM and sent by Chris Mason in private.
Fixes: 8a1968bd997f ("mm/shmem, swap: fix race of truncate and swap entry split")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index fcec40a2dd707..9e0034a424908 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1298,12 +1298,10 @@ void shmem_unlock_mapping(struct address_space *mapping)
static struct folio *shmem_get_partial_folio(struct inode *inode, pgoff_t index)
{
+ const gfp_t gfp = mapping_gfp_mask(inode->i_mapping);
struct folio *folio;
- /*
- * At first avoid shmem_get_folio(,,,SGP_READ): that fails
- * beyond i_size, and reports fallocated folios as holes.
- */
+retry:
folio = filemap_get_entry(inode->i_mapping, index);
if (!folio)
return folio;
@@ -1314,13 +1312,14 @@ static struct folio *shmem_get_partial_folio(struct inode *inode, pgoff_t index)
/* The folio has been swapped out */
folio_unlock(folio);
folio_put(folio);
+ goto retry;
}
/*
- * But read a folio back from swap if any of it is within i_size
- * (although in some cases this is just a waste of time).
+ * Truncation can look up a large swap entry at or beyond i_size.
+ * Swap it in directly to bypass the size check in shmem_get_folio().
*/
- folio = NULL;
- shmem_get_folio(inode, index, 0, &folio, SGP_READ);
+ if (shmem_swapin_folio(inode, index, &folio, SGP_READ, gfp, NULL, NULL) == -EEXIST)
+ goto retry;
return folio;
}
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 13/16] mm/shmem: preserve allocation errors when swapping in large entries
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (11 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 12/16] mm/shmem: fix truncate hang in large swap entry David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 14/16] mm/shmem: fix truncate loop after boundary swapin failure David Hildenbrand (Arm)
` (4 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), stable
shmem_swapin_folio() adjusts swap to the requested slot within a large
swap entry before allocating a folio. If allocation or charging fails,
the error path compares this adjusted value with the original entry
still stored in the XArray in shmem_confirm_swap().
Unless the index is at the start of the entry, this turns -ENOMEM into
-EEXIST even though nothing changed.
The caller retries -EEXIST, so persistent allocation failures can leave
the task spinning instead of returning the error.
Restore swap to index_entry on allocation failure so the existing error
path checks the original entry. This preserves the allocation error if
the entry is unchanged and still retries if it changed concurrently.
Original fix generated by an LLM and sent by Josef Bacik in private.
Fixes: 91ab656ece13 ("mm/shmem, swap: tidy up swap entry splitting")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/mm/shmem.c b/mm/shmem.c
index 9e0034a424908..d1716a8da9075 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2563,6 +2563,8 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
else
error = -ENOMEM;
folio = NULL;
+ /* Make sure to validate the original value. */
+ swap = index_entry;
goto failed;
}
if (fault_type) {
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 14/16] mm/shmem: fix truncate loop after boundary swapin failure
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (12 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 13/16] mm/shmem: preserve allocation errors when swapping in large entries David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 15/16] mm/shmem: fix accounting of swapin-error markers David Hildenbrand (Arm)
` (3 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), stable
If swapping in a large entry at the new EOF fails, shmem_undo_range()
leaves the entry in place. Its final scan skips the entry, then restarts
from the same start index and finds it again. Truncation can loop forever
while holding i_rwsem.
Advance start past the skipped entry so that a restarted scan can finish,
as we already do for folios that cannot be split.
Note that this does not fix the missing zeroing when boundary swapin fails,
it merely prevents the endless loop.
Fixes: 8a1968bd997f ("mm/shmem, swap: fix race of truncate and swap entry split")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index d1716a8da9075..6b24b9d6db26e 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1444,14 +1444,16 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
order = shmem_confirm_swap(mapping, indices[i],
radix_to_swp_entry(folio));
/*
- * If found a large swap entry cross the end or start
- * border, skip it as the truncate_inode_partial_folio
- * above should have at least zerod its content once.
+ * Skip boundary entries when restarting
+ * the scan to avoid endless loops after
+ * swapin failure.
*/
if (order > 0) {
base = round_down(base, 1 << order);
- if (base < start || base + (1 << order) > end)
+ if (base < start || base + (1 << order) > end) {
+ start = max(start, base + (1 << order));
continue;
+ }
}
/* Swap was replaced by page or extended, retry */
index = base;
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 15/16] mm/shmem: fix accounting of swapin-error markers
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (13 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 14/16] mm/shmem: fix truncate loop after boundary swapin failure David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 16/16] mm/shmem: fix truncation of files containing only " David Hildenbrand (Arm)
` (2 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), stable
A failed swap read replaces the swap entry with a swapin-error marker in
the page-cache and subtracts its pages from "alloced" and "swapped";
however, it leaves their block charge in place.
Removing the marker during truncate then subtracts from "swapped" again.
This can underflow the counters or make swapped reach zero while other
pages remain swapped out, causing swapoff to loop without finding them.
When installing the marker, subtract only from "swapped" and let
shmem_recalc_inode() reduce "alloced" and release the block charge.
Make shmem_free_swap() report successful removal separately from the
number of swap pages freed, so removing a swapin-error marker does not
subtract from "swapped" again.
It's a bit confusing, but replacing a swapped out page by a swapin-error
marker in shmem_set_folio_swapin_error() behaves as follows:
Initially:
alloced=1, swapped=1, nrpages=0
Calling shmem_recalc_inode(inode, 0, -1)
-> swapped = 0
-> freed = 1 - 0 - 0 = 1
-> alloced -= freed
-> alloced=0, swapped=0, block charge released
Fixes: 6cec2b95dadf ("mm/shmem: fix infinite loop when swap in shmem error at swapoff time")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 31 ++++++++++++++-----------------
1 file changed, 14 insertions(+), 17 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index 6b24b9d6db26e..f5b5c9a312c53 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1177,12 +1177,11 @@ static void shmem_delete_from_page_cache(struct folio *folio, void *radswap)
}
/*
- * Remove swap entry from page cache, free the swap and its page cache. Returns
- * the number of pages being freed. 0 means entry not found in XArray (0 pages
- * being freed).
+ * Remove a swap entry or swapin-error marker wholly within [index, end].
+ * Return whether it was removed, counting freed swap pages in nr_swaps_freed.
*/
-static long shmem_free_swap(struct address_space *mapping,
- pgoff_t index, pgoff_t end, void *radswap)
+static bool shmem_free_swap(struct address_space *mapping,
+ pgoff_t index, pgoff_t end, void *radswap, long *nr_swaps_freed)
{
XA_STATE(xas, &mapping->i_pages, index);
const softleaf_t swp = radix_to_swp_entry(radswap);
@@ -1203,8 +1202,10 @@ static long shmem_free_swap(struct address_space *mapping,
xas_unlock_irq(&xas);
/* A swapin-error marker holds no swap slot, so just drop it. */
- if (nr_pages && softleaf_is_swap(swp))
+ if (nr_pages && softleaf_is_swap(swp)) {
swap_put_entries_direct(swp, nr_pages);
+ *nr_swaps_freed += nr_pages;
+ }
return nr_pages;
}
@@ -1358,8 +1359,8 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
if (xa_is_value(folio)) {
if (unfalloc)
continue;
- nr_swaps_freed += shmem_free_swap(mapping, indices[i],
- end - 1, folio);
+ shmem_free_swap(mapping, indices[i], end - 1,
+ folio, &nr_swaps_freed);
continue;
}
@@ -1432,13 +1433,11 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
if (xa_is_value(folio)) {
int order;
- long swaps_freed;
if (unfalloc)
continue;
- swaps_freed = shmem_free_swap(mapping, indices[i],
- end - 1, folio);
- if (!swaps_freed) {
+ if (!shmem_free_swap(mapping, indices[i], end - 1,
+ folio, &nr_swaps_freed)) {
pgoff_t base = indices[i];
order = shmem_confirm_swap(mapping, indices[i],
@@ -1459,7 +1458,6 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
index = base;
break;
}
- nr_swaps_freed += swaps_freed;
continue;
}
@@ -2420,11 +2418,10 @@ static void shmem_set_folio_swapin_error(struct inode *inode, pgoff_t index,
folio_put_swap(folio, NULL);
swap_cache_del_folio(folio);
/*
- * Don't treat swapin error folio as alloced. Otherwise inode->i_blocks
- * won't be 0 when inode is released and thus trigger WARN_ON(i_blocks)
- * in shmem_evict_inode().
+ * Let shmem_recalc_inode() reduce alloced and release the block charge
+ * for the freed swap pages.
*/
- shmem_recalc_inode(inode, -nr_pages, -nr_pages);
+ shmem_recalc_inode(inode, 0, -nr_pages);
}
static int shmem_split_large_entry(struct inode *inode, pgoff_t index,
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 16/16] mm/shmem: fix truncation of files containing only swapin-error markers
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (14 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 15/16] mm/shmem: fix accounting of swapin-error markers David Hildenbrand (Arm)
@ 2026-10-26 9:40 ` David Hildenbrand (Arm)
2026-10-26 11:11 ` [PATCH 00/16] mm/shmem: various (urgent?) fixes Baolin Wang
2026-10-26 13:23 ` Kairui Song
17 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 9:40 UTC (permalink / raw)
To: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, David Hildenbrand (Arm), stable
It might not be very common, but we can end up with shmem files that are
composed only of swapin-error/poison markers.
Such markers are not counted in info->alloced. Consequently,
shmem_setattr() can skip shmem_truncate_range(), leaving the markers
behind.
Truncating the file to zero and growing it again will result in reads
failing with -EIO instead of returning zeroes.
Check whether the mapping contains entries, in addition to
info->alloced, before skipping the truncation. Keep the info->alloced
check so that shmem_recalc_inode(), called at the end of
shmem_undo_range(), can fix up accounting after reclaim has emptied the
mapping.
Fixes: 6cec2b95dadf ("mm/shmem: fix infinite loop when swap in shmem error at swapoff time")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index f5b5c9a312c53..45f6c34f161e4 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1602,7 +1602,8 @@ static int shmem_setattr(const struct mnt_idmap *idmap,
if (oldsize > holebegin)
unmap_mapping_range(inode->i_mapping,
holebegin, 0, 1);
- if (info->alloced)
+ /* Swapin-error markers are not counted in alloced. */
+ if (info->alloced || !mapping_empty(inode->i_mapping))
shmem_truncate_range(inode,
newsize, (loff_t)-1);
/* unmap again to remove racily COWed private pages */
--
2.43.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 02/16] tmpfs: keep the quota soft limits of an unused id
2026-10-26 9:40 ` [PATCH 02/16] tmpfs: keep the quota soft limits of an unused id David Hildenbrand (Arm)
@ 2026-10-26 10:53 ` Jan Kara
2026-10-26 11:00 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 30+ messages in thread
From: Jan Kara @ 2026-10-26 10:53 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Josef Bacik, stable
On Mon 26-10-26 10:40:35, David Hildenbrand (Arm) wrote:
> From: Josef Bacik <josef@toxicpanda.com>
>
> When the last reference to a dquot goes away, shmem_release_dquot()
> stores its limits in the rb tree of the mount, unless
> shmem_is_empty_dquot() says that there is nothing worth storing. That
> function looks at the usage and at the two hard limits only.
>
> So if the administrator sets nothing but soft limits for an id that owns
> no file yet, the entry is thrown away as soon as the dquot that
> Q_SETQUOTA used is released, and the id never gets to see a limit.
>
> To fix it, compare the soft limits as well: an entry can be empty only if
> both soft limits are zero as well, which is exactly what
> shmem_acquire_dquot() sets for a new entry.
>
> Fix generated by an LLM and sent by Josef Bacik in private.
>
> Fixes: de4c0e7ca8b5 ("shmem: Add default quota limit mount options")
> Cc: stable@vger.kernel.org
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> [ david: slightly adjusted description ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Good spotting although I consider this pretty minor (unlikely setup proven
by the fact nobody complained so far). Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> mm/shmem_quota.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/mm/shmem_quota.c b/mm/shmem_quota.c
> index a3fa4bd93e753..43d3b4fd6cb1b 100644
> --- a/mm/shmem_quota.c
> +++ b/mm/shmem_quota.c
> @@ -256,6 +256,8 @@ static bool shmem_is_empty_dquot(struct dquot *dquot)
> if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
> (dquot->dq_dqb.dqb_curspace == 0 &&
> dquot->dq_dqb.dqb_curinodes == 0 &&
> + dquot->dq_dqb.dqb_bsoftlimit == 0 &&
> + dquot->dq_dqb.dqb_isoftlimit == 0 &&
> dquot->dq_dqb.dqb_bhardlimit == bhardlimit &&
> dquot->dq_dqb.dqb_ihardlimit == ihardlimit))
> return true;
>
> --
> 2.43.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 02/16] tmpfs: keep the quota soft limits of an unused id
2026-10-26 10:53 ` Jan Kara
@ 2026-10-26 11:00 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 11:00 UTC (permalink / raw)
To: Jan Kara
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Daniel Verkamp, Jeff Xu, Muchun Song,
Oscar Salvador, David Stevens, Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Josef Bacik, stable
On 10/26/26 11:53, Jan Kara wrote:
> On Mon 26-10-26 10:40:35, David Hildenbrand (Arm) wrote:
>> From: Josef Bacik <josef@toxicpanda.com>
>>
>> When the last reference to a dquot goes away, shmem_release_dquot()
>> stores its limits in the rb tree of the mount, unless
>> shmem_is_empty_dquot() says that there is nothing worth storing. That
>> function looks at the usage and at the two hard limits only.
>>
>> So if the administrator sets nothing but soft limits for an id that owns
>> no file yet, the entry is thrown away as soon as the dquot that
>> Q_SETQUOTA used is released, and the id never gets to see a limit.
>>
>> To fix it, compare the soft limits as well: an entry can be empty only if
>> both soft limits are zero as well, which is exactly what
>> shmem_acquire_dquot() sets for a new entry.
>>
>> Fix generated by an LLM and sent by Josef Bacik in private.
>>
>> Fixes: de4c0e7ca8b5 ("shmem: Add default quota limit mount options")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
>> [ david: slightly adjusted description ]
>> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
>
> Good spotting although I consider this pretty minor (unlikely setup proven
> by the fact nobody complained so far). Feel free to add:
Yeah, similar to #3 really minor.
Thanks!
--
Cheers,
David
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 00/16] mm/shmem: various (urgent?) fixes
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (15 preceding siblings ...)
2026-10-26 9:40 ` [PATCH 16/16] mm/shmem: fix truncation of files containing only " David Hildenbrand (Arm)
@ 2026-10-26 11:11 ` Baolin Wang
2026-10-26 12:12 ` David Hildenbrand (Arm)
2026-10-26 13:23 ` Kairui Song
17 siblings, 1 reply; 30+ messages in thread
From: Baolin Wang @ 2026-10-26 11:11 UTC (permalink / raw)
To: David Hildenbrand (Arm),
Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Lukas Czerner, Carlos Maiolino, Christian Brauner,
Jan Kara, Daniel Verkamp, Jeff Xu, Muchun Song, Oscar Salvador,
David Stevens, Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, Chris Mason, stable, Josef Bacik, Eva Kurchatova
On 10/26/26 5:40 PM, David Hildenbrand (Arm) wrote:
> Hi,
>
> the fixing fun continues.
>
> This is a handful of fixes for shmem/tmpfs. I sneaked in a related
> hugetlb fix that stalled upstream.
Wow, thanks David!
> Some of the stuff is more urgent (patch #1, F_SEAL_EXEC, truncation
> hangs), others are less urgent but still rather unpleasant.
>
> The majority are fixes from Anthropic that I cleaned up or reworked.
> While doing that, I found a bunch of other issues and tried to fix them
> somewhat cleanly.
>
> In general, shmem + swap handling is a nightmare. shmem_undo_range() is
> such utter garbage it makes me want to cry. I mean, really, WTF did we
> end up creating there. Does any expert here have time to clean that up and
> make it *actually* maintainable.
Agree. :) I'll re-read shmem_undo_range() and take a close look at the
issues you raised, and see if I can make shmem_undo_range() more readable.
> The following things are still broken in shmem_undo_range():
>
> (1) Truncate/punch of a single page of a large swap-error marker
>
> We don't split. Consequently, read access afterwards will still fail
> instead of returning 0s.
>
> I have a fix to try to split (which can fail), but I don't consider it
> that urgent, really. So I'm not including it. Might send it later
> separately.
>
> Truncate/punch of a partial page can obviously never do the right thing,
> as we cannot poison parts of pages ...
>
> (2) Truncate/punch whole pages within part of a large swap entry
>
> We try to read the entry, but if we are out of memory that might fail.
> Consequently, we will not free up swap space and a re-read will not give
> us 0s but the previous content.
>
> AI proposed a nasty NOFAIL patch which has too many nasty side effects
> IMHO (allowing users to force-read pages and retain them afterwards even
> though memory.max would disallow it). This needs a proper thought. Likely,
> support for splitting large swap entries (and large folios in the
> swapcache, which I recall is under development by Kairui). But splitting
> can fail ...
>
> (3) Truncate/punch of a part of a base-page swap entry
>
> We obviously have to read the page in, in order to 0-out the parts to
> discard. That might obviously fail.
>
> Maybe we should install swapin-error markers if swapping during
> truncate/punchhole fails, to at least tell user space on later access
> that something went wrong? We might affect unrelated data, outside the
> requested range, though. Anyhow, these are long standing issues and
> I don't consider them urgent.
>
> They need a proper solution.
>
> I did my best trying to test these fixes. I was able to reproduce some
> of the things (including unkillable truncation hangs), and the fixes seemed
> to help.
>
> But again, shmem_undo_range() is just an absolute mess.
>
> Please prioritize review of these fixes, I want to get fixes for the
> issues in a timely manner upstream.
OK. Will review and test this serires this week.
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
> ---
> Chris Mason (5):
> mm/mempolicy: charge shared policy nodes
> mm/shmem: honour F_SEAL_EXEC when an ACL is set
> mm/shmem: avoid moving the truncation start backwards
> mm/shmem: stop retrying folios that cannot be split
> mm/shmem: preserve data in clean folios when splitting fails
>
> David Hildenbrand (Arm) (7):
> mm/shmem: discard whole new large folios on fallocate rollback
> mm/shmem: rescan after nonuniform folio splitting
> mm/shmem: fix truncate hang in large swap entry
> mm/shmem: preserve allocation errors when swapping in large entries
> mm/shmem: fix truncate loop after boundary swapin failure
> mm/shmem: fix accounting of swapin-error markers
> mm/shmem: fix truncation of files containing only swapin-error markers
>
> Eva Kurchatova (1):
> hugetlbfs: enforce F_SEAL_EXEC in setattr
>
> Josef Bacik (3):
> tmpfs: keep the quota soft limits of an unused id
> tmpfs: preserve explicitly disabled quota limits
> mm/shmem: hold off eviction in shmem_writeout()
>
> fs/hugetlbfs/inode.c | 8 +++
> include/linux/memfd.h | 9 +++
> mm/mempolicy.c | 4 +-
> mm/shmem.c | 170 +++++++++++++++++++++++++++++++++++---------------
> mm/shmem_quota.c | 28 +++++----
> 5 files changed, 154 insertions(+), 65 deletions(-)
>
> ---
>
> base-commit: 21a7f44fd0765fcc5194130b28767786eba1d1e7
>
> change-id: 20261025-fixes_shmem-2782719b130e
>
> --
>
> Cheers,
>
> David
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 00/16] mm/shmem: various (urgent?) fixes
2026-10-26 11:11 ` [PATCH 00/16] mm/shmem: various (urgent?) fixes Baolin Wang
@ 2026-10-26 12:12 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 30+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-26 12:12 UTC (permalink / raw)
To: Baolin Wang, Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn,
Rakie Kim, Ying Huang, Johannes Weiner, Vladimir Davydov,
Michal Hocko, Hugh Dickins, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi
Cc: linux-mm, linux-kernel, Chris Mason, stable, Josef Bacik, Eva Kurchatova
On 10/26/26 12:11, Baolin Wang wrote:
>
>
> On 10/26/26 5:40 PM, David Hildenbrand (Arm) wrote:
>> Hi,
>>
>> the fixing fun continues.
>>
>> This is a handful of fixes for shmem/tmpfs. I sneaked in a related
>> hugetlb fix that stalled upstream.
>
> Wow, thanks David!
>
>> Some of the stuff is more urgent (patch #1, F_SEAL_EXEC, truncation
>> hangs), others are less urgent but still rather unpleasant.
>>
>> The majority are fixes from Anthropic that I cleaned up or reworked.
>> While doing that, I found a bunch of other issues and tried to fix them
>> somewhat cleanly.
>>
>> In general, shmem + swap handling is a nightmare. shmem_undo_range() is
>> such utter garbage it makes me want to cry. I mean, really, WTF did we
>> end up creating there. Does any expert here have time to clean that up and
>> make it *actually* maintainable.
>
> Agree. :) I'll re-read shmem_undo_range() and take a close look at the issues
> you raised, and see if I can make shmem_undo_range() more readable.
>
>> The following things are still broken in shmem_undo_range():
>>
>> (1) Truncate/punch of a single page of a large swap-error marker
>>
>> We don't split. Consequently, read access afterwards will still fail
>> instead of returning 0s.
>>
>> I have a fix to try to split (which can fail), but I don't consider it
>> that urgent, really. So I'm not including it. Might send it later
>> separately.
>>
>> Truncate/punch of a partial page can obviously never do the right thing,
>> as we cannot poison parts of pages ...
>>
>> (2) Truncate/punch whole pages within part of a large swap entry
>>
>> We try to read the entry, but if we are out of memory that might fail.
>> Consequently, we will not free up swap space and a re-read will not give
>> us 0s but the previous content.
>>
>> AI proposed a nasty NOFAIL patch which has too many nasty side effects
>> IMHO (allowing users to force-read pages and retain them afterwards even
>> though memory.max would disallow it). This needs a proper thought. Likely,
>> support for splitting large swap entries (and large folios in the
>> swapcache, which I recall is under development by Kairui). But splitting
>> can fail ...
>>
>> (3) Truncate/punch of a part of a base-page swap entry
>>
>> We obviously have to read the page in, in order to 0-out the parts to
>> discard. That might obviously fail.
>>
>> Maybe we should install swapin-error markers if swapping during
>> truncate/punchhole fails, to at least tell user space on later access
>> that something went wrong? We might affect unrelated data, outside the
>> requested range, though. Anyhow, these are long standing issues and
>> I don't consider them urgent.
>>
>> They need a proper solution.
>>
>> I did my best trying to test these fixes. I was able to reproduce some
>> of the things (including unkillable truncation hangs), and the fixes seemed
>> to help.
>>
>> But again, shmem_undo_range() is just an absolute mess.
>>
>> Please prioritize review of these fixes, I want to get fixes for the
>> issues in a timely manner upstream.
>
> OK. Will review and test this serires this week.
Thanks!
If there are better simple fixes, please do suggest them. Any bigger cleanups
ideally come on top.
BTW, the next time I get a flood of these patches, would you be willing to do
the early screening+revise? But be careful, some of them are pretty, pretty awful :)
--
Cheers,
David
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 00/16] mm/shmem: various (urgent?) fixes
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
` (16 preceding siblings ...)
2026-10-26 11:11 ` [PATCH 00/16] mm/shmem: various (urgent?) fixes Baolin Wang
@ 2026-10-26 13:23 ` Kairui Song
17 siblings, 0 replies; 30+ messages in thread
From: Kairui Song @ 2026-10-26 13:23 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Baolin Wang, Gregory Price, Zi Yan, Joshua Hahn,
Rakie Kim, Ying Huang, Johannes Weiner, Vladimir Davydov,
Michal Hocko, Hugh Dickins, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Chris Mason, stable,
Josef Bacik, Eva Kurchatova
On Mon, Oct 26, 2026 at 5:44 PM David Hildenbrand (Arm)
<david@kernel.org> wrote:
>
> Hi,
>
> the fixing fun continues.
>
> This is a handful of fixes for shmem/tmpfs. I sneaked in a related
> hugetlb fix that stalled upstream.
>
> Some of the stuff is more urgent (patch #1, F_SEAL_EXEC, truncation
> hangs), others are less urgent but still rather unpleasant.
>
> The majority are fixes from Anthropic that I cleaned up or reworked.
> While doing that, I found a bunch of other issues and tried to fix them
> somewhat cleanly.
>
> In general, shmem + swap handling is a nightmare. shmem_undo_range() is
> such utter garbage it makes me want to cry. I mean, really, WTF did we
> end up creating there. Does any expert here have time to clean that up and
> make it *actually* maintainable.
I had a nightmare understanding what is going on there when I was
trying to fix a few swap related bug... :P, and hesitated to clean it
up... I think we will have a cleaner codebase on this part soon as
Baolin is working on this.
> The following things are still broken in shmem_undo_range():
>
> (1) Truncate/punch of a single page of a large swap-error marker
>
> We don't split. Consequently, read access afterwards will still fail
> instead of returning 0s.
>
> I have a fix to try to split (which can fail), but I don't consider it
> that urgent, really. So I'm not including it. Might send it later
> separately.
>
> Truncate/punch of a partial page can obviously never do the right thing,
> as we cannot poison parts of pages ...
>
> (2) Truncate/punch whole pages within part of a large swap entry
>
> We try to read the entry, but if we are out of memory that might fail.
> Consequently, we will not free up swap space and a re-read will not give
> us 0s but the previous content.
>
> AI proposed a nasty NOFAIL patch which has too many nasty side effects
> IMHO (allowing users to force-read pages and retain them afterwards even
> though memory.max would disallow it). This needs a proper thought. Likely,
> support for splitting large swap entries (and large folios in the
> swapcache, which I recall is under development by Kairui). But splitting
> can fail ...
Right, will post something on that swap cache splitting part soon,
what I found tricky afterward is lazy reclaim of swap cache so I think
a bit further clean up of THP splitting is needed first for better
swap cache splitting.
For shmem side, we don't have to swap in at all when truncating
page-sized holes for large swap entry. There is nothing complex from
swap side, just lock the cluster, then put/free these sub-entries, and
everything is good. It has to do a swapin for shmem right now just to
make the shmem part easier to reuse the folio entry split code and
better synchronize, IIUC. Nothing is blocking this except that we
better cleanup for the large shmem entry splitting code too?
> (3) Truncate/punch of a part of a base-page swap entry
>
> We obviously have to read the page in, in order to 0-out the parts to
> discard. That might obviously fail.
>
> Maybe we should install swapin-error markers if swapping during
> truncate/punchhole fails, to at least tell user space on later access
> that something went wrong? We might affect unrelated data, outside the
> requested range, though. Anyhow, these are long standing issues and
> I don't consider them urgent.
>
> They need a proper solution.
This part is tricky though... perhaps swap side needs to provide some
helpers to trim sub-page sized content on the backend (not sure). If
that's too complex maybe limiting the trim window to just 1 page a
time won't be too ugly?
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 03/16] tmpfs: preserve explicitly disabled quota limits
2026-10-26 9:40 ` [PATCH 03/16] tmpfs: preserve explicitly disabled quota limits David Hildenbrand (Arm)
@ 2026-10-26 13:37 ` Jan Kara
0 siblings, 0 replies; 30+ messages in thread
From: Jan Kara @ 2026-10-26 13:37 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Josef Bacik, stable
On Mon 26-10-26 10:40:36, David Hildenbrand (Arm) wrote:
> From: Josef Bacik <josef@toxicpanda.com>
>
> An administrator’s decision to disable a user’s or group’s quota
> limits can be silently lost when tmpfs restores the mount defaults
> after releasing and reacquiring the quota record. This can cause
> later file creation or writes to fail with EDQUOT, even though the
> administrator has explicitly disabled quota limits for the user or
> group.
>
> do_set_dqblk() sets DQ_FAKE_B when all block and inode limits are zero.
> This means quota enforcement is disabled for that user or group.
> shmem_is_empty_dquot() treats the flag as a reason to discard the entry
> from the mount's quota tree. The next shmem_acquire_dquot() finds no
> saved entry and recreates it with the mount's default hard limits.
>
> If the cached dquot is reused, DQ_FAKE_B remains set: Q_GETQUOTA reports
> the restored defaults, but enforcement is still disabled. Once that
> dquot has been reclaimed, a new dquot enforces the restored defaults.
>
> Remove the DQ_FAKE_B test. Only discard entries with zero usage, zero
> soft limits and hard limits equal to the mount defaults. Explicitly
> disabled limits are then preserved whenever the mount defaults are
> nonzero.
>
> Explicitly disabled limits now retain a quota-tree entry, just like other
> limits that differ from the mount defaults.
>
> Rename shmem_is_empty_dquot() to shmem_is_default_dquot() to describe
> the check for default values.
>
> Fix generated by an LLM and sent by Josef Bacik in private.
>
> Fixes: de4c0e7ca8b5 ("shmem: Add default quota limit mount options")
> Cc: stable@vger.kernel.org
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> [ david: code cleanups; rewrite description ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Good catch. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> mm/shmem_quota.c | 30 +++++++++++++++---------------
> 1 file changed, 15 insertions(+), 15 deletions(-)
>
> diff --git a/mm/shmem_quota.c b/mm/shmem_quota.c
> index 43d3b4fd6cb1b..d191ebda3f00e 100644
> --- a/mm/shmem_quota.c
> +++ b/mm/shmem_quota.c
> @@ -239,7 +239,7 @@ static int shmem_acquire_dquot(struct dquot *dquot)
> return ret;
> }
>
> -static bool shmem_is_empty_dquot(struct dquot *dquot)
> +static bool shmem_is_default_dquot(struct dquot *dquot)
> {
> struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;
> qsize_t bhardlimit;
> @@ -253,21 +253,21 @@ static bool shmem_is_empty_dquot(struct dquot *dquot)
> ihardlimit = sbinfo->qlimits.grpquota_ihardlimit;
> }
>
> - if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
> - (dquot->dq_dqb.dqb_curspace == 0 &&
> - dquot->dq_dqb.dqb_curinodes == 0 &&
> - dquot->dq_dqb.dqb_bsoftlimit == 0 &&
> - dquot->dq_dqb.dqb_isoftlimit == 0 &&
> - dquot->dq_dqb.dqb_bhardlimit == bhardlimit &&
> - dquot->dq_dqb.dqb_ihardlimit == ihardlimit))
> - return true;
> -
> - return false;
> + /*
> + * Defaults are zero usage and soft limits, and the mount's hard limits.
> + * DQ_FAKE_B only means that *all* limits are zero, which need not match
> + * the mount defaults.
> + */
> + return dquot->dq_dqb.dqb_curspace == 0 &&
> + dquot->dq_dqb.dqb_curinodes == 0 &&
> + dquot->dq_dqb.dqb_bsoftlimit == 0 &&
> + dquot->dq_dqb.dqb_isoftlimit == 0 &&
> + dquot->dq_dqb.dqb_bhardlimit == bhardlimit &&
> + dquot->dq_dqb.dqb_ihardlimit == ihardlimit;
> }
> /*
> - * Store limits from dquot in the tree unless it's fake. If it is fake
> - * remove the id from the tree since there is no useful information in
> - * there.
> + * Remove the ID from the tree if it has no usage and its limits match
> + * the mount defaults. Otherwise, save its limits for the next acquire.
> */
> static int shmem_release_dquot(struct dquot *dquot)
> {
> @@ -302,7 +302,7 @@ static int shmem_release_dquot(struct dquot *dquot)
> return -ENOENT;
>
> found:
> - if (shmem_is_empty_dquot(dquot)) {
> + if (shmem_is_default_dquot(dquot)) {
> /* Remove entry from the tree */
> rb_erase(&entry->node, info->dqi_priv);
> kfree(entry);
>
> --
> 2.43.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 01/16] mm/mempolicy: charge shared policy nodes
2026-10-26 9:40 ` [PATCH 01/16] mm/mempolicy: charge shared policy nodes David Hildenbrand (Arm)
@ 2026-10-26 13:38 ` Pedro Falcato
2026-10-26 15:44 ` Kiryl Shutsemau
1 sibling, 0 replies; 30+ messages in thread
From: Pedro Falcato @ 2026-10-26 13:38 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Chris Mason, stable
On Mon, Oct 26, 2026 at 10:40:34AM +0100, David Hildenbrand (Arm) wrote:
> From: Chris Mason <mason@kernel.org>
>
> Any local user can bypass memory.max with mbind() on a tmpfs file, and
> allocate unbounded kernel memory.
>
> mbind() sets a NUMA memory policy for a range of a mapping. If the
> mapping is of a shmem and friends, the kernel also stores the policy on
> the inode, in a tree with one entry per range of the file.
>
> Each mbind() on a single page of a mapping can add one shared policy node
> (struct sp_node) and one struct mempolicy to that tree. They outlive the
> mapping and stay around until the inode is destroyed. The tree is not
> bounded by the file size, so the file can be empty.
>
> Memory that user space can allocate must be charged to a memory cgroup. A
> slab cache does that for all of its objects if it is created with
> SLAB_ACCOUNT. numa_policy_init() creates the slab caches for both
> structs without SLAB_ACCOUNT, so they are not charged to any cgroup.
>
> Reproduced with mbind() on single pages of an empty tmpfs file, from a
> cgroup with memory.max=8M: the global OOM killer killed tasks owned by
> root, and the machine panicked.
>
> Fix this by creating both caches with SLAB_ACCOUNT. This change implies
> that mbind() can now fail when running into memcg limits. Further,
> the policy_cache also serves task and VMA policies, so they are
> similarly accounted now.
>
> Note that per-file limits are not really helpful, as the user could
> simply use many files; having limits per user is tricky as well.
>
> Fix generated by an LLM and submitted by Chris Mason in private.
>
> Fixes: 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chris Mason <mason@kernel.org>
> [ david: smaller description tweaks ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set
2026-10-26 9:40 ` [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set David Hildenbrand (Arm)
@ 2026-10-26 13:48 ` Pedro Falcato
2026-10-26 15:52 ` Kiryl Shutsemau
1 sibling, 0 replies; 30+ messages in thread
From: Pedro Falcato @ 2026-10-26 13:48 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Chris Mason, stable
On Mon, Oct 26, 2026 at 10:40:37AM +0100, David Hildenbrand (Arm) wrote:
> From: Chris Mason <mason@kernel.org>
>
> The owner of a memfd sealed with F_SEAL_EXEC can still change its
> execute bits by setting a POSIX access ACL. With CONFIG_TMPFS_POSIX_ACL,
> this lets an unprivileged owner make a non-executable memfd executable,
> bypassing vm.memfd_noexec=2.
>
> simple_set_acl() updates i_mode directly, bypassing the seal check in
> shmem_setattr(). Add shmem_set_acl() to check the resulting mode. Share
> the F_SEAL_EXEC check with shmem_setattr() using memfd_check_seals_mode()
> in include/linux/memfd.h.
>
> Fix generated by an LLM and sent by Chris Mason in private.
>
> Fixes: 6fd7353829ca ("mm/memfd: add F_SEAL_EXEC")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chris Mason <mason@kernel.org>
> [ david: use common helper; simplify description ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Looks ok, as far as I understand ACLs :)
Acked-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr
2026-10-26 9:40 ` [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr David Hildenbrand (Arm)
@ 2026-10-26 13:49 ` Pedro Falcato
2026-10-26 15:55 ` Kiryl Shutsemau
1 sibling, 0 replies; 30+ messages in thread
From: Pedro Falcato @ 2026-10-26 13:49 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Eva Kurchatova, stable
On Mon, Oct 26, 2026 at 10:40:38AM +0100, David Hildenbrand (Arm) wrote:
> From: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
>
> F_SEAL_EXEC is enforced by shmem_setattr(), but hugetlbfs_setattr() does
> not check it. As a result, fchmod() can change the execute bits of a
> sealed hugetlbfs memfd.
>
> Use memfd_check_seals_mode() in hugetlbfs_setattr() to reject changes
> to execute bits when F_SEAL_EXEC is set.
>
> This also fixes the memfd_test hugetlbfs SEAL-EXEC subtest, where
> fchmod() incorrectly succeeded after applying F_SEAL_EXEC.
>
> LLM assistance was used to reuse the helper and revise the description.
>
> Fixes: 6fd7353829ca ("mm/memfd: add F_SEAL_EXEC")
> Cc: stable@vger.kernel.org
> Signed-off-by: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
> Link: https://patch.msgid.link/20260629180247.2343229-1-eva.kurchatova@virtuozzo.com
> [ david: use common helper; fixup Fixes: ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Similarly:
Acked-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 06/16] mm/shmem: hold off eviction in shmem_writeout()
2026-10-26 9:40 ` [PATCH 06/16] mm/shmem: hold off eviction in shmem_writeout() David Hildenbrand (Arm)
@ 2026-10-26 15:14 ` Pedro Falcato
0 siblings, 0 replies; 30+ messages in thread
From: Pedro Falcato @ 2026-10-26 15:14 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Josef Bacik, stable
On Mon, Oct 26, 2026 at 10:40:39AM +0100, David Hildenbrand (Arm) wrote:
> From: Josef Bacik <josef@toxicpanda.com>
>
> Once shmem_writeout() has moved the folio from the page cache to the
> swap cache, the folio lock no longer keeps the inode alive: truncation
> and eviction find a swap entry, free it and do not wait for anybody.
> That did not matter as long as swap_writeout() was the last thing the
> function did. But since it puts the folio back when swap_writeout()
> returns AOP_WRITEPAGE_ACTIVATE, it uses the mapping and the inode
> afterwards:
>
> reclaim last close of an unlinked file
> shmem_writeout()
> shmem_delete_from_page_cache()
> swap_writeout()
> zswap_store() fails shmem_evict_inode()
> writeback is disabled shmem_truncate_range()
> return AOP_WRITEPAGE_ACTIVATE shmem_free_swap()
> destroy_inode()
> ... RCU grace period, freed
> shmem_add_to_page_cache(mapping)
> xas_lock_irq() <- use after free
>
> swap_writeout() behaves that way if zswap is enabled, cannot take the
> folio and the memcg or one of its ancestors has memory.zswap.writeback
> set to 0.
>
> shmem_unuse() already added infrastructure to deal with that.
> Reuse info->stop_eviction to protect the inode during writeout. Increment
> it before removing the folio from the page cache, and decrement it after
> swap_writeout() and any attempted reinsertion and inode accounting.
> In shmem_evict_inode(), wait for the counter to reach zero even if
> swapoff has already removed the inode from the swap list. Add some helpers
> to make the code easier to digest.
>
> Fix generated by an LLM and sent by Josef Bacik in private.
>
> Fixes: 6344a6d9ce13 ("mm/shmem: writeout free swap if swap_writeout() reactivates")
> Cc: stable@vger.kernel.org
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> [ david: code cleanups; improve description ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
I've been looking at this for a while trying to get a smoother fix, but:
Acked-by: Pedro Falcato <pfalcato@suse.de>
Things I looked at:
1) Could we just drop this logic? No, it's fairly recent and seems to have
impact on zswap setups with no writeback.
2) Could we reorder swap_writeout with removing the folio from the page cache?
Perhaps, but it would be a larger change. I'm not sure if the logic all works out,
but you could possibly have a:
error = swap_writeout(...);
if (error != AOP_WRITEPAGE_ACTIVATE) {
shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap));
} else {
/* the other swap bookkeeping */
}
But I'm not familiar enough with the swap logic to reason about possible
races.
--
Pedro
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 01/16] mm/mempolicy: charge shared policy nodes
2026-10-26 9:40 ` [PATCH 01/16] mm/mempolicy: charge shared policy nodes David Hildenbrand (Arm)
2026-10-26 13:38 ` Pedro Falcato
@ 2026-10-26 15:44 ` Kiryl Shutsemau
1 sibling, 0 replies; 30+ messages in thread
From: Kiryl Shutsemau @ 2026-10-26 15:44 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Chris Mason, stable
On Mon, Oct 26, 2026 at 10:40:34AM +0100, David Hildenbrand (Arm) wrote:
> From: Chris Mason <mason@kernel.org>
>
> Any local user can bypass memory.max with mbind() on a tmpfs file, and
> allocate unbounded kernel memory.
>
> mbind() sets a NUMA memory policy for a range of a mapping. If the
> mapping is of a shmem and friends, the kernel also stores the policy on
> the inode, in a tree with one entry per range of the file.
>
> Each mbind() on a single page of a mapping can add one shared policy node
> (struct sp_node) and one struct mempolicy to that tree. They outlive the
> mapping and stay around until the inode is destroyed. The tree is not
> bounded by the file size, so the file can be empty.
>
> Memory that user space can allocate must be charged to a memory cgroup. A
> slab cache does that for all of its objects if it is created with
> SLAB_ACCOUNT. numa_policy_init() creates the slab caches for both
> structs without SLAB_ACCOUNT, so they are not charged to any cgroup.
>
> Reproduced with mbind() on single pages of an empty tmpfs file, from a
> cgroup with memory.max=8M: the global OOM killer killed tasks owned by
> root, and the machine panicked.
>
> Fix this by creating both caches with SLAB_ACCOUNT. This change implies
> that mbind() can now fail when running into memcg limits. Further,
> the policy_cache also serves task and VMA policies, so they are
> similarly accounted now.
>
> Note that per-file limits are not really helpful, as the user could
> simply use many files; having limits per user is tricky as well.
>
> Fix generated by an LLM and submitted by Chris Mason in private.
>
> Fixes: 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chris Mason <mason@kernel.org>
> [ david: smaller description tweaks ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set
2026-10-26 9:40 ` [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set David Hildenbrand (Arm)
2026-10-26 13:48 ` Pedro Falcato
@ 2026-10-26 15:52 ` Kiryl Shutsemau
1 sibling, 0 replies; 30+ messages in thread
From: Kiryl Shutsemau @ 2026-10-26 15:52 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Chris Mason, stable
On Mon, Oct 26, 2026 at 10:40:37AM +0100, David Hildenbrand (Arm) wrote:
> From: Chris Mason <mason@kernel.org>
>
> The owner of a memfd sealed with F_SEAL_EXEC can still change its
> execute bits by setting a POSIX access ACL. With CONFIG_TMPFS_POSIX_ACL,
> this lets an unprivileged owner make a non-executable memfd executable,
> bypassing vm.memfd_noexec=2.
>
> simple_set_acl() updates i_mode directly, bypassing the seal check in
> shmem_setattr(). Add shmem_set_acl() to check the resulting mode. Share
> the F_SEAL_EXEC check with shmem_setattr() using memfd_check_seals_mode()
> in include/linux/memfd.h.
>
> Fix generated by an LLM and sent by Chris Mason in private.
>
> Fixes: 6fd7353829ca ("mm/memfd: add F_SEAL_EXEC")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chris Mason <mason@kernel.org>
> [ david: use common helper; simplify description ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr
2026-10-26 9:40 ` [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr David Hildenbrand (Arm)
2026-10-26 13:49 ` Pedro Falcato
@ 2026-10-26 15:55 ` Kiryl Shutsemau
1 sibling, 0 replies; 30+ messages in thread
From: Kiryl Shutsemau @ 2026-10-26 15:55 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Gregory Price, Zi Yan, Joshua Hahn, Rakie Kim,
Ying Huang, Johannes Weiner, Vladimir Davydov, Michal Hocko,
Hugh Dickins, Baolin Wang, Lukas Czerner, Carlos Maiolino,
Christian Brauner, Jan Kara, Daniel Verkamp, Jeff Xu,
Muchun Song, Oscar Salvador, David Stevens,
Matthew Wilcox (Oracle),
William Kucharski, Chris Li, Kairui Song, Nhat Pham, Miaohe Lin,
Naoya Horiguchi, linux-mm, linux-kernel, Eva Kurchatova, stable
On Mon, Oct 26, 2026 at 10:40:38AM +0100, David Hildenbrand (Arm) wrote:
> From: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
>
> F_SEAL_EXEC is enforced by shmem_setattr(), but hugetlbfs_setattr() does
> not check it. As a result, fchmod() can change the execute bits of a
> sealed hugetlbfs memfd.
>
> Use memfd_check_seals_mode() in hugetlbfs_setattr() to reject changes
> to execute bits when F_SEAL_EXEC is set.
>
> This also fixes the memfd_test hugetlbfs SEAL-EXEC subtest, where
> fchmod() incorrectly succeeded after applying F_SEAL_EXEC.
>
> LLM assistance was used to reuse the helper and revise the description.
>
> Fixes: 6fd7353829ca ("mm/memfd: add F_SEAL_EXEC")
> Cc: stable@vger.kernel.org
> Signed-off-by: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
> Link: https://patch.msgid.link/20260629180247.2343229-1-eva.kurchatova@virtuozzo.com
> [ david: use common helper; fixup Fixes: ]
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2026-10-26 15:55 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26 9:40 [PATCH 00/16] mm/shmem: various (urgent?) fixes David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 01/16] mm/mempolicy: charge shared policy nodes David Hildenbrand (Arm)
2026-10-26 13:38 ` Pedro Falcato
2026-10-26 15:44 ` Kiryl Shutsemau
2026-10-26 9:40 ` [PATCH 02/16] tmpfs: keep the quota soft limits of an unused id David Hildenbrand (Arm)
2026-10-26 10:53 ` Jan Kara
2026-10-26 11:00 ` David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 03/16] tmpfs: preserve explicitly disabled quota limits David Hildenbrand (Arm)
2026-10-26 13:37 ` Jan Kara
2026-10-26 9:40 ` [PATCH 04/16] mm/shmem: honour F_SEAL_EXEC when an ACL is set David Hildenbrand (Arm)
2026-10-26 13:48 ` Pedro Falcato
2026-10-26 15:52 ` Kiryl Shutsemau
2026-10-26 9:40 ` [PATCH 05/16] hugetlbfs: enforce F_SEAL_EXEC in setattr David Hildenbrand (Arm)
2026-10-26 13:49 ` Pedro Falcato
2026-10-26 15:55 ` Kiryl Shutsemau
2026-10-26 9:40 ` [PATCH 06/16] mm/shmem: hold off eviction in shmem_writeout() David Hildenbrand (Arm)
2026-10-26 15:14 ` Pedro Falcato
2026-10-26 9:40 ` [PATCH 07/16] mm/shmem: avoid moving the truncation start backwards David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 08/16] mm/shmem: stop retrying folios that cannot be split David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 09/16] mm/shmem: preserve data in clean folios when splitting fails David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 10/16] mm/shmem: discard whole new large folios on fallocate rollback David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 11/16] mm/shmem: rescan after nonuniform folio splitting David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 12/16] mm/shmem: fix truncate hang in large swap entry David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 13/16] mm/shmem: preserve allocation errors when swapping in large entries David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 14/16] mm/shmem: fix truncate loop after boundary swapin failure David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 15/16] mm/shmem: fix accounting of swapin-error markers David Hildenbrand (Arm)
2026-10-26 9:40 ` [PATCH 16/16] mm/shmem: fix truncation of files containing only " David Hildenbrand (Arm)
2026-10-26 11:11 ` [PATCH 00/16] mm/shmem: various (urgent?) fixes Baolin Wang
2026-10-26 12:12 ` David Hildenbrand (Arm)
2026-10-26 13:23 ` Kairui Song
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®