[PATCH] kernfs: reorder 'struct kernfs_node' to save some memory

From: Christophe JAILLET
Date: Sun Nov 26 2023 - 09:49:51 EST


'struct kernfs_node' uses a dedicated cache, so shrinking its size is
always a good idea.

On my system, each entry is 128 bytes and their are 32 entries per pages.
After the re-ordering, the struct is 120 bytes and 34 entries are stored
in each page.

Signed-off-by: Christophe JAILLET <christophe.jaillet@xxxxxxxxxx>
---
The numbers below are with a allmodconfig configuration. The delta is
related to CONFIG_DEBUG_LOCK_ALLOC and struct lockdep_map dep_map.

When I checked on my system, it would have saved 372kb of RAM:
sudo less /proc/slabinfo | grep kernf
kernfs_node_cache 49397 49504 128 32 1 : tunables 0 0 0 : slabdata 1547 1547 0

I have left flags close to the union, I *think* that they are related.
I don't if having 'mode' here is logical or not.

Before:
======
struct kernfs_node {
atomic_t count; /* 0 4 */
atomic_t active; /* 4 4 */
struct lockdep_map dep_map; /* 8 48 */
struct kernfs_node * parent; /* 56 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
const char * name; /* 64 8 */
struct rb_node rb __attribute__((__aligned__(8))); /* 72 24 */
const void * ns; /* 96 8 */
unsigned int hash; /* 104 4 */

/* XXX 4 bytes hole, try to pack */

union {
struct kernfs_elem_dir dir; /* 112 32 */
struct kernfs_elem_symlink symlink; /* 112 8 */
struct kernfs_elem_attr attr; /* 112 32 */
}; /* 112 32 */
/* --- cacheline 2 boundary (128 bytes) was 16 bytes ago --- */
void * priv; /* 144 8 */
u64 id; /* 152 8 */
short unsigned int flags; /* 160 2 */
umode_t mode; /* 162 2 */

/* XXX 4 bytes hole, try to pack */

struct kernfs_iattrs * iattr; /* 168 8 */

/* size: 176, cachelines: 3, members: 14 */
/* sum members: 168, holes: 2, sum holes: 8 */
/* forced alignments: 1 */
/* last cacheline: 48 bytes */
} __attribute__((__aligned__(8)));

After:
=====
struct kernfs_node {
atomic_t count; /* 0 4 */
atomic_t active; /* 4 4 */
struct lockdep_map dep_map; /* 8 48 */
struct kernfs_node * parent; /* 56 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
const char * name; /* 64 8 */
struct rb_node rb __attribute__((__aligned__(8))); /* 72 24 */
const void * ns; /* 96 8 */
unsigned int hash; /* 104 4 */
umode_t mode; /* 108 2 */
short unsigned int flags; /* 110 2 */
union {
struct kernfs_elem_dir dir; /* 112 32 */
struct kernfs_elem_symlink symlink; /* 112 8 */
struct kernfs_elem_attr attr; /* 112 32 */
}; /* 112 32 */
/* --- cacheline 2 boundary (128 bytes) was 16 bytes ago --- */
void * priv; /* 144 8 */
u64 id; /* 152 8 */
struct kernfs_iattrs * iattr; /* 160 8 */

/* size: 168, cachelines: 3, members: 14 */
/* forced alignments: 1 */
/* last cacheline: 40 bytes */
} __attribute__((__aligned__(8)));
---
include/linux/kernfs.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 99aaa050ccb7..b7cd6e793286 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -206,6 +206,8 @@ struct kernfs_node {

const void *ns; /* namespace tag */
unsigned int hash; /* ns + name hash */
+ umode_t mode;
+ unsigned short flags;
union {
struct kernfs_elem_dir dir;
struct kernfs_elem_symlink symlink;
@@ -220,8 +222,6 @@ struct kernfs_node {
*/
u64 id;

- unsigned short flags;
- umode_t mode;
struct kernfs_iattrs *iattr;
};

--
2.34.1