diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/sysfs/bin.c | 64 |
1 files changed, 49 insertions, 15 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 67a0d5030c9..5dc47fe5de5 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c | |||
| @@ -20,6 +20,11 @@ | |||
| 20 | 20 | ||
| 21 | #include "sysfs.h" | 21 | #include "sysfs.h" |
| 22 | 22 | ||
| 23 | struct bin_buffer { | ||
| 24 | struct mutex mutex; | ||
| 25 | void *buffer; | ||
| 26 | }; | ||
| 27 | |||
| 23 | static int | 28 | static int |
| 24 | fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) | 29 | fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) |
| 25 | { | 30 | { |
| @@ -36,7 +41,7 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) | |||
| 36 | static ssize_t | 41 | static ssize_t |
| 37 | read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) | 42 | read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) |
| 38 | { | 43 | { |
| 39 | char *buffer = file->private_data; | 44 | struct bin_buffer *bb = file->private_data; |
| 40 | struct dentry *dentry = file->f_path.dentry; | 45 | struct dentry *dentry = file->f_path.dentry; |
| 41 | int size = dentry->d_inode->i_size; | 46 | int size = dentry->d_inode->i_size; |
| 42 | loff_t offs = *off; | 47 | loff_t offs = *off; |
| @@ -49,17 +54,23 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) | |||
| 49 | count = size - offs; | 54 | count = size - offs; |
| 50 | } | 55 | } |
| 51 | 56 | ||
| 52 | count = fill_read(dentry, buffer, offs, count); | 57 | mutex_lock(&bb->mutex); |
| 58 | |||
| 59 | count = fill_read(dentry, bb->buffer, offs, count); | ||
| 53 | if (count < 0) | 60 | if (count < 0) |
| 54 | return count; | 61 | goto out_unlock; |
| 55 | 62 | ||
| 56 | if (copy_to_user(userbuf, buffer, count)) | 63 | if (copy_to_user(userbuf, bb->buffer, count)) { |
| 57 | return -EFAULT; | 64 | count = -EFAULT; |
| 65 | goto out_unlock; | ||
| 66 | } | ||
| 58 | 67 | ||
| 59 | pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); | 68 | pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); |
| 60 | 69 | ||
| 61 | *off = offs + count; | 70 | *off = offs + count; |
| 62 | 71 | ||
| 72 | out_unlock: | ||
| 73 | mutex_unlock(&bb->mutex); | ||
| 63 | return count; | 74 | return count; |
| 64 | } | 75 | } |
| 65 | 76 | ||
| @@ -79,7 +90,7 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) | |||
| 79 | static ssize_t write(struct file *file, const char __user *userbuf, | 90 | static ssize_t write(struct file *file, const char __user *userbuf, |
| 80 | size_t bytes, loff_t *off) | 91 | size_t bytes, loff_t *off) |
| 81 | { | 92 | { |
| 82 | char *buffer = file->private_data; | 93 | struct bin_buffer *bb = file->private_data; |
| 83 | struct dentry *dentry = file->f_path.dentry; | 94 | struct dentry *dentry = file->f_path.dentry; |
| 84 | int size = dentry->d_inode->i_size; | 95 | int size = dentry->d_inode->i_size; |
| 85 | loff_t offs = *off; | 96 | loff_t offs = *off; |
| @@ -92,25 +103,38 @@ static ssize_t write(struct file *file, const char __user *userbuf, | |||
| 92 | count = size - offs; | 103 | count = size - offs; |
| 93 | } | 104 | } |
| 94 | 105 | ||
| 95 | if (copy_from_user(buffer, userbuf, count)) | 106 | mutex_lock(&bb->mutex); |
| 96 | return -EFAULT; | 107 | |
| 108 | if (copy_from_user(bb->buffer, userbuf, count)) { | ||
| 109 | count = -EFAULT; | ||
| 110 | goto out_unlock; | ||
| 111 | } | ||
| 97 | 112 | ||
| 98 | count = flush_write(dentry, buffer, offs, count); | 113 | count = flush_write(dentry, bb->buffer, offs, count); |
| 99 | if (count > 0) | 114 | if (count > 0) |
| 100 | *off = offs + count; | 115 | *off = offs + count; |
| 116 | |||
| 117 | out_unlock: | ||
| 118 | mutex_unlock(&bb->mutex); | ||
| 101 | return count; | 119 | return count; |
| 102 | } | 120 | } |
| 103 | 121 | ||
| 104 | static int mmap(struct file *file, struct vm_area_struct *vma) | 122 | static int mmap(struct file *file, struct vm_area_struct *vma) |
| 105 | { | 123 | { |
| 124 | struct bin_buffer *bb = file->private_data; | ||
| 106 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 125 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
| 107 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 126 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
| 108 | struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); | 127 | struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); |
| 128 | int rc; | ||
| 109 | 129 | ||
| 110 | if (!attr->mmap) | 130 | if (!attr->mmap) |
| 111 | return -EINVAL; | 131 | return -EINVAL; |
| 112 | 132 | ||
| 113 | return attr->mmap(kobj, attr, vma); | 133 | mutex_lock(&bb->mutex); |
| 134 | rc = attr->mmap(kobj, attr, vma); | ||
| 135 | mutex_unlock(&bb->mutex); | ||
| 136 | |||
| 137 | return rc; | ||
| 114 | } | 138 | } |
| 115 | 139 | ||
| 116 | static int open(struct inode * inode, struct file * file) | 140 | static int open(struct inode * inode, struct file * file) |
| @@ -118,6 +142,7 @@ static int open(struct inode * inode, struct file * file) | |||
| 118 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); | 142 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); |
| 119 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 143 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
| 120 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 144 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
| 145 | struct bin_buffer *bb = NULL; | ||
| 121 | int error = -EINVAL; | 146 | int error = -EINVAL; |
| 122 | 147 | ||
| 123 | if (!kobj || !attr) | 148 | if (!kobj || !attr) |
| @@ -135,14 +160,22 @@ static int open(struct inode * inode, struct file * file) | |||
| 135 | goto Error; | 160 | goto Error; |
| 136 | 161 | ||
| 137 | error = -ENOMEM; | 162 | error = -ENOMEM; |
| 138 | file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); | 163 | bb = kzalloc(sizeof(*bb), GFP_KERNEL); |
| 139 | if (!file->private_data) | 164 | if (!bb) |
| 140 | goto Error; | 165 | goto Error; |
| 141 | 166 | ||
| 167 | bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 168 | if (!bb->buffer) | ||
| 169 | goto Error; | ||
| 170 | |||
| 171 | mutex_init(&bb->mutex); | ||
| 172 | file->private_data = bb; | ||
| 173 | |||
| 142 | error = 0; | 174 | error = 0; |
| 143 | goto Done; | 175 | goto Done; |
| 144 | 176 | ||
| 145 | Error: | 177 | Error: |
| 178 | kfree(bb); | ||
| 146 | module_put(attr->attr.owner); | 179 | module_put(attr->attr.owner); |
| 147 | Done: | 180 | Done: |
| 148 | if (error) | 181 | if (error) |
| @@ -155,11 +188,12 @@ static int release(struct inode * inode, struct file * file) | |||
| 155 | struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); | 188 | struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); |
| 156 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 189 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
| 157 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 190 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
| 158 | u8 * buffer = file->private_data; | 191 | struct bin_buffer *bb = file->private_data; |
| 159 | 192 | ||
| 160 | kobject_put(kobj); | 193 | kobject_put(kobj); |
| 161 | module_put(attr->attr.owner); | 194 | module_put(attr->attr.owner); |
| 162 | kfree(buffer); | 195 | kfree(bb->buffer); |
| 196 | kfree(bb); | ||
| 163 | return 0; | 197 | return 0; |
| 164 | } | 198 | } |
| 165 | 199 | ||
