diff options
| author | Patrick Mochel <mochel@osdl.org> | 2002-10-29 02:26:01 -0800 |
|---|---|---|
| committer | Patrick Mochel <mochel@osdl.org> | 2002-10-29 02:26:01 -0800 |
| commit | eda520259938bd3299486f12ab5058c29dcd5326 (patch) | |
| tree | 781df420f8c878a106b3dcb4e305f1a938454d7e | |
| parent | c637d6b116ae3cc7e143b99ea47f29d106c97d09 (diff) | |
sysfs: marry api with struct kobject.
This works on obviating the need for a separate data type to describe a sysfs
directory (which was renamed from struct driver_dir_entry to struct sysfs_dir).
All sysfs creation and removal functions now take a struct kobject, instead
of a struct sysfs_dir. This kobject is embedded in ->d_fsdata of the directory.
sysfs_create_dir() takes only 1 parameter now: the object that we're creating
the directory for. The parent dentry is derived by looking at the object's
parent.
sysfs_create_file() takes the object as the first parameter, and the attribute
as the second, which makes more sense from an API perspective.
sysfs_remove_file() now takes an attribute as a second parameter, to be
consistent with the creation function.
sysfs_remove_link() is created, which is basically the old sysfs_remove_file().
(symlinks don't have an attribute associated with them; only a name, which was
prohibiting the previous change).
open() and close() look for a kobject now, and do refcounting directly on the
object. Because of that, we don't need the ->open() and ->close() callbacks
in struct sysfs_ops, so they've been removed.
read() and write() also now look for a kobject now.
The comments have been updated, too.
| -rw-r--r-- | fs/sysfs/inode.c | 278 | ||||
| -rw-r--r-- | include/linux/kobject.h | 1 | ||||
| -rw-r--r-- | include/linux/sysfs.h | 26 |
3 files changed, 167 insertions, 138 deletions
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 926b37af571c..804f3655dc73 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -33,7 +33,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/backing-dev.h> -#include <linux/sysfs.h> +#include <linux/kobject.h> #include <asm/uaccess.h> @@ -154,30 +154,35 @@ static int sysfs_unlink(struct inode *dir, struct dentry *dentry) } /** - * sysfs_read_file - "read" data from a file. - * @file: file pointer - * @buf: buffer to fill - * @count: number of bytes to read - * @ppos: starting offset in file + * sysfs_read_file - read an attribute. + * @file: file pointer. + * @buf: buffer to fill. + * @count: number of bytes to read. + * @ppos: starting offset in file. * - * Userspace wants data from a file. It is up to the creator of the file to - * provide that data. - * There is a struct device_attribute embedded in file->private_data. We - * obtain that and check if the read callback is implemented. If so, we call - * it, passing the data field of the file entry. - * Said callback is responsible for filling the buffer and returning the number - * of bytes it put in it. We update @ppos correctly. + * Userspace wants to read an attribute file. The attribute descriptor + * is in the file's ->d_fsdata. The target object is in the directory's + * ->d_fsdata. + * + * We allocate a %PAGE_SIZE buffer, and pass it to the object's ->show() + * method (along with the object). We loop doing this until @count is + * satisfied, or ->show() returns %0. */ + static ssize_t sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) { struct attribute * attr = file->f_dentry->d_fsdata; - struct driver_dir_entry * dir; + struct sysfs_ops * ops = NULL; + struct kobject * kobj; unsigned char *page; ssize_t retval = 0; - dir = file->f_dentry->d_parent->d_fsdata; - if (!dir->ops->show) + kobj = file->f_dentry->d_parent->d_fsdata; + if (kobj) + ops = kobj->dir.ops; + + if (!ops || !ops->show) return 0; if (count > PAGE_SIZE) @@ -190,7 +195,7 @@ sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) while (count > 0) { ssize_t len; - len = dir->ops->show(dir,attr,page,count,*ppos); + len = ops->show(kobj,attr,page,count,*ppos); if (len <= 0) { if (len < 0) @@ -214,27 +219,32 @@ sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) } /** - * sysfs_write_file - "write" to a file - * @file: file pointer - * @buf: data to write - * @count: number of bytes - * @ppos: starting offset + * sysfs_write_file - write an attribute. + * @file: file pointer + * @buf: data to write + * @count: number of bytes + * @ppos: starting offset * - * Similarly to sysfs_read_file, we act essentially as a bit pipe. - * We check for a "write" callback in file->private_data, and pass - * @buffer, @count, @ppos, and the file entry's data to the callback. - * The number of bytes written is returned, and we handle updating - * @ppos properly. + * Identical to sysfs_read_file(), though going the opposite direction. + * We allocate a %PAGE_SIZE buffer and copy in the userspace buffer. We + * pass that to the object's ->store() method until we reach @count or + * ->store() returns %0 or less. */ + static ssize_t sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct attribute * attr = file->f_dentry->d_fsdata; - struct driver_dir_entry * dir; + struct sysfs_ops * ops = NULL; + struct kobject * kobj; ssize_t retval = 0; char * page; - dir = file->f_dentry->d_parent->d_fsdata; + kobj = file->f_dentry->d_parent->d_fsdata; + if (kobj) + ops = kobj->dir.ops; + if (!ops || !ops->store) + return 0; page = (char *)__get_free_page(GFP_KERNEL); if (!page) @@ -249,7 +259,7 @@ sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) while (count > 0) { ssize_t len; - len = dir->ops->store(dir,attr,page + retval,count,*ppos); + len = ops->store(kobj,attr,page + retval,count,*ppos); if (len <= 0) { if (len < 0) @@ -268,29 +278,24 @@ sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) static int sysfs_open_file(struct inode * inode, struct file * filp) { - struct driver_dir_entry * dir; + struct kobject * kobj; int error = 0; - dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata; - if (dir) { + kobj = filp->f_dentry->d_parent->d_fsdata; + if ((kobj = kobject_get(kobj))) { struct attribute * attr = filp->f_dentry->d_fsdata; - if (attr && dir->ops) { - if (dir->ops->open) - error = dir->ops->open(dir); - goto Done; - } - } - error = -EINVAL; - Done: + if (!attr) + error = -EINVAL; + } else + error = -EINVAL; return error; } static int sysfs_release(struct inode * inode, struct file * filp) { - struct driver_dir_entry * dir; - dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata; - if (dir->ops->close) - dir->ops->close(dir); + struct kobject * kobj = filp->f_dentry->d_parent->d_fsdata; + if (kobj) + kobject_put(kobj); return 0; } @@ -371,6 +376,7 @@ static int __init sysfs_init(void) core_initcall(sysfs_init); + static struct dentry * get_dentry(struct dentry * parent, const char * name) { struct qstr qstr; @@ -381,137 +387,160 @@ static struct dentry * get_dentry(struct dentry * parent, const char * name) return lookup_hash(&qstr,parent); } + /** - * sysfs_create_dir - create a directory in the filesystem - * @entry: directory entry - * @parent: parent directory entry + * sysfs_create_dir - create a directory for an object. + * @parent: parent parent object. + * @kobj: object we're creating directory for. */ -int -sysfs_create_dir(struct driver_dir_entry * entry, - struct driver_dir_entry * parent) + +int sysfs_create_dir(struct kobject * kobj) { struct dentry * dentry = NULL; - struct dentry * parent_dentry; + struct dentry * parent; int error = 0; - if (!entry) + if (!kobj) return -EINVAL; - parent_dentry = parent ? parent->dentry : NULL; - - if (!parent_dentry) - if (sysfs_mount && sysfs_mount->mnt_sb) - parent_dentry = sysfs_mount->mnt_sb->s_root; - - if (!parent_dentry) + if (kobj->parent) + parent = kobj->parent->dir.dentry; + else if (sysfs_mount && sysfs_mount->mnt_sb) + parent = sysfs_mount->mnt_sb->s_root; + else return -EFAULT; - down(&parent_dentry->d_inode->i_sem); - dentry = get_dentry(parent_dentry,entry->name); + down(&parent->d_inode->i_sem); + dentry = get_dentry(parent,kobj->name); if (!IS_ERR(dentry)) { - dentry->d_fsdata = (void *) entry; - entry->dentry = dentry; - error = sysfs_mkdir(parent_dentry->d_inode,dentry,entry->mode); + dentry->d_fsdata = (void *)kobj; + kobj->dir.dentry = dentry; + error = sysfs_mkdir(parent->d_inode,dentry, + (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO)); } else error = PTR_ERR(dentry); - up(&parent_dentry->d_inode->i_sem); + up(&parent->d_inode->i_sem); return error; } + /** - * sysfs_create_file - create a file - * @entry: structure describing the file - * @parent: directory to create it in + * sysfs_create_file - create an attribute file for an object. + * @kobj: object we're creating for. + * @attr: atrribute descriptor. */ -int -sysfs_create_file(struct attribute * entry, - struct driver_dir_entry * parent) + +int sysfs_create_file(struct kobject * kobj, struct attribute * attr) { struct dentry * dentry; + struct dentry * parent; int error = 0; - if (!entry || !parent) + if (!kobj || !attr) return -EINVAL; - if (!parent->dentry) - return -EINVAL; + if (kobj->parent) + parent = kobj->parent->dir.dentry; + else + return -ENOENT; - down(&parent->dentry->d_inode->i_sem); - dentry = get_dentry(parent->dentry,entry->name); + down(&parent->d_inode->i_sem); + dentry = get_dentry(parent,attr->name); if (!IS_ERR(dentry)) { - dentry->d_fsdata = (void *)entry; - error = sysfs_create(parent->dentry->d_inode,dentry,entry->mode); + dentry->d_fsdata = (void *)attr; + error = sysfs_create(parent->d_inode,dentry,attr->mode); } else error = PTR_ERR(dentry); - up(&parent->dentry->d_inode->i_sem); + up(&parent->d_inode->i_sem); return error; } + /** - * sysfs_create_symlink - make a symlink - * @parent: directory we're creating in - * @entry: entry describing link - * @target: place we're symlinking to - * + * sysfs_create_symlink - make a symlink + * @kobj: object who's directory we're creating in. + * @name: name of the symlink. + * @target: path we're pointing to. */ -int sysfs_create_symlink(struct driver_dir_entry * parent, - char * name, char * target) + +int sysfs_create_link(struct kobject * kobj, char * name, char * target) { struct dentry * dentry; - int error = 0; + int error; - if (!parent || !parent->dentry) - return -EINVAL; + if (kobj) { + struct dentry * parent = kobj->dir.dentry; - down(&parent->dentry->d_inode->i_sem); - dentry = get_dentry(parent->dentry,name); - if (!IS_ERR(dentry)) - error = sysfs_symlink(parent->dentry->d_inode,dentry,target); - else - error = PTR_ERR(dentry); - up(&parent->dentry->d_inode->i_sem); + down(&parent->d_inode->i_sem); + dentry = get_dentry(parent,name); + if (!IS_ERR(dentry)) + error = sysfs_symlink(parent->d_inode,dentry,target); + else + error = PTR_ERR(dentry); + up(&parent->d_inode->i_sem); + } else + error = -EINVAL; return error; } + +static void hash_and_remove(struct dentry * dir, const char * name) +{ + struct dentry * victim; + + down(&dir->d_inode->i_sem); + victim = get_dentry(dir,name); + if (!IS_ERR(victim)) { + /* make sure dentry is really there */ + if (victim->d_inode && + (victim->d_parent->d_inode == dir->d_inode)) { + sysfs_unlink(dir->d_inode,victim); + } + } + up(&dir->d_inode->i_sem); +} + + /** - * sysfs_remove_file - exported file removal - * @dir: directory the file supposedly resides in - * @name: name of the file + * sysfs_remove_file - remove an object attribute. + * @kobj: object we're acting for. + * @attr: attribute descriptor. * - * Try and find the file in the dir's list. - * If it's there, call __remove_file() (above) for the dentry. + * Hash the attribute name and kill the victim. */ -void sysfs_remove_file(struct driver_dir_entry * dir, const char * name) + +void sysfs_remove_file(struct kobject * kobj, struct attribute * attr) { - struct dentry * dentry; + hash_and_remove(kobj->dir.dentry,attr->name); +} - if (!dir->dentry) - return; - down(&dir->dentry->d_inode->i_sem); - dentry = get_dentry(dir->dentry,name); - if (!IS_ERR(dentry)) { - /* make sure dentry is really there */ - if (dentry->d_inode && - (dentry->d_parent->d_inode == dir->dentry->d_inode)) { - sysfs_unlink(dir->dentry->d_inode,dentry); - } - } - up(&dir->dentry->d_inode->i_sem); +/** + * sysfs_remove_link - remove symlink in object's directory. + * @kobj: object we're acting for. + * @name: name of the symlink to remove. + */ + +void sysfs_remove_link(struct kobject * kobj, char * name) +{ + hash_and_remove(kobj->dir.dentry,name); } + /** - * sysfs_remove_dir - exportable directory removal - * @dir: directory to remove + * sysfs_remove_dir - remove an object's directory. + * @kobj: object. * - * To make sure we don't orphan anyone, first remove - * all the children in the list, then do clean up the directory. + * The only thing special about this is that we remove any files in + * the directory before we remove the directory, and we've inlined + * what used to be sysfs_rmdir() below, instead of calling separately. */ -void sysfs_remove_dir(struct driver_dir_entry * dir) + +void sysfs_remove_dir(struct kobject * kobj) { struct list_head * node, * next; - struct dentry * dentry = dir->dentry; + struct dentry * dentry = kobj->dir.dentry; struct dentry * parent; if (!dentry) @@ -542,8 +571,9 @@ void sysfs_remove_dir(struct driver_dir_entry * dir) } EXPORT_SYMBOL(sysfs_create_file); -EXPORT_SYMBOL(sysfs_create_symlink); -EXPORT_SYMBOL(sysfs_create_dir); EXPORT_SYMBOL(sysfs_remove_file); +EXPORT_SYMBOL(sysfs_create_link); +EXPORT_SYMBOL(sysfs_remove_link); +EXPORT_SYMBOL(sysfs_create_dir); EXPORT_SYMBOL(sysfs_remove_dir); MODULE_LICENSE("GPL"); diff --git a/include/linux/kobject.h b/include/linux/kobject.h index c78325e8a199..12431a980712 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -16,6 +16,7 @@ struct kobject { atomic_t refcount; struct list_head entry; struct kobject * parent; + struct sysfs_dir dir; }; extern void kobject_init(struct kobject *); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 6479902e1d20..fe82dff179ce 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -11,18 +11,15 @@ struct driver_dir_entry; struct attribute; +struct kobject; struct sysfs_ops { - int (*open)(struct driver_dir_entry *); - int (*close)(struct driver_dir_entry *); - ssize_t (*show)(struct driver_dir_entry *, struct attribute *,char *, size_t, loff_t); - ssize_t (*store)(struct driver_dir_entry *,struct attribute *,const char *, size_t, loff_t); + ssize_t (*show)(struct kobject *, struct attribute *,char *, size_t, loff_t); + ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t, loff_t); }; -struct driver_dir_entry { - char * name; +struct sysfs_dir { struct dentry * dentry; - mode_t mode; struct sysfs_ops * ops; }; @@ -32,20 +29,21 @@ struct attribute { }; extern int -sysfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *); +sysfs_create_dir(struct kobject *); extern void -sysfs_remove_dir(struct driver_dir_entry * entry); +sysfs_remove_dir(struct kobject *); extern int -sysfs_create_file(struct attribute * attr, - struct driver_dir_entry * parent); +sysfs_create_file(struct kobject *, struct attribute *); + +extern void +sysfs_remove_file(struct kobject *, struct attribute *); extern int -sysfs_create_symlink(struct driver_dir_entry * parent, - char * name, char * target); +sysfs_create_link(struct kobject * kobj, char * name, char * target); extern void -sysfs_remove_file(struct driver_dir_entry *, const char * name); +sysfs_remove_link(struct kobject *, char * name); #endif /* _SYSFS_H_ */ |
