Skip to contentSkip to conversation

Backing Store for sysfs (Overhauled)

RFC In review Maneesh Soni 5 participants 12 messages Nov 12, 2003 – Nov 13, 2003 Driver core

lore
Share & tools
lore
#0 lore raw

cover Backing Store for sysfs (Overhauled)

Hi All,

The following patch set has the overhauled prototype for sysfs backing store for comments. I have tried to keep all the comments and suggestions from the last time in mind.

The main complaint was of over bloating kobject structure which becomes more painful when kobject is not part of sysfs. So now I have changed the data structures entirely. There is _no_ increase in the size of kobject structure. The kobject hierarchy is represented in the form of a new structure called sysfs_dirent (size 48 - bytes). sysfs_dirent will be there only for kobject elements (kobject, attribute, attribute group, symlink) which are represented in sysfs. kobject structre has just one change. Now kobject has a field pointing to its sysfs_dirent instead of dentry.

struct sysfs_dirent {
        struct list_head        s_sibling;
        struct list_head        s_children;
        void                    * s_element;
        struct dentry           * s_dentry;
        int                     s_type;
        struct rw_semaphore     s_rwsem;
};

The concept is still the same that in this prototype also we create dentry and inode on the fly when they are first looked up. This is done for both leaf or non-leaf dentries. The generic nature of sysfs_dirent makes it easy to do for both leaf or non-leaf dentries.

Please review the patches following this posting. For testing apply all the patches as they are splitted just for review.

Thanks
Maneesh

Signature · 6 lines
Maneesh Soni
Linux Technology Center, 
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-5044999 Fax: 91-80-5268553
T/L : 9243696
#1 lore raw

RFC 1/5 sysfs-backing-store.patch

o This patch describes the necessary data structures needed for sysfs backing store and the changes related to initialing sysfs filesystem. sysfs_dirent represents any element from kobject infrastructure. The element could be kobject, attribute, binary attribute, attribute group or symlink. kobject hierarchy is represented by using sysfs_dirent's s_children & s_sibling links.

o Root of sysfs filesystem is represented by statically allocated sysfs_dirent structure. Initialisation now does not mount sysfs filesystem and just does registration.

o The patch also has some support routines internal to sysfs code.

 fs/sysfs/mount.c        |   41 ++++++++++---------
 fs/sysfs/sysfs.h        |  103 ++++++++++++++++++++++++++++++++++++++++++++++--
 include/linux/kobject.h |    2 
 include/linux/sysfs.h   |   19 ++++++++
 4 files changed, 142 insertions(+), 23 deletions(-)
include/linux/kobject.h renamed from include/linux/kobject.h~sysfs-backing-store +1 -1
diff -puN include/linux/kobject.h~sysfs-backing-store include/linux/kobject.h
@@ -31,7 +31,7 @@ struct kobject {
 	struct kobject		* parent;
 	struct kset		* kset;
 	struct kobj_type	* ktype;
-	struct dentry		* dentry;
+	struct sysfs_dirent	* s_dirent;
 };
 
 extern int kobject_set_name(struct kobject *, const char *, ...)
include/linux/sysfs.h renamed from include/linux/sysfs.h~sysfs-backing-store +19 -0
diff -puN include/linux/sysfs.h~sysfs-backing-store include/linux/sysfs.h
@@ -9,6 +9,8 @@
 #ifndef _SYSFS_H_
 #define _SYSFS_H_
 
+#include <linux/rwsem.h>
+
 struct kobject;
 struct module;
 
@@ -63,6 +65,23 @@ struct attribute_group {
 	struct attribute	** attrs;
 };
 
+struct sysfs_dirent {
+	struct list_head	s_sibling;  
+	struct list_head	s_children;
+	void 			* s_element;
+	struct dentry		* s_dentry;
+	int			s_type;
+	struct rw_semaphore	s_rwsem;
+};
+
+/* Types of kobject elements represented by sysfs_dirent */
+#define SYSFS_ROOT		0x0001
+#define	SYSFS_KOBJECT		0x0002
+#define	SYSFS_KOBJ_ATTR		0x0004
+#define	SYSFS_KOBJ_BIN_ATTR	0x0008
+#define	SYSFS_KOBJ_ATTR_GROUP	0x0010
+#define	SYSFS_KOBJ_LINK		0x0020
+
 int sysfs_create_group(struct kobject *, const struct attribute_group *);
 void sysfs_remove_group(struct kobject *, const struct attribute_group *);
 
fs/sysfs/mount.c renamed from fs/sysfs/mount.c~sysfs-backing-store +23 -18

77 lines not on this page · raw message

fs/sysfs/sysfs.h renamed from fs/sysfs/sysfs.h~sysfs-backing-store +99 -4

112 lines not on this page · raw message

_

Signature · 6 lines
Maneesh Soni
Linux Technology Center, 
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-5044999 Fax: 91-80-5268553
T/L : 9243696
#2 lore raw

Re: [RFC 2/5] sysfs-dir.patch

