2004-03-18 06:28:36

by Maneesh Soni

[permalink] [raw]
Subject: [RFC 0/6] sysfs backing store v0.3

Hi,

Please find the following patch set for sysfs backing store. Thanks to Carsten
Otte and S390 Linux guys for reporting the bug while unloading zfcp driver.

Please see the previous posting for more details and numbers
http://marc.theaimsgroup.com/?l=linux-kernel&m=107589464818859&w=2

Changes in Version 0.3
----------------------
o Fixed dentry ref counting for un-named attribute groups. Because of this
zfcp driver was not getting unloaded waiting for the dentry ref. count to
go away.

Details of code changes are mentioned in the respective patches. Patch set
is against 2.6.5-rc1.

Al, it will be great help if you can spare some time in reviewing this.

Thanks
Maneesh

--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696


2004-03-18 06:31:24

by Maneesh Soni

[permalink] [raw]
Subject: [RFC 2/6] sysfs backing store v0.3



=> changes in version 0.3
o corrected dentry ref counting for sysfs_remove_subdir()

=> changes in version 0.2
o sysfs_rename_dir() used to call d_move() with unhashed new_dentry causing
panic in d_move(). Added d_add() call for new_dentry before calling d_move.
o corrected error checking after sysfs_get_dentry() in
sysfs_open_dir_entries().

=> changes in version 0.1
o re-diffed for 2.6.3

=> Changes in last version
o added support for symlink
o changed logic for sysfs_remove_dir
o dir dentries now take a ref on the corresponding kobject so as to have
kobject alive during the lifetime of the dentry.

=====================================================================
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 | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 337 insertions(+), 35 deletions(-)

diff -puN fs/sysfs/dir.c~sysfs-leaves-dir fs/sysfs/dir.c
--- linux-2.6.5-rc1/fs/sysfs/dir.c~sysfs-leaves-dir 2004-03-18 11:39:45.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/dir.c 2004-03-18 11:39:45.000000000 +0530
@@ -10,17 +10,151 @@
#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,
+};
+
+static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+{
+ struct sysfs_dirent * sd = dentry->d_fsdata;
+
+ if (sd) {
+ sd->s_dentry = NULL;
+ if ((sd->s_type & SYSFS_KOBJECT) ||
+ (sd->s_type & SYSFS_KOBJ_ATTR_GROUP))
+ kobject_put(sd->s_element);
+ 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)
+{
+ struct attribute * attr;
+ struct bin_attribute * bin_attr;
+ char ** link_names;
+
+ if (!sd || !sd->s_element)
+ BUG();
+
+ switch (sd->s_type) {
+ case SYSFS_KOBJ_ATTR:
+ attr = sd->s_element;
+ return attr->name;
+
+ case SYSFS_KOBJ_BIN_ATTR:
+ bin_attr = sd->s_element;
+ return bin_attr->attr.name;
+
+ case SYSFS_KOBJ_LINK:
+ link_names = sd->s_element;
+ return link_names[0];
+ }
+ return NULL;
+}
+
+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++;
return 0;
}

+/* attaches attribute's sysfs_dirent to the dentry corresponding to the
+ * attribute file
+ */
+static 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;
+}
+
+static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry)
+{
+ struct inode * dir = dentry->d_parent->d_inode;
+ char ** link_names = sd->s_element;
+ int err = 0;
+
+ err = sysfs_symlink(dir, dentry, link_names[1]);
+ if (!err) {
+ dentry->d_op = &sysfs_dentry_ops;
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ d_rehash(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;
+ int err = 0;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ char * name = sysfs_get_name(sd);
+ if (strcmp(name, dentry->d_name.name))
+ continue;
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ err = sysfs_attach_link(sd, dentry);
+ else
+ 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)
{
@@ -33,8 +167,21 @@ static int create_dir(struct kobject * k
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++;
+ sd->s_element = kobject_get(k);
+ sd->s_dentry = *d;
+ d_rehash(*d);
+ } else
+ error = -ENOMEM;
}
dput(*d);
} else
@@ -43,7 +190,6 @@ static int create_dir(struct kobject * k
return error;
}

-
int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d)
{
return create_dir(k,k->dentry,n,d);
@@ -81,8 +227,13 @@ 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);
+ sd = d->d_fsdata;
+ list_del_init(&sd->s_sibling);
+ sysfs_put(d->d_fsdata);
if (d->d_inode)
simple_rmdir(parent->d_inode,d);

@@ -110,46 +261,32 @@ 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 * d;
+ struct sysfs_dirent * parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent * sd, * tmp;
+ char * name;

if (!dentry)
return;

pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
down(&dentry->d_inode->i_sem);
-
- spin_lock(&dcache_lock);
- node = dentry->d_subdirs.next;
- while (node != &dentry->d_subdirs) {
- 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));
- if (d->d_inode) {
- d = dget_locked(d);
- pr_debug("removing");
-
- /**
- * Unlink and unhash.
- */
- spin_unlock(&dcache_lock);
- d_delete(d);
- simple_unlink(dentry->d_inode,d);
- dput(d);
- spin_lock(&dcache_lock);
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ name = sysfs_get_name(sd);
+ d = sysfs_get_dentry(dentry, name);
+ if (!IS_ERR(d) && d->d_inode) {
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ d_drop(d);
+ simple_unlink(dentry->d_inode, d);
+ }
}
- pr_debug(" done\n");
- node = dentry->d_subdirs.next;
}
- list_del_init(&dentry->d_child);
- spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem);

remove_dir(dentry);
- /**
- * Drop reference from dget() on entrance.
- */
dput(dentry);
}

@@ -168,11 +305,176 @@ void sysfs_rename_dir(struct kobject * k
down(&parent->d_inode->i_sem);

new_dentry = sysfs_get_dentry(parent, new_name);
- d_move(kobj->dentry, new_dentry);
- kobject_set_name(kobj,new_name);
+ if (!IS_ERR(new_dentry)) {
+ d_add(new_dentry, NULL);
+ d_move(kobj->dentry, new_dentry);
+ kobject_set_name(kobj,new_name);
+ }
up(&parent->d_inode->i_sem);
}

