fs: speed up path lookup with cheaper MAY_EXEC checks
Subsystems (4)
PATCH 1/1 fs: speed up path lookup with cheaper MAY_EXEC checks
WARNING:
This is not a real submission yet, will need to do more testing for correctness.
Also I slapped ext4 support into this patch as opposed to posting it separately. The no_acl caching was written by Linus, also see the necroed thread here:
https://lore.kernel.org/linux-fsdevel/kn44smk4dgaj5rqmtcfr7ruecixzrik6omur2l2opitn7lbvfm@rm4y24fcfzbz/T/#m30d6cea6be48e95c0d824e98a328fb90c7a5766d
and full thread:
https://lore.kernel.org/linux-fsdevel/kn44smk4dgaj5rqmtcfr7ruecixzrik6omur2l2opitn7lbvfm@rm4y24fcfzbz/T/#t
Not pointing fingers, but someone promised to get that bit in. ;)
Now to business:
Vast majority of real-world traversals happen on directories which are traversable by anyone. The patch below adds support for pre-computing that state and branching on it during lookup, avoiding tons of work.
Stats from calls to security_inode_permission during kernel build:
missing: 6097127
present: 60579018
Or just above 90% of all lookups on my debian install benefited from it.
A simple microbench of stating /usr/include/linux/fs.h on ext4 in a loop on Sapphire Rapids (ops/s):
before: 3640352
after: 3797258 (+4%)
Note the speed up would be higher if it was not for stat itself being dog slow (to be addressed separately).
Filesystems interested in participating call inode_enable_fast_may_exec() when instatianating an inode.
Explicit opt-in is necessary as some filesystems have custom inode permission check hooks which happen to be of no significance for MAY_EXEC. With an opt-in we now it can be safely ignored. Otherwise any inode with such a func present would need to be excluded.
--- fs/attr.c | 1 + fs/ext4/inode.c | 6 +++ fs/ext4/namei.c | 1 + fs/file_table.c | 12 +++++ fs/namei.c | 106 +++++++++++++++++++++++++++++++++++++++++++-- fs/posix_acl.c | 1 + fs/xattr.c | 1 + include/linux/fs.h | 20 ++++++--- mm/shmem.c | 2 + 9 files changed, 141 insertions(+), 9 deletions(-)
fs/attr.c +1 -0
@@ -549,6 +549,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,if (!error) {fsnotify_change (dentry, ia_valid); +inode_recalc_fast_may_exec (inode);security_inode_post_setattr (idmap, dentry, ia_valid); }
fs/ext4/inode.c +6 -0
@@ -5519,6 +5519,12 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, goto bad_inode;brelse (iloc.bh); +/* Initialize the "no ACL's" state for the simple cases */ +if (!ext4_test_inode_state (inode,EXT4_STATE_XATTR ) && !ei->i_file_acl) +cache_no_acl (inode); + +inode_enable_fast_may_exec (inode); +unlock_new_inode (inode);return inode;
fs/ext4/namei.c +1 -0
@@ -3042,6 +3042,7 @@ static struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir,ext4_fc_track_create (handle, dentry);if (IS_DIRSYNC (dir))ext4_handle_sync (handle); +inode_enable_fast_may_exec (inode); out_stop:if (handle)
fs/file_table.c +12 -0
@@ -109,6 +109,8 @@ static int proc_nr_files(const struct ctl_table *table, int write, void *buffer,return proc_doulongvec_minmax (table, write, buffer, lenp, ppos); } +unsigned long magic_tunable; +static const struct ctl_table fs_stat_sysctls[] = { { .procname ="file-nr" ,
@@ -126,6 +128,16 @@ static const struct ctl_table fs_stat_sysctls[] = { .extra1 = SYSCTL_LONG_ZERO, .extra2 = SYSCTL_LONG_MAX, }, + { + .procname = "magic_tunable", + .data = &magic_tunable, + .maxlen = sizeof(magic_tunable), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + .extra1 = SYSCTL_LONG_ZERO, + .extra2 = SYSCTL_LONG_MAX, + }, + { .procname = "nr_open", .data = &sysctl_nr_open,
fs/namei.c +103 -3
136 lines not on this page · raw message
fs/posix_acl.c +1 -0
@@ -1135,6 +1135,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, error = -EIO ;if (!error) {fsnotify_xattr (dentry); +inode_recalc_fast_may_exec (inode);security_inode_post_set_acl (dentry, acl_name, kacl); }
fs/xattr.c +1 -0
@@ -235,6 +235,7 @@ int __vfs_setxattr_noperm(struct mnt_idmap *idmap, size, flags);if (!error) {fsnotify_xattr (dentry); +inode_recalc_fast_may_exec (inode);security_inode_post_setxattr (dentry, name, value, size, flags); }
include/linux/fs.h +14 -6
@@ -659,13 +659,14 @@ is_uncached_acl(struct posix_acl *acl)return (long )acl &1 ; } -#define IOP_FASTPERM 0x0001 -#define IOP_LOOKUP 0x0002 -#define IOP_NOFOLLOW 0x0004 -#define IOP_XATTR 0x0008 +#define IOP_FASTPERM 0x0001 +#define IOP_LOOKUP 0x0002 +#define IOP_NOFOLLOW 0x0004 +#define IOP_XATTR 0x0008#define IOP_DEFAULT_READLINK 0x0010 -#define IOP_MGTIME 0x0020 -#define IOP_CACHED_LINK 0x0040 +#define IOP_MGTIME 0x0020 +#define IOP_CACHED_LINK 0x0040 +#define IOP_FAST_MAY_EXEC 0x0080/* * Inode state bits. Protected by inode->i_lock
@@ -2607,6 +2608,7 @@ struct super_operations {#define S_VERITY (1 << 16)/* Verity file (using fs/verity/) */ #define S_KERNEL_FILE (1 << 17)/* File is in use by the kernel (eg. fs/cachefiles) */ #define S_ANON_INODE (1 << 19)/* Inode is an anonymous inode */ +#define S_CAN_FAST_EXEC (1 << 20)/* Inode is eligible for IOP_FAST_MAY_EXEC */ /* * Note that nosuid etc flags are inode-specific: setting some file-system
@@ -3395,6 +3397,12 @@ static inline int inode_init_always(struct super_block *sb, struct inode *inode) {return inode_init_always_gfp (sb, inode,GFP_NOFS ); } +void inode_recalc_fast_may_exec (struct inode *); +static inline void inode_enable_fast_may_exec (struct inode *inode) +{ + inode->i_flags |=S_CAN_FAST_EXEC ; +inode_recalc_fast_may_exec (inode); +}extern void inode_init_once (struct inode *);extern void address_space_init_once (struct address_space *mapping);
mm/shmem.c +2 -0
@@ -3105,6 +3105,8 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,break ; } +inode_recalc_fast_may_exec (inode); +lockdep_annotate_inode_mutex_key (inode);return inode; }
Signature · 1 line
2.48.1