[PATCH 07/22] sysfs: implement sysfs_dirent based remove interface sysfs_remove()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch implements new sysfs_remove(), which takes @sd as target
argument and can be used on any type of node.  Also, it recurses into
sub directories.

* sysfs_remove_file(), sysfs_remove_file_from_group(),
  sysfs_remove_bin_file() are reimplemented using sysfs_remove() in
  fs/sysfs/kobject.c.

* To keep backward compatibility sysfs_remove_dir() is reimplemented
  using internal function __sysfs_remove() with @recurse argument set
  to zero so that it doesn't descend into subdirectories.

This patch doesn't introduce any behavior change to the original API.

Signed-off-by: Tejun Heo <[email protected]>
---
 fs/sysfs/bin.c        |   13 -----
 fs/sysfs/dir.c        |  125 ++++++++++++++++++++++++++++++++++---------------
 fs/sysfs/file.c       |   35 --------------
 fs/sysfs/group.c      |    4 +-
 fs/sysfs/kobject.c    |   92 ++++++++++++++++++++++++++++++++++++
 fs/sysfs/symlink.c    |   13 -----
 fs/sysfs/sysfs.h      |    3 +-
 include/linux/sysfs.h |    6 ++
 8 files changed, 189 insertions(+), 102 deletions(-)

diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
index 247ea19..1c12cf0 100644
--- a/fs/sysfs/bin.c
+++ b/fs/sysfs/bin.c
@@ -237,17 +237,4 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 	return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
 }
 
-
-/**
- *	sysfs_remove_bin_file - remove binary file for object.
- *	@kobj:	object.
- *	@attr:	attribute descriptor.
- */
-
-void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
-{
-	sysfs_hash_and_remove(kobj->sd, attr->attr.name);
-}
-
 EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
-EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index b1cf090..7cb3f1e 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -816,64 +816,113 @@ const struct inode_operations sysfs_dir_inode_operations = {
 	.setattr	= sysfs_setattr,
 };
 
-static void remove_dir(struct sysfs_dirent *sd)
+/**
+ *	sysfs_tree_walk_next - walk sysfs subtree
+ *	@top: top of the subtree to walk
+ *	@cur: the current sysfs_dirent (NULL to begin walk)
+ *
+ *	Walk subtree of @top.  Each call to this function returns the
+ *	next node to visit.  The walk is descendant first,
+ *	left-to-right.  The last node naturally is @top.  The walk
+ *	order is to allow removing the previous node while walking the
+ *	tree.
+ *
+ *	LOCKING:
+ *	mutex_lock(sysfs_mutex)
+ *
+ *	RETURNS:
+ *	Next sysfs_dirent to visit, NULL if the walk is complete.
+ */
+static struct sysfs_dirent *sysfs_tree_walk_next(struct sysfs_dirent *top,
+						 struct sysfs_dirent *cur)
 {
-	struct sysfs_addrm_cxt acxt;
+	if (cur == NULL) {
+		/* we're beginning a walk, go to the deepest child */
+		cur = top;
 
-	sysfs_addrm_start(&acxt);
-	sysfs_remove_one(&acxt, sd);
-	sysfs_addrm_finish(&acxt);
-}
+		while (sysfs_type(cur) == SYSFS_DIR && cur->s_dir.children)
+			cur = cur->s_dir.children;
 
-void sysfs_remove_subdir(struct sysfs_dirent *sd)
-{
-	remove_dir(sd);
-}
+		return cur;
+	} else if (cur == top) {
+		/* walk ends at @top */
+		return NULL;
+	}
 
+	/* continue walking */
+	if (cur->s_sibling) {
+		/* if possible, go right and deep */
+		cur = cur->s_sibling;
+
+		/* go to the deepest child */
+		while (sysfs_type(cur) == SYSFS_DIR && cur->s_dir.children)
+			cur = cur->s_dir.children;
+	} else {
+		/* this subtree is done, go up */
+		cur = cur->s_parent;
+	}
+
+	return cur;
+}
 