o This is the main part of the sysfs backing store patch set. It provides the lookup routine, and open, release, readdir, lseek routines for sysfs directories. As we don't create sysfs entires in sysfs_create_xxxx() calls, we create the dentries when we actually look for them in sysfs_lookup and We parse the kobject hierachy for the required object and if found and not already created we go ahead and create the sysfs entry for it.

 fs/sysfs/dir.c |  396 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 341 insertions(+), 55 deletions(-)
fs/sysfs/dir.c renamed from fs/sysfs/dir.c~sysfs-dir +341 -55
diff -puN fs/sysfs/dir.c~sysfs-dir fs/sysfs/dir.c
@@ -10,84 +10,205 @@
 #include <linux/kobject.h>
 #include "sysfs.h"
 
+struct inode_operations sysfs_dir_inode_operations = {
+	.lookup		= sysfs_lookup,
+};
+
+struct file_operations sysfs_dir_operations = {
+	.open		= sysfs_dir_open,
+	.release	= sysfs_dir_close,
+	.llseek		= sysfs_dir_lseek,
+	.read		= generic_read_dir,
+	.readdir	= sysfs_readdir,
+};
+
+static void sysfs_d_iput(struct dentry *dentry, struct inode *inode)
+{
+	struct sysfs_dirent * sd = dentry->d_fsdata;
+
+	sd->s_dentry = NULL;
+	iput(inode);
+}
+
+static struct dentry_operations sysfs_dops = {
+	.d_iput		= sysfs_d_iput,
+};
+
+char * sysfs_get_name(struct sysfs_dirent *sd)
+{
+	struct kobject * k;
+	struct attribute * a;
+	struct bin_attribute * bin_attr;
+	struct attribute_group * grp;
+        char ** link_names;
+                                                                                
+	if (!sd || !sd->s_element) {
+		return NULL;
+	}
+
+	switch(sd->s_type) {
+		case SYSFS_KOBJECT:	
+				k = (struct kobject *) sd->s_element;
+				return kobject_name(k);
+
+		case SYSFS_KOBJ_ATTR:
+				a = (struct attribute *) sd->s_element;
+				return a->name;
+
+		case SYSFS_KOBJ_ATTR_GROUP:
+				grp = (struct attribute_group *) sd->s_element;
+				return grp->name;
+
+                case SYSFS_KOBJ_BIN_ATTR:
+                                bin_attr = (struct bin_attribute *) sd->s_element;
+                                return bin_attr->attr.name;
+		case SYSFS_KOBJ_LINK:
+                                link_names = sd->s_element;
+                                return link_names[0];
+		default:
+				return "/";
+
+	}
+}
+
 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++;
 	return 0;
 }
 
-
-static int create_dir(struct kobject * k, struct dentry * p,
-		      const char * n, struct dentry ** d)
+static int create_dir(struct sysfs_dirent * sd, struct dentry * dentry)
 {
 	int error;
+       
+	error = sysfs_create(dentry, S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO,
+				 init_dir);
+	if (!error) {
+		dentry->d_inode->i_nlink += sysfs_get_link_count(sd);
+		return 0;
+	}
+	dput(dentry);
 
-	down(&p->d_inode->i_sem);
-	*d = sysfs_get_dentry(p,n);
-	if (!IS_ERR(*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++;
-		}
-		dput(*d);
-	} else
-		error = PTR_ERR(*d);
-	up(&p->d_inode->i_sem);
 	return error;
 }
 
+static  int create_attr_file(struct sysfs_dirent * sd, struct dentry * dentry)
+{
+	const struct attribute * attr = NULL;
+	struct bin_attribute * bin_attr = NULL;
+	int (* init) (struct inode *) = NULL;
+	int err = 0;
+
+	if (sd->s_type == SYSFS_KOBJ_ATTR) {
+		attr = sd->s_element;
+		init = sysfs_init_file;
+	} else {
+		bin_attr = sd->s_element;
+		attr = &bin_attr->attr;
+	}
+	
+	err = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init);
+	if (err)
+		return err;
+
+	if (sd->s_type == SYSFS_KOBJ_BIN_ATTR) {
+		dentry->d_inode->i_size = bin_attr->size;
+		dentry->d_inode->i_fop = &bin_fops;
+	}
 
-int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d)
+	return 0;
+}
+
+static int sysfs_create_dirent(struct sysfs_dirent * sd, struct dentry * dentry)
 {
-	return create_dir(k,k->dentry,n,d);
+	int err = 0;
+        char ** link_names;
+
+	switch(sd->s_type) {
+		case SYSFS_KOBJ_ATTR:
+		case SYSFS_KOBJ_BIN_ATTR:
+			err = create_attr_file(sd, dentry);
+			break;
+                case SYSFS_KOBJ_LINK:
+                        link_names = sd->s_element;
+                        err = sysfs_symlink(dentry->d_parent->d_inode, dentry,
+                                       link_names[1]);
+                        break;
+		default:
+			err = create_dir(sd, dentry);
+	}
+	if (!err) {
+		dentry->d_fsdata = sd;
+		dentry->d_op = &sysfs_dops;
+		sd->s_dentry = dentry;
+	}
+
+	return err;
+}
+
+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;
+	char * name;
+	int err = -ENOENT;
+
+	down_read(&parent_sd->s_rwsem);
+	list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+		if (!sd->s_element)
+			continue;
+		name = sysfs_get_name(sd);
+		if (strcmp(name, dentry->d_name.name))
+			continue;
+		err = sysfs_create_dirent(sd, dentry);
+		break;
+	}
+	up_read(&parent_sd->s_rwsem);
+
+	if (err == -ENOENT) {
+		d_add(dentry, NULL);
+		err = 0;
+	}
+
+	return ERR_PTR(err);
 }
 
 /**
  *	sysfs_create_dir - create a directory for an object.
- *	@parent:	parent parent object.
  *	@kobj:		object we're creating directory for. 
  */