+/* called under parent inode's i_sem */
+static void sysfs_close_dir_entries(struct dentry * parent)
+{
+ struct sysfs_dirent * parent_sd = parent->d_fsdata;
+ struct sysfs_dirent * sd, * tmp;
+
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ struct dentry * dentry = sd->s_dentry;
+ if (dentry && dentry->d_inode)
+ dput(dentry);
+ }
+ }
+}
+
+/* called under parent inode's i_sem */
+static int sysfs_open_dir_entries(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_NOT_PINNED) {
+ char * name = sysfs_get_name(sd);
+ dentry = sysfs_get_dentry(parent, name);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ break;
+ }
+ if (!dentry->d_inode) {
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ error = sysfs_attach_link(sd, dentry);
+ else
+ error = sysfs_attach_attr(sd, dentry);
+ }
+ if (error)
+ break;
+ }
+ }
+ if (error) {
+ /* release all successfully opened entires so far*/
+ sysfs_close_dir_entries(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_dir_entries(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_dir_entries(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_dir_entries(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_dir_entries(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: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-18 06:29:38

by Maneesh Soni

[permalink] [raw]
Subject: [RFC 1/6] sysfs backing store v0.3



= changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o Nil, just re-diffed

=> changes in version 0.1
o Corrected sysfs_umount_begin(), it doesnot need lock_super() and also the
s_root check is not required. The reason being, that sysfs filesystem is
always mounted internally during init and there is no chance of sysfs super
block going away.

o corrected comments for umount_begin()

============================================================================
o The following patch contains the sysfs_dirent structure definition.
sysfs_dirent can represent kobject, attribute group, text attribute or
binary attribute for kobjects registered with sysfs. sysfs_dirent is
allocated with a ref count (s_count) of 1. Ref count is incremented when
a dentry is associated with the sysfs_dirent and it is decremented when
the corresponding dentry is freed.

o sysfs_dirent's corresponding to the attribute files of a kobject or attribute
group are linked together with s_sibling and are anchored at s_children of
the corresponding kobject's or attribute-group's sysfs_dirent.

o The patch also contains the mount related changes for sysfs backing store.
Because we mount sysfs once while init(), plain umount of sysfs doesnot
free all the un-used dentries (present in LRU list). To use force umount
flag, umount_begin() routine is provided which does a shrink_dcache_parent()
to release all the unused dentries.



fs/sysfs/mount.c | 27 +++++++++++++++++++++++++--
include/linux/sysfs.h | 19 +++++++++++++++++++
2 files changed, 44 insertions(+), 2 deletions(-)

diff -puN fs/sysfs/mount.c~sysfs-leaves-mount fs/sysfs/mount.c
--- linux-2.6.5-rc1/fs/sysfs/mount.c~sysfs-leaves-mount 2004-03-18 11:35:22.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/mount.c 2004-03-18 11:35:22.000000000 +0530
@@ -20,6 +20,14 @@ struct super_block * sysfs_sb = NULL;
static struct super_operations sysfs_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
+ .umount_begin = sysfs_umount_begin,
+};
+
+struct sysfs_dirent sysfs_root = {
+ .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling),
+ .s_children = LIST_HEAD_INIT(sysfs_root.s_children),
+ .s_element = NULL,
+ .s_type = SYSFS_ROOT,
};

static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
@@ -35,8 +43,8 @@ static int sysfs_fill_super(struct super

inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO);
if (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++;
} else {
@@ -50,6 +58,7 @@ static int sysfs_fill_super(struct super
iput(inode);
return -ENOMEM;
}
+ root->d_fsdata = &sysfs_root;
sb->s_root = root;
return 0;
}
@@ -60,6 +69,20 @@ static struct super_block *sysfs_get_sb(
return get_sb_single(fs_type, flags, data, sysfs_fill_super);
}

+/* For freeing zero refenced dentries / inodes while force unmounting
+ *
+ * sysfs is mounted once within kernel during init(), and this keeps the super
+ * block always active. So in case of user just doing normal umount,
+ * ->kill_sb is never called. So, in order to immediately free the memory used
+ * by un-used dentries and inodes, sysfs should to be umounted with force
+ * option. In anycase there will be normal pruing of unused dentries/inodes
+ * as usual due to memory pressure.
+ */
+void sysfs_umount_begin(struct super_block * sb)
+{
+ shrink_dcache_parent(sb->s_root);
+}
+
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.get_sb = sysfs_get_sb,
diff -puN include/linux/sysfs.h~sysfs-leaves-mount include/linux/sysfs.h
--- linux-2.6.5-rc1/include/linux/sysfs.h~sysfs-leaves-mount 2004-03-18 11:35:22.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/include/linux/sysfs.h 2004-03-18 11:35:22.000000000 +0530
@@ -9,6 +9,8 @@
#ifndef _SYSFS_H_
#define _SYSFS_H_

+#include <asm/atomic.h>
+
struct kobject;
struct module;

@@ -42,6 +44,23 @@ sysfs_remove_dir(struct kobject *);
extern void
sysfs_rename_dir(struct kobject *, const char *new_name);

+struct sysfs_dirent {
+ atomic_t s_count;
+ struct list_head s_sibling;
+ struct list_head s_children;
+ void * s_element;
+ int s_type;
+ struct dentry * s_dentry;
+};
+
+#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
+#define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK)
+
extern int
sysfs_create_file(struct kobject *, const struct attribute *);


_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-18 06:32:33

by Maneesh Soni

[permalink] [raw]
Subject: [RFC 4/6] sysfs backing store v0.3



=> changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o Nil, just re-diffed

o This patch contains changes required for bin attribute files.


fs/sysfs/bin.c | 52 +++++++++++++++++++---------------------------------
1 files changed, 19 insertions(+), 33 deletions(-)

diff -puN fs/sysfs/bin.c~sysfs-leaves-bin fs/sysfs/bin.c
--- linux-2.6.5-rc1/fs/sysfs/bin.c~sysfs-leaves-bin 2004-03-18 11:37:49.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/bin.c 2004-03-18 11:37:49.000000000 +0530
@@ -17,8 +17,10 @@
static int
fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
{
- struct bin_attribute * attr = dentry->d_fsdata;
- struct kobject * kobj = dentry->d_parent->d_fsdata;
+ struct sysfs_dirent * sd_attr = dentry->d_fsdata;
+ struct bin_attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;

return attr->read(kobj, buffer, off, count);
}
@@ -60,8 +62,10 @@ read(struct file * file, char __user * u
static int
flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
{
- struct bin_attribute *attr = dentry->d_fsdata;
- struct kobject *kobj = dentry->d_parent->d_fsdata;
+ struct sysfs_dirent * sd_attr = dentry->d_fsdata;
+ struct bin_attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;

return attr->write(kobj, buffer, offset, count);
}
@@ -94,8 +98,10 @@ static ssize_t write(struct file * file,

static int open(struct inode * inode, struct file * file)
{
- struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata);
- struct bin_attribute * attr = file->f_dentry->d_fsdata;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = kobject_get(sd_kobj->s_element);
+ struct sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct bin_attribute * attr = sd_attr->s_element;
int error = -EINVAL;

if (!kobj || !attr)
@@ -122,7 +128,8 @@ static int open(struct inode * inode, st

static int release(struct inode * inode, struct file * file)
{
- struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
+ struct sysfs_dirent * sd = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd->s_element;
u8 * buffer = file->private_data;

if (kobj)
@@ -131,7 +138,7 @@ static int release(struct inode * inode,
return 0;
}

-static struct file_operations bin_fops = {
+struct file_operations bin_fops = {
.read = read,
.write = write,
.llseek = generic_file_llseek,
@@ -148,31 +155,10 @@ static struct file_operations bin_fops =

int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
{
- struct dentry * dentry;
- struct dentry * parent;
- int error = 0;
-
- if (!kobj || !attr)
- return -EINVAL;
-
- parent = kobj->dentry;
-
- down(&parent->d_inode->i_sem);
- dentry = sysfs_get_dentry(parent,attr->attr.name);
- if (!IS_ERR(dentry)) {
- dentry->d_fsdata = (void *)attr;
- error = sysfs_create(dentry,
- (attr->attr.mode & S_IALLUGO) | S_IFREG,
- NULL);
- if (!error) {
- dentry->d_inode->i_size = attr->size;
- dentry->d_inode->i_fop = &bin_fops;
- }
- dput(dentry);
- } else
- error = PTR_ERR(dentry);
- up(&parent->d_inode->i_sem);
- return error;
+ if (kobj && kobj->dentry && attr)
+ return sysfs_add_file(kobj->dentry, &attr->attr,
+ SYSFS_KOBJ_BIN_ATTR);
+ return -EINVAL;
}



_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-18 06:32:34

by Maneesh Soni

[permalink] [raw]
Subject: [RFC 3/6] sysfs backing store v0.3



=> changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o Nil, just re-diffed

=> Changes:
o Removed the extra kobject_get from sysfs_release()

=======================================================
o sysfs_create_file() will just link a new sysfs_dirent() structure representing
the attribute file to the kobject's s_children list.

o in sysfs_create() we take extra ref. only for dentries corresponding to
non-regular files or in other words pin only non-leaf dentries.



fs/sysfs/file.c | 63 +++++++++++++++++++++++++------------------------------
fs/sysfs/inode.c | 14 +++++++++---
2 files changed, 40 insertions(+), 37 deletions(-)

diff -puN fs/sysfs/file.c~sysfs-leaves-file fs/sysfs/file.c
--- linux-2.6.5-rc1/fs/sysfs/file.c~sysfs-leaves-file 2004-03-18 11:36:30.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/file.c 2004-03-18 11:36:30.000000000 +0530
@@ -9,14 +9,6 @@

#include "sysfs.h"

-static struct file_operations sysfs_file_operations;
-
-static int init_file(struct inode * inode)
-{
- inode->i_size = PAGE_SIZE;
- inode->i_fop = &sysfs_file_operations;
- return 0;
-}

#define to_subsys(k) container_of(k,struct subsystem,kset.kobj)
#define to_sattr(a) container_of(a,struct subsys_attribute,attr)
@@ -77,8 +69,10 @@ 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 sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;
struct sysfs_ops * ops = buffer->ops;
int ret = 0;
ssize_t count;
@@ -198,8 +192,10 @@ 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 sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;
struct sysfs_ops * ops = buffer->ops;

return ops->store(kobj,attr,buffer->page,count);
@@ -238,8 +234,10 @@ 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 sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = kobject_get(sd_kobj->s_element);
struct sysfs_buffer * buffer;
struct sysfs_ops * ops = NULL;
int error = 0;
@@ -320,8 +318,10 @@ 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 sysfs_dirent * sd_attr = filp->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = filp->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;
struct sysfs_buffer * buffer = filp->private_data;

if (kobj)
@@ -336,7 +336,7 @@ static int sysfs_release(struct inode *
return 0;
}

-static struct file_operations sysfs_file_operations = {
+struct file_operations sysfs_file_operations = {
.read = sysfs_read_file,
.write = sysfs_write_file,
.llseek = generic_file_llseek,
@@ -345,23 +345,18 @@ static struct file_operations sysfs_file
};


-int sysfs_add_file(struct dentry * dir, const struct attribute * attr)
+int sysfs_add_file(struct dentry * parent, const struct attribute * attr, int t)
{
- struct dentry * dentry;
- int error;
+ struct sysfs_dirent * sd;
+ struct sysfs_dirent * parent_sd = parent->d_fsdata;
+ int error = 0;

- 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);
+ down(&parent->d_inode->i_sem);
+ sd = sysfs_new_dirent(parent_sd, (void *) attr, t);
+ if (!sd)
+ error = -ENOMEM;
+ up(&parent->d_inode->i_sem);
+
return error;
}

@@ -374,8 +369,8 @@ 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);
+ if (kobj && kobj->dentry && attr)
+ return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR);
return -EINVAL;
}

diff -puN fs/sysfs/inode.c~sysfs-leaves-file fs/sysfs/inode.c
--- linux-2.6.5-rc1/fs/sysfs/inode.c~sysfs-leaves-file 2004-03-18 11:36:30.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/inode.c 2004-03-18 11:36:30.000000000 +0530
@@ -11,6 +11,8 @@
#include <linux/pagemap.h>
#include <linux/namei.h>
#include <linux/backing-dev.h>
+#include "sysfs.h"
+
extern struct super_block * sysfs_sb;

static struct address_space_operations sysfs_aops = {
@@ -61,7 +63,8 @@ int sysfs_create(struct dentry * dentry,
error = init(inode);
if (!error) {
d_instantiate(dentry, inode);
- dget(dentry); /* Extra count - pin the dentry in core */
+ if (S_ISDIR(mode))
+ dget(dentry); /* pin only directory dentry in core */
} else
iput(inode);
Done:
@@ -96,14 +99,19 @@ void sysfs_hash_and_remove(struct dentry
pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name,
atomic_read(&victim->d_count));

- d_delete(victim);
- simple_unlink(dir->d_inode,victim);
+ if (S_ISDIR(victim->d_inode->i_mode)) {
+ d_delete(victim);
+ simple_unlink(dir->d_inode,victim);
+ }
+ else
+ d_drop(victim);
}
/*
* Drop reference from sysfs_get_dentry() above.
*/
dput(victim);
}
+ sysfs_remove_dirent(dir->d_fsdata, name);
up(&dir->d_inode->i_sem);
}


_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-18 06:33:54

by Maneesh Soni

[permalink] [raw]
Subject: [RFC 5/6] sysfs backing store v0.3




= changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o symlink name passed to sysfs_create_link() can be destroyed by the
caller. So, the symlink name should be allocated and copied to the
corresponding sysfs dirent instead of directly using the given name
string. The allocated string is freed when the corresponding sysfs_dirent
is freed through sysfs_put()

=================
o sysfs_create_link() now does not create a dentry but allocates a
sysfs_dirent and links it to the parent kobject.

o sysfs_dirent corresponding to symlink has an array of two string. One of
them is the name of the symlink and the second one is the target path of the
symlink


fs/sysfs/symlink.c | 48 ++++++++++++++++++++++++++++++++++++++----------
1 files changed, 38 insertions(+), 10 deletions(-)

diff -puN fs/sysfs/symlink.c~sysfs-leaves-symlink fs/sysfs/symlink.c
--- linux-2.6.5-rc1/fs/sysfs/symlink.c~sysfs-leaves-symlink 2004-03-18 11:37:04.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/symlink.c 2004-03-18 11:37:04.000000000 +0530
@@ -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;

@@ -63,6 +63,27 @@ static void fill_object_path(struct kobj
}
}

+static int sysfs_add_link(struct sysfs_dirent * parent_sd, char * name, char * target)
+{
+ struct sysfs_dirent * sd;
+ char ** link_names;
+
+ link_names = kmalloc(sizeof(char *) * 2, GFP_KERNEL);
+ if (!link_names)
+ return -ENOMEM;
+
+ link_names[0] = name;
+ link_names[1] = target;
+
+ sd = sysfs_new_dirent(parent_sd, link_names, SYSFS_KOBJ_LINK);
+ if (!sd) {
+ kfree(link_names);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
/**
* sysfs_create_link - create symlink between two objects.
* @kobj: object whose directory we're creating the link in.
@@ -72,13 +93,15 @@ 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 * link_name, * path;
char * s;

+ if (!name)
+ return -EINVAL;
+
depth = object_depth(kobj);
size = object_path_length(target) + depth * 3 - 1;
if (size > PATH_MAX)
@@ -96,15 +119,20 @@ int sysfs_create_link(struct kobject * k
fill_object_path(target,path,size);
pr_debug("%s: path = '%s'\n",__FUNCTION__,path);

+ link_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+ if (!link_name) {
+ kfree(path);
+ return -ENOMEM;
+ }
+ strcpy(link_name, name);
+
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);
+ error = sysfs_add_link(dentry->d_fsdata, link_name, path);
up(&dentry->d_inode->i_sem);
- kfree(path);
+ if (error) {
+ kfree(path);
+ kfree(link_name);
+ }
return error;
}


_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-18 06:33:54

by Maneesh Soni

[permalink] [raw]
Subject: [RFC 6/6] sysfs backing store v0.3



=> changes in version 0.3
o Corrected dentry ref counting for sysfs_create_group() and
sysfs_remove_group().

=> changes in Version 0.2
o Provided error checking after sysfs_get_dentry() call in
sysfs_remove_group().
o kfree the symlink name while freeing the corresponding sysfs_dirent in
sysfs_put().

================
o This patch has the changes required for attribute groups and misc. routines.


fs/sysfs/group.c | 21 +++++++++------
fs/sysfs/sysfs.h | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 89 insertions(+), 9 deletions(-)

diff -puN fs/sysfs/group.c~sysfs-leaves-misc fs/sysfs/group.c
--- linux-2.6.5-rc1/fs/sysfs/group.c~sysfs-leaves-misc 2004-03-18 11:39:08.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/group.c 2004-03-18 11:39:08.000000000 +0530
@@ -31,7 +31,7 @@ static int create_files(struct dentry *
int error = 0;

for (attr = grp->attrs; *attr && !error; attr++) {
- error = sysfs_add_file(dir,*attr);
+ error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR);
}
if (error)
remove_files(dir,grp);
@@ -55,8 +55,9 @@ int sysfs_create_group(struct kobject *
if ((error = create_files(dir,grp))) {
if (grp->name)
sysfs_remove_subdir(dir);
- dput(dir);
}
+ dput(dir);
+
return error;
}

@@ -65,15 +66,19 @@ void sysfs_remove_group(struct kobject *
{
struct dentry * dir;

- if (grp->name)
+ if (grp->name)
dir = sysfs_get_dentry(kobj->dentry,grp->name);
else
- dir = kobj->dentry;
+ dir = dget(kobj->dentry);

- remove_files(dir,grp);
- dput(dir);
- if (grp->name)
- sysfs_remove_subdir(dir);
+ if (!IS_ERR(dir) && dir->d_inode) {
+ remove_files(dir,grp);
+ if (grp->name)
+ sysfs_remove_subdir(dir);
+
+ /* release the ref. taken in this routine */
+ dput(dir);
+ }
}


diff -puN fs/sysfs/sysfs.h~sysfs-leaves-misc fs/sysfs/sysfs.h
--- linux-2.6.5-rc1/fs/sysfs/sysfs.h~sysfs-leaves-misc 2004-03-18 11:39:08.000000000 +0530
+++ linux-2.6.5-rc1-maneesh/fs/sysfs/sysfs.h 2004-03-18 11:39:08.000000000 +0530
@@ -1,4 +1,5 @@

+#include <linux/fs.h>
extern struct vfsmount * sysfs_mount;

extern struct inode * sysfs_new_inode(mode_t mode);
@@ -6,8 +7,82 @@ extern int sysfs_create(struct dentry *,

extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);

-extern int sysfs_add_file(struct dentry * dir, const struct attribute * attr);
+extern int sysfs_add_file(struct dentry *, const struct attribute *, int);
extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);

extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
extern void sysfs_remove_subdir(struct dentry *);
+
+extern loff_t sysfs_dir_lseek(struct file *, loff_t, int);
+extern int sysfs_readdir(struct file *, void *, filldir_t);
+extern void sysfs_umount_begin(struct super_block *);
+extern char * sysfs_get_name(struct sysfs_dirent *);
+extern struct dentry * sysfs_lookup(struct inode *, struct dentry *, struct nameidata *);
+extern int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname);
+
+extern struct file_operations sysfs_file_operations;
+extern struct file_operations bin_fops;
+extern struct inode_operations sysfs_dir_inode_operations;
+extern struct file_operations sysfs_dir_operations;
+
+
+static inline
+struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * p, void * e, int t)
+{
+ struct sysfs_dirent * sd;
+
+ sd = kmalloc(sizeof(*sd), GFP_KERNEL);
+ if (!sd)
+ return NULL;
+ memset(sd, 0, sizeof(*sd));
+ atomic_set(&sd->s_count, 1);
+ sd->s_element = e;
+ sd->s_type = t;
+ sd->s_dentry = NULL;
+ INIT_LIST_HEAD(&sd->s_children);
+ list_add(&sd->s_sibling, &p->s_children);
+
+ return sd;
+}
+
+static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd)
+{
+ if (sd) {
+ WARN_ON(!atomic_read(&sd->s_count));
+ atomic_inc(&sd->s_count);
+ }
+ return sd;
+}
+
+static inline void sysfs_put(struct sysfs_dirent * sd)
+{
+ if (atomic_dec_and_test(&sd->s_count)) {
+ if (sd->s_type & SYSFS_KOBJ_LINK) {
+ char ** link_names = sd->s_element;
+ kfree(link_names[0]);
+ kfree(link_names[1]);
+ kfree(sd->s_element);
+ }
+ kfree(sd);
+ }
+}
+
+static inline
+void sysfs_remove_dirent(struct sysfs_dirent * parent_sd, const char * name)
+{
+ struct list_head * tmp;
+
+ tmp = parent_sd->s_children.next;
+ while (tmp != & parent_sd->s_children) {
+ struct sysfs_dirent * sd;
+ sd = list_entry(tmp, struct sysfs_dirent, s_sibling);
+ tmp = tmp->next;
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ if (!strcmp(sysfs_get_name(sd), name)) {
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ }
+ }
+ }
+}
+

