Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / fs / dcache.c
index 1f8e6acb0ea4ff2507382218fa9fa2e51f5f62fc..d5a23fd0da903848682c659085a6c560e48d8a8c 100644 (file)
@@ -264,6 +264,11 @@ static void __d_free_external(struct rcu_head *head)
        kmem_cache_free(dentry_cache, dentry); 
 }
 
+static inline int dname_external(const struct dentry *dentry)
+{
+       return dentry->d_name.name != dentry->d_iname;
+}
+
 static void dentry_free(struct dentry *dentry)
 {
        if (unlikely(dname_external(dentry))) {
@@ -482,7 +487,7 @@ static void __dentry_kill(struct dentry *dentry)
         * inform the fs via d_prune that this dentry is about to be
         * unhashed and destroyed.
         */
-       if ((dentry->d_flags & DCACHE_OP_PRUNE) && !d_unhashed(dentry))
+       if (dentry->d_flags & DCACHE_OP_PRUNE)
                dentry->d_op->d_prune(dentry);
 
        if (dentry->d_flags & DCACHE_LRU_LIST) {
@@ -645,62 +650,6 @@ kill_it:
 }
 EXPORT_SYMBOL(dput);
 
-/**
- * d_invalidate - invalidate a dentry
- * @dentry: dentry to invalidate
- *
- * Try to invalidate the dentry if it turns out to be
- * possible. If there are other dentries that can be
- * reached through this one we can't delete it and we
- * return -EBUSY. On success we return 0.
- *
- * no dcache lock.
- */
-int d_invalidate(struct dentry * dentry)
-{
-       /*
-        * If it's already been dropped, return OK.
-        */
-       spin_lock(&dentry->d_lock);
-       if (d_unhashed(dentry)) {
-               spin_unlock(&dentry->d_lock);
-               return 0;
-       }
-       /*
-        * Check whether to do a partial shrink_dcache
-        * to get rid of unused child entries.
-        */
-       if (!list_empty(&dentry->d_subdirs)) {
-               spin_unlock(&dentry->d_lock);
-               shrink_dcache_parent(dentry);
-               spin_lock(&dentry->d_lock);
-       }
-
-       /*
-        * Somebody else still using it?
-        *
-        * If it's a directory, we can't drop it
-        * for fear of somebody re-populating it
-        * with children (even though dropping it
-        * would make it unreachable from the root,
-        * we might still populate it if it was a
-        * working directory or similar).
-        * We also need to leave mountpoints alone,
-        * directory or not.
-        */
-       if (dentry->d_lockref.count > 1 && dentry->d_inode) {
-               if (S_ISDIR(dentry->d_inode->i_mode) || d_mountpoint(dentry)) {
-                       spin_unlock(&dentry->d_lock);
-                       return -EBUSY;
-               }
-       }
-
-       __d_drop(dentry);
-       spin_unlock(&dentry->d_lock);
-       return 0;
-}
-EXPORT_SYMBOL(d_invalidate);
 
 /* This must be called with d_lock held */
 static inline void __dget_dlock(struct dentry *dentry)
@@ -826,20 +775,13 @@ restart:
        hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
                spin_lock(&dentry->d_lock);
                if (!dentry->d_lockref.count) {
-                       /*
-                        * inform the fs via d_prune that this dentry
-                        * is about to be unhashed and destroyed.
-                        */
-                       if ((dentry->d_flags & DCACHE_OP_PRUNE) &&
-                           !d_unhashed(dentry))
-                               dentry->d_op->d_prune(dentry);
-
-                       __dget_dlock(dentry);
-                       __d_drop(dentry);
-                       spin_unlock(&dentry->d_lock);
-                       spin_unlock(&inode->i_lock);
-                       dput(dentry);
-                       goto restart;
+                       struct dentry *parent = lock_parent(dentry);
+                       if (likely(!dentry->d_lockref.count)) {
+                               __dentry_kill(dentry);
+                               goto restart;
+                       }
+                       if (parent)
+                               spin_unlock(&parent->d_lock);
                }
                spin_unlock(&dentry->d_lock);
        }
@@ -1220,7 +1162,7 @@ EXPORT_SYMBOL(have_submounts);
  * reachable (e.g. NFS can unhash a directory dentry and then the complete
  * subtree can become unreachable).
  *
- * Only one of check_submounts_and_drop() and d_set_mounted() must succeed.  For
+ * Only one of d_invalidate() and d_set_mounted() must succeed.  For
  * this reason take rename_lock and d_lock on dentry and ancestors.
  */
 int d_set_mounted(struct dentry *dentry)
@@ -1229,7 +1171,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. d_invalidate() */
                spin_lock(&p->d_lock);
                if (unlikely(d_unhashed(p))) {
                        spin_unlock(&p->d_lock);
@@ -1373,70 +1315,84 @@ void shrink_dcache_for_umount(struct super_block *sb)
        }
 }
 
-static enum d_walk_ret check_and_collect(void *_data, struct dentry *dentry)
+struct detach_data {
+       struct select_data select;
+       struct dentry *mountpoint;
+};
+static enum d_walk_ret detach_and_collect(void *_data, struct dentry *dentry)
 {
-       struct select_data *data = _data;
+       struct detach_data *data = _data;
 
        if (d_mountpoint(dentry)) {
-               data->found = -EBUSY;
+               __dget_dlock(dentry);
+               data->mountpoint = dentry;
                return D_WALK_QUIT;
        }
 
-       return select_collect(_data, dentry);
+       return select_collect(&data->select, dentry);
 }
 
 static void check_and_drop(void *_data)
 {
-       struct select_data *data = _data;
+       struct detach_data *data = _data;
 
-       if (d_mountpoint(data->start))
-               data->found = -EBUSY;
-       if (!data->found)
-               __d_drop(data->start);
+       if (!data->mountpoint && !data->select.found)
+               __d_drop(data->select.start);
 }
 
 /**
- * check_submounts_and_drop - prune dcache, check for submounts and drop
+ * d_invalidate - detach submounts, prune dcache, and drop
+ * @dentry: dentry to invalidate (aka detach, prune and drop)
  *
- * 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.
+ * no dcache lock.
  *
- * @dentry: dentry to prune and drop
+ * The final d_drop is done as an atomic operation relative to
+ * rename_lock ensuring there are no races with d_set_mounted.  This
+ * ensures there are no unhashed dentries on the path to a mountpoint.
  */
-int check_submounts_and_drop(struct dentry *dentry)
+void d_invalidate(struct dentry *dentry)
 {
-       int ret = 0;
+       /*
+        * If it's already been dropped, return OK.
+        */
+       spin_lock(&dentry->d_lock);
+       if (d_unhashed(dentry)) {
+               spin_unlock(&dentry->d_lock);
+               return;
+       }
+       spin_unlock(&dentry->d_lock);
 
        /* Negative dentries can be dropped without further checks */
        if (!dentry->d_inode) {
                d_drop(dentry);
-               goto out;
+               return;
        }
 
        for (;;) {
-               struct select_data data;
+               struct detach_data data;
 
-               INIT_LIST_HEAD(&data.dispose);
-               data.start = dentry;
-               data.found = 0;
+               data.mountpoint = NULL;
+               INIT_LIST_HEAD(&data.select.dispose);
+               data.select.start = dentry;
+               data.select.found = 0;
 
-               d_walk(dentry, &data, check_and_collect, check_and_drop);
-               ret = data.found;
+               d_walk(dentry, &data, detach_and_collect, check_and_drop);
 
-               if (!list_empty(&data.dispose))
-                       shrink_dentry_list(&data.dispose);
+               if (data.select.found)
+                       shrink_dentry_list(&data.select.dispose);
 
-               if (ret <= 0)
+               if (data.mountpoint) {
+                       detach_mounts(data.mountpoint);
+                       dput(data.mountpoint);
+               }
+
+               if (!data.mountpoint && !data.select.found)
                        break;
 
                cond_resched();
        }
-
-out:
-       return ret;
 }
-EXPORT_SYMBOL(check_submounts_and_drop);
+EXPORT_SYMBOL(d_invalidate);
 
 /**
  * __d_alloc   -       allocate a dcache entry
@@ -2142,10 +2098,10 @@ struct dentry *d_lookup(const struct dentry *parent, const struct qstr *name)
        struct dentry *dentry;
        unsigned seq;
 
-        do {
-                seq = read_seqbegin(&rename_lock);
-                dentry = __d_lookup(parent, name);
-                if (dentry)
+       do {
+               seq = read_seqbegin(&rename_lock);
+               dentry = __d_lookup(parent, name);
+               if (dentry)
                        break;
        } while (read_seqretry(&rename_lock, seq));
        return dentry;
@@ -2669,10 +2625,8 @@ static struct dentry *__d_unalias(struct inode *inode,
                goto out_err;
        m2 = &alias->d_parent->d_inode->i_mutex;
 out_unalias:
-       if (likely(!d_mountpoint(alias))) {
-               __d_move(alias, dentry, false);
-               ret = alias;
-       }
+       __d_move(alias, dentry, false);
+       ret = alias;
 out_err:
        spin_unlock(&inode->i_lock);
        if (m2)