-static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
+/**
+ *	__sysfs_remove - remove a sysfs_dirent
+ *	@sd: target sysfs_dirent to be removed
+ *	@recurse: recurse into subdirectories
+ *
+ *	Remove @sd.  If @sd is a directory, all its leaf children are
+ *	removed.  If @recurse is not zero, all the directory children
+ *	are recursively removed too.
+ *
+ *	This is an internal function to be used to implement
+ *	sysfs_remove() and sysfs_remove_dir().  @recurse is necessary
+ *	to support the original sysfs_remove_dir() semantics which
+ *	didn't remove subdirectories.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ */
+void __sysfs_remove(struct sysfs_dirent *sd, int recurse)
 {
 	struct sysfs_addrm_cxt acxt;
-	struct sysfs_dirent **pos;
+	struct sysfs_dirent *cur, *next;
 
-	if (!dir_sd)
+	/* noop on NULL */
+	if (!sd)
 		return;
 
-	pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
 	sysfs_addrm_start(&acxt);
-	pos = &dir_sd->s_dir.children;
-	while (*pos) {
-		struct sysfs_dirent *sd = *pos;
 
-		if (sysfs_type(sd) != SYSFS_DIR)
-			sysfs_remove_one(&acxt, sd);
-		else
-			pos = &(*pos)->s_sibling;
-	}
-	sysfs_addrm_finish(&acxt);
+	cur = sysfs_tree_walk_next(sd, NULL); /* prime walk */
+	do {
+		/* find out @next before removing @cur */
+		next = sysfs_tree_walk_next(sd, cur);
 
-	remove_dir(dir_sd);
+		/* if ! recursing, delete only direct leaf children and self */
+		if (!recurse && cur != sd &&
+		    (sysfs_type(cur) == SYSFS_DIR || cur->s_parent != sd))
+			continue;
+
+		sysfs_remove_one(&acxt, cur);
+	} while ((cur = next));
+
+	sysfs_addrm_finish(&acxt);
 }
 
 /**
- *	sysfs_remove_dir - remove an object's directory.
- *	@kobj:	object.
+ *	sysfs_remove - remove a sysfs_dirent and all its children recursively
+ *	@sd: target sysfs_dirent to be removed
  *
- *	The only thing special about this is that we remove any files in
- *	the directory before we remove the directory, and we've inlined
- *	what used to be sysfs_rmdir() below, instead of calling separately.
+ *	Remove @sd and all its children recursively.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
  */
-
-void sysfs_remove_dir(struct kobject * kobj)
+void sysfs_remove(struct sysfs_dirent *sd)
 {
-	struct sysfs_dirent *sd = kobj->sd;
-
-	spin_lock(&sysfs_assoc_lock);
-	kobj->sd = NULL;
-	spin_unlock(&sysfs_assoc_lock);
-
-	__sysfs_remove_dir(sd);
+	__sysfs_remove(sd, 1);
 }
+EXPORT_SYMBOL_GPL(sysfs_remove);
 
 int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
 {
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index fb77d54..1faba5f 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -681,39 +681,4 @@ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
 }
 EXPORT_SYMBOL_GPL(sysfs_chmod_file);
 
-
-/**
- *	sysfs_remove_file - remove an object attribute.
- *	@kobj:	object we're acting for.
- *	@attr:	attribute descriptor.
- *
- *	Hash the attribute name and kill the victim.
- */
-
-void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
-{
-	sysfs_hash_and_remove(kobj->sd, attr->name);
-}
-
-
-/**
- * sysfs_remove_file_from_group - remove an attribute file from a group.
- * @kobj: object we're acting for.
- * @attr: attribute descriptor.
- * @group: group name.
- */
-void sysfs_remove_file_from_group(struct kobject *kobj,
-		const struct attribute *attr, const char *group)
-{
-	struct sysfs_dirent *dir_sd;
-
-	dir_sd = sysfs_get_dirent(kobj->sd, group);
-	if (dir_sd) {
-		sysfs_hash_and_remove(dir_sd, attr->name);
-		sysfs_put(dir_sd);
-	}
-}
-EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
-
 EXPORT_SYMBOL_GPL(sysfs_create_file);