_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-20 17:57:42

by Matt Mackall

[permalink] [raw]
Subject: Re: [RFC 0/6] sysfs backing store v0.3

On Thu, Mar 18, 2004 at 12:03:06PM +0530, Maneesh Soni wrote:
> Hi,
>
> Please find the following patch set for sysfs backing store. Thanks
> to Carsten Otte and S390 Linux guys for reporting the bug while
> unloading zfcp driver.

Could you rediff this to apply against 2.6.5-rc2? It's getting a fair
number of rejects.

--
Matt Mackall : http://www.selenic.com : Linux development and consulting

2004-03-22 06:24:19

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 0/6] sysfs backing store v0.3

On Sat, Mar 20, 2004 at 11:57:09AM -0600, Matt Mackall wrote:
> On Thu, Mar 18, 2004 at 12:03:06PM +0530, Maneesh Soni wrote:
> > Hi,
> >
> > Please find the following patch set for sysfs backing store. Thanks
> > to Carsten Otte and S390 Linux guys for reporting the bug while
> > unloading zfcp driver.
>
> Could you rediff this to apply against 2.6.5-rc2? It's getting a fair
> number of rejects.
>

Please find the following patches for sysfs backing store v0.3 against
2.6.5-rc2. No, changes just fixed the rejects and re-diffed.

