Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Jan 2012 18:29:21 +0000 (10:29 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Jan 2012 18:29:21 +0000 (10:29 -0800)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client:
  ceph: ensure prealloc_blob is in place when removing xattr
  rbd: initialize snap_rwsem in rbd_add()
  ceph: enable/disable dentry complete flags via mount option
  vfs: export symbol d_find_any_alias()
  ceph: always initialize the dentry in open_root_dentry()
  libceph: remove useless return value for osd_client __send_request()
  ceph: avoid iput() while holding spinlock in ceph_dir_fsync
  ceph: avoid useless dget/dput in encode_fh
  ceph: dereference pointer after checking for NULL
  crush: fix force for non-root TAKE
  ceph: remove unnecessary d_fsdata conditional checks
  ceph: Use kmemdup rather than duplicating its implementation

Fix up conflicts in fs/ceph/super.c (d_alloc_root() failure handling vs
always initialize the dentry in open_root_dentry)

1  2 
fs/ceph/dir.c
fs/ceph/inode.c
fs/ceph/super.c
fs/ceph/super.h
fs/dcache.c
include/linux/dcache.h

diff --combined fs/ceph/dir.c
index 74fd74719dc2c5769b0c2982dab7d2b968bf25d5,5259abfb5dd947a69feb169f2f2c9f358eb9756a..618246bc2196eee020f6c53ccf789d784603e090
@@@ -666,7 -666,7 +666,7 @@@ int ceph_handle_notrace_create(struct i
  }
  
  static int ceph_mknod(struct inode *dir, struct dentry *dentry,
 -                    int mode, dev_t rdev)
 +                    umode_t mode, dev_t rdev)
  {
        struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
        struct ceph_mds_client *mdsc = fsc->mdsc;
        if (ceph_snap(dir) != CEPH_NOSNAP)
                return -EROFS;
  
 -      dout("mknod in dir %p dentry %p mode 0%o rdev %d\n",
 +      dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n",
             dir, dentry, mode, rdev);
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS);
        if (IS_ERR(req)) {
        return err;
  }
  
 -static int ceph_create(struct inode *dir, struct dentry *dentry, int mode,
 +static int ceph_create(struct inode *dir, struct dentry *dentry, umode_t mode,
                       struct nameidata *nd)
  {
        dout("create in dir %p dentry %p name '%.*s'\n",
@@@ -753,7 -753,7 +753,7 @@@ static int ceph_symlink(struct inode *d
        return err;
  }
  
 -static int ceph_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 +static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
  {
        struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
        struct ceph_mds_client *mdsc = fsc->mdsc;
                dout("mksnap dir %p snap '%.*s' dn %p\n", dir,
                     dentry->d_name.len, dentry->d_name.name, dentry);
        } else if (ceph_snap(dir) == CEPH_NOSNAP) {
 -              dout("mkdir dir %p dn %p mode 0%o\n", dir, dentry, mode);
 +              dout("mkdir dir %p dn %p mode 0%ho\n", dir, dentry, mode);
                op = CEPH_MDS_OP_MKDIR;
        } else {
                goto out;
@@@ -870,7 -870,7 +870,7 @@@ static int ceph_unlink(struct inode *di
        } else if (ceph_snap(dir) == CEPH_NOSNAP) {
                dout("unlink/rmdir dir %p dn %p inode %p\n",
                     dir, dentry, inode);
 -              op = ((dentry->d_inode->i_mode & S_IFMT) == S_IFDIR) ?
 +              op = S_ISDIR(dentry->d_inode->i_mode) ?
                        CEPH_MDS_OP_RMDIR : CEPH_MDS_OP_UNLINK;
        } else
                goto out;
@@@ -973,7 -973,7 +973,7 @@@ static int dentry_lease_is_valid(struc
  
        spin_lock(&dentry->d_lock);
        di = ceph_dentry(dentry);
-       if (di && di->lease_session) {
+       if (di->lease_session) {
                s = di->lease_session;
                spin_lock(&s->s_cap_lock);
                gen = s->s_cap_gen;
@@@ -1072,13 -1072,11 +1072,11 @@@ static void ceph_d_release(struct dentr
        struct ceph_dentry_info *di = ceph_dentry(dentry);
  
        dout("d_release %p\n", dentry);
-       if (di) {
-               ceph_dentry_lru_del(dentry);
-               if (di->lease_session)
-                       ceph_put_mds_session(di->lease_session);
-               kmem_cache_free(ceph_dentry_cachep, di);
-               dentry->d_fsdata = NULL;
-       }
+       ceph_dentry_lru_del(dentry);
+       if (di->lease_session)
+               ceph_put_mds_session(di->lease_session);
+       kmem_cache_free(ceph_dentry_cachep, di);
+       dentry->d_fsdata = NULL;
  }
  
  static int ceph_snapdir_d_revalidate(struct dentry *dentry,
   */
  void ceph_dir_set_complete(struct inode *inode)
  {
-       /* not yet implemented */
+       struct dentry *dentry = d_find_any_alias(inode);
+       
+       if (dentry && ceph_dentry(dentry) &&
+           ceph_test_mount_opt(ceph_sb_to_client(dentry->d_sb), DCACHE)) {
+               dout(" marking %p (%p) complete\n", inode, dentry);
+               set_bit(CEPH_D_COMPLETE, &ceph_dentry(dentry)->flags);
+       }
+       dput(dentry);
  }
  
  void ceph_dir_clear_complete(struct inode *inode)
  {
-       /* not yet implemented */
+       struct dentry *dentry = d_find_any_alias(inode);
+       if (dentry && ceph_dentry(dentry)) {
+               dout(" marking %p (%p) complete\n", inode, dentry);
+               set_bit(CEPH_D_COMPLETE, &ceph_dentry(dentry)->flags);
+       }
+       dput(dentry);
  }
  
  bool ceph_dir_test_complete(struct inode *inode)
  {
-       /* not yet implemented */
+       struct dentry *dentry = d_find_any_alias(inode);
+       if (dentry && ceph_dentry(dentry)) {
+               dout(" marking %p (%p) NOT complete\n", inode, dentry);
+               clear_bit(CEPH_D_COMPLETE, &ceph_dentry(dentry)->flags);
+       }
+       dput(dentry);
        return false;
  }
  
@@@ -1220,6 -1237,7 +1237,7 @@@ static int ceph_dir_fsync(struct file *
        do {
                ceph_mdsc_get_request(req);
                spin_unlock(&ci->i_unsafe_lock);
                dout("dir_fsync %p wait on tid %llu (until %llu)\n",
                     inode, req->r_tid, last_tid);
                if (req->r_timeout) {
                } else {
                        wait_for_completion(&req->r_safe_completion);
                }
-               spin_lock(&ci->i_unsafe_lock);
                ceph_mdsc_put_request(req);
  
+               spin_lock(&ci->i_unsafe_lock);
                if (ret || list_empty(head))
                        break;
                req = list_entry(head->next,
@@@ -1259,13 -1277,11 +1277,11 @@@ void ceph_dentry_lru_add(struct dentry 
  
        dout("dentry_lru_add %p %p '%.*s'\n", di, dn,
             dn->d_name.len, dn->d_name.name);
-       if (di) {
-               mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
-               spin_lock(&mdsc->dentry_lru_lock);
-               list_add_tail(&di->lru, &mdsc->dentry_lru);
-               mdsc->num_dentry++;
-               spin_unlock(&mdsc->dentry_lru_lock);
-       }
+       mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
+       spin_lock(&mdsc->dentry_lru_lock);
+       list_add_tail(&di->lru, &mdsc->dentry_lru);
+       mdsc->num_dentry++;
+       spin_unlock(&mdsc->dentry_lru_lock);
  }
  
  void ceph_dentry_lru_touch(struct dentry *dn)
  
        dout("dentry_lru_touch %p %p '%.*s' (offset %lld)\n", di, dn,
             dn->d_name.len, dn->d_name.name, di->offset);
-       if (di) {
-               mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
-               spin_lock(&mdsc->dentry_lru_lock);
-               list_move_tail(&di->lru, &mdsc->dentry_lru);
-               spin_unlock(&mdsc->dentry_lru_lock);
-       }
+       mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
+       spin_lock(&mdsc->dentry_lru_lock);
+       list_move_tail(&di->lru, &mdsc->dentry_lru);
+       spin_unlock(&mdsc->dentry_lru_lock);
  }
  
  void ceph_dentry_lru_del(struct dentry *dn)
  
        dout("dentry_lru_del %p %p '%.*s'\n", di, dn,
             dn->d_name.len, dn->d_name.name);
-       if (di) {
-               mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
-               spin_lock(&mdsc->dentry_lru_lock);
-               list_del_init(&di->lru);
-               mdsc->num_dentry--;
-               spin_unlock(&mdsc->dentry_lru_lock);
-       }
+       mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
+       spin_lock(&mdsc->dentry_lru_lock);
+       list_del_init(&di->lru);
+       mdsc->num_dentry--;
+       spin_unlock(&mdsc->dentry_lru_lock);
  }
  
  /*
diff --combined fs/ceph/inode.c
index 25283e7a37f81ecf8ba3606a957a1efc7481afa3,f556e76c72e3284caf2c516b01b09b1ce0949aef..2c489378b4cd6b470d06ecc9f3c60fc67ea6e1cf
@@@ -384,6 -384,7 +384,6 @@@ static void ceph_i_callback(struct rcu_
        struct inode *inode = container_of(head, struct inode, i_rcu);
        struct ceph_inode_info *ci = ceph_inode(inode);
  
 -      INIT_LIST_HEAD(&inode->i_dentry);
        kmem_cache_free(ceph_inode_cachep, ci);
  }
  
@@@ -850,11 -851,12 +850,12 @@@ static void ceph_set_dentry_offset(stru
  {
        struct dentry *dir = dn->d_parent;
        struct inode *inode = dir->d_inode;
-       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_inode_info *ci;
        struct ceph_dentry_info *di;
  
        BUG_ON(!inode);
  
+       ci = ceph_inode(inode);
        di = ceph_dentry(dn);
  
        spin_lock(&ci->i_ceph_lock);
diff --combined fs/ceph/super.c
index 48f61a12af66eecdd57f792c2b5da44d5c86468f,9c62fe02ce05f259d7b9e90f7c021f97ddb00048..00de2c9568cd89b13af544b87bc2f43acefa8b30
@@@ -131,6 -131,8 +131,8 @@@ enum 
        Opt_rbytes,
        Opt_norbytes,
        Opt_noasyncreaddir,
+       Opt_dcache,
+       Opt_nodcache,
        Opt_ino32,
  };
  
@@@ -152,6 -154,8 +154,8 @@@ static match_table_t fsopt_tokens = 
        {Opt_rbytes, "rbytes"},
        {Opt_norbytes, "norbytes"},
        {Opt_noasyncreaddir, "noasyncreaddir"},
+       {Opt_dcache, "dcache"},
+       {Opt_nodcache, "nodcache"},
        {Opt_ino32, "ino32"},
        {-1, NULL}
  };
@@@ -231,6 -235,12 +235,12 @@@ static int parse_fsopt_token(char *c, v
        case Opt_noasyncreaddir:
                fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
                break;
+       case Opt_dcache:
+               fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
+               break;
+       case Opt_nodcache:
+               fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
+               break;
        case Opt_ino32:
                fsopt->flags |= CEPH_MOUNT_OPT_INO32;
                break;
@@@ -341,11 -351,11 +351,11 @@@ out
  /**
   * ceph_show_options - Show mount options in /proc/mounts
   * @m: seq_file to write to
 - * @mnt: mount descriptor
 + * @root: root of that (sub)tree
   */
 -static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt)
 +static int ceph_show_options(struct seq_file *m, struct dentry *root)
  {
 -      struct ceph_fs_client *fsc = ceph_sb_to_client(mnt->mnt_sb);
 +      struct ceph_fs_client *fsc = ceph_sb_to_client(root->d_sb);
        struct ceph_mount_options *fsopt = fsc->mount_options;
        struct ceph_options *opt = fsc->client->options;
  
                seq_puts(m, ",norbytes");
        if (fsopt->flags & CEPH_MOUNT_OPT_NOASYNCREADDIR)
                seq_puts(m, ",noasyncreaddir");
+       if (fsopt->flags & CEPH_MOUNT_OPT_DCACHE)
+               seq_puts(m, ",dcache");
+       else
+               seq_puts(m, ",nodcache");
  
        if (fsopt->wsize)
                seq_printf(m, ",wsize=%d", fsopt->wsize);
@@@ -636,26 -650,18 +650,26 @@@ static struct dentry *open_root_dentry(
        req->r_num_caps = 2;
        err = ceph_mdsc_do_request(mdsc, NULL, req);
        if (err == 0) {
 +              struct inode *inode = req->r_target_inode;
 +              req->r_target_inode = NULL;
                dout("open_root_inode success\n");
 -              if (ceph_ino(req->r_target_inode) == CEPH_INO_ROOT &&
 -                  fsc->sb->s_root == NULL)
 -                      root = d_alloc_root(req->r_target_inode);
 -              else
 -                      root = d_obtain_alias(req->r_target_inode);
 +              if (ceph_ino(inode) == CEPH_INO_ROOT &&
 +                  fsc->sb->s_root == NULL) {
 +                      root = d_alloc_root(inode);
 +                      if (!root) {
 +                              iput(inode);
 +                              root = ERR_PTR(-ENOMEM);
 +                              goto out;
 +                      }
-                       ceph_init_dentry(root);
 +              } else {
 +                      root = d_obtain_alias(inode);
 +              }
+               ceph_init_dentry(root);
 -              req->r_target_inode = NULL;
                dout("open_root_inode success, root dentry is %p\n", root);
        } else {
                root = ERR_PTR(err);
        }
 +out:
        ceph_mdsc_put_request(req);
        return root;
  }
diff --combined fs/ceph/super.h
index cb3652b37271fbc383f784ff35c48bb48c17ff32,140f99f978c4052c70676f683334c51c4da1947a..1421f3d875a22e34e1449bde4d46327678d114fe
@@@ -28,6 -28,7 +28,7 @@@
  #define CEPH_MOUNT_OPT_RBYTES          (1<<5) /* dir st_bytes = rbytes */
  #define CEPH_MOUNT_OPT_NOASYNCREADDIR  (1<<7) /* no dcache readdir */
  #define CEPH_MOUNT_OPT_INO32           (1<<8) /* 32 bit inos */
+ #define CEPH_MOUNT_OPT_DCACHE          (1<<9) /* use dcache for readdir etc */
  
  #define CEPH_MOUNT_OPT_DEFAULT    (CEPH_MOUNT_OPT_RBYTES)
  
@@@ -136,7 -137,7 +137,7 @@@ struct ceph_cap_snap 
        int issued, dirty;
        struct ceph_snap_context *context;
  
 -      mode_t mode;
 +      umode_t mode;
        uid_t uid;
        gid_t gid;
  
diff --combined fs/dcache.c
index 616fedff011a7080c36b6446a33b3479c9c7e7d9,ba960051dfb73130559e62eaf6c8d780376636f7..16a53cc2cc02e079554742bfcd95c1f92d3608d4
@@@ -38,7 -38,6 +38,7 @@@
  #include <linux/prefetch.h>
  #include <linux/ratelimit.h>
  #include "internal.h"
 +#include "mount.h"
  
  /*
   * Usage:
@@@ -243,7 -242,6 +243,7 @@@ static void dentry_lru_add(struct dentr
  static void __dentry_lru_del(struct dentry *dentry)
  {
        list_del_init(&dentry->d_lru);
 +      dentry->d_flags &= ~DCACHE_SHRINK_LIST;
        dentry->d_sb->s_nr_dentry_unused--;
        dentry_stat.nr_unused--;
  }
@@@ -277,15 -275,15 +277,15 @@@ static void dentry_lru_prune(struct den
        }
  }
  
 -static void dentry_lru_move_tail(struct dentry *dentry)
 +static void dentry_lru_move_list(struct dentry *dentry, struct list_head *list)
  {
        spin_lock(&dcache_lru_lock);
        if (list_empty(&dentry->d_lru)) {
 -              list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 +              list_add_tail(&dentry->d_lru, list);
                dentry->d_sb->s_nr_dentry_unused++;
                dentry_stat.nr_unused++;
        } else {
 -              list_move_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 +              list_move_tail(&dentry->d_lru, list);
        }
        spin_unlock(&dcache_lru_lock);
  }
@@@ -771,18 -769,14 +771,18 @@@ static void shrink_dentry_list(struct l
  }
  
  /**
 - * __shrink_dcache_sb - shrink the dentry LRU on a given superblock
 - * @sb:               superblock to shrink dentry LRU.
 - * @count:    number of entries to prune
 - * @flags:    flags to control the dentry processing
 + * prune_dcache_sb - shrink the dcache
 + * @sb: superblock
 + * @count: number of entries to try to free
 + *
 + * Attempt to shrink the superblock dcache LRU by @count entries. This is
 + * done when we need more memory an called from the superblock shrinker
 + * function.
   *
 - * If flags contains DCACHE_REFERENCED reference dentries will not be pruned.
 + * This function may fail to free any resources if all the dentries are in
 + * use.
   */
 -static void __shrink_dcache_sb(struct super_block *sb, int count, int flags)
 +void prune_dcache_sb(struct super_block *sb, int count)
  {
        struct dentry *dentry;
        LIST_HEAD(referenced);
@@@ -801,13 -795,18 +801,13 @@@ relock
                        goto relock;
                }
  
 -              /*
 -               * If we are honouring the DCACHE_REFERENCED flag and the
 -               * dentry has this flag set, don't free it.  Clear the flag
 -               * and put it back on the LRU.
 -               */
 -              if (flags & DCACHE_REFERENCED &&
 -                              dentry->d_flags & DCACHE_REFERENCED) {
 +              if (dentry->d_flags & DCACHE_REFERENCED) {
                        dentry->d_flags &= ~DCACHE_REFERENCED;
                        list_move(&dentry->d_lru, &referenced);
                        spin_unlock(&dentry->d_lock);
                } else {
                        list_move_tail(&dentry->d_lru, &tmp);
 +                      dentry->d_flags |= DCACHE_SHRINK_LIST;
                        spin_unlock(&dentry->d_lock);
                        if (!--count)
                                break;
        shrink_dentry_list(&tmp);
  }
  
 -/**
 - * prune_dcache_sb - shrink the dcache
 - * @sb: superblock
 - * @nr_to_scan: number of entries to try to free
 - *
 - * Attempt to shrink the superblock dcache LRU by @nr_to_scan entries. This is
 - * done when we need more memory an called from the superblock shrinker
 - * function.
 - *
 - * This function may fail to free any resources if all the dentries are in
 - * use.
 - */
 -void prune_dcache_sb(struct super_block *sb, int nr_to_scan)
 -{
 -      __shrink_dcache_sb(sb, nr_to_scan, DCACHE_REFERENCED);
 -}
 -
  /**
   * shrink_dcache_sb - shrink dcache for a superblock
   * @sb: superblock
@@@ -1075,7 -1091,7 +1075,7 @@@ EXPORT_SYMBOL(have_submounts)
   * drop the lock and return early due to latency
   * constraints.
   */
 -static int select_parent(struct dentry * parent)
 +static int select_parent(struct dentry *parent, struct list_head *dispose)
  {
        struct dentry *this_parent;
        struct list_head *next;
@@@ -1097,21 -1113,17 +1097,21 @@@ resume
  
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
  
 -              /* 
 -               * move only zero ref count dentries to the end 
 -               * of the unused list for prune_dcache
 +              /*
 +               * move only zero ref count dentries to the dispose list.
 +               *
 +               * Those which are presently on the shrink list, being processed
 +               * by shrink_dentry_list(), shouldn't be moved.  Otherwise the
 +               * loop in shrink_dcache_parent() might not make any progress
 +               * and loop forever.
                 */
 -              if (!dentry->d_count) {
 -                      dentry_lru_move_tail(dentry);
 -                      found++;
 -              } else {
 +              if (dentry->d_count) {
                        dentry_lru_del(dentry);
 +              } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
 +                      dentry_lru_move_list(dentry, dispose);
 +                      dentry->d_flags |= DCACHE_SHRINK_LIST;
 +                      found++;
                }
 -
                /*
                 * We can return to the caller if we have found some (this
                 * ensures forward progress). We'll be coming back to find
@@@ -1168,13 -1180,14 +1168,13 @@@ rename_retry
   *
   * Prune the dcache to remove unused children of the parent dentry.
   */
 - 
  void shrink_dcache_parent(struct dentry * parent)
  {
 -      struct super_block *sb = parent->d_sb;
 +      LIST_HEAD(dispose);
        int found;
  
 -      while ((found = select_parent(parent)) != 0)
 -              __shrink_dcache_sb(sb, found, 0);
 +      while ((found = select_parent(parent, &dispose)) != 0)
 +              shrink_dentry_list(&dispose);
  }
  EXPORT_SYMBOL(shrink_dcache_parent);
  
@@@ -1447,23 -1460,6 +1447,23 @@@ struct dentry * d_alloc_root(struct ino
  }
  EXPORT_SYMBOL(d_alloc_root);
  
 +struct dentry *d_make_root(struct inode *root_inode)
 +{
 +      struct dentry *res = NULL;
 +
 +      if (root_inode) {
 +              static const struct qstr name = { .name = "/", .len = 1 };
 +
 +              res = __d_alloc(root_inode->i_sb, &name);
 +              if (res)
 +                      d_instantiate(res, root_inode);
 +              else
 +                      iput(root_inode);
 +      }
 +      return res;
 +}
 +EXPORT_SYMBOL(d_make_root);
 +
  static struct dentry * __d_find_any_alias(struct inode *inode)
  {
        struct dentry *alias;
        return alias;
  }
  
- static struct dentry * d_find_any_alias(struct inode *inode)
+ /**
+  * d_find_any_alias - find any alias for a given inode
+  * @inode: inode to find an alias for
+  *
+  * If any aliases exist for the given inode, take and return a
+  * reference for one of them.  If no aliases exist, return %NULL.
+  */
+ struct dentry *d_find_any_alias(struct inode *inode)
  {
        struct dentry *de;
  
        spin_unlock(&inode->i_lock);
        return de;
  }
+ EXPORT_SYMBOL(d_find_any_alias);
  
  /**
   * d_obtain_alias - find or allocate a dentry for a given inode
@@@ -2455,7 -2458,6 +2462,7 @@@ static int prepend_path(const struct pa
  {
        struct dentry *dentry = path->dentry;
        struct vfsmount *vfsmnt = path->mnt;
 +      struct mount *mnt = real_mount(vfsmnt);
        bool slash = false;
        int error = 0;
  
  
                if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
                        /* Global root? */
 -                      if (vfsmnt->mnt_parent == vfsmnt) {
 +                      if (!mnt_has_parent(mnt))
                                goto global_root;
 -                      }
 -                      dentry = vfsmnt->mnt_mountpoint;
 -                      vfsmnt = vfsmnt->mnt_parent;
 +                      dentry = mnt->mnt_mountpoint;
 +                      mnt = mnt->mnt_parent;
 +                      vfsmnt = &mnt->mnt;
                        continue;
                }
                parent = dentry->d_parent;
@@@ -2506,7 -2508,7 +2513,7 @@@ global_root
        if (!slash)
                error = prepend(buffer, buflen, "/", 1);
        if (!error)
 -              error = vfsmnt->mnt_ns ? 1 : 2;
 +              error = real_mount(vfsmnt)->mnt_ns ? 1 : 2;
        goto out;
  }
  
@@@ -2858,6 -2860,31 +2865,6 @@@ int is_subdir(struct dentry *new_dentry
        return result;
  }
  
 -int path_is_under(struct path *path1, struct path *path2)
 -{
 -      struct vfsmount *mnt = path1->mnt;
 -      struct dentry *dentry = path1->dentry;
 -      int res;
 -
 -      br_read_lock(vfsmount_lock);
 -      if (mnt != path2->mnt) {
 -              for (;;) {
 -                      if (mnt->mnt_parent == mnt) {
 -                              br_read_unlock(vfsmount_lock);
 -                              return 0;
 -                      }
 -                      if (mnt->mnt_parent == path2->mnt)
 -                              break;
 -                      mnt = mnt->mnt_parent;
 -              }
 -              dentry = mnt->mnt_mountpoint;
 -      }
 -      res = is_subdir(dentry, path2->dentry);
 -      br_read_unlock(vfsmount_lock);
 -      return res;
 -}
 -EXPORT_SYMBOL(path_is_under);
 -
  void d_genocide(struct dentry *root)
  {
        struct dentry *this_parent;
diff --combined include/linux/dcache.h
index 31f73220e7d71a888e0fcb895f2de54b25ecea29,3871ba743b4cb362a13c82abf9094445ef84a8c4..d64a55b23afda64f4b490ef475e092edcf3c32b7
@@@ -203,7 -203,6 +203,7 @@@ struct dentry_operations 
  
  #define DCACHE_CANT_MOUNT     0x0100
  #define DCACHE_GENOCIDE               0x0200
 +#define DCACHE_SHRINK_LIST    0x0400
  
  #define DCACHE_NFSFS_RENAMED  0x1000
       /* this dentry has been "silly renamed" and has to be deleted on the last
@@@ -242,6 -241,7 +242,7 @@@ extern struct dentry * d_alloc(struct d
  extern struct dentry * d_alloc_pseudo(struct super_block *, const struct qstr *);
  extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
  extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
+ extern struct dentry *d_find_any_alias(struct inode *inode);
  extern struct dentry * d_obtain_alias(struct inode *);
  extern void shrink_dcache_sb(struct super_block *);
  extern void shrink_dcache_parent(struct dentry *);
@@@ -250,7 -250,6 +251,7 @@@ extern int d_invalidate(struct dentry *
  
  /* only used at mount-time */
  extern struct dentry * d_alloc_root(struct inode *);
 +extern struct dentry * d_make_root(struct inode *);
  
  /* <clickety>-<click> the ramfs-type tree */
  extern void d_genocide(struct dentry *);