-
 int sysfs_create_dir(struct kobject * kobj)
 {
-	struct dentry * dentry = NULL;
-	struct dentry * parent;
-	int error = 0;
+	struct sysfs_dirent * sd; 
+	struct sysfs_dirent * parent_sd;
 
 	if (!kobj)
 		return -EINVAL;
 
-	if (kobj->parent)
-		parent = kobj->parent->dentry;
-	else if (sysfs_mount && sysfs_mount->mnt_sb)
-		parent = sysfs_mount->mnt_sb->s_root;
+	if (kobj->parent) 
+		parent_sd = kobj->parent->s_dirent;
 	else
-		return -EFAULT;
+		parent_sd = &sysfs_root;
 
-	error = create_dir(kobj,parent,kobject_name(kobj),&dentry);
-	if (!error)
-		kobj->dentry = dentry;
-	return error;
-}
+	sd = sysfs_alloc_dirent(parent_sd, kobj, SYSFS_KOBJECT);
+	if (IS_ERR(sd))
+		return PTR_ERR(sd);
+	kobj->s_dirent = sd;
 
+	return 0;
+}
 
 static void remove_dir(struct dentry * d)
 {
 	struct dentry * parent = dget(d->d_parent);
+
 	down(&parent->d_inode->i_sem);
-	d_delete(d);
+	d_drop(d);
 	simple_rmdir(parent->d_inode,d);
-
 	pr_debug(" o %s removing done (%d)\n",d->d_name.name,
 		 atomic_read(&d->d_count));
-
 	up(&parent->d_inode->i_sem);
 	dput(parent);
 }
