#include "kernfs-internal.h"
 
-DEFINE_MUTEX(kernfs_mutex);
+DECLARE_RWSEM(kernfs_rwsem);
 static DEFINE_SPINLOCK(kernfs_rename_lock);    /* kn->parent and ->name */
 static char kernfs_pr_cont_buf[PATH_MAX];      /* protected by rename_lock */
 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;
 }
 
  *     @kn->parent->dir.children.
  *
  *     Locking:
- *     mutex_lock(kernfs_mutex)
+ *     kernfs_rwsem held exclusive
  *
  *     RETURNS:
  *     0 on susccess -EEXIST on failure.
  *     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)
 {
  * 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_);
 
        kernfs_drain_open_files(kn);
 
-       mutex_lock(&kernfs_mutex);
+       down_write(&kernfs_rwsem);
 }
 
 /**
        bool has_ns;
        int ret;
 
-       mutex_lock(&kernfs_mutex);
+       down_write(&kernfs_rwsem);
 
        ret = -EINVAL;
        has_ns = kernfs_ns_enabled(parent);
                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.
        return 0;
 
 out_unlock:
-       mutex_unlock(&kernfs_mutex);
+       up_write(&kernfs_rwsem);
        return ret;
 }
 
        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",
        size_t len;
        char *p, *name;
 
-       lockdep_assert_held(&kernfs_mutex);
+       lockdep_assert_held_read(&kernfs_rwsem);
 
        /* grab kernfs_rename_lock to piggy back on kernfs_pr_cont_buf */
        spin_lock_irq(&kernfs_rename_lock);
 {
        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;
 }
 {
        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;
 }
                /* 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.
        }
 
        kn = kernfs_dentry_node(dentry);
-       mutex_lock(&kernfs_mutex);
+       down_read(&kernfs_rwsem);
 
        /* The kernfs node has been deactivated */
        if (!kernfs_active(kn))
            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;
 }
 
        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;
 
                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;
 }
 {
        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)
 {
        struct kernfs_node *pos;
 
-       mutex_lock(&kernfs_mutex);
+       down_write(&kernfs_rwsem);
 
        pos = NULL;
        while ((pos = kernfs_next_descendant_post(pos, 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.
                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.
  */
 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);
 }
 
 /**
 {
        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;
                            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));
        }
 
        /*
-        * 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;
 }
 
                return -ENOENT;
        }
 
-       mutex_lock(&kernfs_mutex);
+       down_write(&kernfs_rwsem);
 
        kn = kernfs_find_ns(parent, name, ns);
        if (kn)
                __kernfs_remove(kn);
 
-       mutex_unlock(&kernfs_mutex);
+       up_write(&kernfs_rwsem);
 
        if (kn)
                return 0;
        if (!kn->parent)
                return -EINVAL;
 
-       mutex_lock(&kernfs_mutex);
+       down_write(&kernfs_rwsem);
 
        error = -ENOENT;
        if (!kernfs_active(kn) || !kernfs_active(new_parent) ||
 
        error = 0;
  out:
-       mutex_unlock(&kernfs_mutex);
+       up_write(&kernfs_rwsem);
        return error;
 }
 
 
        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;
                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;