diff options
Diffstat (limited to 'fs/sysfs/file.c')
-rw-r--r-- | fs/sysfs/file.c | 130 |
1 files changed, 76 insertions, 54 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 04f6b0ebc889..310430baf572 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -87,8 +87,8 @@ remove_from_collection(struct sysfs_buffer *buffer, struct inode *node) */ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { - struct sysfs_dirent * sd = dentry->d_fsdata; - struct kobject * kobj = to_kobj(dentry->d_parent); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_ops * ops = buffer->ops; int ret = 0; ssize_t count; @@ -98,8 +98,15 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer if (!buffer->page) return -ENOMEM; - buffer->event = atomic_read(&sd->s_event); - count = ops->show(kobj, sd->s_elem.attr.attr, buffer->page); + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + buffer->event = atomic_read(&attr_sd->s_event); + count = ops->show(kobj, attr_sd->s_elem.attr.attr, buffer->page); + + sysfs_put_active_two(attr_sd); + BUG_ON(count > (ssize_t)PAGE_SIZE); if (count >= 0) { buffer->needs_read_fill = 0; @@ -195,14 +202,23 @@ fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t * passing the buffer that we acquired in fill_write_buffer(). */ -static int +static int flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; - struct kobject * kobj = to_kobj(dentry->d_parent); + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_ops * ops = buffer->ops; + int rc; + + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + rc = ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count); + + sysfs_put_active_two(attr_sd); - return ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count); + return rc; } @@ -246,22 +262,22 @@ out: static int sysfs_open_file(struct inode *inode, struct file *file) { - struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct attribute *attr = attr_sd->s_elem.attr.attr; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_buffer_collection *set; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; - int error = 0; + int error; - if (!kobj || !attr) - goto Einval; + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; - /* Grab the module reference for this attribute if we have one */ - if (!try_module_get(attr->owner)) { - error = -ENODEV; - goto Done; - } + /* Grab the module reference for this attribute */ + error = -ENODEV; + if (!try_module_get(attr->owner)) + goto err_sput; /* if the kobject has no ktype, then we assume that it is a subsystem * itself, and use ops for it. @@ -276,30 +292,30 @@ static int sysfs_open_file(struct inode *inode, struct file *file) /* No sysfs operations, either from having no subsystem, * or the subsystem have no operations. */ + error = -EACCES; if (!ops) - goto Eaccess; + goto err_mput; /* make sure we have a collection to add our buffers to */ mutex_lock(&inode->i_mutex); if (!(set = inode->i_private)) { - if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) { - error = -ENOMEM; - goto Done; - } else { + error = -ENOMEM; + if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) + goto err_mput; + else INIT_LIST_HEAD(&set->associates); - } } mutex_unlock(&inode->i_mutex); + error = -EACCES; + /* File needs write support. * The inode's perms must say it's ok, * and we must have a store method. */ if (file->f_mode & FMODE_WRITE) { - if (!(inode->i_mode & S_IWUGO) || !ops->store) - goto Eaccess; - + goto err_mput; } /* File needs read support. @@ -308,46 +324,45 @@ static int sysfs_open_file(struct inode *inode, struct file *file) */ if (file->f_mode & FMODE_READ) { if (!(inode->i_mode & S_IRUGO) || !ops->show) - goto Eaccess; + goto err_mput; } /* No error? Great, allocate a buffer for the file, and store it * it in file->private_data for easy access. */ + error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); - if (buffer) { - INIT_LIST_HEAD(&buffer->associates); - init_MUTEX(&buffer->sem); - buffer->needs_read_fill = 1; - buffer->ops = ops; - add_to_collection(buffer, inode); - file->private_data = buffer; - } else - error = -ENOMEM; - goto Done; + if (!buffer) + goto err_mput; - Einval: - error = -EINVAL; - goto Done; - Eaccess: - error = -EACCES; + INIT_LIST_HEAD(&buffer->associates); + init_MUTEX(&buffer->sem); + buffer->needs_read_fill = 1; + buffer->ops = ops; + add_to_collection(buffer, inode); + file->private_data = buffer; + + /* open succeeded, put active references and pin attr_sd */ + sysfs_put_active_two(attr_sd); + sysfs_get(attr_sd); + return 0; + + err_mput: module_put(attr->owner); - Done: - if (error) - kobject_put(kobj); + err_sput: + sysfs_put_active_two(attr_sd); return error; } static int sysfs_release(struct inode * inode, struct file * filp) { - struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; struct attribute *attr = attr_sd->s_elem.attr.attr; struct sysfs_buffer * buffer = filp->private_data; if (buffer) remove_from_collection(buffer, inode); - kobject_put(kobj); + sysfs_put(attr_sd); /* After this point, attr should not be accessed. */ module_put(attr->owner); @@ -376,18 +391,25 @@ static int sysfs_release(struct inode * inode, struct file * filp) static unsigned int sysfs_poll(struct file *filp, poll_table *wait) { struct sysfs_buffer * buffer = filp->private_data; - struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); - struct sysfs_dirent * sd = filp->f_path.dentry->d_fsdata; - int res = 0; + struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + + /* need parent for the kobj, grab both */ + if (!sysfs_get_active_two(attr_sd)) + goto trigger; poll_wait(filp, &kobj->poll, wait); - if (buffer->event != atomic_read(&sd->s_event)) { - res = POLLERR|POLLPRI; - buffer->needs_read_fill = 1; - } + sysfs_put_active_two(attr_sd); - return res; + if (buffer->event != atomic_read(&attr_sd->s_event)) + goto trigger; + + return 0; + + trigger: + buffer->needs_read_fill = 1; + return POLLERR|POLLPRI; } |