All of lore.kernel.org
 help / color / mirror / Atom feed
From: Matthew Wilcox <matthew@wil.cx>
To: Greg Kroah-Hartman <gregkh@suse.de>
Cc: linux-kernel@vger.kernel.org,
	Jesse Barnes <jbarnes@virtuousgeek.org>,
	linux-pci@vger.kernel.org
Subject: [PATCH 2/3] Sysfs: Allow directories to be populated dynamically
Date: Mon, 19 Oct 2009 23:50:21 -0600	[thread overview]
Message-ID: <20091020055021.GE29158@parisc-linux.org> (raw)
In-Reply-To: <20091020054740.GC29158@parisc-linux.org>


From: Matthew Wilcox <willy@linux.intel.com>
Date: Mon, 19 Oct 2009 01:02:38 -0400
Subject: [PATCH 2/3] Sysfs: Allow directories to be populated dynamically

Use function pointers to populate and depopulate sysfs directories with
dynamically files.  Include one user, the attribute groups.
---
 fs/sysfs/dir.c   |   49 ++++++++++++++++-------
 fs/sysfs/group.c |  115 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/sysfs/sysfs.h |    5 ++
 3 files changed, 150 insertions(+), 19 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 5fad489..e32a11d 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -294,9 +294,13 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
 		goto repeat;
 }
 
-static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+static void sysfs_d_iput(struct dentry *dentry, struct inode *inode)
 {
-	struct sysfs_dirent * sd = dentry->d_fsdata;
+	struct sysfs_dirent *sd = dentry->d_fsdata;
+
+	if ((sysfs_type(sd) == SYSFS_DIR) && sd->s_dir.depopulate)
+		sd->s_dir.depopulate(dentry, sd);
+	sd->s_flags &= ~SYSFS_FLAG_POPULATED;
 
 	sysfs_put(sd);
 	iput(inode);
@@ -574,6 +578,22 @@ repeat:
 	iput(inode);
 }
 
+void sysfs_kill_removed_dirents(struct sysfs_addrm_cxt *acxt)
+{
+	/* kill removed sysfs_dirents */
+	while (acxt->removed) {
+		struct sysfs_dirent *sd = acxt->removed;
+
+		acxt->removed = sd->s_sibling;
+		sd->s_sibling = NULL;
+
+		sysfs_drop_dentry(sd);
+		sysfs_deactivate(sd);
+		unmap_bin_file(sd);
+		sysfs_put(sd);
+	}
+}
+
 /**
  *	sysfs_addrm_finish - finish up sysfs_dirent add/remove
  *	@acxt: addrm context to finish up
@@ -600,18 +620,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
 		iput(inode);
 	}
 
-	/* kill removed sysfs_dirents */
-	while (acxt->removed) {
-		struct sysfs_dirent *sd = acxt->removed;
-
-		acxt->removed = sd->s_sibling;
-		sd->s_sibling = NULL;
-
-		sysfs_drop_dentry(sd);
-		sysfs_deactivate(sd);
-		unmap_bin_file(sd);
-		sysfs_put(sd);
-	}
+	sysfs_kill_removed_dirents(acxt);
 }
 
 /**
@@ -731,6 +740,17 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
 
 	mutex_lock(&sysfs_mutex);
 
+	if (!(parent_sd->s_flags & SYSFS_FLAG_POPULATED)) {
+		if (parent_sd->s_dir.populate) {
+			int err = parent_sd->s_dir.populate(dentry->d_parent,
+								parent_sd);
+			if (err) {
+				ret = ERR_PTR(err);
+				goto out_unlock;
+			}
+		}
+		parent_sd->s_flags |= SYSFS_FLAG_POPULATED;
+	}
 	sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
 
 	/* no such entry */
@@ -1012,7 +1032,6 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 	return 0;
 }
 