-EXPORT_SYMBOL_GPL(sysfs_remove_file);
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index d197237..cef0376 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -57,7 +57,7 @@ int sysfs_create_group(struct kobject * kobj,
 	error = create_files(sd, grp);
 	if (error) {
 		if (grp->name)
-			sysfs_remove_subdir(sd);
+			sysfs_remove(sd);
 	}
 	sysfs_put(sd);
 	return error;
@@ -77,7 +77,7 @@ void sysfs_remove_group(struct kobject * kobj,
 
 	remove_files(sd, grp);
 	if (grp->name)
-		sysfs_remove_subdir(sd);
+		sysfs_remove(sd);
 
 	sysfs_put(sd);
 }
diff --git a/fs/sysfs/kobject.c b/fs/sysfs/kobject.c
index 5ebd755..9415f18 100644
--- a/fs/sysfs/kobject.c
+++ b/fs/sysfs/kobject.c
@@ -13,3 +13,95 @@
 
 #include <linux/sysfs.h>
 #include <linux/sysfs-kobject.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include "sysfs.h"
+
+static int sysfs_remove_child(struct sysfs_dirent *parent, const char *name)
+{
+	struct sysfs_dirent *sd;
+
+	sd = sysfs_find_child(parent, name);
+	if (!sd)
+		return -ENOENT;
+
+	sysfs_remove(sd);
+	return 0;
+}
+
+/**
+ *	sysfs_remove_dir - remove an object's directory.
+ *	@kobj:	object.
+ *
+ *	Remove sysfs directory associated with @kobj.  All children
+ *	which are leaf are removed but subdirectories are left alone.
+ */
+void sysfs_remove_dir(struct kobject * kobj)
+{
+	struct sysfs_dirent *sd = kobj->sd;
+
+	spin_lock(&sysfs_assoc_lock);
+	kobj->sd = NULL;
+	spin_unlock(&sysfs_assoc_lock);
+
+	__sysfs_remove(sd, 0);
+}
+
+/**
+ *	sysfs_remove_file - remove an object attribute.
+ *	@kobj:	object we're acting for.
+ *	@attr:	attribute descriptor.
+ *
+ *	Hash the attribute name and kill the victim.
+ */
+void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
+{
+	sysfs_remove_child(kobj->sd, attr->name);
+}
+EXPORT_SYMBOL_GPL(sysfs_remove_file);
+
+/**
+ *	sysfs_remove_bin_file - remove binary file for object.
+ *	@kobj:	object.
+ *	@attr:	attribute descriptor.
+ */
+void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
+{
+	if (sysfs_remove_child(kobj->sd, attr->attr.name) < 0) {
+		printk(KERN_ERR "%s: "
+		       "bad dentry or inode or no such file: \"%s\"\n",
+		       __FUNCTION__, attr->attr.name);
+		dump_stack();
+	}
+}
+EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
+
+/**
+ *	sysfs_remove_link - remove symlink in object's directory.
+ *	@kobj:	object we're acting for.
+ *	@name:	name of the symlink to remove.
+ */
+void sysfs_remove_link(struct kobject * kobj, const char * name)
+{
+	sysfs_remove_child(kobj->sd, name);
+}
+EXPORT_SYMBOL_GPL(sysfs_remove_link);
+
+/**
+ * sysfs_remove_file_from_group - remove an attribute file from a group.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ * @group: group name.
+ */
+void sysfs_remove_file_from_group(struct kobject *kobj,
+		const struct attribute *attr, const char *group)
+{
+	struct sysfs_dirent *dir_sd;
+
+	dir_sd = sysfs_get_dirent(kobj->sd, group);
+	if (dir_sd) {
+		sysfs_remove_child(dir_sd, attr->name);
+		sysfs_put(dir_sd);
+	}
+}
+EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index 897cc2f..9c15a32 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -105,18 +105,6 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char
 	return error;
 }
 
-
-/**
- *	sysfs_remove_link - remove symlink in object's directory.
- *	@kobj:	object we're acting for.
- *	@name:	name of the symlink to remove.
- */
-
-void sysfs_remove_link(struct kobject * kobj, const char * name)
-{
-	sysfs_hash_and_remove(kobj->sd, name);
-}
-
 static int sysfs_get_target_path(struct sysfs_dirent * parent_sd,
 				 struct sysfs_dirent * target_sd, char *path)
 {
@@ -178,4 +166,3 @@ const struct inode_operations sysfs_symlink_inode_operations = {
 
 
 EXPORT_SYMBOL_GPL(sysfs_create_link);
-EXPORT_SYMBOL_GPL(sysfs_remove_link);
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 1f3915f..9494f3d 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -109,11 +109,12 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
 				      const unsigned char *name);
 struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type);
 
+void __sysfs_remove(struct sysfs_dirent *sd, int recurse);
+
 void release_sysfs_dirent(struct sysfs_dirent *sd);
 
 int sysfs_create_subdir(struct kobject *kobj, const char *name,
 			struct sysfs_dirent **p_sd);
-void sysfs_remove_subdir(struct sysfs_dirent *sd);
 
 static inline struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd)
 {
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index f030dc6..4ad2874 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -18,6 +18,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 
+struct sysfs_dirent;
 struct vm_area_struct;
 
 /*
@@ -34,6 +35,7 @@ struct vm_area_struct;
 
 struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
 				      const char *name);
+void sysfs_remove(struct sysfs_dirent *sd);
 
 int __must_check sysfs_init(void);
 
@@ -45,6 +47,10 @@ static inline struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
 	return NULL;
 }
 
+static inline void sysfs_remove(struct sysfs_dirent *sd)
+{
+}
+
 static inline int __must_check sysfs_init(void)
 {
 	return 0;
-- 
1.5.0.3


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux