rk: revert to v3.10
[firefly-linux-kernel-4.4.55.git] / fs / debugfs / inode.c
index c7c83ff0f752ce6a86d6e42a420e34ed996ae8ca..4888cb3fdef76037b7af5f9db898bdcb82ce48c2 100644 (file)
@@ -533,7 +533,8 @@ EXPORT_SYMBOL_GPL(debugfs_remove);
  */
 void debugfs_remove_recursive(struct dentry *dentry)
 {
-       struct dentry *child, *next, *parent;
+       struct dentry *child;
+       struct dentry *parent;
 
        if (IS_ERR_OR_NULL(dentry))
                return;
@@ -543,37 +544,61 @@ void debugfs_remove_recursive(struct dentry *dentry)
                return;
 
        parent = dentry;
- down:
        mutex_lock(&parent->d_inode->i_mutex);
-       list_for_each_entry_safe(child, next, &parent->d_subdirs, d_u.d_child) {
-               if (!debugfs_positive(child))
-                       continue;
 
-               /* perhaps simple_empty(child) makes more sense */
+       while (1) {
+               /*
+                * When all dentries under "parent" has been removed,
+                * walk up the tree until we reach our starting point.
+                */
+               if (list_empty(&parent->d_subdirs)) {
+                       mutex_unlock(&parent->d_inode->i_mutex);
+                       if (parent == dentry)
+                               break;
+                       parent = parent->d_parent;
+                       mutex_lock(&parent->d_inode->i_mutex);
+               }
+               child = list_entry(parent->d_subdirs.next, struct dentry,
+                               d_u.d_child);
+ next_sibling:
+
+               /*
+                * If "child" isn't empty, walk down the tree and
+                * remove all its descendants first.
+                */
                if (!list_empty(&child->d_subdirs)) {
                        mutex_unlock(&parent->d_inode->i_mutex);
                        parent = child;
-                       goto down;
+                       mutex_lock(&parent->d_inode->i_mutex);
+                       continue;
                }
- up:
-               if (!__debugfs_remove(child, parent))
-                       simple_release_fs(&debugfs_mount, &debugfs_mount_count);
-       }
-
-       mutex_unlock(&parent->d_inode->i_mutex);
-       child = parent;
-       parent = parent->d_parent;
-       mutex_lock(&parent->d_inode->i_mutex);
+               __debugfs_remove(child, parent);
+               if (parent->d_subdirs.next == &child->d_u.d_child) {
+                       /*
+                        * Try the next sibling.
+                        */
+                       if (child->d_u.d_child.next != &parent->d_subdirs) {
+                               child = list_entry(child->d_u.d_child.next,
+                                                  struct dentry,
+                                                  d_u.d_child);
+                               goto next_sibling;
+                       }
 
-       if (child != dentry) {
-               next = list_entry(child->d_u.d_child.next, struct dentry,
-                                       d_u.d_child);
-               goto up;
+                       /*
+                        * Avoid infinite loop if we fail to remove
+                        * one dentry.
+                        */
+                       mutex_unlock(&parent->d_inode->i_mutex);
+                       break;
+               }
+               simple_release_fs(&debugfs_mount, &debugfs_mount_count);
        }
 
-       if (!__debugfs_remove(child, parent))
-               simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+       parent = dentry->d_parent;
+       mutex_lock(&parent->d_inode->i_mutex);
+       __debugfs_remove(dentry, parent);
        mutex_unlock(&parent->d_inode->i_mutex);
+       simple_release_fs(&debugfs_mount, &debugfs_mount_count);
 }
 EXPORT_SYMBOL_GPL(debugfs_remove_recursive);