diff options
author | Tejun Heo <tj@kernel.org> | 2013-10-01 17:42:07 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-10-05 20:27:40 -0400 |
commit | 73d9714627adced2942e8d53ce0e73d9699a996c (patch) | |
tree | bd144cf3a7871240386c70fae6c4492f3331c093 /fs | |
parent | 2f0c6b7593a590eef7fa35344da57380fcee7581 (diff) |
sysfs: copy bin mmap support from fs/sysfs/bin.c to fs/sysfs/file.c
sysfs bin file handling will be merged into the regular file support.
This patch copies mmap support from bin so that fs/sysfs/file.c can
handle mmapping bin files.
The code is copied mostly verbatim with the following updates.
* ->mmapped and ->vm_ops are added to sysfs_open_file and bin_buffer
references are replaced with sysfs_open_file ones.
* Symbols are prefixed with sysfs_.
* sysfs_unmap_bin_file() grabs sysfs_open_dirent and traverses
->files. Invocation of this function is added to
sysfs_addrm_finish().
* sysfs_bin_mmap() is added to sysfs_bin_operations.
This is a preparation and the new mmap path isn't used yet.
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/sysfs/dir.c | 1 | ||||
-rw-r--r-- | fs/sysfs/file.c | 247 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 2 |
3 files changed, 249 insertions, 1 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index b518afd0d11e..c4040ddb8308 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -595,6 +595,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) | |||
595 | acxt->removed = sd->u.removed_list; | 595 | acxt->removed = sd->u.removed_list; |
596 | 596 | ||
597 | sysfs_deactivate(sd); | 597 | sysfs_deactivate(sd); |
598 | sysfs_unmap_bin_file(sd); | ||
598 | unmap_bin_file(sd); | 599 | unmap_bin_file(sd); |
599 | sysfs_put(sd); | 600 | sysfs_put(sd); |
600 | } | 601 | } |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 9ba492a3d932..02797a134cf8 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/limits.h> | 22 | #include <linux/limits.h> |
23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
24 | #include <linux/seq_file.h> | 24 | #include <linux/seq_file.h> |
25 | #include <linux/mm.h> | ||
25 | 26 | ||
26 | #include "sysfs.h" | 27 | #include "sysfs.h" |
27 | 28 | ||
@@ -52,6 +53,9 @@ struct sysfs_open_file { | |||
52 | struct mutex mutex; | 53 | struct mutex mutex; |
53 | int event; | 54 | int event; |
54 | struct list_head list; | 55 | struct list_head list; |
56 | |||
57 | bool mmapped; | ||
58 | const struct vm_operations_struct *vm_ops; | ||
55 | }; | 59 | }; |
56 | 60 | ||
57 | static bool sysfs_is_bin(struct sysfs_dirent *sd) | 61 | static bool sysfs_is_bin(struct sysfs_dirent *sd) |
@@ -301,6 +305,218 @@ out_free: | |||
301 | return len; | 305 | return len; |
302 | } | 306 | } |
303 | 307 | ||
308 | static void sysfs_bin_vma_open(struct vm_area_struct *vma) | ||
309 | { | ||
310 | struct file *file = vma->vm_file; | ||
311 | struct sysfs_open_file *of = sysfs_of(file); | ||
312 | |||
313 | if (!of->vm_ops) | ||
314 | return; | ||
315 | |||
316 | if (!sysfs_get_active(of->sd)) | ||
317 | return; | ||
318 | |||
319 | if (of->vm_ops->open) | ||
320 | of->vm_ops->open(vma); | ||
321 | |||
322 | sysfs_put_active(of->sd); | ||
323 | } | ||
324 | |||
325 | static int sysfs_bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
326 | { | ||
327 | struct file *file = vma->vm_file; | ||
328 | struct sysfs_open_file *of = sysfs_of(file); | ||
329 | int ret; | ||
330 | |||
331 | if (!of->vm_ops) | ||
332 | return VM_FAULT_SIGBUS; | ||
333 | |||
334 | if (!sysfs_get_active(of->sd)) | ||
335 | return VM_FAULT_SIGBUS; | ||
336 | |||
337 | ret = VM_FAULT_SIGBUS; | ||
338 | if (of->vm_ops->fault) | ||
339 | ret = of->vm_ops->fault(vma, vmf); | ||
340 | |||
341 | sysfs_put_active(of->sd); | ||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | static int sysfs_bin_page_mkwrite(struct vm_area_struct *vma, | ||
346 | struct vm_fault *vmf) | ||
347 | { | ||
348 | struct file *file = vma->vm_file; | ||
349 | struct sysfs_open_file *of = sysfs_of(file); | ||
350 | int ret; | ||
351 | |||
352 | if (!of->vm_ops) | ||
353 | return VM_FAULT_SIGBUS; | ||
354 | |||
355 | if (!sysfs_get_active(of->sd)) | ||
356 | return VM_FAULT_SIGBUS; | ||
357 | |||
358 | ret = 0; | ||
359 | if (of->vm_ops->page_mkwrite) | ||
360 | ret = of->vm_ops->page_mkwrite(vma, vmf); | ||
361 | else | ||
362 | file_update_time(file); | ||
363 | |||
364 | sysfs_put_active(of->sd); | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | static int sysfs_bin_access(struct vm_area_struct *vma, unsigned long addr, | ||
369 | void *buf, int len, int write) | ||
370 | { | ||
371 | struct file *file = vma->vm_file; | ||
372 | struct sysfs_open_file *of = sysfs_of(file); | ||
373 | int ret; | ||
374 | |||
375 | if (!of->vm_ops) | ||
376 | return -EINVAL; | ||
377 | |||
378 | if (!sysfs_get_active(of->sd)) | ||
379 | return -EINVAL; | ||
380 | |||
381 | ret = -EINVAL; | ||
382 | if (of->vm_ops->access) | ||
383 | ret = of->vm_ops->access(vma, addr, buf, len, write); | ||
384 | |||
385 | sysfs_put_active(of->sd); | ||
386 | return ret; | ||
387 | } | ||
388 | |||
389 | #ifdef CONFIG_NUMA | ||
390 | static int sysfs_bin_set_policy(struct vm_area_struct *vma, | ||
391 | struct mempolicy *new) | ||
392 | { | ||
393 | struct file *file = vma->vm_file; | ||
394 | struct sysfs_open_file *of = sysfs_of(file); | ||
395 | int ret; | ||
396 | |||
397 | if (!of->vm_ops) | ||
398 | return 0; | ||
399 | |||
400 | if (!sysfs_get_active(of->sd)) | ||
401 | return -EINVAL; | ||
402 | |||
403 | ret = 0; | ||
404 | if (of->vm_ops->set_policy) | ||
405 | ret = of->vm_ops->set_policy(vma, new); | ||
406 | |||
407 | sysfs_put_active(of->sd); | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | static struct mempolicy *sysfs_bin_get_policy(struct vm_area_struct *vma, | ||
412 | unsigned long addr) | ||
413 | { | ||
414 | struct file *file = vma->vm_file; | ||
415 | struct sysfs_open_file *of = sysfs_of(file); | ||
416 | struct mempolicy *pol; | ||
417 | |||
418 | if (!of->vm_ops) | ||
419 | return vma->vm_policy; | ||
420 | |||
421 | if (!sysfs_get_active(of->sd)) | ||
422 | return vma->vm_policy; | ||
423 | |||
424 | pol = vma->vm_policy; | ||
425 | if (of->vm_ops->get_policy) | ||
426 | pol = of->vm_ops->get_policy(vma, addr); | ||
427 | |||
428 | sysfs_put_active(of->sd); | ||
429 | return pol; | ||
430 | } | ||
431 | |||
432 | static int sysfs_bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, | ||
433 | const nodemask_t *to, unsigned long flags) | ||
434 | { | ||
435 | struct file *file = vma->vm_file; | ||
436 | struct sysfs_open_file *of = sysfs_of(file); | ||
437 | int ret; | ||
438 | |||
439 | if (!of->vm_ops) | ||
440 | return 0; | ||
441 | |||
442 | if (!sysfs_get_active(of->sd)) | ||
443 | return 0; | ||
444 | |||
445 | ret = 0; | ||
446 | if (of->vm_ops->migrate) | ||
447 | ret = of->vm_ops->migrate(vma, from, to, flags); | ||
448 | |||
449 | sysfs_put_active(of->sd); | ||
450 | return ret; | ||
451 | } | ||
452 | #endif | ||
453 | |||
454 | static const struct vm_operations_struct sysfs_bin_vm_ops = { | ||
455 | .open = sysfs_bin_vma_open, | ||
456 | .fault = sysfs_bin_fault, | ||
457 | .page_mkwrite = sysfs_bin_page_mkwrite, | ||
458 | .access = sysfs_bin_access, | ||
459 | #ifdef CONFIG_NUMA | ||
460 | .set_policy = sysfs_bin_set_policy, | ||
461 | .get_policy = sysfs_bin_get_policy, | ||
462 | .migrate = sysfs_bin_migrate, | ||
463 | #endif | ||
464 | }; | ||
465 | |||
466 | static int sysfs_bin_mmap(struct file *file, struct vm_area_struct *vma) | ||
467 | { | ||
468 | struct sysfs_open_file *of = sysfs_of(file); | ||
469 | struct bin_attribute *battr = of->sd->s_bin_attr.bin_attr; | ||
470 | struct kobject *kobj = of->sd->s_parent->s_dir.kobj; | ||
471 | int rc; | ||
472 | |||
473 | mutex_lock(&of->mutex); | ||
474 | |||
475 | /* need of->sd for battr, its parent for kobj */ | ||
476 | rc = -ENODEV; | ||
477 | if (!sysfs_get_active(of->sd)) | ||
478 | goto out_unlock; | ||
479 | |||
480 | rc = -EINVAL; | ||
481 | if (!battr->mmap) | ||
482 | goto out_put; | ||
483 | |||
484 | rc = battr->mmap(file, kobj, battr, vma); | ||
485 | if (rc) | ||
486 | goto out_put; | ||
487 | |||
488 | /* | ||
489 | * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() | ||
490 | * to satisfy versions of X which crash if the mmap fails: that | ||
491 | * substitutes a new vm_file, and we don't then want bin_vm_ops. | ||
492 | */ | ||
493 | if (vma->vm_file != file) | ||
494 | goto out_put; | ||
495 | |||
496 | rc = -EINVAL; | ||
497 | if (of->mmapped && of->vm_ops != vma->vm_ops) | ||
498 | goto out_put; | ||
499 | |||
500 | /* | ||
501 | * It is not possible to successfully wrap close. | ||
502 | * So error if someone is trying to use close. | ||
503 | */ | ||
504 | rc = -EINVAL; | ||
505 | if (vma->vm_ops && vma->vm_ops->close) | ||
506 | goto out_put; | ||
507 | |||
508 | rc = 0; | ||
509 | of->mmapped = 1; | ||
510 | of->vm_ops = vma->vm_ops; | ||
511 | vma->vm_ops = &sysfs_bin_vm_ops; | ||
512 | out_put: | ||
513 | sysfs_put_active(of->sd); | ||
514 | out_unlock: | ||
515 | mutex_unlock(&of->mutex); | ||
516 | |||
517 | return rc; | ||
518 | } | ||
519 | |||
304 | /** | 520 | /** |
305 | * sysfs_get_open_dirent - get or create sysfs_open_dirent | 521 | * sysfs_get_open_dirent - get or create sysfs_open_dirent |
306 | * @sd: target sysfs_dirent | 522 | * @sd: target sysfs_dirent |
@@ -375,7 +591,9 @@ static void sysfs_put_open_dirent(struct sysfs_dirent *sd, | |||
375 | mutex_lock(&sysfs_open_file_mutex); | 591 | mutex_lock(&sysfs_open_file_mutex); |
376 | spin_lock_irqsave(&sysfs_open_dirent_lock, flags); | 592 | spin_lock_irqsave(&sysfs_open_dirent_lock, flags); |
377 | 593 | ||
378 | list_del(&of->list); | 594 | if (of) |
595 | list_del(&of->list); | ||
596 | |||
379 | if (atomic_dec_and_test(&od->refcnt)) | 597 | if (atomic_dec_and_test(&od->refcnt)) |
380 | sd->s_attr.open = NULL; | 598 | sd->s_attr.open = NULL; |
381 | else | 599 | else |
@@ -477,6 +695,32 @@ static int sysfs_release(struct inode *inode, struct file *filp) | |||
477 | return 0; | 695 | return 0; |
478 | } | 696 | } |
479 | 697 | ||
698 | void sysfs_unmap_bin_file(struct sysfs_dirent *sd) | ||
699 | { | ||
700 | struct sysfs_open_dirent *od; | ||
701 | struct sysfs_open_file *of; | ||
702 | |||
703 | if (!sysfs_is_bin(sd)) | ||
704 | return; | ||
705 | |||
706 | spin_lock_irq(&sysfs_open_dirent_lock); | ||
707 | od = sd->s_attr.open; | ||
708 | if (od) | ||
709 | atomic_inc(&od->refcnt); | ||
710 | spin_unlock_irq(&sysfs_open_dirent_lock); | ||
711 | if (!od) | ||
712 | return; | ||
713 | |||
714 | mutex_lock(&sysfs_open_file_mutex); | ||
715 | list_for_each_entry(of, &od->files, list) { | ||
716 | struct inode *inode = file_inode(of->file); | ||
717 | unmap_mapping_range(inode->i_mapping, 0, 0, 1); | ||
718 | } | ||
719 | mutex_unlock(&sysfs_open_file_mutex); | ||
720 | |||
721 | sysfs_put_open_dirent(sd, NULL); | ||
722 | } | ||
723 | |||
480 | /* Sysfs attribute files are pollable. The idea is that you read | 724 | /* Sysfs attribute files are pollable. The idea is that you read |
481 | * the content and then you use 'poll' or 'select' to wait for | 725 | * the content and then you use 'poll' or 'select' to wait for |
482 | * the content to change. When the content changes (assuming the | 726 | * the content to change. When the content changes (assuming the |
@@ -562,6 +806,7 @@ const struct file_operations sysfs_bin_operations = { | |||
562 | .read = sysfs_bin_read, | 806 | .read = sysfs_bin_read, |
563 | .write = sysfs_write_file, | 807 | .write = sysfs_write_file, |
564 | .llseek = generic_file_llseek, | 808 | .llseek = generic_file_llseek, |
809 | .mmap = sysfs_bin_mmap, | ||
565 | }; | 810 | }; |
566 | 811 | ||
567 | int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, | 812 | int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index f103bac53df7..c960d6272cf6 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -220,6 +220,8 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, | |||
220 | int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, | 220 | int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, |
221 | const struct attribute *attr, int type, | 221 | const struct attribute *attr, int type, |
222 | umode_t amode, const void *ns); | 222 | umode_t amode, const void *ns); |
223 | void sysfs_unmap_bin_file(struct sysfs_dirent *sd); | ||
224 | |||
223 | /* | 225 | /* |
224 | * bin.c | 226 | * bin.c |
225 | */ | 227 | */ |