diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/sysfs/bin.c | 184 | ||||
-rw-r--r-- | fs/sysfs/dir.c | 1 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 2 |
3 files changed, 174 insertions, 13 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index f2c478c3424e..96cc2bf6a84e 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c | |||
@@ -21,15 +21,28 @@ | |||
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
24 | #include <linux/mm.h> | ||
24 | 25 | ||
25 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
26 | 27 | ||
27 | #include "sysfs.h" | 28 | #include "sysfs.h" |
28 | 29 | ||
30 | /* | ||
31 | * There's one bin_buffer for each open file. | ||
32 | * | ||
33 | * filp->private_data points to bin_buffer and | ||
34 | * sysfs_dirent->s_bin_attr.buffers points to a the bin_buffer s | ||
35 | * sysfs_dirent->s_bin_attr.buffers is protected by sysfs_bin_lock | ||
36 | */ | ||
37 | static DEFINE_MUTEX(sysfs_bin_lock); | ||
38 | |||
29 | struct bin_buffer { | 39 | struct bin_buffer { |
30 | struct mutex mutex; | 40 | struct mutex mutex; |
31 | void *buffer; | 41 | void *buffer; |
32 | int mmapped; | 42 | int mmapped; |
43 | struct vm_operations_struct *vm_ops; | ||
44 | struct file *file; | ||
45 | struct hlist_node list; | ||
33 | }; | 46 | }; |
34 | 47 | ||
35 | static int | 48 | static int |
@@ -168,29 +181,148 @@ out_free: | |||
168 | return count; | 181 | return count; |
169 | } | 182 | } |
170 | 183 | ||
184 | static void bin_vma_open(struct vm_area_struct *vma) | ||
185 | { | ||
186 | struct file *file = vma->vm_file; | ||
187 | struct bin_buffer *bb = file->private_data; | ||
188 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
189 | |||
190 | if (!bb->vm_ops || !bb->vm_ops->open) | ||
191 | return; | ||
192 | |||
193 | if (!sysfs_get_active_two(attr_sd)) | ||
194 | return; | ||
195 | |||
196 | bb->vm_ops->open(vma); | ||
197 | |||
198 | sysfs_put_active_two(attr_sd); | ||
199 | } | ||
200 | |||
201 | static void bin_vma_close(struct vm_area_struct *vma) | ||
202 | { | ||
203 | struct file *file = vma->vm_file; | ||
204 | struct bin_buffer *bb = file->private_data; | ||
205 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
206 | |||
207 | if (!bb->vm_ops || !bb->vm_ops->close) | ||
208 | return; | ||
209 | |||
210 | if (!sysfs_get_active_two(attr_sd)) | ||
211 | return; | ||
212 | |||
213 | bb->vm_ops->close(vma); | ||
214 | |||
215 | sysfs_put_active_two(attr_sd); | ||
216 | } | ||
217 | |||
218 | static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
219 | { | ||
220 | struct file *file = vma->vm_file; | ||
221 | struct bin_buffer *bb = file->private_data; | ||
222 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
223 | int ret; | ||
224 | |||
225 | if (!bb->vm_ops || !bb->vm_ops->fault) | ||
226 | return VM_FAULT_SIGBUS; | ||
227 | |||
228 | if (!sysfs_get_active_two(attr_sd)) | ||
229 | return VM_FAULT_SIGBUS; | ||
230 | |||
231 | ret = bb->vm_ops->fault(vma, vmf); | ||
232 | |||
233 | sysfs_put_active_two(attr_sd); | ||
234 | return ret; | ||
235 | } | ||
236 | |||
237 | static int bin_page_mkwrite(struct vm_area_struct *vma, struct page *page) | ||
238 | { | ||
239 | struct file *file = vma->vm_file; | ||
240 | struct bin_buffer *bb = file->private_data; | ||
241 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
242 | int ret; | ||
243 | |||
244 | if (!bb->vm_ops || !bb->vm_ops->page_mkwrite) | ||
245 | return -EINVAL; | ||
246 | |||
247 | if (!sysfs_get_active_two(attr_sd)) | ||
248 | return -EINVAL; | ||
249 | |||
250 | ret = bb->vm_ops->page_mkwrite(vma, page); | ||
251 | |||
252 | sysfs_put_active_two(attr_sd); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | static int bin_access(struct vm_area_struct *vma, unsigned long addr, | ||
257 | void *buf, int len, int write) | ||
258 | { | ||
259 | struct file *file = vma->vm_file; | ||
260 | struct bin_buffer *bb = file->private_data; | ||
261 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
262 | int ret; | ||
263 | |||
264 | if (!bb->vm_ops || !bb->vm_ops->access) | ||
265 | return -EINVAL; | ||
266 | |||
267 | if (!sysfs_get_active_two(attr_sd)) | ||
268 | return -EINVAL; | ||
269 | |||
270 | ret = bb->vm_ops->access(vma, addr, buf, len, write); | ||
271 | |||
272 | sysfs_put_active_two(attr_sd); | ||
273 | return ret; | ||
274 | } | ||
275 | |||
276 | static struct vm_operations_struct bin_vm_ops = { | ||
277 | .open = bin_vma_open, | ||
278 | .close = bin_vma_close, | ||
279 | .fault = bin_fault, | ||
280 | .page_mkwrite = bin_page_mkwrite, | ||
281 | .access = bin_access, | ||
282 | }; | ||
283 | |||
171 | static int mmap(struct file *file, struct vm_area_struct *vma) | 284 | static int mmap(struct file *file, struct vm_area_struct *vma) |
172 | { | 285 | { |
173 | struct bin_buffer *bb = file->private_data; | 286 | struct bin_buffer *bb = file->private_data; |
174 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 287 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
175 | struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; | 288 | struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; |
176 | struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; | 289 | struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; |
290 | struct vm_operations_struct *vm_ops; | ||
177 | int rc; | 291 | int rc; |
178 | 292 | ||
179 | mutex_lock(&bb->mutex); | 293 | mutex_lock(&bb->mutex); |
180 | 294 | ||
181 | /* need attr_sd for attr, its parent for kobj */ | 295 | /* need attr_sd for attr, its parent for kobj */ |
296 | rc = -ENODEV; | ||
182 | if (!sysfs_get_active_two(attr_sd)) | 297 | if (!sysfs_get_active_two(attr_sd)) |
183 | return -ENODEV; | 298 | goto out_unlock; |
184 | 299 | ||
185 | rc = -EINVAL; | 300 | rc = -EINVAL; |
186 | if (attr->mmap) | 301 | if (!attr->mmap) |
187 | rc = attr->mmap(kobj, attr, vma); | 302 | goto out_put; |
188 | 303 | ||
189 | if (rc == 0 && !bb->mmapped) | 304 | rc = attr->mmap(kobj, attr, vma); |
190 | bb->mmapped = 1; | 305 | vm_ops = vma->vm_ops; |
191 | else | 306 | vma->vm_ops = &bin_vm_ops; |
192 | sysfs_put_active_two(attr_sd); | 307 | if (rc) |
308 | goto out_put; | ||
193 | 309 | ||
310 | rc = -EINVAL; | ||
311 | if (bb->mmapped && bb->vm_ops != vma->vm_ops) | ||
312 | goto out_put; | ||
313 | |||
314 | #ifdef CONFIG_NUMA | ||
315 | rc = -EINVAL; | ||
316 | if (vm_ops && ((vm_ops->set_policy || vm_ops->get_policy || vm_ops->migrate))) | ||
317 | goto out_put; | ||
318 | #endif | ||
319 | |||
320 | rc = 0; | ||
321 | bb->mmapped = 1; | ||
322 | bb->vm_ops = vm_ops; | ||
323 | out_put: | ||
324 | sysfs_put_active_two(attr_sd); | ||
325 | out_unlock: | ||
194 | mutex_unlock(&bb->mutex); | 326 | mutex_unlock(&bb->mutex); |
195 | 327 | ||
196 | return rc; | 328 | return rc; |
@@ -223,8 +355,13 @@ static int open(struct inode * inode, struct file * file) | |||
223 | goto err_out; | 355 | goto err_out; |
224 | 356 | ||
225 | mutex_init(&bb->mutex); | 357 | mutex_init(&bb->mutex); |
358 | bb->file = file; | ||
226 | file->private_data = bb; | 359 | file->private_data = bb; |
227 | 360 | ||
361 | mutex_lock(&sysfs_bin_lock); | ||
362 | hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers); | ||
363 | mutex_unlock(&sysfs_bin_lock); | ||
364 | |||
228 | /* open succeeded, put active references */ | 365 | /* open succeeded, put active references */ |
229 | sysfs_put_active_two(attr_sd); | 366 | sysfs_put_active_two(attr_sd); |
230 | return 0; | 367 | return 0; |
@@ -237,11 +374,12 @@ static int open(struct inode * inode, struct file * file) | |||
237 | 374 | ||
238 | static int release(struct inode * inode, struct file * file) | 375 | static int release(struct inode * inode, struct file * file) |
239 | { | 376 | { |
240 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | ||
241 | struct bin_buffer *bb = file->private_data; | 377 | struct bin_buffer *bb = file->private_data; |
242 | 378 | ||
243 | if (bb->mmapped) | 379 | mutex_lock(&sysfs_bin_lock); |
244 | sysfs_put_active_two(attr_sd); | 380 | hlist_del(&bb->list); |
381 | mutex_unlock(&sysfs_bin_lock); | ||
382 | |||
245 | kfree(bb->buffer); | 383 | kfree(bb->buffer); |
246 | kfree(bb); | 384 | kfree(bb); |
247 | return 0; | 385 | return 0; |
@@ -256,6 +394,26 @@ const struct file_operations bin_fops = { | |||
256 | .release = release, | 394 | .release = release, |
257 | }; | 395 | }; |
258 | 396 | ||
397 | |||
398 | void unmap_bin_file(struct sysfs_dirent *attr_sd) | ||
399 | { | ||
400 | struct bin_buffer *bb; | ||
401 | struct hlist_node *tmp; | ||
402 | |||
403 | if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR) | ||
404 | return; | ||
405 | |||
406 | mutex_lock(&sysfs_bin_lock); | ||
407 | |||
408 | hlist_for_each_entry(bb, tmp, &attr_sd->s_bin_attr.buffers, list) { | ||
409 | struct inode *inode = bb->file->f_path.dentry->d_inode; | ||
410 | |||
411 | unmap_mapping_range(inode->i_mapping, 0, 0, 1); | ||
412 | } | ||
413 | |||
414 | mutex_unlock(&sysfs_bin_lock); | ||
415 | } | ||
416 | |||
259 | /** | 417 | /** |
260 | * sysfs_create_bin_file - create binary file for object. | 418 | * sysfs_create_bin_file - create binary file for object. |
261 | * @kobj: object. | 419 | * @kobj: object. |
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index f13d852ab3c1..66aeb4fff0c3 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -609,6 +609,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) | |||
609 | 609 | ||
610 | sysfs_drop_dentry(sd); | 610 | sysfs_drop_dentry(sd); |
611 | sysfs_deactivate(sd); | 611 | sysfs_deactivate(sd); |
612 | unmap_bin_file(sd); | ||
612 | sysfs_put(sd); | 613 | sysfs_put(sd); |
613 | } | 614 | } |
614 | } | 615 | } |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 9055d04e4ab0..3fa0d98481e2 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -28,6 +28,7 @@ struct sysfs_elem_attr { | |||
28 | 28 | ||
29 | struct sysfs_elem_bin_attr { | 29 | struct sysfs_elem_bin_attr { |
30 | struct bin_attribute *bin_attr; | 30 | struct bin_attribute *bin_attr; |
31 | struct hlist_head buffers; | ||
31 | }; | 32 | }; |
32 | 33 | ||
33 | /* | 34 | /* |
@@ -164,6 +165,7 @@ int sysfs_add_file_mode(struct sysfs_dirent *dir_sd, | |||
164 | * bin.c | 165 | * bin.c |
165 | */ | 166 | */ |
166 | extern const struct file_operations bin_fops; | 167 | extern const struct file_operations bin_fops; |
168 | void unmap_bin_file(struct sysfs_dirent *attr_sd); | ||
167 | 169 | ||
168 | /* | 170 | /* |
169 | * symlink.c | 171 | * symlink.c |