All of lore.kernel.org
 help / color / mirror / Atom feed
From: Maneesh Soni <maneesh@in.ibm.com>
To: LKML <linux-kernel@vger.kernel.org>
Cc: Greg KH <greg@kroah.com>,
	Al Viro <viro@parcelfarce.linux.theplanet.co.uk>,
	Patrick Mochel <mochel@digitalimplant.org>,
	Dipankar Sarma <dipankar@in.ibm.com>
Subject: [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch
Date: Mon, 29 Dec 2003 15:04:34 +0530	[thread overview]
Message-ID: <20031229093434.GC1649@in.ibm.com> (raw)
In-Reply-To: <20031229093354.GB1649@in.ibm.com>


o This patch provides the inode operations ->lookup(), ->readdir() and
  ->llseek() for sysfs directories. 

o while sysfs_create_dir() we attach a sysfs_dirent structure to the d_fsdata
  filed of dentry corresponding to the kobject's direcotry. 

o sysfs_lookup does not hash the dentry and we hash the dentry when we have
  attached the sysfs_dirent to it. This was done to cover up a race when
  we attach a negative dentry and instantiate it before updating the d_fsdata
  field. As after instantiating we can get a successfull lookup for the dentry
  but a NULL d_fsdata field. As a result we do not create negative dentries.

o sysfs_readdir() or sysfs_dir_lseek() will bring in the dentries 
  corresponding to the attribute files if the offset is more than 2. These
  are released when we are done with filldir().

o sysfs_d_iput() releases the ref. to the sysfs_dirent() which was taken at
  the time of dentry allocation.

 fs/sysfs/dir.c |  301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 292 insertions(+), 9 deletions(-)

diff -puN fs/sysfs/dir.c~sysfs-leaves-dir fs/sysfs/dir.c
--- linux-2.6.0/fs/sysfs/dir.c~sysfs-leaves-dir	2003-12-29 12:30:50.000000000 +0530
+++ linux-2.6.0-maneesh/fs/sysfs/dir.c	2003-12-29 12:30:56.000000000 +0530
@@ -10,10 +10,54 @@
 #include <linux/kobject.h>
 #include "sysfs.h"
 
+struct inode_operations sysfs_dir_inode_operations = {
+	.lookup		= sysfs_lookup,
+};
+
+struct file_operations sysfs_dir_operations = {
+	.open		= dcache_dir_open,
+	.release	= dcache_dir_close,
+	.llseek		= sysfs_dir_lseek,
+	.read		= generic_read_dir,
+	.readdir	= sysfs_readdir,
+};
+
+/* dentry iput only for sysfs leaf dentries */
+static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) 
+{
+	struct sysfs_dirent * sd = dentry->d_fsdata;
+
+	if (sd)
+		sysfs_put(sd);
+	iput(inode);
+}
+
+
+static struct dentry_operations sysfs_dentry_ops = {
+	.d_iput		= sysfs_d_iput,
+};
+
+char * sysfs_get_name(struct sysfs_dirent *sd)
+{
+	if (!sd || !sd->s_element)
+		BUG();
+
+	return (sd->s_type & SYSFS_KOBJ_ATTR) ?
+		((struct attribute *)(sd->s_element))->name :
+		((struct bin_attribute *)(sd->s_element))->attr.name;
+}
+
+static int init_file(struct inode * inode)
+{
+	inode->i_size = PAGE_SIZE;
+	inode->i_fop = &sysfs_file_operations;
+	return 0;
+}
+
 static int init_dir(struct inode * inode)
 {
-	inode->i_op = &simple_dir_inode_operations;
-	inode->i_fop = &simple_dir_operations;
+	inode->i_op = &sysfs_dir_inode_operations;
+	inode->i_fop = &sysfs_dir_operations;
 
 	/* directory inodes start off with i_nlink == 2 (for "." entry) */
 	inode->i_nlink++;
@@ -21,6 +65,61 @@ static int init_dir(struct inode * inode
 }
 
 
+/* attaches attribute's sysfs_dirent to the dentry corresponding to the 
+ * attribute file
+ */
+int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry)
+{
+	struct attribute * attr = NULL;
+	struct bin_attribute * bin_attr = NULL;
+        int (* init) (struct inode *) = NULL;
+	int error = 0;
+
+        if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) {
+                bin_attr = sd->s_element;
+                attr = &bin_attr->attr;
+        } else {
+                attr = sd->s_element;
+                init = init_file;
+        }
+
+	error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init);
+	if (error)
+		return error;
+
+        if (bin_attr) {
+		dentry->d_inode->i_size = bin_attr->size;
+		dentry->d_inode->i_fop = &bin_fops;
+	}
+	dentry->d_op = &sysfs_dentry_ops;
+	dentry->d_fsdata = sysfs_get(sd);
+	sd->s_dentry = dentry;
+	d_rehash(dentry);
+
+	return 0;
+}
+
+struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, 
+				struct nameidata *nd)
+{
+	struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata;
+	struct sysfs_dirent * sd;
+	int err = 0;
+
+	list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+		if ((sd->s_type == SYSFS_KOBJ_ATTR) 
+			|| (sd->s_type == SYSFS_KOBJ_BIN_ATTR)) {
+			char * name = sysfs_get_name(sd);
+			if (strcmp(name, dentry->d_name.name))
+				continue;
+			err = sysfs_attach_attr(sd, dentry);
+			break;
+		}
+	}
+
+	return ERR_PTR(err);
+}
+
 static int create_dir(struct kobject * k, struct dentry * p,
 		      const char * n, struct dentry ** d)
 {
@@ -29,12 +128,23 @@ static int create_dir(struct kobject * k
 	down(&p->d_inode->i_sem);
 	*d = sysfs_get_dentry(p,n);
 	if (!IS_ERR(*d)) {
-		error = sysfs_create(*d,
+		error = sysfs_create(*d, 
 					 S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO,
 					 init_dir);
 		if (!error) {
-			(*d)->d_fsdata = k;
-			p->d_inode->i_nlink++;
+			struct sysfs_dirent * sd, * parent_sd;
+			parent_sd = p->d_fsdata;
+			sd = sysfs_new_dirent(parent_sd, k, 
+						(parent_sd->s_element == k) ? 
+						SYSFS_KOBJ_ATTR_GROUP : 
+						SYSFS_KOBJECT);
+			if (sd) {
+				(*d)->d_fsdata = sysfs_get(sd);
+				(*d)->d_op = &sysfs_dentry_ops;
+				p->d_inode->i_nlink++;
+				d_rehash(*d);
+			} else
+				error = -ENOMEM;
 		}
 		dput(*d);
 	} else
@@ -81,9 +191,15 @@ int sysfs_create_dir(struct kobject * ko
 static void remove_dir(struct dentry * d)
 {
 	struct dentry * parent = dget(d->d_parent);
+	struct sysfs_dirent * sd;
+
 	down(&parent->d_inode->i_sem);
 	d_delete(d);
-	simple_rmdir(parent->d_inode,d);
+	sd = d->d_fsdata;
+	list_del_init(&sd->s_sibling);
+	sysfs_put(d->d_fsdata);
+	if (parent->d_inode)
+		simple_rmdir(parent->d_inode,d);
 
 	pr_debug(" o %s removing done (%d)\n",d->d_name.name,
 		 atomic_read(&d->d_count));
@@ -133,9 +249,18 @@ void sysfs_remove_dir(struct kobject * k
 			 * Unlink and unhash.
 			 */
 			spin_unlock(&dcache_lock);
-			d_delete(d);
-			simple_unlink(dentry->d_inode,d);
-			dput(d);
+			if (S_ISREG(d->d_inode->i_mode)) {
+				struct sysfs_dirent * sd = d->d_fsdata;
+
+				list_del_init(&sd->s_sibling);
+				sysfs_put(sd);
+				d_drop(d);
+				simple_unlink(dentry->d_inode,d);
+			} else {
+				d_delete(d);
+				simple_unlink(dentry->d_inode,d);
+				dput(d);
+			}
 			spin_lock(&dcache_lock);
 		}
 		pr_debug(" done\n");
@@ -172,6 +297,164 @@ void sysfs_rename_dir(struct kobject * k
 	up(&parent->d_inode->i_sem);	
 }
 
+/* called under parent inode's i_sem (taken in vfs_readdir */
+static void sysfs_close_attr_files(struct dentry * parent)
+{
+	struct sysfs_dirent * parent_sd = parent->d_fsdata;
+	struct sysfs_dirent * sd;
+
+	list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+		if ((sd->s_type == SYSFS_KOBJ_ATTR) || 
+			(sd->s_type == SYSFS_KOBJ_BIN_ATTR)) {
+			struct dentry * dentry = sd->s_dentry;
+			if (dentry && dentry->d_inode)
+				dput(dentry);
+		}
+	}
+}
+
+/* called under parent inode's i_sem (taken in vfs_readdir */
+static int sysfs_open_attr_files(struct dentry * parent)
+{
+	struct sysfs_dirent * parent_sd = parent->d_fsdata;
+	struct sysfs_dirent * sd;
+	struct dentry * dentry;
+	int error = 0;
+
+	list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+		if ((sd->s_type == SYSFS_KOBJ_ATTR) || 
+			(sd->s_type == SYSFS_KOBJ_BIN_ATTR)) {
+			char * name = sysfs_get_name(sd);
+			dentry = sysfs_get_dentry(parent, name);
+			if (IS_ERR(dentry))
+				error = PTR_ERR(dentry);
+			if (!dentry->d_inode) 
+				error = sysfs_attach_attr(sd, dentry);
+			if (error)
+				break;
+		}
+	}
+	if (error) {
+		/* release all successfully opened entires so far*/
+		sysfs_close_attr_files(parent);
+	}
+
+	return error;
+}
+
+/* Relationship between i_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct inode *inode)
+{
+	return (inode->i_mode >> 12) & 15;
+}
+
+/*
+ * Directory is locked and all positive dentries in it are safe, since
+ * for ramfs-type trees they can't go away without unlink() or rmdir(),
+ * both impossible due to the lock on directory.
+ */
+
+int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct dentry *cursor = filp->private_data;
+	struct list_head *p, *q = &cursor->d_child;
+	ino_t ino;
+	int i = filp->f_pos;
+	int err = 0;
+
+	switch (i) {
+		case 0:
+			ino = dentry->d_inode->i_ino;
+			if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
+				break;
+			filp->f_pos++;
+			i++;
+			/* fallthrough */
+		case 1:
+			ino = parent_ino(dentry);
+			if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
+				break;
+			filp->f_pos++;
+			i++;
+			/* fallthrough */
+		default:
+			if ((err = sysfs_open_attr_files(dentry)))
+				return err;
+	
+			spin_lock(&dcache_lock);
+			if (filp->f_pos == 2) {
+				list_del(q);
+				list_add(q, &dentry->d_subdirs);
+			}
+			for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
+				struct dentry *next;
+				next = list_entry(p, struct dentry, d_child);
+				if (d_unhashed(next) || !next->d_inode)
+					continue;
+
+				spin_unlock(&dcache_lock);
+				if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode)) < 0)
+					return 0;
+				spin_lock(&dcache_lock);
+				/* next is still alive */
+				list_del(q);
+				list_add(q, p);
+				p = q;
+				filp->f_pos++;
+			}
+			spin_unlock(&dcache_lock);
+			sysfs_close_attr_files(dentry);
+	}
+	return 0;
+}
+
+loff_t sysfs_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+	int err = 0;
+
+	down(&file->f_dentry->d_inode->i_sem);
+	switch (origin) {
+		case 1:
+			offset += file->f_pos;
+		case 0:
+			if (offset >= 0)
+				break;
+		default:
+			up(&file->f_dentry->d_inode->i_sem);
+			return -EINVAL;
+	}
+	if (offset != file->f_pos) {
+		file->f_pos = offset;
+		if (file->f_pos >= 2) {
+			struct list_head *p;
+			struct dentry *cursor = file->private_data;
+			loff_t n = file->f_pos - 2;
+
+			if ((err = sysfs_open_attr_files(file->f_dentry))) {
+				offset = err;
+				goto exit;
+			}
+
+			spin_lock(&dcache_lock);
+			list_del(&cursor->d_child);
+			p = file->f_dentry->d_subdirs.next;
+			while (n && p != &file->f_dentry->d_subdirs) {
+				struct dentry *next;
+				next = list_entry(p, struct dentry, d_child);
+				if (!d_unhashed(next) && next->d_inode)
+					n--;
+				p = p->next;
+			}
+			list_add_tail(&cursor->d_child, p);
+			spin_unlock(&dcache_lock);
+			sysfs_close_attr_files(file->f_dentry);
+		}
+	}
+exit:
+	up(&file->f_dentry->d_inode->i_sem);
+	return offset;
+}
 EXPORT_SYMBOL(sysfs_create_dir);
 EXPORT_SYMBOL(sysfs_remove_dir);
 EXPORT_SYMBOL(sysfs_rename_dir);