Thanks
Maneesh


--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-22 06:25:48

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 1/6] sysfs backing store v0.3



= changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o Nil, just re-diffed

=> changes in version 0.1
o Corrected sysfs_umount_begin(), it doesnot need lock_super() and also the
s_root check is not required. The reason being, that sysfs filesystem is
always mounted internally during init and there is no chance of sysfs super
block going away.

o corrected comments for umount_begin()

============================================================================
o The following patch contains the sysfs_dirent structure definition.
sysfs_dirent can represent kobject, attribute group, text attribute or
binary attribute for kobjects registered with sysfs. sysfs_dirent is
allocated with a ref count (s_count) of 1. Ref count is incremented when
a dentry is associated with the sysfs_dirent and it is decremented when
the corresponding dentry is freed.

o sysfs_dirent's corresponding to the attribute files of a kobject or attribute
group are linked together with s_sibling and are anchored at s_children of
the corresponding kobject's or attribute-group's sysfs_dirent.

o The patch also contains the mount related changes for sysfs backing store.
Because we mount sysfs once while init(), plain umount of sysfs doesnot
free all the un-used dentries (present in LRU list). To use force umount
flag, umount_begin() routine is provided which does a shrink_dcache_parent()
to release all the unused dentries.



fs/sysfs/mount.c | 27 +++++++++++++++++++++++++--
include/linux/sysfs.h | 19 +++++++++++++++++++
2 files changed, 44 insertions(+), 2 deletions(-)

diff -puN fs/sysfs/mount.c~sysfs-leaves-mount fs/sysfs/mount.c
--- linux-2.6.5-rc2/fs/sysfs/mount.c~sysfs-leaves-mount 2004-03-22 10:44:15.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/mount.c 2004-03-22 10:44:15.000000000 +0530
@@ -20,6 +20,14 @@ struct super_block * sysfs_sb = NULL;
static struct super_operations sysfs_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
+ .umount_begin = sysfs_umount_begin,
+};
+
+struct sysfs_dirent sysfs_root = {
+ .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling),
+ .s_children = LIST_HEAD_INIT(sysfs_root.s_children),
+ .s_element = NULL,
+ .s_type = SYSFS_ROOT,
};

static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
@@ -35,8 +43,8 @@ static int sysfs_fill_super(struct super

inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO);
if (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++;
} else {
@@ -50,6 +58,7 @@ static int sysfs_fill_super(struct super
iput(inode);
return -ENOMEM;
}
+ root->d_fsdata = &sysfs_root;
sb->s_root = root;
return 0;
}
@@ -60,6 +69,20 @@ static struct super_block *sysfs_get_sb(
return get_sb_single(fs_type, flags, data, sysfs_fill_super);
}

