Skip to contentSkip to conversation

sysfs: implement new features

PATCH In review Tejun Heo 3 participants 12 messages Sep 20, 2007 – Sep 27, 2007 Driver core

lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

PATCH 4/4 sysfs: implement new features

Hello, all.

This is the fourth patchset of four sysfs update patchset series[1] and to be applied on top of the third patchset[2].

This patchset implements the following new features.

* Notify pollers on file deactivation.

* Name-formatting for symlinks. e.g. symlink pointing to /dira/dirb/leaf can be named as "symlink:%1-%0" and it will show up as "symlink:dirb-leaf". This only applies when new interface is used.

* Autoremoval of symlinks when target is removed. This only applies when new interface is used.

* Autorenaming of symlinks according to the name format string when target or one of its ancestors is renamed or moved. This only applies when new interface is used.

* Plugged operations. Sysfs users can plug top node and build subtree gradually without revealing the process to userland. When subtree is fully constructed, the top node can be unplugged and userland will see completely built subtree appearing at once. If subtree creation fails in the process, the whole subtree can be removed by simply removing the top node. There won't be any userland noticeable event. This is to be combined with uevent_suppress mechanism of driver model.

* Batch error handling. A plugged node accumulates any error condition occurring below it and can return the first error when asked. Also, all interface functions accepth ERR_PTR() value as sysfs_dirent parameter. This means that constructs like the following can be used to replace the current group interface.

<<-- code -->>
group = sysfs_add_dir(parent, "group_name", 0777 | SYSFS_PLUGGED, NULL); sysfs_add_file(group, "file0", 0777, file0_ops, file0_data); sysfs_add_file(group, "file1", 0777, file1_ops, file1_data);
...
sysfs_add_file(group, "fileN", 0777, fileN_ops, fileN_data); rc = sysfs_check_batch_error(group);

  if (rc) {
	sysfs_remove(group);
	return rc;

}
sysfs_unplug(group);
return 0;
<<-- end of code -->>

The above will create a subdirectory "group_name" which contains N files and show them atomically to userland or remove them without letting userland notice if any failure happens. This will simplify sysfs users quite a bit (not only for groups, other stuff too).

This patchset contains the following 8 patches.

0001-sysfs-notify-file-on-deactivation.patch 0002-sysfs-add-name-formatting-support-for-symlinks.patch 0003-sysfs-chain-symlinks-to-their-targets.patch 0004-sysfs-implement-symlink-auto-removal.patch 0005-sysfs-implement-symlink-auto-rename.patch 0006-sysfs-implement-plugged-creation-of-sysfs-nodes.patch 0007-sysfs-implement-batch-error-handling.patch 0008-sysfs-add-copyrights.patch

0001 implements notification on file deactivation. 0002-0005 implement symlink name formatting, autoremoving and autorenaming. 0006 implements plugged creation. 0007 batch error handling, and 0008 adds copyright notices on top of all sysfs header and source files.

Thanks.

--
tejun

[1] http://thread.gmane.org/gmane.linux.kernel/582105
[2] http://thread.gmane.org/gmane.linux.kernel/582147

author
#1 lore raw

PATCH 1/8 sysfs: notify file on deactivation

Notify file on deactivation so that the pollers get event when the polled file dies, which is very easy to implement with sd based interface.

---
 fs/sysfs/dir.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
fs/sysfs/dir.c +4 -0
index a74ca4a..d50d3ac 100644
@@ -245,6 +245,10 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
 	 */
 	v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active);
 
+	/* file is dying, notify pollers */
+	if (sysfs_type(sd) == SYSFS_FILE)
+		sysfs_notify_file(sd);
+
 	if (v != SD_DEACTIVATED_BIAS)
 		wait_for_completion(&wait);
 
Signature · 1 line
1.5.0.3
author
#2 lore raw

PATCH 2/8 sysfs: add name formatting support for symlinks

In reply to Tejun Heo

This patch implements sysfs name formatting. sysfs_add_link() is passed @name_fmt instead of @fmt. In the format string only "%[:alnum:]" and "%%" are handled specially. "%0" is substitued with the name of @target, "%3" with the name of the third ancestor of @target, "%[aA]" the 10th and "%[zZ]" with 35th. "%%" is substituted with "%".

This formatting is mainly to make symlink renaming automatic such that when the target or one of its ancestors gets renamed the symlink can be renamed together automatically & atomically. It also simplifies sysfs creation a bit by allowing callers to use static format string instead of formatting symlink name itself.

@name to kobject based sysfs_create_link() is not interpreted as format string to keep backward compatibility.

---
 fs/sysfs/dir.c        |    3 +
 fs/sysfs/kobject.c    |    2 +-
 fs/sysfs/symlink.c    |  245 ++++++++++++++++++++++++++++++++++++++++++++++--
 fs/sysfs/sysfs.h      |    9 ++
 include/linux/sysfs.h |    6 +-
 5 files changed, 250 insertions(+), 15 deletions(-)
fs/sysfs/dir.c +3 -0
index d50d3ac..b042a2e 100644
@@ -295,6 +295,9 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
 		sysfs_put(sd->s_link.target);
 	if (sd->s_flags & SYSFS_FLAG_NAME_COPIED)
 		kfree(sd->s_name);
+	if (sd->s_flags & SYSFS_FLAG_LINK_NAME_FMT_COPIED)
+		kfree(sd->s_link.name_fmt);
+
 	kfree(sd->s_iattr);
 	sysfs_free_ino(sd->s_ino);
 	kmem_cache_free(sysfs_dir_cachep, sd);
fs/sysfs/kobject.c +1 -1
index 16e10de..7ea9186 100644
@@ -425,7 +425,7 @@ int sysfs_create_link(struct kobject *kobj, struct kobject *target,
 	if (!target_sd)
 		return -ENOENT;
 
-	sd = sysfs_add_link(parent_sd, name, SYSFS_COPY_NAME, target_sd);
+	sd = __sysfs_add_link(parent_sd, name, SYSFS_COPY_NAME, target_sd, 0);
 
 	sysfs_put(target_sd);
 
fs/sysfs/symlink.c +234 -11

283 lines not on this page · raw message

fs/sysfs/sysfs.h +9 -0
index 732b292..9167032 100644
@@ -9,6 +9,7 @@ struct sysfs_elem_dir {
 
 struct sysfs_elem_link {
 	struct sysfs_dirent	*target;
+	const char		*name_fmt;
 };
 
 struct sysfs_elem_file {
@@ -62,6 +63,7 @@ struct sysfs_dirent {
 #define SYSFS_FLAG_MASK			~SYSFS_TYPE_MASK
 #define SYSFS_FLAG_REMOVED		0x0200
 #define SYSFS_FLAG_NAME_COPIED		0x0400
+#define SYSFS_FLAG_LINK_NAME_FMT_COPIED	0x0800
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
 {
@@ -152,4 +154,11 @@ extern const struct file_operations sysfs_bin_file_operations;
 /*
  * symlink.c
  */
+struct sysfs_dirent *__sysfs_add_link(struct sysfs_dirent *parent,
+				      const char *name, mode_t mode,
+				      struct sysfs_dirent *target, int format);
+int sysfs_link_name(const char *fmt, struct sysfs_dirent *target,
+		    const char **link_name, struct sysfs_dirent *renamed_sd,
+		    struct sysfs_dirent *new_parent, const char *new_name);
+
 extern const struct inode_operations sysfs_link_inode_operations;
include/linux/sysfs.h +3 -3
index f0279a7..5afe3bd 100644
@@ -29,7 +29,7 @@ struct vm_area_struct;
  * specific flags.
  */
 #define SYSFS_DIR_MODE		(S_IRWXU | S_IRUGO | S_IXUGO)
-#define SYSFS_COPY_NAME		010000	/* copy passed @name */
+#define SYSFS_COPY_NAME		010000	/* copy passed @name[_fmt] */
 
 /* collection of all flags for verification */
 #define SYSFS_MODE_FLAGS	SYSFS_COPY_NAME
@@ -61,7 +61,7 @@ struct sysfs_dirent *sysfs_add_bin(struct sysfs_dirent *parent,
 			const char *name, mode_t mode, size_t size,
 			const struct sysfs_bin_ops *bops, void *data);
 struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
-			const char *name, mode_t mode,
+			const char *name_fmt, mode_t mode,
 			struct sysfs_dirent *target);
 
 struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
@@ -100,7 +100,7 @@ static inline struct sysfs_dirent *sysfs_add_bin(struct sysfs_dirent *parent,
 }
 
 static inline struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
-			const char *name, mode_t mode,
+			const char *name_fmt, mode_t mode,
 			struct sysfs_dirent *target)
 {
 	return NULL;
Signature · 1 line
1.5.0.3
author
#3 lore raw

PATCH 3/8 sysfs: chain symlinks to their targets

In reply to Tejun Heo

Add sd->s_dir.links and sd->s_link.next and chain symlinks to their targets. This will be used to implement auto-removal and auto-rename of symlinks.

Symlinks created with kobject-based sysfs_create_symlink() won't be chained to keep backward compatibility.

---
 fs/sysfs/dir.c     |   26 ++++++++++++++++++++++++--
 fs/sysfs/symlink.c |    3 +++
 fs/sysfs/sysfs.h   |    4 ++++
 3 files changed, 31 insertions(+), 2 deletions(-)
fs/sysfs/dir.c +24 -2
index b042a2e..bb9e87e 100644
@@ -495,7 +495,8 @@ static struct inode *sysfs_addrm_get_parent_inode(struct sysfs_addrm_cxt *acxt,
  *
  *	Get @parent and set sd->s_parent to it and increment nlink of
  *	parent inode if @sd is a directory and link into the children
- *	list of the parent.
+ *	list of the parent.  If LINK_CHAINED flag is set, @sd is also
+ *	chained into the parent's s_dir.links list.
  *
  *	This function should be called between calls to
  *	sysfs_addrm_start() and sysfs_addrm_finish() and should be
@@ -522,6 +523,11 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *parent,
 
 	sysfs_link_sibling(sd);
 
+	if (sd->s_flags & SYSFS_FLAG_LINK_CHAINED) {
+		sd->s_link.next = sd->s_link.target->s_dir.links;
+		sd->s_link.target->s_dir.links = sd;
+	}
+
 	if (parent_inode) {
 		parent_inode->i_ctime = parent_inode->i_mtime = CURRENT_TIME;
 		if (sysfs_type(sd) == SYSFS_DIR)
@@ -537,7 +543,8 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *parent,
  *	@sd: sysfs_dirent to be added
  *
  *	Mark @sd removed and drop nlink of parent inode if @sd is a
- *	directory.  @sd is unlinked from the children list.
+ *	directory.  @sd is unlinked from the children list and the
+ *	s_dir.links list if applicable.
  *
  *	This function should be called between calls to
  *	sysfs_addrm_start() and sysfs_addrm_finish() and should be
@@ -554,6 +561,21 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 
 	parent_inode = sysfs_addrm_get_parent_inode(acxt, sd->s_parent);
 
+	if (sd->s_flags & SYSFS_FLAG_LINK_CHAINED) {
+		struct sysfs_dirent *target = sd->s_link.target;
+		struct sysfs_dirent **p;
+		int removed = 0;
+
+		for (p = &target->s_dir.links; *p; p = &(*p)->s_link.next) {
+			if (*p == sd) {
+				*p = sd->s_link.next;
+				removed = 1;
+				break;
+			}
+		}
+		BUG_ON(!removed);
+	}
+
 	sysfs_unlink_sibling(sd);
 
 	sd->s_flags |= SYSFS_FLAG_REMOVED;
fs/sysfs/symlink.c +3 -0
index 296fef5..53cc8a6 100644
@@ -231,6 +231,9 @@ struct sysfs_dirent *__sysfs_add_link(struct sysfs_dirent *parent,
 		goto err;
 
 	if (format) {
+		/* chain into the target's links list */
+		flags |= SYSFS_FLAG_LINK_CHAINED;
+
 		/* SYSFS_COPY_NAME means 'copy the format string' */
 		rc = -ENOMEM;
 		if (mode & SYSFS_COPY_NAME) {
fs/sysfs/sysfs.h +4 -0
index 9167032..f704279 100644
@@ -5,10 +5,13 @@ struct sysfs_elem_dir {
 	void			*data;
 	/* children list starts here and goes through sd->s_sibling */
 	struct sysfs_dirent	*children;
+	/* symlinks list starts here and goes through sd->s_link.next */
+	struct sysfs_dirent	*links;
 };
 
 struct sysfs_elem_link {
 	struct sysfs_dirent	*target;
+	struct sysfs_dirent	*next;
 	const char		*name_fmt;
 };
 
@@ -64,6 +67,7 @@ struct sysfs_dirent {
 #define SYSFS_FLAG_REMOVED		0x0200
 #define SYSFS_FLAG_NAME_COPIED		0x0400
 #define SYSFS_FLAG_LINK_NAME_FMT_COPIED	0x0800
+#define SYSFS_FLAG_LINK_CHAINED		0x1000
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
 {
Signature · 1 line
1.5.0.3
author
#4 lore raw

PATCH 4/8 sysfs: implement symlink auto-removal

In reply to Tejun Heo

When a sysfs_node is removed, automatically remove all symlinks pointing to it together.

Note that as links created with kobject based sysfs_create_link() aren't chained on its target, they aren't removed automatically. This is for backward compatibility.

---
 fs/sysfs/dir.c |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)
fs/sysfs/dir.c +6 -0
index bb9e87e..4a04cb4 100644
@@ -956,6 +956,12 @@ void __sysfs_remove(struct sysfs_dirent *sd, int recurse)
 		    (sysfs_type(cur) == SYSFS_DIR || cur->s_parent != sd))
 			continue;
 
+		/* kill all symlinks pointing to @cur */
+		if (sysfs_type(cur) == SYSFS_DIR)
+			while (cur->s_dir.links)
+				sysfs_remove_one(&acxt, cur->s_dir.links);
+
+		/* kill @cur */
 		sysfs_remove_one(&acxt, cur);
 	} while ((cur = next));
 
Signature · 1 line
1.5.0.3
author
#5 lore raw

PATCH 5/8 sysfs: implement symlink auto-rename

In reply to Tejun Heo

When a sysfs_node or one of its ancestors is renamed, automatically rename symlinks pointing to it according to the name format together.

Note that as links created with kobject based sysfs_create_link() aren't chained on its target, they aren't renamed automatically. This is for backward compatibility.

---
 fs/sysfs/dir.c |   45 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 45 insertions(+), 0 deletions(-)
fs/sysfs/dir.c +45 -0
index 4a04cb4..eac8fef 100644
@@ -1196,6 +1196,7 @@ static int sysfs_prep_rename(struct sysfs_rename_context *rcxt,
 {
 	struct sysfs_rcxt_rename_ent *rent;
 	struct sysfs_rcxt_mutex_ent *ment;
+	struct sysfs_dirent *cur;
 	int rc;
 
 	INIT_LIST_HEAD(&rcxt->mutexes);
@@ -1220,6 +1221,50 @@ static int sysfs_prep_rename(struct sysfs_rename_context *rcxt,
 		goto err;
 
 	/*
+	 * prep links pointing to @sd and its children
+	 */
+	cur = NULL;
+	while ((cur = sysfs_tree_walk_next(sd, cur))) {
+		struct sysfs_dirent *link;
+
+		if (sysfs_type(cur) != SYSFS_DIR)
+			continue;
+
+		for (link = cur->s_dir.links; link; link = link->s_link.next) {
+			const char *link_new_name;
+			int copied;
+
+			rc = sysfs_link_name(link->s_link.name_fmt,
+					     link->s_link.target,
+					     &link_new_name,
+					     sd, new_parent, new_name);
+			if (rc < 0)
+				goto err;
+			copied = rc;
+
+			if (!strcmp(link->s_name, link_new_name)) {
+				if (copied)
+					kfree(link_new_name);
+				continue;
+			}
+
+			rc = -ENOMEM;
+			rent = sysfs_rcxt_add(rcxt, link, link->s_parent);
+			if (!rent) {
+				mutex_unlock(&sysfs_mutex);
+				goto err;
+			}
+
+			rent->new_name = link_new_name;
+			rent->new_name_copied = copied;
+
+			rc = sysfs_rcxt_get_dentries(rcxt, rent);
+			if (rc)
+				goto err;
+		}
+	}
+
+	/*
 	 * lock all i_mutexes
 	 */
  try_lock:
Signature · 1 line
1.5.0.3
author
#6 lore raw

PATCH 6/8 sysfs: implement plugged creation of sysfs nodes

In reply to Tejun Heo

This patch implements plugged creation of sysfs nodes. If SYSFS_PLUGGED is specified to any of sysfs_add_*() functions, the node, its children which get added under it and symlinks pointing to it or its children won't be visible from userland - lookup and directory listing won't show them, until the plugged node is unplugged using sysfs_unplug().

This will be used to prevent userland from seeing sysfs hierarchy for certain entity (device or whatever) appear piece-by-piece. Also, initialization failure won't be visible on userland. Everything including inode nlinks are handled properly.

This will be later combined with uevent_suppress.

---
 fs/sysfs/dir.c        |  165 +++++++++++++++++++++++++++++++++++++++++--------
 fs/sysfs/inode.c      |    3 +-
 fs/sysfs/sysfs.h      |    2 +
 include/linux/sysfs.h |    8 ++-
 4 files changed, 149 insertions(+), 29 deletions(-)
fs/sysfs/dir.c +138 -27
index eac8fef..88ec749 100644
@@ -118,8 +118,15 @@ struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd)
 		/* look it up */
 		parent = dentry;
 		mutex_lock(&parent->d_inode->i_mutex);
+
 		dentry = lookup_one_len_kern(cur->s_name, parent,
 					     strlen(cur->s_name));
+		/* negative dentry means @sd is plugged, return -ENOENT */
+		if (!dentry->d_inode) {
+			dput(dentry);
+			dentry = ERR_PTR(-ENOENT);
+		}
+
 		mutex_unlock(&parent->d_inode->i_mutex);
 		dput(parent);
 
@@ -322,12 +329,13 @@ static struct dentry_operations sysfs_dentry_ops = {
 /**
  *	sysfs_new_dirent - allocate and initialize sysfs_dirent
  *	@name: name for the new sysfs_dirent
- *	@mode: mask of bits from S_IRWXUGO | SYSFS_COPY_NAME
+ *	@mode: mask of bits from S_IRWXUGO | SYSFS_COPY_NAME | SYSFS_PLUGGED
  *	@type: one of SYSFS_{DIR|FILE|BIN|LINK}
  *
  *	Allocate and initialize a sysfs_dirent with the specified
  *	parameters.  If SYSFS_COPY_NAME is specified in @mode, @name
- *	is duplicated.
+ *	is duplicated.  If SYSFS_PLUGGED is set, the node is
+ *	initialized into plugged state.
  *
  *	LOCKING:
  *	Kernel thread context (may sleep).
@@ -351,6 +359,10 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
 		flags |= SYSFS_FLAG_NAME_COPIED;
 	}
 
+	/* start plugged? */
+	if (mode & SYSFS_PLUGGED)
+		flags |= SYSFS_FLAG_PLUGGED;
+
 	/* normalize mode */
 	mode &= S_IALLUGO;
 
@@ -512,12 +524,14 @@ static struct inode *sysfs_addrm_get_parent_inode(struct sysfs_addrm_cxt *acxt,
 int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *parent,
 		  struct sysfs_dirent *sd)
 {
-	struct inode *parent_inode;
+	struct inode *parent_inode = NULL;
 
 	if (sysfs_find_dirent(parent, sd->s_name))
 		return -EEXIST;
 
-	parent_inode = sysfs_addrm_get_parent_inode(acxt, parent);
+	/* if plugged, don't update parent inode, will be updated on unplug */
+	if (!(sd->s_flags & SYSFS_FLAG_PLUGGED))
+		parent_inode = sysfs_addrm_get_parent_inode(acxt, parent);
 
 	sd->s_parent = sysfs_get(parent);
 
@@ -555,11 +569,13 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *parent,
  */
 void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 {
-	struct inode *parent_inode;
+	struct inode *parent_inode = NULL;
 
 	BUG_ON(sd->s_flags & SYSFS_FLAG_REMOVED);
 
-	parent_inode = sysfs_addrm_get_parent_inode(acxt, sd->s_parent);
+	/* if plugged, don't update parent inode, will be updated on unplug */
+	if (!(sd->s_flags & SYSFS_FLAG_PLUGGED))
+		parent_inode = sysfs_addrm_get_parent_inode(acxt, sd->s_parent);
 
 	if (sd->s_flags & SYSFS_FLAG_LINK_CHAINED) {
 		struct sysfs_dirent *target = sd->s_link.target;
@@ -805,7 +821,9 @@ EXPORT_SYMBOL_GPL(sysfs_find_child);
  *
  *	Add a new directory under @parent with the specified
  *	parameters.  If SYSFS_COPY_NAME is set in @mode, @name is
- *	copied before being used.
+ *	copied before being used.  If SYSFS_PLUGGED is set in @mode,
+ *	the new directory will start plugged (won't be visible till
+ *	unplugged).
  *
  *	LOCKING:
  *	Kernel thread context (may sleep).
@@ -830,6 +848,84 @@ struct sysfs_dirent *sysfs_add_dir(struct sysfs_dirent *parent,
 }
 EXPORT_SYMBOL_GPL(sysfs_add_dir);
 
+/**
+ *	sysfs_unplug - unplug a plugged sysfs_dirent
+ *	@sd: sysfs_dirent to unplug
+ *
+ *	A plugged sysfs_dirent isn't visible to userland.  As are all
+ *	its children and symlinks point to the sysfs_dirent or its
+ *	children.  This function unplugs @sd and makes it visible to
+ *	userland.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).  Uses sysfs_mutex.
+ */
+void sysfs_unplug(struct sysfs_dirent *sd)
+{
+	struct sysfs_addrm_cxt acxt;
+
+	/* Unplugging performs parent inode update delayed from
+	 * add/removal.  Also, sysfs_op_mutex grabbed by addrm_start
+	 * guarantees renaming to see consistent plugged status.
+	 */
+	sysfs_addrm_start(&acxt);
+
+	if (sd->s_flags & SYSFS_FLAG_PLUGGED) {
+		struct inode *parent_inode =
+			sysfs_addrm_get_parent_inode(&acxt, sd->s_parent);
+
+		sd->s_flags &= ~SYSFS_FLAG_PLUGGED;
+
+		if (parent_inode) {
+			parent_inode->i_ctime =
+			parent_inode->i_mtime = CURRENT_TIME;
+
+			if (sysfs_type(sd) == SYSFS_DIR)
+				inc_nlink(parent_inode);
+		}
+	}
+
+	sysfs_addrm_finish(&acxt);
+}
+EXPORT_SYMBOL_GPL(sysfs_unplug);
+
+static int sysfs_plugged(struct sysfs_dirent *sd,
+			 struct sysfs_dirent *new_parent)
+{
+	struct sysfs_dirent *cur;
+
+	/* @sd itself is plugged? */
+	if (sd->s_flags & SYSFS_FLAG_PLUGGED)
+		return 1;
+
+	/* Parent override for renaming.  This is used to determine
+	 * whether @sd will be plugged when moved under @new_parent.
+	 */
+	if (!new_parent)
+		new_parent = sd->s_parent;
+
+	/* is one of @sd's acnestors plugged? */
+	for (cur = new_parent; cur; cur = cur->s_parent)
+		if (cur->s_flags & SYSFS_FLAG_PLUGGED)
+			return 1;
+
+	/* A symlink is plugged if its target is plugged.  Check
+	 * whether the target is plugged unless the symlink is known
+	 * to be unplugged.
+	 */
+	if (sysfs_type(sd) != SYSFS_LINK ||
+	    (sd->s_flags & SYSFS_FLAG_LINK_UNPLUGGED))
+		return 0;
+
+	for (cur = sd->s_link.target; cur; cur = cur->s_parent)
+		if (cur->s_flags & SYSFS_FLAG_PLUGGED)
+			return 1;
+
+	/* if unplugged, mark it to accelerate future plugged test */
+	sd->s_flags |= SYSFS_FLAG_LINK_UNPLUGGED;
+	return 0;
+}
+
 static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
 				struct nameidata *nd)
 {
@@ -842,8 +938,8 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
 
 	sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
 
-	/* no such entry */
-	if (!sd)
+	/* no such entry or plugged */
+	if (unlikely(!sd || sysfs_plugged(sd, NULL)))
 		goto out_unlock;
 
 	/* attach dentry and inode */
@@ -1021,6 +1117,10 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 			const char * name;
 			int len;
 
+			/* skip plugged nodes */
+			if (unlikely(sysfs_plugged(pos, NULL)))
+				continue;
+
 			name = pos->s_name;
 			len = strlen(name);
 			filp->f_pos = ino = pos->s_ino;
@@ -1398,33 +1498,44 @@ EXPORT_SYMBOL_GPL(sysfs_rename);
  */
 int sysfs_chmod(struct sysfs_dirent *sd, mode_t mode)
 {
-	struct dentry *dentry;
-	struct inode *inode;
-	struct iattr newattrs;
-	int rc;
+	mode_t new_mode = (mode & S_IALLUGO) | (sd->s_mode & ~S_IALLUGO);
+	struct dentry *dentry = NULL;
+	struct inode *inode = NULL;
+	int rc = 0;
 
 	mutex_lock(&sysfs_op_mutex);
-	dentry = sysfs_get_dentry(sd);
-	mutex_unlock(&sysfs_op_mutex);
-	if (IS_ERR(dentry))
-		return PTR_ERR(dentry);
 
-	inode = dentry->d_inode;
+	/* If @sd is plugged, dentry and inode aren't there and can't
+	 * be looked up.  Just update @sd->s_mode.
+	 */
+	if (!sysfs_plugged(sd, NULL)) {
+		struct iattr newattrs;
 
-	mutex_lock(&inode->i_mutex);
+		dentry = sysfs_get_dentry(sd);
+		if (IS_ERR(dentry)) {
+			rc = PTR_ERR(dentry);
+			goto out_unlock_op;
+		}
+		inode = dentry->d_inode;
 
-	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
-	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	rc = notify_change(dentry, &newattrs);
+		mutex_lock(&inode->i_mutex);
 
-	if (rc == 0) {
-		mutex_lock(&sysfs_mutex);
-		sd->s_mode = newattrs.ia_mode;
-		mutex_unlock(&sysfs_mutex);
+		newattrs.ia_mode = new_mode;
+		newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+		rc = notify_change(dentry, &newattrs);
+		if (rc)
+			goto out_unlock_inode;
 	}
 
-	mutex_unlock(&inode->i_mutex);
+	mutex_lock(&sysfs_mutex);
+	sd->s_mode = new_mode;
+	mutex_unlock(&sysfs_mutex);
 
+ out_unlock_inode:
+	if (inode)
+		mutex_unlock(&inode->i_mutex);
+ out_unlock_op:
+	mutex_unlock(&sysfs_op_mutex);
 	dput(dentry);
 	return rc;
 }
fs/sysfs/inode.c +2 -1
index 0aeea4b..bd61cca 100644
@@ -128,7 +128,8 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd)
 	int nr = 0;
 
 	for (child = sd->s_dir.children; child; child = child->s_sibling)
-		if (sysfs_type(child) == SYSFS_DIR)
+		if (sysfs_type(child) == SYSFS_DIR &&
+		    !(child->s_flags & SYSFS_FLAG_PLUGGED))
 			nr++;
 
 	return nr + 2;
fs/sysfs/sysfs.h +2 -0
index f704279..51f346d 100644
@@ -64,10 +64,12 @@ struct sysfs_dirent {
 #define SYSFS_LINK			0x0008
 
 #define SYSFS_FLAG_MASK			~SYSFS_TYPE_MASK
+#define SYSFS_FLAG_PLUGGED		0x0100
 #define SYSFS_FLAG_REMOVED		0x0200
 #define SYSFS_FLAG_NAME_COPIED		0x0400
 #define SYSFS_FLAG_LINK_NAME_FMT_COPIED	0x0800
 #define SYSFS_FLAG_LINK_CHAINED		0x1000
+#define SYSFS_FLAG_LINK_UNPLUGGED	0x2000
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
 {
include/linux/sysfs.h +7 -1
index 5afe3bd..5a8c9f1 100644
@@ -30,9 +30,10 @@ struct vm_area_struct;
  */
 #define SYSFS_DIR_MODE		(S_IRWXU | S_IRUGO | S_IXUGO)
 #define SYSFS_COPY_NAME		010000	/* copy passed @name[_fmt] */
+#define SYSFS_PLUGGED		020000	/* plug the node on creation */
 
 /* collection of all flags for verification */
-#define SYSFS_MODE_FLAGS	SYSFS_COPY_NAME
+#define SYSFS_MODE_FLAGS	(SYSFS_COPY_NAME | SYSFS_PLUGGED)
 
 struct sysfs_file_ops {
 	ssize_t (*show)(char *page, void *data, void *parent_data);
@@ -63,6 +64,7 @@ struct sysfs_dirent *sysfs_add_bin(struct sysfs_dirent *parent,
 struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
 			const char *name_fmt, mode_t mode,
 			struct sysfs_dirent *target);
+void sysfs_unplug(struct sysfs_dirent *sd);
 
 struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
 				      const char *name);
@@ -106,6 +108,10 @@ static inline struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
 	return NULL;
 }
 
+static inline void sysfs_unplug(struct sysfs_dirent *sd)
+{
+}
+
 static inline struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
 						    const char *name)
 {
Signature · 1 line
1.5.0.3
author
#7 lore raw

PATCH 7/8 sysfs: implement batch error handling

In reply to Tejun Heo

Batch error handling is to be used with plugged creation. It accumulates any error condition which happens under a plugged node and let the user handle error once right before unplugging. This easily replaces attribute group creation.

Batch error handling works by the following two mechanisms.

* When an error occurs, tree is walked toward the top and, if there are plugged nodes which don't have plugged error recorded yet, sd->s_batch_error is set before returning the error code.

* All interface funtions allow ERR_PTR value to be passed as sysfs_dirent arguments and return -EBADF if that happens.

* After all the operations are complete, the user calls sysfs_check_batch_error() on the plugged node (usually the top one) which returns 0 if there hasn't been any error or error code of the first error.

* The user unplugs or removes the node accordingly.

---
 fs/sysfs/bin.c        |    8 +++-
 fs/sysfs/dir.c        |  137 ++++++++++++++++++++++++++++++++++++++++++++++--
 fs/sysfs/file.c       |   12 ++++-
 fs/sysfs/symlink.c    |    7 +++
 fs/sysfs/sysfs.h      |   12 ++++-
 include/linux/sysfs.h |    6 ++
 6 files changed, 173 insertions(+), 9 deletions(-)
fs/sysfs/bin.c +7 -1
index d6cc7b3..1f705d2 100644
@@ -231,10 +231,16 @@ struct sysfs_dirent *sysfs_add_bin(struct sysfs_dirent *parent,
 {
 	struct sysfs_dirent *sd;
 
+	/* for batch error handling */
+	if (IS_ERR(parent))
+		return ERR_PTR(-EBADF);
+
 	/* allocate and initialize */
 	sd = sysfs_new_dirent(name, mode, SYSFS_BIN);
-	if (!sd)
+	if (!sd) {
+		sysfs_set_batch_error(parent, -ENOMEM);
 		return ERR_PTR(-ENOMEM);
+	}
 
 	sd->s_bin.ops = bops;
 	sd->s_bin.size = size;
fs/sysfs/dir.c +131 -6

233 lines not on this page · raw message

fs/sysfs/file.c +11 -1
index 1d93940..3412732 100644
@@ -498,6 +498,10 @@ void sysfs_notify_file(struct sysfs_dirent *sd)
 {
 	struct sysfs_open_dirent *od;
 
+	/* for batch error handling */
+	if (IS_ERR(sd))
+		return;
+
 	BUG_ON(sysfs_type(sd) != SYSFS_FILE);
 
 	spin_lock(&sysfs_open_dirent_lock);
@@ -545,10 +549,16 @@ struct sysfs_dirent *sysfs_add_file(struct sysfs_dirent *parent,
 {
 	struct sysfs_dirent *sd;
 
+	/* for batch error handling */
+	if (IS_ERR(parent))
+		return ERR_PTR(-EBADF);
+
 	/* allocate and initialize */
 	sd = sysfs_new_dirent(name, mode, SYSFS_FILE);
-	if (!sd)
+	if (!sd) {
+		sysfs_set_batch_error(parent, -ENOMEM);
 		return ERR_PTR(-ENOMEM);
+	}
 
 	sd->s_file.ops = fops;
 	sd->s_file.data = data;
fs/sysfs/symlink.c +7 -0
index 53cc8a6..7e0fd89 100644
@@ -217,6 +217,10 @@ struct sysfs_dirent *__sysfs_add_link(struct sysfs_dirent *parent,
 	struct sysfs_addrm_cxt acxt;
 	int rc;
 
+	/* for batch error handling */
+	if (IS_ERR(parent) || IS_ERR(target))
+		return ERR_PTR(-EBADF);
+
 	/* acquire locks early for link name formatting */
 	sysfs_addrm_start(&acxt);
 
@@ -280,6 +284,9 @@ struct sysfs_dirent *__sysfs_add_link(struct sysfs_dirent *parent,
 		kfree(name_fmt);
 	if (flags & SYSFS_FLAG_NAME_COPIED)
 		kfree(name);
+
+	sysfs_set_batch_error(parent, rc);
+	sysfs_set_batch_error(target, rc);
 	return ERR_PTR(rc);
 }
 
fs/sysfs/sysfs.h +11 -1
index 51f346d..ec0b308 100644
@@ -52,7 +52,15 @@ struct sysfs_dirent {
 	unsigned int		s_flags;
 	ino_t			s_ino;
 	umode_t			s_mode;
-	struct iattr		*s_iattr;
+
+	/* Make s_iattr and s_batch_error share the room.  This is
+	 * okay because s_iattr is used only after a node is unplugged
+	 * while s_batch_error is used only while a node is plugged.
+	 */
+	union {
+		struct iattr		*s_iattr;
+		int			s_batch_error;
+	};
 };
 
 #define SD_DEACTIVATED_BIAS		INT_MIN
@@ -120,6 +128,8 @@ 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_set_batch_error(struct sysfs_dirent *sd, int error);
+
 void __sysfs_remove(struct sysfs_dirent *sd, int recurse);
 
 void release_sysfs_dirent(struct sysfs_dirent *sd);
include/linux/sysfs.h +6 -0
index 5a8c9f1..a8dfc6b 100644
@@ -64,6 +64,7 @@ struct sysfs_dirent *sysfs_add_bin(struct sysfs_dirent *parent,
 struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
 			const char *name_fmt, mode_t mode,
 			struct sysfs_dirent *target);
+int sysfs_check_batch_error(struct sysfs_dirent *sd);
 void sysfs_unplug(struct sysfs_dirent *sd);
 
 struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
@@ -108,6 +109,11 @@ static inline struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
 	return NULL;
 }
 
+static inline int sysfs_check_batch_error(struct sysfs_dirent *sd)
+{
+	return 0;
+}
+
 static inline void sysfs_unplug(struct sysfs_dirent *sd)
 {
 }
Signature · 1 line
1.5.0.3
author
#8 lore raw

PATCH 8/8 sysfs: add copyrights

In reply to Tejun Heo

Sysfs has gone through considerable amount of reimplementation. Add copyrights. Any objections? :-)

---
 fs/sysfs/bin.c                |    8 +++++++-
 fs/sysfs/dir.c                |   10 +++++++++-
 fs/sysfs/file.c               |   10 +++++++++-
 fs/sysfs/inode.c              |    8 ++++++--
 fs/sysfs/kobject.c            |    2 ++
 fs/sysfs/mount.c              |   10 +++++++++-
 fs/sysfs/symlink.c            |   10 +++++++++-
 fs/sysfs/sysfs.h              |   10 ++++++++++
 include/linux/sysfs-kobject.h |    2 ++
 include/linux/sysfs.h         |    4 ++++
 10 files changed, 67 insertions(+), 7 deletions(-)
fs/sysfs/bin.c +7 -1
index 1f705d2..ae6986d 100644
@@ -1,9 +1,15 @@
 /*
- * bin.c - binary file operations for sysfs.
+ * fs/sysfs/bin.c - sysfs binary file implementation
  *
  * Copyright (c) 2003 Patrick Mochel
  * Copyright (c) 2003 Matthew Wilcox
  * Copyright (c) 2004 Silicon Graphics, Inc.
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Please see Documentation/filesystems/sysfs.txt for more information.
  */
 
 #undef DEBUG
fs/sysfs/dir.c +9 -1
index f18da1b..a8571f4 100644
@@ -1,5 +1,13 @@
 /*
- * dir.c - Operations for sysfs directories.
+ * fs/sysfs/dir.c - sysfs core and dir operation implementation
+ *
+ * Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Please see Documentation/filesystems/sysfs.txt for more information.
  */
 
 #undef DEBUG
fs/sysfs/file.c +9 -1
index 3412732..68ec732 100644
@@ -1,5 +1,13 @@
 /*
- * file.c - operations for regular (text) files.
+ * fs/sysfs/file.c - sysfs regular (text) file implementation
+ *
+ * Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Please see Documentation/filesystems/sysfs.txt for more information.
  */
 
 #include <linux/module.h>
fs/sysfs/inode.c +6 -2
index bd61cca..6834a11 100644
@@ -1,7 +1,11 @@
 /*
- * inode.c - basic inode and dentry operations.
+ * fs/sysfs/inode.c - basic sysfs inode and dentry operations
  *
- * sysfs is Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
  *
  * Please see Documentation/filesystems/sysfs.txt for more information.
  */
fs/sysfs/kobject.c +2 -0
index 7ea9186..c0bc77d 100644
@@ -3,6 +3,8 @@
  *
  * Copyright (c) 2001,2002 Patrick Mochel
  * Copyright (c) 2004 Silicon Graphics, Inc.
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
  *
  * This is compatibility interface which wraps the primary interface
  * defined in linux/sysfs.h to remain compatible with the original
fs/sysfs/mount.c +9 -1
index d61eb08..642988f 100644
@@ -1,5 +1,13 @@
 /*
- * mount.c - operations for initializing and mounting sysfs.
+ * fs/sysfs/symlink.c - operations for initializing and mounting sysfs
+ *
+ * Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Please see Documentation/filesystems/sysfs.txt for more information.
  */
 
 #define DEBUG 
fs/sysfs/symlink.c +9 -1
index 7e0fd89..ed2550c 100644
@@ -1,5 +1,13 @@
 /*
- * symlink.c - operations for sysfs symlinks.
+ * fs/sysfs/symlink.c - sysfs symlink implementation
+ *
+ * Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Please see Documentation/filesystems/sysfs.txt for more information.
  */
 
 #include <linux/fs.h>
fs/sysfs/sysfs.h +10 -0
index ec0b308..a92a95d 100644
@@ -1,3 +1,13 @@
+/*
+ * fs/sysfs/sysfs.h - sysfs internal header file
+ *
+ * Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ */
+
 struct sysfs_open_dirent;
 
 /* type-specific structures for sysfs_dirent->s_* union members */
include/linux/sysfs-kobject.h +2 -0
index 4c821c2..dbbad99 100644
@@ -3,6 +3,8 @@
  *
  * Copyright (c) 2001,2002 Patrick Mochel
  * Copyright (c) 2004 Silicon Graphics, Inc.
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
  *
  * This is compatibility interface which wraps the primary interface
  * defined in linux/sysfs.h to remain compatible with the original
include/linux/sysfs.h +4 -0
index a8dfc6b..22cdbf4 100644
@@ -1,6 +1,10 @@
 /*
  * sysfs.h - definitions for the device driver filesystem
  *
+ * Copyright (c) 2001-3 Patrick Mochel
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <[email protected]>
+ *
  * Primary sysfs interface based on sysfs_dirent
  *
  * If you're using sysfs directly instead of via driver model, please
Signature · 1 line
1.5.0.3
#9 lore raw
In reply to Tejun Heo
Quoted 8 lines · Tejun Heo

Hello, all.

This is the fourth patchset of four sysfs update patchset series[1] and to be applied on top of the third patchset[2].

This patchset implements the following new features.

* Notify pollers on file deactivation.

This looks nice.

Quoted 4 lines

* Name-formatting for symlinks. e.g. symlink pointing to /dira/dirb/leaf can be named as "symlink:%1-%0" and it will show up as "symlink:dirb-leaf". This only applies when new interface is used.

Is this really necessary? It looks like we are adding a "special" type of parser here that no one uses.

Quoted 2 lines

* Autoremoval of symlinks when target is removed. This only applies when new interface is used.

Nice.

Quoted 3 lines

* Autorenaming of symlinks according to the name format string when target or one of its ancestors is renamed or moved. This only applies when new interface is used.

Nice.

Quoted 8 lines

* Plugged operations. Sysfs users can plug top node and build subtree gradually without revealing the process to userland. When subtree is fully constructed, the top node can be unplugged and userland will see completely built subtree appearing at once. If subtree creation fails in the process, the whole subtree can be removed by simply removing the top node. There won't be any userland noticeable event. This is to be combined with uevent_suppress mechanism of driver model.

Hm, but why? Can't we do this today with the attribute groups?

Quoted 21 lines

* Batch error handling. A plugged node accumulates any error condition occurring below it and can return the first error when asked. Also, all interface functions accepth ERR_PTR() value as sysfs_dirent parameter. This means that constructs like the following can be used to replace the current group interface.

<<-- code -->>
group = sysfs_add_dir(parent, "group_name", 0777 | SYSFS_PLUGGED, NULL); sysfs_add_file(group, "file0", 0777, file0_ops, file0_data); sysfs_add_file(group, "file1", 0777, file1_ops, file1_data);
...
sysfs_add_file(group, "fileN", 0777, fileN_ops, fileN_data); rc = sysfs_check_batch_error(group);

  if (rc) {
	sysfs_remove(group);
	return rc;

}
sysfs_unplug(group);
return 0;
<<-- end of code -->>

Quoted 4 lines

The above will create a subdirectory "group_name" which contains N files and show them atomically to userland or remove them without letting userland notice if any failure happens. This will simplify sysfs users quite a bit (not only for groups, other stuff too).

I'm still not sold on why this is needed. It looks like a lot of extra work for something that we are already handling.

thanks,

greg k-h

author
#10 lore raw

Hello, Greg.

Please read the other reply first. We need some consensus there first.

Quoted 3 lines · Greg KH
Quoted 1 line

* Notify pollers on file deactivation.

This looks nice.

Cool.

Quoted 7 lines
Quoted 4 lines

* Name-formatting for symlinks. e.g. symlink pointing to /dira/dirb/leaf can be named as "symlink:%1-%0" and it will show up as "symlink:dirb-leaf". This only applies when new interface is used.

Is this really necessary? It looks like we are adding a "special" type of parser here that no one uses.

This is a package deal with the autorenaming. More on this later.

Quoted 10 lines
Quoted 2 lines

* Autoremoval of symlinks when target is removed. This only applies when new interface is used.

Nice.

Quoted 3 lines

* Autorenaming of symlinks according to the name format string when target or one of its ancestors is renamed or moved. This only applies when new interface is used.

Nice.

To rename symlink when its target or one of its ancestors is renamed or moved, sysfs should be able to determine what should be the new name of the symlink when it points to the new target, right? So, we need symlink name formatting to do this.

I think the implemented formatting scheme covers all symlinks. Symlink name formatting will also simplify callers. No need to allocate name, format and free it. It can simply use constant string.

Quoted 10 lines
Quoted 8 lines

* Plugged operations. Sysfs users can plug top node and build subtree gradually without revealing the process to userland. When subtree is fully constructed, the top node can be unplugged and userland will see completely built subtree appearing at once. If subtree creation fails in the process, the whole subtree can be removed by simply removing the top node. There won't be any userland noticeable event. This is to be combined with uevent_suppress mechanism of driver model.

Hm, but why? Can't we do this today with the attribute groups?

Yes, we can. This is the way to do it with the new interface. More on this below.

Quoted 21 lines
Quoted 21 lines

* Batch error handling. A plugged node accumulates any error condition occurring below it and can return the first error when asked. Also, all interface functions accepth ERR_PTR() value as sysfs_dirent parameter. This means that constructs like the following can be used to replace the current group interface.

<<-- code -->>
group = sysfs_add_dir(parent, "group_name", 0777 | SYSFS_PLUGGED, NULL); sysfs_add_file(group, "file0", 0777, file0_ops, file0_data); sysfs_add_file(group, "file1", 0777, file1_ops, file1_data);
...
sysfs_add_file(group, "fileN", 0777, fileN_ops, fileN_data); rc = sysfs_check_batch_error(group);

  if (rc) {
	sysfs_remove(group);
	return rc;

}
sysfs_unplug(group);
return 0;
<<-- end of code -->>

Quoted 7 lines
Quoted 4 lines

The above will create a subdirectory "group_name" which contains N files and show them atomically to userland or remove them without letting userland notice if any failure happens. This will simplify sysfs users quite a bit (not only for groups, other stuff too).

I'm still not sold on why this is needed. It looks like a lot of extra work for something that we are already handling.

Plugged creation + batch error handling is a way to group operations and commit them atomically under the new interface. The problem of visible partial creation / removal is mostly solved using device->uevent_suppress but I think things can be improved for both userland and kernel.

After uevent is moved into sysfs, uevent generation can be coupled with grouped commit. sysfs hierarchy and accompanying uevents will appear atomically to userland and in-kernel users end up with simpler and less buggy code - single error check at the end of all sysfs operations instead of piece-by-piece construction coupled with the same amount of unroll code which is usually buggy. Maybe in (long) time, sysfs access can be simplified for embedded systems or whatnot.

I don't really see much downside other than the added complexity and as the added complexity will reduce complexity in the users, I think it's a good trade off.

Thanks.

Signature · 1 line
tejun
#11 lore raw
In reply to Greg KH
Quoted 4 lines · Greg KH
Quoted 1 line · Tejun Heo

* Name-formatting for symlinks. e.g. symlink pointing to /dira/dirb/leaf can be named as "symlink:%1-%0" and it will show up as "symlink:dirb-leaf". This only applies when new interface is used.

Is this really necessary? It looks like we are adding a "special" type of parser here that no one uses.

IMHO this would be nicer if it could reuse existing sprintf code to handle all the nice shiny sprintf format specifiers. The only challenge would be how to dynamically build a varargs list from an array of component names although perhaps there could be an internal __csprintf function which took a callback for retrieving arguments. Also since all of the path components are strings I don't know that numeric specifiers could be made useful, so perhaps it's not the greatest idea.

I think the primary importance for this functionality is:

Quoted 3 lines
Quoted 1 line

* Autorenaming of symlinks according to the name format string when target or one of its ancestors is renamed or moved. This only applies when new interface is used.

Nice.

Cheers,
Kyle Moffett