-
 const struct file_operations sysfs_dir_operations = {
 	.read		= generic_read_dir,
 	.readdir	= sysfs_readdir,
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index 0c4d342..37ac584 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -16,6 +16,102 @@
 #include "sysfs.h"
 
 
+/*
+ * i_mutex is not held, but this inode is on its way out of the system, so
+ * nobody gets to mess with it.  We can't take the sysfs_mutex here as it
+ * leads to a deadlock scenario where it's held in a path that can run
+ * reclaim, and this function can be called from reclaim context.  I guess
+ * prayer is the only solution here (other than splitting sysfs_mutex)
+ */
+void sysfs_depopulate_group(struct dentry *dentry,
+					const struct attribute_group *grp)
+						
+{
+	struct sysfs_dirent *dir_sd = dentry->d_fsdata;
+	struct sysfs_addrm_cxt acxt;
+	struct attribute **attrp;
+
+	memset(&acxt, 0, sizeof(acxt));
+	acxt.parent_sd = dir_sd;
+	acxt.parent_inode = dentry->d_inode;
+
+	for (attrp = grp->attrs; *attrp; attrp++) {
+		struct attribute *attr = *attrp;
+		struct sysfs_dirent *sd;
+
+		sd = sysfs_find_dirent(dir_sd, attr->name);
+		if (sd)
+			sysfs_remove_one(&acxt, sd);
+	}
+
+	sysfs_kill_removed_dirents(&acxt);
+}
+EXPORT_SYMBOL(sysfs_depopulate_group);
+
+/*
+ * inode->i_mutex is held by the VFS, and sysfs_mutex is held by
+ * sysfs_lookup, so there's no need to call sysfs_addrm_start/finish here.
+ * Nor is there a need to mess around with reference counts.
+ */
+int sysfs_populate_group(struct dentry *dentry,
+					const struct attribute_group *grp)
+						
+{
+	struct sysfs_dirent *dir_sd = dentry->d_fsdata;
+	struct kobject *kobj = dir_sd->s_dir.kobj;
+	struct sysfs_addrm_cxt acxt;
+	struct attribute **attrp;
+	int i = 0, error = 0;
+
+	memset(&acxt, 0, sizeof(acxt));
+	acxt.parent_sd = dir_sd;
+	acxt.parent_inode = dentry->d_inode;
+
+	for (attrp = grp->attrs; *attrp; attrp++) {
+		struct attribute *attr = *attrp;
+		mode_t mode = attr->mode;
+		struct sysfs_dirent *sd;
+
+		if (grp->is_visible) {
+			mode_t vis = grp->is_visible(kobj, attr, i++);
+			if (!vis)
+				continue;
+			mode |= vis;
+		}
+
+		sd = sysfs_new_dirent(attr->name, mode, SYSFS_KOBJ_ATTR);
+		if (!sd) {
+			error = -ENOMEM;
+			break;
+		}
+		sd->s_attr.attr = (void *)attr;
+
+		error = sysfs_add_one(&acxt, sd);
+		if (error) {
+			sysfs_put(sd);
+			break;
+		}
+	}
+	if (error)
+		sysfs_depopulate_group(dentry, grp);
+	return error;
+}
+EXPORT_SYMBOL(sysfs_populate_group);
+
+static
+int group_populate(struct dentry *dentry, struct sysfs_dirent *sd)
+{
+	struct attribute_group *grp = sd->s_dir.data;
+	return sysfs_populate_group(dentry, grp);
+}
+
+static
+void group_depopulate(struct dentry *dentry, struct sysfs_dirent *sd)
+{
+	struct attribute_group *grp = sd->s_dir.data;
+	sysfs_depopulate_group(dentry, grp);
+}
+
 static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
 			 const struct attribute_group *grp)
 {
@@ -32,6 +128,10 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
 	struct attribute *const* attr;
 	int error = 0, i;
 
+	/* Directory isn't currently instantiated; nothing to do */
+	if (!(dir_sd->s_flags & SYSFS_FLAG_POPULATED))
+		return 0;
+
 	for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
 		mode_t mode = 0;
 
@@ -55,7 +155,6 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
 	return error;
 }
 
