Commit 393c3714 authored by Minchan Kim's avatar Minchan Kim Committed by Greg Kroah-Hartman

kernfs: switch global kernfs_rwsem lock to per-fs lock

The kernfs implementation has big lock granularity(kernfs_rwsem) so
every kernfs-based(e.g., sysfs, cgroup) fs are able to compete the
lock. It makes trouble for some cases to wait the global lock
for a long time even though they are totally independent contexts
each other.

A general example is process A goes under direct reclaim with holding
the lock when it accessed the file in sysfs and process B is waiting
the lock with exclusive mode and then process C is waiting the lock
until process B could finish the job after it gets the lock from
process A.

This patch switches the global kernfs_rwsem to per-fs lock, which
put the rwsem into kernfs_root.
Suggested-by: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarMinchan Kim <minchan@kernel.org>
Link: https://lore.kernel.org/r/20211118230008.2679780-1-minchan@kernel.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 13605725
This diff is collapsed.
...@@ -847,6 +847,7 @@ static void kernfs_notify_workfn(struct work_struct *work) ...@@ -847,6 +847,7 @@ static void kernfs_notify_workfn(struct work_struct *work)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_super_info *info; struct kernfs_super_info *info;
struct kernfs_root *root;
repeat: repeat:
/* pop one off the notify_list */ /* pop one off the notify_list */
spin_lock_irq(&kernfs_notify_lock); spin_lock_irq(&kernfs_notify_lock);
...@@ -859,8 +860,9 @@ static void kernfs_notify_workfn(struct work_struct *work) ...@@ -859,8 +860,9 @@ static void kernfs_notify_workfn(struct work_struct *work)
kn->attr.notify_next = NULL; kn->attr.notify_next = NULL;
spin_unlock_irq(&kernfs_notify_lock); spin_unlock_irq(&kernfs_notify_lock);
root = kernfs_root(kn);
/* kick fsnotify */ /* kick fsnotify */
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
list_for_each_entry(info, &kernfs_root(kn)->supers, node) { list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
struct kernfs_node *parent; struct kernfs_node *parent;
...@@ -898,7 +900,7 @@ static void kernfs_notify_workfn(struct work_struct *work) ...@@ -898,7 +900,7 @@ static void kernfs_notify_workfn(struct work_struct *work)
iput(inode); iput(inode);
} }
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
kernfs_put(kn); kernfs_put(kn);
goto repeat; goto repeat;
} }
......
...@@ -99,10 +99,11 @@ int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) ...@@ -99,10 +99,11 @@ int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr)
int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr)
{ {
int ret; int ret;
struct kernfs_root *root = kernfs_root(kn);
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
ret = __kernfs_setattr(kn, iattr); ret = __kernfs_setattr(kn, iattr);
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
return ret; return ret;
} }
...@@ -111,12 +112,14 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -111,12 +112,14 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
{ {
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct kernfs_node *kn = inode->i_private; struct kernfs_node *kn = inode->i_private;
struct kernfs_root *root;
int error; int error;
if (!kn) if (!kn)
return -EINVAL; return -EINVAL;
down_write(&kernfs_rwsem); root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
error = setattr_prepare(&init_user_ns, dentry, iattr); error = setattr_prepare(&init_user_ns, dentry, iattr);
if (error) if (error)
goto out; goto out;
...@@ -129,7 +132,7 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -129,7 +132,7 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
setattr_copy(&init_user_ns, inode, iattr); setattr_copy(&init_user_ns, inode, iattr);
out: out:
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
return error; return error;
} }
...@@ -184,13 +187,14 @@ int kernfs_iop_getattr(struct user_namespace *mnt_userns, ...@@ -184,13 +187,14 @@ int kernfs_iop_getattr(struct user_namespace *mnt_userns,
{ {
struct inode *inode = d_inode(path->dentry); struct inode *inode = d_inode(path->dentry);
struct kernfs_node *kn = inode->i_private; struct kernfs_node *kn = inode->i_private;
struct kernfs_root *root = kernfs_root(kn);
down_read(&kernfs_rwsem); down_read(&root->kernfs_rwsem);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
kernfs_refresh_inode(kn, inode); kernfs_refresh_inode(kn, inode);
generic_fillattr(&init_user_ns, inode, stat); generic_fillattr(&init_user_ns, inode, stat);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return 0; return 0;
} }
...@@ -274,19 +278,21 @@ int kernfs_iop_permission(struct user_namespace *mnt_userns, ...@@ -274,19 +278,21 @@ int kernfs_iop_permission(struct user_namespace *mnt_userns,
struct inode *inode, int mask) struct inode *inode, int mask)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_root *root;
int ret; int ret;
if (mask & MAY_NOT_BLOCK) if (mask & MAY_NOT_BLOCK)
return -ECHILD; return -ECHILD;
kn = inode->i_private; kn = inode->i_private;
root = kernfs_root(kn);
down_read(&kernfs_rwsem); down_read(&root->kernfs_rwsem);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
kernfs_refresh_inode(kn, inode); kernfs_refresh_inode(kn, inode);
ret = generic_permission(&init_user_ns, inode, mask); ret = generic_permission(&init_user_ns, inode, mask);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return ret; return ret;
} }
......
...@@ -236,6 +236,7 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn, ...@@ -236,6 +236,7 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *kfc) static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *kfc)
{ {
struct kernfs_super_info *info = kernfs_info(sb); struct kernfs_super_info *info = kernfs_info(sb);
struct kernfs_root *kf_root = kfc->root;
struct inode *inode; struct inode *inode;
struct dentry *root; struct dentry *root;
...@@ -255,9 +256,9 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k ...@@ -255,9 +256,9 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
sb->s_shrink.seeks = 0; sb->s_shrink.seeks = 0;
/* get root inode, initialize and unlock it */ /* get root inode, initialize and unlock it */
down_read(&kernfs_rwsem); down_read(&kf_root->kernfs_rwsem);
inode = kernfs_get_inode(sb, info->root->kn); inode = kernfs_get_inode(sb, info->root->kn);
up_read(&kernfs_rwsem); up_read(&kf_root->kernfs_rwsem);
if (!inode) { if (!inode) {
pr_debug("kernfs: could not get root inode\n"); pr_debug("kernfs: could not get root inode\n");
return -ENOMEM; return -ENOMEM;
...@@ -334,6 +335,7 @@ int kernfs_get_tree(struct fs_context *fc) ...@@ -334,6 +335,7 @@ int kernfs_get_tree(struct fs_context *fc)
if (!sb->s_root) { if (!sb->s_root) {
struct kernfs_super_info *info = kernfs_info(sb); struct kernfs_super_info *info = kernfs_info(sb);
struct kernfs_root *root = kfc->root;
kfc->new_sb_created = true; kfc->new_sb_created = true;
...@@ -344,9 +346,9 @@ int kernfs_get_tree(struct fs_context *fc) ...@@ -344,9 +346,9 @@ int kernfs_get_tree(struct fs_context *fc)
} }
sb->s_flags |= SB_ACTIVE; sb->s_flags |= SB_ACTIVE;
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
list_add(&info->node, &info->root->supers); list_add(&info->node, &info->root->supers);
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
} }
fc->root = dget(sb->s_root); fc->root = dget(sb->s_root);
...@@ -371,10 +373,11 @@ void kernfs_free_fs_context(struct fs_context *fc) ...@@ -371,10 +373,11 @@ void kernfs_free_fs_context(struct fs_context *fc)
void kernfs_kill_sb(struct super_block *sb) void kernfs_kill_sb(struct super_block *sb)
{ {
struct kernfs_super_info *info = kernfs_info(sb); struct kernfs_super_info *info = kernfs_info(sb);
struct kernfs_root *root = info->root;
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
list_del(&info->node); list_del(&info->node);
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
/* /*
* Remove the superblock from fs_supers/s_instances * Remove the superblock from fs_supers/s_instances
......
...@@ -113,11 +113,12 @@ static int kernfs_getlink(struct inode *inode, char *path) ...@@ -113,11 +113,12 @@ static int kernfs_getlink(struct inode *inode, char *path)
struct kernfs_node *kn = inode->i_private; struct kernfs_node *kn = inode->i_private;
struct kernfs_node *parent = kn->parent; struct kernfs_node *parent = kn->parent;
struct kernfs_node *target = kn->symlink.target_kn; struct kernfs_node *target = kn->symlink.target_kn;
struct kernfs_root *root = kernfs_root(parent);
int error; int error;
down_read(&kernfs_rwsem); down_read(&root->kernfs_rwsem);
error = kernfs_get_target_path(parent, target, path); error = kernfs_get_target_path(parent, target, path);
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return error; return error;
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/uidgid.h> #include <linux/uidgid.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/rwsem.h>
struct file; struct file;
struct dentry; struct dentry;
...@@ -197,6 +198,7 @@ struct kernfs_root { ...@@ -197,6 +198,7 @@ struct kernfs_root {
struct list_head supers; struct list_head supers;
wait_queue_head_t deactivate_waitq; wait_queue_head_t deactivate_waitq;
struct rw_semaphore kernfs_rwsem;
}; };
struct kernfs_open_file { struct kernfs_open_file {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment