From: Maneesh Soni o This the first patch in the series of patches implementing sysfs backing store. It introduces the new sysfs_dirent data structure. The sysfs_dirent is added to each of the element which can be represented in sysfs like, kobject (directory), text or binary attributes (files), attribute groups (directory) and symlinks. o It uses dentry's d_fsdata field to attach the corresponding sysfs_dirent. o The sysfs_dirents are maintained in a tree of all the sysfs entries using the s_children and s_sibling list pointers. o The patch just adds the sysfs_dirent structure and hence should not be used independently but system can boot with it. Signed-off-by: Andrew Morton --- 25-akpm/fs/sysfs/bin.c | 17 +++++++++--- 25-akpm/fs/sysfs/dir.c | 27 ++++++++++++++++---- 25-akpm/fs/sysfs/file.c | 25 ++++++++++++++---- 25-akpm/fs/sysfs/group.c | 2 - 25-akpm/fs/sysfs/mount.c | 8 ++++++ 25-akpm/fs/sysfs/symlink.c | 56 ++++++++++++++++++++++++++++++++++++++---- 25-akpm/fs/sysfs/sysfs.h | 37 +++++++++++++++++++++++++-- 25-akpm/include/linux/sysfs.h | 21 +++++++++++++++ 8 files changed, 169 insertions(+), 24 deletions(-) diff -puN fs/sysfs/bin.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry fs/sysfs/bin.c --- 25/fs/sysfs/bin.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/fs/sysfs/bin.c Thu Jul 29 13:59:42 2004 @@ -160,6 +160,8 @@ int sysfs_create_bin_file(struct kobject { struct dentry * dentry; struct dentry * parent; + struct sysfs_dirent * sd; + umode_t mode = (attr->attr.mode & S_IALLUGO) | S_IFREG; int error = 0; if (!kobj || !attr) @@ -170,13 +172,20 @@ int sysfs_create_bin_file(struct kobject 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); + error = sysfs_create(dentry, mode, NULL); if (!error) { dentry->d_inode->i_size = attr->size; dentry->d_inode->i_fop = &bin_fops; + sd = sysfs_new_dirent(parent->d_fsdata, (void *) attr, + SYSFS_KOBJ_BIN_ATTR); + if (!sd) { + sd->s_mode = mode; + dentry->d_fsdata = sd; + sd->s_dentry = dentry; + } else { + dput(dentry); + error = -ENOMEM; + } } dput(dentry); } else diff -puN fs/sysfs/dir.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry fs/sysfs/dir.c --- 25/fs/sysfs/dir.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/fs/sysfs/dir.c Thu Jul 29 13:59:41 2004 @@ -27,17 +27,34 @@ static int create_dir(struct kobject * k const char * n, struct dentry ** d) { int error; + umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; down(&p->d_inode->i_sem); *d = sysfs_get_dentry(p,n); if (!IS_ERR(*d)) { - error = sysfs_create(*d, - S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO, - init_dir); + error = sysfs_create(*d, mode, 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 = sd; + p->d_inode->i_nlink++; + sd->s_dentry = *d; + sd->s_mode = mode; + } else { + /* error, release the ref taken in + * sysfs_create() + */ + dput(*d); + error = -ENOMEM; + } } + /* This will free the dentry in case of error + */ dput(*d); } else error = PTR_ERR(*d); diff -puN fs/sysfs/file.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry fs/sysfs/file.c --- 25/fs/sysfs/file.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/fs/sysfs/file.c Thu Jul 29 13:59:41 2004 @@ -346,10 +346,12 @@ static struct file_operations sysfs_file }; -int sysfs_add_file(struct dentry * dir, const struct attribute * attr) +int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) { struct dentry * dentry; - int error; + struct sysfs_dirent * sd; + struct sysfs_dirent * parent_sd = dir->d_fsdata; + int error = 0; down(&dir->d_inode->i_sem); dentry = sysfs_get_dentry(dir,attr->name); @@ -357,8 +359,18 @@ int sysfs_add_file(struct dentry * dir, error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init_file); - if (!error) - dentry->d_fsdata = (void *)attr; + if (!error) { + + sd = sysfs_new_dirent(parent_sd, (void *) attr, type); + if (sd) { + sd->s_mode = (attr->mode & S_IALLUGO) | S_IFREG; + dentry->d_fsdata = sd; + sd->s_dentry = dentry; + } else { + dput(dentry); + error = -ENOMEM; + } + } dput(dentry); } else error = PTR_ERR(dentry); @@ -375,8 +387,9 @@ 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/group.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry fs/sysfs/group.c --- 25/fs/sysfs/group.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/fs/sysfs/group.c Thu Jul 29 13:59:42 2004 @@ -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/mount.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry fs/sysfs/mount.c --- 25/fs/sysfs/mount.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/fs/sysfs/mount.c Thu Jul 29 13:59:42 2004 @@ -22,6 +22,13 @@ static struct super_operations sysfs_ops .drop_inode = generic_delete_inode, }; +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) { struct inode *inode; @@ -50,6 +57,7 @@ static int sysfs_fill_super(struct super iput(inode); return -ENOMEM; } + root->d_fsdata = &sysfs_root; sb->s_root = root; return 0; } diff -puN fs/sysfs/symlink.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry fs/sysfs/symlink.c --- 25/fs/sysfs/symlink.c~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/fs/sysfs/symlink.c Thu Jul 29 13:59:42 2004 @@ -53,6 +53,39 @@ static void fill_object_path(struct kobj } } +static struct sysfs_dirent * +sysfs_add_link(struct sysfs_dirent * parent_sd, char * name, + struct kobject * target) +{ + struct sysfs_dirent * sd; + struct sysfs_symlink * sl; + int error = 0; + + error = -ENOMEM; + sl = kmalloc(sizeof(*sl), GFP_KERNEL); + if (!sl) + goto exit1; + + sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); + if (!sl->link_name) + goto exit2; + + strcpy(sl->link_name, name); + sl->target_kobj = kobject_get(target); + + sd = sysfs_new_dirent(parent_sd, sl, SYSFS_KOBJ_LINK); + if (sd) { + sd->s_mode = S_IFLNK|S_IRWXUGO; + return sd; + } + + kfree(sl->link_name); +exit2: + kfree(sl); +exit1: + return ERR_PTR(error); +} + /** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. @@ -65,15 +98,28 @@ int sysfs_create_link(struct kobject * k struct dentry * d; int error = 0; + if (!name) + return -EINVAL; + down(&dentry->d_inode->i_sem); d = sysfs_get_dentry(dentry,name); if (!IS_ERR(d)) { error = sysfs_create(d, S_IFLNK|S_IRWXUGO, init_symlink); - if (!error) - /* - * associate the link dentry with the target kobject - */ - d->d_fsdata = kobject_get(target); + if (!error) { + struct sysfs_dirent * sd; + sd = sysfs_add_link(dentry->d_fsdata, name, target); + if (!IS_ERR(sd)) { + /* + * associate the link dentry with the target + * through the corresponding sysfs_dirent. + */ + d->d_fsdata = sd; + sd->s_dentry = dentry; + } else { + dput(d); + error = PTR_ERR(sd); + } + } dput(d); } else error = PTR_ERR(d); diff -puN fs/sysfs/sysfs.h~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry fs/sysfs/sysfs.h --- 25/fs/sysfs/sysfs.h~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/fs/sysfs/sysfs.h Thu Jul 29 13:59:42 2004 @@ -5,8 +5,9 @@ extern struct inode * sysfs_new_inode(mo extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); extern struct dentry * sysfs_get_dentry(struct dentry *, const char *); +extern void sysfs_drop_dentry(struct sysfs_dirent *, struct dentry *); -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 **); @@ -16,14 +17,44 @@ extern int sysfs_readlink(struct dentry extern int sysfs_follow_link(struct dentry *, struct nameidata *); extern struct rw_semaphore sysfs_rename_sem; +struct sysfs_symlink { + char * link_name; + struct kobject * target_kobj; +}; + static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) { struct kobject * kobj = NULL; spin_lock(&dcache_lock); - if (!d_unhashed(dentry)) - kobj = kobject_get(dentry->d_fsdata); + if (!d_unhashed(dentry)) { + struct sysfs_dirent * sd = dentry->d_fsdata; + if (sd->s_type & SYSFS_KOBJ_LINK) { + struct sysfs_symlink * sl = sd->s_element; + kobj = kobject_get(sl->target_kobj); + } else + kobj = kobject_get(sd->s_element); + } spin_unlock(&dcache_lock); return kobj; } + +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; +} diff -puN include/linux/sysfs.h~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry include/linux/sysfs.h --- 25/include/linux/sysfs.h~sysfs-backing-store-add-sysfs_dirent-to-sysfs-dentry Thu Jul 29 13:59:41 2004 +++ 25-akpm/include/linux/sysfs.h Thu Jul 29 13:59:41 2004 @@ -9,6 +9,8 @@ #ifndef _SYSFS_H_ #define _SYSFS_H_ +#include + struct kobject; struct module; @@ -57,6 +59,25 @@ 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; + umode_t s_mode; + 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) + + #ifdef CONFIG_SYSFS extern int _