-
 static int internal_create_group(struct kobject *kobj, int update,
 				 const struct attribute_group *grp)
 {
@@ -82,10 +181,18 @@ static int internal_create_group(struct kobject *kobj, int update,
 	} else {
 		sd = sysfs_get(kobj->sd);
 	}
-	error = create_files(sd, kobj, grp, update);
-	if (error) {
-		if (grp->name)
+
+	error = 0;
+	if (update) {
+		error = create_files(sd, kobj, grp, update);
+		if (error && grp->name)
 			sysfs_remove_subdir(sd);
+	} else if (!sd->s_dir.populate) {
+		sd->s_dir.populate = group_populate;
+		sd->s_dir.depopulate = group_depopulate;
+		sd->s_dir.data = (void *)grp;
+	} else if (sd->s_dir.populate != group_populate) {
+		error = -EEXIST;
 	}
 	sysfs_put(sd);
 	return error;
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index af4c4e7..13843fa 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -17,6 +17,9 @@ struct sysfs_elem_dir {
 	struct kobject		*kobj;
 	/* children list starts here and goes through sd->s_sibling */
 	struct sysfs_dirent	*children;
+	int (*populate)(struct dentry *, struct sysfs_dirent *);
+	void (*depopulate)(struct dentry *, struct sysfs_dirent *);
+	void *data;
 };
 
 struct sysfs_elem_symlink {
@@ -77,6 +80,7 @@ struct sysfs_dirent {
 #define SYSFS_COPY_NAME			(SYSFS_DIR | SYSFS_KOBJ_LINK)
 
 #define SYSFS_FLAG_MASK			~SYSFS_TYPE_MASK
+#define SYSFS_FLAG_POPULATED		0x0100
 #define SYSFS_FLAG_REMOVED		0x0200
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
@@ -120,6 +124,7 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
 int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
 void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
 void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt);
+void sysfs_kill_removed_dirents(struct sysfs_addrm_cxt *acxt);
 
 struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
 				       const unsigned char *name);
-- 
1.6.3.3

-- 
Matthew Wilcox				Intel Open Source Technology Centre
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours.  We can't possibly take such
a retrograde step."

  parent reply	other threads:[~2009-10-20  5:50 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-20  5:47 [PATCH 0/3] Allow sysfs to be dynamically populated Matthew Wilcox
2009-10-20  5:49 ` [PATCH 1/3] Fix updating of named attribute groups Matthew Wilcox
2009-10-29 15:59   ` Tejun Heo
2009-10-29 16:19     ` Matthew Wilcox
2009-10-29 16:24       ` Tejun Heo
2009-10-20  5:50 ` Matthew Wilcox [this message]
2009-10-29 16:20   ` [PATCH 2/3] Sysfs: Allow directories to be populated dynamically Tejun Heo
2009-10-29 16:21     ` Matthew Wilcox
2009-10-29 16:28       ` Tejun Heo
2009-10-29 19:24         ` Matthew Wilcox
2009-10-30 10:17           ` Tejun Heo
2009-10-30 11:14             ` Matthew Wilcox
2009-10-30 16:06               ` Tejun Heo
2009-10-20  5:50 ` [PATCH 3/3] Expose MSI-X interrupts through a dynamically generated sysfs directory Matthew Wilcox
2009-10-20  8:14   ` Américo Wang
2009-10-20  8:26     ` Matthew Wilcox
2009-10-27 17:32       ` Greg KH
2009-10-20  5:52 ` [PATCH 0/3] Allow sysfs to be dynamically populated Matthew Wilcox

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20091020055021.GE29158@parisc-linux.org \
    --to=matthew@wil.cx \
    --cc=gregkh@suse.de \
    --cc=jbarnes@virtuousgeek.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.