| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/22 00:24 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"TRANSPARENT_HUGEPAGE",
"MEMCG",
"BTRFS_FS",
"SHMEM"
],
"FocusSymbols": [
"shmem_unused_huge_shrink",
"shmem_unused_huge_scan",
"shmem_unused_huge_count",
"shmem_unused_huge_add",
"shmem_unused_huge_del",
"shmem_unused_huge_isolate",
"shmem_unused_huge_requeue",
"shmem_unused_huge_drop",
"is_shmem_unused_huge_match",
"shmem_unused_huge_alloc_lru",
"shmem_get_and_clear_memcg",
"is_shmem_unused_huge_isolated",
"set_shmem_unused_huge_isolated",
"btrfs_nr_cached_objects"
],
"Reasoning": "The patch modifies how shmem handles unused huge pages when shrinking, introducing list_lru for shrinklist in shmem_sb_info. It also modifies super_cache_scan and super_cache_count in fs/super.c to remove super_fs_objects_eligible and instead uses mem_cgroup_shrink_is_root in btrfs_nr_cached_objects. These are functional changes to core memory management and filesystem logic, making them highly reachable and worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/22 00:24 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 42ba32dab31e1f1358c70adde49eb8f419ee26a2\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 22 00:24:39 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/fs/btrfs/super.c b/fs/btrfs/super.c\nindex a7d804219bec34..cc453743539976 100644\n--- a/fs/btrfs/super.c\n+++ b/fs/btrfs/super.c\n@@ -22,6 +22,7 @@\n #include \u003clinux/namei.h\u003e\n #include \u003clinux/miscdevice.h\u003e\n #include \u003clinux/magic.h\u003e\n+#include \u003clinux/memcontrol.h\u003e\n #include \u003clinux/slab.h\u003e\n #include \u003clinux/ratelimit.h\u003e\n #include \u003clinux/crc32c.h\u003e\n@@ -2434,6 +2435,15 @@ static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_contro\n \tstruct btrfs_fs_info *fs_info = btrfs_sb(sb);\n \tconst s64 nr = percpu_counter_read_positive(\u0026fs_info-\u003eevictable_extent_maps);\n \n+\t/*\n+\t * The evictable extent map counter is filesystem-global and does not\n+\t * honour sc-\u003ememcg, so it is only meaningful on the global (kswapd or\n+\t * root direct reclaim) shrink path. Skip the per-memcg iterations of\n+\t * shrink_slab_memcg() to avoid queueing duplicate global work.\n+\t */\n+\tif (!mem_cgroup_shrink_is_root(sc))\n+\t\treturn 0;\n+\n \ttrace_btrfs_extent_map_shrinker_count(fs_info, nr);\n \n \treturn nr;\ndiff --git a/fs/super.c b/fs/super.c\nindex 9f82e6b38f07ee..93f24aea75c4d6 100644\n--- a/fs/super.c\n+++ b/fs/super.c\n@@ -24,7 +24,6 @@\n #include \u003clinux/export.h\u003e\n #include \u003clinux/slab.h\u003e\n #include \u003clinux/blkdev.h\u003e\n-#include \u003clinux/memcontrol.h\u003e\n #include \u003clinux/rhashtable.h\u003e\n #include \u003clinux/mount.h\u003e\n #include \u003clinux/security.h\u003e\n@@ -171,19 +170,6 @@ static void super_wake(struct super_block *sb, unsigned int flag)\n \twake_up_var(\u0026sb-\u003es_flags);\n }\n \n-/*\n- * The s_op-\u003enr_cached_objects hooks (used for example by btrfs and xfs)\n- * operate on filesystem-global state and ignore sc-\u003ememcg. Driving them\n- * from per-memcg shrink_slab_memcg() invocations only burns CPU walking\n- * per-cpu counters and queueing duplicate work: the actual reclaim happens on\n- * the global path (kswapd or root direct reclaim) regardless. Restrict them\n- * to that path.\n- */\n-static inline bool super_fs_objects_eligible(struct shrink_control *sc)\n-{\n-\treturn !sc-\u003ememcg || mem_cgroup_is_root(sc-\u003ememcg);\n-}\n-\n /*\n * One thing we have to be careful of with a per-sb shrinker is that we don't\n * drop the last active reference to the superblock from within the shrinker.\n@@ -213,7 +199,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,\n \tif (!super_trylock_shared(sb))\n \t\treturn SHRINK_STOP;\n \n-\tif (sb-\u003es_op-\u003enr_cached_objects \u0026\u0026 super_fs_objects_eligible(sc))\n+\tif (sb-\u003es_op-\u003enr_cached_objects)\n \t\tfs_objects = sb-\u003es_op-\u003enr_cached_objects(sb, sc);\n \n \tinodes = list_lru_shrink_count(\u0026sb-\u003es_inode_lru, sc);\n@@ -274,8 +260,7 @@ static unsigned long super_cache_count(struct shrinker *shrink,\n \t\treturn 0;\n \tsmp_rmb();\n \n-\tif (sb-\u003es_op \u0026\u0026 sb-\u003es_op-\u003enr_cached_objects \u0026\u0026\n-\t super_fs_objects_eligible(sc))\n+\tif (sb-\u003es_op \u0026\u0026 sb-\u003es_op-\u003enr_cached_objects)\n \t\ttotal_objects = sb-\u003es_op-\u003enr_cached_objects(sb, sc);\n \n \ttotal_objects += list_lru_shrink_count(\u0026sb-\u003es_dentry_lru, sc);\ndiff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h\nindex 95726067767815..8170bb8066a22f 100644\n--- a/include/linux/memcontrol.h\n+++ b/include/linux/memcontrol.h\n@@ -520,6 +520,22 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg)\n \treturn (memcg == root_mem_cgroup);\n }\n \n+/**\n+ * mem_cgroup_shrink_is_root - is this a global or root-memcg shrink invocation?\n+ * @sc: shrink_control describing the current shrinker call\n+ *\n+ * Returns true when @sc represents a global reclaim shrink (sc-\u003ememcg == NULL)\n+ * or a root-memcg shrink, i.e. not a per-memcg iteration of\n+ * shrink_slab_memcg(). Filesystems whose -\u003enr_cached_objects()/\n+ * -\u003efree_cached_objects() implementations operate on filesystem-global state\n+ * and do not honour sc-\u003ememcg can use this to early-return 0 in per-memcg\n+ * contexts.\n+ */\n+static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc)\n+{\n+\treturn !sc-\u003ememcg || mem_cgroup_is_root(sc-\u003ememcg);\n+}\n+\n static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg)\n {\n \treturn objcg-\u003eis_root;\n@@ -1071,6 +1087,11 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg)\n \treturn true;\n }\n \n+static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc)\n+{\n+\treturn true;\n+}\n+\n static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg)\n {\n \treturn true;\ndiff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h\nindex 5663dff53186e2..aeb59901a840c7 100644\n--- a/include/linux/shmem_fs.h\n+++ b/include/linux/shmem_fs.h\n@@ -11,6 +11,7 @@\n #include \u003clinux/fs_parser.h\u003e\n #include \u003clinux/userfaultfd_k.h\u003e\n #include \u003clinux/bits.h\u003e\n+#include \u003clinux/list_lru.h\u003e\n \n /* inode in-kernel data */\n \n@@ -54,6 +55,11 @@ struct shmem_inode_info {\n \tstruct dquot __rcu\t*i_dquot[MAXQUOTAS];\n #endif\n \tstruct inode\t\tvfs_inode;\n+\n+#ifdef CONFIG_TRANSPARENT_HUGEPAGE\n+\tstruct mem_cgroup\t*shrinklist_memcg;\n+\tint\t\t\tshrinklist_nid;\n+#endif\n };\n \n #define SHMEM_FL_USER_VISIBLE\t\t(FS_FL_USER_VISIBLE | FS_CASEFOLD_FL)\n@@ -83,9 +89,9 @@ struct shmem_sb_info {\n \tino_t next_ino;\t\t /* The next per-sb inode number to use */\n \tino_t __percpu *ino_batch; /* The next per-cpu inode number to use */\n \tstruct mempolicy *mpol; /* default memory policy for mappings */\n-\tspinlock_t shrinklist_lock; /* Protects shrinklist */\n-\tstruct list_head shrinklist; /* List of shinkable inodes */\n-\tunsigned long shrinklist_len; /* Length of shrinklist */\n+#ifdef CONFIG_TRANSPARENT_HUGEPAGE\n+\tstruct list_lru shrinklist; /* List of shrinkable inodes */\n+#endif\n \tstruct shmem_quota_limits qlimits; /* Default quota limits */\n \tstruct simple_xattr_cache xa_cache;\n };\ndiff --git a/mm/shmem.c b/mm/shmem.c\nindex f189d1ac40db2d..8bf6dd850f85b4 100644\n--- a/mm/shmem.c\n+++ b/mm/shmem.c\n@@ -724,51 +724,242 @@ static const char *shmem_format_huge(int huge)\n }\n #endif\n \n+static bool is_shmem_unused_huge_isolated(struct shmem_inode_info *info)\n+{\n+\n+\treturn info-\u003eshrinklist_nid == -1;\n+}\n+\n+static void set_shmem_unused_huge_isolated(struct shmem_inode_info *info)\n+{\n+\tinfo-\u003eshrinklist_nid = -1;\n+}\n+\n+static struct mem_cgroup *shmem_get_and_clear_memcg(struct shmem_inode_info *info)\n+{\n+\tstruct mem_cgroup *memcg = info-\u003eshrinklist_memcg;\n+\n+\tinfo-\u003eshrinklist_memcg = NULL;\n+\n+\treturn memcg;\n+}\n+\n+#ifdef CONFIG_MEMCG\n+static struct mem_cgroup *\n+shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,\n+\t\t\t gfp_t gfp)\n+{\n+\tstruct mem_cgroup *memcg;\n+\tint ret;\n+\n+\tmemcg = get_mem_cgroup_from_folio(folio);\n+\tif (!memcg)\n+\t\treturn NULL;\n+\n+\tret = memcg_list_lru_alloc(memcg, \u0026sbinfo-\u003eshrinklist, gfp);\n+\tif (ret) {\n+\t\tmem_cgroup_put(memcg);\n+\t\treturn ERR_PTR(ret);\n+\t}\n+\n+\treturn memcg;\n+}\n+#else\n+static struct mem_cgroup *\n+shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,\n+\t\t\t gfp_t gfp)\n+{\n+\treturn NULL;\n+}\n+#endif\n+\n+static void shmem_unused_huge_add(struct inode *inode, struct folio *folio,\n+\t\t\t\t gfp_t gfp)\n+{\n+\tstruct shmem_inode_info *info = SHMEM_I(inode);\n+\tstruct shmem_sb_info *sbinfo = SHMEM_SB(inode-\u003ei_sb);\n+\tint nid = folio_nid(folio);\n+\tstruct mem_cgroup *memcg = NULL, *old_memcg = NULL;\n+\n+\tmemcg = shmem_unused_huge_alloc_lru(sbinfo, folio, gfp);\n+\tif (IS_ERR(memcg))\n+\t\treturn;\n+\n+\tspin_lock(\u0026info-\u003elock);\n+\tif (!list_empty(\u0026info-\u003eshrinklist)) {\n+\t\t/* isolated on scan list, let shrink handle it */\n+\t\tif (is_shmem_unused_huge_isolated(info))\n+\t\t\tgoto unlock;\n+\n+\t\tif (info-\u003eshrinklist_nid == nid \u0026\u0026\n+\t\t info-\u003eshrinklist_memcg == memcg)\n+\t\t\tgoto unlock;\n+\n+\t\tlist_lru_del(\u0026sbinfo-\u003eshrinklist, \u0026info-\u003eshrinklist,\n+\t\t\t info-\u003eshrinklist_nid, info-\u003eshrinklist_memcg);\n+\t\told_memcg = shmem_get_and_clear_memcg(info);\n+\t}\n+\n+\tinfo-\u003eshrinklist_memcg = memcg;\n+\tinfo-\u003eshrinklist_nid = nid;\n+\tlist_lru_add(\u0026sbinfo-\u003eshrinklist, \u0026info-\u003eshrinklist, nid, memcg);\n+\tmemcg = NULL;\n+unlock:\n+\tspin_unlock(\u0026info-\u003elock);\n+\tmem_cgroup_put(old_memcg);\n+\tmem_cgroup_put(memcg);\n+}\n+\n+static void shmem_unused_huge_del(struct inode *inode)\n+{\n+\tstruct shmem_inode_info *info = SHMEM_I(inode);\n+\tstruct shmem_sb_info *sbinfo = SHMEM_SB(inode-\u003ei_sb);\n+\tstruct mem_cgroup *memcg = NULL;\n+\n+\tspin_lock(\u0026info-\u003elock);\n+\tif (!list_empty(\u0026info-\u003eshrinklist)) {\n+\t\tlist_lru_del(\u0026sbinfo-\u003eshrinklist, \u0026info-\u003eshrinklist,\n+\t\t\t info-\u003eshrinklist_nid, info-\u003eshrinklist_memcg);\n+\t\tmemcg = shmem_get_and_clear_memcg(info);\n+\t}\n+\tspin_unlock(\u0026info-\u003elock);\n+\n+\tmem_cgroup_put(memcg);\n+}\n+\n+struct shmem_unused_huge_scan {\n+\tstruct list_head list;\n+\tstruct shrink_control *sc;\n+};\n+\n+static enum lru_status shmem_unused_huge_isolate(struct list_head *item,\n+\t\t\t\t\t\t struct list_lru_one *lru,\n+\t\t\t\t\t\t void *arg)\n+{\n+\tstruct shmem_unused_huge_scan *scan = arg;\n+\tstruct shmem_inode_info *info;\n+\tstruct inode *inode;\n+\tstruct mem_cgroup *memcg = NULL;\n+\n+\tinfo = list_entry(item, struct shmem_inode_info, shrinklist);\n+\n+\t/*\n+\t * Use trylock to avoid ABBA deadlock: add/del path takes info-\u003elock\n+\t * before the list_lru bucket lock, while here the order is reversed.\n+\t */\n+\tif (!spin_trylock(\u0026info-\u003elock))\n+\t\treturn LRU_SKIP;\n+\n+\t/* pin the inode */\n+\tinode = igrab(\u0026info-\u003evfs_inode);\n+\t/* inode is about to be evicted */\n+\tif (!inode) {\n+\t\tlist_lru_isolate(lru, item);\n+\t\tmemcg = shmem_get_and_clear_memcg(info);\n+\t\tspin_unlock(\u0026info-\u003elock);\n+\t\tmem_cgroup_put(memcg);\n+\t\treturn LRU_REMOVED;\n+\t}\n+\n+\tlist_lru_isolate(lru, item);\n+\tmemcg = shmem_get_and_clear_memcg(info);\n+\tset_shmem_unused_huge_isolated(info);\n+\tlist_add_tail(\u0026info-\u003eshrinklist, \u0026scan-\u003elist);\n+\tspin_unlock(\u0026info-\u003elock);\n+\tmem_cgroup_put(memcg);\n+\n+\treturn LRU_REMOVED;\n+}\n+\n+static bool is_shmem_unused_huge_match(struct folio *folio,\n+\t\t\t\t struct shrink_control *sc)\n+{\n+\tstruct mem_cgroup *memcg = NULL;\n+\tbool match;\n+\n+\t/*\n+\t * Only non-root memcg reclaim needs to match the folio charge against\n+\t * sc-\u003ememcg. Skip the folio memcg check for the following cases:\n+\t * 1. shmem quota reclaim (sc == NULL)\n+\t * 2. global shrinker reclaim\n+\t * 3. root memcg reclaim\n+\t */\n+\tif (!sc || !sc-\u003ememcg || mem_cgroup_is_root(sc-\u003ememcg))\n+\t\treturn true;\n+\n+\tif (folio_nid(folio) != sc-\u003enid)\n+\t\treturn false;\n+\n+\tmemcg = get_mem_cgroup_from_folio(folio);\n+\tmatch = memcg == sc-\u003ememcg;\n+\tmem_cgroup_put(memcg);\n+\n+\treturn match;\n+}\n+\n+static void shmem_unused_huge_drop(struct inode *inode)\n+{\n+\tstruct shmem_inode_info *info = SHMEM_I(inode);\n+\n+\tspin_lock(\u0026info-\u003elock);\n+\tlist_del_init(\u0026info-\u003eshrinklist);\n+\tspin_unlock(\u0026info-\u003elock);\n+}\n+\n+static void shmem_unused_huge_requeue(struct inode *inode, struct folio *folio)\n+{\n+\tstruct shmem_inode_info *info = SHMEM_I(inode);\n+\tstruct shmem_sb_info *sbinfo = SHMEM_SB(inode-\u003ei_sb);\n+\tstruct mem_cgroup *memcg;\n+\tint nid = folio_nid(folio);\n+\n+\tmemcg = shmem_unused_huge_alloc_lru(sbinfo, folio, GFP_NOWAIT);\n+\tif (IS_ERR(memcg))\n+\t\tgoto drop;\n+\n+\tspin_lock(\u0026info-\u003elock);\n+\t/* Requeue the inode to shrinklist */\n+\tlist_del_init(\u0026info-\u003eshrinklist);\n+\tlist_lru_add(\u0026sbinfo-\u003eshrinklist, \u0026info-\u003eshrinklist, nid, memcg);\n+\tinfo-\u003eshrinklist_memcg = memcg;\n+\tinfo-\u003eshrinklist_nid = nid;\n+\tmemcg = NULL;\n+\tspin_unlock(\u0026info-\u003elock);\n+\tmem_cgroup_put(memcg);\n+\n+\treturn;\n+\n+drop:\n+\tshmem_unused_huge_drop(inode);\n+}\n+\n static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\n \t\tstruct shrink_control *sc, unsigned long nr_to_free)\n {\n-\tLIST_HEAD(list), *pos, *next;\n+\tstruct shmem_unused_huge_scan scan;\n \tstruct inode *inode;\n \tstruct shmem_inode_info *info;\n \tstruct folio *folio;\n-\tunsigned long batch = sc ? sc-\u003enr_to_scan : 128;\n+\tstruct list_head *pos, *next;\n \tunsigned long split = 0, freed = 0;\n \n-\tif (list_empty(\u0026sbinfo-\u003eshrinklist))\n-\t\treturn SHRINK_STOP;\n-\n-\tspin_lock(\u0026sbinfo-\u003eshrinklist_lock);\n-\tlist_for_each_safe(pos, next, \u0026sbinfo-\u003eshrinklist) {\n-\t\tinfo = list_entry(pos, struct shmem_inode_info, shrinklist);\n-\n-\t\t/* pin the inode */\n-\t\tinode = igrab(\u0026info-\u003evfs_inode);\n-\n-\t\t/* inode is about to be evicted */\n-\t\tif (!inode) {\n-\t\t\tlist_del_init(\u0026info-\u003eshrinklist);\n-\t\t\tgoto next;\n-\t\t}\n-\n-\t\tlist_move(\u0026info-\u003eshrinklist, \u0026list);\n-next:\n-\t\tsbinfo-\u003eshrinklist_len--;\n-\t\tif (!--batch)\n-\t\t\tbreak;\n-\t}\n-\tspin_unlock(\u0026sbinfo-\u003eshrinklist_lock);\n+\tINIT_LIST_HEAD(\u0026scan.list);\n+\tscan.sc = sc;\n+\tif (sc)\n+\t\tlist_lru_shrink_walk(\u0026sbinfo-\u003eshrinklist, sc,\n+\t\t\t\t shmem_unused_huge_isolate, \u0026scan);\n+\telse\n+\t\tlist_lru_walk(\u0026sbinfo-\u003eshrinklist, shmem_unused_huge_isolate,\n+\t\t\t \u0026scan, 128);\n \n-\tlist_for_each_safe(pos, next, \u0026list) {\n-\t\tpgoff_t next, end;\n+\tlist_for_each_safe(pos, next, \u0026scan.list) {\n+\t\tpgoff_t folio_end, end;\n \t\tloff_t i_size;\n \t\tint ret;\n \n \t\tinfo = list_entry(pos, struct shmem_inode_info, shrinklist);\n \t\tinode = \u0026info-\u003evfs_inode;\n \n-\t\tif (nr_to_free \u0026\u0026 freed \u003e= nr_to_free)\n-\t\t\tgoto move_back;\n-\n \t\ti_size = i_size_read(inode);\n \t\tfolio = filemap_get_entry(inode-\u003ei_mapping, i_size / PAGE_SIZE);\n \t\tif (!folio || xa_is_value(folio))\n@@ -781,13 +972,25 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\n \t\t}\n \n \t\t/* Check if there is anything to gain from splitting */\n-\t\tnext = folio_next_index(folio);\n+\t\tfolio_end = folio_next_index(folio);\n \t\tend = shmem_fallocend(inode, DIV_ROUND_UP(i_size, PAGE_SIZE));\n-\t\tif (end \u003c= folio-\u003eindex || end \u003e= next) {\n+\t\tif (end \u003c= folio-\u003eindex || end \u003e= folio_end) {\n \t\t\tfolio_put(folio);\n \t\t\tgoto drop;\n \t\t}\n \n+\t\tif (!is_shmem_unused_huge_match(folio, scan.sc)) {\n+\t\t\tshmem_unused_huge_requeue(inode, folio);\n+\t\t\tfolio_put(folio);\n+\t\t\tgoto put;\n+\t\t}\n+\n+\t\tif (nr_to_free \u0026\u0026 freed \u003e= nr_to_free) {\n+\t\t\tshmem_unused_huge_requeue(inode, folio);\n+\t\t\tfolio_put(folio);\n+\t\t\tgoto put;\n+\t\t}\n+\n \t\t/*\n \t\t * Move the inode on the list back to shrinklist if we failed\n \t\t * to lock the page at this time.\n@@ -796,34 +999,33 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\n \t\t * reclaim path.\n \t\t */\n \t\tif (!folio_trylock(folio)) {\n+\t\t\tshmem_unused_huge_requeue(inode, folio);\n+\t\t\tfolio_put(folio);\n+\t\t\tgoto put;\n+\t\t}\n+\n+\t\tif (!is_shmem_unused_huge_match(folio, scan.sc)) {\n+\t\t\tfolio_unlock(folio);\n+\t\t\tshmem_unused_huge_requeue(inode, folio);\n \t\t\tfolio_put(folio);\n-\t\t\tgoto move_back;\n+\t\t\tgoto put;\n \t\t}\n \n \t\tret = split_folio(folio);\n \t\tfolio_unlock(folio);\n-\t\tfolio_put(folio);\n \n \t\t/* If split failed move the inode on the list back to shrinklist */\n-\t\tif (ret)\n-\t\t\tgoto move_back;\n+\t\tif (ret) {\n+\t\t\tshmem_unused_huge_requeue(inode, folio);\n+\t\t\tfolio_put(folio);\n+\t\t\tgoto put;\n+\t\t}\n \n-\t\tfreed += next - end;\n+\t\tfreed += folio_end - end;\n \t\tsplit++;\n+\t\tfolio_put(folio);\n drop:\n-\t\tlist_del_init(\u0026info-\u003eshrinklist);\n-\t\tgoto put;\n-move_back:\n-\t\t/*\n-\t\t * Make sure the inode is either on the global list or deleted\n-\t\t * from any local list before iput() since it could be deleted\n-\t\t * in another thread once we put the inode (then the local list\n-\t\t * is corrupted).\n-\t\t */\n-\t\tspin_lock(\u0026sbinfo-\u003eshrinklist_lock);\n-\t\tlist_move(\u0026info-\u003eshrinklist, \u0026sbinfo-\u003eshrinklist);\n-\t\tsbinfo-\u003eshrinklist_len++;\n-\t\tspin_unlock(\u0026sbinfo-\u003eshrinklist_lock);\n+\t\tshmem_unused_huge_drop(inode);\n put:\n \t\tiput(inode);\n \t}\n@@ -836,7 +1038,7 @@ static long shmem_unused_huge_scan(struct super_block *sb,\n {\n \tstruct shmem_sb_info *sbinfo = SHMEM_SB(sb);\n \n-\tif (!READ_ONCE(sbinfo-\u003eshrinklist_len))\n+\tif (!list_lru_shrink_count(\u0026sbinfo-\u003eshrinklist, sc))\n \t\treturn SHRINK_STOP;\n \n \treturn shmem_unused_huge_shrink(sbinfo, sc, 0);\n@@ -846,12 +1048,22 @@ static long shmem_unused_huge_count(struct super_block *sb,\n \t\tstruct shrink_control *sc)\n {\n \tstruct shmem_sb_info *sbinfo = SHMEM_SB(sb);\n-\treturn READ_ONCE(sbinfo-\u003eshrinklist_len);\n+\n+\treturn list_lru_shrink_count(\u0026sbinfo-\u003eshrinklist, sc);\n }\n #else /* !CONFIG_TRANSPARENT_HUGEPAGE */\n \n #define shmem_huge SHMEM_HUGE_DENY\n \n+static void shmem_unused_huge_add(struct inode *inode, struct folio *folio,\n+\t\t\t\t gfp_t gfp)\n+{\n+}\n+\n+static void shmem_unused_huge_del(struct inode *inode)\n+{\n+}\n+\n static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\n \t\tstruct shrink_control *sc, unsigned long nr_to_free)\n {\n@@ -1407,14 +1619,7 @@ static void shmem_evict_inode(struct inode *inode)\n \t\tinode-\u003ei_size = 0;\n \t\tmapping_set_exiting(inode-\u003ei_mapping);\n \t\tshmem_truncate_range(inode, 0, (loff_t)-1);\n-\t\tif (!list_empty(\u0026info-\u003eshrinklist)) {\n-\t\t\tspin_lock(\u0026sbinfo-\u003eshrinklist_lock);\n-\t\t\tif (!list_empty(\u0026info-\u003eshrinklist)) {\n-\t\t\t\tlist_del_init(\u0026info-\u003eshrinklist);\n-\t\t\t\tsbinfo-\u003eshrinklist_len--;\n-\t\t\t}\n-\t\t\tspin_unlock(\u0026sbinfo-\u003eshrinklist_lock);\n-\t\t}\n+\t\tshmem_unused_huge_del(inode);\n \t\twhile (!list_empty(\u0026info-\u003eswaplist)) {\n \t\t\t/* Wait while shmem_unuse() is scanning this inode... */\n \t\t\twait_var_event(\u0026info-\u003estop_eviction,\n@@ -2522,28 +2727,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,\n \n alloced:\n \talloced = true;\n-\tif (folio_test_large(folio) \u0026\u0026\n-\t DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) \u003c\n-\t\t\t\t\tfolio_next_index(folio)) {\n-\t\tstruct shmem_sb_info *sbinfo = SHMEM_SB(inode-\u003ei_sb);\n-\t\tstruct shmem_inode_info *info = SHMEM_I(inode);\n-\t\t/*\n-\t\t * Part of the large folio is beyond i_size: subject\n-\t\t * to shrink under memory pressure.\n-\t\t */\n-\t\tspin_lock(\u0026sbinfo-\u003eshrinklist_lock);\n-\t\t/*\n-\t\t * _careful to defend against unlocked access to\n-\t\t * -\u003eshrink_list in shmem_unused_huge_shrink()\n-\t\t */\n-\t\tif (list_empty_careful(\u0026info-\u003eshrinklist)) {\n-\t\t\tlist_add_tail(\u0026info-\u003eshrinklist,\n-\t\t\t\t \u0026sbinfo-\u003eshrinklist);\n-\t\t\tsbinfo-\u003eshrinklist_len++;\n-\t\t}\n-\t\tspin_unlock(\u0026sbinfo-\u003eshrinklist_lock);\n-\t}\n-\n \tif (sgp == SGP_WRITE)\n \t\tfolio_set_referenced(folio);\n \t/*\n@@ -2572,6 +2755,15 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,\n \t\terror = -EINVAL;\n \t\tgoto unlock;\n \t}\n+\n+\tif (alloced \u0026\u0026 folio_test_large(folio) \u0026\u0026\n+\t DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) \u003c folio_next_index(folio)) {\n+\t\t/*\n+\t\t * Part of the large folio is beyond i_size: subject\n+\t\t * to shrink under memory pressure.\n+\t\t */\n+\t\tshmem_unused_huge_add(inode, folio, gfp);\n+\t}\n out:\n \t*foliop = folio;\n \treturn 0;\n@@ -3048,6 +3240,10 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,\n \tif (info-\u003efsflags)\n \t\tshmem_set_inode_flags(inode, info-\u003efsflags, NULL);\n \tINIT_LIST_HEAD(\u0026info-\u003eshrinklist);\n+#ifdef CONFIG_TRANSPARENT_HUGEPAGE\n+\tinfo-\u003eshrinklist_memcg = NULL;\n+\tinfo-\u003eshrinklist_nid = -1;\n+#endif\n \tINIT_LIST_HEAD(\u0026info-\u003eswaplist);\n \tcache_no_acl(inode);\n \tif (sbinfo-\u003enoswap)\n@@ -4914,6 +5110,9 @@ static void shmem_put_super(struct super_block *sb)\n #endif\n \tfree_percpu(sbinfo-\u003eino_batch);\n \tpercpu_counter_destroy(\u0026sbinfo-\u003eused_blocks);\n+#ifdef CONFIG_TRANSPARENT_HUGEPAGE\n+\tlist_lru_destroy(\u0026sbinfo-\u003eshrinklist);\n+#endif\n \tmpol_put(sbinfo-\u003empol);\n #ifdef CONFIG_TMPFS_XATTR\n \tsimple_xattr_cache_cleanup(\u0026sbinfo-\u003exa_cache);\n@@ -5007,8 +5206,11 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)\n \traw_spin_lock_init(\u0026sbinfo-\u003estat_lock);\n \tif (percpu_counter_init(\u0026sbinfo-\u003eused_blocks, 0, GFP_KERNEL))\n \t\tgoto failed;\n-\tspin_lock_init(\u0026sbinfo-\u003eshrinklist_lock);\n-\tINIT_LIST_HEAD(\u0026sbinfo-\u003eshrinklist);\n+\n+#ifdef CONFIG_TRANSPARENT_HUGEPAGE\n+\tif (list_lru_init_memcg(\u0026sbinfo-\u003eshrinklist, sb-\u003es_shrink))\n+\t\tgoto failed;\n+#endif\n \n \tsb-\u003es_maxbytes = MAX_LFS_FILESIZE;\n \tsb-\u003es_blocksize = PAGE_SIZE;\n"
} |
| 2/1 | 2026/07/22 00:24 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"TRANSPARENT_HUGEPAGE",
"MEMCG",
"BTRFS_FS",
"SHMEM"
],
"FocusSymbols": [
"shmem_unused_huge_shrink",
"shmem_unused_huge_scan",
"shmem_unused_huge_count",
"shmem_unused_huge_add",
"shmem_unused_huge_del",
"shmem_unused_huge_isolate",
"shmem_unused_huge_requeue",
"shmem_unused_huge_drop",
"is_shmem_unused_huge_match",
"shmem_unused_huge_alloc_lru",
"shmem_get_and_clear_memcg",
"is_shmem_unused_huge_isolated",
"set_shmem_unused_huge_isolated",
"btrfs_nr_cached_objects"
],
"Reasoning": "The patch modifies how shmem handles unused huge pages when shrinking, introducing list_lru for shrinklist in shmem_sb_info. It also modifies super_cache_scan and super_cache_count in fs/super.c to remove super_fs_objects_eligible and instead uses mem_cgroup_shrink_is_root in btrfs_nr_cached_objects. These are functional changes to core memory management and filesystem logic, making them highly reachable and worth fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 42ba32dab31e1f1358c70adde49eb8f419ee26a2
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 22 00:24:39 2026 +0000
syz-cluster: applied patch under review
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index a7d804219bec34..cc453743539976 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -22,6 +22,7 @@
#include <linux/namei.h>
#include <linux/miscdevice.h>
#include <linux/magic.h>
+#include <linux/memcontrol.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/crc32c.h>
@@ -2434,6 +2435,15 @@ static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_contro
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
const s64 nr = percpu_counter_read_positive(&fs_info->evictable_extent_maps);
+ /*
+ * The evictable extent map counter is filesystem-global and does not
+ * honour sc->memcg, so it is only meaningful on the global (kswapd or
+ * root direct reclaim) shrink path. Skip the per-memcg iterations of
+ * shrink_slab_memcg() to avoid queueing duplicate global work.
+ */
+ if (!mem_cgroup_shrink_is_root(sc))
+ return 0;
+
trace_btrfs_extent_map_shrinker_count(fs_info, nr);
return nr;
diff --git a/fs/super.c b/fs/super.c
index 9f82e6b38f07ee..93f24aea75c4d6 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -24,7 +24,6 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
-#include <linux/memcontrol.h>
#include <linux/rhashtable.h>
#include <linux/mount.h>
#include <linux/security.h>
@@ -171,19 +170,6 @@ static void super_wake(struct super_block *sb, unsigned int flag)
wake_up_var(&sb->s_flags);
}
-/*
- * The s_op->nr_cached_objects hooks (used for example by btrfs and xfs)
- * operate on filesystem-global state and ignore sc->memcg. Driving them
- * from per-memcg shrink_slab_memcg() invocations only burns CPU walking
- * per-cpu counters and queueing duplicate work: the actual reclaim happens on
- * the global path (kswapd or root direct reclaim) regardless. Restrict them
- * to that path.
- */
-static inline bool super_fs_objects_eligible(struct shrink_control *sc)
-{
- return !sc->memcg || mem_cgroup_is_root(sc->memcg);
-}
-
/*
* One thing we have to be careful of with a per-sb shrinker is that we don't
* drop the last active reference to the superblock from within the shrinker.
@@ -213,7 +199,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
if (!super_trylock_shared(sb))
return SHRINK_STOP;
- if (sb->s_op->nr_cached_objects && super_fs_objects_eligible(sc))
+ if (sb->s_op->nr_cached_objects)
fs_objects = sb->s_op->nr_cached_objects(sb, sc);
inodes = list_lru_shrink_count(&sb->s_inode_lru, sc);
@@ -274,8 +260,7 @@ static unsigned long super_cache_count(struct shrinker *shrink,
return 0;
smp_rmb();
- if (sb->s_op && sb->s_op->nr_cached_objects &&
- super_fs_objects_eligible(sc))
+ if (sb->s_op && sb->s_op->nr_cached_objects)
total_objects = sb->s_op->nr_cached_objects(sb, sc);
total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc);
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 95726067767815..8170bb8066a22f 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -520,6 +520,22 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg)
return (memcg == root_mem_cgroup);
}
+/**
+ * mem_cgroup_shrink_is_root - is this a global or root-memcg shrink invocation?
+ * @sc: shrink_control describing the current shrinker call
+ *
+ * Returns true when @sc represents a global reclaim shrink (sc->memcg == NULL)
+ * or a root-memcg shrink, i.e. not a per-memcg iteration of
+ * shrink_slab_memcg(). Filesystems whose ->nr_cached_objects()/
+ * ->free_cached_objects() implementations operate on filesystem-global state
+ * and do not honour sc->memcg can use this to early-return 0 in per-memcg
+ * contexts.
+ */
+static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc)
+{
+ return !sc->memcg || mem_cgroup_is_root(sc->memcg);
+}
+
static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg)
{
return objcg->is_root;
@@ -1071,6 +1087,11 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg)
return true;
}
+static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc)
+{
+ return true;
+}
+
static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg)
{
return true;
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 5663dff53186e2..aeb59901a840c7 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -11,6 +11,7 @@
#include <linux/fs_parser.h>
#include <linux/userfaultfd_k.h>
#include <linux/bits.h>
+#include <linux/list_lru.h>
/* inode in-kernel data */
@@ -54,6 +55,11 @@ struct shmem_inode_info {
struct dquot __rcu *i_dquot[MAXQUOTAS];
#endif
struct inode vfs_inode;
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ struct mem_cgroup *shrinklist_memcg;
+ int shrinklist_nid;
+#endif
};
#define SHMEM_FL_USER_VISIBLE (FS_FL_USER_VISIBLE | FS_CASEFOLD_FL)
@@ -83,9 +89,9 @@ struct shmem_sb_info {
ino_t next_ino; /* The next per-sb inode number to use */
ino_t __percpu *ino_batch; /* The next per-cpu inode number to use */
struct mempolicy *mpol; /* default memory policy for mappings */
- spinlock_t shrinklist_lock; /* Protects shrinklist */
- struct list_head shrinklist; /* List of shinkable inodes */
- unsigned long shrinklist_len; /* Length of shrinklist */
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ struct list_lru shrinklist; /* List of shrinkable inodes */
+#endif
struct shmem_quota_limits qlimits; /* Default quota limits */
struct simple_xattr_cache xa_cache;
};
diff --git a/mm/shmem.c b/mm/shmem.c
index f189d1ac40db2d..8bf6dd850f85b4 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -724,51 +724,242 @@ static const char *shmem_format_huge(int huge)
}
#endif
+static bool is_shmem_unused_huge_isolated(struct shmem_inode_info *info)
+{
+
+ return info->shrinklist_nid == -1;
+}
+
+static void set_shmem_unused_huge_isolated(struct shmem_inode_info *info)
+{
+ info->shrinklist_nid = -1;
+}
+
+static struct mem_cgroup *shmem_get_and_clear_memcg(struct shmem_inode_info *info)
+{
+ struct mem_cgroup *memcg = info->shrinklist_memcg;
+
+ info->shrinklist_memcg = NULL;
+
+ return memcg;
+}
+
+#ifdef CONFIG_MEMCG
+static struct mem_cgroup *
+shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,
+ gfp_t gfp)
+{
+ struct mem_cgroup *memcg;
+ int ret;
+
+ memcg = get_mem_cgroup_from_folio(folio);
+ if (!memcg)
+ return NULL;
+
+ ret = memcg_list_lru_alloc(memcg, &sbinfo->shrinklist, gfp);
+ if (ret) {
+ mem_cgroup_put(memcg);
+ return ERR_PTR(ret);
+ }
+
+ return memcg;
+}
+#else
+static struct mem_cgroup *
+shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,
+ gfp_t gfp)
+{
+ return NULL;
+}
+#endif
+
+static void shmem_unused_huge_add(struct inode *inode, struct folio *folio,
+ gfp_t gfp)
+{
+ struct shmem_inode_info *info = SHMEM_I(inode);
+ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+ int nid = folio_nid(folio);
+ struct mem_cgroup *memcg = NULL, *old_memcg = NULL;
+
+ memcg = shmem_unused_huge_alloc_lru(sbinfo, folio, gfp);
+ if (IS_ERR(memcg))
+ return;
+
+ spin_lock(&info->lock);
+ if (!list_empty(&info->shrinklist)) {
+ /* isolated on scan list, let shrink handle it */
+ if (is_shmem_unused_huge_isolated(info))
+ goto unlock;
+
+ if (info->shrinklist_nid == nid &&
+ info->shrinklist_memcg == memcg)
+ goto unlock;
+
+ list_lru_del(&sbinfo->shrinklist, &info->shrinklist,
+ info->shrinklist_nid, info->shrinklist_memcg);
+ old_memcg = shmem_get_and_clear_memcg(info);
+ }
+
+ info->shrinklist_memcg = memcg;
+ info->shrinklist_nid = nid;
+ list_lru_add(&sbinfo->shrinklist, &info->shrinklist, nid, memcg);
+ memcg = NULL;
+unlock:
+ spin_unlock(&info->lock);
+ mem_cgroup_put(old_memcg);
+ mem_cgroup_put(memcg);
+}
+
+static void shmem_unused_huge_del(struct inode *inode)
+{
+ struct shmem_inode_info *info = SHMEM_I(inode);
+ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+ struct mem_cgroup *memcg = NULL;
+
+ spin_lock(&info->lock);
+ if (!list_empty(&info->shrinklist)) {
+ list_lru_del(&sbinfo->shrinklist, &info->shrinklist,
+ info->shrinklist_nid, info->shrinklist_memcg);
+ memcg = shmem_get_and_clear_memcg(info);
+ }
+ spin_unlock(&info->lock);
+
+ mem_cgroup_put(memcg);
+}
+
+struct shmem_unused_huge_scan {
+ struct list_head list;
+ struct shrink_control *sc;
+};
+
+static enum lru_status shmem_unused_huge_isolate(struct list_head *item,
+ struct list_lru_one *lru,
+ void *arg)
+{
+ struct shmem_unused_huge_scan *scan = arg;
+ struct shmem_inode_info *info;
+ struct inode *inode;
+ struct mem_cgroup *memcg = NULL;
+
+ info = list_entry(item, struct shmem_inode_info, shrinklist);
+
+ /*
+ * Use trylock to avoid ABBA deadlock: add/del path takes info->lock
+ * before the list_lru bucket lock, while here the order is reversed.
+ */
+ if (!spin_trylock(&info->lock))
+ return LRU_SKIP;
+
+ /* pin the inode */
+ inode = igrab(&info->vfs_inode);
+ /* inode is about to be evicted */
+ if (!inode) {
+ list_lru_isolate(lru, item);
+ memcg = shmem_get_and_clear_memcg(info);
+ spin_unlock(&info->lock);
+ mem_cgroup_put(memcg);
+ return LRU_REMOVED;
+ }
+
+ list_lru_isolate(lru, item);
+ memcg = shmem_get_and_clear_memcg(info);
+ set_shmem_unused_huge_isolated(info);
+ list_add_tail(&info->shrinklist, &scan->list);
+ spin_unlock(&info->lock);
+ mem_cgroup_put(memcg);
+
+ return LRU_REMOVED;
+}
+
+static bool is_shmem_unused_huge_match(struct folio *folio,
+ struct shrink_control *sc)
+{
+ struct mem_cgroup *memcg = NULL;
+ bool match;
+
+ /*
+ * Only non-root memcg reclaim needs to match the folio charge against
+ * sc->memcg. Skip the folio memcg check for the following cases:
+ * 1. shmem quota reclaim (sc == NULL)
+ * 2. global shrinker reclaim
+ * 3. root memcg reclaim
+ */
+ if (!sc || !sc->memcg || mem_cgroup_is_root(sc->memcg))
+ return true;
+
+ if (folio_nid(folio) != sc->nid)
+ return false;
+
+ memcg = get_mem_cgroup_from_folio(folio);
+ match = memcg == sc->memcg;
+ mem_cgroup_put(memcg);
+
+ return match;
+}
+
+static void shmem_unused_huge_drop(struct inode *inode)
+{
+ struct shmem_inode_info *info = SHMEM_I(inode);
+
+ spin_lock(&info->lock);
+ list_del_init(&info->shrinklist);
+ spin_unlock(&info->lock);
+}
+
+static void shmem_unused_huge_requeue(struct inode *inode, struct folio *folio)
+{
+ struct shmem_inode_info *info = SHMEM_I(inode);
+ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+ struct mem_cgroup *memcg;
+ int nid = folio_nid(folio);
+
+ memcg = shmem_unused_huge_alloc_lru(sbinfo, folio, GFP_NOWAIT);
+ if (IS_ERR(memcg))
+ goto drop;
+
+ spin_lock(&info->lock);
+ /* Requeue the inode to shrinklist */
+ list_del_init(&info->shrinklist);
+ list_lru_add(&sbinfo->shrinklist, &info->shrinklist, nid, memcg);
+ info->shrinklist_memcg = memcg;
+ info->shrinklist_nid = nid;
+ memcg = NULL;
+ spin_unlock(&info->lock);
+ mem_cgroup_put(memcg);
+
+ return;
+
+drop:
+ shmem_unused_huge_drop(inode);
+}
+
static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
struct shrink_control *sc, unsigned long nr_to_free)
{
- LIST_HEAD(list), *pos, *next;
+ struct shmem_unused_huge_scan scan;
struct inode *inode;
struct shmem_inode_info *info;
struct folio *folio;
- unsigned long batch = sc ? sc->nr_to_scan : 128;
+ struct list_head *pos, *next;
unsigned long split = 0, freed = 0;
- if (list_empty(&sbinfo->shrinklist))
- return SHRINK_STOP;
-
- spin_lock(&sbinfo->shrinklist_lock);
- list_for_each_safe(pos, next, &sbinfo->shrinklist) {
- info = list_entry(pos, struct shmem_inode_info, shrinklist);
-
- /* pin the inode */
- inode = igrab(&info->vfs_inode);
-
- /* inode is about to be evicted */
- if (!inode) {
- list_del_init(&info->shrinklist);
- goto next;
- }
-
- list_move(&info->shrinklist, &list);
-next:
- sbinfo->shrinklist_len--;
- if (!--batch)
- break;
- }
- spin_unlock(&sbinfo->shrinklist_lock);
+ INIT_LIST_HEAD(&scan.list);
+ scan.sc = sc;
+ if (sc)
+ list_lru_shrink_walk(&sbinfo->shrinklist, sc,
+ shmem_unused_huge_isolate, &scan);
+ else
+ list_lru_walk(&sbinfo->shrinklist, shmem_unused_huge_isolate,
+ &scan, 128);
- list_for_each_safe(pos, next, &list) {
- pgoff_t next, end;
+ list_for_each_safe(pos, next, &scan.list) {
+ pgoff_t folio_end, end;
loff_t i_size;
int ret;
info = list_entry(pos, struct shmem_inode_info, shrinklist);
inode = &info->vfs_inode;
- if (nr_to_free && freed >= nr_to_free)
- goto move_back;
-
i_size = i_size_read(inode);
folio = filemap_get_entry(inode->i_mapping, i_size / PAGE_SIZE);
if (!folio || xa_is_value(folio))
@@ -781,13 +972,25 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
}
/* Check if there is anything to gain from splitting */
- next = folio_next_index(folio);
+ folio_end = folio_next_index(folio);
end = shmem_fallocend(inode, DIV_ROUND_UP(i_size, PAGE_SIZE));
- if (end <= folio->index || end >= next) {
+ if (end <= folio->index || end >= folio_end) {
folio_put(folio);
goto drop;
}
+ if (!is_shmem_unused_huge_match(folio, scan.sc)) {
+ shmem_unused_huge_requeue(inode, folio);
+ folio_put(folio);
+ goto put;
+ }
+
+ if (nr_to_free && freed >= nr_to_free) {
+ shmem_unused_huge_requeue(inode, folio);
+ folio_put(folio);
+ goto put;
+ }
+
/*
* Move the inode on the list back to shrinklist if we failed
* to lock the page at this time.
@@ -796,34 +999,33 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
* reclaim path.
*/
if (!folio_trylock(folio)) {
+ shmem_unused_huge_requeue(inode, folio);
+ folio_put(folio);
+ goto put;
+ }
+
+ if (!is_shmem_unused_huge_match(folio, scan.sc)) {
+ folio_unlock(folio);
+ shmem_unused_huge_requeue(inode, folio);
folio_put(folio);
- goto move_back;
+ goto put;
}
ret = split_folio(folio);
folio_unlock(folio);
- folio_put(folio);
/* If split failed move the inode on the list back to shrinklist */
- if (ret)
- goto move_back;
+ if (ret) {
+ shmem_unused_huge_requeue(inode, folio);
+ folio_put(folio);
+ goto put;
+ }
- freed += next - end;
+ freed += folio_end - end;
split++;
+ folio_put(folio);
drop:
- list_del_init(&info->shrinklist);
- goto put;
-move_back:
- /*
- * Make sure the inode is either on the global list or deleted
- * from any local list before iput() since it could be deleted
- * in another thread once we put the inode (then the local list
- * is corrupted).
- */
- spin_lock(&sbinfo->shrinklist_lock);
- list_move(&info->shrinklist, &sbinfo->shrinklist);
- sbinfo->shrinklist_len++;
- spin_unlock(&sbinfo->shrinklist_lock);
+ shmem_unused_huge_drop(inode);
put:
iput(inode);
}
@@ -836,7 +1038,7 @@ static long shmem_unused_huge_scan(struct super_block *sb,
{
struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
- if (!READ_ONCE(sbinfo->shrinklist_len))
+ if (!list_lru_shrink_count(&sbinfo->shrinklist, sc))
return SHRINK_STOP;
return shmem_unused_huge_shrink(sbinfo, sc, 0);
@@ -846,12 +1048,22 @@ static long shmem_unused_huge_count(struct super_block *sb,
struct shrink_control *sc)
{
struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
- return READ_ONCE(sbinfo->shrinklist_len);
+
+ return list_lru_shrink_count(&sbinfo->shrinklist, sc);
}
#else /* !CONFIG_TRANSPARENT_HUGEPAGE */
#define shmem_huge SHMEM_HUGE_DENY
+static void shmem_unused_huge_add(struct inode *inode, struct folio *folio,
+ gfp_t gfp)
+{
+}
+
+static void shmem_unused_huge_del(struct inode *inode)
+{
+}
+
static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
struct shrink_control *sc, unsigned long nr_to_free)
{
@@ -1407,14 +1619,7 @@ static void shmem_evict_inode(struct inode *inode)
inode->i_size = 0;
mapping_set_exiting(inode->i_mapping);
shmem_truncate_range(inode, 0, (loff_t)-1);
- if (!list_empty(&info->shrinklist)) {
- spin_lock(&sbinfo->shrinklist_lock);
- if (!list_empty(&info->shrinklist)) {
- list_del_init(&info->shrinklist);
- sbinfo->shrinklist_len--;
- }
- spin_unlock(&sbinfo->shrinklist_lock);
- }
+ shmem_unused_huge_del(inode);
while (!list_empty(&info->swaplist)) {
/* Wait while shmem_unuse() is scanning this inode... */
wait_var_event(&info->stop_eviction,
@@ -2522,28 +2727,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,
alloced:
alloced = true;
- if (folio_test_large(folio) &&
- DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) <
- folio_next_index(folio)) {
- struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
- struct shmem_inode_info *info = SHMEM_I(inode);
- /*
- * Part of the large folio is beyond i_size: subject
- * to shrink under memory pressure.
- */
- spin_lock(&sbinfo->shrinklist_lock);
- /*
- * _careful to defend against unlocked access to
- * ->shrink_list in shmem_unused_huge_shrink()
- */
- if (list_empty_careful(&info->shrinklist)) {
- list_add_tail(&info->shrinklist,
- &sbinfo->shrinklist);
- sbinfo->shrinklist_len++;
- }
- spin_unlock(&sbinfo->shrinklist_lock);
- }
-
if (sgp == SGP_WRITE)
folio_set_referenced(folio);
/*
@@ -2572,6 +2755,15 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,
error = -EINVAL;
goto unlock;
}
+
+ if (alloced && folio_test_large(folio) &&
+ DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) < folio_next_index(folio)) {
+ /*
+ * Part of the large folio is beyond i_size: subject
+ * to shrink under memory pressure.
+ */
+ shmem_unused_huge_add(inode, folio, gfp);
+ }
out:
*foliop = folio;
return 0;
@@ -3048,6 +3240,10 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
if (info->fsflags)
shmem_set_inode_flags(inode, info->fsflags, NULL);
INIT_LIST_HEAD(&info->shrinklist);
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ info->shrinklist_memcg = NULL;
+ info->shrinklist_nid = -1;
+#endif
INIT_LIST_HEAD(&info->swaplist);
cache_no_acl(inode);
if (sbinfo->noswap)
@@ -4914,6 +5110,9 @@ static void shmem_put_super(struct super_block *sb)
#endif
free_percpu(sbinfo->ino_batch);
percpu_counter_destroy(&sbinfo->used_blocks);
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ list_lru_destroy(&sbinfo->shrinklist);
+#endif
mpol_put(sbinfo->mpol);
#ifdef CONFIG_TMPFS_XATTR
simple_xattr_cache_cleanup(&sbinfo->xa_cache);
@@ -5007,8 +5206,11 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
raw_spin_lock_init(&sbinfo->stat_lock);
if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL))
goto failed;
- spin_lock_init(&sbinfo->shrinklist_lock);
- INIT_LIST_HEAD(&sbinfo->shrinklist);
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ if (list_lru_init_memcg(&sbinfo->shrinklist, sb->s_shrink))
+ goto failed;
+#endif
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_SIZE;
|
| 3/2 | 2026/07/22 00:24 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9327 output: 36 thoughts: 530 Thoughts: **Analyzing a Kernel Patch: Huge Page and Object Caching Modifications** Okay, so I'm looking at this patch, and it seems to be refactoring how `shmem` deals with unused huge pages during shrinking operations. My initial thought is that it's replacing the old `list_head` and spinlock in `shmem_sb_info` with a `list_lru` for the `shrinklist`. That's a classic optimization, potentially leading to better performance in contention scenarios. It also seems to be touching `super_cache_scan` and `super_cache_count` in `fs/super.c`, removing `super_fs_objects_eligible` and leaning on `mem_cgroup_shrink_is_root` within `btrfs_nr_cached_objects` instead. That's interesting, suggesting a move towards more unified memory cgroup management. Functionally, these changes are impacting `mm/shmem.c` and also impacting `fs/btrfs/super.c`. The `shmem` side is definitely tied into `CONFIG_TRANSPARENT_HUGEPAGE` and `CONFIG_MEMCG`. Given this, I need to figure out if it's worth fuzzing and, more importantly, whether these code paths are actually reachable. Alright, let's break this down. `shmem_unused_huge_shrink` is called by `shmem_unused_huge_scan`, which in turn is a superblock shrinker callback – specifically, `s_op->free_cached_objects`. That's a clear entry point, a definite "yes" for reachability. Then, `shmem_unused_huge_add` is also called from `shmem_get_folio_gfp` when a large folio is allocated, specifically when part of the folio is beyond `i_size`. That's another potential trigger. Yup, this patch is absolutely worth fuzzing. Now, let's pinpoint the relevant kernel configs I need to keep in mind. We're looking at: * `CONFIG_TRANSPARENT_HUGEPAGE` * `CONFIG_MEMCG` * `CONFIG_SHMEM` (I'm assuming this is likely enabled by default, but it's always good to double-check) * `CONFIG_TMPFS` Next step: let's go into `mm/shmem.c` and identify the key functions. We need to focus on: * `shmem_unused_huge_shrink` * `shmem_unused_huge_scan` * `shmem_unused_huge_count` * `shmem_unused_huge_add` * `shmem_unused_huge_del` * `shmem_unused_huge_isolate` * `shmem_unused_huge_requeue` * `shmem_unused_huge_drop` And then, over in `fs/btrfs/super.c`, we've got: * `btrfs_nr_cached_objects` * `btrfs_free_cached_objects` Finally, a quick sanity check using `default_api:grepper` to confirm those configs and functions, just to be absolutely certain. Time to get this set up and start the fuzzing run! |
| 4/2 | 2026/07/22 00:24 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_TRANSPARENT_HUGEPAGE",
"PathPrefix": "mm/shmem.c"
}Results: {
"Output": "mm/shmem.c=113=struct shmem_options {\n--\nmm/shmem.c-136-\nmm/shmem.c:137:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-138-static unsigned long huge_shmem_orders_always __read_mostly;\n--\nmm/shmem.c=520=static int shmem_confirm_swap(struct address_space *mapping, pgoff_t index,\n--\nmm/shmem.c-568-\nmm/shmem.c:569:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-570-/* ifdef here to avoid bloating shmem.o when not necessary */\nmm/shmem.c-571-\nmm/shmem.c:572:#if defined(CONFIG_TRANSPARENT_HUGEPAGE_SHMEM_HUGE_NEVER)\nmm/shmem.c-573-#define SHMEM_HUGE_DEFAULT SHMEM_HUGE_NEVER\nmm/shmem.c:574:#elif defined(CONFIG_TRANSPARENT_HUGEPAGE_SHMEM_HUGE_ALWAYS)\nmm/shmem.c-575-#define SHMEM_HUGE_DEFAULT SHMEM_HUGE_ALWAYS\nmm/shmem.c:576:#elif defined(CONFIG_TRANSPARENT_HUGEPAGE_SHMEM_HUGE_WITHIN_SIZE)\nmm/shmem.c-577-#define SHMEM_HUGE_DEFAULT SHMEM_HUGE_WITHIN_SIZE\nmm/shmem.c:578:#elif defined(CONFIG_TRANSPARENT_HUGEPAGE_SHMEM_HUGE_ADVISE)\nmm/shmem.c-579-#define SHMEM_HUGE_DEFAULT SHMEM_HUGE_ADVISE\n--\nmm/shmem.c=584=static int shmem_huge __read_mostly = SHMEM_HUGE_DEFAULT;\n--\nmm/shmem.c-587-\nmm/shmem.c:588:#if defined(CONFIG_TRANSPARENT_HUGEPAGE_TMPFS_HUGE_NEVER)\nmm/shmem.c-589-#define TMPFS_HUGE_DEFAULT SHMEM_HUGE_NEVER\nmm/shmem.c:590:#elif defined(CONFIG_TRANSPARENT_HUGEPAGE_TMPFS_HUGE_ALWAYS)\nmm/shmem.c-591-#define TMPFS_HUGE_DEFAULT SHMEM_HUGE_ALWAYS\nmm/shmem.c:592:#elif defined(CONFIG_TRANSPARENT_HUGEPAGE_TMPFS_HUGE_WITHIN_SIZE)\nmm/shmem.c-593-#define TMPFS_HUGE_DEFAULT SHMEM_HUGE_WITHIN_SIZE\nmm/shmem.c:594:#elif defined(CONFIG_TRANSPARENT_HUGEPAGE_TMPFS_HUGE_ADVISE)\nmm/shmem.c-595-#define TMPFS_HUGE_DEFAULT SHMEM_HUGE_ADVISE\n--\nmm/shmem.c=1047=static long shmem_unused_huge_count(struct super_block *sb,\n--\nmm/shmem.c-1053-}\nmm/shmem.c:1054:#else /* !CONFIG_TRANSPARENT_HUGEPAGE */\nmm/shmem.c-1055-\n--\nmm/shmem.c=1073=static unsigned int shmem_huge_global_enabled(struct inode *inode, pgoff_t index,\n--\nmm/shmem.c-1079-}\nmm/shmem.c:1080:#endif /* CONFIG_TRANSPARENT_HUGEPAGE */\nmm/shmem.c-1081-\n--\nmm/shmem.c=1798=int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,\n--\nmm/shmem.c-1840-\nmm/shmem.c:1841:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-1842-\t\tif (order \u003e= HPAGE_PMD_ORDER) {\n--\nmm/shmem.c=1995=static struct folio *shmem_swapin_cluster(swp_entry_t swap, gfp_t gfp,\n--\nmm/shmem.c-2008-\nmm/shmem.c:2009:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-2010-bool shmem_hpage_pmd_enabled(void)\n--\nmm/shmem.c=2108=static unsigned long shmem_suitable_orders(struct inode *inode, struct vm_fault *vmf,\n--\nmm/shmem.c-2113-}\nmm/shmem.c:2114:#endif /* CONFIG_TRANSPARENT_HUGEPAGE */\nmm/shmem.c-2115-\n--\nmm/shmem.c=2130=static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,\n--\nmm/shmem.c-2141-\nmm/shmem.c:2142:\tif (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))\nmm/shmem.c-2143-\t\torders = 0;\n--\nmm/shmem.c=2280=static int shmem_replace_folio(struct folio **foliop, gfp_t gfp,\n--\nmm/shmem.c-2294-\tgfp \u0026= ~GFP_CONSTRAINT_MASK;\nmm/shmem.c:2295:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-2296-\tif (nr_pages \u003e 1) {\n--\nmm/shmem.c=2918=unsigned long shmem_get_unmapped_area(struct file *file,\n--\nmm/shmem.c-2933-\nmm/shmem.c:2934:\tif (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))\nmm/shmem.c-2935-\t\treturn addr;\n--\nmm/shmem.c-2977-\t\t\t */\nmm/shmem.c:2978:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-2979-\t\t\thpage_orders = READ_ONCE(huge_shmem_orders_always);\n--\nmm/shmem.c=3203=static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,\n--\nmm/shmem.c-3242-\tINIT_LIST_HEAD(\u0026info-\u003eshrinklist);\nmm/shmem.c:3243:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-3244-\tinfo-\u003eshrinklist_memcg = NULL;\n--\nmm/shmem.c=4716=static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)\n--\nmm/shmem.c-4785-\t\tif (ctx-\u003ehuge != SHMEM_HUGE_NEVER \u0026\u0026\nmm/shmem.c:4786:\t\t !(IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) \u0026\u0026\nmm/shmem.c-4787-\t\t has_transparent_hugepage()))\n--\nmm/shmem.c=5026=static int shmem_show_options(struct seq_file *seq, struct dentry *root)\n--\nmm/shmem.c-5065-\t\tseq_printf(seq, \",inode%d\", (sbinfo-\u003efull_inums ? 64 : 32));\nmm/shmem.c:5066:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-5067-\t/* Rightly or wrongly, show huge mount option unmasked by shmem_huge */\n--\nmm/shmem.c=5099=static void shmem_put_super(struct super_block *sb)\n--\nmm/shmem.c-5112-\tpercpu_counter_destroy(\u0026sbinfo-\u003eused_blocks);\nmm/shmem.c:5113:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-5114-\tlist_lru_destroy(\u0026sbinfo-\u003eshrinklist);\n--\nmm/shmem.c=5131=static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)\n--\nmm/shmem.c-5196-\tsbinfo-\u003emode = ctx-\u003emode;\nmm/shmem.c:5197:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-5198-\tif (ctx-\u003eseen \u0026 SHMEM_SEEN_HUGE)\n--\nmm/shmem.c-5209-\nmm/shmem.c:5210:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-5211-\tif (list_lru_init_memcg(\u0026sbinfo-\u003eshrinklist, sb-\u003es_shrink))\n--\nmm/shmem.c=5417=static const struct super_operations shmem_ops = {\n--\nmm/shmem.c-5430-\t.put_super\t= shmem_put_super,\nmm/shmem.c:5431:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-5432-\t.nr_cached_objects\t= shmem_unused_huge_count,\n--\nmm/shmem.c=5556=void __init shmem_init(void)\n--\nmm/shmem.c-5586-\nmm/shmem.c:5587:#ifdef CONFIG_TRANSPARENT_HUGEPAGE\nmm/shmem.c-5588-\tif (has_transparent_hugepage() \u0026\u0026 shmem_huge \u003e SHMEM_HUGE_DENY)\n--\nmm/shmem.c-5611-\nmm/shmem.c:5612:#if defined(CONFIG_TRANSPARENT_HUGEPAGE) \u0026\u0026 defined(CONFIG_SYSFS)\nmm/shmem.c-5613-static ssize_t shmem_enabled_show(struct kobject *kobj,\n--\nmm/shmem.c=5767=struct kobj_attribute thpsize_shmem_enabled_attr =\nmm/shmem.c-5768-\t__ATTR(shmem_enabled, 0644, thpsize_shmem_enabled_show, thpsize_shmem_enabled_store);\nmm/shmem.c:5769:#endif /* CONFIG_TRANSPARENT_HUGEPAGE \u0026\u0026 CONFIG_SYSFS */\nmm/shmem.c-5770-\nmm/shmem.c:5771:#if defined(CONFIG_TRANSPARENT_HUGEPAGE)\nmm/shmem.c-5772-\n--\nmm/shmem.c=5902=__setup(\"thp_shmem=\", setup_thp_shmem);\nmm/shmem.c-5903-\nmm/shmem.c:5904:#endif /* CONFIG_TRANSPARENT_HUGEPAGE */\nmm/shmem.c-5905-\n"
} |
| 5/2 | 2026/07/22 00:24 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13475 output: 30 thoughts: 25 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating THP and cgroups** Right, so we're talking transparent hugepages (`CONFIG_TRANSPARENT_HUGEPAGE`). That's good, that's what we want for performance in many workloads. But my mind immediately jumps to `CONFIG_MEMCG`. If we're using THP, especially in any kind of containerized or virtualized environment, I *need* to understand the interplay with memory cgroups. Are we properly accounting for THP usage within those cgroup limits? Is the scheduler being NUMA-aware enough in its placement of hugepages within a memcg? What about overcommit – how does the system handle a situation where a memcg requests more THP than is available, and what are the repercussions? We're potentially talking about significant performance implications here, depending on how these two features interact. I need to dig into the documentation and source code to see how these configurations behave together. I need to understand if there are any known gotchas or tuning parameters that are critical to optimizing this setup. This is a critical point that needs to be addressed during a system audit. |
| 6/2 | 2026/07/22 00:24 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_MEMCG",
"PathPrefix": "mm/shmem.c"
}Results: {
"Output": "mm/shmem.c=738=static struct mem_cgroup *shmem_get_and_clear_memcg(struct shmem_inode_info *info)\n--\nmm/shmem.c-746-\nmm/shmem.c:747:#ifdef CONFIG_MEMCG\nmm/shmem.c-748-static struct mem_cgroup *\n"
} |
| 7/2 | 2026/07/22 00:24 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13887 output: 39 thoughts: 28 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into Btrfs Superblock Handling** Okay, so I'm thinking, where's the best place to find that `mem_cgroup_shrink_is_root` check within the Btrfs codebase? I know it deals with memory cgroups, which means I should be looking at the superblock code, particularly where it handles things like metadata. Right, that puts me squarely in `fs/btrfs/super.c`. That file is the central hub for superblock operations, so it should contain the relevant context, or at least the calls I need to trace. Now, time to start digging in there. Let's get to it. |
| 8/2 | 2026/07/22 00:24 | tool | grepper |
0mArgs:{
"Expression": "mem_cgroup_shrink_is_root",
"PathPrefix": "fs/btrfs/super.c"
}Results: {
"Output": "fs/btrfs/super.c=2433=static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_control *sc)\n--\nfs/btrfs/super.c-2443-\t */\nfs/btrfs/super.c:2444:\tif (!mem_cgroup_shrink_is_root(sc))\nfs/btrfs/super.c-2445-\t\treturn 0;\n"
} |
| 9/2 | 2026/07/22 00:24 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14239 output: 38 thoughts: 212 Thoughts: **Fuzzing Target Identification** Okay, so this patch looks promising – definitely something worth throwing a fuzzer at. My initial focus will be on these functions: `shmem_unused_huge_shrink`, `shmem_unused_huge_scan`, `shmem_unused_huge_count`, `shmem_unused_huge_add`, `shmem_unused_huge_del`, `shmem_unused_huge_isolate`, `shmem_unused_huge_requeue`, `shmem_unused_huge_drop`, and `btrfs_nr_cached_objects`. I need to understand how these interact and identify potential edge cases. Let me just quickly confirm that `shmem_unused_huge_add` is static... yes, good, that's what I expected. Now I know to watch out for interactions with other files. The relevant kernel configs here are `TRANSPARENT_HUGEPAGE`, `MEMCG`, and `BTRFS_FS`. This gives me a good framework to know what to expect and where to look for dependencies. I'll glance at `fs/btrfs/super.c` next to understand `btrfs_nr_cached_objects` in more detail and see how it interacts with the hugepage code. |
| 10/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "btrfs_nr_cached_objects",
"PathPrefix": "fs/btrfs/super.c"
}Results: {
"Output": "fs/btrfs/super.c=2417=static int btrfs_show_devname(struct seq_file *m, struct dentry *root)\n--\nfs/btrfs/super.c-2432-\nfs/btrfs/super.c:2433:static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_control *sc)\nfs/btrfs/super.c-2434-{\n--\nfs/btrfs/super.c=2533=static const struct super_operations btrfs_super_ops = {\n--\nfs/btrfs/super.c-2545-\t.unfreeze_fs\t= btrfs_unfreeze,\nfs/btrfs/super.c:2546:\t.nr_cached_objects = btrfs_nr_cached_objects,\nfs/btrfs/super.c-2547-\t.free_cached_objects = btrfs_free_cached_objects,\n"
} |
| 11/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15028 output: 34 thoughts: 279 Thoughts: **Analyzing the Changes for Memory Management** Okay, so the changes in `fs/btrfs/super.c` are pretty inconsequential - just a simple early return. The real meat of the update seems to be in `mm/shmem.c`, specifically the introduction of `list_lru` for the `shrinklist`. That means I really need to focus my attention on the `shmem_unused_huge_*` functions. I need to confirm if these functions are properly scoped, given the change. Let me go through them: `shmem_unused_huge_shrink` is static? Yep. `shmem_unused_huge_scan`? Static, check. `shmem_unused_huge_count`? Static, as expected. `shmem_unused_huge_add`? Yup, static. `shmem_unused_huge_del`, static. `shmem_unused_huge_isolate`, and `shmem_unused_huge_requeue`, as well as `shmem_unused_huge_drop` are static as well. Okay, all looks good there. Now, let's verify where `shmem_unused_huge_shrink` is actually defined, just to be sure. |
| 12/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "shmem_unused_huge_shrink",
"PathPrefix": "mm/shmem.c"
}Results: {
"Output": "mm/shmem.c=909=static void shmem_unused_huge_requeue(struct inode *inode, struct folio *folio)\n--\nmm/shmem.c-935-\nmm/shmem.c:936:static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\nmm/shmem.c-937-\t\tstruct shrink_control *sc, unsigned long nr_to_free)\n--\nmm/shmem.c=1036=static long shmem_unused_huge_scan(struct super_block *sb,\n--\nmm/shmem.c-1043-\nmm/shmem.c:1044:\treturn shmem_unused_huge_shrink(sbinfo, sc, 0);\nmm/shmem.c-1045-}\n--\nmm/shmem.c=1063=static void shmem_unused_huge_del(struct inode *inode)\n--\nmm/shmem.c-1066-\nmm/shmem.c:1067:static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\nmm/shmem.c-1068-\t\tstruct shrink_control *sc, unsigned long nr_to_free)\n--\nmm/shmem.c=2130=static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,\n--\nmm/shmem.c-2203-\t\t */\nmm/shmem.c:2204:\t\tshmem_unused_huge_shrink(sbinfo, NULL, pages);\nmm/shmem.c-2205-\t\t/*\n"
} |
| 13/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16032 output: 34 thoughts: 25 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Checking the SHMEM Implementation** Alright, let's get down to brass tacks. I need to verify something about the shared memory implementation within the kernel. Specifically, I'm trying to get a handle on how unused huge pages are handled in shared memory regions. So, the first logical step is to pinpoint the function responsible for this – I'm pretty sure it's called `shmem_unused_huge_scan`. Time to dive into the codebase. I know the shared memory subsystem is housed under `mm/`. That's where all the memory management magic happens. And, considering the name, I'd bet the source file I'm after is `shmem.c`. So, first things first, let's open that file and make sure `shmem_unused_huge_scan` is actually defined there. This is basic, but a good starting point to confirm my assumptions and get a mental roadmap of the code involved. Let's see what we've got. |
| 14/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "shmem_unused_huge_scan",
"PathPrefix": "mm/shmem.c"
}Results: {
"Output": "mm/shmem.c=813=static void shmem_unused_huge_del(struct inode *inode)\n--\nmm/shmem.c-829-\nmm/shmem.c:830:struct shmem_unused_huge_scan {\nmm/shmem.c-831-\tstruct list_head list;\n--\nmm/shmem.c=835=static enum lru_status shmem_unused_huge_isolate(struct list_head *item,\n--\nmm/shmem.c-838-{\nmm/shmem.c:839:\tstruct shmem_unused_huge_scan *scan = arg;\nmm/shmem.c-840-\tstruct shmem_inode_info *info;\n--\nmm/shmem.c=936=static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\n--\nmm/shmem.c-938-{\nmm/shmem.c:939:\tstruct shmem_unused_huge_scan scan;\nmm/shmem.c-940-\tstruct inode *inode;\n--\nmm/shmem.c-1035-\nmm/shmem.c:1036:static long shmem_unused_huge_scan(struct super_block *sb,\nmm/shmem.c-1037-\t\tstruct shrink_control *sc)\n--\nmm/shmem.c=5417=static const struct super_operations shmem_ops = {\n--\nmm/shmem.c-5432-\t.nr_cached_objects\t= shmem_unused_huge_count,\nmm/shmem.c:5433:\t.free_cached_objects\t= shmem_unused_huge_scan,\nmm/shmem.c-5434-#endif\n"
} |
| 15/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16781 output: 36 thoughts: 161 Thoughts: **My Focus: Unused Huge Page Management in Shared Memory** Okay, so I need to get a handle on the key functions involved in managing unused huge pages within the shared memory subsystem. Specifically, my attention needs to be on these: `shmem_unused_huge_shrink`, `shmem_unused_huge_scan`, `shmem_unused_huge_count`, `shmem_unused_huge_add`, `shmem_unused_huge_del`, `shmem_unused_huge_isolate`, `shmem_unused_huge_requeue`, `shmem_unused_huge_drop`, `is_shmem_unused_huge_match`, and `shmem_unused_huge_alloc_lru`. These are the core building blocks, and understanding their interactions is going to be crucial for my analysis. The first thing I'm going to look for is `is_shmem_unused_huge_match`. I need to confirm where this is defined, I'll start by checking `mm/shmem.c` to see if that's where it's located. Then I'll dive into each of those other functions, mapping them out. |
| 16/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "is_shmem_unused_huge_match",
"PathPrefix": "mm/shmem.c"
}Results: {
"Output": "mm/shmem.c=835=static enum lru_status shmem_unused_huge_isolate(struct list_head *item,\n--\nmm/shmem.c-873-\nmm/shmem.c:874:static bool is_shmem_unused_huge_match(struct folio *folio,\nmm/shmem.c-875-\t\t\t\t struct shrink_control *sc)\n--\nmm/shmem.c=936=static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,\n--\nmm/shmem.c-981-\nmm/shmem.c:982:\t\tif (!is_shmem_unused_huge_match(folio, scan.sc)) {\nmm/shmem.c-983-\t\t\tshmem_unused_huge_requeue(inode, folio);\n--\nmm/shmem.c-1006-\nmm/shmem.c:1007:\t\tif (!is_shmem_unused_huge_match(folio, scan.sc)) {\nmm/shmem.c-1008-\t\t\tfolio_unlock(folio);\n"
} |
| 17/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17521 output: 37 thoughts: 28 Thoughts: Okay, let's see. My first thought is: **Where is this function defined, and can I find it quickly?** I need to locate `shmem_unused_huge_alloc_lru`. My immediate instinct is to check the most logical place, and based on my knowledge of the kernel's memory management structure, `mm/shmem.c` seems like the likely candidate. That's the first place I'll start my search. It's almost certain it's in a file related to shared memory, and within the memory management subsystem, that's almost always where it lives. |
| 18/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "shmem_unused_huge_alloc_lru",
"PathPrefix": "mm/shmem.c"
}Results: {
"Output": "mm/shmem.c=748=static struct mem_cgroup *\nmm/shmem.c:749:shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,\nmm/shmem.c-750-\t\t\t gfp_t gfp)\n--\nmm/shmem.c=768=static struct mem_cgroup *\nmm/shmem.c:769:shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,\nmm/shmem.c-770-\t\t\t gfp_t gfp)\n--\nmm/shmem.c=776=static void shmem_unused_huge_add(struct inode *inode, struct folio *folio,\n--\nmm/shmem.c-783-\nmm/shmem.c:784:\tmemcg = shmem_unused_huge_alloc_lru(sbinfo, folio, gfp);\nmm/shmem.c-785-\tif (IS_ERR(memcg))\n--\nmm/shmem.c=909=static void shmem_unused_huge_requeue(struct inode *inode, struct folio *folio)\n--\nmm/shmem.c-915-\nmm/shmem.c:916:\tmemcg = shmem_unused_huge_alloc_lru(sbinfo, folio, GFP_NOWAIT);\nmm/shmem.c-917-\tif (IS_ERR(memcg))\n"
} |
| 19/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18107 output: 38 thoughts: 66 Thoughts: Here's my take on those thoughts: **Analyzing Kernel Configuration and Btrfs Optimization** Okay, so I'm looking at these kernel configs – `TRANSPARENT_HUGEPAGE` and `MEMCG`, which is standard enough for optimization efforts. But hold on, I see `BTRFS_FS` changed as well. That immediately piques my interest. Btrfs is an area where performance tuning can be quite involved, and any config modification suggests a potential area for improvement or a specific use case consideration. My next step is to quickly drill down and see *why* the Btrfs filesystem is touched. To understand the context, I'm now going to confirm if `btrfs_nr_cached_objects` is related to the Btrfs changes. It's time to check `fs/btrfs/super.c`. That's where I would expect to find related logic if this config change is relevant to object caching within the Btrfs filesystems. Depending on what I find, this would help me form my analysis. |
| 20/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "btrfs_nr_cached_objects",
"PathPrefix": "fs/btrfs/super.c"
}Results: {
"Output": "fs/btrfs/super.c=2417=static int btrfs_show_devname(struct seq_file *m, struct dentry *root)\n--\nfs/btrfs/super.c-2432-\nfs/btrfs/super.c:2433:static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_control *sc)\nfs/btrfs/super.c-2434-{\n--\nfs/btrfs/super.c=2533=static const struct super_operations btrfs_super_ops = {\n--\nfs/btrfs/super.c-2545-\t.unfreeze_fs\t= btrfs_unfreeze,\nfs/btrfs/super.c:2546:\t.nr_cached_objects = btrfs_nr_cached_objects,\nfs/btrfs/super.c-2547-\t.free_cached_objects = btrfs_free_cached_objects,\n"
} |
| 21/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18699 output: 27 thoughts: 781 Thoughts: **Analyzing and Integrating Memory Management Changes** Okay, so the changes in `fs/btrfs/super.c` are pretty minor, but let's fold `btrfs_nr_cached_objects` into the focus symbols. Wait, hold on... the patch description mentions something about this being a global counter, not honoring `sc->memcg`, and being for the global shrink path. So, this looks like a small optimization or fix. The bulk of the logic is actually in `mm/shmem.c`, so that's where the interesting stuff lies. Right, time to dig into the `shmem` functions. I'll quickly verify the static status of each of these crucial functions within the shmem subsystem: `shmem_unused_huge_add`, `shmem_unused_huge_del`, `shmem_unused_huge_isolate`, `shmem_unused_huge_requeue`, `shmem_unused_huge_drop`, `is_shmem_unused_huge_match`, `shmem_unused_huge_alloc_lru`, `shmem_unused_huge_shrink`, `shmem_unused_huge_scan`, and `shmem_unused_huge_count`. Okay, all static. Good. I'll also check `shmem_get_and_clear_memcg`, `is_shmem_unused_huge_isolated` and `set_shmem_unused_huge_isolated`. All are static. Perfect. Now, let's include all those functions I just confirmed are static into `FocusSymbols`. That makes it, for sure, easier to track this logic. So we have: - `shmem_unused_huge_shrink` - `shmem_unused_huge_scan` - `shmem_unused_huge_count` - `shmem_unused_huge_add` - `shmem_unused_huge_del` - `shmem_unused_huge_isolate` - `shmem_unused_huge_requeue` - `shmem_unused_huge_drop` - `is_shmem_unused_huge_match` - `shmem_unused_huge_alloc_lru` - `shmem_get_and_clear_memcg` - `is_shmem_unused_huge_isolated` - `set_shmem_unused_huge_isolated` And, since we're here, we can go ahead and include `btrfs_nr_cached_objects` too. Now, let's consider the configuration aspects. We have `TRANSPARENT_HUGEPAGE`, `MEMCG`, and `BTRFS_FS` as relevant. I also want to check whether `CONFIG_SHMEM` is needed. It's often enabled by default, but let's be thorough. Wait, it's not explicitly *required*, but `TRANSPARENT_HUGEPAGE` and `MEMCG` *are* part of the necessary config. I should also see whether `CONFIG_TMPFS` is needed. Let's trace it through `mm/shmem.c`, which is compiled when `CONFIG_SHMEM` is enabled, and then check `mm/Makefile` to confirm the dependencies. |
| 22/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "shmem.o",
"PathPrefix": "mm/Makefile"
}Results: {
"Output": "mm/Makefile=51=obj-y\t\t\t:= filemap.o mempool.o oom_kill.o fadvise.o \\\n--\nmm/Makefile-53-\t\t\t readahead.o folio.o truncate.o vmscan.o shrinker.o \\\nmm/Makefile:54:\t\t\t shmem.o util.o mmzone.o vmstat.o backing-dev.o \\\nmm/Makefile-55-\t\t\t mm_init.o percpu.o slab_common.o \\\n"
} |
| 23/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20350 output: 30 thoughts: 33 Thoughts: Okay, here's my interpretation of those thoughts, summarized from an expert's perspective: **Investigating the `shmem.o` Build Process** Right, `shmem.o` is a core part of the system, so its always compiled, as expected. But, I'm just quickly confirming my understanding by checking where that's actually driven. Ah, interesting, I see the `CONFIG_SHMEM` preprocessor directive is used *within* `mm/shmem.c`. That's the key. That tells me that compilation of that module is conditioned on that config option. Now, that's what I needed. |
| 24/2 | 2026/07/22 00:25 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_SHMEM",
"PathPrefix": "mm/shmem.c"
}Results: {
"Output": "mm/shmem.c=46=static struct vfsmount *shm_mnt __ro_after_init;\nmm/shmem.c-47-\nmm/shmem.c:48:#ifdef CONFIG_SHMEM\nmm/shmem.c-49-/*\n--\nmm/shmem.c=5902=__setup(\"thp_shmem=\", setup_thp_shmem);\n--\nmm/shmem.c-5905-\nmm/shmem.c:5906:#else /* !CONFIG_SHMEM */\nmm/shmem.c-5907-\n--\nmm/shmem.c=5975=static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap,\n--\nmm/shmem.c-5982-\nmm/shmem.c:5983:#endif /* CONFIG_SHMEM */\nmm/shmem.c-5984-\n--\nmm/shmem.c=6137=struct folio *shmem_read_folio_gfp(struct address_space *mapping,\n--\nmm/shmem.c-6139-{\nmm/shmem.c:6140:#ifdef CONFIG_SHMEM\nmm/shmem.c-6141-\tstruct inode *inode = mapping-\u003ehost;\n"
} |
| 25/2 | 2026/07/22 00:25 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20886 output: 329 thoughts: 68 Thoughts: Okay, here's my take on those configurations, running through my mental checklist: **My Configuration Considerations** Alright, so `CONFIG_SHMEM` is definitely in play here. That's a solid choice, so it's good to include it. I'm going to want to make sure I note that. Then, we've also got `CONFIG_TRANSPARENT_HUGEPAGE` and `CONFIG_MEMCG`. Those are pretty standard, and probably being driven by performance and resource management requirements respectively. Again, makes sense and good to know they are active. Finally, `CONFIG_BTRFS_FS` is present. I'm assuming there is a strategic choice for that filesystem, perhaps related to snapshots, data integrity, or general system management requirements. I'm going to have to check in to understand what is being driven by that inclusion. Right, I'll need to remember all of these, so I will now call `set-results`. |
| 26/2 | 2026/07/22 00:25 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"TRANSPARENT_HUGEPAGE",
"MEMCG",
"BTRFS_FS",
"SHMEM"
],
"FocusSymbols": [
"shmem_unused_huge_shrink",
"shmem_unused_huge_scan",
"shmem_unused_huge_count",
"shmem_unused_huge_add",
"shmem_unused_huge_del",
"shmem_unused_huge_isolate",
"shmem_unused_huge_requeue",
"shmem_unused_huge_drop",
"is_shmem_unused_huge_match",
"shmem_unused_huge_alloc_lru",
"shmem_get_and_clear_memcg",
"is_shmem_unused_huge_isolated",
"set_shmem_unused_huge_isolated",
"btrfs_nr_cached_objects"
],
"Reasoning": "The patch modifies how shmem handles unused huge pages when shrinking, introducing list_lru for shrinklist in shmem_sb_info. It also modifies super_cache_scan and super_cache_count in fs/super.c to remove super_fs_objects_eligible and instead uses mem_cgroup_shrink_is_root in btrfs_nr_cached_objects. These are functional changes to core memory management and filesystem logic, making them highly reachable and worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"TRANSPARENT_HUGEPAGE",
"MEMCG",
"BTRFS_FS",
"SHMEM"
],
"FocusSymbols": [
"shmem_unused_huge_shrink",
"shmem_unused_huge_scan",
"shmem_unused_huge_count",
"shmem_unused_huge_add",
"shmem_unused_huge_del",
"shmem_unused_huge_isolate",
"shmem_unused_huge_requeue",
"shmem_unused_huge_drop",
"is_shmem_unused_huge_match",
"shmem_unused_huge_alloc_lru",
"shmem_get_and_clear_memcg",
"is_shmem_unused_huge_isolated",
"set_shmem_unused_huge_isolated",
"btrfs_nr_cached_objects"
],
"Reasoning": "The patch modifies how shmem handles unused huge pages when shrinking, introducing list_lru for shrinklist in shmem_sb_info. It also modifies super_cache_scan and super_cache_count in fs/super.c to remove super_fs_objects_eligible and instead uses mem_cgroup_shrink_is_root in btrfs_nr_cached_objects. These are functional changes to core memory management and filesystem logic, making them highly reachable and worth fuzzing.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|