@@ -110,10 +231,15 @@ void sysfs_remove_subdir(struct dentry *
 void sysfs_remove_dir(struct kobject * kobj)
 {
 	struct list_head * node;
-	struct dentry * dentry = dget(kobj->dentry);
+	struct dentry * dentry = kobj->s_dirent->s_dentry;
+	struct sysfs_dirent * parent_sd;
 
 	if (!dentry)
-		return;
+		goto exit;
+		
+	spin_lock(&dcache_lock);
+	dentry = dget_locked(dentry);
+	spin_unlock(&dcache_lock);
 
 	pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
 	down(&dentry->d_inode->i_sem);
@@ -124,7 +250,7 @@ void sysfs_remove_dir(struct kobject * k
 		struct dentry * d = list_entry(node,struct dentry,d_child);
 		list_del_init(node);
 
-		pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count));
+		pr_debug(" o %s (%d):",d->d_name.name,atomic_read(&d->d_count));
 		if (d->d_inode) {
 			d = dget_locked(d);
 			pr_debug("removing");
@@ -132,10 +258,9 @@ void sysfs_remove_dir(struct kobject * k
 			/**
 			 * Unlink and unhash.
 			 */
+			__d_drop(d);
 			spin_unlock(&dcache_lock);
-			d_delete(d);
 			simple_unlink(dentry->d_inode,d);
-			dput(d);
 			spin_lock(&dcache_lock);
 		}
 		pr_debug(" done\n");
@@ -146,10 +271,15 @@ void sysfs_remove_dir(struct kobject * k
 	up(&dentry->d_inode->i_sem);
 
 	remove_dir(dentry);
-	/**
-	 * Drop reference from dget() on entrance.
-	 */
 	dput(dentry);
+
+exit:
+	sysfs_free_children(kobj->s_dirent);
+	if (kobj->parent)
+		parent_sd = kobj->parent->s_dirent;
+	else
+		parent_sd = &sysfs_root;
+	sysfs_free_dirent(parent_sd, kobject_name(kobj));
 }
 
 void sysfs_rename_dir(struct kobject * kobj, const char *new_name)
@@ -162,14 +292,170 @@ void sysfs_rename_dir(struct kobject * k
 	if (!kobj->parent)
 		return;
 
-	parent = kobj->parent->dentry;
+	parent = kobj->parent->s_dirent->s_dentry;
+	if (parent) {
+		spin_lock(&dcache_lock);
+		dget_locked(parent);
+		spin_unlock(&dcache_lock);
+
+		down(&parent->d_inode->i_sem);
+		new_dentry = sysfs_get_dentry(parent, new_name);
+		if (!IS_ERR(new_dentry))
+			d_move(kobj->s_dirent->s_dentry, new_dentry);
+		up(&parent->d_inode->i_sem);	
+		dput(parent);
+	}
+	kobject_set_name(kobj,new_name);
+}
 
-	down(&parent->d_inode->i_sem);
+int sysfs_dir_close(struct inode *inode, struct file *filp)
+{
+	struct dentry * dentry = filp->f_dentry;
+	struct sysfs_dirent * sd = dentry->d_fsdata;
+	struct sysfs_dirent * cursor = filp->private_data;
+	
+	down_write(&sd->s_rwsem);
+	list_del(&cursor->s_sibling);
+	kfree(cursor);
+	up_write(&sd->s_rwsem);
+	
+	return 0;
+}
 
-	new_dentry = sysfs_get_dentry(parent, new_name);
-	d_move(kobj->dentry, new_dentry);
-	kobject_set_name(kobj,new_name);
-	up(&parent->d_inode->i_sem);	
+int sysfs_dir_open(struct inode *inode, struct file *filp)
+{
+	struct dentry * parent = filp->f_dentry;
+	struct sysfs_dirent * parent_sd = parent->d_fsdata;
+	struct sysfs_dirent * cursor;
+
+	down(&inode->i_sem);
+	cursor = sysfs_alloc_dirent(parent_sd, NULL, 0);
+	up(&inode->i_sem);
+	if (IS_ERR(cursor))
+		return PTR_ERR(cursor);
+	filp->private_data = cursor;
+	return 0;
+}
+
+loff_t sysfs_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+	struct dentry * dentry = file->f_dentry;
+
+	down(&dentry->d_inode->i_sem);
+	switch (origin) {
+		case 1:
+			offset += file->f_pos;
+		case 0:
+			if (offset >= 0)
+				break;
+		default:
+			up(&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 sysfs_dirent * parent_sd = dentry->d_fsdata;
+			struct sysfs_dirent * cursor = file->private_data;
+			loff_t n = file->f_pos - 2;
+
+			down_write(&parent_sd->s_rwsem);
+			p = parent_sd->s_children.next;
+			while (n && p != &parent_sd->s_children) {
+				struct sysfs_dirent * next_sd;
+				struct dentry *next;
+				char * name;
+				next_sd = list_entry(p, struct sysfs_dirent, s_sibling);
+				if (!next_sd->s_element)
+					continue;
+				name = sysfs_get_name(next_sd);
+				up_write(&parent_sd->s_rwsem);
+				next = sysfs_get_dentry(dentry, name);
+				down_write(&parent_sd->s_rwsem);
+				if (IS_ERR(next))
+					break;
+				if (!d_unhashed(next) && next->d_inode)
+					n--;
+				p = p->next;
+				dput(next);
+			}
+			list_del(&cursor->s_sibling);
+			list_add_tail(&cursor->s_sibling, p);
+			up_write(&parent_sd->s_rwsem);
+		}
+	}
+	up(&dentry->d_inode->i_sem);
+	return offset;
+	return 0;
+}
+
+/* Relationship between i_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct inode *inode)
+{
+	return (inode->i_mode >> 12) & 15;
+}
+
+int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct sysfs_dirent *sd = dentry->d_fsdata;
+	struct sysfs_dirent *cursor = filp->private_data;
+	struct list_head *p, *q = &cursor->s_sibling;
+	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:
+			down_write(&sd->s_rwsem);
+			if (filp->f_pos == 2) {
+				list_del(q);
+				list_add(q, &sd->s_children);
+			}
+			for (p = q->next; p != &sd->s_children; p = p->next) {
+				struct sysfs_dirent *next_sd;
+				struct dentry * next;
+				char * name;
+
+				next_sd = list_entry(p, struct sysfs_dirent, 
+						   s_sibling);
+				if (!next_sd->s_element)
+					continue;
+				name = sysfs_get_name(next_sd);
+				up_write(&sd->s_rwsem);
+				next = sysfs_get_dentry(dentry, name);
+				down_write(&sd->s_rwsem);
+				if (IS_ERR(next))
+					break;
+				if (!next || !next->d_inode)
+					continue;
+				err = filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode));
+				dput(next);
+				if (err < 0) 
+					break;
+				list_del(q);
+				list_add(q, p);
+				p = q;
+				filp->f_pos++;
+			}
+			up_write(&sd->s_rwsem);
+	}
+	return 0;
 }
 
 EXPORT_SYMBOL(sysfs_create_dir);

_

Signature · 6 lines
Maneesh Soni
Linux Technology Center, 
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-5044999 Fax: 91-80-5268553
T/L : 9243696
#3 lore raw

Re: [RFC 3/5] sysfs-file.patch

o This patch modifies the sysfs_create_file and sysfs_create_bin_file keeping the same signature. Now these don't actually create files but allocates a sysfs_dirent representing the attribute and links it to the kobject's sysfs_dirent

o Now dentry->d_fsdata does not point to the attribute structure but points to the sysfs_dirent representing the attribute. The attribute pointer is saved in the sysfs_dirent's s_element field.

o At the time of actually creating the dentry for sysfs entries we don't do the dreaded "Extra dget"

 fs/sysfs/bin.c   |   47 +++++++++++++++------------------------
 fs/sysfs/file.c  |   66 +++++++++++++++++++++++++------------------------------
 fs/sysfs/inode.c |   41 ++++++++++++++++++++++++----------
 3 files changed, 78 insertions(+), 76 deletions(-)
fs/sysfs/file.c renamed from fs/sysfs/file.c~sysfs-file +31 -35
diff -puN fs/sysfs/file.c~sysfs-file fs/sysfs/file.c
@@ -11,7 +11,7 @@
 
 static struct file_operations sysfs_file_operations;
 
-static int init_file(struct inode * inode)
+int sysfs_init_file(struct inode * inode)
 {
 	inode->i_size = PAGE_SIZE;
 	inode->i_fop = &sysfs_file_operations;
@@ -77,12 +77,13 @@ struct sysfs_buffer {
  */
 static int fill_read_buffer(struct file * file, struct sysfs_buffer * buffer)
 {
-	struct attribute * attr = file->f_dentry->d_fsdata;
-	struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
+	struct attribute * attr = sysfs_get_file_attr(file);
+	struct kobject * kobj;
 	struct sysfs_ops * ops = buffer->ops;
 	int ret = 0;
 	ssize_t count;
 
+	kobj = sysfs_get_file_kobject(file->f_dentry);
 	if (!buffer->page)
 		buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
 	if (!buffer->page)
@@ -131,8 +132,8 @@ static int flush_read_buffer(struct sysf
  *	@ppos:	starting offset in file.
  *
  *	Userspace wants to read an attribute file. The attribute descriptor
- *	is in the file's ->d_fsdata. The target object is in the directory's
- *	->d_fsdata.
+ *	is in the file's sysfs_dirent which is present in ->d_fsdata. The 
+ *      target object is in the directory's sysfs_dirent.
  *
  *	We call fill_read_buffer() to allocate and fill the buffer from the
  *	object's show() method exactly once (if the read is happening from
@@ -198,8 +199,8 @@ fill_write_buffer(struct sysfs_buffer * 
 static int 
 flush_write_buffer(struct file * file, struct sysfs_buffer * buffer, size_t count)
 {
-	struct attribute * attr = file->f_dentry->d_fsdata;
-	struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
+	struct attribute * attr = sysfs_get_file_attr(file);
+	struct kobject * kobj = sysfs_get_file_kobject(file->f_dentry);
 	struct sysfs_ops * ops = buffer->ops;
 
 	return ops->store(kobj,attr,buffer->page,count);
@@ -238,8 +239,8 @@ sysfs_write_file(struct file *file, cons
 
 static int check_perm(struct inode * inode, struct file * file)
 {
-	struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata);
-	struct attribute * attr = file->f_dentry->d_fsdata;
+	struct kobject * kobj = kobject_get(sysfs_get_file_kobject(file->f_dentry));
+	struct attribute * attr = sysfs_get_file_attr(file);
 	struct sysfs_buffer * buffer;
 	struct sysfs_ops * ops = NULL;
 	int error = 0;
@@ -320,8 +321,8 @@ static int sysfs_open_file(struct inode 
 
 static int sysfs_release(struct inode * inode, struct file * filp)
 {
-	struct kobject * kobj = filp->f_dentry->d_parent->d_fsdata;
-	struct attribute * attr = filp->f_dentry->d_fsdata;
+	struct kobject * kobj = sysfs_get_file_kobject(filp->f_dentry);
+	struct attribute * attr = sysfs_get_file_attr(filp);
 	struct sysfs_buffer * buffer = filp->private_data;
 
 	if (kobj) 
@@ -345,24 +346,16 @@ static struct file_operations sysfs_file
 };
 
 
-int sysfs_add_file(struct dentry * dir, const struct attribute * attr)
+int sysfs_add_file(struct sysfs_dirent * parent_sd, 
+			const struct attribute * attr)
 {
-	struct dentry * dentry;
-	int error;
+	struct sysfs_dirent * sd;
 
-	down(&dir->d_inode->i_sem);
-	dentry = sysfs_get_dentry(dir,attr->name);
-	if (!IS_ERR(dentry)) {
-		error = sysfs_create(dentry,
-				     (attr->mode & S_IALLUGO) | S_IFREG,
-				     init_file);
-		if (!error)
-			dentry->d_fsdata = (void *)attr;
-		dput(dentry);
-	} else
-		error = PTR_ERR(dentry);
-	up(&dir->d_inode->i_sem);
-	return error;
+	sd = sysfs_alloc_dirent(parent_sd, (void *) attr, SYSFS_KOBJ_ATTR);
+	if (IS_ERR(sd))
+		return PTR_ERR(sd);
+
+	return 0;
 }
 
 
@@ -375,7 +368,7 @@ int sysfs_add_file(struct dentry * dir, 
 int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
 {
 	if (kobj && attr)
-		return sysfs_add_file(kobj->dentry,attr);
+		return sysfs_add_file(kobj->s_dirent, attr);
 	return -EINVAL;
 }
 
@@ -390,10 +383,17 @@ int sysfs_create_file(struct kobject * k
  */
 int sysfs_update_file(struct kobject * kobj, const struct attribute * attr)
 {
-	struct dentry * dir = kobj->dentry;
+	struct dentry * dir = kobj->s_dirent->s_dentry;
 	struct dentry * victim;
 	int res = -ENOENT;
 
+	if (!dir)
+		return res;
+
+	spin_lock(&dcache_lock);
+	dget_locked(dir);
+	spin_unlock(&dcache_lock);
+
 	down(&dir->d_inode->i_sem);
 	victim = sysfs_get_dentry(dir, attr->name);
 	if (!IS_ERR(victim)) {
@@ -402,11 +402,6 @@ int sysfs_update_file(struct kobject * k
 		    (victim->d_parent->d_inode == dir->d_inode)) {
 			victim->d_inode->i_mtime = CURRENT_TIME;
 			dnotify_parent(victim, DN_MODIFY);
-
-			/**
-			 * Drop reference from initial sysfs_get_dentry().
-			 */
-			dput(victim);
 			res = 0;
 		}
 		
@@ -416,6 +411,7 @@ int sysfs_update_file(struct kobject * k
 		dput(victim);
 	}
 	up(&dir->d_inode->i_sem);
+	dput(dir);
 
 	return res;
 }
@@ -431,7 +427,7 @@ int sysfs_update_file(struct kobject * k
 
 void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
 {
-	sysfs_hash_and_remove(kobj->dentry,attr->name);
+	sysfs_hash_and_remove(kobj->s_dirent, attr->name);
 }
 
 
fs/sysfs/bin.c renamed from fs/sysfs/bin.c~sysfs-file +18 -29

94 lines not on this page · raw message

fs/sysfs/inode.c renamed from fs/sysfs/inode.c~sysfs-file +29 -12

72 lines not on this page · raw message

_

Signature · 6 lines
Maneesh Soni
Linux Technology Center, 
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-5044999 Fax: 91-80-5268553
T/L : 9243696
#4 lore raw

Re: [RFC 4/5] sysfs-attr_group.patch

o This patch creates attribute group for the given kobject. Again here we don;t create the sysfs directory but just allocated sysfs_dirent for the attribute group and links it to the kobject's hierarchy.

 fs/sysfs/group.c |   75 +++++++++++++++++++++++++++++++++----------------------
 1 files changed, 45 insertions(+), 30 deletions(-)
fs/sysfs/group.c renamed from fs/sysfs/group.c~sysfs-attr_group +45 -30
diff -puN fs/sysfs/group.c~sysfs-attr_group fs/sysfs/group.c
@@ -12,70 +12,85 @@
 #include <linux/module.h>
 #include <linux/dcache.h>
 #include <linux/err.h>
+#include <linux/namei.h>
 #include "sysfs.h"
 
 
-static void remove_files(struct dentry * dir, 
+static void remove_files(struct sysfs_dirent * sd, 
 			 const struct attribute_group * grp)
 {
 	struct attribute *const* attr;
 
 	for (attr = grp->attrs; *attr; attr++)
-		sysfs_hash_and_remove(dir,(*attr)->name);
+		sysfs_hash_and_remove(sd, (*attr)->name);
 }
 
-static int create_files(struct dentry * dir,
+static int create_files(struct sysfs_dirent * parent_sd,
 			const struct attribute_group * grp)
 {
 	struct attribute *const* attr;
 	int error = 0;
 
 	for (attr = grp->attrs; *attr && !error; attr++) {
-		error = sysfs_add_file(dir,*attr);
+		error = sysfs_add_file(parent_sd, *attr);
 	}
 	if (error)
-		remove_files(dir,grp);
+		remove_files(parent_sd, grp);
 	return error;
 }
 
-
 int sysfs_create_group(struct kobject * kobj, 
 		       const struct attribute_group * grp)
 {
-	struct dentry * dir;
-	int error;
+	struct sysfs_dirent * sd;
+	int error = 0;
 
-	if (grp->name) {
-		error = sysfs_create_subdir(kobj,grp->name,&dir);
-		if (error)
-			return error;
-	} else
-		dir = kobj->dentry;
-	dir = dget(dir);
-	if ((error = create_files(dir,grp))) {
-		if (grp->name)
-			sysfs_remove_subdir(dir);
-		dput(dir);
+	if (!kobj || !grp)
+		return -EINVAL;
+
+	sd = sysfs_alloc_dirent(kobj->s_dirent, (void *) grp, SYSFS_KOBJ_ATTR_GROUP);
+	if (IS_ERR(sd))
+		return PTR_ERR(sd);
+
+	if ((error = create_files(sd, grp))) {
+		sysfs_remove_group(kobj, grp);
 	}
+
 	return error;
 }
 
 void sysfs_remove_group(struct kobject * kobj, 
 			const struct attribute_group * grp)
 {
-	struct dentry * dir;
+	struct sysfs_dirent * tmp_sd;
+	struct sysfs_dirent * grp_sd = NULL;
+	struct dentry * grp_dentry;
 
-	if (grp->name)
-		dir = sysfs_get_dentry(kobj->dentry,grp->name);
-	else
-		dir = kobj->dentry;
-
-	remove_files(dir,grp);
-	dput(dir);
-	if (grp->name)
-		sysfs_remove_subdir(dir);
-}
+	if (grp->name) {
+		down_read(&kobj->s_dirent->s_rwsem);
+		list_for_each_entry(tmp_sd, &kobj->s_dirent->s_children, s_sibling) {
+			if (!strcmp(grp->name, sysfs_get_name(tmp_sd))) {
+				grp_sd = tmp_sd;
+				break;
+			}
+		}
+		up_read(&kobj->s_dirent->s_rwsem);
+	} else 
+		grp_sd = kobj->s_dirent;
+
+	if (grp_sd->s_dentry) {
+		spin_lock(&dcache_lock);
+		grp_dentry = dget_locked(grp_sd->s_dentry);
+		spin_unlock(&dcache_lock);
+		remove_files(grp_sd, grp);
+		if (grp->name) 
+			sysfs_remove_subdir(grp_dentry);
+		dput(grp_dentry);
+	} else
+		sysfs_free_children(grp_sd);
 
+	sysfs_free_dirent(kobj->s_dirent, grp->name);
+}
 
 EXPORT_SYMBOL(sysfs_create_group);
 EXPORT_SYMBOL(sysfs_remove_group);

_

Signature · 6 lines
Maneesh Soni
Linux Technology Center, 
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-5044999 Fax: 91-80-5268553
T/L : 9243696
#5 lore raw

Re: [RFC 5/5] sysfs-symlink.patch

o This patch creates symlink kobject. We don't create the symlink in sysfs when sysfs_create_symlink() is called but just allocates one sysfs_dirent of type SYSFS_KOBJ_LINK. The s_element for symlinks is an array of two strings, one prepresenting the name of the link and another patch of the target of the link.

 fs/sysfs/symlink.c |   36 ++++++++++++++++++++----------------
 1 files changed, 20 insertions(+), 16 deletions(-)
fs/sysfs/symlink.c renamed from fs/sysfs/symlink.c~sysfs-symlink +20 -16
diff -puN fs/sysfs/symlink.c~sysfs-symlink fs/sysfs/symlink.c
@@ -15,7 +15,7 @@ static int init_symlink(struct inode * i
 	return 0;
 }
 
-static int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
+int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
 {
 	int error;
 
@@ -71,13 +71,12 @@ static void fill_object_path(struct kobj
  */
 int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name)
 {
-	struct dentry * dentry = kobj->dentry;
-	struct dentry * d;
-	int error = 0;
 	int size;
 	int depth;
 	char * path;
 	char * s;
+        char ** link_names;
+        struct sysfs_dirent * link;
 
 	depth = object_depth(kobj);
 	size = object_path_length(target) + depth * 3 - 1;
@@ -96,18 +95,23 @@ int sysfs_create_link(struct kobject * k
 	fill_object_path(target,path,size);
 	pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
 
-	down(&dentry->d_inode->i_sem);
-	d = sysfs_get_dentry(dentry,name);
-	if (!IS_ERR(d))
-		error = sysfs_symlink(dentry->d_inode,d,path);
-	else
-		error = PTR_ERR(d);
-	dput(d);
-	up(&dentry->d_inode->i_sem);
-	kfree(path);
-	return error;
-}
+        link_names = kmalloc(2 * (sizeof(char *)), GFP_KERNEL);
+        if (!link_names) {
+                kfree(path);
+                return -ENOMEM;
+        }
+        link_names[0] = name;
+        link_names[1] = path;
+                                                                                
+	link = sysfs_alloc_dirent(kobj->s_dirent, link_names, SYSFS_KOBJ_LINK);
+	if (IS_ERR(link)) {
+                kfree(path);
+                kfree(link_names);
+		return PTR_ERR(link);
+	}
 
+	return 0;
+}
 
 /**
  *	sysfs_remove_link - remove symlink in object's directory.
@@ -117,7 +121,7 @@ int sysfs_create_link(struct kobject * k
 
 void sysfs_remove_link(struct kobject * kobj, char * name)
 {
-	sysfs_hash_and_remove(kobj->dentry,name);
+	sysfs_hash_and_remove(kobj->s_dirent, name);
 }
 
 

_

Signature · 6 lines
Maneesh Soni
Linux Technology Center, 
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-5044999 Fax: 91-80-5268553
T/L : 9243696
#6 lore raw

Re: [RFC 2/5] sysfs-dir.patch

In reply to Maneesh Soni
Quoted 5 lines · Maneesh Soni
fs/sysfs/dir.c renamed from fs/sysfs/dir.c~sysfs-dir +0 -1
@@ -110,10 +231,15 @@ void sysfs_remove_subdir(struct dentry *
 void sysfs_remove_dir(struct kobject * kobj)
 {
 	struct list_head * node;
-	struct dentry * dentry = dget(kobj->dentry);

fs/sysfs/dir.c void sysfs_remove_subdir(struct dentry * · line 242

Quoted 10 lines
fs/sysfs/dir.c renamed from fs/sysfs/dir.c~sysfs-dir +7 -1
+	struct dentry * dentry = kobj->s_dirent->s_dentry;
+	struct sysfs_dirent * parent_sd;
 
 	if (!dentry)
-		return;
+		goto exit;
+		
+	spin_lock(&dcache_lock);
+	dentry = dget_locked(dentry);
+	spin_unlock(&dcache_lock);

Racy. Directory might've been looked up just as you've decided that it had no dentry.

fs/sysfs/dir.c void sysfs_rename_dir(struct kobject * k · line 296

Quoted 8 lines
fs/sysfs/dir.c renamed from fs/sysfs/dir.c~sysfs-dir +2 -1
 void sysfs_rename_dir(struct kobject * kobj, const char *new_name)
@@ -162,14 +292,170 @@ void sysfs_rename_dir(struct kobject * k
 	if (!kobj->parent)
 		return;
 
-	parent = kobj->parent->dentry;
+	parent = kobj->parent->s_dirent->s_dentry;
+	if (parent) {

Ditto.

Look, the *only* benefit of ramfs as a backing store for sysfs was that we could easily get locking right. You want second tree - you get to fight for coherency.

#7 lore raw
In reply to Maneesh Soni
Quoted 5 lines · Maneesh Soni

The concept is still the same that in this prototype also we create dentry and inode on the fly when they are first looked up. This is done for both leaf or non-leaf dentries. The generic nature of sysfs_dirent makes it easy to do for both leaf or non-leaf dentries.

What happens once a dentry and inode is created? Is there any way for them to be forced out, or do they stay around in memory forever?

thanks,

greg k-h

#8 lore raw
Quoted 9 lines · Greg KH
Quoted 5 lines · Maneesh Soni

The concept is still the same that in this prototype also we create dentry and inode on the fly when they are first looked up. This is done for both leaf or non-leaf dentries. The generic nature of sysfs_dirent makes it easy to do for both leaf or non-leaf dentries.

What happens once a dentry and inode is created? Is there any way for them to be forced out, or do they stay around in memory forever?

The idea atleast, is that if no one is using a dentry, it will be put in the dentry lru list and eventually returned to the slab. inodes too are returned alongwith. Just like how on-disk filesystems work.

Typically, an open() of a sysfs file would result in creation of the corresponding dentry/inode and holding of the reference. close() releases the reference. The last one to release puts the dentry in the lru list for later pruning. The result is that we have less memory use and smaller number of dcache hash table elements under normal circumstances.

THanks
Dipankar

#9 lore raw
Quoted 8 lines · Dipankar Sarma
Quoted 7 lines · Greg KH
Quoted 5 lines · Maneesh Soni

The concept is still the same that in this prototype also we create dentry and inode on the fly when they are first looked up. This is done for both leaf or non-leaf dentries. The generic nature of sysfs_dirent makes it easy to do for both leaf or non-leaf dentries.

Quoted 6 lines
Quoted 2 lines

What happens once a dentry and inode is created? Is there any way for them to be forced out, or do they stay around in memory forever?

The idea atleast, is that if no one is using a dentry, it will be put in the dentry lru list and eventually returned to the slab. inodes too are returned alongwith. Just like how on-disk filesystems work.

Do you all have any numbers backing this up?

thanks,

greg k-h

#10 lore raw
In reply to Maneesh Soni
Quoted 19 lines

The main complaint was of over bloating kobject structure which becomes more painful when kobject is not part of sysfs. So now I have changed the data structures entirely. There is _no_ increase in the size of kobject structure. The kobject hierarchy is represented in the form of a new structure called sysfs_dirent (size 48 - bytes). sysfs_dirent will be there only for kobject elements (kobject, attribute, attribute group, symlink) which are represented in sysfs. kobject structre has just one change. Now kobject has a field pointing to its sysfs_dirent instead of dentry.

struct sysfs_dirent {
        struct list_head        s_sibling;
        struct list_head        s_children;
        void                    * s_element;
        struct dentry           * s_dentry;
        int                     s_type;
        struct rw_semaphore     s_rwsem;
};
Quoted 4 lines

The concept is still the same that in this prototype also we create dentry and inode on the fly when they are first looked up. This is done for both leaf or non-leaf dentries. The generic nature of sysfs_dirent makes it easy to do for both leaf or non-leaf dentries.

I still don't like it, though I think things are gradually getting better.

I still think that keeping the directories static and only creating the leaf (file) dentries dynamically is the best tradeoff between complexity and memory consumption.

It will require some minor infrastructural modification to associate a kobject with all of its leaf nodes, but the result will be cleaner, and the worst-case memory consumption will be less than your patches with a per-attribute-per-kobject data structure (which there currently isn't).

Thanks,

Pat

#11 lore raw
Quoted 4 lines · Patrick Mochel

I still think that keeping the directories static and only creating the leaf (file) dentries dynamically is the best tradeoff between complexity and memory consumption.

Sounds like after the locking in the current patches fixed, this should perhaps be implemented and compared for locking complexity and memory consumption.

Quoted 5 lines

It will require some minor infrastructural modification to associate a kobject with all of its leaf nodes, but the result will be cleaner, and the worst-case memory consumption will be less than your patches with a per-attribute-per-kobject data structure (which there currently isn't).

Would a smaller replacement for the sysfs_dirent structure to link just leaf nodes to kobject be close to what infrastructural modifications you are talking about ?

Thanks
Dipankar