AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/17 18:11 flow patch-triage
3m Args:
null

Results:
{
  "EnableConfigs": [
    "DRM_NOUVEAU",
    "BPF_SYSCALL",
    "PROVE_LOCKING",
    "NF_TABLES"
  ],
  "FocusSymbols": [
    "folio_scratchpad_alloc",
    "folio_scratchpad_reset",
    "folio_scratchpad_free",
    "folio_pool_init_align",
    "folio_pool_init_key",
    "folio_pool_init",
    "folio_scratchpad_stats",
    "bpf_check",
    "generic_map_update_batch",
    "drm_gpuvm_sm_map",
    "drm_gpuvm_sm_unmap",
    "drm_gpuvm_init"
  ],
  "KMSANReasoning": "The patch introduces a new memory allocator (`folio_pool` and `folio_scratchpad`) and replaces various `kzalloc`, `kmalloc`, `kvmalloc`, and `krealloc` calls with the new allocator's functions. The core allocation function `folio_scratchpad_alloc` explicitly zeroes the allocated memory using `memset(elem, 0, size)`. For reallocations, `folio_pool_realloc` behaves identically to `krealloc` without `__GFP_ZERO`, leaving newly expanded memory uninitialized just as the original code did. In some cases (like `generic_map_update_batch`), the patch actually replaces `kvmalloc` (which does not zero memory) with `folio_scratchpad_alloc` (which does), thereby reducing the risk of uninitialized memory usage. Any potential bugs introduced by this custom allocator (such as incorrect padding, remaining size calculations, or chunk management) would manifest as out-of-bounds accesses, memory corruption, or use-after-free bugs, which are effectively detected by KASAN. Since all newly allocated objects are fully zeroed and no new uninitialized memory risks or info-leaks are introduced, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch introduces a new direct-map large folio bump allocator (`folio_pool` and `folio_scratchpad`) and integrates it into several core kernel subsystems: DRM GPUVM, BPF syscalls/verifier, lockdep, and nf_tables. These changes modify how memory is allocated and managed in these subsystems, which has a significant functional impact and could introduce memory corruption, use-after-free, or memory leak vulnerabilities. The code is reachable from user space via BPF syscalls, netlink messages (nf_tables), and DRM ioctls. Therefore, it is highly worth fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/08/17 18:11 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit c1582d0063f2c118dce289575969335d8319a7e1\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 17 18:11:30 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c\nindex c422c5af1f4bf..6bb82ac9e2d6c 100644\n--- a/drivers/gpu/drm/drm_gpuvm.c\n+++ b/drivers/gpu/drm/drm_gpuvm.c\n@@ -1109,6 +1109,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\n \tspin_lock_init(\u0026gpuvm-\u003eevict.lock);\n \n \tinit_llist_head(\u0026gpuvm-\u003ebo_defer);\n+\tfolio_scratchpad_init_key(\u0026gpuvm-\u003eva_scratchpad, get_order(SZ_64K),\n+\t\t\t\t  \u0026gpuvm_scratchpad_key);\n \n \tkref_init(\u0026gpuvm-\u003ekref);\n \n@@ -1155,6 +1157,7 @@ drm_gpuvm_fini(struct drm_gpuvm *gpuvm)\n \tdrm_WARN(gpuvm-\u003edrm, !llist_empty(\u0026gpuvm-\u003ebo_defer),\n \t\t \"VM BO cleanup list should be empty.\\n\");\n \n+\tfolio_scratchpad_free(\u0026gpuvm-\u003eva_scratchpad);\n \tdrm_gem_object_put(gpuvm-\u003er_obj);\n }\n \n@@ -2849,6 +2852,9 @@ drm_gpuvm_sm_unmap_exec_lock(struct drm_gpuvm *gpuvm, struct drm_exec *exec,\n }\n EXPORT_SYMBOL_GPL(drm_gpuvm_sm_unmap_exec_lock);\n \n+DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(gpuvm_scratchpad_key, va_scratchpad,\n+\t\t\t\t   \"Toggle DRM GPUVM range folio scratchpad allocator\");\n+\n static struct drm_gpuva_op *\n gpuva_op_alloc(struct drm_gpuvm *gpuvm)\n {\n@@ -2858,7 +2864,8 @@ gpuva_op_alloc(struct drm_gpuvm *gpuvm)\n \tif (fn \u0026\u0026 fn-\u003eop_alloc)\n \t\top = fn-\u003eop_alloc();\n \telse\n-\t\top = kzalloc_obj(*op);\n+\t\top = folio_scratchpad_alloc_obj(gpuvm, va_scratchpad,\n+\t\t\t\t\t\tstruct drm_gpuva_op, GFP_KERNEL);\n \n \tif (unlikely(!op))\n \t\treturn NULL;\n@@ -2875,7 +2882,7 @@ gpuva_op_free(struct drm_gpuvm *gpuvm,\n \tif (fn \u0026\u0026 fn-\u003eop_free)\n \t\tfn-\u003eop_free(op);\n \telse\n-\t\tkfree(op);\n+\t\tfolio_scratchpad_free_elem(op);\n }\n \n static int\ndiff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h\nindex 655bd9104ffb2..362b1d88d30d8 100644\n--- a/include/drm/drm_gpuvm.h\n+++ b/include/drm/drm_gpuvm.h\n@@ -30,6 +30,7 @@\n #include \u003clinux/llist.h\u003e\n #include \u003clinux/rbtree.h\u003e\n #include \u003clinux/types.h\u003e\n+#include \u003clinux/folio_pool.h\u003e\n \n #include \u003cdrm/drm_device.h\u003e\n #include \u003cdrm/drm_gem.h\u003e\n@@ -338,6 +339,11 @@ struct drm_gpuvm {\n \t * @bo_defer: structure holding vm_bos that need to be destroyed\n \t */\n \tstruct llist_head bo_defer;\n+\n+\t/**\n+\t * @va_scratchpad: folio scratchpad for transient GPU VA range allocations\n+\t */\n+\tstruct folio_scratchpad va_scratchpad;\n };\n \n void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\ndiff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h\nindex 39a851e690ec4..43f5f0eaffac6 100644\n--- a/include/linux/bpf_verifier.h\n+++ b/include/linux/bpf_verifier.h\n@@ -898,6 +898,8 @@ struct bpf_scc_info {\n \n struct bpf_liveness;\n \n+#include \u003clinux/folio_pool.h\u003e\n+\n /* single container for all structs\n  * one verifier_env per bpf_check() call\n  */\n@@ -907,6 +909,7 @@ struct bpf_verifier_env {\n \tstruct bpf_prog *prog;\t\t/* eBPF program being verified */\n \tconst struct bpf_verifier_ops *ops;\n \tstruct module *attach_btf_mod;\t/* The owner module of prog-\u003eaux-\u003eattach_btf */\n+\tstruct folio_pool state_pool;\t/* pool for transient state nodes */\n \tstruct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */\n \tint stack_size;\t\t\t/* number of states to be processed */\n \tbool strict_alignment;\t\t/* perform strict pointer alignment checks */\ndiff --git a/include/linux/folio_pool.h b/include/linux/folio_pool.h\nnew file mode 100644\nindex 0000000000000..5bbca8818877c\n--- /dev/null\n+++ b/include/linux/folio_pool.h\n@@ -0,0 +1,279 @@\n+/* SPDX-License-Identifier: GPL-2.0+ */\n+#ifndef _LINUX_FOLIO_POOL_H\n+#define _LINUX_FOLIO_POOL_H\n+\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/list.h\u003e\n+#include \u003clinux/spinlock.h\u003e\n+#include \u003clinux/page-flags.h\u003e\n+#include \u003clinux/mm.h\u003e\n+#include \u003clinux/gfp.h\u003e\n+#include \u003clinux/jump_label.h\u003e\n+#include \u003clinux/moduleparam.h\u003e\n+#include \u003clinux/cleanup.h\u003e\n+\n+DECLARE_STATIC_KEY_TRUE(folio_pool_enabled_key);\n+\n+#define DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(key_name, param_name, desc)\t\t\\\n+\tDEFINE_STATIC_KEY_TRUE(key_name);\t\t\t\t\t\\\n+\tstatic int key_name##_set(const char *val, const struct kernel_param *kp) \\\n+\t{\t\t\t\t\t\t\t\t\t\\\n+\t\tbool enable;\t\t\t\t\t\t\t\\\n+\t\tint ret = kstrtobool(val, \u0026enable);\t\t\t\t\\\n+\t\tif (ret)\t\t\t\t\t\t\t\\\n+\t\t\treturn ret;\t\t\t\t\t\t\\\n+\t\tif (enable)\t\t\t\t\t\t\t\\\n+\t\t\tstatic_branch_enable(\u0026key_name);\t\t\t\\\n+\t\telse\t\t\t\t\t\t\t\t\\\n+\t\t\tstatic_branch_disable(\u0026key_name);\t\t\t\\\n+\t\treturn 0;\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\\\n+\tstatic int key_name##_get(char *buffer, const struct kernel_param *kp)\t\\\n+\t{\t\t\t\t\t\t\t\t\t\\\n+\t\treturn sprintf(buffer, \"%c\\n\",\t\t\t\t\t\\\n+\t\t\t       static_branch_likely(\u0026key_name) ? 'Y' : 'N');\t\\\n+\t}\t\t\t\t\t\t\t\t\t\\\n+\tstatic const struct kernel_param_ops key_name##_ops = {\t\t\t\\\n+\t\t.set = key_name##_set,\t\t\t\t\t\t\\\n+\t\t.get = key_name##_get,\t\t\t\t\t\t\\\n+\t};\t\t\t\t\t\t\t\t\t\\\n+\tmodule_param_cb(param_name, \u0026key_name##_ops, NULL, 0644);\t\t\\\n+\tMODULE_PARM_DESC(param_name, desc)\n+\n+#define DEFINE_FOLIO_POOL_STATIC_KEY_PARAM_FALSE(key_name, param_name, desc)\t\\\n+\tDEFINE_STATIC_KEY_FALSE(key_name);\t\t\t\t\t\\\n+\tstatic int key_name##_set(const char *val, const struct kernel_param *kp) \\\n+\t{\t\t\t\t\t\t\t\t\t\\\n+\t\tbool enable;\t\t\t\t\t\t\t\\\n+\t\tint ret = kstrtobool(val, \u0026enable);\t\t\t\t\\\n+\t\tif (ret)\t\t\t\t\t\t\t\\\n+\t\t\treturn ret;\t\t\t\t\t\t\\\n+\t\tif (enable)\t\t\t\t\t\t\t\\\n+\t\t\tstatic_branch_enable(\u0026key_name);\t\t\t\\\n+\t\telse\t\t\t\t\t\t\t\t\\\n+\t\t\tstatic_branch_disable(\u0026key_name);\t\t\t\\\n+\t\treturn 0;\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\\\n+\tstatic int key_name##_get(char *buffer, const struct kernel_param *kp)\t\\\n+\t{\t\t\t\t\t\t\t\t\t\\\n+\t\treturn sprintf(buffer, \"%c\\n\",\t\t\t\t\t\\\n+\t\t\t       static_branch_unlikely(\u0026key_name) ? 'Y' : 'N');\t\\\n+\t}\t\t\t\t\t\t\t\t\t\\\n+\tstatic const struct kernel_param_ops key_name##_ops = {\t\t\t\\\n+\t\t.set = key_name##_set,\t\t\t\t\t\t\\\n+\t\t.get = key_name##_get,\t\t\t\t\t\t\\\n+\t};\t\t\t\t\t\t\t\t\t\\\n+\tmodule_param_cb(param_name, \u0026key_name##_ops, NULL, 0644);\t\t\\\n+\tMODULE_PARM_DESC(param_name, desc)\n+\n+#define FOLIO_POOL_64K_ORDER (PAGE_SHIFT \u003c 16 ? 16 - PAGE_SHIFT : 0)\n+\n+struct folio_pool_chunk {\n+\tstruct list_head link;\n+\tstruct folio *folio;\n+};\n+\n+/*\n+ * 1. Variable-Sized Scratchpad (Core Bump Allocator Engine)\n+ */\n+struct folio_scratchpad {\n+\tstruct list_head chunks;\n+\tvoid *free_ptr;\n+\tsize_t remaining;\n+\tunsigned int chunk_order;\n+\tstruct static_key *key;\n+\tspinlock_t lock;\n+};\n+\n+#define FOLIO_SCRATCHPAD_INIT(name, _order) {\t\t\t\t\\\n+\t.chunks = LIST_HEAD_INIT((name).chunks),\t\t\t\\\n+\t.chunk_order = (_order),\t\t\t\t\t\\\n+\t.key = NULL,\t\t\t\t\t\t\t\\\n+\t.lock = __SPIN_LOCK_UNLOCKED((name).lock),\t\t\t\\\n+}\n+\n+#define FOLIO_SCRATCHPAD_INIT_KEY(name, _order, _key) {\t\t\t\\\n+\t.chunks = LIST_HEAD_INIT((name).chunks),\t\t\t\\\n+\t.chunk_order = (_order),\t\t\t\t\t\\\n+\t.key = (struct static_key *)(_key),\t\t\t\t\\\n+\t.lock = __SPIN_LOCK_UNLOCKED((name).lock),\t\t\t\\\n+}\n+\n+void folio_scratchpad_init(struct folio_scratchpad *sp, unsigned int order);\n+void folio_scratchpad_init_key(struct folio_scratchpad *sp, unsigned int order,\n+\t\t\t       struct static_key *key);\n+void *folio_scratchpad_alloc(struct folio_scratchpad *sp, size_t size,\n+\t\t\t     size_t align, gfp_t gfp);\n+void folio_scratchpad_reset(struct folio_scratchpad *sp);\n+void folio_scratchpad_free(struct folio_scratchpad *sp);\n+void folio_scratchpad_stats(struct folio_scratchpad *sp, unsigned int *nr_chunks,\n+\t\t\t    size_t *chunk_size, size_t *tail_used);\n+\n+DEFINE_FREE(folio_scratchpad, struct folio_scratchpad *, if (_T) folio_scratchpad_free(_T))\n+\n+/**\n+ * is_folio_pool_ptr - Check whether an address resides in a folio pool/scratchpad\n+ * @ptr: Object pointer to test\n+ *\n+ * Direct-map large folios allocated via folio_alloc() are not slab pages,\n+ * unlike objects returned by kmalloc/kzalloc.\n+ */\n+static inline bool is_folio_pool_ptr(const void *ptr)\n+{\n+\treturn ptr \u0026\u0026 !is_vmalloc_addr(ptr) \u0026\u0026 !folio_test_slab(virt_to_folio(ptr));\n+}\n+\n+/**\n+ * folio_pool_free_elem - Safely release a pool object or SLUB fallback element\n+ * @ptr: Object pointer to release\n+ *\n+ * If @ptr belongs to a direct-map large folio, individual deallocation is a safe\n+ * no-op (reclaimed in bulk by folio_scratchpad_free/reset). If @ptr was allocated\n+ * via SLUB/vmalloc fallback, releases it immediately.\n+ */\n+static inline void folio_pool_free_elem(const void *ptr)\n+{\n+\tif (!ptr || is_folio_pool_ptr(ptr))\n+\t\treturn;\n+\tkvfree(ptr);\n+}\n+\n+static inline void folio_scratchpad_free_elem(const void *ptr)\n+{\n+\tfolio_pool_free_elem(ptr);\n+}\n+\n+/**\n+ * folio_pool_realloc - Reallocate memory for an object, handling pool vs slab backing\n+ * @ptr: Existing object pointer (may be from folio_pool/scratchpad or SLUB)\n+ * @old_size: Size of original object\n+ * @new_size: Desired new size\n+ * @gfp: Allocation flags\n+ *\n+ * If @ptr is SLUB-backed, delegates directly to krealloc(). If @ptr resides\n+ * in a direct-map large folio, allocates a fresh @new_size buffer from SLUB\n+ * and copies @old_size bytes; the original scratchpad slot remains abandoned\n+ * until the entire scratchpad is released or reset at batch boundary.\n+ */\n+static inline void *folio_pool_realloc(void *ptr, size_t old_size,\n+\t\t\t\t       size_t new_size, gfp_t gfp)\n+{\n+\tvoid *new_ptr;\n+\n+\tif (!ptr)\n+\t\treturn kmalloc(new_size, gfp);\n+\n+\tif (!is_folio_pool_ptr(ptr))\n+\t\treturn krealloc(ptr, new_size, gfp);\n+\n+\tnew_ptr = kmalloc(new_size, gfp);\n+\tif (new_ptr)\n+\t\tmemcpy(new_ptr, ptr, min(old_size, new_size));\n+\treturn new_ptr;\n+}\n+\n+static inline void *folio_scratchpad_realloc(void *ptr, size_t old_size,\n+\t\t\t\t\t     size_t new_size, gfp_t gfp)\n+{\n+\treturn folio_pool_realloc(ptr, old_size, new_size, gfp);\n+}\n+\n+/**\n+ * folio_scratchpad_alloc_obj - Allocate a typed object from an embedded scratchpad\n+ * @ptr: Pointer to container struct (e.g. nft_net)\n+ * @member: Name of the struct folio_scratchpad field (e.g. trans_scratchpad)\n+ * @type: Type of object being allocated\n+ * @gfp: Allocation flags\n+ */\n+#define folio_scratchpad_alloc_obj(ptr, member, type, gfp)\t\t\t\\\n+\t((type *)folio_scratchpad_alloc(\u0026(ptr)-\u003emember,\t\t\t\t\\\n+\t\t\t\t\tsizeof(type),\t\t\t\t\\\n+\t\t\t\t\t__alignof__(type),\t\t\t\\\n+\t\t\t\t\tgfp))\n+\n+/**\n+ * folio_scratchpad_alloc_bytes - Allocate variable-sized bytes from an embedded scratchpad\n+ * @ptr: Pointer to container struct (e.g. nft_net)\n+ * @member: Name of the struct folio_scratchpad field (e.g. trans_scratchpad)\n+ * @size: Size of memory to allocate\n+ * @align: Alignment requirement\n+ * @gfp: Allocation flags\n+ */\n+#define folio_scratchpad_alloc_bytes(ptr, member, size, align, gfp)\t\t\\\n+\tfolio_scratchpad_alloc(\u0026(ptr)-\u003emember, size, align, gfp)\n+\n+/**\n+ * folio_scratchpad_alloc_type - Allocate a typed object from a scratchpad pointer\n+ * @sp: Pointer to struct folio_scratchpad\n+ * @type: Type of object being allocated\n+ * @gfp: Allocation flags\n+ */\n+#define folio_scratchpad_alloc_type(sp, type, gfp)\t\t\t\t\\\n+\t((type *)folio_scratchpad_alloc(sp,\t\t\t\t\t\\\n+\t\t\t\t\tsizeof(type),\t\t\t\t\\\n+\t\t\t\t\t__alignof__(type),\t\t\t\\\n+\t\t\t\t\tgfp))\n+\n+/*\n+ * 2. Fixed-Slot Uniform Pool (Specialized Thin Wrapper on Scratchpad)\n+ */\n+struct folio_pool {\n+\tstruct folio_scratchpad base;\n+\tsize_t elem_size;\n+\tsize_t elem_align;\n+};\n+\n+#define FOLIO_POOL_INIT(name, _elem_size, _order) {\t\t\t\\\n+\t.base = FOLIO_SCRATCHPAD_INIT((name).base, _order),\t\t\\\n+\t.elem_size = (_elem_size),\t\t\t\t\t\\\n+\t.elem_align = ((_elem_size) \u003e sizeof(void *) ? (_elem_size) : sizeof(void *)), \\\n+}\n+\n+#define FOLIO_POOL_INIT_KEY(name, _elem_size, _order, _key) {\t\t\\\n+\t.base = FOLIO_SCRATCHPAD_INIT_KEY((name).base, _order, _key),\t\\\n+\t.elem_size = (_elem_size),\t\t\t\t\t\\\n+\t.elem_align = ((_elem_size) \u003e sizeof(void *) ? (_elem_size) : sizeof(void *)), \\\n+}\n+\n+void folio_pool_init(struct folio_pool *fp, size_t elem_size, unsigned int order);\n+void folio_pool_init_key(struct folio_pool *fp, size_t elem_size, unsigned int order,\n+\t\t\t struct static_key *key);\n+void folio_pool_init_align(struct folio_pool *fp, size_t elem_size,\n+\t\t\t   size_t elem_align, unsigned int order);\n+\n+static inline void *folio_pool_alloc(struct folio_pool *fp, gfp_t gfp)\n+{\n+\treturn folio_scratchpad_alloc(\u0026fp-\u003ebase, fp-\u003eelem_size, fp-\u003eelem_align, gfp);\n+}\n+\n+static inline void folio_pool_free(struct folio_pool *fp)\n+{\n+\tfolio_scratchpad_free(\u0026fp-\u003ebase);\n+}\n+\n+static inline void folio_pool_stats(struct folio_pool *fp, unsigned int *nr_chunks,\n+\t\t\t\t    size_t *chunk_size, size_t *tail_used)\n+{\n+\tfolio_scratchpad_stats(\u0026fp-\u003ebase, nr_chunks, chunk_size, tail_used);\n+}\n+\n+/**\n+ * folio_pool_alloc_obj - Allocate a typed object from a container's embedded folio_pool\n+ * @ptr: Pointer to container struct (e.g. env)\n+ * @member: Name of the struct folio_pool field (e.g. state_pool)\n+ * @type: Type of object being allocated (e.g. struct bpf_verifier_stack_elem)\n+ * @gfp: Allocation flags\n+ */\n+#define folio_pool_alloc_obj(ptr, member, type, gfp)\t\t\t\t\\\n+\t((type *)folio_pool_alloc(\u0026(ptr)-\u003emember, gfp))\n+\n+/**\n+ * folio_pool_alloc_type - Allocate a typed object from a struct folio_pool pointer\n+ * @fp: Pointer to struct folio_pool\n+ * @type: Type of object being allocated\n+ * @gfp: Allocation flags\n+ */\n+#define folio_pool_alloc_type(fp, type, gfp)\t\t\t\t\t\\\n+\t((type *)folio_pool_alloc(fp, gfp))\n+\n+#endif /* _LINUX_FOLIO_POOL_H */\ndiff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h\nindex 3be612145c130..b4a6cc1f46afd 100644\n--- a/include/net/netfilter/nf_tables.h\n+++ b/include/net/netfilter/nf_tables.h\n@@ -1941,6 +1941,8 @@ __printf(2, 3) int nft_request_module(struct net *net, const char *fmt, ...);\n static inline int nft_request_module(struct net *net, const char *fmt, ...) { return -ENOENT; }\n #endif\n \n+#include \u003clinux/folio_pool.h\u003e\n+\n struct nftables_pernet {\n \tstruct list_head\ttables;\n \tstruct list_head\tcommit_list;\n@@ -1949,6 +1951,7 @@ struct nftables_pernet {\n \tstruct list_head\tbinding_list;\n \tstruct list_head\tmodule_list;\n \tstruct list_head\tnotify_list;\n+\tstruct folio_scratchpad\ttrans_scratchpad;\n \tstruct mutex\t\tcommit_mutex;\n \tu64\t\t\ttable_handle;\n \tu64\t\t\ttstamp;\ndiff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c\nindex 6db306d23b479..8ef48673b9339 100644\n--- a/kernel/bpf/syscall.c\n+++ b/kernel/bpf/syscall.c\n@@ -19,6 +19,7 @@\n #include \u003clinux/fdtable.h\u003e\n #include \u003clinux/file.h\u003e\n #include \u003clinux/fs.h\u003e\n+#include \u003clinux/folio_pool.h\u003e\n #include \u003clinux/license.h\u003e\n #include \u003clinux/filter.h\u003e\n #include \u003clinux/kernel.h\u003e\n@@ -2059,12 +2060,15 @@ int generic_map_delete_batch(struct bpf_map *map,\n \treturn err;\n }\n \n+DEFINE_STATIC_KEY_TRUE(bpf_batch_scratchpad_key);\n+\n int generic_map_update_batch(struct bpf_map *map, struct file *map_file,\n \t\t\t     const union bpf_attr *attr,\n \t\t\t     union bpf_attr __user *uattr)\n {\n \tvoid __user *values = u64_to_user_ptr(attr-\u003ebatch.values);\n \tvoid __user *keys = u64_to_user_ptr(attr-\u003ebatch.keys);\n+\tstruct folio_scratchpad batch_sp;\n \tu32 value_size, cp, max_count;\n \tvoid *key, *value;\n \tint err = 0;\n@@ -2083,13 +2087,17 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,\n \tif (put_user(0, \u0026uattr-\u003ebatch.count))\n \t\treturn -EFAULT;\n \n-\tkey = kvmalloc(map-\u003ekey_size, GFP_USER | __GFP_NOWARN);\n+\tfolio_scratchpad_init_key(\u0026batch_sp, 0, \u0026bpf_batch_scratchpad_key);\n+\n+\tkey = folio_scratchpad_alloc(\u0026batch_sp, map-\u003ekey_size,\n+\t\t\t\t     sizeof(void *), GFP_USER | __GFP_NOWARN);\n \tif (!key)\n \t\treturn -ENOMEM;\n \n-\tvalue = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);\n+\tvalue = folio_scratchpad_alloc(\u0026batch_sp, value_size,\n+\t\t\t\t       sizeof(void *), GFP_USER | __GFP_NOWARN);\n \tif (!value) {\n-\t\tkvfree(key);\n+\t\tfolio_scratchpad_free(\u0026batch_sp);\n \t\treturn -ENOMEM;\n \t}\n \n@@ -2111,8 +2119,7 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,\n \tif (copy_to_user(\u0026uattr-\u003ebatch.count, \u0026cp, sizeof(cp)))\n \t\terr = -EFAULT;\n \n-\tkvfree(value);\n-\tkvfree(key);\n+\tfolio_scratchpad_free(\u0026batch_sp);\n \n \treturn err;\n }\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex fdc5fbb1f78ca..8a9e66ce4dc8e 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -1725,7 +1725,7 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,\n \t\t*prev_insn_idx = head-\u003eprev_insn_idx;\n \telem = head-\u003enext;\n \tbpf_free_verifier_state(\u0026head-\u003est, false);\n-\tkfree(head);\n+\tfolio_pool_free_elem(head);\n \tenv-\u003ehead = elem;\n \tenv-\u003estack_size--;\n \treturn 0;\n@@ -1743,6 +1743,8 @@ static bool error_recoverable_with_nospec(int err)\n \treturn err == -EPERM || err == -EACCES || err == -EINVAL;\n }\n \n+DEFINE_STATIC_KEY_TRUE(bpf_state_pool_key);\n+\n static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,\n \t\t\t\t\t     int insn_idx, int prev_insn_idx,\n \t\t\t\t\t     bool speculative)\n@@ -1751,7 +1753,9 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,\n \tstruct bpf_verifier_stack_elem *elem;\n \tint err;\n \n-\telem = kzalloc_obj(struct bpf_verifier_stack_elem, GFP_KERNEL_ACCOUNT);\n+\telem = folio_pool_alloc_obj(env, state_pool,\n+\t\t\t\t    struct bpf_verifier_stack_elem,\n+\t\t\t\t    GFP_KERNEL_ACCOUNT);\n \tif (!elem)\n \t\treturn ERR_PTR(-ENOMEM);\n \n@@ -2275,7 +2279,9 @@ static struct bpf_verifier_state *push_async_cb(struct bpf_verifier_env *env,\n \tstruct bpf_verifier_stack_elem *elem;\n \tstruct bpf_func_state *frame;\n \n-\telem = kzalloc_obj(struct bpf_verifier_stack_elem, GFP_KERNEL_ACCOUNT);\n+\telem = folio_pool_alloc_obj(env, state_pool,\n+\t\t\t\t    struct bpf_verifier_stack_elem,\n+\t\t\t\t    GFP_KERNEL_ACCOUNT);\n \tif (!elem)\n \t\treturn ERR_PTR(-ENOMEM);\n \n@@ -19789,6 +19795,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n \tif (!env)\n \t\treturn -ENOMEM;\n \n+\tfolio_pool_init_key(\u0026env-\u003estate_pool, sizeof(struct bpf_verifier_stack_elem),\n+\t\t\t    get_order(SZ_64K), \u0026bpf_state_pool_key);\n+\n \tenv-\u003ebt.env = env;\n \n \tlen = (*prog)-\u003elen;\n@@ -20055,6 +20064,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n \tbpf_clear_insn_aux_data(env, 0, env-\u003eprog-\u003elen);\n err_free_env:\n \tbpf_stack_liveness_free(env);\n+\tfolio_pool_free(\u0026env-\u003estate_pool);\n \tkvfree(env-\u003ecfg.insn_postorder);\n \tkvfree(env-\u003escc_info);\n \tkvfree(env-\u003esucc);\ndiff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c\nindex 2d4c5bab5af88..d9541207b342b 100644\n--- a/kernel/locking/lockdep.c\n+++ b/kernel/locking/lockdep.c\n@@ -58,12 +58,37 @@\n #include \u003clinux/context_tracking.h\u003e\n #include \u003clinux/console.h\u003e\n #include \u003clinux/kasan.h\u003e\n+#include \u003clinux/folio_pool.h\u003e\n+\n+DEFINE_STATIC_KEY_TRUE(lockdep_pool_key);\n+static struct folio_pool lockdep_pool =\n+\tFOLIO_POOL_INIT_KEY(lockdep_pool, sizeof(struct lock_list),\n+\t\t\t    FOLIO_POOL_64K_ORDER, \u0026lockdep_pool_key);\n+\n+static int __init setup_lockdep_folio_pool(char *str)\n+{\n+\tbool enable;\n+\n+\tif (!kstrtobool(str, \u0026enable)) {\n+\t\tif (enable)\n+\t\t\tstatic_branch_enable(\u0026lockdep_pool_key);\n+\t\telse\n+\t\t\tstatic_branch_disable(\u0026lockdep_pool_key);\n+\t}\n+\treturn 1;\n+}\n+__setup(\"lockdep.folio_pool=\", setup_lockdep_folio_pool);\n \n #include \u003casm/sections.h\u003e\n \n #include \"lockdep_internals.h\"\n #include \"lock_events.h\"\n \n+void lockdep_pool_stats(unsigned int *nr_chunks, size_t *chunk_size, size_t *tail_used)\n+{\n+\tfolio_pool_stats(\u0026lockdep_pool, nr_chunks, chunk_size, tail_used);\n+}\n+\n #include \u003ctrace/events/lock.h\u003e\n \n #ifdef CONFIG_PROVE_LOCKING\n@@ -204,9 +229,13 @@ static inline int debug_locks_off_graph_unlock(void)\n \treturn ret;\n }\n \n+#define BOOTSTRAP_LOCKDEP_ENTRIES 4096UL\n+\n+static struct lock_list early_list_entries[BOOTSTRAP_LOCKDEP_ENTRIES] __initdata;\n+static unsigned long early_list_entries_in_use[BITS_TO_LONGS(BOOTSTRAP_LOCKDEP_ENTRIES)] __initdata;\n+static struct lock_list *bootstrap_entries __read_mostly = early_list_entries;\n+static unsigned long *bootstrap_entries_in_use __read_mostly = early_list_entries_in_use;\n unsigned long nr_list_entries;\n-static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES];\n-static DECLARE_BITMAP(list_entries_in_use, MAX_LOCKDEP_ENTRIES);\n \n /*\n  * All data structures here are protected by the global debug_lock.\n@@ -1028,9 +1057,9 @@ static bool class_lock_list_valid(struct lock_class *c, struct list_head *h)\n \n \tlist_for_each_entry(e, h, entry) {\n \t\tif (e-\u003elinks_to != c) {\n-\t\t\tprintk(KERN_INFO \"class %s: mismatch for lock entry %ld; class %s \u003c\u003e %s\",\n+\t\t\tprintk(KERN_INFO \"class %s: mismatch for lock entry %px; class %s \u003c\u003e %s\",\n \t\t\t       c-\u003ename ? : \"(?)\",\n-\t\t\t       (unsigned long)(e - list_entries),\n+\t\t\t       e,\n \t\t\t       e-\u003elinks_to \u0026\u0026 e-\u003elinks_to-\u003ename ?\n \t\t\t       e-\u003elinks_to-\u003ename : \"(?)\",\n \t\t\t       e-\u003eclass \u0026\u0026 e-\u003eclass-\u003ename ? e-\u003eclass-\u003ename :\n@@ -1119,35 +1148,37 @@ static bool __check_data_structures(void)\n \t\t}\n \t}\n \n-\t/*\n-\t * Check whether all list entries that are in use occur in a class\n-\t * lock list.\n-\t */\n-\tfor_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {\n-\t\te = list_entries + i;\n-\t\tif (!in_any_class_list(\u0026e-\u003eentry)) {\n-\t\t\tprintk(KERN_INFO \"list entry %d is not in any class list; class %s \u003c\u003e %s\\n\",\n-\t\t\t       (unsigned int)(e - list_entries),\n-\t\t\t       e-\u003eclass-\u003ename ? : \"(?)\",\n-\t\t\t       e-\u003elinks_to-\u003ename ? : \"(?)\");\n-\t\t\treturn false;\n+\tif (bootstrap_entries) {\n+\t\t/*\n+\t\t * Check whether all list entries that are in use occur in a class\n+\t\t * lock list.\n+\t\t */\n+\t\tfor_each_set_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {\n+\t\t\te = bootstrap_entries + i;\n+\t\t\tif (!in_any_class_list(\u0026e-\u003eentry)) {\n+\t\t\t\tprintk(KERN_INFO \"list entry %d is not in any class list; class %s \u003c\u003e %s\\n\",\n+\t\t\t\t       (unsigned int)(e - bootstrap_entries),\n+\t\t\t\t       e-\u003eclass-\u003ename ? : \"(?)\",\n+\t\t\t\t       e-\u003elinks_to-\u003ename ? : \"(?)\");\n+\t\t\t\treturn false;\n+\t\t\t}\n \t\t}\n-\t}\n \n-\t/*\n-\t * Check whether all list entries that are not in use do not occur in\n-\t * a class lock list.\n-\t */\n-\tfor_each_clear_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {\n-\t\te = list_entries + i;\n-\t\tif (in_any_class_list(\u0026e-\u003eentry)) {\n-\t\t\tprintk(KERN_INFO \"list entry %d occurs in a class list; class %s \u003c\u003e %s\\n\",\n-\t\t\t       (unsigned int)(e - list_entries),\n-\t\t\t       e-\u003eclass \u0026\u0026 e-\u003eclass-\u003ename ? e-\u003eclass-\u003ename :\n-\t\t\t       \"(?)\",\n-\t\t\t       e-\u003elinks_to \u0026\u0026 e-\u003elinks_to-\u003ename ?\n-\t\t\t       e-\u003elinks_to-\u003ename : \"(?)\");\n-\t\t\treturn false;\n+\t\t/*\n+\t\t * Check whether all list entries that are not in use do not occur in\n+\t\t * a class lock list.\n+\t\t */\n+\t\tfor_each_clear_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {\n+\t\t\te = bootstrap_entries + i;\n+\t\t\tif (in_any_class_list(\u0026e-\u003eentry)) {\n+\t\t\t\tprintk(KERN_INFO \"list entry %d occurs in a class list; class %s \u003c\u003e %s\\n\",\n+\t\t\t\t       (unsigned int)(e - bootstrap_entries),\n+\t\t\t\t       e-\u003eclass \u0026\u0026 e-\u003eclass-\u003ename ? e-\u003eclass-\u003ename :\n+\t\t\t\t       \"(?)\",\n+\t\t\t\t       e-\u003elinks_to \u0026\u0026 e-\u003elinks_to-\u003ename ?\n+\t\t\t\t       e-\u003elinks_to-\u003ename : \"(?)\");\n+\t\t\t\treturn false;\n+\t\t\t}\n \t\t}\n \t}\n \n@@ -1400,22 +1431,32 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)\n  */\n static struct lock_list *alloc_list_entry(void)\n {\n-\tint idx = find_first_zero_bit(list_entries_in_use,\n-\t\t\t\t      ARRAY_SIZE(list_entries));\n+\tstruct lock_list *p;\n \n-\tif (idx \u003e= ARRAY_SIZE(list_entries)) {\n-\t\tif (!debug_locks_off_graph_unlock())\n-\t\t\treturn NULL;\n+\tif (bootstrap_entries) {\n+\t\tint idx = find_first_zero_bit(bootstrap_entries_in_use,\n+\t\t\t\t\t      BOOTSTRAP_LOCKDEP_ENTRIES);\n \n-\t\tnbcon_cpu_emergency_enter();\n-\t\tprint_lockdep_off(\"BUG: MAX_LOCKDEP_ENTRIES too low!\");\n-\t\tdump_stack();\n-\t\tnbcon_cpu_emergency_exit();\n-\t\treturn NULL;\n+\t\tif (idx \u003c BOOTSTRAP_LOCKDEP_ENTRIES) {\n+\t\t\t__set_bit(idx, bootstrap_entries_in_use);\n+\t\t\tnr_list_entries++;\n+\t\t\treturn bootstrap_entries + idx;\n+\t\t}\n+\t}\n+\n+\tp = folio_pool_alloc_type(\u0026lockdep_pool, struct lock_list, GFP_ATOMIC);\n+\tif (p) {\n+\t\tnr_list_entries++;\n+\t\treturn p;\n \t}\n-\tnr_list_entries++;\n-\t__set_bit(idx, list_entries_in_use);\n-\treturn list_entries + idx;\n+\tif (!debug_locks_off_graph_unlock())\n+\t\treturn NULL;\n+\n+\tnbcon_cpu_emergency_enter();\n+\tprint_lockdep_off(\"BUG: MAX_LOCKDEP_ENTRIES too low and folio_pool exhausted!\");\n+\tdump_stack();\n+\tnbcon_cpu_emergency_exit();\n+\treturn NULL;\n }\n \n /*\n@@ -6238,13 +6279,25 @@ static void remove_class_from_lock_chains(struct pending_free *pf,\n \t}\n }\n \n+static inline bool is_bootstrap_entry(const struct lock_list *entry)\n+{\n+\treturn bootstrap_entries \u0026\u0026\n+\t       entry \u003e= bootstrap_entries \u0026\u0026\n+\t       entry \u003c bootstrap_entries + BOOTSTRAP_LOCKDEP_ENTRIES;\n+}\n+\n+static inline void clear_bootstrap_entry_bit(const struct lock_list *entry)\n+{\n+\tif (is_bootstrap_entry(entry))\n+\t\t__clear_bit(entry - bootstrap_entries, bootstrap_entries_in_use);\n+}\n+\n /*\n  * Remove all references to a lock class. The caller must hold the graph lock.\n  */\n static void zap_class(struct pending_free *pf, struct lock_class *class)\n {\n-\tstruct lock_list *entry;\n-\tint i;\n+\tstruct lock_list *entry, *tmp, *other, *other_tmp;\n \n \tWARN_ON_ONCE(!class-\u003ekey);\n \n@@ -6252,11 +6305,29 @@ static void zap_class(struct pending_free *pf, struct lock_class *class)\n \t * Remove all dependencies this lock is\n \t * involved in:\n \t */\n-\tfor_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {\n-\t\tentry = list_entries + i;\n-\t\tif (entry-\u003eclass != class \u0026\u0026 entry-\u003elinks_to != class)\n-\t\t\tcontinue;\n-\t\t__clear_bit(i, list_entries_in_use);\n+\tlist_for_each_entry_safe(entry, tmp, \u0026class-\u003elocks_after, entry) {\n+\t\tlist_for_each_entry_safe(other, other_tmp, \u0026entry-\u003elinks_to-\u003elocks_before, entry) {\n+\t\t\tif (other-\u003elinks_to == class) {\n+\t\t\t\tclear_bootstrap_entry_bit(other);\n+\t\t\t\tnr_list_entries--;\n+\t\t\t\tlist_del_rcu(\u0026other-\u003eentry);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tclear_bootstrap_entry_bit(entry);\n+\t\tnr_list_entries--;\n+\t\tlist_del_rcu(\u0026entry-\u003eentry);\n+\t}\n+\tlist_for_each_entry_safe(entry, tmp, \u0026class-\u003elocks_before, entry) {\n+\t\tlist_for_each_entry_safe(other, other_tmp, \u0026entry-\u003elinks_to-\u003elocks_after, entry) {\n+\t\t\tif (other-\u003elinks_to == class) {\n+\t\t\t\tclear_bootstrap_entry_bit(other);\n+\t\t\t\tnr_list_entries--;\n+\t\t\t\tlist_del_rcu(\u0026other-\u003eentry);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tclear_bootstrap_entry_bit(entry);\n \t\tnr_list_entries--;\n \t\tlist_del_rcu(\u0026entry-\u003eentry);\n \t}\n@@ -6631,6 +6702,9 @@ EXPORT_SYMBOL_GPL(lockdep_unregister_key);\n \n void __init lockdep_init(void)\n {\n+\tbootstrap_entries = early_list_entries;\n+\tbootstrap_entries_in_use = early_list_entries_in_use;\n+\n \tpr_info(\"Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\\n\");\n \n \tpr_info(\"... MAX_LOCKDEP_SUBCLASSES:  %lu\\n\", MAX_LOCKDEP_SUBCLASSES);\n@@ -6645,8 +6719,8 @@ void __init lockdep_init(void)\n \t       (sizeof(lock_classes) +\n \t\tsizeof(lock_classes_in_use) +\n \t\tsizeof(classhash_table) +\n-\t\tsizeof(list_entries) +\n-\t\tsizeof(list_entries_in_use) +\n+\t\tsizeof(early_list_entries) +\n+\t\tsizeof(early_list_entries_in_use) +\n \t\tsizeof(chainhash_table) +\n \t\tsizeof(delayed_free)\n #ifdef CONFIG_PROVE_LOCKING\n@@ -6668,6 +6742,104 @@ void __init lockdep_init(void)\n \t       sizeof(((struct task_struct *)NULL)-\u003eheld_locks));\n }\n \n+static int __init lockdep_boot_report(void)\n+{\n+\tpr_info(\"lockdep: %lu/%lu bootstrap entries used before buddy init, folio_pool active\\n\",\n+\t\tmin_t(unsigned long, nr_list_entries, BOOTSTRAP_LOCKDEP_ENTRIES),\n+\t\tBOOTSTRAP_LOCKDEP_ENTRIES);\n+\treturn 0;\n+}\n+core_initcall(lockdep_boot_report);\n+\n+static int __init lockdep_compact_boot_graph(void)\n+{\n+\tstruct lock_class *class;\n+\tstruct lock_list *entry, *tmp, *new_entry;\n+\tunsigned long flags;\n+\tunsigned long migrated = 0;\n+\n+\tif (!debug_locks)\n+\t\treturn 0;\n+\n+\t/* Pre-allocate 64KB folio chunk outside graph_lock to avoid MM recursion */\n+\tnew_entry = folio_pool_alloc_type(\u0026lockdep_pool, struct lock_list, GFP_KERNEL);\n+\tif (!new_entry) {\n+\t\tpr_err(\"lockdep: failed to pre-allocate folio chunk for boot compaction\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\traw_local_irq_save(flags);\n+\tif (!graph_lock()) {\n+\t\traw_local_irq_restore(flags);\n+\t\treturn 0;\n+\t}\n+\n+\tlist_for_each_entry(class, \u0026all_lock_classes, lock_entry) {\n+\t\tlist_for_each_entry_safe(entry, tmp, \u0026class-\u003elocks_after, entry) {\n+\t\t\tif (is_bootstrap_entry(entry)) {\n+\t\t\t\tif (new_entry) {\n+\t\t\t\t\t*new_entry = *entry;\n+\t\t\t\t\tlist_replace_rcu(\u0026entry-\u003eentry, \u0026new_entry-\u003eentry);\n+\t\t\t\t\tnew_entry = NULL;\n+\t\t\t\t} else {\n+\t\t\t\t\tstruct lock_list *slot;\n+\n+\t\t\t\t\tslot = folio_pool_alloc_type(\u0026lockdep_pool,\n+\t\t\t\t\t\t\t\t     struct lock_list,\n+\t\t\t\t\t\t\t\t     GFP_ATOMIC);\n+\t\t\t\t\tif (!slot) {\n+\t\t\t\t\t\tdebug_locks_off_graph_unlock();\n+\t\t\t\t\t\traw_local_irq_restore(flags);\n+\t\t\t\t\t\tpr_err(\"lockdep: folio chunk exhausted during boot compaction\\n\");\n+\t\t\t\t\t\treturn -ENOMEM;\n+\t\t\t\t\t}\n+\t\t\t\t\t*slot = *entry;\n+\t\t\t\t\tlist_replace_rcu(\u0026entry-\u003eentry, \u0026slot-\u003eentry);\n+\t\t\t\t}\n+\t\t\t\tmigrated++;\n+\t\t\t}\n+\t\t}\n+\n+\t\tlist_for_each_entry_safe(entry, tmp, \u0026class-\u003elocks_before, entry) {\n+\t\t\tif (is_bootstrap_entry(entry)) {\n+\t\t\t\tif (new_entry) {\n+\t\t\t\t\t*new_entry = *entry;\n+\t\t\t\t\tlist_replace_rcu(\u0026entry-\u003eentry, \u0026new_entry-\u003eentry);\n+\t\t\t\t\tnew_entry = NULL;\n+\t\t\t\t} else {\n+\t\t\t\t\tstruct lock_list *slot;\n+\n+\t\t\t\t\tslot = folio_pool_alloc_type(\u0026lockdep_pool,\n+\t\t\t\t\t\t\t\t     struct lock_list,\n+\t\t\t\t\t\t\t\t     GFP_ATOMIC);\n+\t\t\t\t\tif (!slot) {\n+\t\t\t\t\t\tdebug_locks_off_graph_unlock();\n+\t\t\t\t\t\traw_local_irq_restore(flags);\n+\t\t\t\t\t\tpr_err(\"lockdep: folio chunk exhausted during boot compaction\\n\");\n+\t\t\t\t\t\treturn -ENOMEM;\n+\t\t\t\t\t}\n+\t\t\t\t\t*slot = *entry;\n+\t\t\t\t\tlist_replace_rcu(\u0026entry-\u003eentry, \u0026slot-\u003eentry);\n+\t\t\t\t}\n+\t\t\t\tmigrated++;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* Adjust counter so compaction does not double-count migrated nodes */\n+\tnr_list_entries -= migrated;\n+\n+\tbootstrap_entries = NULL;\n+\tbootstrap_entries_in_use = NULL;\n+\tgraph_unlock();\n+\traw_local_irq_restore(flags);\n+\n+\tpr_info(\"lockdep: compacted %lu boot entries into folio_pool, freeing bootstrap memory\\n\",\n+\t\tmigrated);\n+\treturn 0;\n+}\n+late_initcall(lockdep_compact_boot_graph);\n+\n static void\n print_freed_lock_bug(struct task_struct *curr, const void *mem_from,\n \t\t     const void *mem_to, struct held_lock *hlock)\ndiff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h\nindex 0e5e6ffe91a3f..f802160c0dd53 100644\n--- a/kernel/locking/lockdep_internals.h\n+++ b/kernel/locking/lockdep_internals.h\n@@ -159,6 +159,8 @@ extern unsigned long max_lock_class_idx;\n extern struct lock_class lock_classes[MAX_LOCKDEP_KEYS];\n extern unsigned long lock_classes_in_use[];\n \n+void lockdep_pool_stats(unsigned int *nr_chunks, size_t *chunk_size, size_t *tail_used);\n+\n #ifdef CONFIG_PROVE_LOCKING\n extern unsigned long lockdep_count_forward_deps(struct lock_class *);\n extern unsigned long lockdep_count_backward_deps(struct lock_class *);\ndiff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c\nindex 1916db9aa46b6..3e6f24ae9b34b 100644\n--- a/kernel/locking/lockdep_proc.c\n+++ b/kernel/locking/lockdep_proc.c\n@@ -288,8 +288,20 @@ static int lockdep_stats_show(struct seq_file *m, void *v)\n \t\t\tnr_lock_classes, MAX_LOCKDEP_KEYS);\n \tseq_printf(m, \" dynamic-keys:                  %11lu\\n\",\n \t\t\tnr_dynamic_keys);\n-\tseq_printf(m, \" direct dependencies:           %11lu [max: %lu]\\n\",\n-\t\t\tnr_list_entries, MAX_LOCKDEP_ENTRIES);\n+\t{\n+\t\tunsigned int fp_chunks = 0;\n+\t\tsize_t fp_chunk_sz = 0, fp_tail_used = 0;\n+\n+\t\tlockdep_pool_stats(\u0026fp_chunks, \u0026fp_chunk_sz, \u0026fp_tail_used);\n+\t\tif (fp_chunks) {\n+\t\t\tseq_printf(m, \" direct dependencies:           %11lu [dynamic: %u x %zu kB, tail: %zu kB/%zu kB]\\n\",\n+\t\t\t\t   nr_list_entries, fp_chunks, fp_chunk_sz / 1024,\n+\t\t\t\t   fp_tail_used / 1024, fp_chunk_sz / 1024);\n+\t\t} else {\n+\t\t\tseq_printf(m, \" direct dependencies:           %11lu [max: %lu]\\n\",\n+\t\t\t\t   nr_list_entries, MAX_LOCKDEP_ENTRIES);\n+\t\t}\n+\t}\n \tseq_printf(m, \" indirect dependencies:         %11lu\\n\",\n \t\t\tsum_forward_deps);\n \ndiff --git a/lib/Makefile b/lib/Makefile\nindex 7f75cc6edf94a..b5f2f41f96fbd 100644\n--- a/lib/Makefile\n+++ b/lib/Makefile\n@@ -58,7 +58,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \\\n \t bsearch.o find_bit.o llist.o lwq.o memweight.o kfifo.o \\\n \t percpu-refcount.o rhashtable.o base64.o \\\n \t once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \\\n-\t generic-radix-tree.o bitmap-str.o\n+\t generic-radix-tree.o bitmap-str.o folio_pool.o\n obj-y += string_helpers.o\n obj-y += hexdump.o\n obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o\ndiff --git a/lib/folio_pool.c b/lib/folio_pool.c\nnew file mode 100644\nindex 0000000000000..07a290db6c506\n--- /dev/null\n+++ b/lib/folio_pool.c\n@@ -0,0 +1,230 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * Direct-Map Large Folio Scratchpad \u0026 Pool bump allocators.\n+ */\n+#include \u003clinux/export.h\u003e\n+#include \u003clinux/string.h\u003e\n+#include \u003clinux/moduleparam.h\u003e\n+#include \u003clinux/folio_pool.h\u003e\n+\n+DEFINE_STATIC_KEY_TRUE(folio_pool_enabled_key);\n+EXPORT_SYMBOL_GPL(folio_pool_enabled_key);\n+\n+static int folio_pool_enabled_set(const char *val, const struct kernel_param *kp)\n+{\n+\tbool enable;\n+\tint ret = kstrtobool(val, \u0026enable);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (enable)\n+\t\tstatic_branch_enable(\u0026folio_pool_enabled_key);\n+\telse\n+\t\tstatic_branch_disable(\u0026folio_pool_enabled_key);\n+\n+\treturn 0;\n+}\n+\n+static int folio_pool_enabled_get(char *buffer, const struct kernel_param *kp)\n+{\n+\treturn sprintf(buffer, \"%c\\n\", static_branch_likely(\u0026folio_pool_enabled_key) ? 'Y' : 'N');\n+}\n+\n+static const struct kernel_param_ops folio_pool_enabled_ops = {\n+\t.set = folio_pool_enabled_set,\n+\t.get = folio_pool_enabled_get,\n+};\n+\n+module_param_cb(enabled, \u0026folio_pool_enabled_ops, NULL, 0644);\n+MODULE_PARM_DESC(enabled, \"Toggle folio_pool/scratchpad bump allocator (0 = fallback to SLUB)\");\n+\n+/*\n+ * 1. Variable-Sized Scratchpad (Core Engine)\n+ */\n+void folio_scratchpad_init_key(struct folio_scratchpad *sp, unsigned int order,\n+\t\t\t       struct static_key *key)\n+{\n+\tINIT_LIST_HEAD(\u0026sp-\u003echunks);\n+\tsp-\u003efree_ptr = NULL;\n+\tsp-\u003eremaining = 0;\n+\tsp-\u003echunk_order = order;\n+\tsp-\u003ekey = key;\n+\tspin_lock_init(\u0026sp-\u003elock);\n+}\n+EXPORT_SYMBOL_GPL(folio_scratchpad_init_key);\n+\n+void folio_scratchpad_init(struct folio_scratchpad *sp, unsigned int order)\n+{\n+\tfolio_scratchpad_init_key(sp, order, NULL);\n+}\n+EXPORT_SYMBOL_GPL(folio_scratchpad_init);\n+\n+static inline bool folio_scratchpad_is_enabled(const struct folio_scratchpad *sp)\n+{\n+\tif (sp-\u003ekey)\n+\t\treturn static_key_enabled(sp-\u003ekey);\n+\treturn static_branch_likely(\u0026folio_pool_enabled_key);\n+}\n+\n+noinline void *folio_scratchpad_alloc(struct folio_scratchpad *sp, size_t size,\n+\t\t\t\t     size_t align, gfp_t gfp)\n+{\n+\tstruct folio_pool_chunk *chunk;\n+\tstruct folio *folio;\n+\tvoid *elem, *base;\n+\tsize_t chunk_size, aligned_size, pad, header_offset;\n+\tunsigned long flags;\n+\n+\tif (!folio_scratchpad_is_enabled(sp))\n+\t\treturn kvzalloc(size, gfp);\n+\n+\tif (unlikely(!size))\n+\t\treturn NULL;\n+\n+\talign = max_t(size_t, sizeof(void *), align ? align : sizeof(void *));\n+\n+\tspin_lock_irqsave(\u0026sp-\u003elock, flags);\n+\tpad = (uintptr_t)sp-\u003efree_ptr \u0026 (align - 1);\n+\tif (pad)\n+\t\tpad = align - pad;\n+\taligned_size = size + pad;\n+\n+\tif (sp-\u003eremaining \u003c aligned_size) {\n+\t\tspin_unlock_irqrestore(\u0026sp-\u003elock, flags);\n+\n+\t\tfolio = folio_alloc(gfp, sp-\u003echunk_order);\n+\t\tif (!folio \u0026\u0026 sp-\u003echunk_order \u003e 0)\n+\t\t\tfolio = folio_alloc(gfp, 0);\n+\t\tif (!folio)\n+\t\t\treturn NULL;\n+\n+\t\tbase = folio_address(folio);\n+\t\tchunk = (struct folio_pool_chunk *)base;\n+\t\tchunk-\u003efolio = folio;\n+\t\tchunk_size = folio_size(folio);\n+\t\theader_offset = ALIGN(sizeof(*chunk), max_t(size_t, sizeof(void *), align));\n+\n+\t\tspin_lock_irqsave(\u0026sp-\u003elock, flags);\n+\t\tlist_add(\u0026chunk-\u003elink, \u0026sp-\u003echunks);\n+\t\tsp-\u003efree_ptr = base + header_offset;\n+\t\tsp-\u003eremaining = chunk_size - header_offset;\n+\n+\t\tpad = (uintptr_t)sp-\u003efree_ptr \u0026 (align - 1);\n+\t\tif (pad)\n+\t\t\tpad = align - pad;\n+\t\taligned_size = size + pad;\n+\t}\n+\n+\tif (sp-\u003eremaining \u003c aligned_size) {\n+\t\tspin_unlock_irqrestore(\u0026sp-\u003elock, flags);\n+\t\treturn NULL;\n+\t}\n+\n+\telem = sp-\u003efree_ptr + pad;\n+\tsp-\u003efree_ptr += aligned_size;\n+\tsp-\u003eremaining -= aligned_size;\n+\tspin_unlock_irqrestore(\u0026sp-\u003elock, flags);\n+\n+\tmemset(elem, 0, size);\n+\treturn elem;\n+}\n+EXPORT_SYMBOL_GPL(folio_scratchpad_alloc);\n+\n+noinline void folio_scratchpad_reset(struct folio_scratchpad *sp)\n+{\n+\tstruct folio_pool_chunk *head, *chunk, *tmp;\n+\tsize_t header_offset;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026sp-\u003elock, flags);\n+\tif (list_empty(\u0026sp-\u003echunks)) {\n+\t\tsp-\u003efree_ptr = NULL;\n+\t\tsp-\u003eremaining = 0;\n+\t\tspin_unlock_irqrestore(\u0026sp-\u003elock, flags);\n+\t\treturn;\n+\t}\n+\n+\t/* Retain primary head chunk; free overflow chunks */\n+\thead = list_first_entry(\u0026sp-\u003echunks, struct folio_pool_chunk, link);\n+\tlist_for_each_entry_safe(chunk, tmp, \u0026sp-\u003echunks, link) {\n+\t\tif (chunk == head)\n+\t\t\tcontinue;\n+\t\tlist_del(\u0026chunk-\u003elink);\n+\t\tfolio_put(chunk-\u003efolio);\n+\t}\n+\n+\theader_offset = ALIGN(sizeof(*head), sizeof(void *));\n+\tsp-\u003efree_ptr = folio_address(head-\u003efolio) + header_offset;\n+\tsp-\u003eremaining = folio_size(head-\u003efolio) - header_offset;\n+\tspin_unlock_irqrestore(\u0026sp-\u003elock, flags);\n+}\n+EXPORT_SYMBOL_GPL(folio_scratchpad_reset);\n+\n+noinline void folio_scratchpad_free(struct folio_scratchpad *sp)\n+{\n+\tstruct folio_pool_chunk *chunk, *tmp;\n+\tstruct folio *folio;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026sp-\u003elock, flags);\n+\tsp-\u003efree_ptr = NULL;\n+\tsp-\u003eremaining = 0;\n+\tlist_for_each_entry_safe(chunk, tmp, \u0026sp-\u003echunks, link) {\n+\t\tfolio = chunk-\u003efolio;\n+\t\tlist_del(\u0026chunk-\u003elink);\n+\t\tfolio_put(folio);\n+\t}\n+\tspin_unlock_irqrestore(\u0026sp-\u003elock, flags);\n+}\n+EXPORT_SYMBOL_GPL(folio_scratchpad_free);\n+\n+/*\n+ * 2. Fixed-Slot Uniform Pool (Specialized Thin Wrapper on Scratchpad)\n+ */\n+void folio_pool_init_align(struct folio_pool *fp, size_t elem_size,\n+\t\t\t   size_t elem_align, unsigned int order)\n+{\n+\tfolio_scratchpad_init(\u0026fp-\u003ebase, order);\n+\tfp-\u003eelem_size = elem_size;\n+\tfp-\u003eelem_align = max_t(size_t, sizeof(void *), elem_align ? elem_align : sizeof(void *));\n+}\n+EXPORT_SYMBOL_GPL(folio_pool_init_align);\n+\n+void folio_pool_init_key(struct folio_pool *fp, size_t elem_size, unsigned int order,\n+\t\t\t struct static_key *key)\n+{\n+\tsize_t align = sizeof(void *);\n+\n+\tif (elem_size \u0026\u0026 is_power_of_2(elem_size))\n+\t\talign = max_t(size_t, sizeof(void *), elem_size);\n+\tfolio_scratchpad_init_key(\u0026fp-\u003ebase, order, key);\n+\tfp-\u003eelem_size = elem_size;\n+\tfp-\u003eelem_align = align;\n+}\n+EXPORT_SYMBOL_GPL(folio_pool_init_key);\n+\n+void folio_pool_init(struct folio_pool *fp, size_t elem_size, unsigned int order)\n+{\n+\tfolio_pool_init_key(fp, elem_size, order, NULL);\n+}\n+EXPORT_SYMBOL_GPL(folio_pool_init);\n+\n+void folio_scratchpad_stats(struct folio_scratchpad *sp, unsigned int *nr_chunks,\n+\t\t\t    size_t *chunk_size, size_t *tail_used)\n+{\n+\tunsigned long flags;\n+\tsize_t csz;\n+\n+\tcsz = (PAGE_SIZE \u003c\u003c sp-\u003echunk_order);\n+\tif (chunk_size)\n+\t\t*chunk_size = csz;\n+\n+\tspin_lock_irqsave(\u0026sp-\u003elock, flags);\n+\tif (nr_chunks)\n+\t\t*nr_chunks = list_count_nodes(\u0026sp-\u003echunks);\n+\tif (tail_used)\n+\t\t*tail_used = csz \u003e sp-\u003eremaining ? csz - sp-\u003eremaining : 0;\n+\tspin_unlock_irqrestore(\u0026sp-\u003elock, flags);\n+}\n+EXPORT_SYMBOL_GPL(folio_scratchpad_stats);\ndiff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c\nindex af357f6c5070b..8b9e0c905d841 100644\n--- a/net/netfilter/nf_tables_api.c\n+++ b/net/netfilter/nf_tables_api.c\n@@ -173,12 +173,17 @@ static void nft_ctx_init(struct nft_ctx *ctx,\n \tbitmap_zero(ctx-\u003ereg_inited, NFT_REG32_NUM);\n }\n \n+DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(nft_trans_scratchpad_key, trans_scratchpad,\n+\t\t\t\t   \"Toggle nf_tables transaction folio scratchpad allocator\");\n+\n static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,\n \t\t\t\t\t int msg_type, u32 size)\n {\n+\tstruct nftables_pernet *nft_net = nft_pernet(ctx-\u003enet);\n \tstruct nft_trans *trans;\n \n-\ttrans = kzalloc(size, GFP_KERNEL);\n+\ttrans = folio_scratchpad_alloc_bytes(nft_net, trans_scratchpad, size,\n+\t\t\t\t\t     __alignof__(struct nft_trans), GFP_KERNEL);\n \tif (trans == NULL)\n \t\treturn NULL;\n \n@@ -194,6 +199,18 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,\n \treturn trans;\n }\n \n+/*\n+ * nft_trans_free - release a transaction object.\n+ *\n+ * Scratchpad-backed objects are not individually freed; they are reclaimed in\n+ * bulk by folio_scratchpad_free() at batch commit/abort boundary.  Kzalloc'd\n+ * objects reside on slab pages and are released immediately via kfree().\n+ */\n+static inline void nft_trans_free(struct nft_trans *trans)\n+{\n+\tfolio_scratchpad_free_elem(trans);\n+}\n+\n static struct nft_trans_binding *nft_trans_get_binding(struct nft_trans *trans)\n {\n \tswitch (trans-\u003emsg_type) {\n@@ -219,7 +236,7 @@ static void nft_trans_list_del(struct nft_trans *trans)\n static void nft_trans_destroy(struct nft_trans *trans)\n {\n \tnft_trans_list_del(trans);\n-\tkfree(trans);\n+\tnft_trans_free(trans);\n }\n \n static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set,\n@@ -519,8 +536,10 @@ static bool nft_trans_collapse_set_elem(struct nftables_pernet *nft_net,\n \t/* krealloc might free tail which invalidates list pointers */\n \tlist_del_init(\u0026tail-\u003enft_trans.list);\n \n-\tnew_trans = krealloc(tail, struct_size(tail, elems, nelems),\n-\t\t\t     GFP_KERNEL);\n+\tnew_trans = folio_scratchpad_realloc(tail,\n+\t\t\t\t\t     struct_size(tail, elems, old_nelems),\n+\t\t\t\t\t     struct_size(tail, elems, nelems),\n+\t\t\t\t\t     GFP_KERNEL);\n \tif (!new_trans) {\n \t\tlist_add_tail(\u0026tail-\u003enft_trans.list,\n \t\t\t      \u0026nft_net-\u003ecommit_list);\n@@ -600,7 +619,7 @@ static void nft_trans_commit_list_add_elem(struct net *net, struct nft_trans *tr\n \t\t     trans-\u003emsg_type != NFT_MSG_DELSETELEM);\n \n \tif (nft_trans_try_collapse(nft_net, trans)) {\n-\t\tkfree(trans);\n+\t\tnft_trans_free(trans);\n \t\treturn;\n \t}\n \n@@ -3068,7 +3087,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,\n \n err_trans:\n \tfree_percpu(stats);\n-\tkfree(trans);\n+\tnft_trans_free(trans);\n err_hooks:\n \tif (nla[NFTA_CHAIN_HOOK]) {\n \t\tlist_for_each_entry_safe(h, next, \u0026hook.list, list) {\n@@ -7636,7 +7655,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,\n \treturn set_full ? -ENFILE : 0;\n \n err_element_clash:\n-\tkfree(trans);\n+\tnft_trans_free(trans);\n err_set_size:\n \tif (!(flags \u0026 NFT_SET_ELEM_CATCHALL))\n \t\tatomic_dec(\u0026set-\u003enelems);\n@@ -7904,7 +7923,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,\n \treturn 0;\n \n fail_ops:\n-\tkfree(trans);\n+\tnft_trans_free(trans);\n fail_trans:\n \tkfree(elem.priv);\n fail_elem_key_end:\n@@ -8287,7 +8306,7 @@ static int nf_tables_updobj(const struct nft_ctx *ctx,\n \treturn 0;\n \n err_free_trans:\n-\tkfree(trans);\n+\tnft_trans_free(trans);\n err_trans:\n \tmodule_put(type-\u003eowner);\n \treturn err;\n@@ -10253,7 +10272,7 @@ static void nft_commit_release(struct nft_trans *trans)\n \tif (trans-\u003eput_net)\n \t\tput_net(trans-\u003enet);\n \n-\tkfree(trans);\n+\tnft_trans_free(trans);\n }\n \n static void nf_tables_trans_destroy_work(struct work_struct *w)\n@@ -10275,6 +10294,8 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)\n \t\tnft_trans_list_del(trans);\n \t\tnft_commit_release(trans);\n \t}\n+\n+\tfolio_scratchpad_reset(\u0026nft_net-\u003etrans_scratchpad);\n }\n \n void nf_tables_trans_destroy_flush_work(struct net *net)\n@@ -11244,7 +11265,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)\n \t\t\tnf_tables_flowtable_destroy(nft_trans_flowtable(trans));\n \t\tbreak;\n \t}\n-\tkfree(trans);\n+\tnft_trans_free(trans);\n }\n \n static void nft_set_abort_update(struct list_head *set_update_list)\n@@ -11468,6 +11489,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)\n \t\tnf_tables_abort_release(trans);\n \t}\n \n+\tfolio_scratchpad_reset(\u0026nft_net-\u003etrans_scratchpad);\n+\n \treturn err;\n }\n \n@@ -11499,6 +11522,9 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb,\n \telse\n \t\tnf_tables_module_autoload_cleanup(net);\n \n+\tfolio_scratchpad_free(\u0026nft_net-\u003etrans_scratchpad);\n+\tfolio_scratchpad_init(\u0026nft_net-\u003etrans_scratchpad, 4);\n+\n \tmutex_unlock(\u0026nft_net-\u003ecommit_mutex);\n \n \treturn ret;\n@@ -12142,6 +12168,8 @@ static int __net_init nf_tables_init_net(struct net *net)\n \tINIT_LIST_HEAD(\u0026nft_net-\u003ebinding_list);\n \tINIT_LIST_HEAD(\u0026nft_net-\u003emodule_list);\n \tINIT_LIST_HEAD(\u0026nft_net-\u003enotify_list);\n+\tfolio_scratchpad_init_key(\u0026nft_net-\u003etrans_scratchpad, get_order(SZ_64K),\n+\t\t\t\t  \u0026nft_trans_scratchpad_key);\n \tmutex_init(\u0026nft_net-\u003ecommit_mutex);\n \tnet-\u003enft.base_seq = 1;\n \tnft_net-\u003egc_seq = 0;\n@@ -12186,6 +12214,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)\n \tWARN_ON_ONCE(!list_empty(\u0026nft_net-\u003emodule_list));\n \tWARN_ON_ONCE(!list_empty(\u0026nft_net-\u003enotify_list));\n \tWARN_ON_ONCE(!list_empty(\u0026nft_net-\u003edestroy_list));\n+\tfolio_scratchpad_free(\u0026nft_net-\u003etrans_scratchpad);\n }\n \n static void nf_tables_exit_batch(struct list_head *net_exit_list)\n"
}

2/1 2026/08/17 18:11 agent patch-evaluator
2m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "DRM_NOUVEAU",
    "BPF_SYSCALL",
    "PROVE_LOCKING",
    "NF_TABLES"
  ],
  "FocusSymbols": [
    "folio_scratchpad_alloc",
    "folio_scratchpad_reset",
    "folio_scratchpad_free",
    "folio_pool_init_align",
    "folio_pool_init_key",
    "folio_pool_init",
    "folio_scratchpad_stats",
    "bpf_check",
    "generic_map_update_batch",
    "drm_gpuvm_sm_map",
    "drm_gpuvm_sm_unmap",
    "drm_gpuvm_init"
  ],
  "Reasoning": "The patch introduces a new direct-map large folio bump allocator (`folio_pool` and `folio_scratchpad`) and integrates it into several core kernel subsystems: DRM GPUVM, BPF syscalls/verifier, lockdep, and nf_tables. These changes modify how memory is allocated and managed in these subsystems, which has a significant functional impact and could introduce memory corruption, use-after-free, or memory leak vulnerabilities. The code is reachable from user space via BPF syscalls, netlink messages (nf_tables), and DRM ioctls. Therefore, it is highly 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).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

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.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
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.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. 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 c1582d0063f2c118dce289575969335d8319a7e1
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 17 18:11:30 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c
index c422c5af1f4bf..6bb82ac9e2d6c 100644
--- a/drivers/gpu/drm/drm_gpuvm.c
+++ b/drivers/gpu/drm/drm_gpuvm.c
@@ -1109,6 +1109,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
 	spin_lock_init(&gpuvm->evict.lock);
 
 	init_llist_head(&gpuvm->bo_defer);
+	folio_scratchpad_init_key(&gpuvm->va_scratchpad, get_order(SZ_64K),
+				  &gpuvm_scratchpad_key);
 
 	kref_init(&gpuvm->kref);
 
@@ -1155,6 +1157,7 @@ drm_gpuvm_fini(struct drm_gpuvm *gpuvm)
 	drm_WARN(gpuvm->drm, !llist_empty(&gpuvm->bo_defer),
 		 "VM BO cleanup list should be empty.\n");
 
+	folio_scratchpad_free(&gpuvm->va_scratchpad);
 	drm_gem_object_put(gpuvm->r_obj);
 }
 
@@ -2849,6 +2852,9 @@ drm_gpuvm_sm_unmap_exec_lock(struct drm_gpuvm *gpuvm, struct drm_exec *exec,
 }
 EXPORT_SYMBOL_GPL(drm_gpuvm_sm_unmap_exec_lock);
 
+DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(gpuvm_scratchpad_key, va_scratchpad,
+				   "Toggle DRM GPUVM range folio scratchpad allocator");
+
 static struct drm_gpuva_op *
 gpuva_op_alloc(struct drm_gpuvm *gpuvm)
 {
@@ -2858,7 +2864,8 @@ gpuva_op_alloc(struct drm_gpuvm *gpuvm)
 	if (fn && fn->op_alloc)
 		op = fn->op_alloc();
 	else
-		op = kzalloc_obj(*op);
+		op = folio_scratchpad_alloc_obj(gpuvm, va_scratchpad,
+						struct drm_gpuva_op, GFP_KERNEL);
 
 	if (unlikely(!op))
 		return NULL;
@@ -2875,7 +2882,7 @@ gpuva_op_free(struct drm_gpuvm *gpuvm,
 	if (fn && fn->op_free)
 		fn->op_free(op);
 	else
-		kfree(op);
+		folio_scratchpad_free_elem(op);
 }
 
 static int
diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h
index 655bd9104ffb2..362b1d88d30d8 100644
--- a/include/drm/drm_gpuvm.h
+++ b/include/drm/drm_gpuvm.h
@@ -30,6 +30,7 @@
 #include <linux/llist.h>
 #include <linux/rbtree.h>
 #include <linux/types.h>
+#include <linux/folio_pool.h>
 
 #include <drm/drm_device.h>
 #include <drm/drm_gem.h>
@@ -338,6 +339,11 @@ struct drm_gpuvm {
 	 * @bo_defer: structure holding vm_bos that need to be destroyed
 	 */
 	struct llist_head bo_defer;
+
+	/**
+	 * @va_scratchpad: folio scratchpad for transient GPU VA range allocations
+	 */
+	struct folio_scratchpad va_scratchpad;
 };
 
 void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 39a851e690ec4..43f5f0eaffac6 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -898,6 +898,8 @@ struct bpf_scc_info {
 
 struct bpf_liveness;
 
+#include <linux/folio_pool.h>
+
 /* single container for all structs
  * one verifier_env per bpf_check() call
  */
@@ -907,6 +909,7 @@ struct bpf_verifier_env {
 	struct bpf_prog *prog;		/* eBPF program being verified */
 	const struct bpf_verifier_ops *ops;
 	struct module *attach_btf_mod;	/* The owner module of prog->aux->attach_btf */
+	struct folio_pool state_pool;	/* pool for transient state nodes */
 	struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */
 	int stack_size;			/* number of states to be processed */
 	bool strict_alignment;		/* perform strict pointer alignment checks */
diff --git a/include/linux/folio_pool.h b/include/linux/folio_pool.h
new file mode 100644
index 0000000000000..5bbca8818877c
--- /dev/null
+++ b/include/linux/folio_pool.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef _LINUX_FOLIO_POOL_H
+#define _LINUX_FOLIO_POOL_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/page-flags.h>
+#include <linux/mm.h>
+#include <linux/gfp.h>
+#include <linux/jump_label.h>
+#include <linux/moduleparam.h>
+#include <linux/cleanup.h>
+
+DECLARE_STATIC_KEY_TRUE(folio_pool_enabled_key);
+
+#define DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(key_name, param_name, desc)		\
+	DEFINE_STATIC_KEY_TRUE(key_name);					\
+	static int key_name##_set(const char *val, const struct kernel_param *kp) \
+	{									\
+		bool enable;							\
+		int ret = kstrtobool(val, &enable);				\
+		if (ret)							\
+			return ret;						\
+		if (enable)							\
+			static_branch_enable(&key_name);			\
+		else								\
+			static_branch_disable(&key_name);			\
+		return 0;							\
+	}									\
+	static int key_name##_get(char *buffer, const struct kernel_param *kp)	\
+	{									\
+		return sprintf(buffer, "%c\n",					\
+			       static_branch_likely(&key_name) ? 'Y' : 'N');	\
+	}									\
+	static const struct kernel_param_ops key_name##_ops = {			\
+		.set = key_name##_set,						\
+		.get = key_name##_get,						\
+	};									\
+	module_param_cb(param_name, &key_name##_ops, NULL, 0644);		\
+	MODULE_PARM_DESC(param_name, desc)
+
+#define DEFINE_FOLIO_POOL_STATIC_KEY_PARAM_FALSE(key_name, param_name, desc)	\
+	DEFINE_STATIC_KEY_FALSE(key_name);					\
+	static int key_name##_set(const char *val, const struct kernel_param *kp) \
+	{									\
+		bool enable;							\
+		int ret = kstrtobool(val, &enable);				\
+		if (ret)							\
+			return ret;						\
+		if (enable)							\
+			static_branch_enable(&key_name);			\
+		else								\
+			static_branch_disable(&key_name);			\
+		return 0;							\
+	}									\
+	static int key_name##_get(char *buffer, const struct kernel_param *kp)	\
+	{									\
+		return sprintf(buffer, "%c\n",					\
+			       static_branch_unlikely(&key_name) ? 'Y' : 'N');	\
+	}									\
+	static const struct kernel_param_ops key_name##_ops = {			\
+		.set = key_name##_set,						\
+		.get = key_name##_get,						\
+	};									\
+	module_param_cb(param_name, &key_name##_ops, NULL, 0644);		\
+	MODULE_PARM_DESC(param_name, desc)
+
+#define FOLIO_POOL_64K_ORDER (PAGE_SHIFT < 16 ? 16 - PAGE_SHIFT : 0)
+
+struct folio_pool_chunk {
+	struct list_head link;
+	struct folio *folio;
+};
+
+/*
+ * 1. Variable-Sized Scratchpad (Core Bump Allocator Engine)
+ */
+struct folio_scratchpad {
+	struct list_head chunks;
+	void *free_ptr;
+	size_t remaining;
+	unsigned int chunk_order;
+	struct static_key *key;
+	spinlock_t lock;
+};
+
+#define FOLIO_SCRATCHPAD_INIT(name, _order) {				\
+	.chunks = LIST_HEAD_INIT((name).chunks),			\
+	.chunk_order = (_order),					\
+	.key = NULL,							\
+	.lock = __SPIN_LOCK_UNLOCKED((name).lock),			\
+}
+
+#define FOLIO_SCRATCHPAD_INIT_KEY(name, _order, _key) {			\
+	.chunks = LIST_HEAD_INIT((name).chunks),			\
+	.chunk_order = (_order),					\
+	.key = (struct static_key *)(_key),				\
+	.lock = __SPIN_LOCK_UNLOCKED((name).lock),			\
+}
+
+void folio_scratchpad_init(struct folio_scratchpad *sp, unsigned int order);
+void folio_scratchpad_init_key(struct folio_scratchpad *sp, unsigned int order,
+			       struct static_key *key);
+void *folio_scratchpad_alloc(struct folio_scratchpad *sp, size_t size,
+			     size_t align, gfp_t gfp);
+void folio_scratchpad_reset(struct folio_scratchpad *sp);
+void folio_scratchpad_free(struct folio_scratchpad *sp);
+void folio_scratchpad_stats(struct folio_scratchpad *sp, unsigned int *nr_chunks,
+			    size_t *chunk_size, size_t *tail_used);
+
+DEFINE_FREE(folio_scratchpad, struct folio_scratchpad *, if (_T) folio_scratchpad_free(_T))
+
+/**
+ * is_folio_pool_ptr - Check whether an address resides in a folio pool/scratchpad
+ * @ptr: Object pointer to test
+ *
+ * Direct-map large folios allocated via folio_alloc() are not slab pages,
+ * unlike objects returned by kmalloc/kzalloc.
+ */
+static inline bool is_folio_pool_ptr(const void *ptr)
+{
+	return ptr && !is_vmalloc_addr(ptr) && !folio_test_slab(virt_to_folio(ptr));
+}
+
+/**
+ * folio_pool_free_elem - Safely release a pool object or SLUB fallback element
+ * @ptr: Object pointer to release
+ *
+ * If @ptr belongs to a direct-map large folio, individual deallocation is a safe
+ * no-op (reclaimed in bulk by folio_scratchpad_free/reset). If @ptr was allocated
+ * via SLUB/vmalloc fallback, releases it immediately.
+ */
+static inline void folio_pool_free_elem(const void *ptr)
+{
+	if (!ptr || is_folio_pool_ptr(ptr))
+		return;
+	kvfree(ptr);
+}
+
+static inline void folio_scratchpad_free_elem(const void *ptr)
+{
+	folio_pool_free_elem(ptr);
+}
+
+/**
+ * folio_pool_realloc - Reallocate memory for an object, handling pool vs slab backing
+ * @ptr: Existing object pointer (may be from folio_pool/scratchpad or SLUB)
+ * @old_size: Size of original object
+ * @new_size: Desired new size
+ * @gfp: Allocation flags
+ *
+ * If @ptr is SLUB-backed, delegates directly to krealloc(). If @ptr resides
+ * in a direct-map large folio, allocates a fresh @new_size buffer from SLUB
+ * and copies @old_size bytes; the original scratchpad slot remains abandoned
+ * until the entire scratchpad is released or reset at batch boundary.
+ */
+static inline void *folio_pool_realloc(void *ptr, size_t old_size,
+				       size_t new_size, gfp_t gfp)
+{
+	void *new_ptr;
+
+	if (!ptr)
+		return kmalloc(new_size, gfp);
+
+	if (!is_folio_pool_ptr(ptr))
+		return krealloc(ptr, new_size, gfp);
+
+	new_ptr = kmalloc(new_size, gfp);
+	if (new_ptr)
+		memcpy(new_ptr, ptr, min(old_size, new_size));
+	return new_ptr;
+}
+
+static inline void *folio_scratchpad_realloc(void *ptr, size_t old_size,
+					     size_t new_size, gfp_t gfp)
+{
+	return folio_pool_realloc(ptr, old_size, new_size, gfp);
+}
+
+/**
+ * folio_scratchpad_alloc_obj - Allocate a typed object from an embedded scratchpad
+ * @ptr: Pointer to container struct (e.g. nft_net)
+ * @member: Name of the struct folio_scratchpad field (e.g. trans_scratchpad)
+ * @type: Type of object being allocated
+ * @gfp: Allocation flags
+ */
+#define folio_scratchpad_alloc_obj(ptr, member, type, gfp)			\
+	((type *)folio_scratchpad_alloc(&(ptr)->member,				\
+					sizeof(type),				\
+					__alignof__(type),			\
+					gfp))
+
+/**
+ * folio_scratchpad_alloc_bytes - Allocate variable-sized bytes from an embedded scratchpad
+ * @ptr: Pointer to container struct (e.g. nft_net)
+ * @member: Name of the struct folio_scratchpad field (e.g. trans_scratchpad)
+ * @size: Size of memory to allocate
+ * @align: Alignment requirement
+ * @gfp: Allocation flags
+ */
+#define folio_scratchpad_alloc_bytes(ptr, member, size, align, gfp)		\
+	folio_scratchpad_alloc(&(ptr)->member, size, align, gfp)
+
+/**
+ * folio_scratchpad_alloc_type - Allocate a typed object from a scratchpad pointer
+ * @sp: Pointer to struct folio_scratchpad
+ * @type: Type of object being allocated
+ * @gfp: Allocation flags
+ */
+#define folio_scratchpad_alloc_type(sp, type, gfp)				\
+	((type *)folio_scratchpad_alloc(sp,					\
+					sizeof(type),				\
+					__alignof__(type),			\
+					gfp))
+
+/*
+ * 2. Fixed-Slot Uniform Pool (Specialized Thin Wrapper on Scratchpad)
+ */
+struct folio_pool {
+	struct folio_scratchpad base;
+	size_t elem_size;
+	size_t elem_align;
+};
+
+#define FOLIO_POOL_INIT(name, _elem_size, _order) {			\
+	.base = FOLIO_SCRATCHPAD_INIT((name).base, _order),		\
+	.elem_size = (_elem_size),					\
+	.elem_align = ((_elem_size) > sizeof(void *) ? (_elem_size) : sizeof(void *)), \
+}
+
+#define FOLIO_POOL_INIT_KEY(name, _elem_size, _order, _key) {		\
+	.base = FOLIO_SCRATCHPAD_INIT_KEY((name).base, _order, _key),	\
+	.elem_size = (_elem_size),					\
+	.elem_align = ((_elem_size) > sizeof(void *) ? (_elem_size) : sizeof(void *)), \
+}
+
+void folio_pool_init(struct folio_pool *fp, size_t elem_size, unsigned int order);
+void folio_pool_init_key(struct folio_pool *fp, size_t elem_size, unsigned int order,
+			 struct static_key *key);
+void folio_pool_init_align(struct folio_pool *fp, size_t elem_size,
+			   size_t elem_align, unsigned int order);
+
+static inline void *folio_pool_alloc(struct folio_pool *fp, gfp_t gfp)
+{
+	return folio_scratchpad_alloc(&fp->base, fp->elem_size, fp->elem_align, gfp);
+}
+
+static inline void folio_pool_free(struct folio_pool *fp)
+{
+	folio_scratchpad_free(&fp->base);
+}
+
+static inline void folio_pool_stats(struct folio_pool *fp, unsigned int *nr_chunks,
+				    size_t *chunk_size, size_t *tail_used)
+{
+	folio_scratchpad_stats(&fp->base, nr_chunks, chunk_size, tail_used);
+}
+
+/**
+ * folio_pool_alloc_obj - Allocate a typed object from a container's embedded folio_pool
+ * @ptr: Pointer to container struct (e.g. env)
+ * @member: Name of the struct folio_pool field (e.g. state_pool)
+ * @type: Type of object being allocated (e.g. struct bpf_verifier_stack_elem)
+ * @gfp: Allocation flags
+ */
+#define folio_pool_alloc_obj(ptr, member, type, gfp)				\
+	((type *)folio_pool_alloc(&(ptr)->member, gfp))
+
+/**
+ * folio_pool_alloc_type - Allocate a typed object from a struct folio_pool pointer
+ * @fp: Pointer to struct folio_pool
+ * @type: Type of object being allocated
+ * @gfp: Allocation flags
+ */
+#define folio_pool_alloc_type(fp, type, gfp)					\
+	((type *)folio_pool_alloc(fp, gfp))
+
+#endif /* _LINUX_FOLIO_POOL_H */
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 3be612145c130..b4a6cc1f46afd 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1941,6 +1941,8 @@ __printf(2, 3) int nft_request_module(struct net *net, const char *fmt, ...);
 static inline int nft_request_module(struct net *net, const char *fmt, ...) { return -ENOENT; }
 #endif
 
+#include <linux/folio_pool.h>
+
 struct nftables_pernet {
 	struct list_head	tables;
 	struct list_head	commit_list;
@@ -1949,6 +1951,7 @@ struct nftables_pernet {
 	struct list_head	binding_list;
 	struct list_head	module_list;
 	struct list_head	notify_list;
+	struct folio_scratchpad	trans_scratchpad;
 	struct mutex		commit_mutex;
 	u64			table_handle;
 	u64			tstamp;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 6db306d23b479..8ef48673b9339 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -19,6 +19,7 @@
 #include <linux/fdtable.h>
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/folio_pool.h>
 #include <linux/license.h>
 #include <linux/filter.h>
 #include <linux/kernel.h>
@@ -2059,12 +2060,15 @@ int generic_map_delete_batch(struct bpf_map *map,
 	return err;
 }
 
+DEFINE_STATIC_KEY_TRUE(bpf_batch_scratchpad_key);
+
 int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
 			     const union bpf_attr *attr,
 			     union bpf_attr __user *uattr)
 {
 	void __user *values = u64_to_user_ptr(attr->batch.values);
 	void __user *keys = u64_to_user_ptr(attr->batch.keys);
+	struct folio_scratchpad batch_sp;
 	u32 value_size, cp, max_count;
 	void *key, *value;
 	int err = 0;
@@ -2083,13 +2087,17 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
 	if (put_user(0, &uattr->batch.count))
 		return -EFAULT;
 
-	key = kvmalloc(map->key_size, GFP_USER | __GFP_NOWARN);
+	folio_scratchpad_init_key(&batch_sp, 0, &bpf_batch_scratchpad_key);
+
+	key = folio_scratchpad_alloc(&batch_sp, map->key_size,
+				     sizeof(void *), GFP_USER | __GFP_NOWARN);
 	if (!key)
 		return -ENOMEM;
 
-	value = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);
+	value = folio_scratchpad_alloc(&batch_sp, value_size,
+				       sizeof(void *), GFP_USER | __GFP_NOWARN);
 	if (!value) {
-		kvfree(key);
+		folio_scratchpad_free(&batch_sp);
 		return -ENOMEM;
 	}
 
@@ -2111,8 +2119,7 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
 	if (copy_to_user(&uattr->batch.count, &cp, sizeof(cp)))
 		err = -EFAULT;
 
-	kvfree(value);
-	kvfree(key);
+	folio_scratchpad_free(&batch_sp);
 
 	return err;
 }
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index fdc5fbb1f78ca..8a9e66ce4dc8e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1725,7 +1725,7 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
 		*prev_insn_idx = head->prev_insn_idx;
 	elem = head->next;
 	bpf_free_verifier_state(&head->st, false);
-	kfree(head);
+	folio_pool_free_elem(head);
 	env->head = elem;
 	env->stack_size--;
 	return 0;
@@ -1743,6 +1743,8 @@ static bool error_recoverable_with_nospec(int err)
 	return err == -EPERM || err == -EACCES || err == -EINVAL;
 }
 
+DEFINE_STATIC_KEY_TRUE(bpf_state_pool_key);
+
 static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
 					     int insn_idx, int prev_insn_idx,
 					     bool speculative)
@@ -1751,7 +1753,9 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
 	struct bpf_verifier_stack_elem *elem;
 	int err;
 
-	elem = kzalloc_obj(struct bpf_verifier_stack_elem, GFP_KERNEL_ACCOUNT);
+	elem = folio_pool_alloc_obj(env, state_pool,
+				    struct bpf_verifier_stack_elem,
+				    GFP_KERNEL_ACCOUNT);
 	if (!elem)
 		return ERR_PTR(-ENOMEM);
 
@@ -2275,7 +2279,9 @@ static struct bpf_verifier_state *push_async_cb(struct bpf_verifier_env *env,
 	struct bpf_verifier_stack_elem *elem;
 	struct bpf_func_state *frame;
 
-	elem = kzalloc_obj(struct bpf_verifier_stack_elem, GFP_KERNEL_ACCOUNT);
+	elem = folio_pool_alloc_obj(env, state_pool,
+				    struct bpf_verifier_stack_elem,
+				    GFP_KERNEL_ACCOUNT);
 	if (!elem)
 		return ERR_PTR(-ENOMEM);
 
@@ -19789,6 +19795,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
 	if (!env)
 		return -ENOMEM;
 
+	folio_pool_init_key(&env->state_pool, sizeof(struct bpf_verifier_stack_elem),
+			    get_order(SZ_64K), &bpf_state_pool_key);
+
 	env->bt.env = env;
 
 	len = (*prog)->len;
@@ -20055,6 +20064,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
 	bpf_clear_insn_aux_data(env, 0, env->prog->len);
 err_free_env:
 	bpf_stack_liveness_free(env);
+	folio_pool_free(&env->state_pool);
 	kvfree(env->cfg.insn_postorder);
 	kvfree(env->scc_info);
 	kvfree(env->succ);
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2d4c5bab5af88..d9541207b342b 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -58,12 +58,37 @@
 #include <linux/context_tracking.h>
 #include <linux/console.h>
 #include <linux/kasan.h>
+#include <linux/folio_pool.h>
+
+DEFINE_STATIC_KEY_TRUE(lockdep_pool_key);
+static struct folio_pool lockdep_pool =
+	FOLIO_POOL_INIT_KEY(lockdep_pool, sizeof(struct lock_list),
+			    FOLIO_POOL_64K_ORDER, &lockdep_pool_key);
+
+static int __init setup_lockdep_folio_pool(char *str)
+{
+	bool enable;
+
+	if (!kstrtobool(str, &enable)) {
+		if (enable)
+			static_branch_enable(&lockdep_pool_key);
+		else
+			static_branch_disable(&lockdep_pool_key);
+	}
+	return 1;
+}
+__setup("lockdep.folio_pool=", setup_lockdep_folio_pool);
 
 #include <asm/sections.h>
 
 #include "lockdep_internals.h"
 #include "lock_events.h"
 
+void lockdep_pool_stats(unsigned int *nr_chunks, size_t *chunk_size, size_t *tail_used)
+{
+	folio_pool_stats(&lockdep_pool, nr_chunks, chunk_size, tail_used);
+}
+
 #include <trace/events/lock.h>
 
 #ifdef CONFIG_PROVE_LOCKING
@@ -204,9 +229,13 @@ static inline int debug_locks_off_graph_unlock(void)
 	return ret;
 }
 
+#define BOOTSTRAP_LOCKDEP_ENTRIES 4096UL
+
+static struct lock_list early_list_entries[BOOTSTRAP_LOCKDEP_ENTRIES] __initdata;
+static unsigned long early_list_entries_in_use[BITS_TO_LONGS(BOOTSTRAP_LOCKDEP_ENTRIES)] __initdata;
+static struct lock_list *bootstrap_entries __read_mostly = early_list_entries;
+static unsigned long *bootstrap_entries_in_use __read_mostly = early_list_entries_in_use;
 unsigned long nr_list_entries;
-static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES];
-static DECLARE_BITMAP(list_entries_in_use, MAX_LOCKDEP_ENTRIES);
 
 /*
  * All data structures here are protected by the global debug_lock.
@@ -1028,9 +1057,9 @@ static bool class_lock_list_valid(struct lock_class *c, struct list_head *h)
 
 	list_for_each_entry(e, h, entry) {
 		if (e->links_to != c) {
-			printk(KERN_INFO "class %s: mismatch for lock entry %ld; class %s <> %s",
+			printk(KERN_INFO "class %s: mismatch for lock entry %px; class %s <> %s",
 			       c->name ? : "(?)",
-			       (unsigned long)(e - list_entries),
+			       e,
 			       e->links_to && e->links_to->name ?
 			       e->links_to->name : "(?)",
 			       e->class && e->class->name ? e->class->name :
@@ -1119,35 +1148,37 @@ static bool __check_data_structures(void)
 		}
 	}
 
-	/*
-	 * Check whether all list entries that are in use occur in a class
-	 * lock list.
-	 */
-	for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		e = list_entries + i;
-		if (!in_any_class_list(&e->entry)) {
-			printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
-			       (unsigned int)(e - list_entries),
-			       e->class->name ? : "(?)",
-			       e->links_to->name ? : "(?)");
-			return false;
+	if (bootstrap_entries) {
+		/*
+		 * Check whether all list entries that are in use occur in a class
+		 * lock list.
+		 */
+		for_each_set_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {
+			e = bootstrap_entries + i;
+			if (!in_any_class_list(&e->entry)) {
+				printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
+				       (unsigned int)(e - bootstrap_entries),
+				       e->class->name ? : "(?)",
+				       e->links_to->name ? : "(?)");
+				return false;
+			}
 		}
-	}
 
-	/*
-	 * Check whether all list entries that are not in use do not occur in
-	 * a class lock list.
-	 */
-	for_each_clear_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		e = list_entries + i;
-		if (in_any_class_list(&e->entry)) {
-			printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
-			       (unsigned int)(e - list_entries),
-			       e->class && e->class->name ? e->class->name :
-			       "(?)",
-			       e->links_to && e->links_to->name ?
-			       e->links_to->name : "(?)");
-			return false;
+		/*
+		 * Check whether all list entries that are not in use do not occur in
+		 * a class lock list.
+		 */
+		for_each_clear_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {
+			e = bootstrap_entries + i;
+			if (in_any_class_list(&e->entry)) {
+				printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
+				       (unsigned int)(e - bootstrap_entries),
+				       e->class && e->class->name ? e->class->name :
+				       "(?)",
+				       e->links_to && e->links_to->name ?
+				       e->links_to->name : "(?)");
+				return false;
+			}
 		}
 	}
 
@@ -1400,22 +1431,32 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
  */
 static struct lock_list *alloc_list_entry(void)
 {
-	int idx = find_first_zero_bit(list_entries_in_use,
-				      ARRAY_SIZE(list_entries));
+	struct lock_list *p;
 
-	if (idx >= ARRAY_SIZE(list_entries)) {
-		if (!debug_locks_off_graph_unlock())
-			return NULL;
+	if (bootstrap_entries) {
+		int idx = find_first_zero_bit(bootstrap_entries_in_use,
+					      BOOTSTRAP_LOCKDEP_ENTRIES);
 
-		nbcon_cpu_emergency_enter();
-		print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low!");
-		dump_stack();
-		nbcon_cpu_emergency_exit();
-		return NULL;
+		if (idx < BOOTSTRAP_LOCKDEP_ENTRIES) {
+			__set_bit(idx, bootstrap_entries_in_use);
+			nr_list_entries++;
+			return bootstrap_entries + idx;
+		}
+	}
+
+	p = folio_pool_alloc_type(&lockdep_pool, struct lock_list, GFP_ATOMIC);
+	if (p) {
+		nr_list_entries++;
+		return p;
 	}
-	nr_list_entries++;
-	__set_bit(idx, list_entries_in_use);
-	return list_entries + idx;
+	if (!debug_locks_off_graph_unlock())
+		return NULL;
+
+	nbcon_cpu_emergency_enter();
+	print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low and folio_pool exhausted!");
+	dump_stack();
+	nbcon_cpu_emergency_exit();
+	return NULL;
 }
 
 /*
@@ -6238,13 +6279,25 @@ static void remove_class_from_lock_chains(struct pending_free *pf,
 	}
 }
 
+static inline bool is_bootstrap_entry(const struct lock_list *entry)
+{
+	return bootstrap_entries &&
+	       entry >= bootstrap_entries &&
+	       entry < bootstrap_entries + BOOTSTRAP_LOCKDEP_ENTRIES;
+}
+
+static inline void clear_bootstrap_entry_bit(const struct lock_list *entry)
+{
+	if (is_bootstrap_entry(entry))
+		__clear_bit(entry - bootstrap_entries, bootstrap_entries_in_use);
+}
+
 /*
  * Remove all references to a lock class. The caller must hold the graph lock.
  */
 static void zap_class(struct pending_free *pf, struct lock_class *class)
 {
-	struct lock_list *entry;
-	int i;
+	struct lock_list *entry, *tmp, *other, *other_tmp;
 
 	WARN_ON_ONCE(!class->key);
 
@@ -6252,11 +6305,29 @@ static void zap_class(struct pending_free *pf, struct lock_class *class)
 	 * Remove all dependencies this lock is
 	 * involved in:
 	 */
-	for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		entry = list_entries + i;
-		if (entry->class != class && entry->links_to != class)
-			continue;
-		__clear_bit(i, list_entries_in_use);
+	list_for_each_entry_safe(entry, tmp, &class->locks_after, entry) {
+		list_for_each_entry_safe(other, other_tmp, &entry->links_to->locks_before, entry) {
+			if (other->links_to == class) {
+				clear_bootstrap_entry_bit(other);
+				nr_list_entries--;
+				list_del_rcu(&other->entry);
+				break;
+			}
+		}
+		clear_bootstrap_entry_bit(entry);
+		nr_list_entries--;
+		list_del_rcu(&entry->entry);
+	}
+	list_for_each_entry_safe(entry, tmp, &class->locks_before, entry) {
+		list_for_each_entry_safe(other, other_tmp, &entry->links_to->locks_after, entry) {
+			if (other->links_to == class) {
+				clear_bootstrap_entry_bit(other);
+				nr_list_entries--;
+				list_del_rcu(&other->entry);
+				break;
+			}
+		}
+		clear_bootstrap_entry_bit(entry);
 		nr_list_entries--;
 		list_del_rcu(&entry->entry);
 	}
@@ -6631,6 +6702,9 @@ EXPORT_SYMBOL_GPL(lockdep_unregister_key);
 
 void __init lockdep_init(void)
 {
+	bootstrap_entries = early_list_entries;
+	bootstrap_entries_in_use = early_list_entries_in_use;
+
 	pr_info("Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\n");
 
 	pr_info("... MAX_LOCKDEP_SUBCLASSES:  %lu\n", MAX_LOCKDEP_SUBCLASSES);
@@ -6645,8 +6719,8 @@ void __init lockdep_init(void)
 	       (sizeof(lock_classes) +
 		sizeof(lock_classes_in_use) +
 		sizeof(classhash_table) +
-		sizeof(list_entries) +
-		sizeof(list_entries_in_use) +
+		sizeof(early_list_entries) +
+		sizeof(early_list_entries_in_use) +
 		sizeof(chainhash_table) +
 		sizeof(delayed_free)
 #ifdef CONFIG_PROVE_LOCKING
@@ -6668,6 +6742,104 @@ void __init lockdep_init(void)
 	       sizeof(((struct task_struct *)NULL)->held_locks));
 }
 
+static int __init lockdep_boot_report(void)
+{
+	pr_info("lockdep: %lu/%lu bootstrap entries used before buddy init, folio_pool active\n",
+		min_t(unsigned long, nr_list_entries, BOOTSTRAP_LOCKDEP_ENTRIES),
+		BOOTSTRAP_LOCKDEP_ENTRIES);
+	return 0;
+}
+core_initcall(lockdep_boot_report);
+
+static int __init lockdep_compact_boot_graph(void)
+{
+	struct lock_class *class;
+	struct lock_list *entry, *tmp, *new_entry;
+	unsigned long flags;
+	unsigned long migrated = 0;
+
+	if (!debug_locks)
+		return 0;
+
+	/* Pre-allocate 64KB folio chunk outside graph_lock to avoid MM recursion */
+	new_entry = folio_pool_alloc_type(&lockdep_pool, struct lock_list, GFP_KERNEL);
+	if (!new_entry) {
+		pr_err("lockdep: failed to pre-allocate folio chunk for boot compaction\n");
+		return -ENOMEM;
+	}
+
+	raw_local_irq_save(flags);
+	if (!graph_lock()) {
+		raw_local_irq_restore(flags);
+		return 0;
+	}
+
+	list_for_each_entry(class, &all_lock_classes, lock_entry) {
+		list_for_each_entry_safe(entry, tmp, &class->locks_after, entry) {
+			if (is_bootstrap_entry(entry)) {
+				if (new_entry) {
+					*new_entry = *entry;
+					list_replace_rcu(&entry->entry, &new_entry->entry);
+					new_entry = NULL;
+				} else {
+					struct lock_list *slot;
+
+					slot = folio_pool_alloc_type(&lockdep_pool,
+								     struct lock_list,
+								     GFP_ATOMIC);
+					if (!slot) {
+						debug_locks_off_graph_unlock();
+						raw_local_irq_restore(flags);
+						pr_err("lockdep: folio chunk exhausted during boot compaction\n");
+						return -ENOMEM;
+					}
+					*slot = *entry;
+					list_replace_rcu(&entry->entry, &slot->entry);
+				}
+				migrated++;
+			}
+		}
+
+		list_for_each_entry_safe(entry, tmp, &class->locks_before, entry) {
+			if (is_bootstrap_entry(entry)) {
+				if (new_entry) {
+					*new_entry = *entry;
+					list_replace_rcu(&entry->entry, &new_entry->entry);
+					new_entry = NULL;
+				} else {
+					struct lock_list *slot;
+
+					slot = folio_pool_alloc_type(&lockdep_pool,
+								     struct lock_list,
+								     GFP_ATOMIC);
+					if (!slot) {
+						debug_locks_off_graph_unlock();
+						raw_local_irq_restore(flags);
+						pr_err("lockdep: folio chunk exhausted during boot compaction\n");
+						return -ENOMEM;
+					}
+					*slot = *entry;
+					list_replace_rcu(&entry->entry, &slot->entry);
+				}
+				migrated++;
+			}
+		}
+	}
+
+	/* Adjust counter so compaction does not double-count migrated nodes */
+	nr_list_entries -= migrated;
+
+	bootstrap_entries = NULL;
+	bootstrap_entries_in_use = NULL;
+	graph_unlock();
+	raw_local_irq_restore(flags);
+
+	pr_info("lockdep: compacted %lu boot entries into folio_pool, freeing bootstrap memory\n",
+		migrated);
+	return 0;
+}
+late_initcall(lockdep_compact_boot_graph);
+
 static void
 print_freed_lock_bug(struct task_struct *curr, const void *mem_from,
 		     const void *mem_to, struct held_lock *hlock)
diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h
index 0e5e6ffe91a3f..f802160c0dd53 100644
--- a/kernel/locking/lockdep_internals.h
+++ b/kernel/locking/lockdep_internals.h
@@ -159,6 +159,8 @@ extern unsigned long max_lock_class_idx;
 extern struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
 extern unsigned long lock_classes_in_use[];
 
+void lockdep_pool_stats(unsigned int *nr_chunks, size_t *chunk_size, size_t *tail_used);
+
 #ifdef CONFIG_PROVE_LOCKING
 extern unsigned long lockdep_count_forward_deps(struct lock_class *);
 extern unsigned long lockdep_count_backward_deps(struct lock_class *);
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index 1916db9aa46b6..3e6f24ae9b34b 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -288,8 +288,20 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
 			nr_lock_classes, MAX_LOCKDEP_KEYS);
 	seq_printf(m, " dynamic-keys:                  %11lu\n",
 			nr_dynamic_keys);
-	seq_printf(m, " direct dependencies:           %11lu [max: %lu]\n",
-			nr_list_entries, MAX_LOCKDEP_ENTRIES);
+	{
+		unsigned int fp_chunks = 0;
+		size_t fp_chunk_sz = 0, fp_tail_used = 0;
+
+		lockdep_pool_stats(&fp_chunks, &fp_chunk_sz, &fp_tail_used);
+		if (fp_chunks) {
+			seq_printf(m, " direct dependencies:           %11lu [dynamic: %u x %zu kB, tail: %zu kB/%zu kB]\n",
+				   nr_list_entries, fp_chunks, fp_chunk_sz / 1024,
+				   fp_tail_used / 1024, fp_chunk_sz / 1024);
+		} else {
+			seq_printf(m, " direct dependencies:           %11lu [max: %lu]\n",
+				   nr_list_entries, MAX_LOCKDEP_ENTRIES);
+		}
+	}
 	seq_printf(m, " indirect dependencies:         %11lu\n",
 			sum_forward_deps);
 
diff --git a/lib/Makefile b/lib/Makefile
index 7f75cc6edf94a..b5f2f41f96fbd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -58,7 +58,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \
 	 bsearch.o find_bit.o llist.o lwq.o memweight.o kfifo.o \
 	 percpu-refcount.o rhashtable.o base64.o \
 	 once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \
-	 generic-radix-tree.o bitmap-str.o
+	 generic-radix-tree.o bitmap-str.o folio_pool.o
 obj-y += string_helpers.o
 obj-y += hexdump.o
 obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
diff --git a/lib/folio_pool.c b/lib/folio_pool.c
new file mode 100644
index 0000000000000..07a290db6c506
--- /dev/null
+++ b/lib/folio_pool.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Direct-Map Large Folio Scratchpad & Pool bump allocators.
+ */
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/moduleparam.h>
+#include <linux/folio_pool.h>
+
+DEFINE_STATIC_KEY_TRUE(folio_pool_enabled_key);
+EXPORT_SYMBOL_GPL(folio_pool_enabled_key);
+
+static int folio_pool_enabled_set(const char *val, const struct kernel_param *kp)
+{
+	bool enable;
+	int ret = kstrtobool(val, &enable);
+
+	if (ret)
+		return ret;
+
+	if (enable)
+		static_branch_enable(&folio_pool_enabled_key);
+	else
+		static_branch_disable(&folio_pool_enabled_key);
+
+	return 0;
+}
+
+static int folio_pool_enabled_get(char *buffer, const struct kernel_param *kp)
+{
+	return sprintf(buffer, "%c\n", static_branch_likely(&folio_pool_enabled_key) ? 'Y' : 'N');
+}
+
+static const struct kernel_param_ops folio_pool_enabled_ops = {
+	.set = folio_pool_enabled_set,
+	.get = folio_pool_enabled_get,
+};
+
+module_param_cb(enabled, &folio_pool_enabled_ops, NULL, 0644);
+MODULE_PARM_DESC(enabled, "Toggle folio_pool/scratchpad bump allocator (0 = fallback to SLUB)");
+
+/*
+ * 1. Variable-Sized Scratchpad (Core Engine)
+ */
+void folio_scratchpad_init_key(struct folio_scratchpad *sp, unsigned int order,
+			       struct static_key *key)
+{
+	INIT_LIST_HEAD(&sp->chunks);
+	sp->free_ptr = NULL;
+	sp->remaining = 0;
+	sp->chunk_order = order;
+	sp->key = key;
+	spin_lock_init(&sp->lock);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_init_key);
+
+void folio_scratchpad_init(struct folio_scratchpad *sp, unsigned int order)
+{
+	folio_scratchpad_init_key(sp, order, NULL);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_init);
+
+static inline bool folio_scratchpad_is_enabled(const struct folio_scratchpad *sp)
+{
+	if (sp->key)
+		return static_key_enabled(sp->key);
+	return static_branch_likely(&folio_pool_enabled_key);
+}
+
+noinline void *folio_scratchpad_alloc(struct folio_scratchpad *sp, size_t size,
+				     size_t align, gfp_t gfp)
+{
+	struct folio_pool_chunk *chunk;
+	struct folio *folio;
+	void *elem, *base;
+	size_t chunk_size, aligned_size, pad, header_offset;
+	unsigned long flags;
+
+	if (!folio_scratchpad_is_enabled(sp))
+		return kvzalloc(size, gfp);
+
+	if (unlikely(!size))
+		return NULL;
+
+	align = max_t(size_t, sizeof(void *), align ? align : sizeof(void *));
+
+	spin_lock_irqsave(&sp->lock, flags);
+	pad = (uintptr_t)sp->free_ptr & (align - 1);
+	if (pad)
+		pad = align - pad;
+	aligned_size = size + pad;
+
+	if (sp->remaining < aligned_size) {
+		spin_unlock_irqrestore(&sp->lock, flags);
+
+		folio = folio_alloc(gfp, sp->chunk_order);
+		if (!folio && sp->chunk_order > 0)
+			folio = folio_alloc(gfp, 0);
+		if (!folio)
+			return NULL;
+
+		base = folio_address(folio);
+		chunk = (struct folio_pool_chunk *)base;
+		chunk->folio = folio;
+		chunk_size = folio_size(folio);
+		header_offset = ALIGN(sizeof(*chunk), max_t(size_t, sizeof(void *), align));
+
+		spin_lock_irqsave(&sp->lock, flags);
+		list_add(&chunk->link, &sp->chunks);
+		sp->free_ptr = base + header_offset;
+		sp->remaining = chunk_size - header_offset;
+
+		pad = (uintptr_t)sp->free_ptr & (align - 1);
+		if (pad)
+			pad = align - pad;
+		aligned_size = size + pad;
+	}
+
+	if (sp->remaining < aligned_size) {
+		spin_unlock_irqrestore(&sp->lock, flags);
+		return NULL;
+	}
+
+	elem = sp->free_ptr + pad;
+	sp->free_ptr += aligned_size;
+	sp->remaining -= aligned_size;
+	spin_unlock_irqrestore(&sp->lock, flags);
+
+	memset(elem, 0, size);
+	return elem;
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_alloc);
+
+noinline void folio_scratchpad_reset(struct folio_scratchpad *sp)
+{
+	struct folio_pool_chunk *head, *chunk, *tmp;
+	size_t header_offset;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sp->lock, flags);
+	if (list_empty(&sp->chunks)) {
+		sp->free_ptr = NULL;
+		sp->remaining = 0;
+		spin_unlock_irqrestore(&sp->lock, flags);
+		return;
+	}
+
+	/* Retain primary head chunk; free overflow chunks */
+	head = list_first_entry(&sp->chunks, struct folio_pool_chunk, link);
+	list_for_each_entry_safe(chunk, tmp, &sp->chunks, link) {
+		if (chunk == head)
+			continue;
+		list_del(&chunk->link);
+		folio_put(chunk->folio);
+	}
+
+	header_offset = ALIGN(sizeof(*head), sizeof(void *));
+	sp->free_ptr = folio_address(head->folio) + header_offset;
+	sp->remaining = folio_size(head->folio) - header_offset;
+	spin_unlock_irqrestore(&sp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_reset);
+
+noinline void folio_scratchpad_free(struct folio_scratchpad *sp)
+{
+	struct folio_pool_chunk *chunk, *tmp;
+	struct folio *folio;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sp->lock, flags);
+	sp->free_ptr = NULL;
+	sp->remaining = 0;
+	list_for_each_entry_safe(chunk, tmp, &sp->chunks, link) {
+		folio = chunk->folio;
+		list_del(&chunk->link);
+		folio_put(folio);
+	}
+	spin_unlock_irqrestore(&sp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_free);
+
+/*
+ * 2. Fixed-Slot Uniform Pool (Specialized Thin Wrapper on Scratchpad)
+ */
+void folio_pool_init_align(struct folio_pool *fp, size_t elem_size,
+			   size_t elem_align, unsigned int order)
+{
+	folio_scratchpad_init(&fp->base, order);
+	fp->elem_size = elem_size;
+	fp->elem_align = max_t(size_t, sizeof(void *), elem_align ? elem_align : sizeof(void *));
+}
+EXPORT_SYMBOL_GPL(folio_pool_init_align);
+
+void folio_pool_init_key(struct folio_pool *fp, size_t elem_size, unsigned int order,
+			 struct static_key *key)
+{
+	size_t align = sizeof(void *);
+
+	if (elem_size && is_power_of_2(elem_size))
+		align = max_t(size_t, sizeof(void *), elem_size);
+	folio_scratchpad_init_key(&fp->base, order, key);
+	fp->elem_size = elem_size;
+	fp->elem_align = align;
+}
+EXPORT_SYMBOL_GPL(folio_pool_init_key);
+
+void folio_pool_init(struct folio_pool *fp, size_t elem_size, unsigned int order)
+{
+	folio_pool_init_key(fp, elem_size, order, NULL);
+}
+EXPORT_SYMBOL_GPL(folio_pool_init);
+
+void folio_scratchpad_stats(struct folio_scratchpad *sp, unsigned int *nr_chunks,
+			    size_t *chunk_size, size_t *tail_used)
+{
+	unsigned long flags;
+	size_t csz;
+
+	csz = (PAGE_SIZE << sp->chunk_order);
+	if (chunk_size)
+		*chunk_size = csz;
+
+	spin_lock_irqsave(&sp->lock, flags);
+	if (nr_chunks)
+		*nr_chunks = list_count_nodes(&sp->chunks);
+	if (tail_used)
+		*tail_used = csz > sp->remaining ? csz - sp->remaining : 0;
+	spin_unlock_irqrestore(&sp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_stats);
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index af357f6c5070b..8b9e0c905d841 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -173,12 +173,17 @@ static void nft_ctx_init(struct nft_ctx *ctx,
 	bitmap_zero(ctx->reg_inited, NFT_REG32_NUM);
 }
 
+DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(nft_trans_scratchpad_key, trans_scratchpad,
+				   "Toggle nf_tables transaction folio scratchpad allocator");
+
 static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
 					 int msg_type, u32 size)
 {
+	struct nftables_pernet *nft_net = nft_pernet(ctx->net);
 	struct nft_trans *trans;
 
-	trans = kzalloc(size, GFP_KERNEL);
+	trans = folio_scratchpad_alloc_bytes(nft_net, trans_scratchpad, size,
+					     __alignof__(struct nft_trans), GFP_KERNEL);
 	if (trans == NULL)
 		return NULL;
 
@@ -194,6 +199,18 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
 	return trans;
 }
 
+/*
+ * nft_trans_free - release a transaction object.
+ *
+ * Scratchpad-backed objects are not individually freed; they are reclaimed in
+ * bulk by folio_scratchpad_free() at batch commit/abort boundary.  Kzalloc'd
+ * objects reside on slab pages and are released immediately via kfree().
+ */
+static inline void nft_trans_free(struct nft_trans *trans)
+{
+	folio_scratchpad_free_elem(trans);
+}
+
 static struct nft_trans_binding *nft_trans_get_binding(struct nft_trans *trans)
 {
 	switch (trans->msg_type) {
@@ -219,7 +236,7 @@ static void nft_trans_list_del(struct nft_trans *trans)
 static void nft_trans_destroy(struct nft_trans *trans)
 {
 	nft_trans_list_del(trans);
-	kfree(trans);
+	nft_trans_free(trans);
 }
 
 static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set,
@@ -519,8 +536,10 @@ static bool nft_trans_collapse_set_elem(struct nftables_pernet *nft_net,
 	/* krealloc might free tail which invalidates list pointers */
 	list_del_init(&tail->nft_trans.list);
 
-	new_trans = krealloc(tail, struct_size(tail, elems, nelems),
-			     GFP_KERNEL);
+	new_trans = folio_scratchpad_realloc(tail,
+					     struct_size(tail, elems, old_nelems),
+					     struct_size(tail, elems, nelems),
+					     GFP_KERNEL);
 	if (!new_trans) {
 		list_add_tail(&tail->nft_trans.list,
 			      &nft_net->commit_list);
@@ -600,7 +619,7 @@ static void nft_trans_commit_list_add_elem(struct net *net, struct nft_trans *tr
 		     trans->msg_type != NFT_MSG_DELSETELEM);
 
 	if (nft_trans_try_collapse(nft_net, trans)) {
-		kfree(trans);
+		nft_trans_free(trans);
 		return;
 	}
 
@@ -3068,7 +3087,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
 
 err_trans:
 	free_percpu(stats);
-	kfree(trans);
+	nft_trans_free(trans);
 err_hooks:
 	if (nla[NFTA_CHAIN_HOOK]) {
 		list_for_each_entry_safe(h, next, &hook.list, list) {
@@ -7636,7 +7655,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 	return set_full ? -ENFILE : 0;
 
 err_element_clash:
-	kfree(trans);
+	nft_trans_free(trans);
 err_set_size:
 	if (!(flags & NFT_SET_ELEM_CATCHALL))
 		atomic_dec(&set->nelems);
@@ -7904,7 +7923,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
 	return 0;
 
 fail_ops:
-	kfree(trans);
+	nft_trans_free(trans);
 fail_trans:
 	kfree(elem.priv);
 fail_elem_key_end:
@@ -8287,7 +8306,7 @@ static int nf_tables_updobj(const struct nft_ctx *ctx,
 	return 0;
 
 err_free_trans:
-	kfree(trans);
+	nft_trans_free(trans);
 err_trans:
 	module_put(type->owner);
 	return err;
@@ -10253,7 +10272,7 @@ static void nft_commit_release(struct nft_trans *trans)
 	if (trans->put_net)
 		put_net(trans->net);
 
-	kfree(trans);
+	nft_trans_free(trans);
 }
 
 static void nf_tables_trans_destroy_work(struct work_struct *w)
@@ -10275,6 +10294,8 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)
 		nft_trans_list_del(trans);
 		nft_commit_release(trans);
 	}
+
+	folio_scratchpad_reset(&nft_net->trans_scratchpad);
 }
 
 void nf_tables_trans_destroy_flush_work(struct net *net)
@@ -11244,7 +11265,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
 			nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
 		break;
 	}
-	kfree(trans);
+	nft_trans_free(trans);
 }
 
 static void nft_set_abort_update(struct list_head *set_update_list)
@@ -11468,6 +11489,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
 		nf_tables_abort_release(trans);
 	}
 
+	folio_scratchpad_reset(&nft_net->trans_scratchpad);
+
 	return err;
 }
 
@@ -11499,6 +11522,9 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb,
 	else
 		nf_tables_module_autoload_cleanup(net);
 
+	folio_scratchpad_free(&nft_net->trans_scratchpad);
+	folio_scratchpad_init(&nft_net->trans_scratchpad, 4);
+
 	mutex_unlock(&nft_net->commit_mutex);
 
 	return ret;
@@ -12142,6 +12168,8 @@ static int __net_init nf_tables_init_net(struct net *net)
 	INIT_LIST_HEAD(&nft_net->binding_list);
 	INIT_LIST_HEAD(&nft_net->module_list);
 	INIT_LIST_HEAD(&nft_net->notify_list);
+	folio_scratchpad_init_key(&nft_net->trans_scratchpad, get_order(SZ_64K),
+				  &nft_trans_scratchpad_key);
 	mutex_init(&nft_net->commit_mutex);
 	net->nft.base_seq = 1;
 	nft_net->gc_seq = 0;
@@ -12186,6 +12214,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
 	WARN_ON_ONCE(!list_empty(&nft_net->module_list));
 	WARN_ON_ONCE(!list_empty(&nft_net->notify_list));
 	WARN_ON_ONCE(!list_empty(&nft_net->destroy_list));
+	folio_scratchpad_free(&nft_net->trans_scratchpad);
 }
 
 static void nf_tables_exit_batch(struct list_head *net_exit_list)

3/2 2026/08/17 18:11 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19041
							output: 21
							thoughts: 474
						

Thoughts:
**Analyzing the Folio Pool Patch**

Okay, so I'm looking at this patch introducing a `folio_pool` and `folio_scratchpad` allocator, which leverages direct-map large folios. The implications are pretty significant, touching a bunch of core subsystems. It's impacting:

*   `drivers/gpu/drm/drm_gpuvm.c`
*   `kernel/bpf/syscall.c` due to batch map updates.
*   `kernel/bpf/verifier.c` which needs a verifier state stack.
*   `kernel/locking/lockdep.c` concerning lockdep lists.
*   `net/netfilter/nf_tables_api.c` as a result of nftables transactions.

It's introducing `lib/folio_pool.c` and `include/linux/folio_pool.h` – that makes sense, centralizing the pool management. The interesting bit is the use of static keys for each subsystem to control the folio pool's activation. I need to understand this.

Let's break down these static keys and the associated module parameters. Okay, I've got:

*   `gpuvm_scratchpad_key` - This one's tied to `va_scratchpad` as a module parameter.
*   `bpf_batch_scratchpad_key` and `bpf_state_pool_key` - These are just `DEFINE_STATIC_KEY_TRUE`, no module params. Probably hardcoded.
*   `lockdep_pool_key` - This one uses the boot parameter `lockdep.folio_pool=`.
*   `nft_trans_scratchpad_key` - This is parameterized as `trans_scratchpad`
*   Finally, `folio_pool_enabled_key` – This one has the module parameter `enabled` from `folio_pool.c`, so it's `folio_pool.enabled=`

Hmm, the definition of `gpuvm_scratchpad_key` uses `DEFINE_FOLIO_POOL_STATIC_KEY_PARAM`, and therefore links to the module param `va_scratchpad`. Since this is used in `drm_gpuvm.c`, is the module `drm` or `drm_gpuvm`? I need to figure out which kernel module this is. Is `drm_gpuvm.c` compiled into `drm.ko` or `drm_exec.ko`? Gotta check the `drivers/gpu/drm/Makefile`. Time to get the grepper involved to find out.



4/2 2026/08/17 18:11 tool grepper
0m Args:
{
  "Expression": "drm_gpuvm.o"
}

Results:
{
  "Output": "drivers/gpu/drm/Makefile=106=obj-$(CONFIG_DRM_EXEC) += drm_exec.o\ndrivers/gpu/drm/Makefile:107:obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o\ndrivers/gpu/drm/Makefile-108-\n--\ndrivers/gpu/drm/drm_gpuvm.c-124- * Depending on how the new GPU VA mapping intersects with the existing mappings\ndrivers/gpu/drm/drm_gpuvm.c:125: * of the GPU VA space the \u0026drm_gpuvm_ops callbacks contain an arbitrary amount\ndrivers/gpu/drm/drm_gpuvm.c-126- * of unmap operations, a maximum of two remap operations and a single map\n--\ndrivers/gpu/drm/drm_gpuvm.c-147- *\ndrivers/gpu/drm/drm_gpuvm.c:148: * Analogous to drm_gpuvm_sm_map() drm_gpuvm_sm_unmap() uses \u0026drm_gpuvm_ops to\ndrivers/gpu/drm/drm_gpuvm.c-149- * call back into the driver in order to unmap a range of GPU VA space. The\n--\ndrivers/gpu/drm/drm_gpuvm.c-167- * drm_gpuva_remove() may be used. These functions can safely be used from\ndrivers/gpu/drm/drm_gpuvm.c:168: * \u0026drm_gpuvm_ops callbacks originating from drm_gpuvm_sm_map() or\ndrivers/gpu/drm/drm_gpuvm.c-169- * drm_gpuvm_sm_unmap(). However, it might be more convenient to use the\n--\ndrivers/gpu/drm/drm_gpuvm.c-649- *\t// ops to pass to drm_gpuvm_init()\ndrivers/gpu/drm/drm_gpuvm.c:650: *\tstatic const struct drm_gpuvm_ops driver_gpuvm_ops = {\ndrivers/gpu/drm/drm_gpuvm.c-651- *\t\t.sm_step_map = driver_gpuva_map,\n--\ndrivers/gpu/drm/drm_gpuvm.c=1039=drm_gpuvm_gem_object_free(struct drm_gem_object *obj)\n--\ndrivers/gpu/drm/drm_gpuvm.c-1044-\ndrivers/gpu/drm/drm_gpuvm.c:1045:static const struct drm_gem_object_funcs drm_gpuvm_object_funcs = {\ndrivers/gpu/drm/drm_gpuvm.c-1046-\t.free = drm_gpuvm_gem_object_free,\n--\ndrivers/gpu/drm/drm_gpuvm.c=1060=drm_gpuvm_resv_object_alloc(struct drm_device *drm)\n--\ndrivers/gpu/drm/drm_gpuvm.c-1067-\ndrivers/gpu/drm/drm_gpuvm.c:1068:\tobj-\u003efuncs = \u0026drm_gpuvm_object_funcs;\ndrivers/gpu/drm/drm_gpuvm.c-1069-\tdrm_gem_private_object_init(drm, obj, 0);\n--\ndrivers/gpu/drm/drm_gpuvm.c=1073=EXPORT_SYMBOL_GPL(drm_gpuvm_resv_object_alloc);\n--\ndrivers/gpu/drm/drm_gpuvm.c-1085- * @reserve_range: the size of the kernel reserved GPU VA area\ndrivers/gpu/drm/drm_gpuvm.c:1086: * @ops: \u0026drm_gpuvm_ops called on \u0026drm_gpuvm_sm_map / \u0026drm_gpuvm_sm_unmap\ndrivers/gpu/drm/drm_gpuvm.c-1087- *\n--\ndrivers/gpu/drm/drm_gpuvm.c=1094=drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\n--\ndrivers/gpu/drm/drm_gpuvm.c-1099-\t       u64 reserve_offset, u64 reserve_range,\ndrivers/gpu/drm/drm_gpuvm.c:1100:\t       const struct drm_gpuvm_ops *ops)\ndrivers/gpu/drm/drm_gpuvm.c-1101-{\n--\ndrivers/gpu/drm/drm_gpuvm.c=1476=__drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec)\ndrivers/gpu/drm/drm_gpuvm.c-1477-{\ndrivers/gpu/drm/drm_gpuvm.c:1478:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1479-\tstruct drm_gpuvm_bo *vm_bo;\n--\ndrivers/gpu/drm/drm_gpuvm.c=1496=drm_gpuvm_validate_locked(struct drm_gpuvm *gpuvm, struct drm_exec *exec)\ndrivers/gpu/drm/drm_gpuvm.c-1497-{\ndrivers/gpu/drm/drm_gpuvm.c:1498:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1499-\tstruct drm_gpuvm_bo *vm_bo, *next;\n--\ndrivers/gpu/drm/drm_gpuvm.c-1525- *\ndrivers/gpu/drm/drm_gpuvm.c:1526: * Calls the \u0026drm_gpuvm_ops::vm_bo_validate callback for all evicted buffer\ndrivers/gpu/drm/drm_gpuvm.c-1527- * objects being mapped in the given \u0026drm_gpuvm.\n--\ndrivers/gpu/drm/drm_gpuvm.c=1532=drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec)\ndrivers/gpu/drm/drm_gpuvm.c-1533-{\ndrivers/gpu/drm/drm_gpuvm.c:1534:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1535-\n--\ndrivers/gpu/drm/drm_gpuvm.c=1571=EXPORT_SYMBOL_GPL(drm_gpuvm_resv_add_fence);\n--\ndrivers/gpu/drm/drm_gpuvm.c-1577- *\ndrivers/gpu/drm/drm_gpuvm.c:1578: * If provided by the driver, this function uses the \u0026drm_gpuvm_ops\ndrivers/gpu/drm/drm_gpuvm.c-1579- * vm_bo_alloc() callback to allocate.\n--\ndrivers/gpu/drm/drm_gpuvm.c=1584=drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,\n--\ndrivers/gpu/drm/drm_gpuvm.c-1586-{\ndrivers/gpu/drm/drm_gpuvm.c:1587:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1588-\tstruct drm_gpuvm_bo *vm_bo;\n--\ndrivers/gpu/drm/drm_gpuvm.c=1626=drm_gpuvm_bo_destroy_not_in_lists(struct drm_gpuvm_bo *vm_bo)\n--\ndrivers/gpu/drm/drm_gpuvm.c-1628-\tstruct drm_gpuvm *gpuvm = vm_bo-\u003evm;\ndrivers/gpu/drm/drm_gpuvm.c:1629:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1630-\tstruct drm_gem_object *obj = vm_bo-\u003eobj;\n--\ndrivers/gpu/drm/drm_gpuvm.c=2360=static int\ndrivers/gpu/drm/drm_gpuvm.c:2361:op_map_cb(const struct drm_gpuvm_ops *fn, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2362-\t  const struct drm_gpuvm_map_req *req)\n--\ndrivers/gpu/drm/drm_gpuvm.c=2378=static int\ndrivers/gpu/drm/drm_gpuvm.c:2379:op_remap_cb(const struct drm_gpuvm_ops *fn, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2380-\t    struct drm_gpuva_op_map *prev,\n--\ndrivers/gpu/drm/drm_gpuvm.c=2396=static int\ndrivers/gpu/drm/drm_gpuvm.c:2397:op_unmap_cb(const struct drm_gpuvm_ops *fn, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2398-\t    struct drm_gpuva *va, bool merge, bool madvise)\n--\ndrivers/gpu/drm/drm_gpuvm.c=2413=__drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c:2414:\t\t   const struct drm_gpuvm_ops *ops, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2415-\t\t   const struct drm_gpuvm_map_req *req,\n--\ndrivers/gpu/drm/drm_gpuvm.c=2592=__drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c:2593:\t\t     const struct drm_gpuvm_ops *ops, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2594-\t\t     u64 req_addr, u64 req_range)\n--\ndrivers/gpu/drm/drm_gpuvm.c-2655- * This function iterates the given range of the GPU VA space. It utilizes the\ndrivers/gpu/drm/drm_gpuvm.c:2656: * \u0026drm_gpuvm_ops to call back into the driver providing the split and merge\ndrivers/gpu/drm/drm_gpuvm.c-2657- * steps.\n--\ndrivers/gpu/drm/drm_gpuvm.c=2679=drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2681-{\ndrivers/gpu/drm/drm_gpuvm.c:2682:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2683-\n--\ndrivers/gpu/drm/drm_gpuvm.c=2691=EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map);\n--\ndrivers/gpu/drm/drm_gpuvm.c-2700- * This function iterates the given range of the GPU VA space. It utilizes the\ndrivers/gpu/drm/drm_gpuvm.c:2701: * \u0026drm_gpuvm_ops to call back into the driver providing the operations to\ndrivers/gpu/drm/drm_gpuvm.c-2702- * unmap and, if required, split existing mappings.\n--\ndrivers/gpu/drm/drm_gpuvm.c=2721=drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2723-{\ndrivers/gpu/drm/drm_gpuvm.c:2724:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2725-\n--\ndrivers/gpu/drm/drm_gpuvm.c=2736=drm_gpuva_sm_step_lock(struct drm_gpuva_op *op, void *priv)\n--\ndrivers/gpu/drm/drm_gpuvm.c-2753-\ndrivers/gpu/drm/drm_gpuvm.c:2754:static const struct drm_gpuvm_ops lock_ops = {\ndrivers/gpu/drm/drm_gpuvm.c-2755-\t.sm_step_map = drm_gpuva_sm_step_lock,\n--\ndrivers/gpu/drm/drm_gpuvm.c=2859=gpuva_op_alloc(struct drm_gpuvm *gpuvm)\ndrivers/gpu/drm/drm_gpuvm.c-2860-{\ndrivers/gpu/drm/drm_gpuvm.c:2861:\tconst struct drm_gpuvm_ops *fn = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2862-\tstruct drm_gpuva_op *op;\n--\ndrivers/gpu/drm/drm_gpuvm.c=2877=gpuva_op_free(struct drm_gpuvm *gpuvm,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2879-{\ndrivers/gpu/drm/drm_gpuvm.c:2880:\tconst struct drm_gpuvm_ops *fn = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2881-\n--\ndrivers/gpu/drm/drm_gpuvm.c=2889=drm_gpuva_sm_step(struct drm_gpuva_op *__op,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2943-\ndrivers/gpu/drm/drm_gpuvm.c:2944:static const struct drm_gpuvm_ops gpuvm_list_ops = {\ndrivers/gpu/drm/drm_gpuvm.c-2945-\t.sm_step_map = drm_gpuva_sm_step,\n--\ndrivers/gpu/drm/imagination/pvr_vm.c=507=static void pvr_gpuvm_free(struct drm_gpuvm *gpuvm)\n--\ndrivers/gpu/drm/imagination/pvr_vm.c-511-\ndrivers/gpu/drm/imagination/pvr_vm.c:512:static const struct drm_gpuvm_ops pvr_vm_gpuva_ops = {\ndrivers/gpu/drm/imagination/pvr_vm.c-513-\t.vm_free = pvr_gpuvm_free,\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c=633=msm_gem_vm_sm_step_unmap(struct drm_gpuva_op *op, void *_arg)\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c-692-\ndrivers/gpu/drm/msm/msm_gem_vma.c:693:static const struct drm_gpuvm_ops msm_gpuvm_ops = {\ndrivers/gpu/drm/msm/msm_gem_vma.c-694-\t.vm_free = msm_gem_vm_free,\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c=792=static const struct drm_sched_backend_ops msm_vm_bind_ops = {\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c-810- *\ndrivers/gpu/drm/msm/msm_gem_vma.c:811: * Returns: pointer to the created \u0026struct drm_gpuvm on success\ndrivers/gpu/drm/msm/msm_gem_vma.c-812- * or an ERR_PTR(-errno) on failure.\n--\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c=1873=nouveau_uvmm_bo_validate(struct drm_gpuvm_bo *vm_bo, struct drm_exec *exec)\n--\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c-1880-\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c:1881:static const struct drm_gpuvm_ops gpuvm_ops = {\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c-1882-\t.vm_free = nouveau_uvmm_free,\n--\ndrivers/gpu/drm/panthor/panthor_mmu.c=2535=static int panthor_vm_bo_validate(struct drm_gpuvm_bo *vm_bo,\n--\ndrivers/gpu/drm/panthor/panthor_mmu.c-2555-\ndrivers/gpu/drm/panthor/panthor_mmu.c:2556:static const struct drm_gpuvm_ops panthor_gpuvm_ops = {\ndrivers/gpu/drm/panthor/panthor_mmu.c-2557-\t.vm_free = panthor_vm_free,\n--\ndrivers/gpu/drm/xe/xe_vm.c=1351=static void xe_vm_free(struct drm_gpuvm *gpuvm);\ndrivers/gpu/drm/xe/xe_vm.c-1352-\ndrivers/gpu/drm/xe/xe_vm.c:1353:static const struct drm_gpuvm_ops gpuvm_ops = {\ndrivers/gpu/drm/xe/xe_vm.c-1354-\t.op_alloc = xe_vm_op_alloc,\n--\ninclude/drm/drm_gpuvm.h=40=struct drm_gpuvm_bo;\ninclude/drm/drm_gpuvm.h:41:struct drm_gpuvm_ops;\ninclude/drm/drm_gpuvm.h-42-\n--\ninclude/drm/drm_gpuvm.h=230=struct drm_gpuvm {\n--\ninclude/drm/drm_gpuvm.h-284-\t/**\ninclude/drm/drm_gpuvm.h:285:\t * @ops: \u0026drm_gpuvm_ops providing the split/merge steps to drivers\ninclude/drm/drm_gpuvm.h-286-\t */\ninclude/drm/drm_gpuvm.h:287:\tconst struct drm_gpuvm_ops *ops;\ninclude/drm/drm_gpuvm.h-288-\n--\ninclude/drm/drm_gpuvm.h=349=void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\n--\ninclude/drm/drm_gpuvm.h-354-\t\t    u64 reserve_offset, u64 reserve_range,\ninclude/drm/drm_gpuvm.h:355:\t\t    const struct drm_gpuvm_ops *ops);\ninclude/drm/drm_gpuvm.h-356-\n--\ninclude/drm/drm_gpuvm.h=1129=static inline void drm_gpuva_init_from_op(struct drm_gpuva *va,\n--\ninclude/drm/drm_gpuvm.h-1138-/**\ninclude/drm/drm_gpuvm.h:1139: * struct drm_gpuvm_ops - callbacks for split/merge steps\ninclude/drm/drm_gpuvm.h-1140- *\n--\ninclude/drm/drm_gpuvm.h-1144- */\ninclude/drm/drm_gpuvm.h:1145:struct drm_gpuvm_ops {\ninclude/drm/drm_gpuvm.h-1146-\t/**\n--\nrust/kernel/drm/gpuvm/mod.rs=102=impl\u003cT: DriverGpuVm\u003e GpuVm\u003cT\u003e {\nrust/kernel/drm/gpuvm/mod.rs:103:    const fn vtable() -\u003e \u0026'static bindings::drm_gpuvm_ops {\nrust/kernel/drm/gpuvm/mod.rs:104:        \u0026bindings::drm_gpuvm_ops {\nrust/kernel/drm/gpuvm/mod.rs-105-            vm_free: Some(Self::vm_free),\n"
}

5/2 2026/08/17 18:11 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25312
							output: 19
							thoughts: 93
						

Thoughts:
**My Initial Thoughts on the DRM Driver Compilation**

Okay, so I'm looking at how the Direct Rendering Manager (DRM) driver components are compiled. My understanding is that `drm_gpuvm.c` ends up being compiled into either `drm_exec.o` or `drm_gpuvm.o`. These, in turn, are then incorporated into either `drm.ko` or `drm_gpuvm.ko`. However, something feels slightly off with that second step. It's like `drm_gpuvm.o` can go into either `drm_gpuvm.ko` or `drm.ko`, depending? I need to clarify that build process. I'm going to double-check the `drivers/gpu/drm/Makefile` to confirm the exact dependencies and compilation targets and make sure I'm not missing anything. It's the only place to get a definitive answer.



6/2 2026/08/17 18:11 tool grepper
0m Args:
{
  "Expression": "drm_gpuvm"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 3919 lines.\nUse more precise expression if possible.\n\n.clang-format=71=ForEachMacros:\n--\n.clang-format-187-  - 'drm_gpuva_for_each_op_safe'\n.clang-format:188:  - 'drm_gpuvm_bo_for_each_va'\n.clang-format:189:  - 'drm_gpuvm_bo_for_each_va_safe'\n.clang-format:190:  - 'drm_gpuvm_for_each_va'\n.clang-format:191:  - 'drm_gpuvm_for_each_va_range'\n.clang-format:192:  - 'drm_gpuvm_for_each_va_range_safe'\n.clang-format:193:  - 'drm_gpuvm_for_each_va_safe'\n.clang-format-194-  - 'drm_mm_for_each_hole'\n--\nDocumentation/gpu/drm-mm.rst=479=DRM MM Range Allocator Function References\n--\nDocumentation/gpu/drm-mm.rst-487-\nDocumentation/gpu/drm-mm.rst:488:.. _drm_gpuvm:\nDocumentation/gpu/drm-mm.rst-489-\n--\nDocumentation/gpu/drm-mm.rst=493=Overview\n--\nDocumentation/gpu/drm-mm.rst-495-\nDocumentation/gpu/drm-mm.rst:496:.. kernel-doc:: drivers/gpu/drm/drm_gpuvm.c\nDocumentation/gpu/drm-mm.rst-497-   :doc: Overview\n--\nDocumentation/gpu/drm-mm.rst=499=Split and Merge\n--\nDocumentation/gpu/drm-mm.rst-501-\nDocumentation/gpu/drm-mm.rst:502:.. kernel-doc:: drivers/gpu/drm/drm_gpuvm.c\nDocumentation/gpu/drm-mm.rst-503-   :doc: Split and Merge\nDocumentation/gpu/drm-mm.rst-504-\nDocumentation/gpu/drm-mm.rst:505:.. _drm_gpuvm_locking:\nDocumentation/gpu/drm-mm.rst-506-\nDocumentation/gpu/drm-mm.rst=507=Locking\n--\nDocumentation/gpu/drm-mm.rst-509-\nDocumentation/gpu/drm-mm.rst:510:.. kernel-doc:: drivers/gpu/drm/drm_gpuvm.c\nDocumentation/gpu/drm-mm.rst-511-   :doc: Locking\n--\nDocumentation/gpu/drm-mm.rst=513=Examples\n--\nDocumentation/gpu/drm-mm.rst-515-\nDocumentation/gpu/drm-mm.rst:516:.. kernel-doc:: drivers/gpu/drm/drm_gpuvm.c\nDocumentation/gpu/drm-mm.rst-517-   :doc: Examples\n--\nDocumentation/gpu/drm-mm.rst=519=DRM GPUVM Function References\n--\nDocumentation/gpu/drm-mm.rst-521-\nDocumentation/gpu/drm-mm.rst:522:.. kernel-doc:: include/drm/drm_gpuvm.h\nDocumentation/gpu/drm-mm.rst-523-   :internal:\nDocumentation/gpu/drm-mm.rst-524-\nDocumentation/gpu/drm-mm.rst:525:.. kernel-doc:: drivers/gpu/drm/drm_gpuvm.c\nDocumentation/gpu/drm-mm.rst-526-   :export:\n--\nDocumentation/gpu/drm-vm-bind-locking.rst=21=implementation in detail, but it is covered in :ref:`its own\nDocumentation/gpu/drm-vm-bind-locking.rst:22:documentation \u003cdrm_gpuvm\u003e`. It is highly recommended for any driver\nDocumentation/gpu/drm-vm-bind-locking.rst-23-implementing VM_BIND to use the DRM GPUVM helpers and to extend it if\n--\nDocumentation/gpu/drm-vm-bind-locking.rst=379=Please refer to the :ref:`DRM GPUVM locking section\nDocumentation/gpu/drm-vm-bind-locking.rst:380:\u003cdrm_gpuvm_locking\u003e` and its internal\nDocumentation/gpu/drm-vm-bind-locking.rst-381-:c:func:`get_next_vm_bo_from_list` function.\n--\nMAINTAINERS=8995=T:\tgit https://gitlab.freedesktop.org/drm/misc/kernel.git\nMAINTAINERS:8996:F:\tdrivers/gpu/drm/drm_gpuvm.c\nMAINTAINERS:8997:F:\tinclude/drm/drm_gpuvm.h\nMAINTAINERS:8998:F:\trust/helpers/drm_gpuvm.c\nMAINTAINERS-8999-F:\trust/kernel/drm/gpuvm/\n--\ndrivers/gpu/drm/Makefile=106=obj-$(CONFIG_DRM_EXEC) += drm_exec.o\ndrivers/gpu/drm/Makefile:107:obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o\ndrivers/gpu/drm/Makefile-108-\n--\ndrivers/gpu/drm/drm_debugfs.c-41-#include \u003cdrm/drm_managed.h\u003e\ndrivers/gpu/drm/drm_debugfs.c:42:#include \u003cdrm/drm_gpuvm.h\u003e\ndrivers/gpu/drm/drm_debugfs.c-43-\n--\ndrivers/gpu/drm/drm_debugfs.c=189=static const struct file_operations drm_debugfs_fops = {\n--\ndrivers/gpu/drm/drm_debugfs.c-199- * @m: pointer to the \u0026seq_file to write\ndrivers/gpu/drm/drm_debugfs.c:200: * @gpuvm: the \u0026drm_gpuvm representing the GPU VA space\ndrivers/gpu/drm/drm_debugfs.c-201- *\n--\ndrivers/gpu/drm/drm_debugfs.c=209=int drm_debugfs_gpuva_info(struct seq_file *m,\ndrivers/gpu/drm/drm_debugfs.c:210:\t\t\t   struct drm_gpuvm *gpuvm)\ndrivers/gpu/drm/drm_debugfs.c-211-{\n--\ndrivers/gpu/drm/drm_debugfs.c-223-\tseq_puts(m, \"-------------------------------------------------------------------------------------------------------------\\n\");\ndrivers/gpu/drm/drm_debugfs.c:224:\tdrm_gpuvm_for_each_va(va, gpuvm) {\ndrivers/gpu/drm/drm_debugfs.c-225-\t\tif (unlikely(va == kva))\n--\ndrivers/gpu/drm/drm_gpuvm.c-28-#include \u003cdrm/drm_drv.h\u003e\ndrivers/gpu/drm/drm_gpuvm.c:29:#include \u003cdrm/drm_gpuvm.h\u003e\ndrivers/gpu/drm/drm_gpuvm.c-30-#include \u003cdrm/drm_print.h\u003e\n--\ndrivers/gpu/drm/drm_gpuvm.c-38- *\ndrivers/gpu/drm/drm_gpuvm.c:39: * The DRM GPU VA Manager, represented by struct drm_gpuvm keeps track of a\ndrivers/gpu/drm/drm_gpuvm.c-40- * GPU's virtual address (VA) space and manages the corresponding virtual\n--\ndrivers/gpu/drm/drm_gpuvm.c-52- *\ndrivers/gpu/drm/drm_gpuvm.c:53: * The \u0026drm_gpuvm structure contains a special \u0026drm_gpuva representing the\ndrivers/gpu/drm/drm_gpuvm.c-54- * portion of VA space reserved by the kernel. This node is initialized together\n--\ndrivers/gpu/drm/drm_gpuvm.c-57- *\ndrivers/gpu/drm/drm_gpuvm.c:58: * In a typical application drivers would embed struct drm_gpuvm and\ndrivers/gpu/drm/drm_gpuvm.c-59- * struct drm_gpuva within their own driver specific structures, there won't be\n--\ndrivers/gpu/drm/drm_gpuvm.c-62- *\ndrivers/gpu/drm/drm_gpuvm.c:63: * The data structures needed to store \u0026drm_gpuvas within the \u0026drm_gpuvm are\ndrivers/gpu/drm/drm_gpuvm.c-64- * contained within struct drm_gpuva already. Hence, for inserting \u0026drm_gpuva\n--\ndrivers/gpu/drm/drm_gpuvm.c-69- * \u0026dma_resv in order to improve locking efficiency (e.g. with \u0026drm_exec).\ndrivers/gpu/drm/drm_gpuvm.c:70: * For this purpose drivers must pass a \u0026drm_gem_object to drm_gpuvm_init(), in\ndrivers/gpu/drm/drm_gpuvm.c-71- * the following called 'resv object', which serves as the container of the\n--\ndrivers/gpu/drm/drm_gpuvm.c-74- * but it can also be a 'dummy' object, which can be allocated with\ndrivers/gpu/drm/drm_gpuvm.c:75: * drm_gpuvm_resv_object_alloc().\ndrivers/gpu/drm/drm_gpuvm.c-76- *\ndrivers/gpu/drm/drm_gpuvm.c-77- * In order to connect a struct drm_gpuva to its backing \u0026drm_gem_object each\ndrivers/gpu/drm/drm_gpuvm.c:78: * \u0026drm_gem_object maintains a list of \u0026drm_gpuvm_bo structures, and each\ndrivers/gpu/drm/drm_gpuvm.c:79: * \u0026drm_gpuvm_bo contains a list of \u0026drm_gpuva structures.\ndrivers/gpu/drm/drm_gpuvm.c-80- *\ndrivers/gpu/drm/drm_gpuvm.c:81: * A \u0026drm_gpuvm_bo is an abstraction that represents a combination of a\ndrivers/gpu/drm/drm_gpuvm.c:82: * \u0026drm_gpuvm and a \u0026drm_gem_object. Every such combination should be unique.\ndrivers/gpu/drm/drm_gpuvm.c:83: * This is ensured by the API through drm_gpuvm_bo_obtain() and\ndrivers/gpu/drm/drm_gpuvm.c:84: * drm_gpuvm_bo_obtain_prealloc() which first look into the corresponding\ndrivers/gpu/drm/drm_gpuvm.c:85: * \u0026drm_gem_object list of \u0026drm_gpuvm_bos for an existing instance of this\ndrivers/gpu/drm/drm_gpuvm.c-86- * particular combination. If not present, a new instance is created and linked\n--\ndrivers/gpu/drm/drm_gpuvm.c-88- *\ndrivers/gpu/drm/drm_gpuvm.c:89: * \u0026drm_gpuvm_bo structures, since unique for a given \u0026drm_gpuvm, are also used\ndrivers/gpu/drm/drm_gpuvm.c:90: * as entry for the \u0026drm_gpuvm's lists of external and evicted objects. Those\ndrivers/gpu/drm/drm_gpuvm.c-91- * lists are maintained in order to accelerate locking of dma-resv locks and\ndrivers/gpu/drm/drm_gpuvm.c:92: * validation of evicted objects bound in a \u0026drm_gpuvm. For instance, all\ndrivers/gpu/drm/drm_gpuvm.c:93: * \u0026drm_gem_object's \u0026dma_resv of a given \u0026drm_gpuvm can be locked by calling\ndrivers/gpu/drm/drm_gpuvm.c:94: * drm_gpuvm_exec_lock(). Once locked drivers can call drm_gpuvm_validate() in\ndrivers/gpu/drm/drm_gpuvm.c-95- * order to validate all evicted \u0026drm_gem_objects. It is also possible to lock\ndrivers/gpu/drm/drm_gpuvm.c-96- * additional \u0026drm_gem_objects by providing the corresponding parameters to\ndrivers/gpu/drm/drm_gpuvm.c:97: * drm_gpuvm_exec_lock() as well as open code the \u0026drm_exec loop while making\ndrivers/gpu/drm/drm_gpuvm.c:98: * use of helper functions such as drm_gpuvm_prepare_range() or\ndrivers/gpu/drm/drm_gpuvm.c:99: * drm_gpuvm_prepare_objects().\ndrivers/gpu/drm/drm_gpuvm.c-100- *\ndrivers/gpu/drm/drm_gpuvm.c-101- * Every bound \u0026drm_gem_object is treated as external object when its \u0026dma_resv\ndrivers/gpu/drm/drm_gpuvm.c:102: * structure is different than the \u0026drm_gpuvm's common \u0026dma_resv structure.\ndrivers/gpu/drm/drm_gpuvm.c-103- */\n--\ndrivers/gpu/drm/drm_gpuvm.c-108- * Besides its capability to manage and represent a GPU VA space, the\ndrivers/gpu/drm/drm_gpuvm.c:109: * GPU VA manager also provides functions to let the \u0026drm_gpuvm calculate a\ndrivers/gpu/drm/drm_gpuvm.c-110- * sequence of operations to satisfy a given map or unmap request.\n--\ndrivers/gpu/drm/drm_gpuvm.c-117- *\ndrivers/gpu/drm/drm_gpuvm.c:118: * Drivers can call drm_gpuvm_sm_map() to receive a sequence of callbacks\ndrivers/gpu/drm/drm_gpuvm.c-119- * containing map, unmap and remap operations for a given newly requested\n--\ndrivers/gpu/drm/drm_gpuvm.c-124- * Depending on how the new GPU VA mapping intersects with the existing mappings\ndrivers/gpu/drm/drm_gpuvm.c:125: * of the GPU VA space the \u0026drm_gpuvm_ops callbacks contain an arbitrary amount\ndrivers/gpu/drm/drm_gpuvm.c-126- * of unmap operations, a maximum of two remap operations and a single map\n--\ndrivers/gpu/drm/drm_gpuvm.c-136- * entries for this \u0026drm_gpuva, adding the missing page table entries only and\ndrivers/gpu/drm/drm_gpuvm.c:137: * update the \u0026drm_gpuvm's view of things accordingly.\ndrivers/gpu/drm/drm_gpuvm.c-138- *\n--\ndrivers/gpu/drm/drm_gpuvm.c-147- *\ndrivers/gpu/drm/drm_gpuvm.c:148: * Analogous to drm_gpuvm_sm_map() drm_gpuvm_sm_unmap() uses \u0026drm_gpuvm_ops to\ndrivers/gpu/drm/drm_gpuvm.c-149- * call back into the driver in order to unmap a range of GPU VA space. The\n--\ndrivers/gpu/drm/drm_gpuvm.c-154- *\ndrivers/gpu/drm/drm_gpuvm.c:155: * As an alternative to drm_gpuvm_sm_map() and drm_gpuvm_sm_unmap(),\ndrivers/gpu/drm/drm_gpuvm.c:156: * drm_gpuvm_sm_map_ops_create() and drm_gpuvm_sm_unmap_ops_create() can be used\ndrivers/gpu/drm/drm_gpuvm.c-157- * to directly obtain an instance of struct drm_gpuva_ops containing a list of\n--\ndrivers/gpu/drm/drm_gpuvm.c-159- * contains the \u0026drm_gpuva_ops analogous to the callbacks one would receive when\ndrivers/gpu/drm/drm_gpuvm.c:160: * calling drm_gpuvm_sm_map() or drm_gpuvm_sm_unmap(). While this way requires\ndrivers/gpu/drm/drm_gpuvm.c-161- * more memory (to allocate the \u0026drm_gpuva_ops), it provides drivers a way to\n--\ndrivers/gpu/drm/drm_gpuvm.c-165- *\ndrivers/gpu/drm/drm_gpuvm.c:166: * To update the \u0026drm_gpuvm's view of the GPU VA space drm_gpuva_insert() and\ndrivers/gpu/drm/drm_gpuvm.c-167- * drm_gpuva_remove() may be used. These functions can safely be used from\ndrivers/gpu/drm/drm_gpuvm.c:168: * \u0026drm_gpuvm_ops callbacks originating from drm_gpuvm_sm_map() or\ndrivers/gpu/drm/drm_gpuvm.c:169: * drm_gpuvm_sm_unmap(). However, it might be more convenient to use the\ndrivers/gpu/drm/drm_gpuvm.c-170- * provided helper functions drm_gpuva_map(), drm_gpuva_remap() and\n--\ndrivers/gpu/drm/drm_gpuvm.c-174- * mappings, a newly requested mapping and the resulting mappings as implemented\ndrivers/gpu/drm/drm_gpuvm.c:175: * by drm_gpuvm_sm_map() - it doesn't cover any arbitrary combinations of these.\ndrivers/gpu/drm/drm_gpuvm.c-176- *\n--\ndrivers/gpu/drm/drm_gpuvm.c-485- *\ndrivers/gpu/drm/drm_gpuvm.c:486: * Unlike drm_gpuvm_sm_map_ops_create, this logic avoids unmaps and merging,\ndrivers/gpu/drm/drm_gpuvm.c-487- * focusing solely on remap and map operations for efficient traversal and update.\n--\ndrivers/gpu/drm/drm_gpuvm.c-499- * DRM GPUVM also does not take care of the locking of the backing\ndrivers/gpu/drm/drm_gpuvm.c:500: * \u0026drm_gem_object buffers GPU VA lists and \u0026drm_gpuvm_bo abstractions by\ndrivers/gpu/drm/drm_gpuvm.c-501- * itself; drivers are responsible to enforce mutual exclusion using either the\n--\ndrivers/gpu/drm/drm_gpuvm.c-506- * by functions such as drm_gpuva_link() or drm_gpuva_unlink(), but also\ndrivers/gpu/drm/drm_gpuvm.c:507: * drm_gpuvm_bo_obtain() and drm_gpuvm_bo_put().\ndrivers/gpu/drm/drm_gpuvm.c-508- *\ndrivers/gpu/drm/drm_gpuvm.c:509: * The latter is required since on creation and destruction of a \u0026drm_gpuvm_bo\ndrivers/gpu/drm/drm_gpuvm.c:510: * the \u0026drm_gpuvm_bo is attached / removed from the \u0026drm_gem_objects gpuva list.\ndrivers/gpu/drm/drm_gpuvm.c:511: * Subsequent calls to drm_gpuvm_bo_obtain() for the same \u0026drm_gpuvm and\ndrivers/gpu/drm/drm_gpuvm.c-512- * \u0026drm_gem_object must be able to observe previous creations and destructions\ndrivers/gpu/drm/drm_gpuvm.c:513: * of \u0026drm_gpuvm_bos in order to keep instances unique.\ndrivers/gpu/drm/drm_gpuvm.c-514- *\ndrivers/gpu/drm/drm_gpuvm.c:515: * The \u0026drm_gpuvm's lists for keeping track of external and evicted objects are\ndrivers/gpu/drm/drm_gpuvm.c-516- * protected against concurrent insertion / removal and iteration internally.\n--\ndrivers/gpu/drm/drm_gpuvm.c-518- * However, drivers still need ensure to protect concurrent calls to functions\ndrivers/gpu/drm/drm_gpuvm.c:519: * iterating those lists, namely drm_gpuvm_prepare_objects() and\ndrivers/gpu/drm/drm_gpuvm.c:520: * drm_gpuvm_validate().\ndrivers/gpu/drm/drm_gpuvm.c-521- *\n--\ndrivers/gpu/drm/drm_gpuvm.c-547- *\ndrivers/gpu/drm/drm_gpuvm.c:548: *\t// Typically drivers would embed the \u0026drm_gpuvm and \u0026drm_gpuva\ndrivers/gpu/drm/drm_gpuvm.c-549- *\t// structure in individual driver structures and lock the dma-resv with\ndrivers/gpu/drm/drm_gpuvm.c-550- *\t// drm_exec or similar helpers.\ndrivers/gpu/drm/drm_gpuvm.c:551: *\tint driver_mapping_create(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c-552- *\t\t\t\t  u64 addr, u64 range,\n--\ndrivers/gpu/drm/drm_gpuvm.c-554- *\t{\ndrivers/gpu/drm/drm_gpuvm.c:555: *\t\tstruct drm_gpuvm_map_req map_req = {\ndrivers/gpu/drm/drm_gpuvm.c-556- *\t\t        .map.va.addr = addr,\n--\ndrivers/gpu/drm/drm_gpuvm.c-562- *\t\tstruct drm_gpuva_op *op\ndrivers/gpu/drm/drm_gpuvm.c:563: *\t\tstruct drm_gpuvm_bo *vm_bo;\ndrivers/gpu/drm/drm_gpuvm.c-564- *\ndrivers/gpu/drm/drm_gpuvm.c-565- *\t\tdriver_lock_va_space();\ndrivers/gpu/drm/drm_gpuvm.c:566: *\t\tops = drm_gpuvm_sm_map_ops_create(gpuvm, \u0026map_req);\ndrivers/gpu/drm/drm_gpuvm.c-567- *\t\tif (IS_ERR(ops))\n--\ndrivers/gpu/drm/drm_gpuvm.c-569- *\ndrivers/gpu/drm/drm_gpuvm.c:570: *\t\tvm_bo = drm_gpuvm_bo_obtain(gpuvm, obj);\ndrivers/gpu/drm/drm_gpuvm.c-571- *\t\tif (IS_ERR(vm_bo))\n--\ndrivers/gpu/drm/drm_gpuvm.c-632- *\t\t}\ndrivers/gpu/drm/drm_gpuvm.c:633: *\t\tdrm_gpuvm_bo_put(vm_bo);\ndrivers/gpu/drm/drm_gpuvm.c-634- *\t\tdriver_unlock_va_space();\n--\ndrivers/gpu/drm/drm_gpuvm.c-641- *\tstruct driver_context {\ndrivers/gpu/drm/drm_gpuvm.c:642: *\t\tstruct drm_gpuvm *gpuvm;\ndrivers/gpu/drm/drm_gpuvm.c:643: *\t\tstruct drm_gpuvm_bo *vm_bo;\ndrivers/gpu/drm/drm_gpuvm.c-644- *\t\tstruct drm_gpuva *new_va;\n--\ndrivers/gpu/drm/drm_gpuvm.c-648- *\ndrivers/gpu/drm/drm_gpuvm.c:649: *\t// ops to pass to drm_gpuvm_init()\ndrivers/gpu/drm/drm_gpuvm.c:650: *\tstatic const struct drm_gpuvm_ops driver_gpuvm_ops = {\ndrivers/gpu/drm/drm_gpuvm.c-651- *\t\t.sm_step_map = driver_gpuva_map,\n--\ndrivers/gpu/drm/drm_gpuvm.c-655- *\ndrivers/gpu/drm/drm_gpuvm.c:656: *\t// Typically drivers would embed the \u0026drm_gpuvm and \u0026drm_gpuva\ndrivers/gpu/drm/drm_gpuvm.c-657- *\t// structure in individual driver structures and lock the dma-resv with\ndrivers/gpu/drm/drm_gpuvm.c-658- *\t// drm_exec or similar helpers.\ndrivers/gpu/drm/drm_gpuvm.c:659: *\tint driver_mapping_create(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c-660- *\t\t\t\t  u64 addr, u64 range,\n--\ndrivers/gpu/drm/drm_gpuvm.c-663- *\t\tstruct driver_context ctx;\ndrivers/gpu/drm/drm_gpuvm.c:664: *\t\tstruct drm_gpuvm_bo *vm_bo;\ndrivers/gpu/drm/drm_gpuvm.c-665- *\t\tstruct drm_gpuva_ops *ops;\n--\ndrivers/gpu/drm/drm_gpuvm.c-673- *\t\tctx.next_va = kzalloc(sizeof(*ctx.next_va), GFP_KERNEL);\ndrivers/gpu/drm/drm_gpuvm.c:674: *\t\tctx.vm_bo = drm_gpuvm_bo_create(gpuvm, obj);\ndrivers/gpu/drm/drm_gpuvm.c-675- *\t\tif (!ctx.new_va || !ctx.prev_va || !ctx.next_va || !vm_bo) {\n--\ndrivers/gpu/drm/drm_gpuvm.c-682- *\t\t// drm_gpuva_unlink(), hence pre-allocate.\ndrivers/gpu/drm/drm_gpuvm.c:683: *\t\tctx.vm_bo = drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo);\ndrivers/gpu/drm/drm_gpuvm.c-684- *\ndrivers/gpu/drm/drm_gpuvm.c-685- *\t\tdriver_lock_va_space();\ndrivers/gpu/drm/drm_gpuvm.c:686: *\t\tret = drm_gpuvm_sm_map(gpuvm, \u0026ctx, addr, range, obj, offset);\ndrivers/gpu/drm/drm_gpuvm.c-687- *\t\tdriver_unlock_va_space();\n--\ndrivers/gpu/drm/drm_gpuvm.c-689- *\tout:\ndrivers/gpu/drm/drm_gpuvm.c:690: *\t\tdrm_gpuvm_bo_put(ctx.vm_bo);\ndrivers/gpu/drm/drm_gpuvm.c-691- *\t\tkfree(ctx.new_va);\n--\ndrivers/gpu/drm/drm_gpuvm.c-746- * get_next_vm_bo_from_list() - get the next vm_bo element\ndrivers/gpu/drm/drm_gpuvm.c:747: * @__gpuvm: the \u0026drm_gpuvm\ndrivers/gpu/drm/drm_gpuvm.c-748- * @__list_name: the name of the list we're iterating on\n--\ndrivers/gpu/drm/drm_gpuvm.c-760-\t({\t\t\t\t\t\t\t\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c:761:\t\tstruct drm_gpuvm_bo *__vm_bo = NULL;\t\t\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c-762-\t\t\t\t\t\t\t\t\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c:763:\t\tdrm_gpuvm_bo_put(__prev_vm_bo);\t\t\t\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c-764-\t\t\t\t\t\t\t\t\t\t\t\\\n--\ndrivers/gpu/drm/drm_gpuvm.c-773-\t\t\t__vm_bo = list_first_entry(\u0026(__gpuvm)-\u003e__list_name.list,\t\\\ndrivers/gpu/drm/drm_gpuvm.c:774:\t\t\t\t\t\t   struct drm_gpuvm_bo,\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c-775-\t\t\t\t\t\t   list.entry.__list_name);\t\t\\\n--\ndrivers/gpu/drm/drm_gpuvm.c-791- * for_each_vm_bo_in_list() - internal vm_bo list iterator\ndrivers/gpu/drm/drm_gpuvm.c:792: * @__gpuvm: the \u0026drm_gpuvm\ndrivers/gpu/drm/drm_gpuvm.c-793- * @__list_name: the name of the list we're iterating on\ndrivers/gpu/drm/drm_gpuvm.c-794- * @__local_list: a pointer to the local list used to store already iterated items\ndrivers/gpu/drm/drm_gpuvm.c:795: * @__vm_bo: the struct drm_gpuvm_bo to assign in each iteration step\ndrivers/gpu/drm/drm_gpuvm.c-796- *\n--\ndrivers/gpu/drm/drm_gpuvm.c-804- *\ndrivers/gpu/drm/drm_gpuvm.c:805: *\tstruct drm_gpuvm_bo *vm_bo;\ndrivers/gpu/drm/drm_gpuvm.c-806- *\tLIST_HEAD(my_local_list);\n--\ndrivers/gpu/drm/drm_gpuvm.c-814- *\t// Drop ref in case we break out of the loop.\ndrivers/gpu/drm/drm_gpuvm.c:815: *\tdrm_gpuvm_bo_put(vm_bo);\ndrivers/gpu/drm/drm_gpuvm.c-816- *\trestore_vm_bo_list(gpuvm, \u003clist_name\u003e, \u0026my_local_list);\n--\ndrivers/gpu/drm/drm_gpuvm.c=829=static void\ndrivers/gpu/drm/drm_gpuvm.c:830:__restore_vm_bo_list(struct drm_gpuvm *gpuvm, spinlock_t *lock,\ndrivers/gpu/drm/drm_gpuvm.c-831-\t\t     struct list_head *list, struct list_head **local_list)\n--\ndrivers/gpu/drm/drm_gpuvm.c-845- * restore_vm_bo_list() - move vm_bo elements back to their original list\ndrivers/gpu/drm/drm_gpuvm.c:846: * @__gpuvm: the \u0026drm_gpuvm\ndrivers/gpu/drm/drm_gpuvm.c-847- * @__list_name: the name of the list we're iterating on\n--\ndrivers/gpu/drm/drm_gpuvm.c=871=static void\ndrivers/gpu/drm/drm_gpuvm.c:872:__drm_gpuvm_bo_list_add(struct drm_gpuvm *gpuvm, spinlock_t *lock,\ndrivers/gpu/drm/drm_gpuvm.c-873-\t\t\tstruct list_head *entry, struct list_head *list)\n--\ndrivers/gpu/drm/drm_gpuvm.c-881-/**\ndrivers/gpu/drm/drm_gpuvm.c:882: * drm_gpuvm_bo_is_zombie() - check whether this vm_bo is scheduled for cleanup\ndrivers/gpu/drm/drm_gpuvm.c:883: * @vm_bo: the \u0026drm_gpuvm_bo\ndrivers/gpu/drm/drm_gpuvm.c-884- *\n--\ndrivers/gpu/drm/drm_gpuvm.c-897- * during run_job() in immediate mode, meaning that they need to remain on the\ndrivers/gpu/drm/drm_gpuvm.c:898: * lists until drm_gpuvm_bo_deferred_cleanup() is called.\ndrivers/gpu/drm/drm_gpuvm.c-899- */\ndrivers/gpu/drm/drm_gpuvm.c=900=static bool\ndrivers/gpu/drm/drm_gpuvm.c:901:drm_gpuvm_bo_is_zombie(struct drm_gpuvm_bo *vm_bo)\ndrivers/gpu/drm/drm_gpuvm.c-902-{\n--\ndrivers/gpu/drm/drm_gpuvm.c-906-/**\ndrivers/gpu/drm/drm_gpuvm.c:907: * drm_gpuvm_bo_list_add() - insert a vm_bo into the given list\ndrivers/gpu/drm/drm_gpuvm.c:908: * @__vm_bo: the \u0026drm_gpuvm_bo\ndrivers/gpu/drm/drm_gpuvm.c-909- * @__list_name: the name of the list to insert into\n--\ndrivers/gpu/drm/drm_gpuvm.c-913- */\ndrivers/gpu/drm/drm_gpuvm.c:914:#define drm_gpuvm_bo_list_add(__vm_bo, __list_name, __lock)\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c:915:\t__drm_gpuvm_bo_list_add((__vm_bo)-\u003evm,\t\t\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c-916-\t\t\t\t__lock ? \u0026(__vm_bo)-\u003evm-\u003e__list_name.lock :\t\\\n--\ndrivers/gpu/drm/drm_gpuvm.c=921=static void\ndrivers/gpu/drm/drm_gpuvm.c:922:__drm_gpuvm_bo_list_del(struct drm_gpuvm *gpuvm, spinlock_t *lock,\ndrivers/gpu/drm/drm_gpuvm.c-923-\t\t\tstruct list_head *entry, bool init)\n--\ndrivers/gpu/drm/drm_gpuvm.c-935-/**\ndrivers/gpu/drm/drm_gpuvm.c:936: * drm_gpuvm_bo_list_del_init() - remove a vm_bo from the given list\ndrivers/gpu/drm/drm_gpuvm.c:937: * @__vm_bo: the \u0026drm_gpuvm_bo\ndrivers/gpu/drm/drm_gpuvm.c-938- * @__list_name: the name of the list to insert into\n--\ndrivers/gpu/drm/drm_gpuvm.c-942- */\ndrivers/gpu/drm/drm_gpuvm.c:943:#define drm_gpuvm_bo_list_del_init(__vm_bo, __list_name, __lock)\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c:944:\t__drm_gpuvm_bo_list_del((__vm_bo)-\u003evm,\t\t\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c-945-\t\t\t\t__lock ? \u0026(__vm_bo)-\u003evm-\u003e__list_name.lock :\t\\\n--\ndrivers/gpu/drm/drm_gpuvm.c-950-/**\ndrivers/gpu/drm/drm_gpuvm.c:951: * drm_gpuvm_bo_list_del() - remove a vm_bo from the given list\ndrivers/gpu/drm/drm_gpuvm.c:952: * @__vm_bo: the \u0026drm_gpuvm_bo\ndrivers/gpu/drm/drm_gpuvm.c-953- * @__list_name: the name of the list to insert into\n--\ndrivers/gpu/drm/drm_gpuvm.c-957- */\ndrivers/gpu/drm/drm_gpuvm.c:958:#define drm_gpuvm_bo_list_del(__vm_bo, __list_name, __lock)\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c:959:\t__drm_gpuvm_bo_list_del((__vm_bo)-\u003evm,\t\t\t\t\t\\\ndrivers/gpu/drm/drm_gpuvm.c-960-\t\t\t\t__lock ? \u0026(__vm_bo)-\u003evm-\u003e__list_name.lock :\t\\\n--\ndrivers/gpu/drm/drm_gpuvm.c=973=INTERVAL_TREE_DEFINE(struct drm_gpuva, rb.node, u64, rb.__subtree_last,\n--\ndrivers/gpu/drm/drm_gpuvm.c-976-\ndrivers/gpu/drm/drm_gpuvm.c:977:static int __drm_gpuva_insert(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c-978-\t\t\t      struct drm_gpuva *va);\n--\ndrivers/gpu/drm/drm_gpuvm.c=981=static bool\ndrivers/gpu/drm/drm_gpuvm.c:982:drm_gpuvm_check_overflow(u64 addr, u64 range)\ndrivers/gpu/drm/drm_gpuvm.c-983-{\n--\ndrivers/gpu/drm/drm_gpuvm.c=989=static bool\ndrivers/gpu/drm/drm_gpuvm.c:990:drm_gpuvm_warn_check_overflow(struct drm_gpuvm *gpuvm, u64 addr, u64 range)\ndrivers/gpu/drm/drm_gpuvm.c-991-{\ndrivers/gpu/drm/drm_gpuvm.c:992:\treturn drm_WARN(gpuvm-\u003edrm, drm_gpuvm_check_overflow(addr, range),\ndrivers/gpu/drm/drm_gpuvm.c-993-\t\t\t\"GPUVA address limited to %zu bytes.\\n\", sizeof(addr));\n--\ndrivers/gpu/drm/drm_gpuvm.c=996=static bool\ndrivers/gpu/drm/drm_gpuvm.c:997:drm_gpuvm_in_mm_range(struct drm_gpuvm *gpuvm, u64 addr, u64 range)\ndrivers/gpu/drm/drm_gpuvm.c-998-{\n--\ndrivers/gpu/drm/drm_gpuvm.c=1006=static bool\ndrivers/gpu/drm/drm_gpuvm.c:1007:drm_gpuvm_in_kernel_node(struct drm_gpuvm *gpuvm, u64 addr, u64 range)\ndrivers/gpu/drm/drm_gpuvm.c-1008-{\n--\ndrivers/gpu/drm/drm_gpuvm.c-1017-/**\ndrivers/gpu/drm/drm_gpuvm.c:1018: * drm_gpuvm_range_valid() - checks whether the given range is valid for the\ndrivers/gpu/drm/drm_gpuvm.c:1019: * given \u0026drm_gpuvm\ndrivers/gpu/drm/drm_gpuvm.c-1020- * @gpuvm: the GPUVM to check the range for\n--\ndrivers/gpu/drm/drm_gpuvm.c=1028=bool\ndrivers/gpu/drm/drm_gpuvm.c:1029:drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c-1030-\t\t      u64 addr, u64 range)\ndrivers/gpu/drm/drm_gpuvm.c-1031-{\ndrivers/gpu/drm/drm_gpuvm.c:1032:\treturn !drm_gpuvm_check_overflow(addr, range) \u0026\u0026\ndrivers/gpu/drm/drm_gpuvm.c:1033:\t       drm_gpuvm_in_mm_range(gpuvm, addr, range) \u0026\u0026\ndrivers/gpu/drm/drm_gpuvm.c:1034:\t       !drm_gpuvm_in_kernel_node(gpuvm, addr, range);\ndrivers/gpu/drm/drm_gpuvm.c-1035-}\ndrivers/gpu/drm/drm_gpuvm.c:1036:EXPORT_SYMBOL_GPL(drm_gpuvm_range_valid);\ndrivers/gpu/drm/drm_gpuvm.c-1037-\ndrivers/gpu/drm/drm_gpuvm.c=1038=static void\ndrivers/gpu/drm/drm_gpuvm.c:1039:drm_gpuvm_gem_object_free(struct drm_gem_object *obj)\ndrivers/gpu/drm/drm_gpuvm.c-1040-{\n--\ndrivers/gpu/drm/drm_gpuvm.c-1044-\ndrivers/gpu/drm/drm_gpuvm.c:1045:static const struct drm_gem_object_funcs drm_gpuvm_object_funcs = {\ndrivers/gpu/drm/drm_gpuvm.c:1046:\t.free = drm_gpuvm_gem_object_free,\ndrivers/gpu/drm/drm_gpuvm.c-1047-};\n--\ndrivers/gpu/drm/drm_gpuvm.c-1049-/**\ndrivers/gpu/drm/drm_gpuvm.c:1050: * drm_gpuvm_resv_object_alloc() - allocate a dummy \u0026drm_gem_object\ndrivers/gpu/drm/drm_gpuvm.c-1051- * @drm: the drivers \u0026drm_device\ndrivers/gpu/drm/drm_gpuvm.c-1052- *\ndrivers/gpu/drm/drm_gpuvm.c:1053: * Allocates a dummy \u0026drm_gem_object which can be passed to drm_gpuvm_init() in\ndrivers/gpu/drm/drm_gpuvm.c-1054- * order to serve as root GEM object providing the \u0026drm_resv shared across\n--\ndrivers/gpu/drm/drm_gpuvm.c=1059=struct drm_gem_object *\ndrivers/gpu/drm/drm_gpuvm.c:1060:drm_gpuvm_resv_object_alloc(struct drm_device *drm)\ndrivers/gpu/drm/drm_gpuvm.c-1061-{\n--\ndrivers/gpu/drm/drm_gpuvm.c-1067-\ndrivers/gpu/drm/drm_gpuvm.c:1068:\tobj-\u003efuncs = \u0026drm_gpuvm_object_funcs;\ndrivers/gpu/drm/drm_gpuvm.c-1069-\tdrm_gem_private_object_init(drm, obj, 0);\n--\ndrivers/gpu/drm/drm_gpuvm.c-1072-}\ndrivers/gpu/drm/drm_gpuvm.c:1073:EXPORT_SYMBOL_GPL(drm_gpuvm_resv_object_alloc);\ndrivers/gpu/drm/drm_gpuvm.c-1074-\ndrivers/gpu/drm/drm_gpuvm.c-1075-/**\ndrivers/gpu/drm/drm_gpuvm.c:1076: * drm_gpuvm_init() - initialize a \u0026drm_gpuvm\ndrivers/gpu/drm/drm_gpuvm.c:1077: * @gpuvm: pointer to the \u0026drm_gpuvm to initialize\ndrivers/gpu/drm/drm_gpuvm.c-1078- * @name: the name of the GPU VA space\ndrivers/gpu/drm/drm_gpuvm.c:1079: * @flags: the \u0026drm_gpuvm_flags for this GPUVM\ndrivers/gpu/drm/drm_gpuvm.c-1080- * @drm: the \u0026drm_device this VM resides in\n--\ndrivers/gpu/drm/drm_gpuvm.c-1085- * @reserve_range: the size of the kernel reserved GPU VA area\ndrivers/gpu/drm/drm_gpuvm.c:1086: * @ops: \u0026drm_gpuvm_ops called on \u0026drm_gpuvm_sm_map / \u0026drm_gpuvm_sm_unmap\ndrivers/gpu/drm/drm_gpuvm.c-1087- *\ndrivers/gpu/drm/drm_gpuvm.c:1088: * The \u0026drm_gpuvm must be initialized with this function before use.\ndrivers/gpu/drm/drm_gpuvm.c-1089- *\n--\ndrivers/gpu/drm/drm_gpuvm.c=1093=void\ndrivers/gpu/drm/drm_gpuvm.c:1094:drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\ndrivers/gpu/drm/drm_gpuvm.c:1095:\t       enum drm_gpuvm_flags flags,\ndrivers/gpu/drm/drm_gpuvm.c-1096-\t       struct drm_device *drm,\n--\ndrivers/gpu/drm/drm_gpuvm.c-1099-\t       u64 reserve_offset, u64 reserve_range,\ndrivers/gpu/drm/drm_gpuvm.c:1100:\t       const struct drm_gpuvm_ops *ops)\ndrivers/gpu/drm/drm_gpuvm.c-1101-{\n--\ndrivers/gpu/drm/drm_gpuvm.c-1125-\ndrivers/gpu/drm/drm_gpuvm.c:1126:\tdrm_gpuvm_warn_check_overflow(gpuvm, start_offset, range);\ndrivers/gpu/drm/drm_gpuvm.c-1127-\tgpuvm-\u003emm_start = start_offset;\n--\ndrivers/gpu/drm/drm_gpuvm.c-1134-\ndrivers/gpu/drm/drm_gpuvm.c:1135:\t\tif (likely(!drm_gpuvm_warn_check_overflow(gpuvm, reserve_offset,\ndrivers/gpu/drm/drm_gpuvm.c-1136-\t\t\t\t\t\t\t  reserve_range)))\n--\ndrivers/gpu/drm/drm_gpuvm.c-1139-}\ndrivers/gpu/drm/drm_gpuvm.c:1140:EXPORT_SYMBOL_GPL(drm_gpuvm_init);\ndrivers/gpu/drm/drm_gpuvm.c-1141-\ndrivers/gpu/drm/drm_gpuvm.c=1142=static void\ndrivers/gpu/drm/drm_gpuvm.c:1143:drm_gpuvm_fini(struct drm_gpuvm *gpuvm)\ndrivers/gpu/drm/drm_gpuvm.c-1144-{\n--\ndrivers/gpu/drm/drm_gpuvm.c=1164=static void\ndrivers/gpu/drm/drm_gpuvm.c:1165:drm_gpuvm_free(struct kref *kref)\ndrivers/gpu/drm/drm_gpuvm.c-1166-{\ndrivers/gpu/drm/drm_gpuvm.c:1167:\tstruct drm_gpuvm *gpuvm = container_of(kref, struct drm_gpuvm, kref);\ndrivers/gpu/drm/drm_gpuvm.c-1168-\tstruct drm_device *drm = gpuvm-\u003edrm;\ndrivers/gpu/drm/drm_gpuvm.c-1169-\ndrivers/gpu/drm/drm_gpuvm.c:1170:\tdrm_gpuvm_fini(gpuvm);\ndrivers/gpu/drm/drm_gpuvm.c-1171-\n--\ndrivers/gpu/drm/drm_gpuvm.c-1179-/**\ndrivers/gpu/drm/drm_gpuvm.c:1180: * drm_gpuvm_put() - drop a struct drm_gpuvm reference\ndrivers/gpu/drm/drm_gpuvm.c:1181: * @gpuvm: the \u0026drm_gpuvm to release the reference of\ndrivers/gpu/drm/drm_gpuvm.c-1182- *\n--\ndrivers/gpu/drm/drm_gpuvm.c=1187=void\ndrivers/gpu/drm/drm_gpuvm.c:1188:drm_gpuvm_put(struct drm_gpuvm *gpuvm)\ndrivers/gpu/drm/drm_gpuvm.c-1189-{\ndrivers/gpu/drm/drm_gpuvm.c-1190-\tif (gpuvm)\ndrivers/gpu/drm/drm_gpuvm.c:1191:\t\tkref_put(\u0026gpuvm-\u003ekref, drm_gpuvm_free);\ndrivers/gpu/drm/drm_gpuvm.c-1192-}\ndrivers/gpu/drm/drm_gpuvm.c:1193:EXPORT_SYMBOL_GPL(drm_gpuvm_put);\ndrivers/gpu/drm/drm_gpuvm.c-1194-\n--\ndrivers/gpu/drm/drm_gpuvm.c=1196=exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj,\n--\ndrivers/gpu/drm/drm_gpuvm.c-1203-/**\ndrivers/gpu/drm/drm_gpuvm.c:1204: * drm_gpuvm_prepare_vm() - prepare the GPUVMs common dma-resv\ndrivers/gpu/drm/drm_gpuvm.c:1205: * @gpuvm: the \u0026drm_gpuvm\ndrivers/gpu/drm/drm_gpuvm.c-1206- * @exec: the \u0026drm_exec context\n--\ndrivers/gpu/drm/drm_gpuvm.c=1217=int\ndrivers/gpu/drm/drm_gpuvm.c:1218:drm_gpuvm_prepare_vm(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c-1219-\t\t     struct drm_exec *exec,\n--\ndrivers/gpu/drm/drm_gpuvm.c-1223-}\ndrivers/gpu/drm/drm_gpuvm.c:1224:EXPORT_SYMBOL_GPL(drm_gpuvm_prepare_vm);\ndrivers/gpu/drm/drm_gpuvm.c-1225-\ndrivers/gpu/drm/drm_gpuvm.c=1226=static int\ndrivers/gpu/drm/drm_gpuvm.c:1227:__drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c-1228-\t\t\t    struct drm_exec *exec,\n\n"
}

7/2 2026/08/17 18:11 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 38428
							output: 22
							thoughts: 168
						

Thoughts:
**Investigating the `va_scratchpad` Module**

Okay, so the `va_scratchpad` parameter is defined within `drm_gpuvm.c`. Right, let's just double-check that `drivers/gpu/drm/Makefile` again, make sure I haven't missed anything obvious. Ah, there it is: `obj-$(CONFIG_DRM_EXEC) += drm_exec.o` and `obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o`. Okay, so, that's it: the module's either `drm_gpuvm.ko`, or built-in, depending on if `CONFIG_DRM_GPUVM` is set to `y`.

Hold on... going over the build process, `drm_gpuvm.c` gets compiled into `drm_gpuvm.o`. If it's *separate* module, then the module name itself would simply be `drm_gpuvm`. Got it. Now I need to confirm whether `drm_gpuvm` is a module.



8/2 2026/08/17 18:12 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "MODULE_.*drm_gpuvm"
}

Results:
{
  "error": "no matches"
}

9/2 2026/08/17 18:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 38867
							output: 21
							thoughts: 45
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Kernel Module Dependency Check**

Okay, so I'm suspecting a module linking issue with `drm_gpuvm.c`. It's possible that `drm_gpuvm.c` could be getting compiled directly into `drm.ko` or as its own `drm_gpuvm.ko`. Before I start digging into the linker scripts or dependencies too deeply, let's just do a sanity check on the build configuration. I'll take another look at the `drivers/gpu/drm/Makefile`. That should give me the definitive answer on how `drm_gpuvm.c` is being handled during the build process and clarify whether it's statically linked into the main DRM module or built as a separate module, which is what I'm hoping for.



10/2 2026/08/17 18:12 tool grepper
0m Args:
{
  "Expression": "drm_gpuvm.o"
}

Results:
{
  "Output": "drivers/gpu/drm/Makefile=106=obj-$(CONFIG_DRM_EXEC) += drm_exec.o\ndrivers/gpu/drm/Makefile:107:obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o\ndrivers/gpu/drm/Makefile-108-\n--\ndrivers/gpu/drm/drm_gpuvm.c-124- * Depending on how the new GPU VA mapping intersects with the existing mappings\ndrivers/gpu/drm/drm_gpuvm.c:125: * of the GPU VA space the \u0026drm_gpuvm_ops callbacks contain an arbitrary amount\ndrivers/gpu/drm/drm_gpuvm.c-126- * of unmap operations, a maximum of two remap operations and a single map\n--\ndrivers/gpu/drm/drm_gpuvm.c-147- *\ndrivers/gpu/drm/drm_gpuvm.c:148: * Analogous to drm_gpuvm_sm_map() drm_gpuvm_sm_unmap() uses \u0026drm_gpuvm_ops to\ndrivers/gpu/drm/drm_gpuvm.c-149- * call back into the driver in order to unmap a range of GPU VA space. The\n--\ndrivers/gpu/drm/drm_gpuvm.c-167- * drm_gpuva_remove() may be used. These functions can safely be used from\ndrivers/gpu/drm/drm_gpuvm.c:168: * \u0026drm_gpuvm_ops callbacks originating from drm_gpuvm_sm_map() or\ndrivers/gpu/drm/drm_gpuvm.c-169- * drm_gpuvm_sm_unmap(). However, it might be more convenient to use the\n--\ndrivers/gpu/drm/drm_gpuvm.c-649- *\t// ops to pass to drm_gpuvm_init()\ndrivers/gpu/drm/drm_gpuvm.c:650: *\tstatic const struct drm_gpuvm_ops driver_gpuvm_ops = {\ndrivers/gpu/drm/drm_gpuvm.c-651- *\t\t.sm_step_map = driver_gpuva_map,\n--\ndrivers/gpu/drm/drm_gpuvm.c=1039=drm_gpuvm_gem_object_free(struct drm_gem_object *obj)\n--\ndrivers/gpu/drm/drm_gpuvm.c-1044-\ndrivers/gpu/drm/drm_gpuvm.c:1045:static const struct drm_gem_object_funcs drm_gpuvm_object_funcs = {\ndrivers/gpu/drm/drm_gpuvm.c-1046-\t.free = drm_gpuvm_gem_object_free,\n--\ndrivers/gpu/drm/drm_gpuvm.c=1060=drm_gpuvm_resv_object_alloc(struct drm_device *drm)\n--\ndrivers/gpu/drm/drm_gpuvm.c-1067-\ndrivers/gpu/drm/drm_gpuvm.c:1068:\tobj-\u003efuncs = \u0026drm_gpuvm_object_funcs;\ndrivers/gpu/drm/drm_gpuvm.c-1069-\tdrm_gem_private_object_init(drm, obj, 0);\n--\ndrivers/gpu/drm/drm_gpuvm.c=1073=EXPORT_SYMBOL_GPL(drm_gpuvm_resv_object_alloc);\n--\ndrivers/gpu/drm/drm_gpuvm.c-1085- * @reserve_range: the size of the kernel reserved GPU VA area\ndrivers/gpu/drm/drm_gpuvm.c:1086: * @ops: \u0026drm_gpuvm_ops called on \u0026drm_gpuvm_sm_map / \u0026drm_gpuvm_sm_unmap\ndrivers/gpu/drm/drm_gpuvm.c-1087- *\n--\ndrivers/gpu/drm/drm_gpuvm.c=1094=drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\n--\ndrivers/gpu/drm/drm_gpuvm.c-1099-\t       u64 reserve_offset, u64 reserve_range,\ndrivers/gpu/drm/drm_gpuvm.c:1100:\t       const struct drm_gpuvm_ops *ops)\ndrivers/gpu/drm/drm_gpuvm.c-1101-{\n--\ndrivers/gpu/drm/drm_gpuvm.c=1476=__drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec)\ndrivers/gpu/drm/drm_gpuvm.c-1477-{\ndrivers/gpu/drm/drm_gpuvm.c:1478:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1479-\tstruct drm_gpuvm_bo *vm_bo;\n--\ndrivers/gpu/drm/drm_gpuvm.c=1496=drm_gpuvm_validate_locked(struct drm_gpuvm *gpuvm, struct drm_exec *exec)\ndrivers/gpu/drm/drm_gpuvm.c-1497-{\ndrivers/gpu/drm/drm_gpuvm.c:1498:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1499-\tstruct drm_gpuvm_bo *vm_bo, *next;\n--\ndrivers/gpu/drm/drm_gpuvm.c-1525- *\ndrivers/gpu/drm/drm_gpuvm.c:1526: * Calls the \u0026drm_gpuvm_ops::vm_bo_validate callback for all evicted buffer\ndrivers/gpu/drm/drm_gpuvm.c-1527- * objects being mapped in the given \u0026drm_gpuvm.\n--\ndrivers/gpu/drm/drm_gpuvm.c=1532=drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec)\ndrivers/gpu/drm/drm_gpuvm.c-1533-{\ndrivers/gpu/drm/drm_gpuvm.c:1534:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1535-\n--\ndrivers/gpu/drm/drm_gpuvm.c=1571=EXPORT_SYMBOL_GPL(drm_gpuvm_resv_add_fence);\n--\ndrivers/gpu/drm/drm_gpuvm.c-1577- *\ndrivers/gpu/drm/drm_gpuvm.c:1578: * If provided by the driver, this function uses the \u0026drm_gpuvm_ops\ndrivers/gpu/drm/drm_gpuvm.c-1579- * vm_bo_alloc() callback to allocate.\n--\ndrivers/gpu/drm/drm_gpuvm.c=1584=drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,\n--\ndrivers/gpu/drm/drm_gpuvm.c-1586-{\ndrivers/gpu/drm/drm_gpuvm.c:1587:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1588-\tstruct drm_gpuvm_bo *vm_bo;\n--\ndrivers/gpu/drm/drm_gpuvm.c=1626=drm_gpuvm_bo_destroy_not_in_lists(struct drm_gpuvm_bo *vm_bo)\n--\ndrivers/gpu/drm/drm_gpuvm.c-1628-\tstruct drm_gpuvm *gpuvm = vm_bo-\u003evm;\ndrivers/gpu/drm/drm_gpuvm.c:1629:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-1630-\tstruct drm_gem_object *obj = vm_bo-\u003eobj;\n--\ndrivers/gpu/drm/drm_gpuvm.c=2360=static int\ndrivers/gpu/drm/drm_gpuvm.c:2361:op_map_cb(const struct drm_gpuvm_ops *fn, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2362-\t  const struct drm_gpuvm_map_req *req)\n--\ndrivers/gpu/drm/drm_gpuvm.c=2378=static int\ndrivers/gpu/drm/drm_gpuvm.c:2379:op_remap_cb(const struct drm_gpuvm_ops *fn, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2380-\t    struct drm_gpuva_op_map *prev,\n--\ndrivers/gpu/drm/drm_gpuvm.c=2396=static int\ndrivers/gpu/drm/drm_gpuvm.c:2397:op_unmap_cb(const struct drm_gpuvm_ops *fn, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2398-\t    struct drm_gpuva *va, bool merge, bool madvise)\n--\ndrivers/gpu/drm/drm_gpuvm.c=2413=__drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c:2414:\t\t   const struct drm_gpuvm_ops *ops, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2415-\t\t   const struct drm_gpuvm_map_req *req,\n--\ndrivers/gpu/drm/drm_gpuvm.c=2592=__drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,\ndrivers/gpu/drm/drm_gpuvm.c:2593:\t\t     const struct drm_gpuvm_ops *ops, void *priv,\ndrivers/gpu/drm/drm_gpuvm.c-2594-\t\t     u64 req_addr, u64 req_range)\n--\ndrivers/gpu/drm/drm_gpuvm.c-2655- * This function iterates the given range of the GPU VA space. It utilizes the\ndrivers/gpu/drm/drm_gpuvm.c:2656: * \u0026drm_gpuvm_ops to call back into the driver providing the split and merge\ndrivers/gpu/drm/drm_gpuvm.c-2657- * steps.\n--\ndrivers/gpu/drm/drm_gpuvm.c=2679=drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2681-{\ndrivers/gpu/drm/drm_gpuvm.c:2682:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2683-\n--\ndrivers/gpu/drm/drm_gpuvm.c=2691=EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map);\n--\ndrivers/gpu/drm/drm_gpuvm.c-2700- * This function iterates the given range of the GPU VA space. It utilizes the\ndrivers/gpu/drm/drm_gpuvm.c:2701: * \u0026drm_gpuvm_ops to call back into the driver providing the operations to\ndrivers/gpu/drm/drm_gpuvm.c-2702- * unmap and, if required, split existing mappings.\n--\ndrivers/gpu/drm/drm_gpuvm.c=2721=drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2723-{\ndrivers/gpu/drm/drm_gpuvm.c:2724:\tconst struct drm_gpuvm_ops *ops = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2725-\n--\ndrivers/gpu/drm/drm_gpuvm.c=2736=drm_gpuva_sm_step_lock(struct drm_gpuva_op *op, void *priv)\n--\ndrivers/gpu/drm/drm_gpuvm.c-2753-\ndrivers/gpu/drm/drm_gpuvm.c:2754:static const struct drm_gpuvm_ops lock_ops = {\ndrivers/gpu/drm/drm_gpuvm.c-2755-\t.sm_step_map = drm_gpuva_sm_step_lock,\n--\ndrivers/gpu/drm/drm_gpuvm.c=2859=gpuva_op_alloc(struct drm_gpuvm *gpuvm)\ndrivers/gpu/drm/drm_gpuvm.c-2860-{\ndrivers/gpu/drm/drm_gpuvm.c:2861:\tconst struct drm_gpuvm_ops *fn = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2862-\tstruct drm_gpuva_op *op;\n--\ndrivers/gpu/drm/drm_gpuvm.c=2877=gpuva_op_free(struct drm_gpuvm *gpuvm,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2879-{\ndrivers/gpu/drm/drm_gpuvm.c:2880:\tconst struct drm_gpuvm_ops *fn = gpuvm-\u003eops;\ndrivers/gpu/drm/drm_gpuvm.c-2881-\n--\ndrivers/gpu/drm/drm_gpuvm.c=2889=drm_gpuva_sm_step(struct drm_gpuva_op *__op,\n--\ndrivers/gpu/drm/drm_gpuvm.c-2943-\ndrivers/gpu/drm/drm_gpuvm.c:2944:static const struct drm_gpuvm_ops gpuvm_list_ops = {\ndrivers/gpu/drm/drm_gpuvm.c-2945-\t.sm_step_map = drm_gpuva_sm_step,\n--\ndrivers/gpu/drm/imagination/pvr_vm.c=507=static void pvr_gpuvm_free(struct drm_gpuvm *gpuvm)\n--\ndrivers/gpu/drm/imagination/pvr_vm.c-511-\ndrivers/gpu/drm/imagination/pvr_vm.c:512:static const struct drm_gpuvm_ops pvr_vm_gpuva_ops = {\ndrivers/gpu/drm/imagination/pvr_vm.c-513-\t.vm_free = pvr_gpuvm_free,\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c=633=msm_gem_vm_sm_step_unmap(struct drm_gpuva_op *op, void *_arg)\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c-692-\ndrivers/gpu/drm/msm/msm_gem_vma.c:693:static const struct drm_gpuvm_ops msm_gpuvm_ops = {\ndrivers/gpu/drm/msm/msm_gem_vma.c-694-\t.vm_free = msm_gem_vm_free,\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c=792=static const struct drm_sched_backend_ops msm_vm_bind_ops = {\n--\ndrivers/gpu/drm/msm/msm_gem_vma.c-810- *\ndrivers/gpu/drm/msm/msm_gem_vma.c:811: * Returns: pointer to the created \u0026struct drm_gpuvm on success\ndrivers/gpu/drm/msm/msm_gem_vma.c-812- * or an ERR_PTR(-errno) on failure.\n--\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c=1873=nouveau_uvmm_bo_validate(struct drm_gpuvm_bo *vm_bo, struct drm_exec *exec)\n--\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c-1880-\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c:1881:static const struct drm_gpuvm_ops gpuvm_ops = {\ndrivers/gpu/drm/nouveau/nouveau_uvmm.c-1882-\t.vm_free = nouveau_uvmm_free,\n--\ndrivers/gpu/drm/panthor/panthor_mmu.c=2535=static int panthor_vm_bo_validate(struct drm_gpuvm_bo *vm_bo,\n--\ndrivers/gpu/drm/panthor/panthor_mmu.c-2555-\ndrivers/gpu/drm/panthor/panthor_mmu.c:2556:static const struct drm_gpuvm_ops panthor_gpuvm_ops = {\ndrivers/gpu/drm/panthor/panthor_mmu.c-2557-\t.vm_free = panthor_vm_free,\n--\ndrivers/gpu/drm/xe/xe_vm.c=1351=static void xe_vm_free(struct drm_gpuvm *gpuvm);\ndrivers/gpu/drm/xe/xe_vm.c-1352-\ndrivers/gpu/drm/xe/xe_vm.c:1353:static const struct drm_gpuvm_ops gpuvm_ops = {\ndrivers/gpu/drm/xe/xe_vm.c-1354-\t.op_alloc = xe_vm_op_alloc,\n--\ninclude/drm/drm_gpuvm.h=40=struct drm_gpuvm_bo;\ninclude/drm/drm_gpuvm.h:41:struct drm_gpuvm_ops;\ninclude/drm/drm_gpuvm.h-42-\n--\ninclude/drm/drm_gpuvm.h=230=struct drm_gpuvm {\n--\ninclude/drm/drm_gpuvm.h-284-\t/**\ninclude/drm/drm_gpuvm.h:285:\t * @ops: \u0026drm_gpuvm_ops providing the split/merge steps to drivers\ninclude/drm/drm_gpuvm.h-286-\t */\ninclude/drm/drm_gpuvm.h:287:\tconst struct drm_gpuvm_ops *ops;\ninclude/drm/drm_gpuvm.h-288-\n--\ninclude/drm/drm_gpuvm.h=349=void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\n--\ninclude/drm/drm_gpuvm.h-354-\t\t    u64 reserve_offset, u64 reserve_range,\ninclude/drm/drm_gpuvm.h:355:\t\t    const struct drm_gpuvm_ops *ops);\ninclude/drm/drm_gpuvm.h-356-\n--\ninclude/drm/drm_gpuvm.h=1129=static inline void drm_gpuva_init_from_op(struct drm_gpuva *va,\n--\ninclude/drm/drm_gpuvm.h-1138-/**\ninclude/drm/drm_gpuvm.h:1139: * struct drm_gpuvm_ops - callbacks for split/merge steps\ninclude/drm/drm_gpuvm.h-1140- *\n--\ninclude/drm/drm_gpuvm.h-1144- */\ninclude/drm/drm_gpuvm.h:1145:struct drm_gpuvm_ops {\ninclude/drm/drm_gpuvm.h-1146-\t/**\n--\nrust/kernel/drm/gpuvm/mod.rs=102=impl\u003cT: DriverGpuVm\u003e GpuVm\u003cT\u003e {\nrust/kernel/drm/gpuvm/mod.rs:103:    const fn vtable() -\u003e \u0026'static bindings::drm_gpuvm_ops {\nrust/kernel/drm/gpuvm/mod.rs:104:        \u0026bindings::drm_gpuvm_ops {\nrust/kernel/drm/gpuvm/mod.rs-105-            vm_free: Some(Self::vm_free),\n"
}

11/2 2026/08/17 18:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 44338
							output: 20
							thoughts: 1507
						

Thoughts:
**Fuzzing Target Identification: A Deep Dive**

Okay, so I've been handed this patch that introduces a new allocator, `folio_pool` and `folio_scratchpad`, and it's touched a decent chunk of the kernel: core logic, drivers, and networking. My spidey-sense is tingling, because new allocators are almost *always* prime fuzzing targets. The potential for resource exhaustion, use-after-free, double-frees – it's all there. The functional impact is definitely enough to warrant a focused effort.

First, let's break down the dependencies.  `drm_gpuvm.c` needs `CONFIG_DRM_GPUVM`, BPF code in both `syscall.c` and `verifier.c` need `CONFIG_BPF_SYSCALL`,  `lockdep.c` needs `CONFIG_PROVE_LOCKING` or `CONFIG_LOCKDEP`, and `nf_tables_api.c` depends on `CONFIG_NF_TABLES`.  Easy enough, these define the build requirements.  Also, it looks like `folio_pool.c` is always built-in since it's added to the Makefile with `obj-y += ... folio_pool.o`, so that's good to know.

Now, the real meat of the matter: what specific functions are we going to target? I've got a list forming in my mind.

From `folio_pool.c`, we have the core operations: `folio_scratchpad_alloc`, `folio_scratchpad_reset`, `folio_scratchpad_free`, `folio_pool_init_align`, `folio_pool_init_key`, `folio_pool_init`, and `folio_scratchpad_stats`.  These are the basic building blocks and are prime candidates for finding memory corruption.

For BPF, we've got `bpf_check` and `generic_map_update_batch`. BPF is always a good target.

For `nf_tables`, I need to make sure I am finding functions that are entrypoints. `nf_tables_trans_destroy_work` is static but `nfnetlink_rcv_batch` is the likely interface. `nf_tables_newtable`, `nf_tables_newchain`, `nf_tables_newrule`, `nf_tables_newset`, `nf_tables_newsetelem`, `nf_tables_delsetelem`, `nf_tables_newobj`, `nf_tables_delobj`, `nf_tables_newflowtable`, and `nf_tables_delflowtable` look promising, but I need to double-check that they are not static first.

Finally, while the functions in `drm_gpuvm.c` and `lockdep.c` may be interesting, many are static, meaning they're less likely to be directly callable from a fuzzing harness. I'm going to focus my efforts on finding non-static functions.

Essentially, I'll be looking at the public-facing APIs of `folio_pool`, BPF verification and update, and netfilter, focusing my fuzzing efforts there.  We've got a solid set of targets and I'm ready to start throwing inputs at them.



12/2 2026/08/17 18:12 tool grepper
0m Args:
{
  "Expression": "^int nf_tables_"
}

Results:
{
  "Output": "include/net/netfilter/nf_tables.h=678=void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,\n--\ninclude/net/netfilter/nf_tables.h-680-\t\t\t      enum nft_trans_phase phase);\ninclude/net/netfilter/nf_tables.h:681:int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,\ninclude/net/netfilter/nf_tables.h-682-\t\t       struct nft_set_binding *binding);\n--\ninclude/net/netfilter/nf_tables.h=1162=int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);\ninclude/net/netfilter/nf_tables.h:1163:int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);\ninclude/net/netfilter/nf_tables.h-1164-void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);\n--\ninclude/net/netfilter/nf_tables_core.h=27=extern struct nft_object_type nft_counter_obj_type;\ninclude/net/netfilter/nf_tables_core.h-28-\ninclude/net/netfilter/nf_tables_core.h:29:int nf_tables_core_module_init(void);\ninclude/net/netfilter/nf_tables_core.h-30-void nf_tables_core_module_exit(void);\n--\nnet/netfilter/nf_tables_api.c=302=static void nft_chain_trans_bind(const struct nft_ctx *ctx,\n--\nnet/netfilter/nf_tables_api.c-307-\nnet/netfilter/nf_tables_api.c:308:int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)\nnet/netfilter/nf_tables_api.c-309-{\n--\nnet/netfilter/nf_tables_api.c=5873=static int nft_set_catchall_bind_check(const struct nft_ctx *ctx,\n--\nnet/netfilter/nf_tables_api.c-5894-\nnet/netfilter/nf_tables_api.c:5895:int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,\nnet/netfilter/nf_tables_api.c-5896-\t\t       struct nft_set_binding *binding)\n"
}

13/2 2026/08/17 18:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 47228
							output: 25
							thoughts: 47
						

Thoughts:
Okay, let's see...

**Diving into the `nf_tables` API Code**

Hmm, right, I'm going to need to track down how this `nf_tables_api.c` module works. It has quite a few static functions, and I'm betting some of them are registered as callbacks. I'll need to figure out what registers them and how they're connected to the overall Netfilter infrastructure. Let's see... I'll search through `nf_tables_api.c` for something that looks like it could be a Netlink subsystem – like `nfnetlink_subsystem` or a similar naming convention. That will likely point me to the registration points.



14/2 2026/08/17 18:12 tool grepper
0m Args:
{
  "Expression": "struct nfnl_callback nf_tables_cb"
}

Results:
{
  "Output": "net/netfilter/nf_tables_api.c=9920=static int nf_tables_getgen(struct sk_buff *skb, const struct nfnl_info *info,\n--\nnet/netfilter/nf_tables_api.c-9941-\nnet/netfilter/nf_tables_api.c:9942:static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {\nnet/netfilter/nf_tables_api.c-9943-\t[NFT_MSG_NEWTABLE] = {\n"
}

15/2 2026/08/17 18:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 47601
							output: 23
							thoughts: 15
						

Thoughts:
Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:

**Analyzing `nf_tables_cb`**

Alright, time to get my hands dirty. My initial thought is, "Let's crack open `nf_tables_cb` and see what's really happening inside." I need to understand its role in this system. I want to know its structure, the data it holds, and most importantly, how it's interacting with the rest of the netfilter subsystem. Are there any potential bugs, or opportunities to optimize the code? I am very curious about how this specific callback function is influencing the performance and functionality of the overall network filtering system. I know where to look. Let's see what this thing is *actually* doing.



16/2 2026/08/17 18:12 tool grepper
0m Error:
bad expression: fatal: -e option, '\[NFT_MSG_.*\] = {': Unmatched \{

Args:
{
  "Expression": "\\[NFT_MSG_.*\\] = {"
}

Results:
{
  "error": "bad expression: fatal: -e option, '\\[NFT_MSG_.*\\] = {': Unmatched \\{"
}

17/2 2026/08/17 18:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 47844
							output: 23
							thoughts: 13
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Decoding the Digital Blueprint: My Initial Assessment**

Alright, that's what I expect to see. The moment I see that pattern, `\[NFT_MSG_.*\] = \{`, my brain instantly jumps into action. I'm recognizing the standard signature of a structured data block, probably JSON or something similar, representing a Non-Fungible Token message. The `\[NFT_MSG_.*]` part screams "NFT-related," and that regex specifically designed to grab a group, indicating a dynamic key likely related to a transaction ID, message type, or some other contextual identifier. The `= {` is the giveaway - it's a key-value pair declaration, where the value is either a series of sub-objects, simple types, arrays, etc. The curly braces are a dead giveaway for object notation.

My immediate assumptions are:

1.  **Data Structure:** It's a structured message, likely JSON, but possibly a similar format like YAML or even a custom, delimited format. The presence of that pattern is an early indication that the message can be readily interpreted.
2.  **NFT Context:** I'm dealing with data about an NFT. The `NFT_MSG` prefix provides the necessary context.
3.  **Dynamic Key:** The `.*` in the key means there is variability there. The specific key is likely critical for identifying the particular NFT or transaction. I'm already anticipating needing to parse that dynamic part. It's probably a unique identifier, hash, or some other key that distinguishes this message from others.
4.  **Content Expectation:** The data following the equals sign (`=`) will be the meat of the message. It's the actual content related to the NFT – the metadata, transaction details, or other pertinent information. The curly braces indicates that this is a JSON-like object or a structure containing other properties.

My next steps will obviously involve figuring out the exact format, the specific data elements, and, most importantly, the significance of the key. I will expect to find other parts of the message. I also need to check the format type, to see if it is JSON or some other format. I am already anticipating needing to use a parser. I need to know where it comes from and what exactly is contained within the message. My brain starts to gear up for parsing, validation, and contextual analysis.



18/2 2026/08/17 18:12 tool grepper
0m Args:
{
  "Expression": "\\[NFT_MSG_.*\\] = \\{"
}

Results:
{
  "Output": "net/netfilter/nf_tables_api.c=74=struct nft_audit_data {\n--\nnet/netfilter/nf_tables_api.c-80-\nnet/netfilter/nf_tables_api.c:81:static const u8 nft2audit_op[NFT_MSG_MAX] = { // enum nf_tables_msg_types\nnet/netfilter/nf_tables_api.c-82-\t[NFT_MSG_NEWTABLE]\t= AUDIT_NFT_OP_TABLE_REGISTER,\n--\nnet/netfilter/nf_tables_api.c=9920=static int nf_tables_getgen(struct sk_buff *skb, const struct nfnl_info *info,\n--\nnet/netfilter/nf_tables_api.c-9941-\nnet/netfilter/nf_tables_api.c:9942:static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {\nnet/netfilter/nf_tables_api.c:9943:\t[NFT_MSG_NEWTABLE] = {\nnet/netfilter/nf_tables_api.c-9944-\t\t.call\t\t= nf_tables_newtable,\n--\nnet/netfilter/nf_tables_api.c-9948-\t},\nnet/netfilter/nf_tables_api.c:9949:\t[NFT_MSG_GETTABLE] = {\nnet/netfilter/nf_tables_api.c-9950-\t\t.call\t\t= nf_tables_gettable,\n--\nnet/netfilter/nf_tables_api.c-9954-\t},\nnet/netfilter/nf_tables_api.c:9955:\t[NFT_MSG_DELTABLE] = {\nnet/netfilter/nf_tables_api.c-9956-\t\t.call\t\t= nf_tables_deltable,\n--\nnet/netfilter/nf_tables_api.c-9960-\t},\nnet/netfilter/nf_tables_api.c:9961:\t[NFT_MSG_DESTROYTABLE] = {\nnet/netfilter/nf_tables_api.c-9962-\t\t.call\t\t= nf_tables_deltable,\n--\nnet/netfilter/nf_tables_api.c-9966-\t},\nnet/netfilter/nf_tables_api.c:9967:\t[NFT_MSG_NEWCHAIN] = {\nnet/netfilter/nf_tables_api.c-9968-\t\t.call\t\t= nf_tables_newchain,\n--\nnet/netfilter/nf_tables_api.c-9972-\t},\nnet/netfilter/nf_tables_api.c:9973:\t[NFT_MSG_GETCHAIN] = {\nnet/netfilter/nf_tables_api.c-9974-\t\t.call\t\t= nf_tables_getchain,\n--\nnet/netfilter/nf_tables_api.c-9978-\t},\nnet/netfilter/nf_tables_api.c:9979:\t[NFT_MSG_DELCHAIN] = {\nnet/netfilter/nf_tables_api.c-9980-\t\t.call\t\t= nf_tables_delchain,\n--\nnet/netfilter/nf_tables_api.c-9984-\t},\nnet/netfilter/nf_tables_api.c:9985:\t[NFT_MSG_DESTROYCHAIN] = {\nnet/netfilter/nf_tables_api.c-9986-\t\t.call\t\t= nf_tables_delchain,\n--\nnet/netfilter/nf_tables_api.c-9990-\t},\nnet/netfilter/nf_tables_api.c:9991:\t[NFT_MSG_NEWRULE] = {\nnet/netfilter/nf_tables_api.c-9992-\t\t.call\t\t= nf_tables_newrule,\n--\nnet/netfilter/nf_tables_api.c-9996-\t},\nnet/netfilter/nf_tables_api.c:9997:\t[NFT_MSG_GETRULE] = {\nnet/netfilter/nf_tables_api.c-9998-\t\t.call\t\t= nf_tables_getrule,\n--\nnet/netfilter/nf_tables_api.c-10002-\t},\nnet/netfilter/nf_tables_api.c:10003:\t[NFT_MSG_GETRULE_RESET] = {\nnet/netfilter/nf_tables_api.c-10004-\t\t.call\t\t= nf_tables_getrule,\n--\nnet/netfilter/nf_tables_api.c-10008-\t},\nnet/netfilter/nf_tables_api.c:10009:\t[NFT_MSG_DELRULE] = {\nnet/netfilter/nf_tables_api.c-10010-\t\t.call\t\t= nf_tables_delrule,\n--\nnet/netfilter/nf_tables_api.c-10014-\t},\nnet/netfilter/nf_tables_api.c:10015:\t[NFT_MSG_DESTROYRULE] = {\nnet/netfilter/nf_tables_api.c-10016-\t\t.call\t\t= nf_tables_delrule,\n--\nnet/netfilter/nf_tables_api.c-10020-\t},\nnet/netfilter/nf_tables_api.c:10021:\t[NFT_MSG_NEWSET] = {\nnet/netfilter/nf_tables_api.c-10022-\t\t.call\t\t= nf_tables_newset,\n--\nnet/netfilter/nf_tables_api.c-10026-\t},\nnet/netfilter/nf_tables_api.c:10027:\t[NFT_MSG_GETSET] = {\nnet/netfilter/nf_tables_api.c-10028-\t\t.call\t\t= nf_tables_getset,\n--\nnet/netfilter/nf_tables_api.c-10032-\t},\nnet/netfilter/nf_tables_api.c:10033:\t[NFT_MSG_DELSET] = {\nnet/netfilter/nf_tables_api.c-10034-\t\t.call\t\t= nf_tables_delset,\n--\nnet/netfilter/nf_tables_api.c-10038-\t},\nnet/netfilter/nf_tables_api.c:10039:\t[NFT_MSG_DESTROYSET] = {\nnet/netfilter/nf_tables_api.c-10040-\t\t.call\t\t= nf_tables_delset,\n--\nnet/netfilter/nf_tables_api.c-10044-\t},\nnet/netfilter/nf_tables_api.c:10045:\t[NFT_MSG_NEWSETELEM] = {\nnet/netfilter/nf_tables_api.c-10046-\t\t.call\t\t= nf_tables_newsetelem,\n--\nnet/netfilter/nf_tables_api.c-10050-\t},\nnet/netfilter/nf_tables_api.c:10051:\t[NFT_MSG_GETSETELEM] = {\nnet/netfilter/nf_tables_api.c-10052-\t\t.call\t\t= nf_tables_getsetelem,\n--\nnet/netfilter/nf_tables_api.c-10056-\t},\nnet/netfilter/nf_tables_api.c:10057:\t[NFT_MSG_GETSETELEM_RESET] = {\nnet/netfilter/nf_tables_api.c-10058-\t\t.call\t\t= nf_tables_getsetelem,\n--\nnet/netfilter/nf_tables_api.c-10062-\t},\nnet/netfilter/nf_tables_api.c:10063:\t[NFT_MSG_DELSETELEM] = {\nnet/netfilter/nf_tables_api.c-10064-\t\t.call\t\t= nf_tables_delsetelem,\n--\nnet/netfilter/nf_tables_api.c-10068-\t},\nnet/netfilter/nf_tables_api.c:10069:\t[NFT_MSG_DESTROYSETELEM] = {\nnet/netfilter/nf_tables_api.c-10070-\t\t.call\t\t= nf_tables_delsetelem,\n--\nnet/netfilter/nf_tables_api.c-10074-\t},\nnet/netfilter/nf_tables_api.c:10075:\t[NFT_MSG_GETGEN] = {\nnet/netfilter/nf_tables_api.c-10076-\t\t.call\t\t= nf_tables_getgen,\n--\nnet/netfilter/nf_tables_api.c-10078-\t},\nnet/netfilter/nf_tables_api.c:10079:\t[NFT_MSG_NEWOBJ] = {\nnet/netfilter/nf_tables_api.c-10080-\t\t.call\t\t= nf_tables_newobj,\n--\nnet/netfilter/nf_tables_api.c-10084-\t},\nnet/netfilter/nf_tables_api.c:10085:\t[NFT_MSG_GETOBJ] = {\nnet/netfilter/nf_tables_api.c-10086-\t\t.call\t\t= nf_tables_getobj,\n--\nnet/netfilter/nf_tables_api.c-10090-\t},\nnet/netfilter/nf_tables_api.c:10091:\t[NFT_MSG_DELOBJ] = {\nnet/netfilter/nf_tables_api.c-10092-\t\t.call\t\t= nf_tables_delobj,\n--\nnet/netfilter/nf_tables_api.c-10096-\t},\nnet/netfilter/nf_tables_api.c:10097:\t[NFT_MSG_DESTROYOBJ] = {\nnet/netfilter/nf_tables_api.c-10098-\t\t.call\t\t= nf_tables_delobj,\n--\nnet/netfilter/nf_tables_api.c-10102-\t},\nnet/netfilter/nf_tables_api.c:10103:\t[NFT_MSG_GETOBJ_RESET] = {\nnet/netfilter/nf_tables_api.c-10104-\t\t.call\t\t= nf_tables_getobj,\n--\nnet/netfilter/nf_tables_api.c-10108-\t},\nnet/netfilter/nf_tables_api.c:10109:\t[NFT_MSG_NEWFLOWTABLE] = {\nnet/netfilter/nf_tables_api.c-10110-\t\t.call\t\t= nf_tables_newflowtable,\n--\nnet/netfilter/nf_tables_api.c-10114-\t},\nnet/netfilter/nf_tables_api.c:10115:\t[NFT_MSG_GETFLOWTABLE] = {\nnet/netfilter/nf_tables_api.c-10116-\t\t.call\t\t= nf_tables_getflowtable,\n--\nnet/netfilter/nf_tables_api.c-10120-\t},\nnet/netfilter/nf_tables_api.c:10121:\t[NFT_MSG_DELFLOWTABLE] = {\nnet/netfilter/nf_tables_api.c-10122-\t\t.call\t\t= nf_tables_delflowtable,\n--\nnet/netfilter/nf_tables_api.c-10126-\t},\nnet/netfilter/nf_tables_api.c:10127:\t[NFT_MSG_DESTROYFLOWTABLE] = {\nnet/netfilter/nf_tables_api.c-10128-\t\t.call\t\t= nf_tables_delflowtable,\n"
}

19/2 2026/08/17 18:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 51411
							output: 21
							thoughts: 263
						

Thoughts:
**Analyzing the nf_tables Entry Points**

Okay, so I'm digging into `nf_tables` and trying to pin down the key entry points for how it processes commands. My first instinct was to look at `nfnetlink_rcv_batch` along with the individual handlers that get called from it, like `nf_tables_newsetelem` and `nf_tables_newrule`. Those seem like the logical places to start, right? But then I remember that `nf_tables_api.c` isn't the whole story. This patch modifies `nft_trans_alloc`, which is a function that's called by the new rule and set element handlers. 

So, `nfnetlink_rcv_batch` *is* a good top-level entry point, but that's in `net/netfilter/nfnetlink.c`, not specifically within the `nf_tables` code. It's the receiver, not the processor, of those messages. Within `nf_tables` itself, I should probably also consider `nf_tables_newrule`, `nf_tables_newsetelem`, `nf_tables_delsetelem`, `nf_tables_newchain`, and `nf_tables_delchain`. Those seem like they'd be critical. But wait, those are static.

Then I got to thinking, what about `nf_tables_commit` or `nf_tables_abort`? They handle the transaction part of the message processing. Ah, but `nf_tables_abort` is *also* static.

Alright, let's keep it simple. Given all of this, let's just use `nfnetlink_rcv_batch` for now, for the core `nf_tables` work. Or maybe `nf_tables_commit`? Wait, is `nf_tables_commit` static, too? I'll need to double check that.



20/2 2026/08/17 18:12 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "^int nf_tables_commit"
}

Results:
{
  "error": "no matches"
}

21/2 2026/08/17 18:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 52120
							output: 19
							thoughts: 18
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:

**Checking `nf_tables_commit`**

Right, let's dig into this `nf_tables_commit` function. I'm getting a hunch it might be static. It feels like something that could be tightly scoped, maybe internal to a specific module or subsystem within the Netfilter framework. A static declaration would make sense from a design perspective, enhancing encapsulation and potentially simplifying the compiler's optimizations. So, the first step is to confirm whether my suspicion holds any weight. Let's see... I'll check its definition to see if it's declared static.



22/2 2026/08/17 18:12 tool grepper
0m Args:
{
  "Expression": "nf_tables_commit"
}

Results:
{
  "Output": "net/netfilter/nf_tables_api.c=10307=EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);\nnet/netfilter/nf_tables_api.c-10308-\nnet/netfilter/nf_tables_api.c:10309:static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain)\nnet/netfilter/nf_tables_api.c-10310-{\n--\nnet/netfilter/nf_tables_api.c-10383-\nnet/netfilter/nf_tables_api.c:10384:static void nf_tables_commit_chain_prepare_cancel(struct net *net)\nnet/netfilter/nf_tables_api.c-10385-{\n--\nnet/netfilter/nf_tables_api.c-10399-\nnet/netfilter/nf_tables_api.c:10400:static void __nf_tables_commit_chain_free_rules(struct rcu_head *h)\nnet/netfilter/nf_tables_api.c-10401-{\n--\nnet/netfilter/nf_tables_api.c-10406-\nnet/netfilter/nf_tables_api.c:10407:static void nf_tables_commit_chain_free_rules_old(struct nft_rule_blob *blob)\nnet/netfilter/nf_tables_api.c-10408-{\n--\nnet/netfilter/nf_tables_api.c-10414-\nnet/netfilter/nf_tables_api.c:10415:\tcall_rcu(\u0026last-\u003eh, __nf_tables_commit_chain_free_rules);\nnet/netfilter/nf_tables_api.c-10416-}\nnet/netfilter/nf_tables_api.c-10417-\nnet/netfilter/nf_tables_api.c:10418:static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)\nnet/netfilter/nf_tables_api.c-10419-{\n--\nnet/netfilter/nf_tables_api.c-10440-\t\t\trcu_assign_pointer(chain-\u003eblob_gen_1, g0);\nnet/netfilter/nf_tables_api.c:10441:\t\t\tnf_tables_commit_chain_free_rules_old(g1);\nnet/netfilter/nf_tables_api.c-10442-\t\t} else {\nnet/netfilter/nf_tables_api.c-10443-\t\t\trcu_assign_pointer(chain-\u003eblob_gen_0, g1);\nnet/netfilter/nf_tables_api.c:10444:\t\t\tnf_tables_commit_chain_free_rules_old(g0);\nnet/netfilter/nf_tables_api.c-10445-\t\t}\n--\nnet/netfilter/nf_tables_api.c-10460-\tif (next_genbit)\nnet/netfilter/nf_tables_api.c:10461:\t\tnf_tables_commit_chain_free_rules_old(g1);\nnet/netfilter/nf_tables_api.c-10462-\telse\nnet/netfilter/nf_tables_api.c:10463:\t\tnf_tables_commit_chain_free_rules_old(g0);\nnet/netfilter/nf_tables_api.c-10464-}\n--\nnet/netfilter/nf_tables_api.c=10713=static void nf_tables_module_autoload_cleanup(struct net *net)\n--\nnet/netfilter/nf_tables_api.c-10725-\nnet/netfilter/nf_tables_api.c:10726:static void nf_tables_commit_release(struct net *net)\nnet/netfilter/nf_tables_api.c-10727-{\n--\nnet/netfilter/nf_tables_api.c=10760=static void nft_commit_notify(struct net *net, u32 portid)\n--\nnet/netfilter/nf_tables_api.c-10795-\nnet/netfilter/nf_tables_api.c:10796:static int nf_tables_commit_audit_alloc(struct list_head *adl,\nnet/netfilter/nf_tables_api.c-10797-\t\t\t\t\tstruct nft_table *table)\n--\nnet/netfilter/nf_tables_api.c-10812-\nnet/netfilter/nf_tables_api.c:10813:static void nf_tables_commit_audit_free(struct list_head *adl)\nnet/netfilter/nf_tables_api.c-10814-{\n--\nnet/netfilter/nf_tables_api.c-10825- */\nnet/netfilter/nf_tables_api.c:10826:static unsigned int nf_tables_commit_audit_entrycount(const struct nft_trans *trans)\nnet/netfilter/nf_tables_api.c-10827-{\n--\nnet/netfilter/nf_tables_api.c-10836-\nnet/netfilter/nf_tables_api.c:10837:static void nf_tables_commit_audit_collect(struct list_head *adl,\nnet/netfilter/nf_tables_api.c-10838-\t\t\t\t\t   const struct nft_trans *trans, u32 op)\n--\nnet/netfilter/nf_tables_api.c-10849-found:\nnet/netfilter/nf_tables_api.c:10850:\tadp-\u003eentries += nf_tables_commit_audit_entrycount(trans);\nnet/netfilter/nf_tables_api.c-10851-\tif (!adp-\u003eop || adp-\u003eop \u003e op)\n--\nnet/netfilter/nf_tables_api.c-10856-\nnet/netfilter/nf_tables_api.c:10857:static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation)\nnet/netfilter/nf_tables_api.c-10858-{\n--\nnet/netfilter/nf_tables_api.c=10897=static void nft_gc_seq_end(struct nftables_pernet *nft_net, unsigned int gc_seq)\n--\nnet/netfilter/nf_tables_api.c-10901-\nnet/netfilter/nf_tables_api.c:10902:static int nf_tables_commit(struct net *net, struct sk_buff *skb)\nnet/netfilter/nf_tables_api.c-10903-{\n--\nnet/netfilter/nf_tables_api.c-10963-\nnet/netfilter/nf_tables_api.c:10964:\t\tret = nf_tables_commit_audit_alloc(\u0026adl, table);\nnet/netfilter/nf_tables_api.c-10965-\t\tif (ret) {\nnet/netfilter/nf_tables_api.c:10966:\t\t\tnf_tables_commit_chain_prepare_cancel(net);\nnet/netfilter/nf_tables_api.c:10967:\t\t\tnf_tables_commit_audit_free(\u0026adl);\nnet/netfilter/nf_tables_api.c-10968-\t\t\treturn ret;\n--\nnet/netfilter/nf_tables_api.c-10973-\nnet/netfilter/nf_tables_api.c:10974:\t\t\tret = nf_tables_commit_chain_prepare(net, chain);\nnet/netfilter/nf_tables_api.c-10975-\t\t\tif (ret \u003c 0) {\nnet/netfilter/nf_tables_api.c:10976:\t\t\t\tnf_tables_commit_chain_prepare_cancel(net);\nnet/netfilter/nf_tables_api.c:10977:\t\t\t\tnf_tables_commit_audit_free(\u0026adl);\nnet/netfilter/nf_tables_api.c-10978-\t\t\t\treturn ret;\n--\nnet/netfilter/nf_tables_api.c-10985-\t\tlist_for_each_entry(chain, \u0026table-\u003echains, list)\nnet/netfilter/nf_tables_api.c:10986:\t\t\tnf_tables_commit_chain(net, chain);\nnet/netfilter/nf_tables_api.c-10987-\t}\n--\nnet/netfilter/nf_tables_api.c-11009-\nnet/netfilter/nf_tables_api.c:11010:\t\tnf_tables_commit_audit_collect(\u0026adl, trans, trans-\u003emsg_type);\nnet/netfilter/nf_tables_api.c-11011-\t\tswitch (trans-\u003emsg_type) {\n--\nnet/netfilter/nf_tables_api.c-11207-\tnf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);\nnet/netfilter/nf_tables_api.c:11208:\tnf_tables_commit_audit_log(\u0026adl, nft_base_seq(net));\nnet/netfilter/nf_tables_api.c-11209-\n--\nnet/netfilter/nf_tables_api.c-11211-\tnft_net-\u003evalidate_state = NFT_VALIDATE_SKIP;\nnet/netfilter/nf_tables_api.c:11212:\tnf_tables_commit_release(net);\nnet/netfilter/nf_tables_api.c-11213-\n--\nnet/netfilter/nf_tables_api.c=11549=static const struct nfnetlink_subsystem nf_tables_subsys = {\n--\nnet/netfilter/nf_tables_api.c-11553-\t.cb\t\t= nf_tables_cb,\nnet/netfilter/nf_tables_api.c:11554:\t.commit\t\t= nf_tables_commit,\nnet/netfilter/nf_tables_api.c-11555-\t.abort\t\t= nf_tables_abort,\n--\nnet/netfilter/nft_lookup.c=58=static unsigned int nft_base_seq(const struct net *net)\nnet/netfilter/nft_lookup.c-59-{\nnet/netfilter/nft_lookup.c:60:\t/* pairs with smp_store_release() in nf_tables_commit() */\nnet/netfilter/nft_lookup.c-61-\treturn smp_load_acquire(\u0026net-\u003enft.base_seq);\n--\nnet/netfilter/nft_lookup.c=70=nft_set_do_lookup(const struct net *net, const struct nft_set *set,\n--\nnet/netfilter/nft_lookup.c-82-\t\t/* No match?  There is a small chance that lookup was\nnet/netfilter/nft_lookup.c:83:\t\t * performed in the old generation, but nf_tables_commit()\nnet/netfilter/nft_lookup.c-84-\t\t * already unlinked a (matching) element.\n"
}

23/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 55148
							output: 20
							thoughts: 238
						

Thoughts:
**My Reasoning for Selecting API Entrypoints**

Alright, let's break this down. I've got `nf_tables_commit` and `nf_tables_abort` flagged as static, which means they are likely internal helper functions. Then, I remember `nfnetlink_rcv_batch` is the entry point for handling netfilter netlink batches. Crucially, it's the one that calls those static functions. Okay, so that's in `net/netfilter/nfnetlink.c`.

Now, the instruction is to prioritize non-static, non-inlined API entrypoint functions over these internal helpers. For `nf_tables`, clearly, `nfnetlink_rcv_batch` is the way to go. It's the public-facing entry point we need.

Moving onto `bpf`, I need to find the equivalent. I'm thinking, `sys_bpf`, `__sys_bpf`, or maybe even `bpf_check` are likely candidates. Oh, right, `bpf_check` is non-static! Okay, let's verify that quickly. I'll need to check the definition of `bpf_check` in `kernel/bpf/verifier.c` to confirm.



24/2 2026/08/17 18:13 tool grepper
0m Args:
{
  "Expression": "^int bpf_check"
}

Results:
{
  "Output": "include/linux/bpf.h=3041=int bpf_get_file_flag(int flags);\ninclude/linux/bpf.h:3042:int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,\ninclude/linux/bpf.h-3043-\t\t\t     size_t actual_size);\n--\ninclude/linux/bpf.h=3046=struct bpf_log_attr;\ninclude/linux/bpf.h:3047:int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr,\ninclude/linux/bpf.h-3048-\t      struct bpf_log_attr *attr_log);\n--\ninclude/linux/bpf_verifier.h=1154=static inline void bpf_trampoline_unpack_key(u64 key, u32 *obj_id, u32 *btf_id)\n--\ninclude/linux/bpf_verifier.h-1161-\ninclude/linux/bpf_verifier.h:1162:int bpf_check_btf_info_early(struct bpf_verifier_env *env,\ninclude/linux/bpf_verifier.h-1163-\t\t\t     const union bpf_attr *attr, bpfptr_t uattr);\ninclude/linux/bpf_verifier.h:1164:int bpf_check_btf_info(struct bpf_verifier_env *env,\ninclude/linux/bpf_verifier.h-1165-\t\t       const union bpf_attr *attr, bpfptr_t uattr);\ninclude/linux/bpf_verifier.h-1166-\ninclude/linux/bpf_verifier.h:1167:int bpf_check_attach_target(struct bpf_verifier_log *log,\ninclude/linux/bpf_verifier.h-1168-\t\t\t    const struct bpf_prog *prog,\n--\ninclude/linux/bpf_verifier.h=1415=int bpf_prune_dead_branches(struct bpf_verifier_env *env);\ninclude/linux/bpf_verifier.h:1416:int bpf_check_cfg(struct bpf_verifier_env *env);\ninclude/linux/bpf_verifier.h-1417-int bpf_compute_postorder(struct bpf_verifier_env *env);\n--\ninclude/linux/bpf_verifier.h=1595=int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\ninclude/linux/bpf_verifier.h-1598-/* Functions exported from verifier.c, used by trampoline.c */\ninclude/linux/bpf_verifier.h:1599:int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 btf_id,\ninclude/linux/bpf_verifier.h-1600-\t\t\t\t  struct bpf_attach_target_info *tgt_info);\n--\nkernel/bpf/cfg.c=426=static int visit_insn(int t, struct bpf_verifier_env *env)\n--\nkernel/bpf/cfg.c-561- */\nkernel/bpf/cfg.c:562:int bpf_check_cfg(struct bpf_verifier_env *env)\nkernel/bpf/cfg.c-563-{\n--\nkernel/bpf/check_btf.c=341=static int check_core_relo(struct bpf_verifier_env *env,\n--\nkernel/bpf/check_btf.c-409-\nkernel/bpf/check_btf.c:410:int bpf_check_btf_info_early(struct bpf_verifier_env *env,\nkernel/bpf/check_btf.c-411-\t\t\t     const union bpf_attr *attr,\n--\nkernel/bpf/check_btf.c-437-\nkernel/bpf/check_btf.c:438:int bpf_check_btf_info(struct bpf_verifier_env *env,\nkernel/bpf/check_btf.c-439-\t\t       const union bpf_attr *attr,\n--\nkernel/bpf/syscall.c=73=static const struct bpf_map_ops * const bpf_map_types[] = {\n--\nkernel/bpf/syscall.c-92- */\nkernel/bpf/syscall.c:93:int bpf_check_uarg_tail_zero(bpfptr_t uaddr,\nkernel/bpf/syscall.c-94-\t\t\t     size_t expected_size,\n--\nkernel/bpf/verifier.c=18896=btf_attach_func_proto(struct bpf_verifier_log *log, struct btf *btf, u32 func_id)\n--\nkernel/bpf/verifier.c-18930-\nkernel/bpf/verifier.c:18931:int bpf_check_attach_target(struct bpf_verifier_log *log,\nkernel/bpf/verifier.c-18932-\t\t\t    const struct bpf_prog *prog,\n--\nkernel/bpf/verifier.c=19336=static int check_attach_btf_id(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-19435-\nkernel/bpf/verifier.c:19436:int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 btf_id,\nkernel/bpf/verifier.c-19437-\t\t\t\t  struct bpf_attach_target_info *tgt_info)\n--\nkernel/bpf/verifier.c=19633=int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-19776-\nkernel/bpf/verifier.c:19777:int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\nkernel/bpf/verifier.c-19778-\t      struct bpf_log_attr *attr_log)\n"
}

25/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 57306
							output: 23
							thoughts: 26
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Checking Code Visibility**

Right, so `bpf_check`... definitely non-static. No surprises there. That's good; it's exactly what I'd expect given its role. Now, where am I seeing that function being called, and how does that relate to the current problem? The context is the important part.

Okay, let me shift gears. What about `generic_map_update_batch`? I vaguely recall that function. Hmm, what's its visibility like? Is it being exposed appropriately? I need to quickly recall if this is static or not. My intuition is that it isn't static either, and likely needs to be global. Gotta be careful with those batch updates. Those functions often require special considerations. Need to verify that's configured properly.



26/2 2026/08/17 18:13 tool grepper
0m Args:
{
  "Expression": "^int generic_map_update_batch"
}

Results:
{
  "Output": "kernel/bpf/syscall.c=2063=DEFINE_STATIC_KEY_TRUE(bpf_batch_scratchpad_key);\nkernel/bpf/syscall.c-2064-\nkernel/bpf/syscall.c:2065:int generic_map_update_batch(struct bpf_map *map, struct file *map_file,\nkernel/bpf/syscall.c-2066-\t\t\t     const union bpf_attr *attr,\n"
}

27/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 57668
							output: 23
							thoughts: 29
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Checking on Static Functions**

Right, so I'm focusing on these updates. I know that `generic_map_update_batch` isn't static. Good. That's one check off the list.

Now, what about `drm_gpuvm_init`? I need to quickly recall the implications of that one. Let's see... I'll need to check the potential impact on the overall architecture. Gotta make sure everything lines up properly. That's the key question now.



28/2 2026/08/17 18:13 tool grepper
0m Args:
{
  "Expression": "^void drm_gpuvm_init"
}

Results:
{
  "Output": "include/drm/drm_gpuvm.h=230=struct drm_gpuvm {\n--\ninclude/drm/drm_gpuvm.h-348-\ninclude/drm/drm_gpuvm.h:349:void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,\ninclude/drm/drm_gpuvm.h-350-\t\t    enum drm_gpuvm_flags flags,\n"
}

29/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 57975
							output: 25
							thoughts: 678
						

Thoughts:
**My Analysis of the Patch's Impact**

Okay, let's break this down systematically. My initial thought was to quickly identify the key functions this patch touches and understand their context. I started by looking at `drm_gpuvm_init`, seeing it was exported. Then I moved on to consider how the patch modifies `gpuva_op_alloc` and `gpuva_op_free`, which are in turn called by `drm_gpuvm_sm_map` and `drm_gpuvm_sm_unmap`. These latter functions appear to be important entry points.

Next, I focused on the lockdep aspect, since the changes touch `alloc_list_entry`, which is used in several places related to dependency tracking, like `add_chain_cache`, and `register_lock_class`. This made me think that *any* lock acquisition that creates a new dependency could trigger these changes. Initially, I thought about adding `lock_acquire`, but then I remembered the instruction to avoid generic hot-path functions, so `lock_acquire` is out. I then looked at `lockdep_register_key`, but I also realized that the patch also touches `lockdep_compact_boot_graph`, which is a `late_initcall` function that is executed during boot. So, lockdep itself is triggered by everything, so it is probably a bad idea to specify specific lockdep functions.

So, here's the list of functions I've identified as important based on these considerations:

*   `folio_scratchpad_alloc`
*   `folio_scratchpad_reset`
*   `folio_scratchpad_free`
*   `folio_pool_init_align`
*   `folio_pool_init_key`
*   `folio_pool_init`
*   `folio_scratchpad_stats`
*   `bpf_check`
*   `generic_map_update_batch`
*   `nfnetlink_rcv_batch`
*   `drm_gpuvm_sm_map`
*   `drm_gpuvm_sm_unmap`
*   `drm_gpuvm_init`

Now, let's examine these one by one. I want to make sure I am on the right track and will likely check the code for `nfnetlink_rcv_batch` next.



30/2 2026/08/17 18:13 tool grepper
0m Args:
{
  "Expression": "^static void nfnetlink_rcv_batch"
}

Results:
{
  "Output": "net/netfilter/nfnetlink.c=363=enum {\n--\nnet/netfilter/nfnetlink.c-368-\nnet/netfilter/nfnetlink.c:369:static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,\nnet/netfilter/nfnetlink.c-370-\t\t\t\tu16 subsys_id, u32 genid)\n"
}

31/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 59317
							output: 23
							thoughts: 28
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Analyzing `nfnetlink_rcv_batch`**

Okay, so `nfnetlink_rcv_batch` is static. That immediately piques my interest. Static functions within a kernel context often signal a few things: it's either an internal helper, tightly coupled to a specific module, or potentially used to limit external access to it. It's time to dig deeper and see how it interacts within the Netfilter framework. I'll need to understand the call hierarchy and the scope.

Given it's static, it's very likely `nfnetlink_rcv_batch` is only called from within the same compilation unit, or perhaps a limited set of functions within the netfilter subsystem. The fact that it is a *batch* processing function also suggests it will be pulling messages off the network and running some kind of loop. 

Now, let's trace this back and examine `nfnetlink_rcv` - that's the more likely entry point. Understanding its relationship to `nfnetlink_rcv_batch` will be crucial. I'll check its definition to understand the role of `nfnetlink_rcv` and what kind of context it provides. Does it handle socket management, queueing, or just basic message reception? The answers will quickly frame `nfnetlink_rcv_batch`'s purpose. I'll be looking for shared data structures, locks, and any buffering mechanisms that link the two.



32/2 2026/08/17 18:13 tool grepper
0m Args:
{
  "Expression": "^static void nfnetlink_rcv"
}

Results:
{
  "Output": "net/netfilter/nfnetlink.c=363=enum {\n--\nnet/netfilter/nfnetlink.c-368-\nnet/netfilter/nfnetlink.c:369:static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,\nnet/netfilter/nfnetlink.c-370-\t\t\t\tu16 subsys_id, u32 genid)\n--\nnet/netfilter/nfnetlink.c=608=static const struct nla_policy nfnl_batch_policy[NFNL_BATCH_MAX + 1] = {\n--\nnet/netfilter/nfnetlink.c-611-\nnet/netfilter/nfnetlink.c:612:static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh)\nnet/netfilter/nfnetlink.c-613-{\n--\nnet/netfilter/nfnetlink.c-649-\nnet/netfilter/nfnetlink.c:650:static void nfnetlink_rcv(struct sk_buff *skb)\nnet/netfilter/nfnetlink.c-651-{\n"
}

33/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 60012
							output: 20
							thoughts: 64
						

Thoughts:
**Diving into the Netfilter Architecture**

Okay, things feel a bit stagnant here, a little too static, which always makes me wary. Where does this whole `nfnetlink` thing actually begin? I need the entry point. Ah, right, `nfnetlink_rcv`. That's registered as the netlink receive function, so that's where the action *should* start. Now, I'm trying to recall if there's any crucial bit of logic in the `nf_tables` side that *isn't* declared as `static`. Something that reveals the hook point for this data? Let me revisit `nf_tables_api.c` and make absolutely sure I haven't missed a non-static function definition. Time to double-check my assumptions and see if I missed any obvious pathways.



34/2 2026/08/17 18:13 tool grepper
0m Args:
{
  "Expression": "^int nf_tables_"
}

Results:
{
  "Output": "include/net/netfilter/nf_tables.h=678=void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,\n--\ninclude/net/netfilter/nf_tables.h-680-\t\t\t      enum nft_trans_phase phase);\ninclude/net/netfilter/nf_tables.h:681:int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,\ninclude/net/netfilter/nf_tables.h-682-\t\t       struct nft_set_binding *binding);\n--\ninclude/net/netfilter/nf_tables.h=1162=int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);\ninclude/net/netfilter/nf_tables.h:1163:int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);\ninclude/net/netfilter/nf_tables.h-1164-void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);\n--\ninclude/net/netfilter/nf_tables_core.h=27=extern struct nft_object_type nft_counter_obj_type;\ninclude/net/netfilter/nf_tables_core.h-28-\ninclude/net/netfilter/nf_tables_core.h:29:int nf_tables_core_module_init(void);\ninclude/net/netfilter/nf_tables_core.h-30-void nf_tables_core_module_exit(void);\n--\nnet/netfilter/nf_tables_api.c=302=static void nft_chain_trans_bind(const struct nft_ctx *ctx,\n--\nnet/netfilter/nf_tables_api.c-307-\nnet/netfilter/nf_tables_api.c:308:int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)\nnet/netfilter/nf_tables_api.c-309-{\n--\nnet/netfilter/nf_tables_api.c=5873=static int nft_set_catchall_bind_check(const struct nft_ctx *ctx,\n--\nnet/netfilter/nf_tables_api.c-5894-\nnet/netfilter/nf_tables_api.c:5895:int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,\nnet/netfilter/nf_tables_api.c-5896-\t\t       struct nft_set_binding *binding)\n"
}

35/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 60932
							output: 19
							thoughts: 301
						

Thoughts:
**My Analysis of Kernel Entry Points**

Okay, so I'm diving into this kernel code, trying to figure out the right entry points for what I'm doing. I see that `nf_tables_bind_set` and `nf_tables_bind_chain` are non-static, but they don't seem like the *main* ones I'm looking for. I also spot `nft_trans_alloc`, which is static, and that makes me pause. The prompt *prefers* non-static, but if there's nothing suitable, I can always omit or use the static ones.

Actually, scratch that, looking more closely, `bpf_check` and `generic_map_update_batch` look really promising for BPF-related stuff; I think those will work nicely. And for DRM, `drm_gpuvm_sm_map` and `drm_gpuvm_sm_unmap` seem like the correct functions to look into.

For the core allocator, I've got a good list going with: `folio_scratchpad_alloc`, `folio_scratchpad_reset`, `folio_scratchpad_free`, `folio_pool_init_align`, `folio_pool_init_key`, `folio_pool_init`, and `folio_scratchpad_stats`. That gives me a good spread.

Now, let me just double-check the required configurations to make sure I'm not missing anything obvious: `DRM_GPUVM`, `BPF_SYSCALL`, `PROVE_LOCKING`, and `NF_TABLES`. Ah, hang on... `DRM_GPUVM` is a hidden config in `drivers/gpu/drm/Kconfig`? Okay, I should check that out, just to be sure.



36/2 2026/08/17 18:13 tool grepper
0m Args:
{
  "Expression": "config DRM_GPUVM"
}

Results:
{
  "Output": "drivers/gpu/drm/Kconfig=207=config DRM_EXEC\n--\ndrivers/gpu/drm/Kconfig-212-\ndrivers/gpu/drm/Kconfig:213:config DRM_GPUVM\ndrivers/gpu/drm/Kconfig-214-\ttristate\n"
}

37/2 2026/08/17 18:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 61719
							output: 19
							thoughts: 38
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own:

**Investigating the `DRM_GPUVM` Configuration Option**

Right, `DRM_GPUVM` is one of those sneaky ones, a hidden config. No direct prompt to set it – it's tristate and gets quietly picked up by other drivers. Now, the real question is, who *is* selecting it? Time to dig in and figure out the dependencies. Let's see what the code's got to say about what's enabling this thing. I've got a feeling this will lead me down a rabbit hole of driver interactions, but it's important to understand the bigger picture.



38/2 2026/08/17 18:14 tool grepper
0m Args:
{
  "Expression": "select DRM_GPUVM"
}

Results:
{
  "Output": "drivers/gpu/drm/Kconfig=221=config RUST_DRM_GPUVM\n--\ndrivers/gpu/drm/Kconfig-223-\tdepends on DRM\ndrivers/gpu/drm/Kconfig:224:\tselect DRM_GPUVM\ndrivers/gpu/drm/Kconfig-225-\thelp\n--\ndrivers/gpu/drm/imagination/Kconfig=4=config DRM_POWERVR\n--\ndrivers/gpu/drm/imagination/Kconfig-13-\tselect DRM_SCHED\ndrivers/gpu/drm/imagination/Kconfig:14:\tselect DRM_GPUVM\ndrivers/gpu/drm/imagination/Kconfig-15-\tselect FW_LOADER\n--\ndrivers/gpu/drm/msm/Kconfig=3=config DRM_MSM\n--\ndrivers/gpu/drm/msm/Kconfig-18-\tselect DRM_EXEC\ndrivers/gpu/drm/msm/Kconfig:19:\tselect DRM_GPUVM\ndrivers/gpu/drm/msm/Kconfig-20-\tselect DRM_SCHED\n--\ndrivers/gpu/drm/nouveau/Kconfig=2=config DRM_NOUVEAU\n--\ndrivers/gpu/drm/nouveau/Kconfig-15-\tselect DRM_EXEC\ndrivers/gpu/drm/nouveau/Kconfig:16:\tselect DRM_GPUVM\ndrivers/gpu/drm/nouveau/Kconfig-17-\tselect DRM_SCHED\n--\ndrivers/gpu/drm/panthor/Kconfig=3=config DRM_PANTHOR\n--\ndrivers/gpu/drm/panthor/Kconfig-10-\tselect DRM_EXEC\ndrivers/gpu/drm/panthor/Kconfig:11:\tselect DRM_GPUVM\ndrivers/gpu/drm/panthor/Kconfig-12-\tselect DRM_SCHED\n--\ndrivers/gpu/drm/xe/Kconfig=2=config DRM_XE\n--\ndrivers/gpu/drm/xe/Kconfig-42-\tselect DRM_GPUSVM if !UML\ndrivers/gpu/drm/xe/Kconfig:43:\tselect DRM_GPUVM\ndrivers/gpu/drm/xe/Kconfig-44-\tselect DRM_SCHED\n"
}

39/2 2026/08/17 18:14 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 62464
							output: 19
							thoughts: 924
						

Thoughts:
**Fuzzing Configuration for Core Kernel Subsystems**

Okay, so we need to enable `DRM_GPUVM` for fuzzing. Looking at the options, `DRM_NOUVEAU`, `DRM_MSM`, `DRM_PANTHOR`, and `DRM_XE` are the likely candidates. I'll just include `DRM_NOUVEAU` and `DRM_MSM` in the configs initially. Wait, `DRM_GPUVM` is selected when `DRM_NOUVEAU` is enabled, so that simplifies things. Listing `DRM_NOUVEAU` should take care of that. Although, we *could* list `DRM_GPUVM` directly; if the fuzzer is capable, it might enable it, but since it has no prompt it cannot be enabled directly by `make defconfig`. The fuzzer tools usually know how to handle this, or we should provide the user-visible config like `DRM_NOUVEAU`. So yeah, let's go with both `DRM_NOUVEAU` and `DRM_MSM`.

Now, considering user-visible configs, we have `BPF_SYSCALL`, `PROVE_LOCKING`, and `NF_TABLES`. However, hold on, `PROVE_LOCKING` selects `LOCKDEP`, and `lockdep.c` is compiled when `CONFIG_LOCKDEP` is enabled, making `LOCKDEP` the base config we should look at. So, `LOCKDEP` is the key.

Let's quickly review the code changes. The `lockdep.c` changes, including the `setup_lockdep_folio_pool` and the use of `lockdep_pool` to allocate `struct lock_list` entries, appear fundamental to the lockdep core. That's worth fuzzing.

Moving on, `kernel/bpf/syscall.c` utilizes `folio_scratchpad_alloc` in `generic_map_update_batch`. In `kernel/bpf/verifier.c`, `folio_pool_alloc_obj` is used in `push_stack` and `push_async_cb`. Also, `net/netfilter/nf_tables_api.c` uses `folio_scratchpad_alloc_bytes` in `nft_trans_alloc`. Finally, `drivers/gpu/drm/drm_gpuvm.c` employs `folio_scratchpad_alloc_obj` in `gpuva_op_alloc`.

These are significant functional changes across core kernel subsystems, making them high-priority targets for fuzzing.

My initial list of `FocusSymbols` will be:
*   `folio_scratchpad_alloc`
*   `folio_scratchpad_reset`
*   `folio_scratchpad_free`
*   `folio_pool_init_align`
*   `folio_pool_init_key`
*   `folio_pool_init`
*   `folio_scratchpad_stats`
*   `bpf_check`
*   `generic_map_update_batch`
*   `drm_gpuvm_sm_map`
*   `drm_gpuvm_sm_unmap`
*   `drm_gpuvm_init`

Wait, `folio_pool_alloc` is a static inline function, so it's not a symbol. Let me double-check `lib/folio_pool.c` for any other functions that are *not* inlined, and confirm that the others are exported. Okay, it looks like `folio_scratchpad_alloc`, `folio_scratchpad_reset`, `folio_scratchpad_free`, `folio_pool_init_align`, `folio_pool_init_key`, `folio_pool_init`, and `folio_scratchpad_stats` are all exported, so those are good.



40/2 2026/08/17 18:14 tool grepper
0m Args:
{
  "Expression": "^void .*folio_"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 990 lines.\nUse more precise expression if possible.\n\narch/mips/include/asm/cacheflush.h=52=extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn);\narch/mips/include/asm/cacheflush.h:53:void __flush_dcache_folio_pages(struct folio *folio, struct page *page, unsigned int nr);\narch/mips/include/asm/cacheflush.h-54-\n--\narch/mips/mm/cache.c=89=SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,\n--\narch/mips/mm/cache.c-101-\narch/mips/mm/cache.c:102:void __flush_dcache_folio_pages(struct folio *folio, struct page *page,\narch/mips/mm/cache.c-103-\t\tunsigned int nr)\n--\narch/sparc/include/asm/cacheflush_32.h=38=void sparc_flush_page_to_ram(struct page *page);\narch/sparc/include/asm/cacheflush_32.h:39:void sparc_flush_folio_to_ram(struct folio *folio);\narch/sparc/include/asm/cacheflush_32.h-40-\n--\narch/sparc/include/asm/cacheflush_64.h=37=void __flush_dcache_page(void *addr, int flush_icache);\narch/sparc/include/asm/cacheflush_64.h:38:void flush_dcache_folio_impl(struct folio *folio);\narch/sparc/include/asm/cacheflush_64.h-39-#ifdef CONFIG_SMP\narch/sparc/include/asm/cacheflush_64.h:40:void smp_flush_dcache_folio_impl(struct folio *folio, int cpu);\narch/sparc/include/asm/cacheflush_64.h:41:void flush_dcache_folio_all(struct mm_struct *mm, struct folio *folio);\narch/sparc/include/asm/cacheflush_64.h-42-#else\n--\narch/sparc/kernel/smp_64.c=921=static inline void __local_flush_dcache_folio(struct folio *folio)\n--\narch/sparc/kernel/smp_64.c-939-\narch/sparc/kernel/smp_64.c:940:void smp_flush_dcache_folio_impl(struct folio *folio, int cpu)\narch/sparc/kernel/smp_64.c-941-{\n--\narch/sparc/kernel/smp_64.c-984-\narch/sparc/kernel/smp_64.c:985:void flush_dcache_folio_all(struct mm_struct *mm, struct folio *folio)\narch/sparc/kernel/smp_64.c-986-{\n--\narch/sparc/mm/init_32.c=269=EXPORT_SYMBOL(sparc_flush_page_to_ram);\narch/sparc/mm/init_32.c-270-\narch/sparc/mm/init_32.c:271:void sparc_flush_folio_to_ram(struct folio *folio)\narch/sparc/mm/init_32.c-272-{\n--\nblock/bio.c=1074=EXPORT_SYMBOL(bio_add_page);\nblock/bio.c-1075-\nblock/bio.c:1076:void bio_add_folio_nofail(struct bio *bio, struct folio *folio, size_t len,\nblock/bio.c-1077-\t\t\t  size_t off)\n--\nfs/btrfs/extent_io.c=931=int set_folio_extent_mapped(struct folio *folio)\n--\nfs/btrfs/extent_io.c-948-\nfs/btrfs/extent_io.c:949:void clear_folio_extent_mapped(struct folio *folio)\nfs/btrfs/extent_io.c-950-{\n--\nfs/btrfs/extent_io.h=265=int set_folio_extent_mapped(struct folio *folio);\nfs/btrfs/extent_io.h:266:void clear_folio_extent_mapped(struct folio *folio);\nfs/btrfs/extent_io.h-267-\n--\nfs/btrfs/subpage.c=38=int btrfs_attach_folio_state(const struct btrfs_fs_info *fs_info,\n--\nfs/btrfs/subpage.c-69-\nfs/btrfs/subpage.c:70:void btrfs_detach_folio_state(const struct btrfs_fs_info *fs_info, struct folio *folio,\nfs/btrfs/subpage.c-71-\t\t\t      enum btrfs_folio_type type)\n--\nfs/btrfs/subpage.c=88=struct btrfs_folio_state *btrfs_alloc_folio_state(const struct btrfs_fs_info *fs_info,\n--\nfs/btrfs/subpage.c-119- */\nfs/btrfs/subpage.c:120:void btrfs_folio_inc_eb_refs(const struct btrfs_fs_info *fs_info, struct folio *folio)\nfs/btrfs/subpage.c-121-{\n--\nfs/btrfs/subpage.c-133-\nfs/btrfs/subpage.c:134:void btrfs_folio_dec_eb_refs(const struct btrfs_fs_info *fs_info, struct folio *folio)\nfs/btrfs/subpage.c-135-{\n--\nfs/btrfs/subpage.c=195=static bool btrfs_subpage_end_and_test_lock(const struct btrfs_fs_info *fs_info,\n--\nfs/btrfs/subpage.c-240- */\nfs/btrfs/subpage.c:241:void btrfs_folio_end_lock(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.c-242-\t\t\t  struct folio *folio, u64 start, u32 len)\n--\nfs/btrfs/subpage.c-270-\nfs/btrfs/subpage.c:271:void btrfs_folio_end_lock_bitmap(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.c-272-\t\t\t\t struct folio *folio, unsigned long *bitmap)\n--\nfs/btrfs/subpage.c=524=static void btrfs_subpage_set_fixup_dirty(const struct btrfs_fs_info *fs_info,\n--\nfs/btrfs/subpage.c-556- */\nfs/btrfs/subpage.c:557:void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.c-558-\t\t\t\t struct folio *folio, u64 start, u32 len)\n--\nfs/btrfs/subpage.c=580=static bool btrfs_subpage_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,\n--\nfs/btrfs/subpage.c-619- */\nfs/btrfs/subpage.c:620:void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.c-621-\t\t\t\t   struct folio *folio, u64 start, u32 len)\n--\nfs/btrfs/subpage.c=647=bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info,\n--\nfs/btrfs/subpage.c-654-\nfs/btrfs/subpage.c:655:void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.c-656-\t\t\t     struct folio *folio, u64 start, u32 len)\n--\nfs/btrfs/subpage.c=688=IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(fixup);\n--\nfs/btrfs/subpage.c-696-\t\t\t\t folio_clear_func, folio_test_func)\t\\\nfs/btrfs/subpage.c:697:void btrfs_folio_set_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.c-698-\t\t\t    struct folio *folio, u64 start, u32 len)\t\\\n--\nfs/btrfs/subpage.c-706-}\t\t\t\t\t\t\t\t\t\\\nfs/btrfs/subpage.c:707:void btrfs_folio_clear_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.c-708-\t\t\t      struct folio *folio, u64 start, u32 len)\t\\\n--\nfs/btrfs/subpage.c=717=bool btrfs_folio_test_##name(const struct btrfs_fs_info *fs_info,\t\\\n--\nfs/btrfs/subpage.c-724-}\t\t\t\t\t\t\t\t\t\\\nfs/btrfs/subpage.c:725:void btrfs_folio_clamp_set_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.c-726-\t\t\t\t  struct folio *folio, u64 start, u32 len) \\\n--\nfs/btrfs/subpage.c-735-}\t\t\t\t\t\t\t\t\t\\\nfs/btrfs/subpage.c:736:void btrfs_folio_clamp_clear_##name(const struct btrfs_fs_info *fs_info, \\\nfs/btrfs/subpage.c-737-\t\t\t\t    struct folio *folio, u64 start, u32 len) \\\n--\nfs/btrfs/subpage.c=747=bool btrfs_folio_clamp_test_##name(const struct btrfs_fs_info *fs_info,\t\\\n--\nfs/btrfs/subpage.c-755-}\t\t\t\t\t\t\t\t\t\\\nfs/btrfs/subpage.c:756:void btrfs_meta_folio_set_##name(struct folio *folio, const struct extent_buffer *eb) \\\nfs/btrfs/subpage.c-757-{\t\t\t\t\t\t\t\t\t\\\n--\nfs/btrfs/subpage.c-763-}\t\t\t\t\t\t\t\t\t\\\nfs/btrfs/subpage.c:764:void btrfs_meta_folio_clear_##name(struct folio *folio, const struct extent_buffer *eb) \\\nfs/btrfs/subpage.c-765-{\t\t\t\t\t\t\t\t\t\\\n--\nfs/btrfs/subpage.c=815=DEFINE_GET_SUBPAGE_BITMAP(writeback);\n--\nfs/btrfs/subpage.c-838- */\nfs/btrfs/subpage.c:839:void btrfs_folio_assert_not_dirty(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.c-840-\t\t\t\t  struct folio *folio, u64 start, u32 len)\n--\nfs/btrfs/subpage.c-874- */\nfs/btrfs/subpage.c:875:void btrfs_folio_set_lock(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.c-876-\t\t\t  struct folio *folio, u64 start, u32 len)\n--\nfs/btrfs/subpage.h=106=int btrfs_attach_folio_state(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-107-\t\t\t     struct folio *folio, enum btrfs_folio_type type);\nfs/btrfs/subpage.h:108:void btrfs_detach_folio_state(const struct btrfs_fs_info *fs_info, struct folio *folio,\nfs/btrfs/subpage.h-109-\t\t\t      enum btrfs_folio_type type);\n--\nfs/btrfs/subpage.h=114=static inline void btrfs_free_folio_state(struct btrfs_folio_state *bfs)\n--\nfs/btrfs/subpage.h-118-\nfs/btrfs/subpage.h:119:void btrfs_folio_inc_eb_refs(const struct btrfs_fs_info *fs_info, struct folio *folio);\nfs/btrfs/subpage.h:120:void btrfs_folio_dec_eb_refs(const struct btrfs_fs_info *fs_info, struct folio *folio);\nfs/btrfs/subpage.h-121-\nfs/btrfs/subpage.h:122:void btrfs_folio_end_lock(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-123-\t\t\t  struct folio *folio, u64 start, u32 len);\nfs/btrfs/subpage.h:124:void btrfs_folio_set_lock(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-125-\t\t\t  struct folio *folio, u64 start, u32 len);\nfs/btrfs/subpage.h:126:void btrfs_folio_end_lock_bitmap(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-127-\t\t\t\t struct folio *folio, unsigned long *bitmap);\n--\nfs/btrfs/subpage.h=154=bool btrfs_subpage_test_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.h-155-\t\tstruct folio *folio, u64 start, u32 len);\t\t\t\\\nfs/btrfs/subpage.h:156:void btrfs_folio_set_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.h-157-\t\tstruct folio *folio, u64 start, u32 len);\t\t\t\\\nfs/btrfs/subpage.h:158:void btrfs_folio_clear_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.h-159-\t\tstruct folio *folio, u64 start, u32 len);\t\t\t\\\nfs/btrfs/subpage.h=160=bool btrfs_folio_test_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.h-161-\t\tstruct folio *folio, u64 start, u32 len);\t\t\t\\\nfs/btrfs/subpage.h:162:void btrfs_folio_clamp_set_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.h-163-\t\tstruct folio *folio, u64 start, u32 len);\t\t\t\\\nfs/btrfs/subpage.h:164:void btrfs_folio_clamp_clear_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.h-165-\t\tstruct folio *folio, u64 start, u32 len);\t\t\t\\\nfs/btrfs/subpage.h=166=bool btrfs_folio_clamp_test_##name(const struct btrfs_fs_info *fs_info,\t\\\nfs/btrfs/subpage.h-167-\t\tstruct folio *folio, u64 start, u32 len);\t\t\\\nfs/btrfs/subpage.h:168:void btrfs_meta_folio_set_##name(struct folio *folio, const struct extent_buffer *eb); \\\nfs/btrfs/subpage.h:169:void btrfs_meta_folio_clear_##name(struct folio *folio, const struct extent_buffer *eb); \\\nfs/btrfs/subpage.h-170-bool btrfs_meta_folio_test_##name(struct folio *folio, const struct extent_buffer *eb);\n--\nfs/btrfs/subpage.h=188=bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-189-\t\t\t    struct folio *folio, u64 start, u32 len);\nfs/btrfs/subpage.h:190:void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-191-\t\t\t\t struct folio *folio, u64 start, u32 len);\nfs/btrfs/subpage.h-192-/* For a block that just got its space reserved; it stays dirty. */\nfs/btrfs/subpage.h:193:void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-194-\t\t\t     struct folio *folio, u64 start, u32 len);\nfs/btrfs/subpage.h-195-/* For callers discarding the data; clears the dirty bits too. */\nfs/btrfs/subpage.h:196:void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-197-\t\t\t\t   struct folio *folio, u64 start, u32 len);\n--\nfs/btrfs/subpage.h=212=bool btrfs_subpage_clear_and_test_dirty(const struct btrfs_fs_info *fs_info,\n--\nfs/btrfs/subpage.h-214-\nfs/btrfs/subpage.h:215:void btrfs_folio_assert_not_dirty(const struct btrfs_fs_info *fs_info,\nfs/btrfs/subpage.h-216-\t\t\t\t  struct folio *folio, u64 start, u32 len);\n--\nfs/buffer.c=1546=void invalidate_bh_lrus_cpu(void)\n--\nfs/buffer.c-1555-\nfs/buffer.c:1556:void folio_set_bh(struct buffer_head *bh, struct folio *folio,\nfs/buffer.c-1557-\t\t  unsigned long offset)\n--\nfs/buffer.c=1975=EXPORT_SYMBOL(__block_write_full_folio);\n--\nfs/buffer.c-1981- */\nfs/buffer.c:1982:void folio_zero_new_buffers(struct folio *folio, size_t from, size_t to)\nfs/buffer.c-1983-{\n--\nfs/ceph/addr.c=1035=static inline\nfs/ceph/addr.c:1036:void ceph_folio_batch_init(struct ceph_writeback_ctl *ceph_wbc)\nfs/ceph/addr.c-1037-{\n--\nfs/ceph/addr.c=1042=static inline\nfs/ceph/addr.c:1043:void ceph_folio_batch_reinit(struct ceph_writeback_ctl *ceph_wbc)\nfs/ceph/addr.c-1044-{\n--\nfs/ceph/addr.c=1307=static\nfs/ceph/addr.c:1308:void ceph_process_folio_batch(struct address_space *mapping,\nfs/ceph/addr.c-1309-\t\t\t      struct writeback_control *wbc,\n--\nfs/ceph/addr.c=1409=static inline\nfs/ceph/addr.c:1410:void ceph_shift_unused_folios_left(struct folio_batch *fbatch)\nfs/ceph/addr.c-1411-{\n--\nfs/erofs/data.c=209=int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)\n--\nfs/erofs/data.c-258-\nfs/erofs/data.c:259:void erofs_onlinefolio_init(struct folio *folio)\nfs/erofs/data.c-260-{\n--\nfs/erofs/data.c-268-\nfs/erofs/data.c:269:void erofs_onlinefolio_split(struct folio *folio)\nfs/erofs/data.c-270-{\n--\nfs/erofs/data.c-273-\nfs/erofs/data.c:274:void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty)\nfs/erofs/data.c-275-{\n--\nfs/erofs/internal.h=414=int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map);\nfs/erofs/internal.h:415:void erofs_onlinefolio_init(struct folio *folio);\nfs/erofs/internal.h:416:void erofs_onlinefolio_split(struct folio *folio);\nfs/erofs/internal.h:417:void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty);\nfs/erofs/internal.h-418-struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);\n--\nfs/f2fs/compress.c=1845=void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,\n--\nfs/f2fs/compress.c-1887- */\nfs/f2fs/compress.c:1888:void f2fs_put_folio_dic(struct folio *folio, bool in_task)\nfs/f2fs/compress.c-1889-{\n--\nfs/f2fs/f2fs.h=4059=void f2fs_update_device_state(struct f2fs_sb_info *sbi, nid_t ino,\nfs/f2fs/f2fs.h-4060-\t\t\t\t\tblock_t blkaddr, unsigned int blkcnt);\nfs/f2fs/f2fs.h:4061:void f2fs_folio_wait_writeback(struct folio *folio, enum page_type type,\nfs/f2fs/f2fs.h-4062-\t\tbool ordered, bool locked);\n--\nfs/f2fs/f2fs.h=4687=void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,\nfs/f2fs/f2fs.h-4688-\t\t\t\tbool in_task);\nfs/f2fs/f2fs.h:4689:void f2fs_put_folio_dic(struct folio *folio, bool in_task);\nfs/f2fs/f2fs.h-4690-unsigned int f2fs_cluster_blocks_are_contiguous(struct dnode_of_data *dn,\n--\nfs/f2fs/iostat.c=179=static inline void __f2fs_update_iostat(struct f2fs_sb_info *sbi,\n--\nfs/f2fs/iostat.c-185-\nfs/f2fs/iostat.c:186:void f2fs_update_read_folio_count(struct f2fs_sb_info *sbi, struct folio *folio)\nfs/f2fs/iostat.c-187-{\n--\nfs/f2fs/segment.c=4222=void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,\n--\nfs/f2fs/segment.c-4236-\nfs/f2fs/segment.c:4237:void f2fs_folio_wait_writeback(struct folio *folio, enum page_type type,\nfs/f2fs/segment.c-4238-\t\tbool ordered, bool locked)\n--\nfs/iomap/buffered-io.c=412=static int iomap_read_inline_data(const struct iomap_iter *iter,\n--\nfs/iomap/buffered-io.c-437-\nfs/iomap/buffered-io.c:438:void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,\nfs/iomap/buffered-io.c-439-\t\tint error)\n--\nfs/iomap/buffered-io.c=1852=static void iomap_writeback_init(struct inode *inode, struct folio *folio)\n--\nfs/iomap/buffered-io.c-1871-\nfs/iomap/buffered-io.c:1872:void iomap_finish_folio_write(struct inode *inode, struct folio *folio,\nfs/iomap/buffered-io.c-1873-\t\tsize_t len)\n--\nfs/netfs/misc.c=69=EXPORT_SYMBOL(netfs_alloc_folioq_buffer);\n--\nfs/netfs/misc.c-77- */\nfs/netfs/misc.c:78:void netfs_free_folioq_buffer(struct folio_queue *fq)\nfs/netfs/misc.c-79-{\n--\nfs/netfs/rolling_buffer.c=43=EXPORT_SYMBOL(netfs_folioq_alloc);\n--\nfs/netfs/rolling_buffer.c-51- */\nfs/netfs/rolling_buffer.c:52:void netfs_folioq_free(struct folio_queue *folioq,\nfs/netfs/rolling_buffer.c-53-\t\t       unsigned int /*enum netfs_trace_folioq*/ trace)\n--\nfs/nilfs2/page.c=140=bool nilfs_folio_buffers_clean(struct folio *folio)\n--\nfs/nilfs2/page.c-152-\nfs/nilfs2/page.c:153:void nilfs_folio_bug(struct folio *folio)\nfs/nilfs2/page.c-154-{\n--\nfs/nilfs2/page.c=361=void nilfs_clear_dirty_pages(struct address_space *mapping)\n--\nfs/nilfs2/page.c-399- */\nfs/nilfs2/page.c:400:void nilfs_clear_folio_dirty(struct folio *folio)\nfs/nilfs2/page.c-401-{\n--\nfs/nilfs2/page.c=447=unsigned int nilfs_page_count_clean_buffers(struct folio *folio,\n--\nfs/nilfs2/page.c-473- */\nfs/nilfs2/page.c:474:void __nilfs_clear_folio_dirty(struct folio *folio)\nfs/nilfs2/page.c-475-{\n--\nfs/nilfs2/page.h=30=BUFFER_FNS(NILFS_Redirected, nilfs_redirected)\t/* redirected to a copy */\n--\nfs/nilfs2/page.h-32-\nfs/nilfs2/page.h:33:void __nilfs_clear_folio_dirty(struct folio *);\nfs/nilfs2/page.h-34-\n--\nfs/nilfs2/page.h=39=bool nilfs_folio_buffers_clean(struct folio *);\nfs/nilfs2/page.h:40:void nilfs_folio_bug(struct folio *);\nfs/nilfs2/page.h-41-\n--\nfs/nilfs2/page.h=43=void nilfs_copy_back_pages(struct address_space *, struct address_space *);\nfs/nilfs2/page.h:44:void nilfs_clear_folio_dirty(struct folio *folio);\nfs/nilfs2/page.h-45-void nilfs_clear_dirty_pages(struct address_space *mapping);\n--\ninclude/linux/bio.h=451=void __bio_add_page(struct bio *bio, struct page *page,\ninclude/linux/bio.h-452-\t\tunsigned int len, unsigned int off);\ninclude/linux/bio.h:453:void bio_add_folio_nofail(struct bio *bio, struct folio *folio, size_t len,\ninclude/linux/bio.h-454-\t\t\t  size_t off);\n--\ninclude/linux/buffer_head.h=195=void touch_buffer(struct buffer_head *bh);\ninclude/linux/buffer_head.h:196:void folio_set_bh(struct buffer_head *bh, struct folio *folio,\ninclude/linux/buffer_head.h-197-\t\t  unsigned long offset);\n--\ninclude/linux/buffer_head.h=268=int generic_write_end(const struct kiocb *, struct address_space *,\n--\ninclude/linux/buffer_head.h-270-\t\t\t\tstruct folio *, void *);\ninclude/linux/buffer_head.h:271:void folio_zero_new_buffers(struct folio *folio, size_t from, size_t to);\ninclude/linux/buffer_head.h-272-int cont_write_begin(const struct kiocb *, struct address_space *, loff_t,\n--\ninclude/linux/folio_batch.h=89=static inline struct folio *folio_batch_next(struct folio_batch *fbatch)\n--\ninclude/linux/folio_batch.h-95-\ninclude/linux/folio_batch.h:96:void __folio_batch_release(struct folio_batch *fbatch);\ninclude/linux/folio_batch.h-97-\ninclude/linux/folio_batch.h=98=static inline void folio_batch_release(struct folio_batch *fbatch)\n--\ninclude/linux/folio_batch.h-103-\ninclude/linux/folio_batch.h:104:void folio_batch_remove_exceptionals(struct folio_batch *fbatch);\ninclude/linux/folio_batch.h-105-#endif /* _LINUX_FOLIO_BATCH_H */\n--\ninclude/linux/folio_pool.h=79=struct folio_scratchpad {\n--\ninclude/linux/folio_pool.h-101-\ninclude/linux/folio_pool.h:102:void folio_scratchpad_init(struct folio_scratchpad *sp, unsigned int order);\ninclude/linux/folio_pool.h:103:void folio_scratchpad_init_key(struct folio_scratchpad *sp, unsigned int order,\ninclude/linux/folio_pool.h-104-\t\t\t       struct static_key *key);\ninclude/linux/folio_pool.h:105:void *folio_scratchpad_alloc(struct folio_scratchpad *sp, size_t size,\ninclude/linux/folio_pool.h-106-\t\t\t     size_t align, gfp_t gfp);\ninclude/linux/folio_pool.h:107:void folio_scratchpad_reset(struct folio_scratchpad *sp);\ninclude/linux/folio_pool.h:108:void folio_scratchpad_free(struct folio_scratchpad *sp);\ninclude/linux/folio_pool.h:109:void folio_scratchpad_stats(struct folio_scratchpad *sp, unsigned int *nr_chunks,\ninclude/linux/folio_pool.h-110-\t\t\t    size_t *chunk_size, size_t *tail_used);\n--\ninclude/linux/folio_pool.h=220=struct folio_pool {\n--\ninclude/linux/folio_pool.h-237-\ninclude/linux/folio_pool.h:238:void folio_pool_init(struct folio_pool *fp, size_t elem_size, unsigned int order);\ninclude/linux/folio_pool.h:239:void folio_pool_init_key(struct folio_pool *fp, size_t elem_size, unsigned int order,\ninclude/linux/folio_pool.h-240-\t\t\t struct static_key *key);\ninclude/linux/folio_pool.h:241:void folio_pool_init_align(struct folio_pool *fp, size_t elem_size,\ninclude/linux/folio_pool.h-242-\t\t\t   size_t elem_align, unsigned int order);\n--\ninclude/linux/huge_mm.h=575=bool unmap_huge_pmd_locked(struct vm_area_struct *vma, unsigned long addr,\ninclude/linux/huge_mm.h-576-\t\t\t   pmd_t *pmdp, struct folio *folio);\ninclude/linux/huge_mm.h:577:void map_anon_folio_pmd_nopf(struct folio *folio, pmd_t *pmd,\ninclude/linux/huge_mm.h-578-\t\tstruct vm_area_struct *vma, unsigned long haddr);\n--\ninclude/linux/hugetlb.h=155=int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison);\ninclude/linux/hugetlb.h:156:void folio_putback_hugetlb(struct folio *folio);\ninclude/linux/hugetlb.h-157-void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason);\n--\ninclude/linux/hugetlb.h=604=static __always_inline\t\t\t\t\t\t\\\ninclude/linux/hugetlb.h:605:void folio_set_hugetlb_##flname(struct folio *folio)\t\t\\\ninclude/linux/hugetlb.h-606-\t{\tvoid *private = \u0026folio-\u003eprivate;\t\t\\\n--\ninclude/linux/hugetlb.h=611=static __always_inline\t\t\t\t\t\t\\\ninclude/linux/hugetlb.h:612:void folio_clear_hugetlb_##flname(struct folio *folio)\t\t\\\ninclude/linux/hugetlb.h-613-\t{\tvoid *private = \u0026folio-\u003eprivate;\t\t\\\n--\ninclude/linux/iomap.h=494=int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error);\ninclude/linux/iomap.h-495-\ninclude/linux/iomap.h:496:void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,\ninclude/linux/iomap.h-497-\t\tint error);\ninclude/linux/iomap.h:498:void iomap_finish_folio_write(struct inode *inode, struct folio *folio,\ninclude/linux/iomap.h-499-\t\tsize_t len);\n--\ninclude/linux/ksm.h=97=void rmap_walk_ksm(struct folio *folio, struct rmap_walk_control *rwc);\ninclude/linux/ksm.h:98:void folio_migrate_ksm(struct folio *newfolio, struct folio *folio);\ninclude/linux/ksm.h-99-void collect_procs_ksm(const struct folio *folio, const struct page *page,\n--\ninclude/linux/memcontrol.h=673=static inline void mem_cgroup_uncharge(struct folio *folio)\n--\ninclude/linux/memcontrol.h-679-\ninclude/linux/memcontrol.h:680:void __mem_cgroup_uncharge_folios(struct folio_batch *folios);\ninclude/linux/memcontrol.h-681-static inline void mem_cgroup_uncharge_folios(struct folio_batch *folios)\n--\ninclude/linux/memcontrol.h=1020=void split_page_memcg(struct page *first, unsigned order);\ninclude/linux/memcontrol.h:1021:void folio_split_memcg_refs(struct folio *folio, unsigned old_order,\ninclude/linux/memcontrol.h-1022-\t\tunsigned new_order);\n--\ninclude/linux/migrate.h=68=void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)\ninclude/linux/migrate.h-69-\t\t__releases(ptl);\ninclude/linux/migrate.h:70:void folio_migrate_flags(struct folio *newfolio, struct folio *folio);\ninclude/linux/migrate.h-71-int folio_migrate_mapping(struct address_space *mapping,\n--\ninclude/linux/mm.h=1942=static inline struct folio *virt_to_folio(const void *x)\n--\ninclude/linux/mm.h-1948-\ninclude/linux/mm.h:1949:void __folio_put(struct folio *folio);\ninclude/linux/mm.h-1950-\ninclude/linux/mm.h=1951=void split_page(struct page *page, unsigned int order);\ninclude/linux/mm.h:1952:void folio_copy(struct folio *dst, struct folio *src);\ninclude/linux/mm.h-1953-int folio_mc_copy(struct folio *dst, struct folio *src);\n--\ninclude/linux/mm.h=2141=static inline void folio_put_refs(struct folio *folio, int refs)\n--\ninclude/linux/mm.h-2146-\ninclude/linux/mm.h:2147:void folios_put_refs(struct folio_batch *folios, unsigned int *refs);\ninclude/linux/mm.h-2148-\n--\ninclude/linux/mm.h=3283=int pin_user_pages_fast(unsigned long start, int nr_pages,\ninclude/linux/mm.h-3284-\t\t\tunsigned int gup_flags, struct page **pages);\ninclude/linux/mm.h:3285:void folio_add_pin(struct folio *folio);\ninclude/linux/mm.h-3286-\n--\ninclude/linux/mm.h=5057=enum mf_action_page_type {\n--\ninclude/linux/mm.h-5082-#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLBFS)\ninclude/linux/mm.h:5083:void folio_zero_user(struct folio *folio, unsigned long addr_hint);\ninclude/linux/mm.h-5084-int copy_user_large_folio(struct folio *dst, struct folio *src,\n--\ninclude/linux/mm.h=5270=void snapshot_page(struct page_snapshot *ps, const struct page *page);\ninclude/linux/mm.h-5271-\ninclude/linux/mm.h:5272:void map_anon_folio_pte_nopf(struct folio *folio, pte_t *pte,\ninclude/linux/mm.h-5273-\t\tstruct vm_area_struct *vma, unsigned long addr,\n--\ninclude/linux/mm_inline.h=362=static __always_inline\ninclude/linux/mm_inline.h:363:void lruvec_add_folio_tail(struct lruvec *lruvec, struct folio *folio)\ninclude/linux/mm_inline.h-364-{\n--\ninclude/linux/netfs.h=457=struct folio_queue *netfs_folioq_alloc(unsigned int rreq_id, gfp_t gfp,\ninclude/linux/netfs.h-458-\t\t\t\t       unsigned int trace /*enum netfs_folioq_trace*/);\ninclude/linux/netfs.h:459:void netfs_folioq_free(struct folio_queue *folioq,\ninclude/linux/netfs.h-460-\t\t       unsigned int trace /*enum netfs_trace_folioq*/);\n--\ninclude/linux/netfs.h=463=int netfs_alloc_folioq_buffer(struct address_space *mapping,\n--\ninclude/linux/netfs.h-465-\t\t\t      size_t *_cur_size, ssize_t size, gfp_t gfp);\ninclude/linux/netfs.h:466:void netfs_free_folioq_buffer(struct folio_queue *fq);\ninclude/linux/netfs.h-467-\n--\ninclude/linux/page-flags.h=833=CLEARPAGEFLAG(Uptodate, uptodate, PF_NO_TAIL)\ninclude/linux/page-flags.h-834-\ninclude/linux/page-flags.h:835:void __folio_start_writeback(struct folio *folio, bool keep_write);\ninclude/linux/page-flags.h-836-void set_page_writeback(struct page *page);\n--\ninclude/linux/pagemap.h=1087=static inline bool wake_page_match(struct wait_page_queue *wait_page,\n--\ninclude/linux/pagemap.h-1099-\ninclude/linux/pagemap.h:1100:void __folio_lock(struct folio *folio);\ninclude/linux/pagemap.h-1101-int __folio_lock_killable(struct folio *folio);\n--\ninclude/linux/pagemap.h=1103=void unlock_page(struct page *page);\ninclude/linux/pagemap.h:1104:void folio_unlock(struct folio *folio);\ninclude/linux/pagemap.h-1105-\n--\ninclude/linux/pagemap.h=1206=static inline vm_fault_t folio_lock_or_retry(struct folio *folio,\n--\ninclude/linux/pagemap.h-1218- */\ninclude/linux/pagemap.h:1219:void folio_wait_bit(struct folio *folio, int bit_nr);\ninclude/linux/pagemap.h-1220-int folio_wait_bit_killable(struct folio *folio, int bit_nr);\n--\ninclude/linux/pagemap.h=1235=static inline int folio_wait_locked_killable(struct folio *folio)\n--\ninclude/linux/pagemap.h-1241-\ninclude/linux/pagemap.h:1242:void folio_end_read(struct folio *folio, bool success);\ninclude/linux/pagemap.h-1243-void wait_on_page_writeback(struct page *page);\ninclude/linux/pagemap.h:1244:void folio_wait_writeback(struct folio *folio);\ninclude/linux/pagemap.h-1245-int folio_wait_writeback_killable(struct folio *folio);\ninclude/linux/pagemap.h=1246=void end_page_writeback(struct page *page);\ninclude/linux/pagemap.h:1247:void folio_end_writeback(struct folio *folio);\ninclude/linux/pagemap.h:1248:void folio_end_writeback_no_dropbehind(struct folio *folio);\ninclude/linux/pagemap.h:1249:void folio_end_dropbehind(struct folio *folio);\ninclude/linux/pagemap.h:1250:void folio_wait_stable(struct folio *folio);\ninclude/linux/pagemap.h:1251:void __folio_mark_dirty(struct folio *folio, struct address_space *, int warn);\ninclude/linux/pagemap.h:1252:void folio_account_cleaned(struct folio *folio, struct bdi_writeback *wb);\ninclude/linux/pagemap.h:1253:void __folio_cancel_dirty(struct folio *folio);\ninclude/linux/pagemap.h-1254-static inline void folio_cancel_dirty(struct folio *folio)\n--\ninclude/linux/pagemap.h=1261=bool clear_page_dirty_for_io(struct page *page);\ninclude/linux/pagemap.h:1262:void folio_invalidate(struct folio *folio, size_t offset, size_t length);\ninclude/linux/pagemap.h-1263-bool noop_dirty_folio(struct address_space *mapping, struct folio *folio);\n--\ninclude/linux/pagemap.h=1266=int filemap_migrate_folio(struct address_space *mapping, struct folio *dst,\n\n"
}

41/2 2026/08/17 18:14 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 75667
							output: 303
							thoughts: 880
						

Thoughts:
**My Analysis of Kernel Function Entry Points and Configuration for Fuzzing**

Alright, let's break this down. My goal here is to identify key kernel functions and relevant configurations to effectively fuzz a new folio bump allocator and its integration into various subsystems. I need to make a good, informed decision.

First, I've identified the "FocusSymbols" – the functions that are either newly introduced or heavily modified related to this folio allocator and its use. I've confirmed that `folio_scratchpad_alloc`, `folio_scratchpad_reset`, `folio_scratchpad_free`, `folio_pool_init_align`, `folio_pool_init_key`, `folio_pool_init`, and `folio_scratchpad_stats` are all exported. That's a solid start. These are the core folio functions.  Then, I've included the obvious subsystem entry points: `bpf_check`, `generic_map_update_batch`, `drm_gpuvm_sm_map`, `drm_gpuvm_sm_unmap`, and `drm_gpuvm_init`.

Now, I'm slightly hesitant about `nfnetlink_rcv_batch` as it's static, but it seems to be an entry point.  While I *prefer* non-static functions, the prompt indicates that it is okay to include a static entry point if it is not an internal helper.  And this seems to be a significant entry point, plus it is unlikely to be inlined since it is a large function. Therefore, I will include it. I'm omitting `nf_tables_bind_set` and `nf_tables_bind_chain`, but I may re-evaluate later.

Now for the configurations. I need to enable the relevant subsystems.  `DRM_GPUVM` is an important one, so I need to make sure that's set. Since it's a tristate, I'll use `DRM_NOUVEAU` to ensure it is selected. I also need to enable `BPF_SYSCALL`, `PROVE_LOCKING`, and `NF_TABLES`. I've confirmed these are the key configs for each subsystem.

So my "EnableConfigs" list will be: `DRM_NOUVEAU`, `BPF_SYSCALL`, `PROVE_LOCKING`, and `NF_TABLES`.

In essence, the changes introduced by this patch warrant fuzzing because they touch memory allocation and management in critical areas of the kernel that are reachable from user space through system calls, netlink messages, and DRM ioctls. Any vulnerabilities could lead to serious issues, such as memory corruption or leaks. My goal is to maximize the chance of detecting these issues by targeting the core functionalities and subsystems that are impacted. I think I've created a good first pass and should call the `set-results` tool and move on.



42/2 2026/08/17 18:14 tool set-results
0m Args:
{
  "EnableConfigs": [
    "DRM_NOUVEAU",
    "BPF_SYSCALL",
    "PROVE_LOCKING",
    "NF_TABLES"
  ],
  "FocusSymbols": [
    "folio_scratchpad_alloc",
    "folio_scratchpad_reset",
    "folio_scratchpad_free",
    "folio_pool_init_align",
    "folio_pool_init_key",
    "folio_pool_init",
    "folio_scratchpad_stats",
    "bpf_check",
    "generic_map_update_batch",
    "drm_gpuvm_sm_map",
    "drm_gpuvm_sm_unmap",
    "drm_gpuvm_init"
  ],
  "Reasoning": "The patch introduces a new direct-map large folio bump allocator (`folio_pool` and `folio_scratchpad`) and integrates it into several core kernel subsystems: DRM GPUVM, BPF syscalls/verifier, lockdep, and nf_tables. These changes modify how memory is allocated and managed in these subsystems, which has a significant functional impact and could introduce memory corruption, use-after-free, or memory leak vulnerabilities. The code is reachable from user space via BPF syscalls, netlink messages (nf_tables), and DRM ioctls. Therefore, it is highly worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "DRM_NOUVEAU",
    "BPF_SYSCALL",
    "PROVE_LOCKING",
    "NF_TABLES"
  ],
  "FocusSymbols": [
    "folio_scratchpad_alloc",
    "folio_scratchpad_reset",
    "folio_scratchpad_free",
    "folio_pool_init_align",
    "folio_pool_init_key",
    "folio_pool_init",
    "folio_scratchpad_stats",
    "bpf_check",
    "generic_map_update_batch",
    "drm_gpuvm_sm_map",
    "drm_gpuvm_sm_unmap",
    "drm_gpuvm_init"
  ],
  "Reasoning": "The patch introduces a new direct-map large folio bump allocator (`folio_pool` and `folio_scratchpad`) and integrates it into several core kernel subsystems: DRM GPUVM, BPF syscalls/verifier, lockdep, and nf_tables. These changes modify how memory is allocated and managed in these subsystems, which has a significant functional impact and could introduce memory corruption, use-after-free, or memory leak vulnerabilities. The code is reachable from user space via BPF syscalls, netlink messages (nf_tables), and DRM ioctls. Therefore, it is highly worth fuzzing.",
  "WorthFuzzing": true
}

43/1 2026/08/17 18:14 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

44/2 2026/08/17 18:14 agent kmsan-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch introduces a new memory allocator (`folio_pool` and `folio_scratchpad`) and replaces various `kzalloc`, `kmalloc`, `kvmalloc`, and `krealloc` calls with the new allocator's functions. The core allocation function `folio_scratchpad_alloc` explicitly zeroes the allocated memory using `memset(elem, 0, size)`. For reallocations, `folio_pool_realloc` behaves identically to `krealloc` without `__GFP_ZERO`, leaving newly expanded memory uninitialized just as the original code did. In some cases (like `generic_map_update_batch`), the patch actually replaces `kvmalloc` (which does not zero memory) with `folio_scratchpad_alloc` (which does), thereby reducing the risk of uninitialized memory usage. Any potential bugs introduced by this custom allocator (such as incorrect padding, remaining size calculations, or chunk management) would manifest as out-of-bounds accesses, memory corruption, or use-after-free bugs, which are effectively detected by KASAN. Since all newly allocated objects are fully zeroed and no new uninitialized memory risks or info-leaks are introduced, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
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 c1582d0063f2c118dce289575969335d8319a7e1
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 17 18:11:30 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c
index c422c5af1f4bf..6bb82ac9e2d6c 100644
--- a/drivers/gpu/drm/drm_gpuvm.c
+++ b/drivers/gpu/drm/drm_gpuvm.c
@@ -1109,6 +1109,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
 	spin_lock_init(&gpuvm->evict.lock);
 
 	init_llist_head(&gpuvm->bo_defer);
+	folio_scratchpad_init_key(&gpuvm->va_scratchpad, get_order(SZ_64K),
+				  &gpuvm_scratchpad_key);
 
 	kref_init(&gpuvm->kref);
 
@@ -1155,6 +1157,7 @@ drm_gpuvm_fini(struct drm_gpuvm *gpuvm)
 	drm_WARN(gpuvm->drm, !llist_empty(&gpuvm->bo_defer),
 		 "VM BO cleanup list should be empty.\n");
 
+	folio_scratchpad_free(&gpuvm->va_scratchpad);
 	drm_gem_object_put(gpuvm->r_obj);
 }
 
@@ -2849,6 +2852,9 @@ drm_gpuvm_sm_unmap_exec_lock(struct drm_gpuvm *gpuvm, struct drm_exec *exec,
 }
 EXPORT_SYMBOL_GPL(drm_gpuvm_sm_unmap_exec_lock);
 
+DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(gpuvm_scratchpad_key, va_scratchpad,
+				   "Toggle DRM GPUVM range folio scratchpad allocator");
+
 static struct drm_gpuva_op *
 gpuva_op_alloc(struct drm_gpuvm *gpuvm)
 {
@@ -2858,7 +2864,8 @@ gpuva_op_alloc(struct drm_gpuvm *gpuvm)
 	if (fn && fn->op_alloc)
 		op = fn->op_alloc();
 	else
-		op = kzalloc_obj(*op);
+		op = folio_scratchpad_alloc_obj(gpuvm, va_scratchpad,
+						struct drm_gpuva_op, GFP_KERNEL);
 
 	if (unlikely(!op))
 		return NULL;
@@ -2875,7 +2882,7 @@ gpuva_op_free(struct drm_gpuvm *gpuvm,
 	if (fn && fn->op_free)
 		fn->op_free(op);
 	else
-		kfree(op);
+		folio_scratchpad_free_elem(op);
 }
 
 static int
diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h
index 655bd9104ffb2..362b1d88d30d8 100644
--- a/include/drm/drm_gpuvm.h
+++ b/include/drm/drm_gpuvm.h
@@ -30,6 +30,7 @@
 #include <linux/llist.h>
 #include <linux/rbtree.h>
 #include <linux/types.h>
+#include <linux/folio_pool.h>
 
 #include <drm/drm_device.h>
 #include <drm/drm_gem.h>
@@ -338,6 +339,11 @@ struct drm_gpuvm {
 	 * @bo_defer: structure holding vm_bos that need to be destroyed
 	 */
 	struct llist_head bo_defer;
+
+	/**
+	 * @va_scratchpad: folio scratchpad for transient GPU VA range allocations
+	 */
+	struct folio_scratchpad va_scratchpad;
 };
 
 void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 39a851e690ec4..43f5f0eaffac6 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -898,6 +898,8 @@ struct bpf_scc_info {
 
 struct bpf_liveness;
 
+#include <linux/folio_pool.h>
+
 /* single container for all structs
  * one verifier_env per bpf_check() call
  */
@@ -907,6 +909,7 @@ struct bpf_verifier_env {
 	struct bpf_prog *prog;		/* eBPF program being verified */
 	const struct bpf_verifier_ops *ops;
 	struct module *attach_btf_mod;	/* The owner module of prog->aux->attach_btf */
+	struct folio_pool state_pool;	/* pool for transient state nodes */
 	struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */
 	int stack_size;			/* number of states to be processed */
 	bool strict_alignment;		/* perform strict pointer alignment checks */
diff --git a/include/linux/folio_pool.h b/include/linux/folio_pool.h
new file mode 100644
index 0000000000000..5bbca8818877c
--- /dev/null
+++ b/include/linux/folio_pool.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef _LINUX_FOLIO_POOL_H
+#define _LINUX_FOLIO_POOL_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/page-flags.h>
+#include <linux/mm.h>
+#include <linux/gfp.h>
+#include <linux/jump_label.h>
+#include <linux/moduleparam.h>
+#include <linux/cleanup.h>
+
+DECLARE_STATIC_KEY_TRUE(folio_pool_enabled_key);
+
+#define DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(key_name, param_name, desc)		\
+	DEFINE_STATIC_KEY_TRUE(key_name);					\
+	static int key_name##_set(const char *val, const struct kernel_param *kp) \
+	{									\
+		bool enable;							\
+		int ret = kstrtobool(val, &enable);				\
+		if (ret)							\
+			return ret;						\
+		if (enable)							\
+			static_branch_enable(&key_name);			\
+		else								\
+			static_branch_disable(&key_name);			\
+		return 0;							\
+	}									\
+	static int key_name##_get(char *buffer, const struct kernel_param *kp)	\
+	{									\
+		return sprintf(buffer, "%c\n",					\
+			       static_branch_likely(&key_name) ? 'Y' : 'N');	\
+	}									\
+	static const struct kernel_param_ops key_name##_ops = {			\
+		.set = key_name##_set,						\
+		.get = key_name##_get,						\
+	};									\
+	module_param_cb(param_name, &key_name##_ops, NULL, 0644);		\
+	MODULE_PARM_DESC(param_name, desc)
+
+#define DEFINE_FOLIO_POOL_STATIC_KEY_PARAM_FALSE(key_name, param_name, desc)	\
+	DEFINE_STATIC_KEY_FALSE(key_name);					\
+	static int key_name##_set(const char *val, const struct kernel_param *kp) \
+	{									\
+		bool enable;							\
+		int ret = kstrtobool(val, &enable);				\
+		if (ret)							\
+			return ret;						\
+		if (enable)							\
+			static_branch_enable(&key_name);			\
+		else								\
+			static_branch_disable(&key_name);			\
+		return 0;							\
+	}									\
+	static int key_name##_get(char *buffer, const struct kernel_param *kp)	\
+	{									\
+		return sprintf(buffer, "%c\n",					\
+			       static_branch_unlikely(&key_name) ? 'Y' : 'N');	\
+	}									\
+	static const struct kernel_param_ops key_name##_ops = {			\
+		.set = key_name##_set,						\
+		.get = key_name##_get,						\
+	};									\
+	module_param_cb(param_name, &key_name##_ops, NULL, 0644);		\
+	MODULE_PARM_DESC(param_name, desc)
+
+#define FOLIO_POOL_64K_ORDER (PAGE_SHIFT < 16 ? 16 - PAGE_SHIFT : 0)
+
+struct folio_pool_chunk {
+	struct list_head link;
+	struct folio *folio;
+};
+
+/*
+ * 1. Variable-Sized Scratchpad (Core Bump Allocator Engine)
+ */
+struct folio_scratchpad {
+	struct list_head chunks;
+	void *free_ptr;
+	size_t remaining;
+	unsigned int chunk_order;
+	struct static_key *key;
+	spinlock_t lock;
+};
+
+#define FOLIO_SCRATCHPAD_INIT(name, _order) {				\
+	.chunks = LIST_HEAD_INIT((name).chunks),			\
+	.chunk_order = (_order),					\
+	.key = NULL,							\
+	.lock = __SPIN_LOCK_UNLOCKED((name).lock),			\
+}
+
+#define FOLIO_SCRATCHPAD_INIT_KEY(name, _order, _key) {			\
+	.chunks = LIST_HEAD_INIT((name).chunks),			\
+	.chunk_order = (_order),					\
+	.key = (struct static_key *)(_key),				\
+	.lock = __SPIN_LOCK_UNLOCKED((name).lock),			\
+}
+
+void folio_scratchpad_init(struct folio_scratchpad *sp, unsigned int order);
+void folio_scratchpad_init_key(struct folio_scratchpad *sp, unsigned int order,
+			       struct static_key *key);
+void *folio_scratchpad_alloc(struct folio_scratchpad *sp, size_t size,
+			     size_t align, gfp_t gfp);
+void folio_scratchpad_reset(struct folio_scratchpad *sp);
+void folio_scratchpad_free(struct folio_scratchpad *sp);
+void folio_scratchpad_stats(struct folio_scratchpad *sp, unsigned int *nr_chunks,
+			    size_t *chunk_size, size_t *tail_used);
+
+DEFINE_FREE(folio_scratchpad, struct folio_scratchpad *, if (_T) folio_scratchpad_free(_T))
+
+/**
+ * is_folio_pool_ptr - Check whether an address resides in a folio pool/scratchpad
+ * @ptr: Object pointer to test
+ *
+ * Direct-map large folios allocated via folio_alloc() are not slab pages,
+ * unlike objects returned by kmalloc/kzalloc.
+ */
+static inline bool is_folio_pool_ptr(const void *ptr)
+{
+	return ptr && !is_vmalloc_addr(ptr) && !folio_test_slab(virt_to_folio(ptr));
+}
+
+/**
+ * folio_pool_free_elem - Safely release a pool object or SLUB fallback element
+ * @ptr: Object pointer to release
+ *
+ * If @ptr belongs to a direct-map large folio, individual deallocation is a safe
+ * no-op (reclaimed in bulk by folio_scratchpad_free/reset). If @ptr was allocated
+ * via SLUB/vmalloc fallback, releases it immediately.
+ */
+static inline void folio_pool_free_elem(const void *ptr)
+{
+	if (!ptr || is_folio_pool_ptr(ptr))
+		return;
+	kvfree(ptr);
+}
+
+static inline void folio_scratchpad_free_elem(const void *ptr)
+{
+	folio_pool_free_elem(ptr);
+}
+
+/**
+ * folio_pool_realloc - Reallocate memory for an object, handling pool vs slab backing
+ * @ptr: Existing object pointer (may be from folio_pool/scratchpad or SLUB)
+ * @old_size: Size of original object
+ * @new_size: Desired new size
+ * @gfp: Allocation flags
+ *
+ * If @ptr is SLUB-backed, delegates directly to krealloc(). If @ptr resides
+ * in a direct-map large folio, allocates a fresh @new_size buffer from SLUB
+ * and copies @old_size bytes; the original scratchpad slot remains abandoned
+ * until the entire scratchpad is released or reset at batch boundary.
+ */
+static inline void *folio_pool_realloc(void *ptr, size_t old_size,
+				       size_t new_size, gfp_t gfp)
+{
+	void *new_ptr;
+
+	if (!ptr)
+		return kmalloc(new_size, gfp);
+
+	if (!is_folio_pool_ptr(ptr))
+		return krealloc(ptr, new_size, gfp);
+
+	new_ptr = kmalloc(new_size, gfp);
+	if (new_ptr)
+		memcpy(new_ptr, ptr, min(old_size, new_size));
+	return new_ptr;
+}
+
+static inline void *folio_scratchpad_realloc(void *ptr, size_t old_size,
+					     size_t new_size, gfp_t gfp)
+{
+	return folio_pool_realloc(ptr, old_size, new_size, gfp);
+}
+
+/**
+ * folio_scratchpad_alloc_obj - Allocate a typed object from an embedded scratchpad
+ * @ptr: Pointer to container struct (e.g. nft_net)
+ * @member: Name of the struct folio_scratchpad field (e.g. trans_scratchpad)
+ * @type: Type of object being allocated
+ * @gfp: Allocation flags
+ */
+#define folio_scratchpad_alloc_obj(ptr, member, type, gfp)			\
+	((type *)folio_scratchpad_alloc(&(ptr)->member,				\
+					sizeof(type),				\
+					__alignof__(type),			\
+					gfp))
+
+/**
+ * folio_scratchpad_alloc_bytes - Allocate variable-sized bytes from an embedded scratchpad
+ * @ptr: Pointer to container struct (e.g. nft_net)
+ * @member: Name of the struct folio_scratchpad field (e.g. trans_scratchpad)
+ * @size: Size of memory to allocate
+ * @align: Alignment requirement
+ * @gfp: Allocation flags
+ */
+#define folio_scratchpad_alloc_bytes(ptr, member, size, align, gfp)		\
+	folio_scratchpad_alloc(&(ptr)->member, size, align, gfp)
+
+/**
+ * folio_scratchpad_alloc_type - Allocate a typed object from a scratchpad pointer
+ * @sp: Pointer to struct folio_scratchpad
+ * @type: Type of object being allocated
+ * @gfp: Allocation flags
+ */
+#define folio_scratchpad_alloc_type(sp, type, gfp)				\
+	((type *)folio_scratchpad_alloc(sp,					\
+					sizeof(type),				\
+					__alignof__(type),			\
+					gfp))
+
+/*
+ * 2. Fixed-Slot Uniform Pool (Specialized Thin Wrapper on Scratchpad)
+ */
+struct folio_pool {
+	struct folio_scratchpad base;
+	size_t elem_size;
+	size_t elem_align;
+};
+
+#define FOLIO_POOL_INIT(name, _elem_size, _order) {			\
+	.base = FOLIO_SCRATCHPAD_INIT((name).base, _order),		\
+	.elem_size = (_elem_size),					\
+	.elem_align = ((_elem_size) > sizeof(void *) ? (_elem_size) : sizeof(void *)), \
+}
+
+#define FOLIO_POOL_INIT_KEY(name, _elem_size, _order, _key) {		\
+	.base = FOLIO_SCRATCHPAD_INIT_KEY((name).base, _order, _key),	\
+	.elem_size = (_elem_size),					\
+	.elem_align = ((_elem_size) > sizeof(void *) ? (_elem_size) : sizeof(void *)), \
+}
+
+void folio_pool_init(struct folio_pool *fp, size_t elem_size, unsigned int order);
+void folio_pool_init_key(struct folio_pool *fp, size_t elem_size, unsigned int order,
+			 struct static_key *key);
+void folio_pool_init_align(struct folio_pool *fp, size_t elem_size,
+			   size_t elem_align, unsigned int order);
+
+static inline void *folio_pool_alloc(struct folio_pool *fp, gfp_t gfp)
+{
+	return folio_scratchpad_alloc(&fp->base, fp->elem_size, fp->elem_align, gfp);
+}
+
+static inline void folio_pool_free(struct folio_pool *fp)
+{
+	folio_scratchpad_free(&fp->base);
+}
+
+static inline void folio_pool_stats(struct folio_pool *fp, unsigned int *nr_chunks,
+				    size_t *chunk_size, size_t *tail_used)
+{
+	folio_scratchpad_stats(&fp->base, nr_chunks, chunk_size, tail_used);
+}
+
+/**
+ * folio_pool_alloc_obj - Allocate a typed object from a container's embedded folio_pool
+ * @ptr: Pointer to container struct (e.g. env)
+ * @member: Name of the struct folio_pool field (e.g. state_pool)
+ * @type: Type of object being allocated (e.g. struct bpf_verifier_stack_elem)
+ * @gfp: Allocation flags
+ */
+#define folio_pool_alloc_obj(ptr, member, type, gfp)				\
+	((type *)folio_pool_alloc(&(ptr)->member, gfp))
+
+/**
+ * folio_pool_alloc_type - Allocate a typed object from a struct folio_pool pointer
+ * @fp: Pointer to struct folio_pool
+ * @type: Type of object being allocated
+ * @gfp: Allocation flags
+ */
+#define folio_pool_alloc_type(fp, type, gfp)					\
+	((type *)folio_pool_alloc(fp, gfp))
+
+#endif /* _LINUX_FOLIO_POOL_H */
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 3be612145c130..b4a6cc1f46afd 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1941,6 +1941,8 @@ __printf(2, 3) int nft_request_module(struct net *net, const char *fmt, ...);
 static inline int nft_request_module(struct net *net, const char *fmt, ...) { return -ENOENT; }
 #endif
 
+#include <linux/folio_pool.h>
+
 struct nftables_pernet {
 	struct list_head	tables;
 	struct list_head	commit_list;
@@ -1949,6 +1951,7 @@ struct nftables_pernet {
 	struct list_head	binding_list;
 	struct list_head	module_list;
 	struct list_head	notify_list;
+	struct folio_scratchpad	trans_scratchpad;
 	struct mutex		commit_mutex;
 	u64			table_handle;
 	u64			tstamp;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 6db306d23b479..8ef48673b9339 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -19,6 +19,7 @@
 #include <linux/fdtable.h>
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/folio_pool.h>
 #include <linux/license.h>
 #include <linux/filter.h>
 #include <linux/kernel.h>
@@ -2059,12 +2060,15 @@ int generic_map_delete_batch(struct bpf_map *map,
 	return err;
 }
 
+DEFINE_STATIC_KEY_TRUE(bpf_batch_scratchpad_key);
+
 int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
 			     const union bpf_attr *attr,
 			     union bpf_attr __user *uattr)
 {
 	void __user *values = u64_to_user_ptr(attr->batch.values);
 	void __user *keys = u64_to_user_ptr(attr->batch.keys);
+	struct folio_scratchpad batch_sp;
 	u32 value_size, cp, max_count;
 	void *key, *value;
 	int err = 0;
@@ -2083,13 +2087,17 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
 	if (put_user(0, &uattr->batch.count))
 		return -EFAULT;
 
-	key = kvmalloc(map->key_size, GFP_USER | __GFP_NOWARN);
+	folio_scratchpad_init_key(&batch_sp, 0, &bpf_batch_scratchpad_key);
+
+	key = folio_scratchpad_alloc(&batch_sp, map->key_size,
+				     sizeof(void *), GFP_USER | __GFP_NOWARN);
 	if (!key)
 		return -ENOMEM;
 
-	value = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);
+	value = folio_scratchpad_alloc(&batch_sp, value_size,
+				       sizeof(void *), GFP_USER | __GFP_NOWARN);
 	if (!value) {
-		kvfree(key);
+		folio_scratchpad_free(&batch_sp);
 		return -ENOMEM;
 	}
 
@@ -2111,8 +2119,7 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
 	if (copy_to_user(&uattr->batch.count, &cp, sizeof(cp)))
 		err = -EFAULT;
 
-	kvfree(value);
-	kvfree(key);
+	folio_scratchpad_free(&batch_sp);
 
 	return err;
 }
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index fdc5fbb1f78ca..8a9e66ce4dc8e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1725,7 +1725,7 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
 		*prev_insn_idx = head->prev_insn_idx;
 	elem = head->next;
 	bpf_free_verifier_state(&head->st, false);
-	kfree(head);
+	folio_pool_free_elem(head);
 	env->head = elem;
 	env->stack_size--;
 	return 0;
@@ -1743,6 +1743,8 @@ static bool error_recoverable_with_nospec(int err)
 	return err == -EPERM || err == -EACCES || err == -EINVAL;
 }
 
+DEFINE_STATIC_KEY_TRUE(bpf_state_pool_key);
+
 static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
 					     int insn_idx, int prev_insn_idx,
 					     bool speculative)
@@ -1751,7 +1753,9 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
 	struct bpf_verifier_stack_elem *elem;
 	int err;
 
-	elem = kzalloc_obj(struct bpf_verifier_stack_elem, GFP_KERNEL_ACCOUNT);
+	elem = folio_pool_alloc_obj(env, state_pool,
+				    struct bpf_verifier_stack_elem,
+				    GFP_KERNEL_ACCOUNT);
 	if (!elem)
 		return ERR_PTR(-ENOMEM);
 
@@ -2275,7 +2279,9 @@ static struct bpf_verifier_state *push_async_cb(struct bpf_verifier_env *env,
 	struct bpf_verifier_stack_elem *elem;
 	struct bpf_func_state *frame;
 
-	elem = kzalloc_obj(struct bpf_verifier_stack_elem, GFP_KERNEL_ACCOUNT);
+	elem = folio_pool_alloc_obj(env, state_pool,
+				    struct bpf_verifier_stack_elem,
+				    GFP_KERNEL_ACCOUNT);
 	if (!elem)
 		return ERR_PTR(-ENOMEM);
 
@@ -19789,6 +19795,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
 	if (!env)
 		return -ENOMEM;
 
+	folio_pool_init_key(&env->state_pool, sizeof(struct bpf_verifier_stack_elem),
+			    get_order(SZ_64K), &bpf_state_pool_key);
+
 	env->bt.env = env;
 
 	len = (*prog)->len;
@@ -20055,6 +20064,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
 	bpf_clear_insn_aux_data(env, 0, env->prog->len);
 err_free_env:
 	bpf_stack_liveness_free(env);
+	folio_pool_free(&env->state_pool);
 	kvfree(env->cfg.insn_postorder);
 	kvfree(env->scc_info);
 	kvfree(env->succ);
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2d4c5bab5af88..d9541207b342b 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -58,12 +58,37 @@
 #include <linux/context_tracking.h>
 #include <linux/console.h>
 #include <linux/kasan.h>
+#include <linux/folio_pool.h>
+
+DEFINE_STATIC_KEY_TRUE(lockdep_pool_key);
+static struct folio_pool lockdep_pool =
+	FOLIO_POOL_INIT_KEY(lockdep_pool, sizeof(struct lock_list),
+			    FOLIO_POOL_64K_ORDER, &lockdep_pool_key);
+
+static int __init setup_lockdep_folio_pool(char *str)
+{
+	bool enable;
+
+	if (!kstrtobool(str, &enable)) {
+		if (enable)
+			static_branch_enable(&lockdep_pool_key);
+		else
+			static_branch_disable(&lockdep_pool_key);
+	}
+	return 1;
+}
+__setup("lockdep.folio_pool=", setup_lockdep_folio_pool);
 
 #include <asm/sections.h>
 
 #include "lockdep_internals.h"
 #include "lock_events.h"
 
+void lockdep_pool_stats(unsigned int *nr_chunks, size_t *chunk_size, size_t *tail_used)
+{
+	folio_pool_stats(&lockdep_pool, nr_chunks, chunk_size, tail_used);
+}
+
 #include <trace/events/lock.h>
 
 #ifdef CONFIG_PROVE_LOCKING
@@ -204,9 +229,13 @@ static inline int debug_locks_off_graph_unlock(void)
 	return ret;
 }
 
+#define BOOTSTRAP_LOCKDEP_ENTRIES 4096UL
+
+static struct lock_list early_list_entries[BOOTSTRAP_LOCKDEP_ENTRIES] __initdata;
+static unsigned long early_list_entries_in_use[BITS_TO_LONGS(BOOTSTRAP_LOCKDEP_ENTRIES)] __initdata;
+static struct lock_list *bootstrap_entries __read_mostly = early_list_entries;
+static unsigned long *bootstrap_entries_in_use __read_mostly = early_list_entries_in_use;
 unsigned long nr_list_entries;
-static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES];
-static DECLARE_BITMAP(list_entries_in_use, MAX_LOCKDEP_ENTRIES);
 
 /*
  * All data structures here are protected by the global debug_lock.
@@ -1028,9 +1057,9 @@ static bool class_lock_list_valid(struct lock_class *c, struct list_head *h)
 
 	list_for_each_entry(e, h, entry) {
 		if (e->links_to != c) {
-			printk(KERN_INFO "class %s: mismatch for lock entry %ld; class %s <> %s",
+			printk(KERN_INFO "class %s: mismatch for lock entry %px; class %s <> %s",
 			       c->name ? : "(?)",
-			       (unsigned long)(e - list_entries),
+			       e,
 			       e->links_to && e->links_to->name ?
 			       e->links_to->name : "(?)",
 			       e->class && e->class->name ? e->class->name :
@@ -1119,35 +1148,37 @@ static bool __check_data_structures(void)
 		}
 	}
 
-	/*
-	 * Check whether all list entries that are in use occur in a class
-	 * lock list.
-	 */
-	for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		e = list_entries + i;
-		if (!in_any_class_list(&e->entry)) {
-			printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
-			       (unsigned int)(e - list_entries),
-			       e->class->name ? : "(?)",
-			       e->links_to->name ? : "(?)");
-			return false;
+	if (bootstrap_entries) {
+		/*
+		 * Check whether all list entries that are in use occur in a class
+		 * lock list.
+		 */
+		for_each_set_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {
+			e = bootstrap_entries + i;
+			if (!in_any_class_list(&e->entry)) {
+				printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
+				       (unsigned int)(e - bootstrap_entries),
+				       e->class->name ? : "(?)",
+				       e->links_to->name ? : "(?)");
+				return false;
+			}
 		}
-	}
 
-	/*
-	 * Check whether all list entries that are not in use do not occur in
-	 * a class lock list.
-	 */
-	for_each_clear_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		e = list_entries + i;
-		if (in_any_class_list(&e->entry)) {
-			printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
-			       (unsigned int)(e - list_entries),
-			       e->class && e->class->name ? e->class->name :
-			       "(?)",
-			       e->links_to && e->links_to->name ?
-			       e->links_to->name : "(?)");
-			return false;
+		/*
+		 * Check whether all list entries that are not in use do not occur in
+		 * a class lock list.
+		 */
+		for_each_clear_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {
+			e = bootstrap_entries + i;
+			if (in_any_class_list(&e->entry)) {
+				printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
+				       (unsigned int)(e - bootstrap_entries),
+				       e->class && e->class->name ? e->class->name :
+				       "(?)",
+				       e->links_to && e->links_to->name ?
+				       e->links_to->name : "(?)");
+				return false;
+			}
 		}
 	}
 
@@ -1400,22 +1431,32 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
  */
 static struct lock_list *alloc_list_entry(void)
 {
-	int idx = find_first_zero_bit(list_entries_in_use,
-				      ARRAY_SIZE(list_entries));
+	struct lock_list *p;
 
-	if (idx >= ARRAY_SIZE(list_entries)) {
-		if (!debug_locks_off_graph_unlock())
-			return NULL;
+	if (bootstrap_entries) {
+		int idx = find_first_zero_bit(bootstrap_entries_in_use,
+					      BOOTSTRAP_LOCKDEP_ENTRIES);
 
-		nbcon_cpu_emergency_enter();
-		print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low!");
-		dump_stack();
-		nbcon_cpu_emergency_exit();
-		return NULL;
+		if (idx < BOOTSTRAP_LOCKDEP_ENTRIES) {
+			__set_bit(idx, bootstrap_entries_in_use);
+			nr_list_entries++;
+			return bootstrap_entries + idx;
+		}
+	}
+
+	p = folio_pool_alloc_type(&lockdep_pool, struct lock_list, GFP_ATOMIC);
+	if (p) {
+		nr_list_entries++;
+		return p;
 	}
-	nr_list_entries++;
-	__set_bit(idx, list_entries_in_use);
-	return list_entries + idx;
+	if (!debug_locks_off_graph_unlock())
+		return NULL;
+
+	nbcon_cpu_emergency_enter();
+	print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low and folio_pool exhausted!");
+	dump_stack();
+	nbcon_cpu_emergency_exit();
+	return NULL;
 }
 
 /*
@@ -6238,13 +6279,25 @@ static void remove_class_from_lock_chains(struct pending_free *pf,
 	}
 }
 
+static inline bool is_bootstrap_entry(const struct lock_list *entry)
+{
+	return bootstrap_entries &&
+	       entry >= bootstrap_entries &&
+	       entry < bootstrap_entries + BOOTSTRAP_LOCKDEP_ENTRIES;
+}
+
+static inline void clear_bootstrap_entry_bit(const struct lock_list *entry)
+{
+	if (is_bootstrap_entry(entry))
+		__clear_bit(entry - bootstrap_entries, bootstrap_entries_in_use);
+}
+
 /*
  * Remove all references to a lock class. The caller must hold the graph lock.
  */
 static void zap_class(struct pending_free *pf, struct lock_class *class)
 {
-	struct lock_list *entry;
-	int i;
+	struct lock_list *entry, *tmp, *other, *other_tmp;
 
 	WARN_ON_ONCE(!class->key);
 
@@ -6252,11 +6305,29 @@ static void zap_class(struct pending_free *pf, struct lock_class *class)
 	 * Remove all dependencies this lock is
 	 * involved in:
 	 */
-	for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		entry = list_entries + i;
-		if (entry->class != class && entry->links_to != class)
-			continue;
-		__clear_bit(i, list_entries_in_use);
+	list_for_each_entry_safe(entry, tmp, &class->locks_after, entry) {
+		list_for_each_entry_safe(other, other_tmp, &entry->links_to->locks_before, entry) {
+			if (other->links_to == class) {
+				clear_bootstrap_entry_bit(other);
+				nr_list_entries--;
+				list_del_rcu(&other->entry);
+				break;
+			}
+		}
+		clear_bootstrap_entry_bit(entry);
+		nr_list_entries--;
+		list_del_rcu(&entry->entry);
+	}
+	list_for_each_entry_safe(entry, tmp, &class->locks_before, entry) {
+		list_for_each_entry_safe(other, other_tmp, &entry->links_to->locks_after, entry) {
+			if (other->links_to == class) {
+				clear_bootstrap_entry_bit(other);
+				nr_list_entries--;
+				list_del_rcu(&other->entry);
+				break;
+			}
+		}
+		clear_bootstrap_entry_bit(entry);
 		nr_list_entries--;
 		list_del_rcu(&entry->entry);
 	}
@@ -6631,6 +6702,9 @@ EXPORT_SYMBOL_GPL(lockdep_unregister_key);
 
 void __init lockdep_init(void)
 {
+	bootstrap_entries = early_list_entries;
+	bootstrap_entries_in_use = early_list_entries_in_use;
+
 	pr_info("Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\n");
 
 	pr_info("... MAX_LOCKDEP_SUBCLASSES:  %lu\n", MAX_LOCKDEP_SUBCLASSES);
@@ -6645,8 +6719,8 @@ void __init lockdep_init(void)
 	       (sizeof(lock_classes) +
 		sizeof(lock_classes_in_use) +
 		sizeof(classhash_table) +
-		sizeof(list_entries) +
-		sizeof(list_entries_in_use) +
+		sizeof(early_list_entries) +
+		sizeof(early_list_entries_in_use) +
 		sizeof(chainhash_table) +
 		sizeof(delayed_free)
 #ifdef CONFIG_PROVE_LOCKING
@@ -6668,6 +6742,104 @@ void __init lockdep_init(void)
 	       sizeof(((struct task_struct *)NULL)->held_locks));
 }
 
+static int __init lockdep_boot_report(void)
+{
+	pr_info("lockdep: %lu/%lu bootstrap entries used before buddy init, folio_pool active\n",
+		min_t(unsigned long, nr_list_entries, BOOTSTRAP_LOCKDEP_ENTRIES),
+		BOOTSTRAP_LOCKDEP_ENTRIES);
+	return 0;
+}
+core_initcall(lockdep_boot_report);
+
+static int __init lockdep_compact_boot_graph(void)
+{
+	struct lock_class *class;
+	struct lock_list *entry, *tmp, *new_entry;
+	unsigned long flags;
+	unsigned long migrated = 0;
+
+	if (!debug_locks)
+		return 0;
+
+	/* Pre-allocate 64KB folio chunk outside graph_lock to avoid MM recursion */
+	new_entry = folio_pool_alloc_type(&lockdep_pool, struct lock_list, GFP_KERNEL);
+	if (!new_entry) {
+		pr_err("lockdep: failed to pre-allocate folio chunk for boot compaction\n");
+		return -ENOMEM;
+	}
+
+	raw_local_irq_save(flags);
+	if (!graph_lock()) {
+		raw_local_irq_restore(flags);
+		return 0;
+	}
+
+	list_for_each_entry(class, &all_lock_classes, lock_entry) {
+		list_for_each_entry_safe(entry, tmp, &class->locks_after, entry) {
+			if (is_bootstrap_entry(entry)) {
+				if (new_entry) {
+					*new_entry = *entry;
+					list_replace_rcu(&entry->entry, &new_entry->entry);
+					new_entry = NULL;
+				} else {
+					struct lock_list *slot;
+
+					slot = folio_pool_alloc_type(&lockdep_pool,
+								     struct lock_list,
+								     GFP_ATOMIC);
+					if (!slot) {
+						debug_locks_off_graph_unlock();
+						raw_local_irq_restore(flags);
+						pr_err("lockdep: folio chunk exhausted during boot compaction\n");
+						return -ENOMEM;
+					}
+					*slot = *entry;
+					list_replace_rcu(&entry->entry, &slot->entry);
+				}
+				migrated++;
+			}
+		}
+
+		list_for_each_entry_safe(entry, tmp, &class->locks_before, entry) {
+			if (is_bootstrap_entry(entry)) {
+				if (new_entry) {
+					*new_entry = *entry;
+					list_replace_rcu(&entry->entry, &new_entry->entry);
+					new_entry = NULL;
+				} else {
+					struct lock_list *slot;
+
+					slot = folio_pool_alloc_type(&lockdep_pool,
+								     struct lock_list,
+								     GFP_ATOMIC);
+					if (!slot) {
+						debug_locks_off_graph_unlock();
+						raw_local_irq_restore(flags);
+						pr_err("lockdep: folio chunk exhausted during boot compaction\n");
+						return -ENOMEM;
+					}
+					*slot = *entry;
+					list_replace_rcu(&entry->entry, &slot->entry);
+				}
+				migrated++;
+			}
+		}
+	}
+
+	/* Adjust counter so compaction does not double-count migrated nodes */
+	nr_list_entries -= migrated;
+
+	bootstrap_entries = NULL;
+	bootstrap_entries_in_use = NULL;
+	graph_unlock();
+	raw_local_irq_restore(flags);
+
+	pr_info("lockdep: compacted %lu boot entries into folio_pool, freeing bootstrap memory\n",
+		migrated);
+	return 0;
+}
+late_initcall(lockdep_compact_boot_graph);
+
 static void
 print_freed_lock_bug(struct task_struct *curr, const void *mem_from,
 		     const void *mem_to, struct held_lock *hlock)
diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h
index 0e5e6ffe91a3f..f802160c0dd53 100644
--- a/kernel/locking/lockdep_internals.h
+++ b/kernel/locking/lockdep_internals.h
@@ -159,6 +159,8 @@ extern unsigned long max_lock_class_idx;
 extern struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
 extern unsigned long lock_classes_in_use[];
 
+void lockdep_pool_stats(unsigned int *nr_chunks, size_t *chunk_size, size_t *tail_used);
+
 #ifdef CONFIG_PROVE_LOCKING
 extern unsigned long lockdep_count_forward_deps(struct lock_class *);
 extern unsigned long lockdep_count_backward_deps(struct lock_class *);
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index 1916db9aa46b6..3e6f24ae9b34b 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -288,8 +288,20 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
 			nr_lock_classes, MAX_LOCKDEP_KEYS);
 	seq_printf(m, " dynamic-keys:                  %11lu\n",
 			nr_dynamic_keys);
-	seq_printf(m, " direct dependencies:           %11lu [max: %lu]\n",
-			nr_list_entries, MAX_LOCKDEP_ENTRIES);
+	{
+		unsigned int fp_chunks = 0;
+		size_t fp_chunk_sz = 0, fp_tail_used = 0;
+
+		lockdep_pool_stats(&fp_chunks, &fp_chunk_sz, &fp_tail_used);
+		if (fp_chunks) {
+			seq_printf(m, " direct dependencies:           %11lu [dynamic: %u x %zu kB, tail: %zu kB/%zu kB]\n",
+				   nr_list_entries, fp_chunks, fp_chunk_sz / 1024,
+				   fp_tail_used / 1024, fp_chunk_sz / 1024);
+		} else {
+			seq_printf(m, " direct dependencies:           %11lu [max: %lu]\n",
+				   nr_list_entries, MAX_LOCKDEP_ENTRIES);
+		}
+	}
 	seq_printf(m, " indirect dependencies:         %11lu\n",
 			sum_forward_deps);
 
diff --git a/lib/Makefile b/lib/Makefile
index 7f75cc6edf94a..b5f2f41f96fbd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -58,7 +58,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \
 	 bsearch.o find_bit.o llist.o lwq.o memweight.o kfifo.o \
 	 percpu-refcount.o rhashtable.o base64.o \
 	 once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \
-	 generic-radix-tree.o bitmap-str.o
+	 generic-radix-tree.o bitmap-str.o folio_pool.o
 obj-y += string_helpers.o
 obj-y += hexdump.o
 obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
diff --git a/lib/folio_pool.c b/lib/folio_pool.c
new file mode 100644
index 0000000000000..07a290db6c506
--- /dev/null
+++ b/lib/folio_pool.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Direct-Map Large Folio Scratchpad & Pool bump allocators.
+ */
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/moduleparam.h>
+#include <linux/folio_pool.h>
+
+DEFINE_STATIC_KEY_TRUE(folio_pool_enabled_key);
+EXPORT_SYMBOL_GPL(folio_pool_enabled_key);
+
+static int folio_pool_enabled_set(const char *val, const struct kernel_param *kp)
+{
+	bool enable;
+	int ret = kstrtobool(val, &enable);
+
+	if (ret)
+		return ret;
+
+	if (enable)
+		static_branch_enable(&folio_pool_enabled_key);
+	else
+		static_branch_disable(&folio_pool_enabled_key);
+
+	return 0;
+}
+
+static int folio_pool_enabled_get(char *buffer, const struct kernel_param *kp)
+{
+	return sprintf(buffer, "%c\n", static_branch_likely(&folio_pool_enabled_key) ? 'Y' : 'N');
+}
+
+static const struct kernel_param_ops folio_pool_enabled_ops = {
+	.set = folio_pool_enabled_set,
+	.get = folio_pool_enabled_get,
+};
+
+module_param_cb(enabled, &folio_pool_enabled_ops, NULL, 0644);
+MODULE_PARM_DESC(enabled, "Toggle folio_pool/scratchpad bump allocator (0 = fallback to SLUB)");
+
+/*
+ * 1. Variable-Sized Scratchpad (Core Engine)
+ */
+void folio_scratchpad_init_key(struct folio_scratchpad *sp, unsigned int order,
+			       struct static_key *key)
+{
+	INIT_LIST_HEAD(&sp->chunks);
+	sp->free_ptr = NULL;
+	sp->remaining = 0;
+	sp->chunk_order = order;
+	sp->key = key;
+	spin_lock_init(&sp->lock);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_init_key);
+
+void folio_scratchpad_init(struct folio_scratchpad *sp, unsigned int order)
+{
+	folio_scratchpad_init_key(sp, order, NULL);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_init);
+
+static inline bool folio_scratchpad_is_enabled(const struct folio_scratchpad *sp)
+{
+	if (sp->key)
+		return static_key_enabled(sp->key);
+	return static_branch_likely(&folio_pool_enabled_key);
+}
+
+noinline void *folio_scratchpad_alloc(struct folio_scratchpad *sp, size_t size,
+				     size_t align, gfp_t gfp)
+{
+	struct folio_pool_chunk *chunk;
+	struct folio *folio;
+	void *elem, *base;
+	size_t chunk_size, aligned_size, pad, header_offset;
+	unsigned long flags;
+
+	if (!folio_scratchpad_is_enabled(sp))
+		return kvzalloc(size, gfp);
+
+	if (unlikely(!size))
+		return NULL;
+
+	align = max_t(size_t, sizeof(void *), align ? align : sizeof(void *));
+
+	spin_lock_irqsave(&sp->lock, flags);
+	pad = (uintptr_t)sp->free_ptr & (align - 1);
+	if (pad)
+		pad = align - pad;
+	aligned_size = size + pad;
+
+	if (sp->remaining < aligned_size) {
+		spin_unlock_irqrestore(&sp->lock, flags);
+
+		folio = folio_alloc(gfp, sp->chunk_order);
+		if (!folio && sp->chunk_order > 0)
+			folio = folio_alloc(gfp, 0);
+		if (!folio)
+			return NULL;
+
+		base = folio_address(folio);
+		chunk = (struct folio_pool_chunk *)base;
+		chunk->folio = folio;
+		chunk_size = folio_size(folio);
+		header_offset = ALIGN(sizeof(*chunk), max_t(size_t, sizeof(void *), align));
+
+		spin_lock_irqsave(&sp->lock, flags);
+		list_add(&chunk->link, &sp->chunks);
+		sp->free_ptr = base + header_offset;
+		sp->remaining = chunk_size - header_offset;
+
+		pad = (uintptr_t)sp->free_ptr & (align - 1);
+		if (pad)
+			pad = align - pad;
+		aligned_size = size + pad;
+	}
+
+	if (sp->remaining < aligned_size) {
+		spin_unlock_irqrestore(&sp->lock, flags);
+		return NULL;
+	}
+
+	elem = sp->free_ptr + pad;
+	sp->free_ptr += aligned_size;
+	sp->remaining -= aligned_size;
+	spin_unlock_irqrestore(&sp->lock, flags);
+
+	memset(elem, 0, size);
+	return elem;
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_alloc);
+
+noinline void folio_scratchpad_reset(struct folio_scratchpad *sp)
+{
+	struct folio_pool_chunk *head, *chunk, *tmp;
+	size_t header_offset;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sp->lock, flags);
+	if (list_empty(&sp->chunks)) {
+		sp->free_ptr = NULL;
+		sp->remaining = 0;
+		spin_unlock_irqrestore(&sp->lock, flags);
+		return;
+	}
+
+	/* Retain primary head chunk; free overflow chunks */
+	head = list_first_entry(&sp->chunks, struct folio_pool_chunk, link);
+	list_for_each_entry_safe(chunk, tmp, &sp->chunks, link) {
+		if (chunk == head)
+			continue;
+		list_del(&chunk->link);
+		folio_put(chunk->folio);
+	}
+
+	header_offset = ALIGN(sizeof(*head), sizeof(void *));
+	sp->free_ptr = folio_address(head->folio) + header_offset;
+	sp->remaining = folio_size(head->folio) - header_offset;
+	spin_unlock_irqrestore(&sp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_reset);
+
+noinline void folio_scratchpad_free(struct folio_scratchpad *sp)
+{
+	struct folio_pool_chunk *chunk, *tmp;
+	struct folio *folio;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sp->lock, flags);
+	sp->free_ptr = NULL;
+	sp->remaining = 0;
+	list_for_each_entry_safe(chunk, tmp, &sp->chunks, link) {
+		folio = chunk->folio;
+		list_del(&chunk->link);
+		folio_put(folio);
+	}
+	spin_unlock_irqrestore(&sp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_free);
+
+/*
+ * 2. Fixed-Slot Uniform Pool (Specialized Thin Wrapper on Scratchpad)
+ */
+void folio_pool_init_align(struct folio_pool *fp, size_t elem_size,
+			   size_t elem_align, unsigned int order)
+{
+	folio_scratchpad_init(&fp->base, order);
+	fp->elem_size = elem_size;
+	fp->elem_align = max_t(size_t, sizeof(void *), elem_align ? elem_align : sizeof(void *));
+}
+EXPORT_SYMBOL_GPL(folio_pool_init_align);
+
+void folio_pool_init_key(struct folio_pool *fp, size_t elem_size, unsigned int order,
+			 struct static_key *key)
+{
+	size_t align = sizeof(void *);
+
+	if (elem_size && is_power_of_2(elem_size))
+		align = max_t(size_t, sizeof(void *), elem_size);
+	folio_scratchpad_init_key(&fp->base, order, key);
+	fp->elem_size = elem_size;
+	fp->elem_align = align;
+}
+EXPORT_SYMBOL_GPL(folio_pool_init_key);
+
+void folio_pool_init(struct folio_pool *fp, size_t elem_size, unsigned int order)
+{
+	folio_pool_init_key(fp, elem_size, order, NULL);
+}
+EXPORT_SYMBOL_GPL(folio_pool_init);
+
+void folio_scratchpad_stats(struct folio_scratchpad *sp, unsigned int *nr_chunks,
+			    size_t *chunk_size, size_t *tail_used)
+{
+	unsigned long flags;
+	size_t csz;
+
+	csz = (PAGE_SIZE << sp->chunk_order);
+	if (chunk_size)
+		*chunk_size = csz;
+
+	spin_lock_irqsave(&sp->lock, flags);
+	if (nr_chunks)
+		*nr_chunks = list_count_nodes(&sp->chunks);
+	if (tail_used)
+		*tail_used = csz > sp->remaining ? csz - sp->remaining : 0;
+	spin_unlock_irqrestore(&sp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(folio_scratchpad_stats);
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index af357f6c5070b..8b9e0c905d841 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -173,12 +173,17 @@ static void nft_ctx_init(struct nft_ctx *ctx,
 	bitmap_zero(ctx->reg_inited, NFT_REG32_NUM);
 }
 
+DEFINE_FOLIO_POOL_STATIC_KEY_PARAM(nft_trans_scratchpad_key, trans_scratchpad,
+				   "Toggle nf_tables transaction folio scratchpad allocator");
+
 static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
 					 int msg_type, u32 size)
 {
+	struct nftables_pernet *nft_net = nft_pernet(ctx->net);
 	struct nft_trans *trans;
 
-	trans = kzalloc(size, GFP_KERNEL);
+	trans = folio_scratchpad_alloc_bytes(nft_net, trans_scratchpad, size,
+					     __alignof__(struct nft_trans), GFP_KERNEL);
 	if (trans == NULL)
 		return NULL;
 
@@ -194,6 +199,18 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
 	return trans;
 }
 
+/*
+ * nft_trans_free - release a transaction object.
+ *
+ * Scratchpad-backed objects are not individually freed; they are reclaimed in
+ * bulk by folio_scratchpad_free() at batch commit/abort boundary.  Kzalloc'd
+ * objects reside on slab pages and are released immediately via kfree().
+ */
+static inline void nft_trans_free(struct nft_trans *trans)
+{
+	folio_scratchpad_free_elem(trans);
+}
+
 static struct nft_trans_binding *nft_trans_get_binding(struct nft_trans *trans)
 {
 	switch (trans->msg_type) {
@@ -219,7 +236,7 @@ static void nft_trans_list_del(struct nft_trans *trans)
 static void nft_trans_destroy(struct nft_trans *trans)
 {
 	nft_trans_list_del(trans);
-	kfree(trans);
+	nft_trans_free(trans);
 }
 
 static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set,
@@ -519,8 +536,10 @@ static bool nft_trans_collapse_set_elem(struct nftables_pernet *nft_net,
 	/* krealloc might free tail which invalidates list pointers */
 	list_del_init(&tail->nft_trans.list);
 
-	new_trans = krealloc(tail, struct_size(tail, elems, nelems),
-			     GFP_KERNEL);
+	new_trans = folio_scratchpad_realloc(tail,
+					     struct_size(tail, elems, old_nelems),
+					     struct_size(tail, elems, nelems),
+					     GFP_KERNEL);
 	if (!new_trans) {
 		list_add_tail(&tail->nft_trans.list,
 			      &nft_net->commit_list);
@@ -600,7 +619,7 @@ static void nft_trans_commit_list_add_elem(struct net *net, struct nft_trans *tr
 		     trans->msg_type != NFT_MSG_DELSETELEM);
 
 	if (nft_trans_try_collapse(nft_net, trans)) {
-		kfree(trans);
+		nft_trans_free(trans);
 		return;
 	}
 
@@ -3068,7 +3087,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
 
 err_trans:
 	free_percpu(stats);
-	kfree(trans);
+	nft_trans_free(trans);
 err_hooks:
 	if (nla[NFTA_CHAIN_HOOK]) {
 		list_for_each_entry_safe(h, next, &hook.list, list) {
@@ -7636,7 +7655,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 	return set_full ? -ENFILE : 0;
 
 err_element_clash:
-	kfree(trans);
+	nft_trans_free(trans);
 err_set_size:
 	if (!(flags & NFT_SET_ELEM_CATCHALL))
 		atomic_dec(&set->nelems);
@@ -7904,7 +7923,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
 	return 0;
 
 fail_ops:
-	kfree(trans);
+	nft_trans_free(trans);
 fail_trans:
 	kfree(elem.priv);
 fail_elem_key_end:
@@ -8287,7 +8306,7 @@ static int nf_tables_updobj(const struct nft_ctx *ctx,
 	return 0;
 
 err_free_trans:
-	kfree(trans);
+	nft_trans_free(trans);
 err_trans:
 	module_put(type->owner);
 	return err;
@@ -10253,7 +10272,7 @@ static void nft_commit_release(struct nft_trans *trans)
 	if (trans->put_net)
 		put_net(trans->net);
 
-	kfree(trans);
+	nft_trans_free(trans);
 }
 
 static void nf_tables_trans_destroy_work(struct work_struct *w)
@@ -10275,6 +10294,8 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)
 		nft_trans_list_del(trans);
 		nft_commit_release(trans);
 	}
+
+	folio_scratchpad_reset(&nft_net->trans_scratchpad);
 }
 
 void nf_tables_trans_destroy_flush_work(struct net *net)
@@ -11244,7 +11265,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
 			nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
 		break;
 	}
-	kfree(trans);
+	nft_trans_free(trans);
 }
 
 static void nft_set_abort_update(struct list_head *set_update_list)
@@ -11468,6 +11489,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
 		nf_tables_abort_release(trans);
 	}
 
+	folio_scratchpad_reset(&nft_net->trans_scratchpad);
+
 	return err;
 }
 
@@ -11499,6 +11522,9 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb,
 	else
 		nf_tables_module_autoload_cleanup(net);
 
+	folio_scratchpad_free(&nft_net->trans_scratchpad);
+	folio_scratchpad_init(&nft_net->trans_scratchpad, 4);
+
 	mutex_unlock(&nft_net->commit_mutex);
 
 	return ret;
@@ -12142,6 +12168,8 @@ static int __net_init nf_tables_init_net(struct net *net)
 	INIT_LIST_HEAD(&nft_net->binding_list);
 	INIT_LIST_HEAD(&nft_net->module_list);
 	INIT_LIST_HEAD(&nft_net->notify_list);
+	folio_scratchpad_init_key(&nft_net->trans_scratchpad, get_order(SZ_64K),
+				  &nft_trans_scratchpad_key);
 	mutex_init(&nft_net->commit_mutex);
 	net->nft.base_seq = 1;
 	nft_net->gc_seq = 0;
@@ -12186,6 +12214,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
 	WARN_ON_ONCE(!list_empty(&nft_net->module_list));
 	WARN_ON_ONCE(!list_empty(&nft_net->notify_list));
 	WARN_ON_ONCE(!list_empty(&nft_net->destroy_list));
+	folio_scratchpad_free(&nft_net->trans_scratchpad);
 }
 
 static void nf_tables_exit_batch(struct list_head *net_exit_list)

45/3 2026/08/17 18:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19038
							output: 282
							thoughts: 2619
						

Thoughts:
**KMSAN Assessment: Folio-based Memory Allocator Patch**

Alright, let's break this down. My initial question is whether this patch, introducing the `folio_pool` and `folio_scratchpad` allocators, warrants a dedicated KMSAN fuzzing session. The core of the patch revolves around replacing `kzalloc`, `kmalloc`, and `kvmalloc` with these new functions. My immediate concern is the potential for uninitialized memory reads, a key target for KMSAN.

My initial scan highlights that `folio_scratchpad_alloc`, and by extension `folio_pool_alloc`, *explicitly* zero the allocated memory via `memset`. This is a positive sign and generally makes the new allocator safer than the previous ones in terms of uninitialized memory. I noted this is evident in `drm_gpuvm.c`, `bpf/syscall.c`, `bpf/verifier.c`, `lockdep.c`, and `nf_tables_api.c`.

The implementation of `folio_scratchpad_realloc` initially gave me pause. It uses `kmalloc` and `memcpy` for the re-allocation, which *could* introduce uninitialized memory if the new size is larger than the old size. However, upon inspection, it behaves identically to `krealloc`, which is the old implementation and also does *not* zero the memory that is newly allocated when expanding. This behavior in `nf_tables_api.c` is unchanged, and I confirmed this. `bpf/syscall.c` sees an improvement, since `folio_scratchpad_alloc` replaces `kvmalloc`, which does *not* zero memory, thus increasing the chance that a value is initialized.

I spent time examining the `folio_scratchpad_alloc` alignment padding. The padding bytes before the allocated element are not zeroed, but they shouldn't be accessed.

Considering the patch as a whole, it appears to be *safer* regarding uninitialized memory than the original code in several cases, since `kvmalloc` is replaced by `folio_scratchpad_alloc`.

Now, does the `folio_pool_realloc` implementation introduce new uninitialized read risks? After reviewing, the answer is no, because it behaves the same as `krealloc`, returning a memory block that may have uninitialized bytes beyond the size of the old allocation. This means the patch *retains* the same behavior as the previous implementation regarding this aspect.

Finally, I reviewed the KMSAN rules. The code changes primarily involve replacing allocation functions, and it's clear the new allocation functions zero the memory. This means the patch is not introducing uninitialized memory usage or info-leaks, and KASAN will catch any out-of-bounds access.

Therefore, my conclusion is that *NeedsKMSAN=false* for this patch. There are no new uninitialized memory risks. The new allocator may have its own bugs, such as OOB access or use-after-free, but KMSAN is not the right tool here.



46/3 2026/08/17 18:14 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch introduces a new memory allocator (`folio_pool` and `folio_scratchpad`) and replaces various `kzalloc`, `kmalloc`, `kvmalloc`, and `krealloc` calls with the new allocator's functions. The core allocation function `folio_scratchpad_alloc` explicitly zeroes the allocated memory using `memset(elem, 0, size)`. For reallocations, `folio_pool_realloc` behaves identically to `krealloc` without `__GFP_ZERO`, leaving newly expanded memory uninitialized just as the original code did. In some cases (like `generic_map_update_batch`), the patch actually replaces `kvmalloc` (which does not zero memory) with `folio_scratchpad_alloc` (which does), thereby reducing the risk of uninitialized memory usage. Any potential bugs introduced by this custom allocator (such as incorrect padding, remaining size calculations, or chunk management) would manifest as out-of-bounds accesses, memory corruption, or use-after-free bugs, which are effectively detected by KASAN. Since all newly allocated objects are fully zeroed and no new uninitialized memory risks or info-leaks are introduced, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch introduces a new memory allocator (`folio_pool` and `folio_scratchpad`) and replaces various `kzalloc`, `kmalloc`, `kvmalloc`, and `krealloc` calls with the new allocator's functions. The core allocation function `folio_scratchpad_alloc` explicitly zeroes the allocated memory using `memset(elem, 0, size)`. For reallocations, `folio_pool_realloc` behaves identically to `krealloc` without `__GFP_ZERO`, leaving newly expanded memory uninitialized just as the original code did. In some cases (like `generic_map_update_batch`), the patch actually replaces `kvmalloc` (which does not zero memory) with `folio_scratchpad_alloc` (which does), thereby reducing the risk of uninitialized memory usage. Any potential bugs introduced by this custom allocator (such as incorrect padding, remaining size calculations, or chunk management) would manifest as out-of-bounds accesses, memory corruption, or use-after-free bugs, which are effectively detected by KASAN. Since all newly allocated objects are fully zeroed and no new uninitialized memory risks or info-leaks are introduced, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)