diff mbox series

vfs: move getattr in inode_operations to a more commonly read area

Message ID 20241118002024.451858-1-mjguzik@gmail.com (mailing list archive)
State New
Headers show
Series vfs: move getattr in inode_operations to a more commonly read area | expand

Commit Message

Mateusz Guzik Nov. 18, 2024, 12:20 a.m. UTC
Notabaly occupied by lookup, get_link and permission.

This pushes unlink to another cache line, otherwise the layout is the
same on that front.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---

Probably more can be done to rearrange struct. If someone is down to do
it, I'm happy with this patch being dropped.

 include/linux/fs.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Comments

Jan Kara Nov. 18, 2024, 11:26 a.m. UTC | #1
On Mon 18-11-24 01:20:24, Mateusz Guzik wrote:
> Notabaly occupied by lookup, get_link and permission.
> 
> This pushes unlink to another cache line, otherwise the layout is the
> same on that front.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
> 
> Probably more can be done to rearrange struct. If someone is down to do
> it, I'm happy with this patch being dropped.

This makes some sense to me although I'd like to establish some higher
level guidelines (and document them in a comment) about what goes where in
the inode_operations struct. A lot of accesses to inode->i_op actually do
get optimized away with inode->i_opflags (e.g. frequent stuff like
.permission or .get_inode_acl) so there are actually high chances there's
only one access to inode->i_op for the operation we are doing and in such
case the ordering inside inode_operations doesn't really matter (it's
likely cache cold anyway). So I'm somewhat uncertain what the right
grouping should be and if it matters at all.

								Honza

> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 7e29433c5ecc..972147da71f9 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2140,6 +2140,8 @@ struct inode_operations {
>  	const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
>  	int (*permission) (struct mnt_idmap *, struct inode *, int);
>  	struct posix_acl * (*get_inode_acl)(struct inode *, int, bool);
> +	int (*getattr) (struct mnt_idmap *, const struct path *,
> +			struct kstat *, u32, unsigned int);
>  
>  	int (*readlink) (struct dentry *, char __user *,int);
>  
> @@ -2157,8 +2159,6 @@ struct inode_operations {
>  	int (*rename) (struct mnt_idmap *, struct inode *, struct dentry *,
>  			struct inode *, struct dentry *, unsigned int);
>  	int (*setattr) (struct mnt_idmap *, struct dentry *, struct iattr *);
> -	int (*getattr) (struct mnt_idmap *, const struct path *,
> -			struct kstat *, u32, unsigned int);
>  	ssize_t (*listxattr) (struct dentry *, char *, size_t);
>  	int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
>  		      u64 len);
> -- 
> 2.43.0
>
Mateusz Guzik Nov. 18, 2024, 12:18 p.m. UTC | #2
On Mon, Nov 18, 2024 at 12:26 PM Jan Kara <jack@suse.cz> wrote:
>
> On Mon 18-11-24 01:20:24, Mateusz Guzik wrote:
> > Notabaly occupied by lookup, get_link and permission.
> >
> > This pushes unlink to another cache line, otherwise the layout is the
> > same on that front.
> >
> > Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> > ---
> >
> > Probably more can be done to rearrange struct. If someone is down to do
> > it, I'm happy with this patch being dropped.
>
> This makes some sense to me although I'd like to establish some higher
> level guidelines (and document them in a comment) about what goes where in
> the inode_operations struct. A lot of accesses to inode->i_op actually do
> get optimized away with inode->i_opflags (e.g. frequent stuff like
> .permission or .get_inode_acl) so there are actually high chances there's
> only one access to inode->i_op for the operation we are doing and in such
> case the ordering inside inode_operations doesn't really matter (it's
> likely cache cold anyway). So I'm somewhat uncertain what the right
> grouping should be and if it matters at all.
>

So I ran bpftrace attached to all ext4 inode ops during the venerable
kernel build.

As I expected getattr is most commonly used. But indeed the rest is a
footnote in comparison, so it very well may be this change is a nop or
close to it.

So ye, I this is probably droppable as is, I'm not definitely not
going to push one way or the other.

result:
@[kprobe:ext4_tmpfile]: 1
@[kprobe:ext4_symlink]: 2
@[kprobe:ext4_set_acl]: 18
@[kprobe:ext4_rename2]: 69
@[kprobe:ext4_rmdir]: 163
@[kprobe:ext4_mkdir]: 172
@[kprobe:ext4_get_acl]: 753
@[kprobe:ext4_setattr]: 3938
@[kprobe:ext4_unlink]: 7218
@[kprobe:ext4_create]: 18576
@[kprobe:ext4_lookup]: 99644
@[kprobe:ext4_file_getattr]: 5737047
@[kprobe:ext4_getattr]: 5909325

oneliner: bpftrace -e
'kprobe:ext4_create,kprobe:ext4_fiemap,kprobe:ext4_fileattr_get,kprobe:ext4_fileattr_set,kprobe:ext4_file_getattr,kprobe:ext4_get_acl,kprobe:ext4_getattr,kprobe:ext4_link,kprobe:ext4_listxattr,kprobe:ext4_lookup,kprobe:ext4_mkdir,kprobe:ext4_mknod,kprobe:ext4_rename2,kprobe:ext4_rmdir,kprobe:ext4_set_acl,kprobe:ext4_setattr,kprobe:ext4_symlink,kprobe:ext4_tmpfile,kprobe:ext4_unlink
{ @[probe] = count(); }'
diff mbox series

Patch

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7e29433c5ecc..972147da71f9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2140,6 +2140,8 @@  struct inode_operations {
 	const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
 	int (*permission) (struct mnt_idmap *, struct inode *, int);
 	struct posix_acl * (*get_inode_acl)(struct inode *, int, bool);
+	int (*getattr) (struct mnt_idmap *, const struct path *,
+			struct kstat *, u32, unsigned int);
 
 	int (*readlink) (struct dentry *, char __user *,int);
 
@@ -2157,8 +2159,6 @@  struct inode_operations {
 	int (*rename) (struct mnt_idmap *, struct inode *, struct dentry *,
 			struct inode *, struct dentry *, unsigned int);
 	int (*setattr) (struct mnt_idmap *, struct dentry *, struct iattr *);
-	int (*getattr) (struct mnt_idmap *, const struct path *,
-			struct kstat *, u32, unsigned int);
 	ssize_t (*listxattr) (struct dentry *, char *, size_t);
 	int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
 		      u64 len);