Re: [PATCH v2 0/6] kernfs: proposed locking and concurrency improvement

Ian Kent
2020-12-19T07:09:49Z

On Fri, 2020-12-18 at 09:59 -0500, Tejun Heo wrote:
> Hello,
>
> On Fri, Dec 18, 2020 at 03:36:21PM +0800, Ian Kent wrote:
> > Sounds like your saying it would be ok to add a lock to the
> > attrs structure, am I correct?
>
> Yeah, adding a lock to attrs is a lot less of a problem and it looks
> like
> it's gonna have to be either that or hashed locks, which might
> actually make
> sense if we're worried about the size of attrs (I don't think we need
> to).

Maybe that isn't needed.

And looking further I see there's a race that kernfs can't do anything
about between kernfs_refresh_inode() and fs/inode.c:update_times().

kernfs could avoid fighting with the VFS to keep the attributes set to
those of the kernfs node by using the inode operation .update_times()
and, if it makes sense, the kernfs node attributes that it wants to be
updated on file system activity could also be updated here.

I can't find any reason why this shouldn't be done but kernfs is
fairly widely used in other kernel subsystems so what does everyone
think of this patch, updated to set kernfs node attributes that
should be updated of course, see comment in the patch?

kernfs: fix attributes update race

From: Ian Kent <raven@themaw.net>

kernfs uses kernfs_refresh_inode() (called from kernfs_iop_getattr()
and kernfs_iop_permission()) to keep the inode attributes set to the
attibutes of the kernfs node.

But there is no way for kernfs to prevent racing with the function
fs/inode.c:update_times().

The better choice is to use the inode operation .update_times() and
just let the VFS use the generic functions for .getattr() and
.permission().

Signed-off-by: Ian Kent <raven@themaw.net>
---
fs/kernfs/inode.c | 37 ++++++++++++++-----------------------
fs/kernfs/kernfs-internal.h | 4 +---
2 files changed, 15 insertions(+), 26 deletions(-)

diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index fc2469a20fed..51780329590c 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -24,9 +24,8 @@ static const struct address_space_operations kernfs_aops = {
};

static const struct inode_operations kernfs_iops = {
- .permission = kernfs_iop_permission,
+ .update_time = kernfs_update_time,
.setattr = kernfs_iop_setattr,
- .getattr = kernfs_iop_getattr,
.listxattr = kernfs_iop_listxattr,
};

@@ -183,18 +182,26 @@ static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
set_nlink(inode, kn->dir.subdirs + 2);
}

-int kernfs_iop_getattr(const struct path *path, struct kstat *stat,
- u32 request_mask, unsigned int query_flags)
+static int kernfs_iop_update_time(struct inode *inode, struct timespec64 *time, int flags)
{
- struct inode *inode = d_inode(path->dentry);
struct kernfs_node *kn = inode->i_private;
+ struct kernfs_iattrs *attrs;

mutex_lock(&kernfs_mutex);
+ attrs = kernfs_iattrs(kn);
+ if (!attrs) {
+ mutex_unlock(&kernfs_mutex);
+ return -ENOMEM;
+ }
+
+ /* Which kernfs node attributes should be updated from
+ * time?
+ */
+
kernfs_refresh_inode(kn, inode);
mutex_unlock(&kernfs_mutex);

- generic_fillattr(inode, stat);
- return 0;
+ return 0
}

static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
@@ -272,22 +279,6 @@ void kernfs_evict_inode(struct inode *inode)
kernfs_put(kn);
}

-int kernfs_iop_permission(struct inode *inode, int mask)
-{
- struct kernfs_node *kn;
-
- if (mask & MAY_NOT_BLOCK)
- return -ECHILD;
-
- kn = inode->i_private;
-
- mutex_lock(&kernfs_mutex);
- kernfs_refresh_inode(kn, inode);
- mutex_unlock(&kernfs_mutex);
-
- return generic_permission(inode, mask);
-}
-
int kernfs_xattr_get(struct kernfs_node *kn, const char *name,
void *value, size_t size)
{
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index 7ee97ef59184..98d08b928f93 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -89,10 +89,8 @@ extern struct kmem_cache *kernfs_node_cache, *kernfs_iattrs_cache;
*/
extern const struct xattr_handler *kernfs_xattr_handlers[];
void kernfs_evict_inode(struct inode *inode);
-int kernfs_iop_permission(struct inode *inode, int mask);
+int kernfs_update_time(struct inode *inode, struct timespec64 *time, int flags);
int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr);
-int kernfs_iop_getattr(const struct path *path, struct kstat *stat,
- u32 request_mask, unsigned int query_flags);
ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size);
int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr);