sd->s_parent->s_flags &= ~SYSFS_FLAG_HAS_NS;
}
-#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;
-}
-
-#endif
-
/**
* sysfs_get_active - get an active reference to sysfs_dirent
* @sd: sysfs_dirent to get an active reference to
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;
}
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))
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;
}
* 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;
acxt->removed = sd->u.removed_list;
sysfs_deactivate(sd);
- unmap_bin_file(sd);
+ sysfs_unmap_bin_file(sd);
sysfs_put(sd);
}
}
.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);
- 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;
+
+ /* 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));
-static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
+ /* 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);
+ pr_debug("sysfs %s: removing\n", sd->s_name);
+
+ 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);
- 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_remove(&acxt, sd);
sysfs_addrm_finish(&acxt);
-
- remove_dir(dir_sd);
}
/**
* 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,