+/* For freeing zero refenced dentries / inodes while force unmounting
+ *
+ * sysfs is mounted once within kernel during init(), and this keeps the super
+ * block always active. So in case of user just doing normal umount,
+ * ->kill_sb is never called. So, in order to immediately free the memory used
+ * by un-used dentries and inodes, sysfs should to be umounted with force
+ * option. In anycase there will be normal pruing of unused dentries/inodes
+ * as usual due to memory pressure.
+ */
+void sysfs_umount_begin(struct super_block * sb)
+{
+ shrink_dcache_parent(sb->s_root);
+}
+
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.get_sb = sysfs_get_sb,
diff -puN include/linux/sysfs.h~sysfs-leaves-mount include/linux/sysfs.h
--- linux-2.6.5-rc2/include/linux/sysfs.h~sysfs-leaves-mount 2004-03-22 10:44:15.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/include/linux/sysfs.h 2004-03-22 10:44:15.000000000 +0530
@@ -9,6 +9,8 @@
#ifndef _SYSFS_H_
#define _SYSFS_H_

+#include <asm/atomic.h>
+
struct kobject;
struct module;

@@ -42,6 +44,23 @@ sysfs_remove_dir(struct kobject *);
extern void
sysfs_rename_dir(struct kobject *, const char *new_name);

+struct sysfs_dirent {
+ atomic_t s_count;
+ struct list_head s_sibling;
+ struct list_head s_children;
+ void * s_element;
+ int s_type;
+ struct dentry * s_dentry;
+};
+
+#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
+#define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK)
+
extern int
sysfs_create_file(struct kobject *, const struct attribute *);


_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-22 06:27:46

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 3/6] sysfs backing store v0.3



=> changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o Nil, just re-diffed

=> Changes:
o Removed the extra kobject_get from sysfs_release()

=======================================================
o sysfs_create_file() will just link a new sysfs_dirent() structure representing
the attribute file to the kobject's s_children list.

o in sysfs_create() we take extra ref. only for dentries corresponding to
non-regular files or in other words pin only non-leaf dentries.



fs/sysfs/file.c | 63 +++++++++++++++++++++++++------------------------------
fs/sysfs/inode.c | 14 +++++++++---
2 files changed, 40 insertions(+), 37 deletions(-)

diff -puN fs/sysfs/file.c~sysfs-leaves-file fs/sysfs/file.c
--- linux-2.6.5-rc2/fs/sysfs/file.c~sysfs-leaves-file 2004-03-22 10:44:16.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/file.c 2004-03-22 10:44:16.000000000 +0530
@@ -9,14 +9,6 @@

#include "sysfs.h"

-static struct file_operations sysfs_file_operations;
-
-static int init_file(struct inode * inode)
-{
- inode->i_size = PAGE_SIZE;
- inode->i_fop = &sysfs_file_operations;
- return 0;
-}

#define to_subsys(k) container_of(k,struct subsystem,kset.kobj)
#define to_sattr(a) container_of(a,struct subsys_attribute,attr)
@@ -77,8 +69,10 @@ 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 sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;
struct sysfs_ops * ops = buffer->ops;
int ret = 0;
ssize_t count;
@@ -198,8 +192,10 @@ 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 sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;
struct sysfs_ops * ops = buffer->ops;

return ops->store(kobj,attr,buffer->page,count);
@@ -238,8 +234,10 @@ 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 sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = kobject_get(sd_kobj->s_element);
struct sysfs_buffer * buffer;
struct sysfs_ops * ops = NULL;
int error = 0;
@@ -320,8 +318,10 @@ 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 sysfs_dirent * sd_attr = filp->f_dentry->d_fsdata;
+ struct attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = filp->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;
struct sysfs_buffer * buffer = filp->private_data;

if (kobj)
@@ -336,7 +336,7 @@ static int sysfs_release(struct inode *
return 0;
}

-static struct file_operations sysfs_file_operations = {
+struct file_operations sysfs_file_operations = {
.read = sysfs_read_file,
.write = sysfs_write_file,
.llseek = generic_file_llseek,
@@ -345,23 +345,18 @@ static struct file_operations sysfs_file
};


-int sysfs_add_file(struct dentry * dir, const struct attribute * attr)
+int sysfs_add_file(struct dentry * parent, const struct attribute * attr, int t)
{
- struct dentry * dentry;
- int error;
+ struct sysfs_dirent * sd;
+ struct sysfs_dirent * parent_sd = parent->d_fsdata;
+ int error = 0;

- 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);
+ down(&parent->d_inode->i_sem);
+ sd = sysfs_new_dirent(parent_sd, (void *) attr, t);
+ if (!sd)
+ error = -ENOMEM;
+ up(&parent->d_inode->i_sem);
+
return error;
}

@@ -374,8 +369,8 @@ 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);
+ if (kobj && kobj->dentry && attr)
+ return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR);
return -EINVAL;
}

diff -puN fs/sysfs/inode.c~sysfs-leaves-file fs/sysfs/inode.c
--- linux-2.6.5-rc2/fs/sysfs/inode.c~sysfs-leaves-file 2004-03-22 10:44:16.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/inode.c 2004-03-22 10:44:16.000000000 +0530
@@ -11,6 +11,8 @@
#include <linux/pagemap.h>
#include <linux/namei.h>
#include <linux/backing-dev.h>
+#include "sysfs.h"
+
extern struct super_block * sysfs_sb;

static struct address_space_operations sysfs_aops = {
@@ -61,7 +63,8 @@ int sysfs_create(struct dentry * dentry,
error = init(inode);
if (!error) {
d_instantiate(dentry, inode);
- dget(dentry); /* Extra count - pin the dentry in core */
+ if (S_ISDIR(mode))
+ dget(dentry); /* pin only directory dentry in core */
} else
iput(inode);
Done:
@@ -96,14 +99,19 @@ void sysfs_hash_and_remove(struct dentry
pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name,
atomic_read(&victim->d_count));

- d_delete(victim);
- simple_unlink(dir->d_inode,victim);
+ if (S_ISDIR(victim->d_inode->i_mode)) {
+ d_delete(victim);
+ simple_unlink(dir->d_inode,victim);
+ }
+ else
+ d_drop(victim);
}
/*
* Drop reference from sysfs_get_dentry() above.
*/
dput(victim);
}
+ sysfs_remove_dirent(dir->d_fsdata, name);
up(&dir->d_inode->i_sem);
}


_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-22 06:26:56

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 2/6] sysfs backing store v0.3



=> changes in version 0.3
o corrected dentry ref counting for sysfs_remove_subdir()

=> changes in version 0.2
o sysfs_rename_dir() used to call d_move() with unhashed new_dentry causing
panic in d_move(). Added d_add() call for new_dentry before calling d_move.
o corrected error checking after sysfs_get_dentry() in
sysfs_open_dir_entries().

=> changes in version 0.1
o re-diffed for 2.6.3

=> Changes in last version
o added support for symlink
o changed logic for sysfs_remove_dir
o dir dentries now take a ref on the corresponding kobject so as to have
kobject alive during the lifetime of the dentry.

=====================================================================
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 | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 333 insertions(+), 42 deletions(-)

diff -puN fs/sysfs/dir.c~sysfs-leaves-dir fs/sysfs/dir.c
--- linux-2.6.5-rc2/fs/sysfs/dir.c~sysfs-leaves-dir 2004-03-22 10:50:37.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/dir.c 2004-03-22 10:58:47.000000000 +0530
@@ -10,29 +10,151 @@
#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,
+};
+
+static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+{
+ struct sysfs_dirent * sd = dentry->d_fsdata;
+
+ if (sd) {
+ sd->s_dentry = NULL;
+ if ((sd->s_type & SYSFS_KOBJECT) ||
+ (sd->s_type & SYSFS_KOBJ_ATTR_GROUP))
+ kobject_put(sd->s_element);
+ 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)
+{
+ struct attribute * attr;
+ struct bin_attribute * bin_attr;
+ char ** link_names;
+
+ if (!sd || !sd->s_element)
+ BUG();
+
+ switch (sd->s_type) {
+ case SYSFS_KOBJ_ATTR:
+ attr = sd->s_element;
+ return attr->name;
+
+ case SYSFS_KOBJ_BIN_ATTR:
+ bin_attr = sd->s_element;
+ return bin_attr->attr.name;
+
+ case SYSFS_KOBJ_LINK:
+ link_names = sd->s_element;
+ return link_names[0];
+ }
+ return NULL;
+}
+
+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++;
return 0;
}

