* [PATCH v2] fs: hide names_cache behind runtime const machinery
@ 2025-12-01 8:32 Mateusz Guzik
2025-12-01 8:51 ` Al Viro
0 siblings, 1 reply; 9+ messages in thread
From: Mateusz Guzik @ 2025-12-01 8:32 UTC (permalink / raw)
To: viro; +Cc: brauner, jack, linux-kernel, linux-fsdevel, Mateusz Guzik
s/names_cachep/names_cache/ for consistency with dentry cache.
Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
v2:
- rebased on top of work.filename-refcnt
ACHTUNG: there is a change queued for 6.19 merge window which treats
dentry cache the same way:
commit 21b561dab1406e63740ebe240c7b69f19e1bcf58
Author: Mateusz Guzik <mjguzik@gmail.com>
Date: Wed Nov 5 16:36:22 2025 +0100
fs: hide dentry_cache behind runtime const machinery
which would result in a merge conflict in vmlinux.lds.h. thus I
cherry-picked before generating the diff to avoid the issue for later.
fs/namei.c | 16 ++++++++++------
include/asm-generic/vmlinux.lds.h | 3 ++-
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index d6eac90084e1..eff4cbffe241 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -41,6 +41,8 @@
#include <linux/init_task.h>
#include <linux/uaccess.h>
+#include <asm/runtime-const.h>
+
#include "internal.h"
#include "mount.h"
@@ -124,23 +126,25 @@
*/
/* SLAB cache for struct filename instances */
-static struct kmem_cache *names_cachep __ro_after_init;
+static struct kmem_cache *__names_cache __ro_after_init;
+#define names_cache runtime_const_ptr(__names_cache)
void __init filename_init(void)
{
- names_cachep = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname),
- EMBEDDED_NAME_MAX, NULL);
+ __names_cache = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname),
+ EMBEDDED_NAME_MAX, NULL);
+ runtime_const_init(ptr, __names_cache);
}
static inline struct filename *alloc_filename(void)
{
- return kmem_cache_alloc(names_cachep, GFP_KERNEL);
+ return kmem_cache_alloc(names_cache, GFP_KERNEL);
}
static inline void free_filename(struct filename *p)
{
- kmem_cache_free(names_cachep, p);
+ kmem_cache_free(names_cache, p);
}
static inline void initname(struct filename *name)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 20939d2445e7..3abd76ac723a 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -956,7 +956,8 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
#define RUNTIME_CONST_VARIABLES \
RUNTIME_CONST(shift, d_hash_shift) \
RUNTIME_CONST(ptr, dentry_hashtable) \
- RUNTIME_CONST(ptr, __dentry_cache)
+ RUNTIME_CONST(ptr, __dentry_cache) \
+ RUNTIME_CONST(ptr, __names_cache)
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_TABLE() \
--
2.48.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-01 8:32 [PATCH v2] fs: hide names_cache behind runtime const machinery Mateusz Guzik
@ 2025-12-01 8:51 ` Al Viro
2025-12-02 2:31 ` Al Viro
0 siblings, 1 reply; 9+ messages in thread
From: Al Viro @ 2025-12-01 8:51 UTC (permalink / raw)
To: Mateusz Guzik; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Mon, Dec 01, 2025 at 09:32:26AM +0100, Mateusz Guzik wrote:
> s/names_cachep/names_cache/ for consistency with dentry cache.
>
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
>
> v2:
> - rebased on top of work.filename-refcnt
>
> ACHTUNG: there is a change queued for 6.19 merge window which treats
> dentry cache the same way:
> commit 21b561dab1406e63740ebe240c7b69f19e1bcf58
> Author: Mateusz Guzik <mjguzik@gmail.com>
> Date: Wed Nov 5 16:36:22 2025 +0100
>
> fs: hide dentry_cache behind runtime const machinery
>
> which would result in a merge conflict in vmlinux.lds.h. thus I
> cherry-picked before generating the diff to avoid the issue for later.
*shrug*
For now I'm working on top of v6.18; rebase to -rc1 will happen at the
end of window...
Anyway, not a problem; applied with obvious massage. Will push tomorrow
once I sort the linearization out.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-01 8:51 ` Al Viro
@ 2025-12-02 2:31 ` Al Viro
2025-12-02 5:10 ` Mateusz Guzik
0 siblings, 1 reply; 9+ messages in thread
From: Al Viro @ 2025-12-02 2:31 UTC (permalink / raw)
To: Mateusz Guzik; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Mon, Dec 01, 2025 at 08:51:17AM +0000, Al Viro wrote:
> On Mon, Dec 01, 2025 at 09:32:26AM +0100, Mateusz Guzik wrote:
> > s/names_cachep/names_cache/ for consistency with dentry cache.
> >
> > Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> > ---
> >
> > v2:
> > - rebased on top of work.filename-refcnt
> >
> > ACHTUNG: there is a change queued for 6.19 merge window which treats
> > dentry cache the same way:
> > commit 21b561dab1406e63740ebe240c7b69f19e1bcf58
> > Author: Mateusz Guzik <mjguzik@gmail.com>
> > Date: Wed Nov 5 16:36:22 2025 +0100
> >
> > fs: hide dentry_cache behind runtime const machinery
> >
> > which would result in a merge conflict in vmlinux.lds.h. thus I
> > cherry-picked before generating the diff to avoid the issue for later.
>
> *shrug*
> For now I'm working on top of v6.18; rebase to -rc1 will happen at the
> end of window...
>
> Anyway, not a problem; applied with obvious massage. Will push tomorrow
> once I sort the linearization out.
FWIW, I wonder if we would be better off with the following trick:
add
struct kmem_cache *preallocated;
to struct kmem_cache_args. Semantics: if the value is non-NULL, it must
point to an unitialized object of type struct kmem_cache; in that case
__kmem_cache_create_args() will use that object (and return its address
on success) instead of allocating one from kmem_cache. kmem_cache_destroy()
should not be called for it.
It's very easy to do, AFAICS:
1) non-NULL => have __kmem_cache_create_args() skip the __kmem_cache_alias()
path.
2) non-NULL => have create_cache() zero what it points to and use that pointer
instead of calling kmem_cache_zalloc()
3) non-NULL => skip kmem_cache_free() at create_cache() out_free_cache:
"Don't do kmem_cache_destroy() to those" might or might not be worth relaxing -
I hadn't looked into the lifetime issues for kmem_cache instances, no idea
how painful would that be; for core kernel caches it's not an issue, obviously.
For modules it is, but then runtime_constant machinery is not an option there
either.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-02 2:31 ` Al Viro
@ 2025-12-02 5:10 ` Mateusz Guzik
2025-12-02 5:52 ` Al Viro
0 siblings, 1 reply; 9+ messages in thread
From: Mateusz Guzik @ 2025-12-02 5:10 UTC (permalink / raw)
To: Al Viro; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Tue, Dec 2, 2025 at 3:31 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
> FWIW, I wonder if we would be better off with the following trick:
> add
> struct kmem_cache *preallocated;
> to struct kmem_cache_args. Semantics: if the value is non-NULL, it must
> point to an unitialized object of type struct kmem_cache; in that case
> __kmem_cache_create_args() will use that object (and return its address
> on success) instead of allocating one from kmem_cache. kmem_cache_destroy()
> should not be called for it.
>
> It's very easy to do, AFAICS:
> 1) non-NULL => have __kmem_cache_create_args() skip the __kmem_cache_alias()
> path.
> 2) non-NULL => have create_cache() zero what it points to and use that pointer
> instead of calling kmem_cache_zalloc()
> 3) non-NULL => skip kmem_cache_free() at create_cache() out_free_cache:
>
> "Don't do kmem_cache_destroy() to those" might or might not be worth relaxing -
> I hadn't looked into the lifetime issues for kmem_cache instances, no idea
> how painful would that be; for core kernel caches it's not an issue, obviously.
> For modules it is, but then runtime_constant machinery is not an option there
> either.
So IIUC whatever APIs aside, the crux of this idea is to have
kmem_cache objs defined instead of having pointers to them, as in:
-struct kmem_cache *names_cachep __ro_after_init;
+struct kmem_cache names_cachep __ro_after_init;
I thought about doing it that way prior to runtime const machinery,
but given that said machinery exists I don't know if that's
justifiable.
To elaborate, while it apparently was created as a hack and does not
work for modules, it does not have to be that way and I would argue it
should be patched up to a fully-fleshed out solution.
Everything marked __ro_after_init is eligible for being patched up to
avoid being accessed, including numerous kmem caches.
Apart from those an example frequently read var is
percpu_counter_batch, which for vfs comes into play very time a new
file obj is allocated. The thing is also used by some of the
filesystems.
So if one was to pretend for a minute runtime-const *does* work for
modules and there are no header mess issues and usage is popping up
everywhere, is there a reason to handle kmem differently?
Both with your idea and the runtime thing extra changes would be
needed. in your case the thing at hand is no longer a pointer and all
consumers of a given cache need to get adjusted. If instead one went
the runtime route, some macros could be added for syntactic sugar to
provide the relevant accessor + init, which should be very easy to do
by wrapping existing code.
So I would vote against your idea, but it's the call of the mm folk.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-02 5:10 ` Mateusz Guzik
@ 2025-12-02 5:52 ` Al Viro
2025-12-02 6:18 ` Mateusz Guzik
2025-12-02 6:20 ` Al Viro
0 siblings, 2 replies; 9+ messages in thread
From: Al Viro @ 2025-12-02 5:52 UTC (permalink / raw)
To: Mateusz Guzik; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Tue, Dec 02, 2025 at 06:10:36AM +0100, Mateusz Guzik wrote:
> So IIUC whatever APIs aside, the crux of this idea is to have
> kmem_cache objs defined instead of having pointers to them, as in:
> -struct kmem_cache *names_cachep __ro_after_init;
> +struct kmem_cache names_cachep __ro_after_init;
Huh? __ro_after_init will break instantly - the contents changes with
each allocation, after all. What I want is
static struct kmem_cache_store names_cache;
As for the many places to modify...
fs/file.c:390: newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);
fs/file.c:422: kmem_cache_free(files_cachep, newf);
fs/file.c:514: kmem_cache_free(files_cachep, files);
include/linux/fdtable.h:116:extern struct kmem_cache *files_cachep;
kernel/fork.c:429:struct kmem_cache *files_cachep;
kernel/fork.c:2987: files_cachep = kmem_cache_create("files_cache",
samples/kmemleak/kmemleak-test.c:52: pr_info("kmem_cache_alloc(files_cachep) = 0x%px\n",
samples/kmemleak/kmemleak-test.c:53: kmem_cache_alloc(files_cachep, GFP_KERNEL));
samples/kmemleak/kmemleak-test.c:54: pr_info("kmem_cache_alloc(files_cachep) = 0x%px\n",
samples/kmemleak/kmemleak-test.c:55: kmem_cache_alloc(files_cachep, GFP_KERNEL));
I would argue for making it static in fs/file.c, where we have the grand
total of 3 places using the sucker, between two functions.
dentry_cache:
fs/dcache.c:345: kmem_cache_free(dentry_cache, dentry);
fs/dcache.c:352: kmem_cache_free(dentry_cache, dentry);
fs/dcache.c:1690: dentry = kmem_cache_alloc_lru(dentry_cache, &sb->s_dentry_lru,
fs/dcache.c:1711: kmem_cache_free(dentry_cache, dentry);
fs/dcache.c:1748: kmem_cache_free(dentry_cache, dentry);
5 lines, between 3 functions (__d_free(), __d_free_external(), __d_allock()).
mnt_cache:
fs/namespace.c:293: struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
fs/namespace.c:342: kmem_cache_free(mnt_cache, mnt);
fs/namespace.c:737: kmem_cache_free(mnt_cache, mnt);
3 lines, alloc_vfsmnt() and free_vfsmnt()
sock_inode_cachep:
net/socket.c:322: ei = alloc_inode_sb(sb, sock_inode_cachep, GFP_KERNEL);
net/socket.c:343: kmem_cache_free(sock_inode_cachep, ei);
2 lines, sock_alloc_inode() and sock_free_inode() (sockets are coallocated with
inodes).
struct filename: two lines after that series.
task_struct_cachep:
kernel/fork.c:184: return kmem_cache_alloc_node(task_struct_cachep, GFP_KERNEL, node);
kernel/fork.c:189: kmem_cache_free(task_struct_cachep, tsk);
and so it goes; that's the sane pattern - you want few places where objects of given
type are allocated and freed, so that tracing the callchains would be feasible.
names_cachep used to be shitty in that respect, what with its abuse by weird __getname()
callers. It's not the common situation, thankfully.
The delicate part is headers, indeed - we don't want to expose struct kmem_cache guts
anywhere outside of mm/*, and not the entire mm/* either. But that's not hard to
deal with - see include/generate/bounds.h, include/generate/rq-offsets.h, etc.
Exact same technics can be used to get sizeof(struct kmem_cache) calculated and
put into generated header. Then we get something like struct kmem_cache_store with
the right size and alignment, and _that_ would be what the variables would be.
With static inline struct kmem_cache *to_kmem_cache(struct kmem_cache_store *)
returning a cast and e.g.
static inline void free_filename(struct __filename *p)
{
kmem_cache_free(to_kmem_cache(&names_cache), p);
}
as an example of use.
Anyway, for now I've applied your patch pretty much as-is; conversion of the
sort described above can be done afterwards just fine.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-02 5:52 ` Al Viro
@ 2025-12-02 6:18 ` Mateusz Guzik
2025-12-02 6:32 ` Al Viro
2025-12-02 6:20 ` Al Viro
1 sibling, 1 reply; 9+ messages in thread
From: Mateusz Guzik @ 2025-12-02 6:18 UTC (permalink / raw)
To: Al Viro; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Tue, Dec 2, 2025 at 6:52 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Tue, Dec 02, 2025 at 06:10:36AM +0100, Mateusz Guzik wrote:
>
> > So IIUC whatever APIs aside, the crux of this idea is to have
> > kmem_cache objs defined instead of having pointers to them, as in:
> > -struct kmem_cache *names_cachep __ro_after_init;
> > +struct kmem_cache names_cachep __ro_after_init;
>
> Huh? __ro_after_init will break instantly - the contents changes with
> each allocation, after all. What I want is
> static struct kmem_cache_store names_cache;
>
c'mon man, I copy pasted the existing line and removed the asterisk to
de-pointer it to make for illustrative purposes. You went straight to
description how to make your idea happen, so I wanted to make sure we
are on the same page on what it is.
> As for the many places to modify...
>
> fs/file.c:390: newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);
> fs/file.c:422: kmem_cache_free(files_cachep, newf);
> fs/file.c:514: kmem_cache_free(files_cachep, files);
> include/linux/fdtable.h:116:extern struct kmem_cache *files_cachep;
> kernel/fork.c:429:struct kmem_cache *files_cachep;
> kernel/fork.c:2987: files_cachep = kmem_cache_create("files_cache",
> samples/kmemleak/kmemleak-test.c:52: pr_info("kmem_cache_alloc(files_cachep) = 0x%px\n",
> samples/kmemleak/kmemleak-test.c:53: kmem_cache_alloc(files_cachep, GFP_KERNEL));
> samples/kmemleak/kmemleak-test.c:54: pr_info("kmem_cache_alloc(files_cachep) = 0x%px\n",
> samples/kmemleak/kmemleak-test.c:55: kmem_cache_alloc(files_cachep, GFP_KERNEL));
>
> I would argue for making it static in fs/file.c, where we have the grand
> total of 3 places using the sucker, between two functions.
>
The claim was not that your idea results in insurmountable churn. The
claim was *both* your idea and runtime const require churn on per kmem
cache basis. Then the question is if one is going to churn it
regardless, why this way over runtime const. I do think the runtime
thing is a little bit less churn and less work on the mm side to get
it going, but then the runtime thing *itself* needs productizing
(which I'm not signing up to do).
Per the previous e-mail I don't have a strong opinion myself and it is
the mm folk who need either idea sold to anyway.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-02 5:52 ` Al Viro
2025-12-02 6:18 ` Mateusz Guzik
@ 2025-12-02 6:20 ` Al Viro
1 sibling, 0 replies; 9+ messages in thread
From: Al Viro @ 2025-12-02 6:20 UTC (permalink / raw)
To: Mateusz Guzik; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Tue, Dec 02, 2025 at 05:52:58AM +0000, Al Viro wrote:
> The delicate part is headers, indeed - we don't want to expose struct kmem_cache guts
> anywhere outside of mm/*, and not the entire mm/* either. But that's not hard to
> deal with - see include/generate/bounds.h, include/generate/rq-offsets.h, etc.
> Exact same technics can be used to get sizeof(struct kmem_cache) calculated and
> put into generated header. Then we get something like struct kmem_cache_store with
> the right size and alignment, and _that_ would be what the variables would be.
> With static inline struct kmem_cache *to_kmem_cache(struct kmem_cache_store *)
> returning a cast and e.g.
>
> static inline void free_filename(struct __filename *p)
> {
> kmem_cache_free(to_kmem_cache(&names_cache), p);
> }
>
> as an example of use.
>
> Anyway, for now I've applied your patch pretty much as-is; conversion of the
> sort described above can be done afterwards just fine.
>
FWIW, the Kbuild side of that would be like this - not a lot of magic there:
diff --git a/Kbuild b/Kbuild
index 13324b4bbe23..eb985a6614eb 100644
--- a/Kbuild
+++ b/Kbuild
@@ -45,13 +45,24 @@ kernel/sched/rq-offsets.s: $(offsets-file)
$(rq-offsets-file): kernel/sched/rq-offsets.s FORCE
$(call filechk,offsets,__RQ_OFFSETS_H__)
+# generate kmem_cache_size.h
+
+kmem_cache_size-file := include/generated/kmem_cache_size.h
+
+targets += mm/kmem_cache_size.s
+
+mm/kmem_cache_size.s: $(rq-offsets-file)
+
+$(kmem_cache_size-file): mm/kmem_cache_size.s FORCE
+ $(call filechk,offsets,__KMEM_CACHE_SIZE_H__)
+
# Check for missing system calls
quiet_cmd_syscalls = CALL $<
cmd_syscalls = $(CONFIG_SHELL) $< $(CC) $(c_flags) $(missing_syscalls_flags)
PHONY += missing-syscalls
-missing-syscalls: scripts/checksyscalls.sh $(rq-offsets-file)
+missing-syscalls: scripts/checksyscalls.sh $(kmem_cache_size-file)
$(call cmd,syscalls)
# Check the manual modification of atomic headers
diff --git a/include/linux/kmem_cache_store.h b/include/linux/kmem_cache_store.h
new file mode 100644
index 000000000000..4bd21480d3cf
--- /dev/null
+++ b/include/linux/kmem_cache_store.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_KMEM_CACHE_STORE_H
+#define __LINUX_KMEM_CACHE_STORE_H
+
+#include <generated/kmem_cache_size.h>
+
+/* same size and alignment as struct kmem_cache */
+struct kmem_cache_store {
+ unsigned char opaque[KMEM_CACHE_SIZE];
+} __attribute__((__aligned__(KMEM_CACHE_ALIGN)));
+
+struct kmem_cache;
+
+static inline struct kmem_cache *to_kmem_cache(struct kmem_cache_store *p)
+{
+ return (struct kmem_cache *)p;
+}
+#endif
diff --git a/mm/kmem_cache_size.c b/mm/kmem_cache_size.c
new file mode 100644
index 000000000000..52395b225aa1
--- /dev/null
+++ b/mm/kmem_cache_size.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generate definitions needed by the preprocessor.
+ * This code generates raw asm output which is post-processed
+ * to extract and format the required data.
+ */
+
+#define __GENERATING_KMEM_CACHE_SIZE_H
+/* Include headers that define the enum constants of interest */
+#include <linux/kbuild.h>
+#include "slab.h"
+
+int main(void)
+{
+ /* The enum constants to put into include/generated/bounds.h */
+ DEFINE(KMEM_CACHE_SIZE, sizeof(struct kmem_cache));
+ DEFINE(KMEM_CACHE_ALIGN, __alignof(struct kmem_cache));
+ /* End of constants */
+
+ return 0;
+}
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-02 6:18 ` Mateusz Guzik
@ 2025-12-02 6:32 ` Al Viro
2025-12-02 7:21 ` Al Viro
0 siblings, 1 reply; 9+ messages in thread
From: Al Viro @ 2025-12-02 6:32 UTC (permalink / raw)
To: Mateusz Guzik; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Tue, Dec 02, 2025 at 07:18:16AM +0100, Mateusz Guzik wrote:
> The claim was not that your idea results in insurmountable churn. The
> claim was *both* your idea and runtime const require churn on per kmem
> cache basis. Then the question is if one is going to churn it
> regardless, why this way over runtime const. I do think the runtime
> thing is a little bit less churn and less work on the mm side to get
> it going, but then the runtime thing *itself* needs productizing
> (which I'm not signing up to do).
Umm... runtime thing is lovely for shifts, but for pointers it's
going to be a headache on a bunch of architectures; for something
like dentry_hashtable it's either that or the cost of dereference,
but for kmem_cache I'd try it - if architecture has a good way for
"load a 64bit constant into a register staying within I$", I'd
expect the code generated for &global_variable to be not worse than
that, after all.
Churn is pretty much negligible in case of core kernel caches either
way.
As for the amount of churn in mm/*... Turns out to be fairly minor;
kmem_cache_args allows to propagate it without any calling convention
changes.
I'll post when I get it to reasonable shape - so far it looks easy...
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] fs: hide names_cache behind runtime const machinery
2025-12-02 6:32 ` Al Viro
@ 2025-12-02 7:21 ` Al Viro
0 siblings, 0 replies; 9+ messages in thread
From: Al Viro @ 2025-12-02 7:21 UTC (permalink / raw)
To: Mateusz Guzik; +Cc: brauner, jack, linux-kernel, linux-fsdevel
On Tue, Dec 02, 2025 at 06:32:28AM +0000, Al Viro wrote:
> On Tue, Dec 02, 2025 at 07:18:16AM +0100, Mateusz Guzik wrote:
>
> > The claim was not that your idea results in insurmountable churn. The
> > claim was *both* your idea and runtime const require churn on per kmem
> > cache basis. Then the question is if one is going to churn it
> > regardless, why this way over runtime const. I do think the runtime
> > thing is a little bit less churn and less work on the mm side to get
> > it going, but then the runtime thing *itself* needs productizing
> > (which I'm not signing up to do).
>
> Umm... runtime thing is lovely for shifts, but for pointers it's
> going to be a headache on a bunch of architectures; for something
> like dentry_hashtable it's either that or the cost of dereference,
> but for kmem_cache I'd try it - if architecture has a good way for
> "load a 64bit constant into a register staying within I$", I'd
> expect the code generated for &global_variable to be not worse than
> that, after all.
>
> Churn is pretty much negligible in case of core kernel caches either
> way.
>
> As for the amount of churn in mm/*... Turns out to be fairly minor;
> kmem_cache_args allows to propagate it without any calling convention
> changes.
>
> I'll post when I get it to reasonable shape - so far it looks easy...
OK, I'm going to grab some sleep; current (completely untested) delta
below, with conversion of mnt_cache as an example of use.
Uses of to_kmem_cache can be reduced with some use of _Generic
for kmem_cache_...alloc() and kmem_cache_free(). Even as it is,
the churn in fs/namespace.c is pretty minor...
Anyway, this is an intermediate variant:
diff --git a/Kbuild b/Kbuild
index 13324b4bbe23..eb985a6614eb 100644
--- a/Kbuild
+++ b/Kbuild
@@ -45,13 +45,24 @@ kernel/sched/rq-offsets.s: $(offsets-file)
$(rq-offsets-file): kernel/sched/rq-offsets.s FORCE
$(call filechk,offsets,__RQ_OFFSETS_H__)
+# generate kmem_cache_size.h
+
+kmem_cache_size-file := include/generated/kmem_cache_size.h
+
+targets += mm/kmem_cache_size.s
+
+mm/kmem_cache_size.s: $(rq-offsets-file)
+
+$(kmem_cache_size-file): mm/kmem_cache_size.s FORCE
+ $(call filechk,offsets,__KMEM_CACHE_SIZE_H__)
+
# Check for missing system calls
quiet_cmd_syscalls = CALL $<
cmd_syscalls = $(CONFIG_SHELL) $< $(CC) $(c_flags) $(missing_syscalls_flags)
PHONY += missing-syscalls
-missing-syscalls: scripts/checksyscalls.sh $(rq-offsets-file)
+missing-syscalls: scripts/checksyscalls.sh $(kmem_cache_size-file)
$(call cmd,syscalls)
# Check the manual modification of atomic headers
diff --git a/fs/namespace.c b/fs/namespace.c
index d766e08e0736..08c7870de413 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -34,6 +34,7 @@
#include <linux/mnt_idmapping.h>
#include <linux/pidfs.h>
#include <linux/nstree.h>
+#include <linux/kmem_cache_static.h>
#include "pnode.h"
#include "internal.h"
@@ -85,7 +86,7 @@ static u64 mnt_id_ctr = MNT_UNIQUE_ID_OFFSET;
static struct hlist_head *mount_hashtable __ro_after_init;
static struct hlist_head *mountpoint_hashtable __ro_after_init;
-static struct kmem_cache *mnt_cache __ro_after_init;
+static struct kmem_cache_store mnt_cache;
static DECLARE_RWSEM(namespace_sem);
static HLIST_HEAD(unmounted); /* protected by namespace_sem */
static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */
@@ -290,7 +291,8 @@ int mnt_get_count(struct mount *mnt)
static struct mount *alloc_vfsmnt(const char *name)
{
- struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
+ struct mount *mnt = kmem_cache_zalloc(to_kmem_cache(&mnt_cache),
+ GFP_KERNEL);
if (mnt) {
int err;
@@ -339,7 +341,7 @@ static struct mount *alloc_vfsmnt(const char *name)
out_free_id:
mnt_free_id(mnt);
out_free_cache:
- kmem_cache_free(mnt_cache, mnt);
+ kmem_cache_free(to_kmem_cache(&mnt_cache), mnt);
return NULL;
}
@@ -734,7 +736,7 @@ static void free_vfsmnt(struct mount *mnt)
#ifdef CONFIG_SMP
free_percpu(mnt->mnt_pcp);
#endif
- kmem_cache_free(mnt_cache, mnt);
+ kmem_cache_free(to_kmem_cache(&mnt_cache), mnt);
}
static void delayed_free_vfsmnt(struct rcu_head *head)
@@ -6013,8 +6015,9 @@ void __init mnt_init(void)
{
int err;
- mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount),
- 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL);
+ kmem_cache_setup("mnt_cache", sizeof(struct mount),
+ 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
+ NULL, &mnt_cache);
mount_hashtable = alloc_large_system_hash("Mount-cache",
sizeof(struct hlist_head),
diff --git a/include/linux/kmem_cache_static.h b/include/linux/kmem_cache_static.h
new file mode 100644
index 000000000000..f007c3bf3e88
--- /dev/null
+++ b/include/linux/kmem_cache_static.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_KMEM_CACHE_STATIC_H
+#define __LINUX_KMEM_CACHE_STATIC_H
+
+#include <generated/kmem_cache_size.h>
+#include <linux/slab.h>
+
+/* same size and alignment as struct kmem_cache */
+struct kmem_cache_store {
+ unsigned char opaque[KMEM_CACHE_SIZE];
+} __attribute__((__aligned__(KMEM_CACHE_ALIGN)));
+
+struct kmem_cache;
+
+static inline struct kmem_cache *to_kmem_cache(struct kmem_cache_store *p)
+{
+ return (struct kmem_cache *)p;
+}
+
+static inline int
+kmem_cache_setup(const char *name, unsigned int size, unsigned int align,
+ slab_flags_t flags, void (*ctor)(void *),
+ struct kmem_cache_store *s)
+{
+ struct kmem_cache *res;
+
+ res = __kmem_cache_create_args(name, size,
+ &(struct kmem_cache_args){
+ .align = align,
+ .ctor = ctor,
+ .preallocated = s},
+ flags);
+ return PTR_ERR_OR_ZERO(res);
+}
+
+#endif
diff --git a/include/linux/slab.h b/include/linux/slab.h
index cf443f064a66..a016aa817139 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -266,6 +266,8 @@ struct mem_cgroup;
*/
bool slab_is_available(void);
+struct kmem_cache_store;
+
/**
* struct kmem_cache_args - Less common arguments for kmem_cache_create()
*
@@ -366,6 +368,7 @@ struct kmem_cache_args {
* %0 means no sheaves will be created.
*/
unsigned int sheaf_capacity;
+ struct kmem_cache_store *preallocated;
};
struct kmem_cache *__kmem_cache_create_args(const char *name,
diff --git a/mm/kmem_cache_size.c b/mm/kmem_cache_size.c
new file mode 100644
index 000000000000..52395b225aa1
--- /dev/null
+++ b/mm/kmem_cache_size.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generate definitions needed by the preprocessor.
+ * This code generates raw asm output which is post-processed
+ * to extract and format the required data.
+ */
+
+#define __GENERATING_KMEM_CACHE_SIZE_H
+/* Include headers that define the enum constants of interest */
+#include <linux/kbuild.h>
+#include "slab.h"
+
+int main(void)
+{
+ /* The enum constants to put into include/generated/bounds.h */
+ DEFINE(KMEM_CACHE_SIZE, sizeof(struct kmem_cache));
+ DEFINE(KMEM_CACHE_ALIGN, __alignof(struct kmem_cache));
+ /* End of constants */
+
+ return 0;
+}
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 932d13ada36c..e48775475097 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -29,6 +29,7 @@
#include <linux/memcontrol.h>
#include <linux/stackdepot.h>
#include <trace/events/rcu.h>
+#include <linux/kmem_cache_static.h>
#include "../kernel/rcu/rcu.h"
#include "internal.h"
@@ -224,21 +225,21 @@ static struct kmem_cache *create_cache(const char *name,
struct kmem_cache_args *args,
slab_flags_t flags)
{
- struct kmem_cache *s;
+ struct kmem_cache *s = to_kmem_cache(args->preallocated);
int err;
/* If a custom freelist pointer is requested make sure it's sane. */
- err = -EINVAL;
if (args->use_freeptr_offset &&
(args->freeptr_offset >= object_size ||
!(flags & SLAB_TYPESAFE_BY_RCU) ||
!IS_ALIGNED(args->freeptr_offset, __alignof__(freeptr_t))))
- goto out;
+ return ERR_PTR(-EINVAL);
- err = -ENOMEM;
- s = kmem_cache_zalloc(kmem_cache, GFP_KERNEL);
- if (!s)
- goto out;
+ if (!s) {
+ s = kmem_cache_zalloc(kmem_cache, GFP_KERNEL);
+ if (!s)
+ return ERR_PTR(-ENOMEM);
+ }
err = do_kmem_cache_create(s, name, object_size, args, flags);
if (err)
goto out_free_cache;
@@ -248,8 +249,8 @@ static struct kmem_cache *create_cache(const char *name,
return s;
out_free_cache:
- kmem_cache_free(kmem_cache, s);
-out:
+ if (!args->preallocated)
+ kmem_cache_free(kmem_cache, s);
return ERR_PTR(err);
}
@@ -324,7 +325,7 @@ struct kmem_cache *__kmem_cache_create_args(const char *name,
object_size - args->usersize < args->useroffset))
args->usersize = args->useroffset = 0;
- if (!args->usersize && !args->sheaf_capacity)
+ if (!args->usersize && !args->sheaf_capacity && !args->preallocated)
s = __kmem_cache_alias(name, object_size, args->align, flags,
args->ctor);
if (s)
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-12-02 7:20 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-01 8:32 [PATCH v2] fs: hide names_cache behind runtime const machinery Mateusz Guzik
2025-12-01 8:51 ` Al Viro
2025-12-02 2:31 ` Al Viro
2025-12-02 5:10 ` Mateusz Guzik
2025-12-02 5:52 ` Al Viro
2025-12-02 6:18 ` Mateusz Guzik
2025-12-02 6:32 ` Al Viro
2025-12-02 7:21 ` Al Viro
2025-12-02 6:20 ` Al Viro
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).