]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'userns/for-next'
authorThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 13:02:30 +0000 (15:02 +0200)
committerThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 13:02:30 +0000 (15:02 +0200)
Conflicts:
fs/fuse/dir.c

1  2 
fs/afs/dir.c
fs/dcache.c
fs/fuse/dir.c
fs/namei.c
fs/namespace.c
fs/nfs/dir.c
fs/sysfs/dir.c
include/linux/dcache.h

diff --combined fs/afs/dir.c
index 529300327f4574d2dc36345be3c7398442548e08,7fb69d45f1b9621a045219e06d3b9ac60bcdfcab..3756d4fe129fe13bf98995ce37e2321eff0b2fbf
@@@ -600,6 -600,9 +600,6 @@@ static int afs_d_revalidate(struct dent
  
        /* lock down the parent dentry so we can peer at it */
        parent = dget_parent(dentry);
 -      if (!parent->d_inode)
 -              goto out_bad;
 -
        dir = AFS_FS_I(parent->d_inode);
  
        /* validate the parent directory */
@@@ -683,8 -686,7 +683,7 @@@ not_found
  
  out_bad:
        /* don't unhash if we have submounts */
-       if (check_submounts_and_drop(dentry) != 0)
-               goto out_skip;
+       shrink_submounts_and_drop(dentry);
  
        _debug("dropping dentry %s/%s",
               parent->d_name.name, dentry->d_name.name);
diff --combined fs/dcache.c
index d70df2e0e0da8a6b41d9bfeba798fb4cc9481a80,1e9bf96b013213034ec8e98f074e731145b367be..093a68d5fec5d2129d271066b1f4e4cbc83286f3
@@@ -1331,6 -1331,14 +1331,6 @@@ rename_retry
   * list is non-empty and continue searching.
   */
  
 -/**
 - * have_submounts - check for mounts over a dentry
 - * @parent: dentry to check.
 - *
 - * Return true if the parent or its subdirectories contain
 - * a mount point
 - */
 -
  static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
  {
        int *ret = data;
        return D_WALK_CONTINUE;
  }
  
 +/**
 + * have_submounts - check for mounts over a dentry
 + * @parent: dentry to check.
 + *
 + * Return true if the parent or its subdirectories contain
 + * a mount point
 + */
  int have_submounts(struct dentry *parent)
  {
        int ret = 0;
@@@ -1372,7 -1373,7 +1372,7 @@@ int d_set_mounted(struct dentry *dentry
        int ret = -ENOENT;
        write_seqlock(&rename_lock);
        for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) {
-               /* Need exclusion wrt. check_submounts_and_drop() */
+               /* Need exclusion wrt. shrink_submounts_and_drop() */
                spin_lock(&p->d_lock);
                if (unlikely(d_unhashed(p))) {
                        spin_unlock(&p->d_lock);
@@@ -1477,70 -1478,56 +1477,56 @@@ void shrink_dcache_parent(struct dentr
  }
  EXPORT_SYMBOL(shrink_dcache_parent);
  
- static enum d_walk_ret check_and_collect(void *_data, struct dentry *dentry)
+ struct detach_data {
+       struct dentry *found;
+ };
+ static enum d_walk_ret do_detach_submounts(void *ptr, struct dentry *dentry)
  {
-       struct select_data *data = _data;
+       struct detach_data *data = ptr;
  
-       if (d_mountpoint(dentry)) {
-               data->found = -EBUSY;
-               return D_WALK_QUIT;
-       }
+       if (d_mountpoint(dentry))
+               data->found = dentry;
  
-       return select_collect(_data, dentry);
- }
- static void check_and_drop(void *_data)
- {
-       struct select_data *data = _data;
-       if (d_mountpoint(data->start))
-               data->found = -EBUSY;
-       if (!data->found)
-               __d_drop(data->start);
+       return data->found ? D_WALK_QUIT : D_WALK_CONTINUE;
  }
  
  /**
-  * check_submounts_and_drop - prune dcache, check for submounts and drop
+  * detach_submounts - check for submounts and detach them.
   *
-  * All done as a single atomic operation relative to has_unlinked_ancestor().
-  * Returns 0 if successfully unhashed @parent.  If there were submounts then
-  * return -EBUSY.
+  * @dentry: dentry to find mount points under.
   *
-  * @dentry: dentry to prune and drop
+  * If dentry or any of it's children is a mount point detach those mounts.
   */
int check_submounts_and_drop(struct dentry *dentry)
void detach_submounts(struct dentry *dentry)
  {
-       int ret = 0;
-       /* Negative dentries can be dropped without further checks */
-       if (!dentry->d_inode) {
-               d_drop(dentry);
-               goto out;
-       }
+       struct detach_data data;
        for (;;) {
-               struct select_data data;
-               INIT_LIST_HEAD(&data.dispose);
-               data.start = dentry;
-               data.found = 0;
+               data.found = NULL;
+               d_walk(dentry, &data, do_detach_submounts, NULL);
  
-               d_walk(dentry, &data, check_and_collect, check_and_drop);
-               ret = data.found;
-               if (!list_empty(&data.dispose))
-                       shrink_dentry_list(&data.dispose);
-               if (ret <= 0)
+               if (!data.found)
                        break;
  
+               detach_mounts(data.found);
                cond_resched();
        }
+       detach_mounts(dentry);
+ }
  
- out:
-       return ret;
+ /**
+  * shrink_submounts_and_drop - detach submounts, prune dcache, and drop
+  *
+  * All done as a single atomic operation reletaive to d_set_mounted().
+  *
+  * @dentry: dentry to detach, prune and drop
+  */
+ void shrink_submounts_and_drop(struct dentry *dentry)
+ {
+       d_drop(dentry);
+       detach_submounts(dentry);
+       shrink_dcache_parent(dentry);
  }
- EXPORT_SYMBOL(check_submounts_and_drop);
+ EXPORT_SYMBOL(shrink_submounts_and_drop);
  
  /**
   * __d_alloc  -       allocate a dcache entry
@@@ -1800,32 -1787,6 +1786,32 @@@ struct dentry *d_instantiate_unique(str
  
  EXPORT_SYMBOL(d_instantiate_unique);
  
 +/**
 + * d_instantiate_no_diralias - instantiate a non-aliased dentry
 + * @entry: dentry to complete
 + * @inode: inode to attach to this dentry
 + *
 + * Fill in inode information in the entry.  If a directory alias is found, then
 + * return an error.  Together with d_materialise_unique() this guarantees that a
 + * directory inode may never have more than one alias.
 + */
 +int d_instantiate_no_diralias(struct dentry *entry, struct inode *inode)
 +{
 +      BUG_ON(!hlist_unhashed(&entry->d_alias));
 +
 +      spin_lock(&inode->i_lock);
 +      if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) {
 +              spin_unlock(&inode->i_lock);
 +              return -EBUSY;
 +      }
 +      __d_instantiate(entry, inode);
 +      spin_unlock(&inode->i_lock);
 +      security_d_instantiate(entry, inode);
 +
 +      return 0;
 +}
 +EXPORT_SYMBOL(d_instantiate_no_diralias);
 +
  struct dentry *d_make_root(struct inode *root_inode)
  {
        struct dentry *res = NULL;
diff --combined fs/fuse/dir.c
index 0747f6eed59836954a23743cddde06fef4d8c05c,b1cd7b79a325dfdb22df0cfd3a3fe0fba9386171..3959aa981f17a3a23da34d3cc23645b57b22b321
@@@ -182,7 -182,6 +182,7 @@@ static int fuse_dentry_revalidate(struc
        struct inode *inode;
        struct dentry *parent;
        struct fuse_conn *fc;
 +      struct fuse_inode *fi;
        int ret;
  
        inode = ACCESS_ONCE(entry->d_inode);
                if (!err && !outarg.nodeid)
                        err = -ENOENT;
                if (!err) {
 -                      struct fuse_inode *fi = get_fuse_inode(inode);
 +                      fi = get_fuse_inode(inode);
                        if (outarg.nodeid != get_node_id(inode)) {
                                fuse_queue_forget(fc, forget, outarg.nodeid, 1);
                                goto invalid;
                                       attr_version);
                fuse_change_entry_timeout(entry, &outarg);
        } else if (inode) {
 -              fc = get_fuse_conn(inode);
 -              if (fc->readdirplus_auto) {
 +              fi = get_fuse_inode(inode);
 +              if (flags & LOOKUP_RCU) {
 +                      if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
 +                              return -ECHILD;
 +              } else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
                        parent = dget_parent(entry);
                        fuse_advise_use_readdirplus(parent->d_inode);
                        dput(parent);
@@@ -263,9 -259,7 +263,10 @@@ out
  
  invalid:
        ret = 0;
 -      shrink_submounts_and_drop(entry);
 +
-       if (!(flags & LOOKUP_RCU) && check_submounts_and_drop(entry) != 0)
-               ret = 1;
++      if (!(flags & LOOKUP_RCU))
++              shrink_submounts_and_drop(entry);
++
        goto out;
  }
  
@@@ -342,6 -336,24 +343,6 @@@ int fuse_lookup_name(struct super_bloc
        return err;
  }
  
 -static struct dentry *fuse_materialise_dentry(struct dentry *dentry,
 -                                            struct inode *inode)
 -{
 -      struct dentry *newent;
 -
 -      if (inode && S_ISDIR(inode->i_mode)) {
 -              struct fuse_conn *fc = get_fuse_conn(inode);
 -
 -              mutex_lock(&fc->inst_mutex);
 -              newent = d_materialise_unique(dentry, inode);
 -              mutex_unlock(&fc->inst_mutex);
 -      } else {
 -              newent = d_materialise_unique(dentry, inode);
 -      }
 -
 -      return newent;
 -}
 -
  static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
                                  unsigned int flags)
  {
        if (inode && get_node_id(inode) == FUSE_ROOT_ID)
                goto out_iput;
  
 -      newent = fuse_materialise_dentry(entry, inode);
 +      newent = d_materialise_unique(entry, inode);
        err = PTR_ERR(newent);
        if (IS_ERR(newent))
                goto out_err;
@@@ -583,11 -595,21 +584,11 @@@ static int create_new_entry(struct fuse
        }
        kfree(forget);
  
 -      if (S_ISDIR(inode->i_mode)) {
 -              struct dentry *alias;
 -              mutex_lock(&fc->inst_mutex);
 -              alias = d_find_alias(inode);
 -              if (alias) {
 -                      /* New directory must have moved since mkdir */
 -                      mutex_unlock(&fc->inst_mutex);
 -                      dput(alias);
 -                      iput(inode);
 -                      return -EBUSY;
 -              }
 -              d_instantiate(entry, inode);
 -              mutex_unlock(&fc->inst_mutex);
 -      } else
 -              d_instantiate(entry, inode);
 +      err = d_instantiate_no_diralias(entry, inode);
 +      if (err) {
 +              iput(inode);
 +              return err;
 +      }
  
        fuse_change_entry_timeout(entry, &outarg);
        fuse_invalidate_attr(dir);
@@@ -1040,8 -1062,6 +1041,8 @@@ static int fuse_access(struct inode *in
        struct fuse_access_in inarg;
        int err;
  
 +      BUG_ON(mask & MAY_NOT_BLOCK);
 +
        if (fc->no_access)
                return 0;
  
@@@ -1129,6 -1149,9 +1130,6 @@@ static int fuse_permission(struct inod
                   noticed immediately, only after the attribute
                   timeout has expired */
        } else if (mask & (MAY_ACCESS | MAY_CHDIR)) {
 -              if (mask & MAY_NOT_BLOCK)
 -                      return -ECHILD;
 -
                err = fuse_access(inode, mask);
        } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
                if (!(inode->i_mode & S_IXUGO)) {
@@@ -1256,7 -1279,7 +1257,7 @@@ static int fuse_direntplus_link(struct 
        if (!inode)
                goto out;
  
 -      alias = fuse_materialise_dentry(dentry, inode);
 +      alias = d_materialise_unique(dentry, inode);
        err = PTR_ERR(alias);
        if (IS_ERR(alias))
                goto out;
        }
  
  found:
 +      if (fc->readdirplus_auto)
 +              set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
        fuse_change_entry_timeout(dentry, o);
  
        err = 0;
diff --combined fs/namei.c
index caa28051e197e898e3c2bc52afce37bcbb284853,a12c1d31d4c8ab87231be38387dd3165b447ddb4..f892ef9b3ead08b6ffea75833349d58d889e42c6
   * path_mountpoint - look up a path to be umounted
   * @dfd:      directory file descriptor to start walk from
   * @name:     full pathname to walk
 + * @path:     pointer to container for result
   * @flags:    lookup flags
   *
   * Look up the given name, but don't attempt to revalidate the last component.
 - * Returns 0 and "path" will be valid on success; Retuns error otherwise.
 + * Returns 0 and "path" will be valid on success; Returns error otherwise.
   */
  static int
  path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags)
@@@ -3548,6 -3547,20 +3548,20 @@@ void dentry_unhash(struct dentry *dentr
        spin_unlock(&dentry->d_lock);
  }
  
+ static bool covered(struct vfsmount *mnt, struct dentry *dentry)
+ {
+       /* test to see if a dentry is covered with a mount in
+        * the current mount namespace.
+        */
+       bool is_covered;
+       rcu_read_lock();
+       is_covered = d_mountpoint(dentry) && __lookup_mnt(mnt, dentry, 1);
+       rcu_read_unlock();
+       return is_covered;
+ }
  int vfs_rmdir(struct inode *dir, struct dentry *dentry)
  {
        int error = may_delete(dir, dentry, 1);
        dget(dentry);
        mutex_lock(&dentry->d_inode->i_mutex);
  
-       error = -EBUSY;
-       if (d_mountpoint(dentry))
-               goto out;
        error = security_inode_rmdir(dir, dentry);
        if (error)
                goto out;
  
        dentry->d_inode->i_flags |= S_DEAD;
        dont_mount(dentry);
+       detach_mounts(dentry);
  
  out:
        mutex_unlock(&dentry->d_inode->i_mutex);
@@@ -3623,6 -3633,9 +3634,9 @@@ retry
                error = -ENOENT;
                goto exit3;
        }
+       error = -EBUSY;
+       if (covered(nd.path.mnt, dentry))
+               goto exit3;
        error = security_path_rmdir(&nd.path, dentry);
        if (error)
                goto exit3;
@@@ -3658,14 -3671,12 +3672,12 @@@ int vfs_unlink(struct inode *dir, struc
                return -EPERM;
  
        mutex_lock(&dentry->d_inode->i_mutex);
-       if (d_mountpoint(dentry))
-               error = -EBUSY;
-       else {
-               error = security_inode_unlink(dir, dentry);
+       error = security_inode_unlink(dir, dentry);
+       if (!error) {
+               error = dir->i_op->unlink(dir, dentry);
                if (!error) {
-                       error = dir->i_op->unlink(dir, dentry);
-                       if (!error)
-                               dont_mount(dentry);
+                       dont_mount(dentry);
+                       detach_mounts(dentry);
                }
        }
        mutex_unlock(&dentry->d_inode->i_mutex);
@@@ -3717,6 -3728,9 +3729,9 @@@ retry
                inode = dentry->d_inode;
                if (!inode)
                        goto slashes;
+               error = -EBUSY;
+               if (covered(nd.path.mnt, dentry))
+                       goto exit2;
                ihold(inode);
                error = security_path_unlink(&nd.path, dentry);
                if (error)
@@@ -3989,10 -4003,6 +4004,6 @@@ static int vfs_rename_dir(struct inode 
        if (target)
                mutex_lock(&target->i_mutex);
  
-       error = -EBUSY;
-       if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
-               goto out;
        error = -EMLINK;
        if (max_links && !target && new_dir != old_dir &&
            new_dir->i_nlink >= max_links)
        if (target) {
                target->i_flags |= S_DEAD;
                dont_mount(new_dentry);
+               detach_mounts(new_dentry);
        }
  out:
        if (target)
@@@ -4032,16 -4043,14 +4044,14 @@@ static int vfs_rename_other(struct inod
        if (target)
                mutex_lock(&target->i_mutex);
  
-       error = -EBUSY;
-       if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
-               goto out;
        error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
        if (error)
                goto out;
  
-       if (target)
+       if (target) {
                dont_mount(new_dentry);
+               detach_mounts(new_dentry);
+       }
        if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                d_move(old_dentry, new_dentry);
  out:
@@@ -4165,6 -4174,11 +4175,11 @@@ retry
        error = -ENOTEMPTY;
        if (new_dentry == trap)
                goto exit5;
+       error = -EBUSY;
+       if (covered(oldnd.path.mnt, old_dentry))
+               goto exit5;
+       if (covered(newnd.path.mnt, new_dentry))
+               goto exit5;
  
        error = security_path_rename(&oldnd.path, old_dentry,
                                     &newnd.path, new_dentry);
diff --combined fs/namespace.c
index 3ee6e59ead55a9ca2398a30b86f347315532a8b5,78f7c5c9e6733625a8b646e7cfd2ef853ad1fbf4..ca81c46ea649831ea3767ba114ba9a5c360df1a9
@@@ -39,7 -39,7 +39,7 @@@ static int mnt_group_start = 1
  static struct list_head *mount_hashtable __read_mostly;
  static struct list_head *mountpoint_hashtable __read_mostly;
  static struct kmem_cache *mnt_cache __read_mostly;
 -static struct rw_semaphore namespace_sem;
 +static DECLARE_RWSEM(namespace_sem);
  
  /* /sys/fs */
  struct kobject *fs_kobj;
@@@ -197,6 -197,7 +197,7 @@@ static struct mount *alloc_vfsmnt(cons
                INIT_LIST_HEAD(&mnt->mnt_share);
                INIT_LIST_HEAD(&mnt->mnt_slave_list);
                INIT_LIST_HEAD(&mnt->mnt_slave);
+               INIT_LIST_HEAD(&mnt->mnt_mp_list);
  #ifdef CONFIG_FSNOTIFY
                INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
  #endif
@@@ -636,6 -637,7 +637,7 @@@ static struct mountpoint *new_mountpoin
        mp->m_dentry = dentry;
        mp->m_count = 1;
        list_add(&mp->m_hash, chain);
+       INIT_LIST_HEAD(&mp->m_list);
        return mp;
  }
  
@@@ -643,6 -645,7 +645,7 @@@ static void put_mountpoint(struct mount
  {
        if (!--mp->m_count) {
                struct dentry *dentry = mp->m_dentry;
+               BUG_ON(!list_empty(&mp->m_list));
                spin_lock(&dentry->d_lock);
                dentry->d_flags &= ~DCACHE_MOUNTED;
                spin_unlock(&dentry->d_lock);
@@@ -689,6 -692,7 +692,7 @@@ static void detach_mnt(struct mount *mn
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        list_del_init(&mnt->mnt_child);
        list_del_init(&mnt->mnt_hash);
+       list_del_init(&mnt->mnt_mp_list);
        put_mountpoint(mnt->mnt_mp);
        mnt->mnt_mp = NULL;
  }
@@@ -705,6 -709,7 +709,7 @@@ void mnt_set_mountpoint(struct mount *m
        child_mnt->mnt_mountpoint = dget(mp->m_dentry);
        child_mnt->mnt_parent = mnt;
        child_mnt->mnt_mp = mp;
+       list_add_tail(&child_mnt->mnt_mp_list, &mp->m_list);
  }
  
  /*
@@@ -1191,6 -1196,7 +1196,7 @@@ void umount_tree(struct mount *mnt, in
                list_del_init(&p->mnt_child);
                if (mnt_has_parent(p)) {
                        p->mnt_parent->mnt_ghosts++;
+                       list_del_init(&p->mnt_mp_list);
                        put_mountpoint(p->mnt_mp);
                        p->mnt_mp = NULL;
                }
@@@ -1289,6 -1295,30 +1295,30 @@@ static int do_umount(struct mount *mnt
        return retval;
  }
  
+ void detach_mounts(struct dentry *dentry)
+ {
+       struct mountpoint *mp;
+       struct mount *mnt;
+       namespace_lock();
+       if (!d_mountpoint(dentry))
+               goto out_unlock;
+       mp = new_mountpoint(dentry);
+       if (IS_ERR(mp))
+               goto out_unlock;
+       br_write_lock(&vfsmount_lock);
+       while (!list_empty(&mp->m_list)) {
+               mnt = list_first_entry(&mp->m_list, struct mount, mnt_mp_list);
+               umount_tree(mnt, 1);
+       }
+       br_write_unlock(&vfsmount_lock);
+       put_mountpoint(mp);
+ out_unlock:
+       namespace_unlock();
+ }
  /* 
   * Is the caller allowed to modify his namespace?
   */
@@@ -1849,10 -1879,14 +1879,10 @@@ static int do_remount(struct path *path
                br_write_lock(&vfsmount_lock);
                mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK;
                mnt->mnt.mnt_flags = mnt_flags;
 -              br_write_unlock(&vfsmount_lock);
 -      }
 -      up_write(&sb->s_umount);
 -      if (!err) {
 -              br_write_lock(&vfsmount_lock);
                touch_mnt_namespace(mnt->mnt_ns);
                br_write_unlock(&vfsmount_lock);
        }
 +      up_write(&sb->s_umount);
        return err;
  }
  
@@@ -2440,7 -2474,9 +2470,7 @@@ static struct mnt_namespace *dup_mnt_ns
                return ERR_CAST(new);
        }
        new_ns->root = new;
 -      br_write_lock(&vfsmount_lock);
        list_add_tail(&new_ns->list, &new->mnt_list);
 -      br_write_unlock(&vfsmount_lock);
  
        /*
         * Second pass: switch the tsk->fs->* elements and mark new vfsmounts
@@@ -2761,6 -2797,8 +2791,6 @@@ void __init mnt_init(void
        unsigned u;
        int err;
  
 -      init_rwsem(&namespace_sem);
 -
        mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount),
                        0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
  
@@@ -2794,7 -2832,11 +2824,7 @@@ void put_mnt_ns(struct mnt_namespace *n
  {
        if (!atomic_dec_and_test(&ns->count))
                return;
 -      namespace_lock();
 -      br_write_lock(&vfsmount_lock);
 -      umount_tree(ns->root, 0);
 -      br_write_unlock(&vfsmount_lock);
 -      namespace_unlock();
 +      drop_collected_mounts(&ns->root->mnt);
        free_mnt_ns(ns);
  }
  
@@@ -2863,7 -2905,7 +2893,7 @@@ bool fs_fully_visible(struct file_syste
        if (unlikely(!ns))
                return false;
  
 -      namespace_lock();
 +      down_read(&namespace_sem);
        list_for_each_entry(mnt, &ns->list, mnt_list) {
                struct mount *child;
                if (mnt->mnt.mnt_sb->s_type != type)
        next:   ;
        }
  found:
 -      namespace_unlock();
 +      up_read(&namespace_sem);
        return visible;
  }
  
diff --combined fs/nfs/dir.c
index 76548d81f926c8843a291350a080f09938c0d0fd,e8e35acd88500949857e10a495db27820445326f..c20fffac621e0f4032ae066c4cc884f800b5f684
@@@ -98,7 -98,9 +98,7 @@@ nfs_opendir(struct inode *inode, struc
        struct nfs_open_dir_context *ctx;
        struct rpc_cred *cred;
  
 -      dfprintk(FILE, "NFS: open dir(%s/%s)\n",
 -                      filp->f_path.dentry->d_parent->d_name.name,
 -                      filp->f_path.dentry->d_name.name);
 +      dfprintk(FILE, "NFS: open dir(%pD2)\n", filp);
  
        nfs_inc_stats(inode, NFSIOS_VFSOPEN);
  
@@@ -295,10 -297,11 +295,10 @@@ int nfs_readdir_search_for_cookie(struc
                                if (ctx->duped > 0
                                    && ctx->dup_cookie == *desc->dir_cookie) {
                                        if (printk_ratelimit()) {
 -                                              pr_notice("NFS: directory %s/%s contains a readdir loop."
 +                                              pr_notice("NFS: directory %pD2 contains a readdir loop."
                                                                "Please contact your server vendor.  "
                                                                "The file: %s has duplicate cookie %llu\n",
 -                                                              desc->file->f_dentry->d_parent->d_name.name,
 -                                                              desc->file->f_dentry->d_name.name,
 +                                                              desc->file,
                                                                array->array[i].string.name,
                                                                *desc->dir_cookie);
                                        }
@@@ -819,8 -822,9 +819,8 @@@ static int nfs_readdir(struct file *fil
        struct nfs_open_dir_context *dir_ctx = file->private_data;
        int res = 0;
  
 -      dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
 -                      dentry->d_parent->d_name.name, dentry->d_name.name,
 -                      (long long)ctx->pos);
 +      dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n",
 +                      file, (long long)ctx->pos);
        nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);
  
        /*
@@@ -876,17 -880,22 +876,17 @@@ out
        nfs_unblock_sillyrename(dentry);
        if (res > 0)
                res = 0;
 -      dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n",
 -                      dentry->d_parent->d_name.name, dentry->d_name.name,
 -                      res);
 +      dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file, res);
        return res;
  }
  
  static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
  {
 -      struct dentry *dentry = filp->f_path.dentry;
 -      struct inode *inode = dentry->d_inode;
 +      struct inode *inode = file_inode(filp);
        struct nfs_open_dir_context *dir_ctx = filp->private_data;
  
 -      dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n",
 -                      dentry->d_parent->d_name.name,
 -                      dentry->d_name.name,
 -                      offset, whence);
 +      dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n",
 +                      filp, offset, whence);
  
        mutex_lock(&inode->i_mutex);
        switch (whence) {
@@@ -916,12 -925,15 +916,12 @@@ out
  static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
                         int datasync)
  {
 -      struct dentry *dentry = filp->f_path.dentry;
 -      struct inode *inode = dentry->d_inode;
 +      struct inode *inode = file_inode(filp);
  
 -      dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d\n",
 -                      dentry->d_parent->d_name.name, dentry->d_name.name,
 -                      datasync);
 +      dfprintk(FILE, "NFS: fsync dir(%pD2) datasync %d\n", filp, datasync);
  
        mutex_lock(&inode->i_mutex);
 -      nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC);
 +      nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
        mutex_unlock(&inode->i_mutex);
        return 0;
  }
@@@ -1061,8 -1073,9 +1061,8 @@@ static int nfs_lookup_revalidate(struc
        }
  
        if (is_bad_inode(inode)) {
 -              dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode\n",
 -                              __func__, dentry->d_parent->d_name.name,
 -                              dentry->d_name.name);
 +              dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
 +                              __func__, dentry);
                goto out_bad;
        }
  
@@@ -1112,8 -1125,9 +1112,8 @@@ out_set_verifier
        nfs_advise_use_readdirplus(dir);
   out_valid_noent:
        dput(parent);
 -      dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
 -                      __func__, dentry->d_parent->d_name.name,
 -                      dentry->d_name.name);
 +      dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n",
 +                      __func__, dentry);
        return 1;
  out_zap_parent:
        nfs_zap_caches(dir);
                if (dentry->d_flags & DCACHE_DISCONNECTED)
                        goto out_valid;
        }
-       /* If we have submounts, don't unhash ! */
-       if (check_submounts_and_drop(dentry) != 0)
-               goto out_valid;
+       shrink_submounts_and_drop(dentry);
        dput(parent);
 -      dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n",
 -                      __func__, dentry->d_parent->d_name.name,
 -                      dentry->d_name.name);
 +      dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is invalid\n",
 +                      __func__, dentry);
        return 0;
  out_error:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
        nfs4_label_free(label);
        dput(parent);
 -      dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n",
 -                      __func__, dentry->d_parent->d_name.name,
 -                      dentry->d_name.name, error);
 +      dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) lookup returned error %d\n",
 +                      __func__, dentry, error);
        return error;
  }
  
@@@ -1166,14 -1179,16 +1163,14 @@@ static int nfs_weak_revalidate(struct d
         * eventually need to do something more here.
         */
        if (!inode) {
 -              dfprintk(LOOKUPCACHE, "%s: %s/%s has negative inode\n",
 -                              __func__, dentry->d_parent->d_name.name,
 -                              dentry->d_name.name);
 +              dfprintk(LOOKUPCACHE, "%s: %pd2 has negative inode\n",
 +                              __func__, dentry);
                return 1;
        }
  
        if (is_bad_inode(inode)) {
 -              dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode\n",
 -                              __func__, dentry->d_parent->d_name.name,
 -                              dentry->d_name.name);
 +              dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
 +                              __func__, dentry);
                return 0;
        }
  
   */
  static int nfs_dentry_delete(const struct dentry *dentry)
  {
 -      dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
 -              dentry->d_parent->d_name.name, dentry->d_name.name,
 -              dentry->d_flags);
 +      dfprintk(VFS, "NFS: dentry_delete(%pd2, %x)\n",
 +              dentry, dentry->d_flags);
  
        /* Unhash any dentry with a stale inode */
        if (dentry->d_inode != NULL && NFS_STALE(dentry->d_inode))
@@@ -1267,7 -1283,8 +1264,7 @@@ struct dentry *nfs_lookup(struct inode 
        struct nfs4_label *label = NULL;
        int error;
  
 -      dfprintk(VFS, "NFS: lookup(%s/%s)\n",
 -              dentry->d_parent->d_name.name, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry);
        nfs_inc_stats(dir, NFSIOS_VFSLOOKUP);
  
        res = ERR_PTR(-ENAMETOOLONG);
@@@ -1361,7 -1378,7 +1358,7 @@@ static struct nfs_open_context *create_
  
  static int do_open(struct inode *inode, struct file *filp)
  {
 -      nfs_fscache_set_inode_cookie(inode, filp);
 +      nfs_fscache_open_file(inode, filp);
        return 0;
  }
  
@@@ -1398,8 -1415,8 +1395,8 @@@ int nfs_atomic_open(struct inode *dir, 
        /* Expect a negative dentry */
        BUG_ON(dentry->d_inode);
  
 -      dfprintk(VFS, "NFS: atomic_open(%s/%ld), %s\n",
 -                      dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: atomic_open(%s/%ld), %pd\n",
 +                      dir->i_sb->s_id, dir->i_ino, dentry);
  
        err = nfs_check_flags(open_flags);
        if (err)
  
        trace_nfs_atomic_open_enter(dir, ctx, open_flags);
        nfs_block_sillyrename(dentry->d_parent);
 -      inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
 +      inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr, opened);
        nfs_unblock_sillyrename(dentry->d_parent);
        if (IS_ERR(inode)) {
                err = PTR_ERR(inode);
@@@ -1588,8 -1605,8 +1585,8 @@@ int nfs_create(struct inode *dir, struc
        int open_flags = excl ? O_CREAT | O_EXCL : O_CREAT;
        int error;
  
 -      dfprintk(VFS, "NFS: create(%s/%ld), %s\n",
 -                      dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: create(%s/%ld), %pd\n",
 +                      dir->i_sb->s_id, dir->i_ino, dentry);
  
        attr.ia_mode = mode;
        attr.ia_valid = ATTR_MODE;
@@@ -1615,8 -1632,8 +1612,8 @@@ nfs_mknod(struct inode *dir, struct den
        struct iattr attr;
        int status;
  
 -      dfprintk(VFS, "NFS: mknod(%s/%ld), %s\n",
 -                      dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: mknod(%s/%ld), %pd\n",
 +                      dir->i_sb->s_id, dir->i_ino, dentry);
  
        if (!new_valid_dev(rdev))
                return -EINVAL;
@@@ -1644,8 -1661,8 +1641,8 @@@ int nfs_mkdir(struct inode *dir, struc
        struct iattr attr;
        int error;
  
 -      dfprintk(VFS, "NFS: mkdir(%s/%ld), %s\n",
 -                      dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: mkdir(%s/%ld), %pd\n",
 +                      dir->i_sb->s_id, dir->i_ino, dentry);
  
        attr.ia_valid = ATTR_MODE;
        attr.ia_mode = mode | S_IFDIR;
@@@ -1672,8 -1689,8 +1669,8 @@@ int nfs_rmdir(struct inode *dir, struc
  {
        int error;
  
 -      dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n",
 -                      dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: rmdir(%s/%ld), %pd\n",
 +                      dir->i_sb->s_id, dir->i_ino, dentry);
  
        trace_nfs_rmdir_enter(dir, dentry);
        if (dentry->d_inode) {
@@@ -1708,7 -1725,8 +1705,7 @@@ static int nfs_safe_remove(struct dentr
        struct inode *inode = dentry->d_inode;
        int error = -EBUSY;
                
 -      dfprintk(VFS, "NFS: safe_remove(%s/%s)\n",
 -              dentry->d_parent->d_name.name, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: safe_remove(%pd2)\n", dentry);
  
        /* If the dentry was sillyrenamed, we simply call d_delete() */
        if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
@@@ -1741,8 -1759,8 +1738,8 @@@ int nfs_unlink(struct inode *dir, struc
        int error;
        int need_rehash = 0;
  
 -      dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
 -              dir->i_ino, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: unlink(%s/%ld, %pd)\n", dir->i_sb->s_id,
 +              dir->i_ino, dentry);
  
        trace_nfs_unlink_enter(dir, dentry);
        spin_lock(&dentry->d_lock);
@@@ -1792,8 -1810,8 +1789,8 @@@ int nfs_symlink(struct inode *dir, stru
        unsigned int pathlen = strlen(symname);
        int error;
  
 -      dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
 -              dir->i_ino, dentry->d_name.name, symname);
 +      dfprintk(VFS, "NFS: symlink(%s/%ld, %pd, %s)\n", dir->i_sb->s_id,
 +              dir->i_ino, dentry, symname);
  
        if (pathlen > PAGE_SIZE)
                return -ENAMETOOLONG;
        error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
        trace_nfs_symlink_exit(dir, dentry, error);
        if (error != 0) {
 -              dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
 +              dfprintk(VFS, "NFS: symlink(%s/%ld, %pd, %s) error %d\n",
                        dir->i_sb->s_id, dir->i_ino,
 -                      dentry->d_name.name, symname, error);
 +                      dentry, symname, error);
                d_drop(dentry);
                __free_page(page);
                return error;
@@@ -1844,8 -1862,9 +1841,8 @@@ nfs_link(struct dentry *old_dentry, str
        struct inode *inode = old_dentry->d_inode;
        int error;
  
 -      dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
 -              old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
 -              dentry->d_parent->d_name.name, dentry->d_name.name);
 +      dfprintk(VFS, "NFS: link(%pd2 -> %pd2)\n",
 +              old_dentry, dentry);
  
        trace_nfs_link_enter(inode, dir, dentry);
        NFS_PROTO(inode)->return_delegation(inode);
@@@ -1893,8 -1912,9 +1890,8 @@@ int nfs_rename(struct inode *old_dir, s
        struct dentry *dentry = NULL, *rehash = NULL;
        int error = -EBUSY;
  
 -      dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
 -               old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
 -               new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
 +      dfprintk(VFS, "NFS: rename(%pd2 -> %pd2, ct=%d)\n",
 +               old_dentry, new_dentry,
                 d_count(new_dentry));
  
        trace_nfs_rename_enter(old_dir, old_dentry, new_dir, new_dentry);
diff --combined fs/sysfs/dir.c
index eab59de47556568e522c66e83bd0844de4cd5146,477c66d4e2a85a4a81962993feda816ee1e2a76f..11837b73abbfac18fdc733b41808dac2800d6af9
  DEFINE_MUTEX(sysfs_mutex);
  DEFINE_SPINLOCK(sysfs_assoc_lock);
  
 -#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb);
 +#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb)
  
  static DEFINE_SPINLOCK(sysfs_ino_lock);
  static DEFINE_IDA(sysfs_ino_ida);
  
  /**
   *    sysfs_name_hash
 - *    @ns:   Namespace tag to hash
   *    @name: Null terminated string to hash
 + *    @ns:   Namespace tag to hash
   *
   *    Returns 31 bit hash of ns + name (so it fits in an off_t )
   */
 -static unsigned int sysfs_name_hash(const void *ns, const char *name)
 +static unsigned int sysfs_name_hash(const char *name, const void *ns)
  {
        unsigned long hash = init_name_hash();
        unsigned int len = strlen(name);
@@@ -56,8 -56,8 +56,8 @@@
        return hash;
  }
  
 -static int sysfs_name_compare(unsigned int hash, const void *ns,
 -      const char *name, const struct sysfs_dirent *sd)
 +static int sysfs_name_compare(unsigned int hash, const char *name,
 +                            const void *ns, const struct sysfs_dirent *sd)
  {
        if (hash != sd->s_hash)
                return hash - sd->s_hash;
@@@ -69,7 -69,7 +69,7 @@@
  static int sysfs_sd_compare(const struct sysfs_dirent *left,
                            const struct sysfs_dirent *right)
  {
 -      return sysfs_name_compare(left->s_hash, left->s_ns, left->s_name,
 +      return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns,
                                  right);
  }
  
@@@ -111,11 -111,6 +111,11 @@@ static int sysfs_link_sibling(struct sy
        /* add new node and rebalance the tree */
        rb_link_node(&sd->s_rb, parent, node);
        rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
 +
 +      /* if @sd has ns tag, mark the parent to enable ns filtering */
 +      if (sd->s_ns)
 +              sd->s_parent->s_flags |= SYSFS_FLAG_HAS_NS;
 +
        return 0;
  }
  
@@@ -135,15 -130,26 +135,15 @@@ static void sysfs_unlink_sibling(struc
                sd->s_parent->s_dir.subdirs--;
  
        rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
 -}
 -
 -#ifdef CONFIG_DEBUG_LOCK_ALLOC
 -
 -/* Test for attributes that want to ignore lockdep for read-locking */
 -static bool ignore_lockdep(struct sysfs_dirent *sd)
 -{
 -      return sysfs_type(sd) == SYSFS_KOBJ_ATTR &&
 -                      sd->s_attr.attr->ignore_lockdep;
 -}
 -
 -#else
  
 -static inline bool ignore_lockdep(struct sysfs_dirent *sd)
 -{
 -      return true;
 +      /*
 +       * Either all or none of the children have tags.  Clearing HAS_NS
 +       * when there's no child left is enough to keep the flag synced.
 +       */
 +      if (RB_EMPTY_ROOT(&sd->s_parent->s_dir.children))
 +              sd->s_parent->s_flags &= ~SYSFS_FLAG_HAS_NS;
  }
  
 -#endif
 -
  /**
   *    sysfs_get_active - get an active reference to sysfs_dirent
   *    @sd: sysfs_dirent to get an active reference to
@@@ -162,7 -168,7 +162,7 @@@ struct sysfs_dirent *sysfs_get_active(s
        if (!atomic_inc_unless_negative(&sd->s_active))
                return NULL;
  
 -      if (likely(!ignore_lockdep(sd)))
 +      if (likely(!sysfs_ignore_lockdep(sd)))
                rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
        return sd;
  }
@@@ -181,7 -187,7 +181,7 @@@ void sysfs_put_active(struct sysfs_dire
        if (unlikely(!sd))
                return;
  
 -      if (likely(!ignore_lockdep(sd)))
 +      if (likely(!sysfs_ignore_lockdep(sd)))
                rwsem_release(&sd->dep_map, 1, _RET_IP_);
        v = atomic_dec_return(&sd->s_active);
        if (likely(v != SD_DEACTIVATED_BIAS))
@@@ -291,6 -297,7 +291,6 @@@ static int sysfs_dentry_delete(const st
  static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
  {
        struct sysfs_dirent *sd;
 -      int type;
  
        if (flags & LOOKUP_RCU)
                return -ECHILD;
                goto out_bad;
  
        /* The sysfs dirent has been moved to a different namespace */
 -      type = KOBJ_NS_TYPE_NONE;
 -      if (sd->s_parent) {
 -              type = sysfs_ns_type(sd->s_parent);
 -              if (type != KOBJ_NS_TYPE_NONE &&
 -                              sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns)
 -                      goto out_bad;
 -      }
 +      if (sd->s_ns && sd->s_ns != sysfs_info(dentry->d_sb)->ns)
 +              goto out_bad;
  
        mutex_unlock(&sysfs_mutex);
- out_valid:
        return 1;
  out_bad:
        /* Remove the dentry from the dcache hashes.
         * to the dcache hashes.
         */
        mutex_unlock(&sysfs_mutex);
-       /* If we have submounts we must allow the vfs caches
-        * to lie about the state of the filesystem to prevent
-        * leaks and other nasty things.
-        */
-       if (check_submounts_and_drop(dentry) != 0)
-               goto out_valid;
+       shrink_submounts_and_drop(dentry);
  
        return 0;
  }
@@@ -388,19 -393,22 +381,19 @@@ struct sysfs_dirent *sysfs_new_dirent(c
  /**
   *    sysfs_addrm_start - prepare for sysfs_dirent add/remove
   *    @acxt: pointer to sysfs_addrm_cxt to be used
 - *    @parent_sd: parent sysfs_dirent
   *
 - *    This function is called when the caller is about to add or
 - *    remove sysfs_dirent under @parent_sd.  This function acquires
 - *    sysfs_mutex.  @acxt is used to keep and pass context to
 - *    other addrm functions.
 + *    This function is called when the caller is about to add or remove
 + *    sysfs_dirent.  This function acquires sysfs_mutex.  @acxt is used
 + *    to keep and pass context to other addrm functions.
   *
   *    LOCKING:
   *    Kernel thread context (may sleep).  sysfs_mutex is locked on
   *    return.
   */
 -void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
 -                     struct sysfs_dirent *parent_sd)
 +void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt)
 +      __acquires(sysfs_mutex)
  {
        memset(acxt, 0, sizeof(*acxt));
 -      acxt->parent_sd = parent_sd;
  
        mutex_lock(&sysfs_mutex);
  }
   *    __sysfs_add_one - add sysfs_dirent to parent without warning
   *    @acxt: addrm context to use
   *    @sd: sysfs_dirent to be added
 + *    @parent_sd: the parent sysfs_dirent to add @sd to
   *
 - *    Get @acxt->parent_sd and set sd->s_parent to it and increment
 - *    nlink of parent inode if @sd is a directory and link into the
 - *    children list of the parent.
 + *    Get @parent_sd and set @sd->s_parent to it and increment nlink of
 + *    the parent inode if @sd is a directory and link into the children
 + *    list of the parent.
   *
   *    This function should be called between calls to
   *    sysfs_addrm_start() and sysfs_addrm_finish() and should be
   *    0 on success, -EEXIST if entry with the given name already
   *    exists.
   */
 -int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 +int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd,
 +                  struct sysfs_dirent *parent_sd)
  {
        struct sysfs_inode_attrs *ps_iattr;
        int ret;
  
 -      if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {
 -              WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
 -                      sysfs_ns_type(acxt->parent_sd) ? "required" : "invalid",
 -                      acxt->parent_sd->s_name, sd->s_name);
 -              return -EINVAL;
 -      }
 -
 -      sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
 -      sd->s_parent = sysfs_get(acxt->parent_sd);
 +      sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns);
 +      sd->s_parent = sysfs_get(parent_sd);
  
        ret = sysfs_link_sibling(sd);
        if (ret)
                return ret;
  
        /* Update timestamps on the parent */
 -      ps_iattr = acxt->parent_sd->s_iattr;
 +      ps_iattr = parent_sd->s_iattr;
        if (ps_iattr) {
                struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
                ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
@@@ -474,11 -487,10 +467,11 @@@ static char *sysfs_pathname(struct sysf
   *    sysfs_add_one - add sysfs_dirent to parent
   *    @acxt: addrm context to use
   *    @sd: sysfs_dirent to be added
 + *    @parent_sd: the parent sysfs_dirent to add @sd to
   *
 - *    Get @acxt->parent_sd and set sd->s_parent to it and increment
 - *    nlink of parent inode if @sd is a directory and link into the
 - *    children list of the parent.
 + *    Get @parent_sd and set @sd->s_parent to it and increment nlink of
 + *    the parent inode if @sd is a directory and link into the children
 + *    list of the parent.
   *
   *    This function should be called between calls to
   *    sysfs_addrm_start() and sysfs_addrm_finish() and should be
   *    0 on success, -EEXIST if entry with the given name already
   *    exists.
   */
 -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd,
 +                struct sysfs_dirent *parent_sd)
  {
        int ret;
  
 -      ret = __sysfs_add_one(acxt, sd);
 +      ret = __sysfs_add_one(acxt, sd, parent_sd);
        if (ret == -EEXIST) {
                char *path = kzalloc(PATH_MAX, GFP_KERNEL);
                WARN(1, KERN_WARNING
                     "sysfs: cannot create duplicate filename '%s'\n",
                     (path == NULL) ? sd->s_name
 -                                  : (sysfs_pathname(acxt->parent_sd, path),
 +                                  : (sysfs_pathname(parent_sd, path),
                                       strlcat(path, "/", PATH_MAX),
                                       strlcat(path, sd->s_name, PATH_MAX),
                                       path));
   *    LOCKING:
   *    Determined by sysfs_addrm_start().
   */
 -void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 +static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt,
 +                           struct sysfs_dirent *sd)
  {
        struct sysfs_inode_attrs *ps_iattr;
  
 -      BUG_ON(sd->s_flags & SYSFS_FLAG_REMOVED);
 +      /*
 +       * Removal can be called multiple times on the same node.  Only the
 +       * first invocation is effective and puts the base ref.
 +       */
 +      if (sd->s_flags & SYSFS_FLAG_REMOVED)
 +              return;
  
        sysfs_unlink_sibling(sd);
  
        /* Update timestamps on the parent */
 -      ps_iattr = acxt->parent_sd->s_iattr;
 +      ps_iattr = sd->s_parent->s_iattr;
        if (ps_iattr) {
                struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
                ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
   *    sysfs_mutex is released.
   */
  void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
 +      __releases(sysfs_mutex)
  {
        /* release resources acquired by sysfs_addrm_start() */
        mutex_unlock(&sysfs_mutex);
                acxt->removed = sd->u.removed_list;
  
                sysfs_deactivate(sd);
 -              unmap_bin_file(sd);
 +              sysfs_unmap_bin_file(sd);
                sysfs_put(sd);
        }
  }
   *    sysfs_find_dirent - find sysfs_dirent with the given name
   *    @parent_sd: sysfs_dirent to search under
   *    @name: name to look for
 + *    @ns: the namespace tag to use
   *
   *    Look for sysfs_dirent with name @name under @parent_sd.
   *
   *    Pointer to sysfs_dirent if found, NULL if not.
   */
  struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
 -                                     const void *ns,
 -                                     const unsigned char *name)
 +                                     const unsigned char *name,
 +                                     const void *ns)
  {
        struct rb_node *node = parent_sd->s_dir.children.rb_node;
        unsigned int hash;
  
 -      if (!!sysfs_ns_type(parent_sd) != !!ns) {
 -              WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
 -                      sysfs_ns_type(parent_sd) ? "required" : "invalid",
 -                      parent_sd->s_name, name);
 -              return NULL;
 -      }
 -
 -      hash = sysfs_name_hash(ns, name);
 +      hash = sysfs_name_hash(name, ns);
        while (node) {
                struct sysfs_dirent *sd;
                int result;
  
                sd = to_sysfs_dirent(node);
 -              result = sysfs_name_compare(hash, ns, name, sd);
 +              result = sysfs_name_compare(hash, name, ns, sd);
                if (result < 0)
                        node = node->rb_left;
                else if (result > 0)
  }
  
  /**
 - *    sysfs_get_dirent - find and get sysfs_dirent with the given name
 + *    sysfs_get_dirent_ns - find and get sysfs_dirent with the given name
   *    @parent_sd: sysfs_dirent to search under
   *    @name: name to look for
 + *    @ns: the namespace tag to use
   *
   *    Look for sysfs_dirent with name @name under @parent_sd and get
   *    it if found.
   *    RETURNS:
   *    Pointer to sysfs_dirent if found, NULL if not.
   */
 -struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
 -                                    const void *ns,
 -                                    const unsigned char *name)
 +struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd,
 +                                       const unsigned char *name,
 +                                       const void *ns)
  {
        struct sysfs_dirent *sd;
  
        mutex_lock(&sysfs_mutex);
 -      sd = sysfs_find_dirent(parent_sd, ns, name);
 +      sd = sysfs_find_dirent(parent_sd, name, ns);
        sysfs_get(sd);
        mutex_unlock(&sysfs_mutex);
  
        return sd;
  }
 -EXPORT_SYMBOL_GPL(sysfs_get_dirent);
 +EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns);
  
  static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
 -      enum kobj_ns_type type, const void *ns, const char *name,
 -      struct sysfs_dirent **p_sd)
 +                    const char *name, const void *ns,
 +                    struct sysfs_dirent **p_sd)
  {
        umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
        struct sysfs_addrm_cxt acxt;
        if (!sd)
                return -ENOMEM;
  
 -      sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);
        sd->s_ns = ns;
        sd->s_dir.kobj = kobj;
  
        /* link in */
 -      sysfs_addrm_start(&acxt, parent_sd);
 -      rc = sysfs_add_one(&acxt, sd);
 +      sysfs_addrm_start(&acxt);
 +      rc = sysfs_add_one(&acxt, sd, parent_sd);
        sysfs_addrm_finish(&acxt);
  
        if (rc == 0)
  int sysfs_create_subdir(struct kobject *kobj, const char *name,
                        struct sysfs_dirent **p_sd)
  {
 -      return create_dir(kobj, kobj->sd,
 -                        KOBJ_NS_TYPE_NONE, NULL, name, p_sd);
 +      return create_dir(kobj, kobj->sd, name, NULL, p_sd);
  }
  
  /**
 - *    sysfs_read_ns_type: return associated ns_type
 - *    @kobj: the kobject being queried
 - *
 - *    Each kobject can be tagged with exactly one namespace type
 - *    (i.e. network or user).  Return the ns_type associated with
 - *    this object if any
 + * sysfs_create_dir_ns - create a directory for an object with a namespace tag
 + * @kobj: object we're creating directory for
 + * @ns: the namespace tag to use
   */
 -static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj)
 +int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
  {
 -      const struct kobj_ns_type_operations *ops;
 -      enum kobj_ns_type type;
 -
 -      ops = kobj_child_ns_ops(kobj);
 -      if (!ops)
 -              return KOBJ_NS_TYPE_NONE;
 -
 -      type = ops->type;
 -      BUG_ON(type <= KOBJ_NS_TYPE_NONE);
 -      BUG_ON(type >= KOBJ_NS_TYPES);
 -      BUG_ON(!kobj_ns_type_registered(type));
 -
 -      return type;
 -}
 -
 -/**
 - *    sysfs_create_dir - create a directory for an object.
 - *    @kobj:          object we're creating directory for.
 - */
 -int sysfs_create_dir(struct kobject *kobj)
 -{
 -      enum kobj_ns_type type;
        struct sysfs_dirent *parent_sd, *sd;
 -      const void *ns = NULL;
        int error = 0;
  
        BUG_ON(!kobj);
        if (!parent_sd)
                return -ENOENT;
  
 -      if (sysfs_ns_type(parent_sd))
 -              ns = kobj->ktype->namespace(kobj);
 -      type = sysfs_read_ns_type(kobj);
 -
 -      error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
 +      error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd);
        if (!error)
                kobj->sd = sd;
        return error;
@@@ -720,14 -761,15 +713,14 @@@ static struct dentry *sysfs_lookup(stru
        struct sysfs_dirent *parent_sd = parent->d_fsdata;
        struct sysfs_dirent *sd;
        struct inode *inode;
 -      enum kobj_ns_type type;
 -      const void *ns;
 +      const void *ns = NULL;
  
        mutex_lock(&sysfs_mutex);
  
 -      type = sysfs_ns_type(parent_sd);
 -      ns = sysfs_info(dir->i_sb)->ns[type];
 +      if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS)
 +              ns = sysfs_info(dir->i_sb)->ns;
  
 -      sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);
 +      sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns);
  
        /* no such entry */
        if (!sd) {
@@@ -758,92 -800,41 +751,92 @@@ const struct inode_operations sysfs_dir
        .setxattr       = sysfs_setxattr,
  };
  
 -static void remove_dir(struct sysfs_dirent *sd)
 +static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos)
  {
 -      struct sysfs_addrm_cxt acxt;
 +      struct sysfs_dirent *last;
  
 -      sysfs_addrm_start(&acxt, sd->s_parent);
 -      sysfs_remove_one(&acxt, sd);
 -      sysfs_addrm_finish(&acxt);
 +      while (true) {
 +              struct rb_node *rbn;
 +
 +              last = pos;
 +
 +              if (sysfs_type(pos) != SYSFS_DIR)
 +                      break;
 +
 +              rbn = rb_first(&pos->s_dir.children);
 +              if (!rbn)
 +                      break;
 +
 +              pos = to_sysfs_dirent(rbn);
 +      }
 +
 +      return last;
  }
  
 -void sysfs_remove_subdir(struct sysfs_dirent *sd)
 +/**
 + * sysfs_next_descendant_post - find the next descendant for post-order walk
 + * @pos: the current position (%NULL to initiate traversal)
 + * @root: sysfs_dirent whose descendants to walk
 + *
 + * Find the next descendant to visit for post-order traversal of @root's
 + * descendants.  @root is included in the iteration and the last node to be
 + * visited.
 + */
 +static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos,
 +                                                     struct sysfs_dirent *root)
  {
 -      remove_dir(sd);
 -}
 +      struct rb_node *rbn;
 +
 +      lockdep_assert_held(&sysfs_mutex);
  
 +      /* if first iteration, visit leftmost descendant which may be root */
 +      if (!pos)
 +              return sysfs_leftmost_descendant(root);
 +
 +      /* if we visited @root, we're done */
 +      if (pos == root)
 +              return NULL;
  
 -static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
 +      /* if there's an unvisited sibling, visit its leftmost descendant */
 +      rbn = rb_next(&pos->s_rb);
 +      if (rbn)
 +              return sysfs_leftmost_descendant(to_sysfs_dirent(rbn));
 +
 +      /* no sibling left, visit parent */
 +      return pos->s_parent;
 +}
 +
 +void __sysfs_remove(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
  {
 -      struct sysfs_addrm_cxt acxt;
 -      struct rb_node *pos;
 +      struct sysfs_dirent *pos, *next;
  
 -      if (!dir_sd)
 +      if (!sd)
                return;
  
 -      pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
 -      sysfs_addrm_start(&acxt, dir_sd);
 -      pos = rb_first(&dir_sd->s_dir.children);
 -      while (pos) {
 -              struct sysfs_dirent *sd = to_sysfs_dirent(pos);
 -              pos = rb_next(pos);
 -              if (sysfs_type(sd) != SYSFS_DIR)
 -                      sysfs_remove_one(&acxt, sd);
 -      }
 -      sysfs_addrm_finish(&acxt);
 +      pr_debug("sysfs %s: removing\n", sd->s_name);
  
 -      remove_dir(dir_sd);
 +      next = NULL;
 +      do {
 +              pos = next;
 +              next = sysfs_next_descendant_post(pos, sd);
 +              if (pos)
 +                      sysfs_remove_one(acxt, pos);
 +      } while (next);
 +}
 +
 +/**
 + * sysfs_remove - remove a sysfs_dirent recursively
 + * @sd: the sysfs_dirent to remove
 + *
 + * Remove @sd along with all its subdirectories and files.
 + */
 +void sysfs_remove(struct sysfs_dirent *sd)
 +{
 +      struct sysfs_addrm_cxt acxt;
 +
 +      sysfs_addrm_start(&acxt);
 +      __sysfs_remove(&acxt, sd);
 +      sysfs_addrm_finish(&acxt);
  }
  
  /**
   *    the directory before we remove the directory, and we've inlined
   *    what used to be sysfs_rmdir() below, instead of calling separately.
   */
 -
  void sysfs_remove_dir(struct kobject *kobj)
  {
        struct sysfs_dirent *sd = kobj->sd;
        kobj->sd = NULL;
        spin_unlock(&sysfs_assoc_lock);
  
 -      __sysfs_remove_dir(sd);
 +      if (sd) {
 +              WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR);
 +              sysfs_remove(sd);
 +      }
  }
  
 -int sysfs_rename(struct sysfs_dirent *sd,
 -      struct sysfs_dirent *new_parent_sd, const void *new_ns,
 -      const char *new_name)
 +int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd,
 +               const char *new_name, const void *new_ns)
  {
        int error;
  
                goto out;       /* nothing to rename */
  
        error = -EEXIST;
 -      if (sysfs_find_dirent(new_parent_sd, new_ns, new_name))
 +      if (sysfs_find_dirent(new_parent_sd, new_name, new_ns))
                goto out;
  
        /* rename sysfs_dirent */
        sysfs_get(new_parent_sd);
        sysfs_put(sd->s_parent);
        sd->s_ns = new_ns;
 -      sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
 +      sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns);
        sd->s_parent = new_parent_sd;
        sysfs_link_sibling(sd);
  
        return error;
  }
  
 -int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
 +int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
 +                      const void *new_ns)
  {
        struct sysfs_dirent *parent_sd = kobj->sd->s_parent;
 -      const void *new_ns = NULL;
 -
 -      if (sysfs_ns_type(parent_sd))
 -              new_ns = kobj->ktype->namespace(kobj);
  
 -      return sysfs_rename(kobj->sd, parent_sd, new_ns, new_name);
 +      return sysfs_rename(kobj->sd, parent_sd, new_name, new_ns);
  }
  
 -int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
 +int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
 +                    const void *new_ns)
  {
        struct sysfs_dirent *sd = kobj->sd;
        struct sysfs_dirent *new_parent_sd;
 -      const void *new_ns = NULL;
  
        BUG_ON(!sd->s_parent);
 -      if (sysfs_ns_type(sd->s_parent))
 -              new_ns = kobj->ktype->namespace(kobj);
        new_parent_sd = new_parent_kobj && new_parent_kobj->sd ?
                new_parent_kobj->sd : &sysfs_root;
  
 -      return sysfs_rename(sd, new_parent_sd, new_ns, sd->s_name);
 +      return sysfs_rename(sd, new_parent_sd, sd->s_name, new_ns);
  }
  
  /* Relationship between s_mode and the DT_xxx types */
@@@ -1000,15 -995,15 +993,15 @@@ static int sysfs_readdir(struct file *f
        struct dentry *dentry = file->f_path.dentry;
        struct sysfs_dirent *parent_sd = dentry->d_fsdata;
        struct sysfs_dirent *pos = file->private_data;
 -      enum kobj_ns_type type;
 -      const void *ns;
 -
 -      type = sysfs_ns_type(parent_sd);
 -      ns = sysfs_info(dentry->d_sb)->ns[type];
 +      const void *ns = NULL;
  
        if (!dir_emit_dots(file, ctx))
                return 0;
        mutex_lock(&sysfs_mutex);
 +
 +      if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS)
 +              ns = sysfs_info(dentry->d_sb)->ns;
 +
        for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos);
             pos;
             pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) {
diff --combined include/linux/dcache.h
index 716c3760ee3970908577ccdb4ea32fa0275d7e33,17948b49f3d52951914f4d1f3d4acdaf93183846..ae57848f92d924255e417ede6c38ceb258fc9d1c
@@@ -224,7 -224,6 +224,7 @@@ static inline int dname_external(const 
  extern void d_instantiate(struct dentry *, struct inode *);
  extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
  extern struct dentry * d_materialise_unique(struct dentry *, struct inode *);
 +extern int d_instantiate_no_diralias(struct dentry *, struct inode *);
  extern void __d_drop(struct dentry *dentry);
  extern void d_drop(struct dentry *dentry);
  extern void d_delete(struct dentry *);
@@@ -255,7 -254,8 +255,8 @@@ extern void d_prune_aliases(struct inod
  
  /* test whether we have any submounts in a subdir tree */
  extern int have_submounts(struct dentry *);
- extern int check_submounts_and_drop(struct dentry *);
+ extern void detach_submounts(struct dentry *dentry);
+ extern void shrink_submounts_and_drop(struct dentry *);
  
  /*
   * This adds the entry to the hash queues.