-static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+/* attaches attribute's sysfs_dirent to the dentry corresponding to the
+ * attribute file
+ */
+static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry)
{
- struct kobject * kobj = dentry->d_fsdata;
+ struct attribute * attr = NULL;
+ struct bin_attribute * bin_attr = NULL;
+ int (* init) (struct inode *) = NULL;
+ int error = 0;

- if (kobj)
- kobject_put(kobj);
- iput(inode);
+ 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;
}

-static struct dentry_operations sysfs_dentry_operations = {
- .d_iput = &sysfs_d_iput,
-};
+static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry)
+{
+ struct inode * dir = dentry->d_parent->d_inode;
+ char ** link_names = sd->s_element;
+ int err = 0;
+
+ err = sysfs_symlink(dir, dentry, link_names[1]);
+ if (!err) {
+ dentry->d_op = &sysfs_dentry_ops;
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ d_rehash(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;
+ int err = 0;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ char * name = sysfs_get_name(sd);
+ if (strcmp(name, dentry->d_name.name))
+ continue;
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ err = sysfs_attach_link(sd, dentry);
+ else
+ 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)
{
@@ -45,9 +167,21 @@ static int create_dir(struct kobject * k
S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO,
init_dir);
if (!error) {
- (*d)->d_op = &sysfs_dentry_operations;
- (*d)->d_fsdata = kobject_get(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++;
+ sd->s_element = kobject_get(k);
+ sd->s_dentry = *d;
+ d_rehash(*d);
+ } else
+ error = -ENOMEM;
}
dput(*d);
} else
@@ -56,7 +190,6 @@ static int create_dir(struct kobject * k
return error;
}

-
int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d)
{
return create_dir(k,k->dentry,n,d);
@@ -94,8 +227,13 @@ 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);
+ sd = d->d_fsdata;
+ list_del_init(&sd->s_sibling);
+ sysfs_put(d->d_fsdata);
if (d->d_inode)
simple_rmdir(parent->d_inode,d);

@@ -123,41 +261,29 @@ 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 * d;
+ struct sysfs_dirent * parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent * sd, * tmp;
+ char * name;

if (!dentry)
return;

pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
down(&dentry->d_inode->i_sem);
-
- spin_lock(&dcache_lock);
-restart:
- node = dentry->d_subdirs.next;
- while (node != &dentry->d_subdirs) {
- struct dentry * d = list_entry(node,struct dentry,d_child);
-
- node = node->next;
- pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count));
- if (!d_unhashed(d) && (d->d_inode)) {
- d = dget_locked(d);
- pr_debug("removing");
-
- /**
- * Unlink and unhash.
- */
- spin_unlock(&dcache_lock);
- d_delete(d);
- simple_unlink(dentry->d_inode,d);
- dput(d);
- pr_debug(" done\n");
- spin_lock(&dcache_lock);
- /* re-acquired dcache_lock, need to restart */
- goto restart;
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ name = sysfs_get_name(sd);
+ d = sysfs_get_dentry(dentry, name);
+ if (!IS_ERR(d) && d->d_inode) {
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ d_drop(d);
+ simple_unlink(dentry->d_inode, d);
+ }
}
}
- spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem);

remove_dir(dentry);
@@ -182,11 +308,176 @@ void sysfs_rename_dir(struct kobject * k
down(&parent->d_inode->i_sem);

new_dentry = sysfs_get_dentry(parent, new_name);
- d_move(kobj->dentry, new_dentry);
- kobject_set_name(kobj,new_name);
+ if (!IS_ERR(new_dentry)) {
+ d_add(new_dentry, NULL);
+ d_move(kobj->dentry, new_dentry);
+ kobject_set_name(kobj,new_name);
+ }
up(&parent->d_inode->i_sem);
}

+/* called under parent inode's i_sem */
+static void sysfs_close_dir_entries(struct dentry * parent)
+{
+ struct sysfs_dirent * parent_sd = parent->d_fsdata;
+ struct sysfs_dirent * sd, * tmp;
+
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ struct dentry * dentry = sd->s_dentry;
+ if (dentry && dentry->d_inode)
+ dput(dentry);
+ }
+ }
+}
+
+/* called under parent inode's i_sem */
+static int sysfs_open_dir_entries(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_NOT_PINNED) {
+ char * name = sysfs_get_name(sd);
+ dentry = sysfs_get_dentry(parent, name);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ break;
+ }
+ if (!dentry->d_inode) {
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ error = sysfs_attach_link(sd, dentry);
+ else
+ error = sysfs_attach_attr(sd, dentry);
+ }
+ if (error)
+ break;
+ }
+ }
+ if (error) {
+ /* release all successfully opened entires so far*/
+ sysfs_close_dir_entries(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_dir_entries(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_dir_entries(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_dir_entries(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_dir_entries(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: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-22 06:29:45

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 6/6] sysfs backing store v0.3



=> changes in version 0.3
o Corrected dentry ref counting for sysfs_create_group() and
sysfs_remove_group().

=> changes in Version 0.2
o Provided error checking after sysfs_get_dentry() call in
sysfs_remove_group().
o kfree the symlink name while freeing the corresponding sysfs_dirent in
sysfs_put().

================
o This patch has the changes required for attribute groups and misc. routines.


fs/sysfs/group.c | 2 -
fs/sysfs/sysfs.h | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 77 insertions(+), 2 deletions(-)

diff -puN fs/sysfs/group.c~sysfs-leaves-misc fs/sysfs/group.c
--- linux-2.6.5-rc2/fs/sysfs/group.c~sysfs-leaves-misc 2004-03-22 10:45:43.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/group.c 2004-03-22 10:48:34.000000000 +0530
@@ -31,7 +31,7 @@ static int create_files(struct dentry *
int error = 0;

for (attr = grp->attrs; *attr && !error; attr++) {
- error = sysfs_add_file(dir,*attr);
+ error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR);
}
if (error)
remove_files(dir,grp);
diff -puN fs/sysfs/sysfs.h~sysfs-leaves-misc fs/sysfs/sysfs.h
--- linux-2.6.5-rc2/fs/sysfs/sysfs.h~sysfs-leaves-misc 2004-03-22 10:45:54.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/sysfs.h 2004-03-22 10:46:39.000000000 +0530
@@ -1,4 +1,5 @@

+#include <linux/fs.h>
extern struct vfsmount * sysfs_mount;

extern struct inode * sysfs_new_inode(mode_t mode);
@@ -6,8 +7,82 @@ extern int sysfs_create(struct dentry *,

extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);

-extern int sysfs_add_file(struct dentry * dir, const struct attribute * attr);
+extern int sysfs_add_file(struct dentry *, const struct attribute *, int);
extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);

extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
extern void sysfs_remove_subdir(struct dentry *);
+
+extern loff_t sysfs_dir_lseek(struct file *, loff_t, int);
+extern int sysfs_readdir(struct file *, void *, filldir_t);
+extern void sysfs_umount_begin(struct super_block *);
+extern char * sysfs_get_name(struct sysfs_dirent *);
+extern struct dentry * sysfs_lookup(struct inode *, struct dentry *, struct nameidata *);
+extern int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname);
+
+extern struct file_operations sysfs_file_operations;
+extern struct file_operations bin_fops;
+extern struct inode_operations sysfs_dir_inode_operations;
+extern struct file_operations sysfs_dir_operations;
+
+
+static inline
+struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * p, void * e, int t)
+{
+ struct sysfs_dirent * sd;
+
+ sd = kmalloc(sizeof(*sd), GFP_KERNEL);
+ if (!sd)
+ return NULL;
+ memset(sd, 0, sizeof(*sd));
+ atomic_set(&sd->s_count, 1);
+ sd->s_element = e;
+ sd->s_type = t;
+ sd->s_dentry = NULL;
+ INIT_LIST_HEAD(&sd->s_children);
+ list_add(&sd->s_sibling, &p->s_children);
+
+ return sd;
+}
+
+static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd)
+{
+ if (sd) {
+ WARN_ON(!atomic_read(&sd->s_count));
+ atomic_inc(&sd->s_count);
+ }
+ return sd;
+}
+
+static inline void sysfs_put(struct sysfs_dirent * sd)
+{
+ if (atomic_dec_and_test(&sd->s_count)) {
+ if (sd->s_type & SYSFS_KOBJ_LINK) {
+ char ** link_names = sd->s_element;
+ kfree(link_names[0]);
+ kfree(link_names[1]);
+ kfree(sd->s_element);
+ }
+ kfree(sd);
+ }
+}
+
+static inline
+void sysfs_remove_dirent(struct sysfs_dirent * parent_sd, const char * name)
+{
+ struct list_head * tmp;
+
+ tmp = parent_sd->s_children.next;
+ while (tmp != & parent_sd->s_children) {
+ struct sysfs_dirent * sd;
+ sd = list_entry(tmp, struct sysfs_dirent, s_sibling);
+ tmp = tmp->next;
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ if (!strcmp(sysfs_get_name(sd), name)) {
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ }
+ }
+ }
+}
+