_
-- 
Maneesh Soni
Linux Technology Center, 
IBM Software Lab, Bangalore, India
email: maneesh@in.ibm.com
Phone: 91-80-5044999 Fax: 91-80-5268553
T/L : 9243696

  reply	other threads:[~2003-12-29  9:39 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-12-29  9:32 [RFC 0/5] sysfs backing store (leaves only) Maneesh Soni
2003-12-29  9:33 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-mount.patch Maneesh Soni
2003-12-29  9:34   ` Maneesh Soni [this message]
2003-12-29  9:35     ` [RFC 3/5] sysfs backing store (leaves only) - sysfs-leaves-file.patch Maneesh Soni
2003-12-29  9:35       ` [RFC 4/5] sysfs backing store (leaves only) - sysfs-leaves-bin.patch Maneesh Soni
2003-12-29  9:36         ` [RFC 5/5] sysfs backing store (leaves only) - sysfs-leaves-misc.patch Maneesh Soni
2004-01-02  6:45     ` [RFC 2/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni

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=20031229093434.GC1649@in.ibm.com \
    --to=maneesh@in.ibm.com \
    --cc=dipankar@in.ibm.com \
    --cc=greg@kroah.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mochel@digitalimplant.org \
    --cc=viro@parcelfarce.linux.theplanet.co.uk \
    /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.