* [RFC 0/5] sysfs backing store (leaves only) @ 2003-12-29 9:32 Maneesh Soni 2003-12-29 9:33 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-mount.patch Maneesh Soni 0 siblings, 1 reply; 7+ messages in thread From: Maneesh Soni @ 2003-12-29 9:32 UTC (permalink / raw) To: LKML; +Cc: Greg KH, Al Viro, Patrick Mochel, Dipankar Sarma Hi all, The following patch set(mailed separately) implements a backing store mechanism for sysfs filesystem but only for leaf entires. In the previous attempt I tried to do backing store for all the entires in sysfs (directories & regular files) but that prototype had problems related to coherency between sysfs tree and VFS based dentry tree and solution for which will probably need VFS layer changes. In leaves only backing store, attribute files, text or binary, are created on demand and there is no need to pin the dentries & inodes corresponding to such sysfs files. Generally there are around 60-70% leaf entries in sysfs tree. Non-regular files (directories and symlinks) are pinned as usual. This helps in solving the coherency problem as we can use the parent inode's (or directory's) i_sem while creating, removing or walking files present in a given directory. There are no changes in the way sysfs or kobjects are used. All interfaces are same and function as without this patch. Please review the patch set following this posting. I have also collected following numbers in support of the backing store. Test1 ----- Following test shows how dentries are created on demand and are pruned whenever there is a memory pressure. - after boot [root@llm04 root]# cat /proc/sys/fs/dentry-state 6900 5783 45 0 0 0 [root@llm04 root]# mount -t sysfs none /sys [root@llm04 root]# cat /proc/sys/fs/dentry-state 6902 5784 45 0 0 0 [root@llm04 root]# tree /sys | tail -1 848 directories, 2171 files [root@llm04 root]# cat /proc/sys/fs/dentry-state 9081 7968 45 0 0 0 Ran some memory stress test (mmapstress01 from ltp-full-20031106) and parallely watching dentry statistics. [root@llm04 tmp]# ./mmapstress01 -p 4 -f 1000000000 write error: No space left on device mmapstress01 1 FAIL : Test failed [root@llm04 root]# while true; do cat /proc/sys/fs/dentry-state; sleep 2; done 9087 7974 45 0 0 0 9089 7976 45 0 0 0 9091 7977 45 0 0 0 9091 7977 45 0 0 0 9091 7977 45 0 0 0 9091 7977 45 0 0 0 9091 7897 45 0 0 0 9091 7879 45 0 0 0 6977 5844 45 0 0 0 <===== dcache pruning started here 6423 5293 45 0 0 0 6422 5294 45 0 0 0 6422 5294 45 0 0 0 6422 5294 45 0 0 0 As the numbers are collected from /proc/sys/fs/dentry-state, they are for entire system. But because during measurement there were file creation/removal in sysfs tree only, I think we can consider most of them correspond to sysfs. The test was done on a 4-way SMP box with 512 MB RAM. Test 2 ------ One more test I did was trying to create 4000 scsi_debug disks and observe the lowmem statistics on a 2-way SMP box with 4.5 GB RAM. After creating 4000 debug disks we have the following stats: (Please bear that I couldn't actually create 4000 disks because of error I was getting regarding scsi generic device (minor number exceeded 255) but I was able to create lots of sysfs entries more than 50 thousand. With backing store patch ======================== /proc/sys/fs/dentry-state LowFree no sysfs 22903 4132 45 0 0 0 780664 kB mount 22904 4133 45 0 0 0 780600 kB cat /sys/block/sdk/queue/nr_requests 128 22905 4134 45 0 0 0 780600 kB tree /sys | tail -1 18557 directories, 48510 files 71410 52639 45 0 0 0 749040 kB umount -f /sys/ 22905 4134 45 0 0 0 780088 kB Without backing store ===================== /proc/sys/fs/dentry-state; LowFree no sysfs 71395 4114 45 0 0 0; 767412 kB mount sysfs 71396 4115 45 0 0 0; 767476 kB cat /sys/block/sdk/queue/nr_requests 128 71396 4115 45 0 0 0; 767540 kB tree /sys |tail -1 18557 directories, 48510 files 71396 4115 45 0 0 0; 767340 kB umount -f /sys 71397 4116 45 0 0 0; 767340 kB As we can see there is no change in Lowmem stats without patch when we mount or unmount sysfs. Where as with backing store patch we can save approx. 30 MB of lowmem in when sysfs files are not in use. -- Maneesh Soni Linux Technology Center, IBM Software Lab, Bangalore, India email: maneesh@in.ibm.com Phone: 91-80-5044999 Fax: 91-80-5268553 T/L : 9243696 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-mount.patch 2003-12-29 9:32 [RFC 0/5] sysfs backing store (leaves only) Maneesh Soni @ 2003-12-29 9:33 ` Maneesh Soni 2003-12-29 9:34 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni 0 siblings, 1 reply; 7+ messages in thread From: Maneesh Soni @ 2003-12-29 9:33 UTC (permalink / raw) To: LKML; +Cc: Greg KH, Al Viro, Patrick Mochel, Dipankar Sarma 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 | 21 +++++++++++++++++++-- include/linux/sysfs.h | 17 +++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff -puN include/linux/sysfs.h~sysfs-leaves-mount include/linux/sysfs.h --- linux-2.6.0/include/linux/sysfs.h~sysfs-leaves-mount 2003-12-29 12:50:50.000000000 +0530 +++ linux-2.6.0-maneesh/include/linux/sysfs.h 2003-12-29 12:53:50.000000000 +0530 @@ -9,6 +9,8 @@ #ifndef _SYSFS_H_ #define _SYSFS_H_ +#include <asm/atomic.h> + struct kobject; struct module; @@ -33,6 +35,21 @@ struct sysfs_ops { ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); }; +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 + extern int sysfs_create_dir(struct kobject *); diff -puN fs/sysfs/mount.c~sysfs-leaves-mount fs/sysfs/mount.c --- linux-2.6.0/fs/sysfs/mount.c~sysfs-leaves-mount 2003-12-29 12:51:04.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/mount.c 2003-12-29 12:59:21.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,14 @@ static struct super_block *sysfs_get_sb( return get_sb_single(fs_type, flags, data, sysfs_fill_super); } +void sysfs_umount_begin(struct super_block * sb) +{ + lock_super(sb); + if (sb->s_root) + shrink_dcache_parent(sb->s_root); + unlock_super(sb); +} + static struct file_system_type sysfs_fs_type = { .name = "sysfs", .get_sb = sysfs_get_sb, _ -- Maneesh Soni Linux Technology Center, IBM Software Lab, Bangalore, India email: maneesh@in.ibm.com Phone: 91-80-5044999 Fax: 91-80-5268553 T/L : 9243696 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch 2003-12-29 9:33 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-mount.patch Maneesh Soni @ 2003-12-29 9:34 ` Maneesh Soni 2003-12-29 9:35 ` [RFC 3/5] sysfs backing store (leaves only) - sysfs-leaves-file.patch Maneesh Soni 2004-01-02 6:45 ` [RFC 2/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni 0 siblings, 2 replies; 7+ messages in thread From: Maneesh Soni @ 2003-12-29 9:34 UTC (permalink / raw) To: LKML; +Cc: Greg KH, Al Viro, Patrick Mochel, Dipankar Sarma o This patch provides the inode operations ->lookup(), ->readdir() and ->llseek() for sysfs directories. o while sysfs_create_dir() we attach a sysfs_dirent structure to the d_fsdata filed of dentry corresponding to the kobject's direcotry. o sysfs_lookup does not hash the dentry and we hash the dentry when we have attached the sysfs_dirent to it. This was done to cover up a race when we attach a negative dentry and instantiate it before updating the d_fsdata field. As after instantiating we can get a successfull lookup for the dentry but a NULL d_fsdata field. As a result we do not create negative dentries. o sysfs_readdir() or sysfs_dir_lseek() will bring in the dentries corresponding to the attribute files if the offset is more than 2. These are released when we are done with filldir(). o sysfs_d_iput() releases the ref. to the sysfs_dirent() which was taken at the time of dentry allocation. fs/sysfs/dir.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 292 insertions(+), 9 deletions(-) diff -puN fs/sysfs/dir.c~sysfs-leaves-dir fs/sysfs/dir.c --- linux-2.6.0/fs/sysfs/dir.c~sysfs-leaves-dir 2003-12-29 12:30:50.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/dir.c 2003-12-29 12:30:56.000000000 +0530 @@ -10,10 +10,54 @@ #include <linux/kobject.h> #include "sysfs.h" +struct inode_operations sysfs_dir_inode_operations = { + .lookup = sysfs_lookup, +}; + +struct file_operations sysfs_dir_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .llseek = sysfs_dir_lseek, + .read = generic_read_dir, + .readdir = sysfs_readdir, +}; + +/* dentry iput only for sysfs leaf dentries */ +static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) +{ + struct sysfs_dirent * sd = dentry->d_fsdata; + + if (sd) + sysfs_put(sd); + iput(inode); +} + + +static struct dentry_operations sysfs_dentry_ops = { + .d_iput = sysfs_d_iput, +}; + +char * sysfs_get_name(struct sysfs_dirent *sd) +{ + if (!sd || !sd->s_element) + BUG(); + + return (sd->s_type & SYSFS_KOBJ_ATTR) ? + ((struct attribute *)(sd->s_element))->name : + ((struct bin_attribute *)(sd->s_element))->attr.name; +} + +static int init_file(struct inode * inode) +{ + inode->i_size = PAGE_SIZE; + inode->i_fop = &sysfs_file_operations; + return 0; +} + static int init_dir(struct inode * inode) { - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; @@ -21,6 +65,61 @@ static int init_dir(struct inode * inode } +/* attaches attribute's sysfs_dirent to the dentry corresponding to the + * attribute file + */ +int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) +{ + struct attribute * attr = NULL; + struct bin_attribute * bin_attr = NULL; + int (* init) (struct inode *) = NULL; + int error = 0; + + if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { + bin_attr = sd->s_element; + attr = &bin_attr->attr; + } else { + attr = sd->s_element; + init = init_file; + } + + error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init); + if (error) + return error; + + if (bin_attr) { + dentry->d_inode->i_size = bin_attr->size; + dentry->d_inode->i_fop = &bin_fops; + } + dentry->d_op = &sysfs_dentry_ops; + dentry->d_fsdata = sysfs_get(sd); + sd->s_dentry = dentry; + d_rehash(dentry); + + return 0; +} + +struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata; + struct sysfs_dirent * sd; + int err = 0; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if ((sd->s_type == SYSFS_KOBJ_ATTR) + || (sd->s_type == SYSFS_KOBJ_BIN_ATTR)) { + char * name = sysfs_get_name(sd); + if (strcmp(name, dentry->d_name.name)) + continue; + err = sysfs_attach_attr(sd, dentry); + break; + } + } + + return ERR_PTR(err); +} + static int create_dir(struct kobject * k, struct dentry * p, const char * n, struct dentry ** d) { @@ -29,12 +128,23 @@ static int create_dir(struct kobject * k down(&p->d_inode->i_sem); *d = sysfs_get_dentry(p,n); if (!IS_ERR(*d)) { - error = sysfs_create(*d, + error = sysfs_create(*d, S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO, init_dir); if (!error) { - (*d)->d_fsdata = k; - p->d_inode->i_nlink++; + struct sysfs_dirent * sd, * parent_sd; + parent_sd = p->d_fsdata; + sd = sysfs_new_dirent(parent_sd, k, + (parent_sd->s_element == k) ? + SYSFS_KOBJ_ATTR_GROUP : + SYSFS_KOBJECT); + if (sd) { + (*d)->d_fsdata = sysfs_get(sd); + (*d)->d_op = &sysfs_dentry_ops; + p->d_inode->i_nlink++; + d_rehash(*d); + } else + error = -ENOMEM; } dput(*d); } else @@ -81,9 +191,15 @@ int sysfs_create_dir(struct kobject * ko static void remove_dir(struct dentry * d) { struct dentry * parent = dget(d->d_parent); + struct sysfs_dirent * sd; + down(&parent->d_inode->i_sem); d_delete(d); - simple_rmdir(parent->d_inode,d); + sd = d->d_fsdata; + list_del_init(&sd->s_sibling); + sysfs_put(d->d_fsdata); + if (parent->d_inode) + simple_rmdir(parent->d_inode,d); pr_debug(" o %s removing done (%d)\n",d->d_name.name, atomic_read(&d->d_count)); @@ -133,9 +249,18 @@ void sysfs_remove_dir(struct kobject * k * Unlink and unhash. */ spin_unlock(&dcache_lock); - d_delete(d); - simple_unlink(dentry->d_inode,d); - dput(d); + if (S_ISREG(d->d_inode->i_mode)) { + struct sysfs_dirent * sd = d->d_fsdata; + + list_del_init(&sd->s_sibling); + sysfs_put(sd); + d_drop(d); + simple_unlink(dentry->d_inode,d); + } else { + d_delete(d); + simple_unlink(dentry->d_inode,d); + dput(d); + } spin_lock(&dcache_lock); } pr_debug(" done\n"); @@ -172,6 +297,164 @@ void sysfs_rename_dir(struct kobject * k up(&parent->d_inode->i_sem); } +/* called under parent inode's i_sem (taken in vfs_readdir */ +static void sysfs_close_attr_files(struct dentry * parent) +{ + struct sysfs_dirent * parent_sd = parent->d_fsdata; + struct sysfs_dirent * sd; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if ((sd->s_type == SYSFS_KOBJ_ATTR) || + (sd->s_type == SYSFS_KOBJ_BIN_ATTR)) { + struct dentry * dentry = sd->s_dentry; + if (dentry && dentry->d_inode) + dput(dentry); + } + } +} + +/* called under parent inode's i_sem (taken in vfs_readdir */ +static int sysfs_open_attr_files(struct dentry * parent) +{ + struct sysfs_dirent * parent_sd = parent->d_fsdata; + struct sysfs_dirent * sd; + struct dentry * dentry; + int error = 0; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if ((sd->s_type == SYSFS_KOBJ_ATTR) || + (sd->s_type == SYSFS_KOBJ_BIN_ATTR)) { + char * name = sysfs_get_name(sd); + dentry = sysfs_get_dentry(parent, name); + if (IS_ERR(dentry)) + error = PTR_ERR(dentry); + if (!dentry->d_inode) + error = sysfs_attach_attr(sd, dentry); + if (error) + break; + } + } + if (error) { + /* release all successfully opened entires so far*/ + sysfs_close_attr_files(parent); + } + + return error; +} + +/* Relationship between i_mode and the DT_xxx types */ +static inline unsigned char dt_type(struct inode *inode) +{ + return (inode->i_mode >> 12) & 15; +} + +/* + * Directory is locked and all positive dentries in it are safe, since + * for ramfs-type trees they can't go away without unlink() or rmdir(), + * both impossible due to the lock on directory. + */ + +int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_dentry; + struct dentry *cursor = filp->private_data; + struct list_head *p, *q = &cursor->d_child; + ino_t ino; + int i = filp->f_pos; + int err = 0; + + switch (i) { + case 0: + ino = dentry->d_inode->i_ino; + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) + break; + filp->f_pos++; + i++; + /* fallthrough */ + case 1: + ino = parent_ino(dentry); + if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) + break; + filp->f_pos++; + i++; + /* fallthrough */ + default: + if ((err = sysfs_open_attr_files(dentry))) + return err; + + spin_lock(&dcache_lock); + if (filp->f_pos == 2) { + list_del(q); + list_add(q, &dentry->d_subdirs); + } + for (p=q->next; p != &dentry->d_subdirs; p=p->next) { + struct dentry *next; + next = list_entry(p, struct dentry, d_child); + if (d_unhashed(next) || !next->d_inode) + continue; + + spin_unlock(&dcache_lock); + if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode)) < 0) + return 0; + spin_lock(&dcache_lock); + /* next is still alive */ + list_del(q); + list_add(q, p); + p = q; + filp->f_pos++; + } + spin_unlock(&dcache_lock); + sysfs_close_attr_files(dentry); + } + return 0; +} + +loff_t sysfs_dir_lseek(struct file *file, loff_t offset, int origin) +{ + int err = 0; + + down(&file->f_dentry->d_inode->i_sem); + switch (origin) { + case 1: + offset += file->f_pos; + case 0: + if (offset >= 0) + break; + default: + up(&file->f_dentry->d_inode->i_sem); + return -EINVAL; + } + if (offset != file->f_pos) { + file->f_pos = offset; + if (file->f_pos >= 2) { + struct list_head *p; + struct dentry *cursor = file->private_data; + loff_t n = file->f_pos - 2; + + if ((err = sysfs_open_attr_files(file->f_dentry))) { + offset = err; + goto exit; + } + + spin_lock(&dcache_lock); + list_del(&cursor->d_child); + p = file->f_dentry->d_subdirs.next; + while (n && p != &file->f_dentry->d_subdirs) { + struct dentry *next; + next = list_entry(p, struct dentry, d_child); + if (!d_unhashed(next) && next->d_inode) + n--; + p = p->next; + } + list_add_tail(&cursor->d_child, p); + spin_unlock(&dcache_lock); + sysfs_close_attr_files(file->f_dentry); + } + } +exit: + up(&file->f_dentry->d_inode->i_sem); + return offset; +} EXPORT_SYMBOL(sysfs_create_dir); EXPORT_SYMBOL(sysfs_remove_dir); EXPORT_SYMBOL(sysfs_rename_dir); _ -- Maneesh Soni Linux Technology Center, IBM Software Lab, Bangalore, India email: maneesh@in.ibm.com Phone: 91-80-5044999 Fax: 91-80-5268553 T/L : 9243696 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC 3/5] sysfs backing store (leaves only) - sysfs-leaves-file.patch 2003-12-29 9:34 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni @ 2003-12-29 9:35 ` Maneesh Soni 2003-12-29 9:35 ` [RFC 4/5] sysfs backing store (leaves only) - sysfs-leaves-bin.patch Maneesh Soni 2004-01-02 6:45 ` [RFC 2/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni 1 sibling, 1 reply; 7+ messages in thread From: Maneesh Soni @ 2003-12-29 9:35 UTC (permalink / raw) To: LKML; +Cc: Greg KH, Al Viro, Patrick Mochel, Dipankar Sarma 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 | 19 +++++++++++++--- 2 files changed, 45 insertions(+), 37 deletions(-) diff -puN fs/sysfs/file.c~sysfs-leaves-file fs/sysfs/file.c --- linux-2.6.0/fs/sysfs/file.c~sysfs-leaves-file 2003-12-29 12:43:44.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/file.c 2003-12-29 12:43:44.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 = kobject_get(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, int t, const struct attribute * attr) { - 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, SYSFS_KOBJ_ATTR, attr); return -EINVAL; } diff -puN fs/sysfs/inode.c~sysfs-leaves-file fs/sysfs/inode.c --- linux-2.6.0/fs/sysfs/inode.c~sysfs-leaves-file 2003-12-29 12:43:44.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/inode.c 2003-12-29 12:43:44.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_ISREG(inode->i_mode)) + dget(dentry); /* pin non-leaf dentry in core */ } else iput(inode); Done: @@ -96,14 +99,24 @@ 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_ISREG(victim->d_inode->i_mode)) { + spin_lock(&dcache_lock); + spin_lock(&victim->d_lock); + __d_drop(victim); + spin_unlock(&dcache_lock); + spin_unlock(&victim->d_lock); + } + else { + d_delete(victim); + simple_unlink(dir->d_inode,victim); + } } /* * Drop reference from sysfs_get_dentry() above. */ dput(victim); } + sysfs_remove_attr_dirent(dir->d_fsdata, name); up(&dir->d_inode->i_sem); } _ -- Maneesh Soni Linux Technology Center, IBM Software Lab, Bangalore, India email: maneesh@in.ibm.com Phone: 91-80-5044999 Fax: 91-80-5268553 T/L : 9243696 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC 4/5] sysfs backing store (leaves only) - sysfs-leaves-bin.patch 2003-12-29 9:35 ` [RFC 3/5] sysfs backing store (leaves only) - sysfs-leaves-file.patch Maneesh Soni @ 2003-12-29 9:35 ` Maneesh Soni 2003-12-29 9:36 ` [RFC 5/5] sysfs backing store (leaves only) - sysfs-leaves-misc.patch Maneesh Soni 0 siblings, 1 reply; 7+ messages in thread From: Maneesh Soni @ 2003-12-29 9:35 UTC (permalink / raw) To: LKML; +Cc: Greg KH, Al Viro, Patrick Mochel, Dipankar Sarma fs/sysfs/bin.c | 53 ++++++++++++++++++++--------------------------------- 1 files changed, 20 insertions(+), 33 deletions(-) diff -puN fs/sysfs/bin.c~sysfs-leaves-bin fs/sysfs/bin.c --- linux-2.6.0/fs/sysfs/bin.c~sysfs-leaves-bin 2003-12-29 12:28:56.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/bin.c 2003-12-29 12:29:00.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,9 @@ 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 +139,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 +156,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, SYSFS_KOBJ_BIN_ATTR, + &attr->attr); + return -EINVAL; } _ -- Maneesh Soni Linux Technology Center, IBM Software Lab, Bangalore, India email: maneesh@in.ibm.com Phone: 91-80-5044999 Fax: 91-80-5268553 T/L : 9243696 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC 5/5] sysfs backing store (leaves only) - sysfs-leaves-misc.patch 2003-12-29 9:35 ` [RFC 4/5] sysfs backing store (leaves only) - sysfs-leaves-bin.patch Maneesh Soni @ 2003-12-29 9:36 ` Maneesh Soni 0 siblings, 0 replies; 7+ messages in thread From: Maneesh Soni @ 2003-12-29 9:36 UTC (permalink / raw) To: LKML; +Cc: Greg KH, Al Viro, Patrick Mochel, Dipankar Sarma o This patch has the changes require for attribute groups and symlinks, and misc. routines. fs/sysfs/group.c | 2 - fs/sysfs/symlink.c | 2 + fs/sysfs/sysfs.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff -puN fs/sysfs/symlink.c~sysfs-leaves-misc fs/sysfs/symlink.c --- linux-2.6.0/fs/sysfs/symlink.c~sysfs-leaves-misc 2003-12-29 14:39:03.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/symlink.c 2003-12-29 14:39:36.000000000 +0530 @@ -25,6 +25,8 @@ static int sysfs_symlink(struct inode * error = page_symlink(dentry->d_inode, symname, l); if (error) iput(dentry->d_inode); + else + d_rehash(dentry); } return error; } diff -puN fs/sysfs/group.c~sysfs-leaves-misc fs/sysfs/group.c --- linux-2.6.0/fs/sysfs/group.c~sysfs-leaves-misc 2003-12-29 14:39:03.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/group.c 2003-12-29 14:39:03.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, SYSFS_KOBJ_ATTR, * attr); } if (error) remove_files(dir,grp); diff -puN fs/sysfs/sysfs.h~sysfs-leaves-misc fs/sysfs/sysfs.h --- linux-2.6.0/fs/sysfs/sysfs.h~sysfs-leaves-misc 2003-12-29 14:39:03.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/sysfs.h 2003-12-29 14:39:03.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,75 @@ 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 *, int, const struct attribute *); 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 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)) + kfree(sd); +} + +static inline +void sysfs_remove_attr_dirent(struct sysfs_dirent * sd, const char * name) +{ + struct list_head * tmp; + + tmp = sd->s_children.next; + while (tmp != & sd->s_children) { + struct sysfs_dirent * tmp_sd; + tmp_sd = list_entry(tmp, struct sysfs_dirent, s_sibling); + tmp = tmp->next; + if ((tmp_sd->s_type == SYSFS_KOBJ_ATTR) || + (tmp_sd->s_type == SYSFS_KOBJ_BIN_ATTR)) { + if (!strcmp(sysfs_get_name(tmp_sd), name)) { + list_del_init(&tmp_sd->s_sibling); + sysfs_put(tmp_sd); + } + } + } +} + _ -- Maneesh Soni Linux Technology Center, IBM Software Lab, Bangalore, India email: maneesh@in.ibm.com Phone: 91-80-5044999 Fax: 91-80-5268553 T/L : 9243696 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC 2/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch 2003-12-29 9:34 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni 2003-12-29 9:35 ` [RFC 3/5] sysfs backing store (leaves only) - sysfs-leaves-file.patch Maneesh Soni @ 2004-01-02 6:45 ` Maneesh Soni 1 sibling, 0 replies; 7+ messages in thread From: Maneesh Soni @ 2004-01-02 6:45 UTC (permalink / raw) To: LKML Cc: Greg KH, Al Viro, Patrick Mochel, Dipankar Sarma, Martin J. Bligh, Christian Borntraeger Please use this patch instead, there was a typo in the previous one in remove_dir(). Also corrected the subject line. It is 2/5. Thanks Maneesh o This patch provides the inode operations ->lookup(), ->readdir() and ->llseek() for sysfs directories. o while sysfs_create_dir() we attach a sysfs_dirent structure to the d_fsdata filed of dentry corresponding to the kobject's direcotry. o sysfs_lookup does not hash the dentry and we hash the dentry when we have attached the sysfs_dirent to it. This was done to cover up a race when we attach a negative dentry and instantiate it before updating the d_fsdata field. As after instantiating we can get a successfull lookup for the dentry but a NULL d_fsdata field. As a result we do not create negative dentries. o sysfs_readdir() or sysfs_dir_lseek() will bring in the dentries corresponding to the attribute files if the offset is more than 2. These are released when we are done with filldir(). o sysfs_d_iput() releases the ref. to the sysfs_dirent() which was taken at the time of dentry allocation. fs/sysfs/dir.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 292 insertions(+), 9 deletions(-) diff -puN fs/sysfs/dir.c~sysfs-leaves-dir fs/sysfs/dir.c --- linux-2.6.0/fs/sysfs/dir.c~sysfs-leaves-dir 2003-12-29 12:30:50.000000000 +0530 +++ linux-2.6.0-maneesh/fs/sysfs/dir.c 2003-12-29 12:30:56.000000000 +0530 @@ -10,10 +10,54 @@ #include <linux/kobject.h> #include "sysfs.h" +struct inode_operations sysfs_dir_inode_operations = { + .lookup = sysfs_lookup, +}; + +struct file_operations sysfs_dir_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .llseek = sysfs_dir_lseek, + .read = generic_read_dir, + .readdir = sysfs_readdir, +}; + +/* dentry iput only for sysfs leaf dentries */ +static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) +{ + struct sysfs_dirent * sd = dentry->d_fsdata; + + if (sd) + sysfs_put(sd); + iput(inode); +} + + +static struct dentry_operations sysfs_dentry_ops = { + .d_iput = sysfs_d_iput, +}; + +char * sysfs_get_name(struct sysfs_dirent *sd) +{ + if (!sd || !sd->s_element) + BUG(); + + return (sd->s_type & SYSFS_KOBJ_ATTR) ? + ((struct attribute *)(sd->s_element))->name : + ((struct bin_attribute *)(sd->s_element))->attr.name; +} + +static int init_file(struct inode * inode) +{ + inode->i_size = PAGE_SIZE; + inode->i_fop = &sysfs_file_operations; + return 0; +} + static int init_dir(struct inode * inode) { - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; @@ -21,6 +65,61 @@ static int init_dir(struct inode * inode } +/* attaches attribute's sysfs_dirent to the dentry corresponding to the + * attribute file + */ +int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) +{ + struct attribute * attr = NULL; + struct bin_attribute * bin_attr = NULL; + int (* init) (struct inode *) = NULL; + int error = 0; + + if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { + bin_attr = sd->s_element; + attr = &bin_attr->attr; + } else { + attr = sd->s_element; + init = init_file; + } + + error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init); + if (error) + return error; + + if (bin_attr) { + dentry->d_inode->i_size = bin_attr->size; + dentry->d_inode->i_fop = &bin_fops; + } + dentry->d_op = &sysfs_dentry_ops; + dentry->d_fsdata = sysfs_get(sd); + sd->s_dentry = dentry; + d_rehash(dentry); + + return 0; +} + +struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata; + struct sysfs_dirent * sd; + int err = 0; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if ((sd->s_type == SYSFS_KOBJ_ATTR) + || (sd->s_type == SYSFS_KOBJ_BIN_ATTR)) { + char * name = sysfs_get_name(sd); + if (strcmp(name, dentry->d_name.name)) + continue; + err = sysfs_attach_attr(sd, dentry); + break; + } + } + + return ERR_PTR(err); +} + static int create_dir(struct kobject * k, struct dentry * p, const char * n, struct dentry ** d) { @@ -29,12 +128,23 @@ static int create_dir(struct kobject * k down(&p->d_inode->i_sem); *d = sysfs_get_dentry(p,n); if (!IS_ERR(*d)) { - error = sysfs_create(*d, + error = sysfs_create(*d, S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO, init_dir); if (!error) { - (*d)->d_fsdata = k; - p->d_inode->i_nlink++; + struct sysfs_dirent * sd, * parent_sd; + parent_sd = p->d_fsdata; + sd = sysfs_new_dirent(parent_sd, k, + (parent_sd->s_element == k) ? + SYSFS_KOBJ_ATTR_GROUP : + SYSFS_KOBJECT); + if (sd) { + (*d)->d_fsdata = sysfs_get(sd); + (*d)->d_op = &sysfs_dentry_ops; + p->d_inode->i_nlink++; + d_rehash(*d); + } else + error = -ENOMEM; } dput(*d); } else @@ -81,9 +191,15 @@ int sysfs_create_dir(struct kobject * ko static void remove_dir(struct dentry * d) { struct dentry * parent = dget(d->d_parent); + struct sysfs_dirent * sd; + down(&parent->d_inode->i_sem); d_delete(d); - simple_rmdir(parent->d_inode,d); + sd = d->d_fsdata; + list_del_init(&sd->s_sibling); + sysfs_put(d->d_fsdata); + if (d->d_inode) + simple_rmdir(parent->d_inode,d); pr_debug(" o %s removing done (%d)\n",d->d_name.name, atomic_read(&d->d_count)); @@ -133,9 +249,18 @@ void sysfs_remove_dir(struct kobject * k * Unlink and unhash. */ spin_unlock(&dcache_lock); - d_delete(d); - simple_unlink(dentry->d_inode,d); - dput(d); + if (S_ISREG(d->d_inode->i_mode)) { + struct sysfs_dirent * sd = d->d_fsdata; + + list_del_init(&sd->s_sibling); + sysfs_put(sd); + d_drop(d); + simple_unlink(dentry->d_inode,d); + } else { + d_delete(d); + simple_unlink(dentry->d_inode,d); + dput(d); + } spin_lock(&dcache_lock); } pr_debug(" done\n"); @@ -172,6 +297,164 @@ void sysfs_rename_dir(struct kobject * k up(&parent->d_inode->i_sem); } +/* called under parent inode's i_sem (taken in vfs_readdir */ +static void sysfs_close_attr_files(struct dentry * parent) +{ + struct sysfs_dirent * parent_sd = parent->d_fsdata; + struct sysfs_dirent * sd; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if ((sd->s_type == SYSFS_KOBJ_ATTR) || + (sd->s_type == SYSFS_KOBJ_BIN_ATTR)) { + struct dentry * dentry = sd->s_dentry; + if (dentry && dentry->d_inode) + dput(dentry); + } + } +} + +/* called under parent inode's i_sem (taken in vfs_readdir */ +static int sysfs_open_attr_files(struct dentry * parent) +{ + struct sysfs_dirent * parent_sd = parent->d_fsdata; + struct sysfs_dirent * sd; + struct dentry * dentry; + int error = 0; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if ((sd->s_type == SYSFS_KOBJ_ATTR) || + (sd->s_type == SYSFS_KOBJ_BIN_ATTR)) { + char * name = sysfs_get_name(sd); + dentry = sysfs_get_dentry(parent, name); + if (IS_ERR(dentry)) + error = PTR_ERR(dentry); + if (!dentry->d_inode) + error = sysfs_attach_attr(sd, dentry); + if (error) + break; + } + } + if (error) { + /* release all successfully opened entires so far*/ + sysfs_close_attr_files(parent); + } + + return error; +} + +/* Relationship between i_mode and the DT_xxx types */ +static inline unsigned char dt_type(struct inode *inode) +{ + return (inode->i_mode >> 12) & 15; +} + +/* + * Directory is locked and all positive dentries in it are safe, since + * for ramfs-type trees they can't go away without unlink() or rmdir(), + * both impossible due to the lock on directory. + */ + +int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_dentry; + struct dentry *cursor = filp->private_data; + struct list_head *p, *q = &cursor->d_child; + ino_t ino; + int i = filp->f_pos; + int err = 0; + + switch (i) { + case 0: + ino = dentry->d_inode->i_ino; + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) + break; + filp->f_pos++; + i++; + /* fallthrough */ + case 1: + ino = parent_ino(dentry); + if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) + break; + filp->f_pos++; + i++; + /* fallthrough */ + default: + if ((err = sysfs_open_attr_files(dentry))) + return err; + + spin_lock(&dcache_lock); + if (filp->f_pos == 2) { + list_del(q); + list_add(q, &dentry->d_subdirs); + } + for (p=q->next; p != &dentry->d_subdirs; p=p->next) { + struct dentry *next; + next = list_entry(p, struct dentry, d_child); + if (d_unhashed(next) || !next->d_inode) + continue; + + spin_unlock(&dcache_lock); + if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, dt_type(next->d_inode)) < 0) + return 0; + spin_lock(&dcache_lock); + /* next is still alive */ + list_del(q); + list_add(q, p); + p = q; + filp->f_pos++; + } + spin_unlock(&dcache_lock); + sysfs_close_attr_files(dentry); + } + return 0; +} + +loff_t sysfs_dir_lseek(struct file *file, loff_t offset, int origin) +{ + int err = 0; + + down(&file->f_dentry->d_inode->i_sem); + switch (origin) { + case 1: + offset += file->f_pos; + case 0: + if (offset >= 0) + break; + default: + up(&file->f_dentry->d_inode->i_sem); + return -EINVAL; + } + if (offset != file->f_pos) { + file->f_pos = offset; + if (file->f_pos >= 2) { + struct list_head *p; + struct dentry *cursor = file->private_data; + loff_t n = file->f_pos - 2; + + if ((err = sysfs_open_attr_files(file->f_dentry))) { + offset = err; + goto exit; + } + + spin_lock(&dcache_lock); + list_del(&cursor->d_child); + p = file->f_dentry->d_subdirs.next; + while (n && p != &file->f_dentry->d_subdirs) { + struct dentry *next; + next = list_entry(p, struct dentry, d_child); + if (!d_unhashed(next) && next->d_inode) + n--; + p = p->next; + } + list_add_tail(&cursor->d_child, p); + spin_unlock(&dcache_lock); + sysfs_close_attr_files(file->f_dentry); + } + } +exit: + up(&file->f_dentry->d_inode->i_sem); + return offset; +} EXPORT_SYMBOL(sysfs_create_dir); EXPORT_SYMBOL(sysfs_remove_dir); EXPORT_SYMBOL(sysfs_rename_dir); _ -- Maneesh Soni Linux Technology Center, IBM Software Lab, Bangalore, India email: maneesh@in.ibm.com Phone: 91-80-5044999 Fax: 91-80-5268553 T/L : 9243696 ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2004-01-02 6:46 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2003-12-29 9:32 [RFC 0/5] sysfs backing store (leaves only) Maneesh Soni 2003-12-29 9:33 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-mount.patch Maneesh Soni 2003-12-29 9:34 ` [RFC 1/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni 2003-12-29 9:35 ` [RFC 3/5] sysfs backing store (leaves only) - sysfs-leaves-file.patch Maneesh Soni 2003-12-29 9:35 ` [RFC 4/5] sysfs backing store (leaves only) - sysfs-leaves-bin.patch Maneesh Soni 2003-12-29 9:36 ` [RFC 5/5] sysfs backing store (leaves only) - sysfs-leaves-misc.patch Maneesh Soni 2004-01-02 6:45 ` [RFC 2/5] sysfs backing store (leaves only) - sysfs-leaves-dir.patch Maneesh Soni
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox