
This is a backport of a collection of 12 upstream patches. The main one being the switch to use a rwsem instead. The next important one being the switch of the rwsem to be a per filesystem lock instead of global. See the individual patches for details. They did not require much work or wiggling to get them applied. They all come from Linus' tree and are easily located. As such I have not modified their individual headers with upstream commit ids. Verification: - two scripts, the concept behind them supplied by Vefa Bicakci. The first one causes a lot of concurrent contention in sysfs. The second script highlights how well systemd is also contending. Run Script1 followed by Script2 Without this change, Script2 has timeouts and fails. Script1: for i in `seq 20`; do (while :; do find /sys/fs/cgroup/ -type f -readable -print0 \ 2>/dev/null | xargs -0 -n 20 -r cat >&/dev/null ; done) & done for i in `seq 10`; do (while :; do systemd-run --scope -q sleep 0.5 >/dev/null; done) & done Script2: while true; do date -Is /usr/bin/time -f %e systemctl enable -q lighttpd.service || break /usr/bin/time -f %e systemctl disable -q lighttpd.service || break /usr/bin/time -f %e systemctl restart -q lighttpd.service || break sleep 0.5 || break done - also soak testing to ensure that these patches don't introduce issues Partial-Bug: 2016028 Signed-off-by: Jim Somerville <jim.somerville@windriver.com> Change-Id: I6ad64cd7c90f756c6eb904065febfeb516e73009
577 lines
17 KiB
Diff
577 lines
17 KiB
Diff
From ab1b2b46cfa230d14cbe6a02f4eee34ed9152f7c Mon Sep 17 00:00:00 2001
|
|
From: Ian Kent <raven@themaw.net>
|
|
Date: Fri, 16 Jul 2021 17:28:29 +0800
|
|
Subject: [PATCH] kernfs: switch kernfs to use an rwsem
|
|
|
|
The kernfs global lock restricts the ability to perform kernfs node
|
|
lookup operations in parallel during path walks.
|
|
|
|
Change the kernfs mutex to an rwsem so that, when opportunity arises,
|
|
node searches can be done in parallel with path walk lookups.
|
|
|
|
Reviewed-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
Signed-off-by: Ian Kent <raven@themaw.net>
|
|
Link: https://lore.kernel.org/r/162642770946.63632.2218304587223241374.stgit@web.messagingengine.com
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
Signed-off-by: Jim Somerville <jim.somerville@windriver.com>
|
|
---
|
|
fs/kernfs/dir.c | 100 ++++++++++++++++++------------------
|
|
fs/kernfs/file.c | 4 +-
|
|
fs/kernfs/inode.c | 16 +++---
|
|
fs/kernfs/kernfs-internal.h | 5 +-
|
|
fs/kernfs/mount.c | 12 ++---
|
|
fs/kernfs/symlink.c | 4 +-
|
|
include/linux/kernfs.h | 2 +-
|
|
7 files changed, 72 insertions(+), 71 deletions(-)
|
|
|
|
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
|
|
index 0bbd09af8520..cdc57ab2e5ea 100644
|
|
--- a/fs/kernfs/dir.c
|
|
+++ b/fs/kernfs/dir.c
|
|
@@ -17,7 +17,7 @@
|
|
|
|
#include "kernfs-internal.h"
|
|
|
|
-DEFINE_MUTEX(kernfs_mutex);
|
|
+DECLARE_RWSEM(kernfs_rwsem);
|
|
static DEFINE_SPINLOCK(kernfs_rename_lock); /* kn->parent and ->name */
|
|
/*
|
|
* Don't use rename_lock to piggy back on pr_cont_buf. We don't want to
|
|
@@ -34,7 +34,7 @@ static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */
|
|
|
|
static bool kernfs_active(struct kernfs_node *kn)
|
|
{
|
|
- lockdep_assert_held(&kernfs_mutex);
|
|
+ lockdep_assert_held(&kernfs_rwsem);
|
|
return atomic_read(&kn->active) >= 0;
|
|
}
|
|
|
|
@@ -348,7 +348,7 @@ static int kernfs_sd_compare(const struct kernfs_node *left,
|
|
* @kn->parent->dir.children.
|
|
*
|
|
* Locking:
|
|
- * mutex_lock(kernfs_mutex)
|
|
+ * kernfs_rwsem held exclusive
|
|
*
|
|
* RETURNS:
|
|
* 0 on susccess -EEXIST on failure.
|
|
@@ -394,7 +394,7 @@ static int kernfs_link_sibling(struct kernfs_node *kn)
|
|
* removed, %false if @kn wasn't on the rbtree.
|
|
*
|
|
* Locking:
|
|
- * mutex_lock(kernfs_mutex)
|
|
+ * kernfs_rwsem held exclusive
|
|
*/
|
|
static bool kernfs_unlink_sibling(struct kernfs_node *kn)
|
|
{
|
|
@@ -465,14 +465,14 @@ void kernfs_put_active(struct kernfs_node *kn)
|
|
* return after draining is complete.
|
|
*/
|
|
static void kernfs_drain(struct kernfs_node *kn)
|
|
- __releases(&kernfs_mutex) __acquires(&kernfs_mutex)
|
|
+ __releases(&kernfs_rwsem) __acquires(&kernfs_rwsem)
|
|
{
|
|
struct kernfs_root *root = kernfs_root(kn);
|
|
|
|
- lockdep_assert_held(&kernfs_mutex);
|
|
+ lockdep_assert_held_write(&kernfs_rwsem);
|
|
WARN_ON_ONCE(kernfs_active(kn));
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
|
|
if (kernfs_lockdep(kn)) {
|
|
rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
|
|
@@ -491,7 +491,7 @@ static void kernfs_drain(struct kernfs_node *kn)
|
|
|
|
kernfs_drain_open_files(kn);
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
}
|
|
|
|
/**
|
|
@@ -731,7 +731,7 @@ int kernfs_add_one(struct kernfs_node *kn)
|
|
bool has_ns;
|
|
int ret;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
|
|
ret = -EINVAL;
|
|
has_ns = kernfs_ns_enabled(parent);
|
|
@@ -762,7 +762,7 @@ int kernfs_add_one(struct kernfs_node *kn)
|
|
ps_iattr->ia_mtime = ps_iattr->ia_ctime;
|
|
}
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
|
|
/*
|
|
* Activate the new node unless CREATE_DEACTIVATED is requested.
|
|
@@ -776,7 +776,7 @@ int kernfs_add_one(struct kernfs_node *kn)
|
|
return 0;
|
|
|
|
out_unlock:
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
return ret;
|
|
}
|
|
|
|
@@ -797,7 +797,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
|
|
bool has_ns = kernfs_ns_enabled(parent);
|
|
unsigned int hash;
|
|
|
|
- lockdep_assert_held(&kernfs_mutex);
|
|
+ lockdep_assert_held(&kernfs_rwsem);
|
|
|
|
if (has_ns != (bool)ns) {
|
|
WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
|
|
@@ -829,7 +829,7 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
|
|
size_t len;
|
|
char *p, *name;
|
|
|
|
- lockdep_assert_held(&kernfs_mutex);
|
|
+ lockdep_assert_held_read(&kernfs_rwsem);
|
|
|
|
spin_lock_irq(&kernfs_pr_cont_lock);
|
|
|
|
@@ -868,10 +868,10 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
|
|
{
|
|
struct kernfs_node *kn;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
kn = kernfs_find_ns(parent, name, ns);
|
|
kernfs_get(kn);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
|
|
return kn;
|
|
}
|
|
@@ -892,10 +892,10 @@ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
|
|
{
|
|
struct kernfs_node *kn;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
kn = kernfs_walk_ns(parent, path, ns);
|
|
kernfs_get(kn);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
|
|
return kn;
|
|
}
|
|
@@ -1054,18 +1054,18 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
|
|
/* If the kernfs parent node has changed discard and
|
|
* proceed to ->lookup.
|
|
*/
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
spin_lock(&dentry->d_lock);
|
|
parent = kernfs_dentry_node(dentry->d_parent);
|
|
if (parent) {
|
|
if (kernfs_dir_changed(parent, dentry)) {
|
|
spin_unlock(&dentry->d_lock);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
return 0;
|
|
}
|
|
}
|
|
spin_unlock(&dentry->d_lock);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
|
|
/* The kernfs parent node hasn't changed, leave the
|
|
* dentry negative and return success.
|
|
@@ -1074,7 +1074,7 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
|
|
}
|
|
|
|
kn = kernfs_dentry_node(dentry);
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
|
|
/* The kernfs node has been deactivated */
|
|
if (!kernfs_active(kn))
|
|
@@ -1093,10 +1093,10 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
|
|
kernfs_info(dentry->d_sb)->ns != kn->ns)
|
|
goto out_bad;
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
return 1;
|
|
out_bad:
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
return 0;
|
|
}
|
|
|
|
@@ -1114,7 +1114,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
|
|
struct inode *inode = NULL;
|
|
const void *ns = NULL;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
if (kernfs_ns_enabled(parent))
|
|
ns = kernfs_info(dir->i_sb)->ns;
|
|
|
|
@@ -1130,7 +1130,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
|
|
kernfs_set_rev(parent, dentry);
|
|
/* instantiate and hash (possibly negative) dentry */
|
|
ret = d_splice_alias(inode, dentry);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
|
|
return ret;
|
|
}
|
|
@@ -1250,7 +1250,7 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
|
|
{
|
|
struct rb_node *rbn;
|
|
|
|
- lockdep_assert_held(&kernfs_mutex);
|
|
+ lockdep_assert_held_write(&kernfs_rwsem);
|
|
|
|
/* if first iteration, visit leftmost descendant which may be root */
|
|
if (!pos)
|
|
@@ -1286,7 +1286,7 @@ void kernfs_activate(struct kernfs_node *kn)
|
|
{
|
|
struct kernfs_node *pos;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
|
|
pos = NULL;
|
|
while ((pos = kernfs_next_descendant_post(pos, kn))) {
|
|
@@ -1300,14 +1300,14 @@ void kernfs_activate(struct kernfs_node *kn)
|
|
pos->flags |= KERNFS_ACTIVATED;
|
|
}
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
}
|
|
|
|
static void __kernfs_remove(struct kernfs_node *kn)
|
|
{
|
|
struct kernfs_node *pos;
|
|
|
|
- lockdep_assert_held(&kernfs_mutex);
|
|
+ lockdep_assert_held_write(&kernfs_rwsem);
|
|
|
|
/*
|
|
* Short-circuit if non-root @kn has already finished removal.
|
|
@@ -1330,7 +1330,7 @@ static void __kernfs_remove(struct kernfs_node *kn)
|
|
pos = kernfs_leftmost_descendant(kn);
|
|
|
|
/*
|
|
- * kernfs_drain() drops kernfs_mutex temporarily and @pos's
|
|
+ * kernfs_drain() drops kernfs_rwsem temporarily and @pos's
|
|
* base ref could have been put by someone else by the time
|
|
* the function returns. Make sure it doesn't go away
|
|
* underneath us.
|
|
@@ -1377,9 +1377,9 @@ static void __kernfs_remove(struct kernfs_node *kn)
|
|
*/
|
|
void kernfs_remove(struct kernfs_node *kn)
|
|
{
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
__kernfs_remove(kn);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
}
|
|
|
|
/**
|
|
@@ -1466,17 +1466,17 @@ bool kernfs_remove_self(struct kernfs_node *kn)
|
|
{
|
|
bool ret;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
kernfs_break_active_protection(kn);
|
|
|
|
/*
|
|
* SUICIDAL is used to arbitrate among competing invocations. Only
|
|
* the first one will actually perform removal. When the removal
|
|
* is complete, SUICIDED is set and the active ref is restored
|
|
- * while holding kernfs_mutex. The ones which lost arbitration
|
|
- * waits for SUICDED && drained which can happen only after the
|
|
- * enclosing kernfs operation which executed the winning instance
|
|
- * of kernfs_remove_self() finished.
|
|
+ * while kernfs_rwsem for held exclusive. The ones which lost
|
|
+ * arbitration waits for SUICIDED && drained which can happen only
|
|
+ * after the enclosing kernfs operation which executed the winning
|
|
+ * instance of kernfs_remove_self() finished.
|
|
*/
|
|
if (!(kn->flags & KERNFS_SUICIDAL)) {
|
|
kn->flags |= KERNFS_SUICIDAL;
|
|
@@ -1494,9 +1494,9 @@ bool kernfs_remove_self(struct kernfs_node *kn)
|
|
atomic_read(&kn->active) == KN_DEACTIVATED_BIAS)
|
|
break;
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
schedule();
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
}
|
|
finish_wait(waitq, &wait);
|
|
WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb));
|
|
@@ -1504,12 +1504,12 @@ bool kernfs_remove_self(struct kernfs_node *kn)
|
|
}
|
|
|
|
/*
|
|
- * This must be done while holding kernfs_mutex; otherwise, waiting
|
|
- * for SUICIDED && deactivated could finish prematurely.
|
|
+ * This must be done while kernfs_rwsem held exclusive; otherwise,
|
|
+ * waiting for SUICIDED && deactivated could finish prematurely.
|
|
*/
|
|
kernfs_unbreak_active_protection(kn);
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
return ret;
|
|
}
|
|
|
|
@@ -1533,7 +1533,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
|
|
return -ENOENT;
|
|
}
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
|
|
kn = kernfs_find_ns(parent, name, ns);
|
|
if (kn) {
|
|
@@ -1542,7 +1542,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
|
|
kernfs_put(kn);
|
|
}
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
|
|
if (kn)
|
|
return 0;
|
|
@@ -1568,7 +1568,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
|
|
if (!kn->parent)
|
|
return -EINVAL;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
|
|
error = -ENOENT;
|
|
if (!kernfs_active(kn) || !kernfs_active(new_parent) ||
|
|
@@ -1622,7 +1622,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
|
|
|
|
error = 0;
|
|
out:
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
return error;
|
|
}
|
|
|
|
@@ -1697,7 +1697,7 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
|
|
|
|
if (!dir_emit_dots(file, ctx))
|
|
return 0;
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
|
|
if (kernfs_ns_enabled(parent))
|
|
ns = kernfs_info(dentry->d_sb)->ns;
|
|
@@ -1714,12 +1714,12 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
|
|
file->private_data = pos;
|
|
kernfs_get(pos);
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
if (!dir_emit(ctx, name, len, ino, type))
|
|
return 0;
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
}
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
file->private_data = NULL;
|
|
ctx->pos = INT_MAX;
|
|
return 0;
|
|
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
|
|
index c75719312147..60e2a86c535e 100644
|
|
--- a/fs/kernfs/file.c
|
|
+++ b/fs/kernfs/file.c
|
|
@@ -860,7 +860,7 @@ static void kernfs_notify_workfn(struct work_struct *work)
|
|
spin_unlock_irq(&kernfs_notify_lock);
|
|
|
|
/* kick fsnotify */
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
|
|
list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
|
|
struct kernfs_node *parent;
|
|
@@ -898,7 +898,7 @@ static void kernfs_notify_workfn(struct work_struct *work)
|
|
iput(inode);
|
|
}
|
|
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
kernfs_put(kn);
|
|
goto repeat;
|
|
}
|
|
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
|
|
index fc2469a20fed..ddaf18198935 100644
|
|
--- a/fs/kernfs/inode.c
|
|
+++ b/fs/kernfs/inode.c
|
|
@@ -106,9 +106,9 @@ int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr)
|
|
{
|
|
int ret;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
ret = __kernfs_setattr(kn, iattr);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
return ret;
|
|
}
|
|
|
|
@@ -121,7 +121,7 @@ int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr)
|
|
if (!kn)
|
|
return -EINVAL;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
error = setattr_prepare(dentry, iattr);
|
|
if (error)
|
|
goto out;
|
|
@@ -134,7 +134,7 @@ int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr)
|
|
setattr_copy(inode, iattr);
|
|
|
|
out:
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
return error;
|
|
}
|
|
|
|
@@ -189,9 +189,9 @@ int kernfs_iop_getattr(const struct path *path, struct kstat *stat,
|
|
struct inode *inode = d_inode(path->dentry);
|
|
struct kernfs_node *kn = inode->i_private;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
kernfs_refresh_inode(kn, inode);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
|
|
generic_fillattr(inode, stat);
|
|
return 0;
|
|
@@ -281,9 +281,9 @@ int kernfs_iop_permission(struct inode *inode, int mask)
|
|
|
|
kn = inode->i_private;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
kernfs_refresh_inode(kn, inode);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
|
|
return generic_permission(inode, mask);
|
|
}
|
|
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
|
|
index 6a8d0ca26d03..c933d9bd8a78 100644
|
|
--- a/fs/kernfs/kernfs-internal.h
|
|
+++ b/fs/kernfs/kernfs-internal.h
|
|
@@ -13,6 +13,7 @@
|
|
#include <linux/lockdep.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mutex.h>
|
|
+#include <linux/rwsem.h>
|
|
#include <linux/xattr.h>
|
|
|
|
#include <linux/kernfs.h>
|
|
@@ -69,7 +70,7 @@ struct kernfs_super_info {
|
|
*/
|
|
const void *ns;
|
|
|
|
- /* anchored at kernfs_root->supers, protected by kernfs_mutex */
|
|
+ /* anchored at kernfs_root->supers, protected by kernfs_rwsem */
|
|
struct list_head node;
|
|
};
|
|
#define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info))
|
|
@@ -118,7 +119,7 @@ int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr);
|
|
/*
|
|
* dir.c
|
|
*/
|
|
-extern struct mutex kernfs_mutex;
|
|
+extern struct rw_semaphore kernfs_rwsem;
|
|
extern const struct dentry_operations kernfs_dops;
|
|
extern const struct file_operations kernfs_dir_fops;
|
|
extern const struct inode_operations kernfs_dir_iops;
|
|
diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c
|
|
index 9dc7e7a64e10..baa4155ba2ed 100644
|
|
--- a/fs/kernfs/mount.c
|
|
+++ b/fs/kernfs/mount.c
|
|
@@ -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 */
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
inode = kernfs_get_inode(sb, info->root->kn);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
if (!inode) {
|
|
pr_debug("kernfs: could not get root inode\n");
|
|
return -ENOMEM;
|
|
@@ -344,9 +344,9 @@ int kernfs_get_tree(struct fs_context *fc)
|
|
}
|
|
sb->s_flags |= SB_ACTIVE;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
list_add(&info->node, &info->root->supers);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
}
|
|
|
|
fc->root = dget(sb->s_root);
|
|
@@ -372,9 +372,9 @@ void kernfs_kill_sb(struct super_block *sb)
|
|
{
|
|
struct kernfs_super_info *info = kernfs_info(sb);
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_write(&kernfs_rwsem);
|
|
list_del(&info->node);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_write(&kernfs_rwsem);
|
|
|
|
/*
|
|
* Remove the superblock from fs_supers/s_instances
|
|
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
|
|
index 5432883d819f..c8f8e41b8411 100644
|
|
--- a/fs/kernfs/symlink.c
|
|
+++ b/fs/kernfs/symlink.c
|
|
@@ -116,9 +116,9 @@ static int kernfs_getlink(struct inode *inode, char *path)
|
|
struct kernfs_node *target = kn->symlink.target_kn;
|
|
int error;
|
|
|
|
- mutex_lock(&kernfs_mutex);
|
|
+ down_read(&kernfs_rwsem);
|
|
error = kernfs_get_target_path(parent, target, path);
|
|
- mutex_unlock(&kernfs_mutex);
|
|
+ up_read(&kernfs_rwsem);
|
|
|
|
return error;
|
|
}
|
|
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
|
|
index 195afa63ab1c..95e1948379d0 100644
|
|
--- a/include/linux/kernfs.h
|
|
+++ b/include/linux/kernfs.h
|
|
@@ -193,7 +193,7 @@ struct kernfs_root {
|
|
u32 id_highbits;
|
|
struct kernfs_syscall_ops *syscall_ops;
|
|
|
|
- /* list of kernfs_super_info of this root, protected by kernfs_mutex */
|
|
+ /* list of kernfs_super_info of this root, protected by kernfs_rwsem */
|
|
struct list_head supers;
|
|
|
|
wait_queue_head_t deactivate_waitq;
|
|
--
|
|
2.25.1
|
|
|