_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-22 06:29:46

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 4/6] sysfs backing store v0.3




= changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o symlink name passed to sysfs_create_link() can be destroyed by the
caller. So, the symlink name should be allocated and copied to the
corresponding sysfs dirent instead of directly using the given name
string. The allocated string is freed when the corresponding sysfs_dirent
is freed through sysfs_put()

=================
o sysfs_create_link() now does not create a dentry but allocates a
sysfs_dirent and links it to the parent kobject.

o sysfs_dirent corresponding to symlink has an array of two string. One of
them is the name of the symlink and the second one is the target path of the
symlink


fs/sysfs/symlink.c | 48 ++++++++++++++++++++++++++++++++++++++----------
1 files changed, 38 insertions(+), 10 deletions(-)

diff -puN fs/sysfs/symlink.c~sysfs-leaves-symlink fs/sysfs/symlink.c
--- linux-2.6.5-rc2/fs/sysfs/symlink.c~sysfs-leaves-symlink 2004-03-22 10:44:17.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/symlink.c 2004-03-22 10:44:17.000000000 +0530
@@ -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;

@@ -63,6 +63,27 @@ static void fill_object_path(struct kobj
}
}

+static int sysfs_add_link(struct sysfs_dirent * parent_sd, char * name, char * target)
+{
+ struct sysfs_dirent * sd;
+ char ** link_names;
+
+ link_names = kmalloc(sizeof(char *) * 2, GFP_KERNEL);
+ if (!link_names)
+ return -ENOMEM;
+
+ link_names[0] = name;
+ link_names[1] = target;
+
+ sd = sysfs_new_dirent(parent_sd, link_names, SYSFS_KOBJ_LINK);
+ if (!sd) {
+ kfree(link_names);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
/**
* sysfs_create_link - create symlink between two objects.
* @kobj: object whose directory we're creating the link in.
@@ -72,13 +93,15 @@ 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 * link_name, * path;
char * s;

+ if (!name)
+ return -EINVAL;
+
depth = object_depth(kobj);
size = object_path_length(target) + depth * 3 - 1;
if (size > PATH_MAX)
@@ -96,15 +119,20 @@ int sysfs_create_link(struct kobject * k
fill_object_path(target,path,size);
pr_debug("%s: path = '%s'\n",__FUNCTION__,path);

+ link_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+ if (!link_name) {
+ kfree(path);
+ return -ENOMEM;
+ }
+ strcpy(link_name, name);
+
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);
+ error = sysfs_add_link(dentry->d_fsdata, link_name, path);
up(&dentry->d_inode->i_sem);
- kfree(path);
+ if (error) {
+ kfree(path);
+ kfree(link_name);
+ }
return error;
}


_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-22 06:29:45

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 5/6] sysfs backing store v0.3



=> changes in version 0.3
o Nil, just re-diffed

=> changes in version 0.2
o Nil, just re-diffed

o This patch contains changes required for bin attribute files.


fs/sysfs/bin.c | 52 +++++++++++++++++++---------------------------------
1 files changed, 19 insertions(+), 33 deletions(-)

diff -puN fs/sysfs/bin.c~sysfs-leaves-bin fs/sysfs/bin.c
--- linux-2.6.5-rc2/fs/sysfs/bin.c~sysfs-leaves-bin 2004-03-22 10:44:18.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/bin.c 2004-03-22 10:44:18.000000000 +0530
@@ -17,8 +17,10 @@
static int
fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
{
- struct bin_attribute * attr = dentry->d_fsdata;
- struct kobject * kobj = dentry->d_parent->d_fsdata;
+ struct sysfs_dirent * sd_attr = dentry->d_fsdata;
+ struct bin_attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;

return attr->read(kobj, buffer, off, count);
}
@@ -60,8 +62,10 @@ read(struct file * file, char __user * u
static int
flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
{
- struct bin_attribute *attr = dentry->d_fsdata;
- struct kobject *kobj = dentry->d_parent->d_fsdata;
+ struct sysfs_dirent * sd_attr = dentry->d_fsdata;
+ struct bin_attribute * attr = sd_attr->s_element;
+ struct sysfs_dirent * sd_kobj = dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd_kobj->s_element;

return attr->write(kobj, buffer, offset, count);
}
@@ -94,8 +98,10 @@ static ssize_t write(struct file * file,

static int open(struct inode * inode, struct file * file)
{
- struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata);
- struct bin_attribute * attr = file->f_dentry->d_fsdata;
+ struct sysfs_dirent * sd_kobj = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = kobject_get(sd_kobj->s_element);
+ struct sysfs_dirent * sd_attr = file->f_dentry->d_fsdata;
+ struct bin_attribute * attr = sd_attr->s_element;
int error = -EINVAL;

if (!kobj || !attr)
@@ -122,7 +128,8 @@ static int open(struct inode * inode, st

static int release(struct inode * inode, struct file * file)
{
- struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
+ struct sysfs_dirent * sd = file->f_dentry->d_parent->d_fsdata;
+ struct kobject * kobj = sd->s_element;
u8 * buffer = file->private_data;

if (kobj)
@@ -131,7 +138,7 @@ static int release(struct inode * inode,
return 0;
}

-static struct file_operations bin_fops = {
+struct file_operations bin_fops = {
.read = read,
.write = write,
.llseek = generic_file_llseek,
@@ -148,31 +155,10 @@ static struct file_operations bin_fops =

int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
{
- struct dentry * dentry;
- struct dentry * parent;
- int error = 0;
-
- if (!kobj || !attr)
- return -EINVAL;
-
- parent = kobj->dentry;
-
- down(&parent->d_inode->i_sem);
- dentry = sysfs_get_dentry(parent,attr->attr.name);
- if (!IS_ERR(dentry)) {
- dentry->d_fsdata = (void *)attr;
- error = sysfs_create(dentry,
- (attr->attr.mode & S_IALLUGO) | S_IFREG,
- NULL);
- if (!error) {
- dentry->d_inode->i_size = attr->size;
- dentry->d_inode->i_fop = &bin_fops;
- }
- dput(dentry);
- } else
- error = PTR_ERR(dentry);
- up(&parent->d_inode->i_sem);
- return error;
+ if (kobj && kobj->dentry && attr)
+ return sysfs_add_file(kobj->dentry, &attr->attr,
+ SYSFS_KOBJ_BIN_ATTR);
+ return -EINVAL;
}



_
--
Maneesh Soni
Linux Technology Center,
IBM Software Lab, Bangalore, India
email: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696

2004-03-24 10:41:09

by Maneesh Soni

[permalink] [raw]
Subject: Re: [RFC 2/6] sysfs backing store v0.3a

Hello,

Please find the following patch (2/6) for sysfs backing store. This fixes
a leak observed in case of sysfs_readdir() for _big_ sysfs directories. The
dentries were not getting released in case of error return from filldir()
in sysfs_readdir().

Thanks again to Christian Borntraeger (S390 Linux) for spotting this and testing
the fix.

As all other patches are not changed, I thought of not to over populate people's
mailboxes and have just posted this replacement patch.


Thanks
Maneesh



=> changes in version 0.3a
o call sysfs_close_dir_entries() when filldir() returns error in
sysfs_readdir().

=> changes in version 0.3
o corrected dentry ref counting for sysfs_remove_subdir()

=> changes in version 0.2
o sysfs_rename_dir() used to call d_move() with unhashed new_dentry causing
panic in d_move(). Added d_add() call for new_dentry before calling d_move.
o corrected error checking after sysfs_get_dentry() in
sysfs_open_dir_entries().

=> changes in version 0.1
o re-diffed for 2.6.3

=> Changes in last version
o added support for symlink
o changed logic for sysfs_remove_dir
o dir dentries now take a ref on the corresponding kobject so as to have
kobject alive during the lifetime of the dentry.

=====================================================================
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 | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 335 insertions(+), 42 deletions(-)

diff -puN fs/sysfs/dir.c~sysfs-leaves-dir fs/sysfs/dir.c
--- linux-2.6.5-rc2/fs/sysfs/dir.c~sysfs-leaves-dir 2004-03-22 11:11:38.000000000 +0530
+++ linux-2.6.5-rc2-maneesh/fs/sysfs/dir.c 2004-03-24 14:58:34.000000000 +0530
@@ -10,29 +10,151 @@
#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,
+};
+
+static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+{
+ struct sysfs_dirent * sd = dentry->d_fsdata;
+
+ if (sd) {
+ sd->s_dentry = NULL;
+ if ((sd->s_type & SYSFS_KOBJECT) ||
+ (sd->s_type & SYSFS_KOBJ_ATTR_GROUP))
+ kobject_put(sd->s_element);
+ 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)
+{
+ struct attribute * attr;
+ struct bin_attribute * bin_attr;
+ char ** link_names;
+
+ if (!sd || !sd->s_element)
+ BUG();
+
+ switch (sd->s_type) {
+ case SYSFS_KOBJ_ATTR:
+ attr = sd->s_element;
+ return attr->name;
+
+ case SYSFS_KOBJ_BIN_ATTR:
+ bin_attr = sd->s_element;
+ return bin_attr->attr.name;
+
+ case SYSFS_KOBJ_LINK:
+ link_names = sd->s_element;
+ return link_names[0];
+ }
+ return NULL;
+}
+
+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++;
return 0;
}

-static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+/* attaches attribute's sysfs_dirent to the dentry corresponding to the
+ * attribute file
+ */
+static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry)
{
- struct kobject * kobj = dentry->d_fsdata;
+ struct attribute * attr = NULL;
+ struct bin_attribute * bin_attr = NULL;
+ int (* init) (struct inode *) = NULL;
+ int error = 0;

- if (kobj)
- kobject_put(kobj);
- iput(inode);
+ 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;
}

-static struct dentry_operations sysfs_dentry_operations = {
- .d_iput = &sysfs_d_iput,
-};
+static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry)
+{
+ struct inode * dir = dentry->d_parent->d_inode;
+ char ** link_names = sd->s_element;
+ int err = 0;
+
+ err = sysfs_symlink(dir, dentry, link_names[1]);
+ if (!err) {
+ dentry->d_op = &sysfs_dentry_ops;
+ dentry->d_fsdata = sysfs_get(sd);
+ sd->s_dentry = dentry;
+ d_rehash(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;
+ int err = 0;
+
+ list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ char * name = sysfs_get_name(sd);
+ if (strcmp(name, dentry->d_name.name))
+ continue;
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ err = sysfs_attach_link(sd, dentry);
+ else
+ 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)
{
@@ -45,9 +167,21 @@ static int create_dir(struct kobject * k
S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO,
init_dir);
if (!error) {
- (*d)->d_op = &sysfs_dentry_operations;
- (*d)->d_fsdata = kobject_get(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++;
+ sd->s_element = kobject_get(k);
+ sd->s_dentry = *d;
+ d_rehash(*d);
+ } else
+ error = -ENOMEM;
}
dput(*d);
} else
@@ -56,7 +190,6 @@ static int create_dir(struct kobject * k
return error;
}

-
int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d)
{
return create_dir(k,k->dentry,n,d);
@@ -94,8 +227,13 @@ 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);
+ sd = d->d_fsdata;
+ list_del_init(&sd->s_sibling);
+ sysfs_put(d->d_fsdata);
if (d->d_inode)
simple_rmdir(parent->d_inode,d);

@@ -123,41 +261,29 @@ 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 * d;
+ struct sysfs_dirent * parent_sd = dentry->d_fsdata;
+ struct sysfs_dirent * sd, * tmp;
+ char * name;

if (!dentry)
return;

pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
down(&dentry->d_inode->i_sem);
-
- spin_lock(&dcache_lock);
-restart:
- node = dentry->d_subdirs.next;
- while (node != &dentry->d_subdirs) {
- struct dentry * d = list_entry(node,struct dentry,d_child);
-
- node = node->next;
- pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count));
- if (!d_unhashed(d) && (d->d_inode)) {
- d = dget_locked(d);
- pr_debug("removing");
-
- /**
- * Unlink and unhash.
- */
- spin_unlock(&dcache_lock);
- d_delete(d);
- simple_unlink(dentry->d_inode,d);
- dput(d);
- pr_debug(" done\n");
- spin_lock(&dcache_lock);
- /* re-acquired dcache_lock, need to restart */
- goto restart;
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ name = sysfs_get_name(sd);
+ d = sysfs_get_dentry(dentry, name);
+ if (!IS_ERR(d) && d->d_inode) {
+ list_del_init(&sd->s_sibling);
+ sysfs_put(sd);
+ d_drop(d);
+ simple_unlink(dentry->d_inode, d);
+ }
}
}
- spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem);

