| From htejun@gmail.com Sun Apr 8 21:19:04 2007 |
| From: Tejun Heo <htejun@gmail.com> |
| Date: Mon, 9 Apr 2007 13:18:48 +0900 |
| Subject: sysfs: implement bin_buffer |
| To: gregkh@suse.de, maneesh@in.ibm.com, dmitry.torokhov@gmail.com, cornelia.huck@de.ibm.com, oneukum@suse.de, rpurdie@rpsys.net, James.Bottomley@SteelEye.com, stern@rowland.harvard.edu, linux-kernel@vger.kernel.org, htejun@gmail.com |
| Cc: Tejun Heo <htejun@gmail.com> |
| Message-ID: <1176092328564-git-send-email-htejun@gmail.com> |
| |
| |
| Implement bin_buffer which contains a mutex and pointer to PAGE_SIZE |
| buffer to properly synchronize accesses to per-openfile buffer and |
| prepare for immediate-kobj-disconnect. |
| |
| Signed-off-by: Tejun Heo <htejun@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/sysfs/bin.c | 64 +++++++++++++++++++++++++++++++++++++++++++-------------- |
| 1 file changed, 49 insertions(+), 15 deletions(-) |
| |
| --- a/fs/sysfs/bin.c |
| +++ b/fs/sysfs/bin.c |
| @@ -20,6 +20,11 @@ |
| |
| #include "sysfs.h" |
| |
| +struct bin_buffer { |
| + struct mutex mutex; |
| + void *buffer; |
| +}; |
| + |
| static int |
| fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) |
| { |
| @@ -36,7 +41,7 @@ fill_read(struct dentry *dentry, char *b |
| static ssize_t |
| read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) |
| { |
| - char *buffer = file->private_data; |
| + struct bin_buffer *bb = file->private_data; |
| struct dentry *dentry = file->f_path.dentry; |
| int size = dentry->d_inode->i_size; |
| loff_t offs = *off; |
| @@ -49,17 +54,23 @@ read(struct file *file, char __user *use |
| count = size - offs; |
| } |
| |
| - count = fill_read(dentry, buffer, offs, count); |
| + mutex_lock(&bb->mutex); |
| + |
| + count = fill_read(dentry, bb->buffer, offs, count); |
| if (count < 0) |
| - return count; |
| + goto out_unlock; |
| |
| - if (copy_to_user(userbuf, buffer, count)) |
| - return -EFAULT; |
| + if (copy_to_user(userbuf, bb->buffer, count)) { |
| + count = -EFAULT; |
| + goto out_unlock; |
| + } |
| |
| pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count); |
| |
| *off = offs + count; |
| |
| + out_unlock: |
| + mutex_unlock(&bb->mutex); |
| return count; |
| } |
| |
| @@ -79,7 +90,7 @@ flush_write(struct dentry *dentry, char |
| static ssize_t write(struct file *file, const char __user *userbuf, |
| size_t bytes, loff_t *off) |
| { |
| - char *buffer = file->private_data; |
| + struct bin_buffer *bb = file->private_data; |
| struct dentry *dentry = file->f_path.dentry; |
| int size = dentry->d_inode->i_size; |
| loff_t offs = *off; |
| @@ -92,25 +103,38 @@ static ssize_t write(struct file *file, |
| count = size - offs; |
| } |
| |
| - if (copy_from_user(buffer, userbuf, count)) |
| - return -EFAULT; |
| + mutex_lock(&bb->mutex); |
| + |
| + if (copy_from_user(bb->buffer, userbuf, count)) { |
| + count = -EFAULT; |
| + goto out_unlock; |
| + } |
| |
| - count = flush_write(dentry, buffer, offs, count); |
| + count = flush_write(dentry, bb->buffer, offs, count); |
| if (count > 0) |
| *off = offs + count; |
| + |
| + out_unlock: |
| + mutex_unlock(&bb->mutex); |
| return count; |
| } |
| |
| static int mmap(struct file *file, struct vm_area_struct *vma) |
| { |
| + struct bin_buffer *bb = file->private_data; |
| struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
| struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
| struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); |
| + int rc; |
| |
| if (!attr->mmap) |
| return -EINVAL; |
| |
| - return attr->mmap(kobj, attr, vma); |
| + mutex_lock(&bb->mutex); |
| + rc = attr->mmap(kobj, attr, vma); |
| + mutex_unlock(&bb->mutex); |
| + |
| + return rc; |
| } |
| |
| static int open(struct inode * inode, struct file * file) |
| @@ -118,6 +142,7 @@ static int open(struct inode * inode, st |
| struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); |
| struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
| struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
| + struct bin_buffer *bb = NULL; |
| int error = -EINVAL; |
| |
| if (!kobj || !attr) |
| @@ -135,14 +160,22 @@ static int open(struct inode * inode, st |
| goto Error; |
| |
| error = -ENOMEM; |
| - file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); |
| - if (!file->private_data) |
| + bb = kzalloc(sizeof(*bb), GFP_KERNEL); |
| + if (!bb) |
| goto Error; |
| |
| + bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); |
| + if (!bb->buffer) |
| + goto Error; |
| + |
| + mutex_init(&bb->mutex); |
| + file->private_data = bb; |
| + |
| error = 0; |
| - goto Done; |
| + goto Done; |
| |
| Error: |
| + kfree(bb); |
| module_put(attr->attr.owner); |
| Done: |
| if (error) |
| @@ -155,11 +188,12 @@ static int release(struct inode * inode, |
| struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); |
| struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
| struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
| - u8 * buffer = file->private_data; |
| + struct bin_buffer *bb = file->private_data; |
| |
| kobject_put(kobj); |
| module_put(attr->attr.owner); |
| - kfree(buffer); |
| + kfree(bb->buffer); |
| + kfree(bb); |
| return 0; |
| } |
| |