Skip to content

Commit

Permalink
kernfs: use i_lock to protect concurrent inode updates
Browse files Browse the repository at this point in the history
The inode operations .permission() and .getattr() use the kernfs node
write lock but all that's needed is the read lock to protect against
partial updates of these kernfs node fields which are all done under
the write lock.

And .permission() is called frequently during path walks and can cause
quite a bit of contention between kernfs node operations and path
walks when the number of concurrent walks is high.

To change kernfs_iop_getattr() and kernfs_iop_permission() to take
the rw sem read lock instead of the write lock an additional lock is
needed to protect against multiple processes concurrently updating
the inode attributes and link count in kernfs_refresh_inode().

The inode i_lock seems like the sensible thing to use to protect these
inode attribute updates so use it in kernfs_refresh_inode().

The last hunk in the patch, applied to kernfs_fill_super(), is possibly
not needed but taking the lock was present originally. I prefer to
continue to take it to protect against a partial update of the source
kernfs fields during the call to kernfs_refresh_inode() made by
kernfs_get_inode().

Reviewed-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Ian Kent <raven@themaw.net>
Link: https://lore.kernel.org/r/162642771474.63632.16295959115893904470.stgit@web.messagingengine.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Ian Kent authored and Greg Kroah-Hartman committed Jul 27, 2021
1 parent 7ba0273 commit 47b5c64
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 8 deletions.
18 changes: 12 additions & 6 deletions fs/kernfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,13 @@ int kernfs_iop_getattr(struct user_namespace *mnt_userns,
struct inode *inode = d_inode(path->dentry);
struct kernfs_node *kn = inode->i_private;

down_write(&kernfs_rwsem);
down_read(&kernfs_rwsem);
spin_lock(&inode->i_lock);
kernfs_refresh_inode(kn, inode);
up_write(&kernfs_rwsem);

generic_fillattr(&init_user_ns, inode, stat);
spin_unlock(&inode->i_lock);
up_read(&kernfs_rwsem);

return 0;
}

Expand Down Expand Up @@ -272,17 +274,21 @@ int kernfs_iop_permission(struct user_namespace *mnt_userns,
struct inode *inode, int mask)
{
struct kernfs_node *kn;
int ret;

if (mask & MAY_NOT_BLOCK)
return -ECHILD;

kn = inode->i_private;

down_write(&kernfs_rwsem);
down_read(&kernfs_rwsem);
spin_lock(&inode->i_lock);
kernfs_refresh_inode(kn, inode);
up_write(&kernfs_rwsem);
ret = generic_permission(&init_user_ns, inode, mask);
spin_unlock(&inode->i_lock);
up_read(&kernfs_rwsem);

return generic_permission(&init_user_ns, inode, mask);
return ret;
}

int kernfs_xattr_get(struct kernfs_node *kn, const char *name,
Expand Down
4 changes: 2 additions & 2 deletions fs/kernfs/mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,9 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
sb->s_shrink.seeks = 0;

/* get root inode, initialize and unlock it */
down_write(&kernfs_rwsem);
down_read(&kernfs_rwsem);
inode = kernfs_get_inode(sb, info->root->kn);
up_write(&kernfs_rwsem);
up_read(&kernfs_rwsem);
if (!inode) {
pr_debug("kernfs: could not get root inode\n");
return -ENOMEM;
Expand Down

0 comments on commit 47b5c64

Please sign in to comment.