remove_dir(dentry);
@@ -182,11 +308,178 @@ void sysfs_rename_dir(struct kobject * k
down(&parent->d_inode->i_sem);

new_dentry = sysfs_get_dentry(parent, new_name);
- d_move(kobj->dentry, new_dentry);
- kobject_set_name(kobj,new_name);
+ if (!IS_ERR(new_dentry)) {
+ d_add(new_dentry, NULL);
+ d_move(kobj->dentry, new_dentry);
+ kobject_set_name(kobj,new_name);
+ }
up(&parent->d_inode->i_sem);
}

+/* called under parent inode's i_sem */
+static void sysfs_close_dir_entries(struct dentry * parent)
+{
+ struct sysfs_dirent * parent_sd = parent->d_fsdata;
+ struct sysfs_dirent * sd, * tmp;
+
+ list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
+ if (sd->s_type & SYSFS_NOT_PINNED) {
+ struct dentry * dentry = sd->s_dentry;
+ if (dentry && dentry->d_inode)
+ dput(dentry);
+ }
+ }
+}
+
+/* called under parent inode's i_sem */
+static int sysfs_open_dir_entries(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_NOT_PINNED) {
+ char * name = sysfs_get_name(sd);
+ dentry = sysfs_get_dentry(parent, name);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ break;
+ }
+ if (!dentry->d_inode) {
+ if (sd->s_type & SYSFS_KOBJ_LINK)
+ error = sysfs_attach_link(sd, dentry);
+ else
+ error = sysfs_attach_attr(sd, dentry);
+ }
+ if (error)
+ break;
+ }
+ }
+ if (error) {
+ /* release all successfully opened entires so far*/
+ sysfs_close_dir_entries(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_dir_entries(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) {
+ sysfs_close_dir_entries(dentry);
+ 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_dir_entries(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_dir_entries(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_dir_entries(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: [email protected]
Phone: 91-80-25044999 Fax: 91-80-25268553
T/L : 9243696