[RFC PATCH] kernfs: release kernfs_mutex before the inode allocation

From: Minchan Kim
Date: Tue Nov 16 2021 - 14:43:25 EST


The kernfs implementation has big lock granularity(kernfs_rwsem) so
every kernfs-based(e.g., sysfs, cgroup, dmabuf) fs are able to compete
the lock. Thus, if one of userspace goes the sleep under holding
the lock for a long time, rest of them should wait it. A example is
the holder goes direct reclaim with the lock since it needs memory
allocation. Let's fix it at common technique that release the lock
and then allocate the memory. Fortunately, kernfs looks like have
an refcount so I hope it's fine.

Signed-off-by: Minchan Kim <minchan@xxxxxxxxxx>
---
fs/kernfs/dir.c | 14 +++++++++++---
fs/kernfs/inode.c | 2 +-
fs/kernfs/kernfs-internal.h | 1 +
3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 8e0a1378a4b1..ecdb2975060d 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1119,9 +1119,17 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
up_read(&kernfs_rwsem);
return NULL;
}
- inode = kernfs_get_inode(dir->i_sb, kn);
- if (!inode)
- inode = ERR_PTR(-ENOMEM);
+ kernfs_get(kn);
+ up_read(&kernfs_rwsem);
+ inode = iget_locked(dir->i_sb, kernfs_ino(kn));
+ if (!inode) {
+ kernfs_put(kn);
+ return ERR_PTR(-ENOMEM);
+ }
+ down_read(&kernfs_rwsem);
+ if (inode->i_state & I_NEW)
+ kernfs_init_inode(kn, inode);
+ kernfs_put(kn);
}
/*
* Needed for negative dentry validation.
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index c0eae1725435..6e2004010435 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -195,7 +195,7 @@ int kernfs_iop_getattr(struct user_namespace *mnt_userns,
return 0;
}

-static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
+void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
{
kernfs_get(kn);
inode->i_private = kn;
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index f9cc912c31e1..eef7656f7cd8 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -118,6 +118,7 @@ int kernfs_iop_getattr(struct user_namespace *mnt_userns,
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);
+void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode);

/*
* dir.c
--
2.34.0.rc1.387.gb447b232ab-goog