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: Tejun Heo <tj@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Minchan Kim <minchan@kernel.org>
Link: https://lore.kernel.org/r/20211118230008.2679780-1-minchan@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Minchan Kim 2021-11-18 15:00:08 -08:00 committed by Greg Kroah-Hartman
parent 1360572566
commit 393c371408
6 changed files with 97 additions and 63 deletions

View File

@ -17,7 +17,6 @@
#include "kernfs-internal.h" #include "kernfs-internal.h"
DECLARE_RWSEM(kernfs_rwsem);
static DEFINE_SPINLOCK(kernfs_rename_lock); /* kn->parent and ->name */ static DEFINE_SPINLOCK(kernfs_rename_lock); /* kn->parent and ->name */
static char kernfs_pr_cont_buf[PATH_MAX]; /* protected by rename_lock */ static char kernfs_pr_cont_buf[PATH_MAX]; /* protected by rename_lock */
static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */ static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */
@ -26,7 +25,7 @@ static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */
static bool kernfs_active(struct kernfs_node *kn) static bool kernfs_active(struct kernfs_node *kn)
{ {
lockdep_assert_held(&kernfs_rwsem); lockdep_assert_held(&kernfs_root(kn)->kernfs_rwsem);
return atomic_read(&kn->active) >= 0; return atomic_read(&kn->active) >= 0;
} }
@ -457,14 +456,15 @@ void kernfs_put_active(struct kernfs_node *kn)
* return after draining is complete. * return after draining is complete.
*/ */
static void kernfs_drain(struct kernfs_node *kn) static void kernfs_drain(struct kernfs_node *kn)
__releases(&kernfs_rwsem) __acquires(&kernfs_rwsem) __releases(&kernfs_root(kn)->kernfs_rwsem)
__acquires(&kernfs_root(kn)->kernfs_rwsem)
{ {
struct kernfs_root *root = kernfs_root(kn); struct kernfs_root *root = kernfs_root(kn);
lockdep_assert_held_write(&kernfs_rwsem); lockdep_assert_held_write(&root->kernfs_rwsem);
WARN_ON_ONCE(kernfs_active(kn)); WARN_ON_ONCE(kernfs_active(kn));
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
if (kernfs_lockdep(kn)) { if (kernfs_lockdep(kn)) {
rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
@ -483,7 +483,7 @@ static void kernfs_drain(struct kernfs_node *kn)
kernfs_drain_open_files(kn); kernfs_drain_open_files(kn);
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
} }
/** /**
@ -718,11 +718,12 @@ err_unlock:
int kernfs_add_one(struct kernfs_node *kn) int kernfs_add_one(struct kernfs_node *kn)
{ {
struct kernfs_node *parent = kn->parent; struct kernfs_node *parent = kn->parent;
struct kernfs_root *root = kernfs_root(parent);
struct kernfs_iattrs *ps_iattr; struct kernfs_iattrs *ps_iattr;
bool has_ns; bool has_ns;
int ret; int ret;
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
ret = -EINVAL; ret = -EINVAL;
has_ns = kernfs_ns_enabled(parent); has_ns = kernfs_ns_enabled(parent);
@ -753,7 +754,7 @@ int kernfs_add_one(struct kernfs_node *kn)
ps_iattr->ia_mtime = ps_iattr->ia_ctime; ps_iattr->ia_mtime = ps_iattr->ia_ctime;
} }
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
/* /*
* Activate the new node unless CREATE_DEACTIVATED is requested. * Activate the new node unless CREATE_DEACTIVATED is requested.
@ -767,7 +768,7 @@ int kernfs_add_one(struct kernfs_node *kn)
return 0; return 0;
out_unlock: out_unlock:
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
return ret; return ret;
} }
@ -788,7 +789,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
bool has_ns = kernfs_ns_enabled(parent); bool has_ns = kernfs_ns_enabled(parent);
unsigned int hash; unsigned int hash;
lockdep_assert_held(&kernfs_rwsem); lockdep_assert_held(&kernfs_root(parent)->kernfs_rwsem);
if (has_ns != (bool)ns) { if (has_ns != (bool)ns) {
WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
@ -820,7 +821,7 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
size_t len; size_t len;
char *p, *name; char *p, *name;
lockdep_assert_held_read(&kernfs_rwsem); lockdep_assert_held_read(&kernfs_root(parent)->kernfs_rwsem);
/* grab kernfs_rename_lock to piggy back on kernfs_pr_cont_buf */ /* grab kernfs_rename_lock to piggy back on kernfs_pr_cont_buf */
spin_lock_irq(&kernfs_rename_lock); spin_lock_irq(&kernfs_rename_lock);
@ -859,11 +860,12 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
const char *name, const void *ns) const char *name, const void *ns)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_root *root = kernfs_root(parent);
down_read(&kernfs_rwsem); down_read(&root->kernfs_rwsem);
kn = kernfs_find_ns(parent, name, ns); kn = kernfs_find_ns(parent, name, ns);
kernfs_get(kn); kernfs_get(kn);
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return kn; return kn;
} }
@ -883,11 +885,12 @@ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
const char *path, const void *ns) const char *path, const void *ns)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_root *root = kernfs_root(parent);
down_read(&kernfs_rwsem); down_read(&root->kernfs_rwsem);
kn = kernfs_walk_ns(parent, path, ns); kn = kernfs_walk_ns(parent, path, ns);
kernfs_get(kn); kernfs_get(kn);
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return kn; return kn;
} }
@ -912,6 +915,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
idr_init(&root->ino_idr); idr_init(&root->ino_idr);
init_rwsem(&root->kernfs_rwsem);
INIT_LIST_HEAD(&root->supers); INIT_LIST_HEAD(&root->supers);
/* /*
@ -1035,6 +1039,7 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_root *root;
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
return -ECHILD; return -ECHILD;
@ -1046,18 +1051,19 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
/* If the kernfs parent node has changed discard and /* If the kernfs parent node has changed discard and
* proceed to ->lookup. * proceed to ->lookup.
*/ */
down_read(&kernfs_rwsem);
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
parent = kernfs_dentry_node(dentry->d_parent); parent = kernfs_dentry_node(dentry->d_parent);
if (parent) { if (parent) {
spin_unlock(&dentry->d_lock);
root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
if (kernfs_dir_changed(parent, dentry)) { if (kernfs_dir_changed(parent, dentry)) {
spin_unlock(&dentry->d_lock); up_read(&root->kernfs_rwsem);
up_read(&kernfs_rwsem);
return 0; return 0;
} }
} up_read(&root->kernfs_rwsem);
spin_unlock(&dentry->d_lock); } else
up_read(&kernfs_rwsem); spin_unlock(&dentry->d_lock);
/* The kernfs parent node hasn't changed, leave the /* The kernfs parent node hasn't changed, leave the
* dentry negative and return success. * dentry negative and return success.
@ -1066,7 +1072,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
} }
kn = kernfs_dentry_node(dentry); kn = kernfs_dentry_node(dentry);
down_read(&kernfs_rwsem); root = kernfs_root(kn);
down_read(&root->kernfs_rwsem);
/* The kernfs node has been deactivated */ /* The kernfs node has been deactivated */
if (!kernfs_active(kn)) if (!kernfs_active(kn))
@ -1085,10 +1092,10 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
kernfs_info(dentry->d_sb)->ns != kn->ns) kernfs_info(dentry->d_sb)->ns != kn->ns)
goto out_bad; goto out_bad;
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return 1; return 1;
out_bad: out_bad:
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return 0; return 0;
} }
@ -1102,10 +1109,12 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
{ {
struct kernfs_node *parent = dir->i_private; struct kernfs_node *parent = dir->i_private;
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_root *root;
struct inode *inode = NULL; struct inode *inode = NULL;
const void *ns = NULL; const void *ns = NULL;
down_read(&kernfs_rwsem); root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
if (kernfs_ns_enabled(parent)) if (kernfs_ns_enabled(parent))
ns = kernfs_info(dir->i_sb)->ns; ns = kernfs_info(dir->i_sb)->ns;
@ -1116,7 +1125,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
* create a negative. * create a negative.
*/ */
if (!kernfs_active(kn)) { if (!kernfs_active(kn)) {
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
return NULL; return NULL;
} }
inode = kernfs_get_inode(dir->i_sb, kn); inode = kernfs_get_inode(dir->i_sb, kn);
@ -1131,7 +1140,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
*/ */
if (!IS_ERR(inode)) if (!IS_ERR(inode))
kernfs_set_rev(parent, dentry); kernfs_set_rev(parent, dentry);
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
/* instantiate and hash (possibly negative) dentry */ /* instantiate and hash (possibly negative) dentry */
return d_splice_alias(inode, dentry); return d_splice_alias(inode, dentry);
@ -1254,7 +1263,7 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
{ {
struct rb_node *rbn; struct rb_node *rbn;
lockdep_assert_held_write(&kernfs_rwsem); lockdep_assert_held_write(&kernfs_root(root)->kernfs_rwsem);
/* if first iteration, visit leftmost descendant which may be root */ /* if first iteration, visit leftmost descendant which may be root */
if (!pos) if (!pos)
@ -1289,8 +1298,9 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
void kernfs_activate(struct kernfs_node *kn) void kernfs_activate(struct kernfs_node *kn)
{ {
struct kernfs_node *pos; struct kernfs_node *pos;
struct kernfs_root *root = kernfs_root(kn);
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
pos = NULL; pos = NULL;
while ((pos = kernfs_next_descendant_post(pos, kn))) { while ((pos = kernfs_next_descendant_post(pos, kn))) {
@ -1304,14 +1314,14 @@ void kernfs_activate(struct kernfs_node *kn)
pos->flags |= KERNFS_ACTIVATED; pos->flags |= KERNFS_ACTIVATED;
} }
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
} }
static void __kernfs_remove(struct kernfs_node *kn) static void __kernfs_remove(struct kernfs_node *kn)
{ {
struct kernfs_node *pos; struct kernfs_node *pos;
lockdep_assert_held_write(&kernfs_rwsem); lockdep_assert_held_write(&kernfs_root(kn)->kernfs_rwsem);
/* /*
* Short-circuit if non-root @kn has already finished removal. * Short-circuit if non-root @kn has already finished removal.
@ -1381,9 +1391,11 @@ static void __kernfs_remove(struct kernfs_node *kn)
*/ */
void kernfs_remove(struct kernfs_node *kn) void kernfs_remove(struct kernfs_node *kn)
{ {
down_write(&kernfs_rwsem); struct kernfs_root *root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
__kernfs_remove(kn); __kernfs_remove(kn);
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
} }
/** /**
@ -1469,8 +1481,9 @@ void kernfs_unbreak_active_protection(struct kernfs_node *kn)
bool kernfs_remove_self(struct kernfs_node *kn) bool kernfs_remove_self(struct kernfs_node *kn)
{ {
bool ret; bool ret;
struct kernfs_root *root = kernfs_root(kn);
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
kernfs_break_active_protection(kn); kernfs_break_active_protection(kn);
/* /*
@ -1498,9 +1511,9 @@ bool kernfs_remove_self(struct kernfs_node *kn)
atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) atomic_read(&kn->active) == KN_DEACTIVATED_BIAS)
break; break;
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
schedule(); schedule();
down_write(&kernfs_rwsem); down_write(&root->kernfs_rwsem);
} }
finish_wait(waitq, &wait); finish_wait(waitq, &wait);
WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb)); WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb));
@ -1513,7 +1526,7 @@ bool kernfs_remove_self(struct kernfs_node *kn)
*/ */
kernfs_unbreak_active_protection(kn); kernfs_unbreak_active_protection(kn);
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
return ret; return ret;
} }
@ -1530,6 +1543,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
const void *ns) const void *ns)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_root *root;
if (!parent) { if (!parent) {
WARN(1, KERN_WARNING "kernfs: can not remove '%s', no directory\n", WARN(1, KERN_WARNING "kernfs: can not remove '%s', no directory\n",
@ -1537,13 +1551,14 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
return -ENOENT; return -ENOENT;
} }
down_write(&kernfs_rwsem); root = kernfs_root(parent);
down_write(&root->kernfs_rwsem);
kn = kernfs_find_ns(parent, name, ns); kn = kernfs_find_ns(parent, name, ns);
if (kn) if (kn)
__kernfs_remove(kn); __kernfs_remove(kn);
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
if (kn) if (kn)
return 0; return 0;
@ -1562,6 +1577,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
const char *new_name, const void *new_ns) const char *new_name, const void *new_ns)
{ {
struct kernfs_node *old_parent; struct kernfs_node *old_parent;
struct kernfs_root *root;
const char *old_name = NULL; const char *old_name = NULL;
int error; int error;
@ -1569,7 +1585,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
if (!kn->parent) if (!kn->parent)
return -EINVAL; return -EINVAL;
down_write(&kernfs_rwsem); root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
error = -ENOENT; error = -ENOENT;
if (!kernfs_active(kn) || !kernfs_active(new_parent) || if (!kernfs_active(kn) || !kernfs_active(new_parent) ||
@ -1623,7 +1640,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
error = 0; error = 0;
out: out:
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
return error; return error;
} }
@ -1694,11 +1711,14 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
struct kernfs_node *parent = kernfs_dentry_node(dentry); struct kernfs_node *parent = kernfs_dentry_node(dentry);
struct kernfs_node *pos = file->private_data; struct kernfs_node *pos = file->private_data;
struct kernfs_root *root;
const void *ns = NULL; const void *ns = NULL;
if (!dir_emit_dots(file, ctx)) if (!dir_emit_dots(file, ctx))
return 0; return 0;
down_read(&kernfs_rwsem);
root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
if (kernfs_ns_enabled(parent)) if (kernfs_ns_enabled(parent))
ns = kernfs_info(dentry->d_sb)->ns; ns = kernfs_info(dentry->d_sb)->ns;
@ -1715,12 +1735,12 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
file->private_data = pos; file->private_data = pos;
kernfs_get(pos); kernfs_get(pos);
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
if (!dir_emit(ctx, name, len, ino, type)) if (!dir_emit(ctx, name, len, ino, type))
return 0; return 0;
down_read(&kernfs_rwsem); down_read(&root->kernfs_rwsem);
} }
up_read(&kernfs_rwsem); up_read(&root->kernfs_rwsem);
file->private_data = NULL; file->private_data = NULL;
ctx->pos = INT_MAX; ctx->pos = INT_MAX;
return 0; return 0;

View File

@ -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 @@ repeat:
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 @@ repeat:
iput(inode); iput(inode);
} }
up_write(&kernfs_rwsem); up_write(&root->kernfs_rwsem);
kernfs_put(kn); kernfs_put(kn);
goto repeat; goto repeat;
} }

View File

@ -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,
{ {
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,
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,
{ {
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,
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;
} }

View File

@ -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
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)
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)
} }
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)
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

View File

@ -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;
} }

View File

@ -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 {
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 {