diff options
Diffstat (limited to 'fs/9p/vfs_file.c')
-rw-r--r-- | fs/9p/vfs_file.c | 316 |
1 files changed, 253 insertions, 63 deletions
diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 240c30674396..78bcb97c3425 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c | |||
@@ -44,8 +44,7 @@ | |||
44 | #include "fid.h" | 44 | #include "fid.h" |
45 | #include "cache.h" | 45 | #include "cache.h" |
46 | 46 | ||
47 | static const struct file_operations v9fs_cached_file_operations; | 47 | static const struct vm_operations_struct v9fs_file_vm_ops; |
48 | static const struct file_operations v9fs_cached_file_operations_dotl; | ||
49 | 48 | ||
50 | /** | 49 | /** |
51 | * v9fs_file_open - open a file (or directory) | 50 | * v9fs_file_open - open a file (or directory) |
@@ -57,11 +56,13 @@ static const struct file_operations v9fs_cached_file_operations_dotl; | |||
57 | int v9fs_file_open(struct inode *inode, struct file *file) | 56 | int v9fs_file_open(struct inode *inode, struct file *file) |
58 | { | 57 | { |
59 | int err; | 58 | int err; |
59 | struct v9fs_inode *v9inode; | ||
60 | struct v9fs_session_info *v9ses; | 60 | struct v9fs_session_info *v9ses; |
61 | struct p9_fid *fid; | 61 | struct p9_fid *fid; |
62 | int omode; | 62 | int omode; |
63 | 63 | ||
64 | P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file); | 64 | P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file); |
65 | v9inode = V9FS_I(inode); | ||
65 | v9ses = v9fs_inode2v9ses(inode); | 66 | v9ses = v9fs_inode2v9ses(inode); |
66 | if (v9fs_proto_dotl(v9ses)) | 67 | if (v9fs_proto_dotl(v9ses)) |
67 | omode = file->f_flags; | 68 | omode = file->f_flags; |
@@ -89,20 +90,30 @@ int v9fs_file_open(struct inode *inode, struct file *file) | |||
89 | } | 90 | } |
90 | 91 | ||
91 | file->private_data = fid; | 92 | file->private_data = fid; |
92 | if ((fid->qid.version) && (v9ses->cache)) { | 93 | if (v9ses->cache && !v9inode->writeback_fid) { |
93 | P9_DPRINTK(P9_DEBUG_VFS, "cached"); | 94 | /* |
94 | /* enable cached file options */ | 95 | * clone a fid and add it to writeback_fid |
95 | if(file->f_op == &v9fs_file_operations) | 96 | * we do it during open time instead of |
96 | file->f_op = &v9fs_cached_file_operations; | 97 | * page dirty time via write_begin/page_mkwrite |
97 | else if (file->f_op == &v9fs_file_operations_dotl) | 98 | * because we want write after unlink usecase |
98 | file->f_op = &v9fs_cached_file_operations_dotl; | 99 | * to work. |
99 | 100 | */ | |
101 | fid = v9fs_writeback_fid(file->f_path.dentry); | ||
102 | if (IS_ERR(fid)) { | ||
103 | err = PTR_ERR(fid); | ||
104 | goto out_error; | ||
105 | } | ||
106 | v9inode->writeback_fid = (void *) fid; | ||
107 | } | ||
100 | #ifdef CONFIG_9P_FSCACHE | 108 | #ifdef CONFIG_9P_FSCACHE |
109 | if (v9ses->cache) | ||
101 | v9fs_cache_inode_set_cookie(inode, file); | 110 | v9fs_cache_inode_set_cookie(inode, file); |
102 | #endif | 111 | #endif |
103 | } | ||
104 | |||
105 | return 0; | 112 | return 0; |
113 | out_error: | ||
114 | p9_client_clunk(file->private_data); | ||
115 | file->private_data = NULL; | ||
116 | return err; | ||
106 | } | 117 | } |
107 | 118 | ||
108 | /** | 119 | /** |
@@ -335,25 +346,22 @@ out_err: | |||
335 | } | 346 | } |
336 | 347 | ||
337 | /** | 348 | /** |
338 | * v9fs_file_readn - read from a file | 349 | * v9fs_fid_readn - read from a fid |
339 | * @filp: file pointer to read | 350 | * @fid: fid to read |
340 | * @data: data buffer to read data into | 351 | * @data: data buffer to read data into |
341 | * @udata: user data buffer to read data into | 352 | * @udata: user data buffer to read data into |
342 | * @count: size of buffer | 353 | * @count: size of buffer |
343 | * @offset: offset at which to read data | 354 | * @offset: offset at which to read data |
344 | * | 355 | * |
345 | */ | 356 | */ |
346 | |||
347 | ssize_t | 357 | ssize_t |
348 | v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, | 358 | v9fs_fid_readn(struct p9_fid *fid, char *data, char __user *udata, u32 count, |
349 | u64 offset) | 359 | u64 offset) |
350 | { | 360 | { |
351 | int n, total, size; | 361 | int n, total, size; |
352 | struct p9_fid *fid = filp->private_data; | ||
353 | 362 | ||
354 | P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid, | 363 | P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid, |
355 | (long long unsigned) offset, count); | 364 | (long long unsigned) offset, count); |
356 | |||
357 | n = 0; | 365 | n = 0; |
358 | total = 0; | 366 | total = 0; |
359 | size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; | 367 | size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; |
@@ -379,6 +387,22 @@ v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, | |||
379 | } | 387 | } |
380 | 388 | ||
381 | /** | 389 | /** |
390 | * v9fs_file_readn - read from a file | ||
391 | * @filp: file pointer to read | ||
392 | * @data: data buffer to read data into | ||
393 | * @udata: user data buffer to read data into | ||
394 | * @count: size of buffer | ||
395 | * @offset: offset at which to read data | ||
396 | * | ||
397 | */ | ||
398 | ssize_t | ||
399 | v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, | ||
400 | u64 offset) | ||
401 | { | ||
402 | return v9fs_fid_readn(filp->private_data, data, udata, count, offset); | ||
403 | } | ||
404 | |||
405 | /** | ||
382 | * v9fs_file_read - read from a file | 406 | * v9fs_file_read - read from a file |
383 | * @filp: file pointer to read | 407 | * @filp: file pointer to read |
384 | * @udata: user data buffer to read data into | 408 | * @udata: user data buffer to read data into |
@@ -410,45 +434,22 @@ v9fs_file_read(struct file *filp, char __user *udata, size_t count, | |||
410 | return ret; | 434 | return ret; |
411 | } | 435 | } |
412 | 436 | ||
413 | /** | 437 | ssize_t |
414 | * v9fs_file_write - write to a file | 438 | v9fs_file_write_internal(struct inode *inode, struct p9_fid *fid, |
415 | * @filp: file pointer to write | 439 | const char __user *data, size_t count, |
416 | * @data: data buffer to write data from | 440 | loff_t *offset, int invalidate) |
417 | * @count: size of buffer | ||
418 | * @offset: offset at which to write data | ||
419 | * | ||
420 | */ | ||
421 | |||
422 | static ssize_t | ||
423 | v9fs_file_write(struct file *filp, const char __user * data, | ||
424 | size_t count, loff_t * offset) | ||
425 | { | 441 | { |
426 | ssize_t retval; | ||
427 | size_t total = 0; | ||
428 | int n; | 442 | int n; |
429 | struct p9_fid *fid; | 443 | loff_t i_size; |
444 | size_t total = 0; | ||
430 | struct p9_client *clnt; | 445 | struct p9_client *clnt; |
431 | struct inode *inode = filp->f_path.dentry->d_inode; | ||
432 | loff_t origin = *offset; | 446 | loff_t origin = *offset; |
433 | unsigned long pg_start, pg_end; | 447 | unsigned long pg_start, pg_end; |
434 | 448 | ||
435 | P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data, | 449 | P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data, |
436 | (int)count, (int)*offset); | 450 | (int)count, (int)*offset); |
437 | 451 | ||
438 | fid = filp->private_data; | ||
439 | clnt = fid->clnt; | 452 | clnt = fid->clnt; |
440 | |||
441 | retval = generic_write_checks(filp, &origin, &count, 0); | ||
442 | if (retval) | ||
443 | goto out; | ||
444 | |||
445 | retval = -EINVAL; | ||
446 | if ((ssize_t) count < 0) | ||
447 | goto out; | ||
448 | retval = 0; | ||
449 | if (!count) | ||
450 | goto out; | ||
451 | |||
452 | do { | 453 | do { |
453 | n = p9_client_write(fid, NULL, data+total, origin+total, count); | 454 | n = p9_client_write(fid, NULL, data+total, origin+total, count); |
454 | if (n <= 0) | 455 | if (n <= 0) |
@@ -457,25 +458,60 @@ v9fs_file_write(struct file *filp, const char __user * data, | |||
457 | total += n; | 458 | total += n; |
458 | } while (count > 0); | 459 | } while (count > 0); |
459 | 460 | ||
460 | if (total > 0) { | 461 | if (invalidate && (total > 0)) { |
461 | pg_start = origin >> PAGE_CACHE_SHIFT; | 462 | pg_start = origin >> PAGE_CACHE_SHIFT; |
462 | pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT; | 463 | pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT; |
463 | if (inode->i_mapping && inode->i_mapping->nrpages) | 464 | if (inode->i_mapping && inode->i_mapping->nrpages) |
464 | invalidate_inode_pages2_range(inode->i_mapping, | 465 | invalidate_inode_pages2_range(inode->i_mapping, |
465 | pg_start, pg_end); | 466 | pg_start, pg_end); |
466 | *offset += total; | 467 | *offset += total; |
467 | i_size_write(inode, i_size_read(inode) + total); | 468 | i_size = i_size_read(inode); |
468 | inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9; | 469 | if (*offset > i_size) { |
470 | inode_add_bytes(inode, *offset - i_size); | ||
471 | i_size_write(inode, *offset); | ||
472 | } | ||
469 | } | 473 | } |
470 | |||
471 | if (n < 0) | 474 | if (n < 0) |
472 | retval = n; | 475 | return n; |
473 | else | 476 | |
474 | retval = total; | 477 | return total; |
478 | } | ||
479 | |||
480 | /** | ||
481 | * v9fs_file_write - write to a file | ||
482 | * @filp: file pointer to write | ||
483 | * @data: data buffer to write data from | ||
484 | * @count: size of buffer | ||
485 | * @offset: offset at which to write data | ||
486 | * | ||
487 | */ | ||
488 | static ssize_t | ||
489 | v9fs_file_write(struct file *filp, const char __user * data, | ||
490 | size_t count, loff_t *offset) | ||
491 | { | ||
492 | ssize_t retval = 0; | ||
493 | loff_t origin = *offset; | ||
494 | |||
495 | |||
496 | retval = generic_write_checks(filp, &origin, &count, 0); | ||
497 | if (retval) | ||
498 | goto out; | ||
499 | |||
500 | retval = -EINVAL; | ||
501 | if ((ssize_t) count < 0) | ||
502 | goto out; | ||
503 | retval = 0; | ||
504 | if (!count) | ||
505 | goto out; | ||
506 | |||
507 | return v9fs_file_write_internal(filp->f_path.dentry->d_inode, | ||
508 | filp->private_data, | ||
509 | data, count, offset, 1); | ||
475 | out: | 510 | out: |
476 | return retval; | 511 | return retval; |
477 | } | 512 | } |
478 | 513 | ||
514 | |||
479 | static int v9fs_file_fsync(struct file *filp, int datasync) | 515 | static int v9fs_file_fsync(struct file *filp, int datasync) |
480 | { | 516 | { |
481 | struct p9_fid *fid; | 517 | struct p9_fid *fid; |
@@ -505,28 +541,182 @@ int v9fs_file_fsync_dotl(struct file *filp, int datasync) | |||
505 | return retval; | 541 | return retval; |
506 | } | 542 | } |
507 | 543 | ||
508 | static const struct file_operations v9fs_cached_file_operations = { | 544 | static int |
545 | v9fs_file_mmap(struct file *file, struct vm_area_struct *vma) | ||
546 | { | ||
547 | int retval; | ||
548 | |||
549 | retval = generic_file_mmap(file, vma); | ||
550 | if (!retval) | ||
551 | vma->vm_ops = &v9fs_file_vm_ops; | ||
552 | |||
553 | return retval; | ||
554 | } | ||
555 | |||
556 | static int | ||
557 | v9fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
558 | { | ||
559 | struct v9fs_inode *v9inode; | ||
560 | struct page *page = vmf->page; | ||
561 | struct file *filp = vma->vm_file; | ||
562 | struct inode *inode = filp->f_path.dentry->d_inode; | ||
563 | |||
564 | |||
565 | P9_DPRINTK(P9_DEBUG_VFS, "page %p fid %lx\n", | ||
566 | page, (unsigned long)filp->private_data); | ||
567 | |||
568 | v9inode = V9FS_I(inode); | ||
569 | /* make sure the cache has finished storing the page */ | ||
570 | v9fs_fscache_wait_on_page_write(inode, page); | ||
571 | BUG_ON(!v9inode->writeback_fid); | ||
572 | lock_page(page); | ||
573 | if (page->mapping != inode->i_mapping) | ||
574 | goto out_unlock; | ||
575 | |||
576 | return VM_FAULT_LOCKED; | ||
577 | out_unlock: | ||
578 | unlock_page(page); | ||
579 | return VM_FAULT_NOPAGE; | ||
580 | } | ||
581 | |||
582 | static ssize_t | ||
583 | v9fs_direct_read(struct file *filp, char __user *udata, size_t count, | ||
584 | loff_t *offsetp) | ||
585 | { | ||
586 | loff_t size, offset; | ||
587 | struct inode *inode; | ||
588 | struct address_space *mapping; | ||
589 | |||
590 | offset = *offsetp; | ||
591 | mapping = filp->f_mapping; | ||
592 | inode = mapping->host; | ||
593 | if (!count) | ||
594 | return 0; | ||
595 | size = i_size_read(inode); | ||
596 | if (offset < size) | ||
597 | filemap_write_and_wait_range(mapping, offset, | ||
598 | offset + count - 1); | ||
599 | |||
600 | return v9fs_file_read(filp, udata, count, offsetp); | ||
601 | } | ||
602 | |||
603 | /** | ||
604 | * v9fs_cached_file_read - read from a file | ||
605 | * @filp: file pointer to read | ||
606 | * @udata: user data buffer to read data into | ||
607 | * @count: size of buffer | ||
608 | * @offset: offset at which to read data | ||
609 | * | ||
610 | */ | ||
611 | static ssize_t | ||
612 | v9fs_cached_file_read(struct file *filp, char __user *data, size_t count, | ||
613 | loff_t *offset) | ||
614 | { | ||
615 | if (filp->f_flags & O_DIRECT) | ||
616 | return v9fs_direct_read(filp, data, count, offset); | ||
617 | return do_sync_read(filp, data, count, offset); | ||
618 | } | ||
619 | |||
620 | static ssize_t | ||
621 | v9fs_direct_write(struct file *filp, const char __user * data, | ||
622 | size_t count, loff_t *offsetp) | ||
623 | { | ||
624 | loff_t offset; | ||
625 | ssize_t retval; | ||
626 | struct inode *inode; | ||
627 | struct address_space *mapping; | ||
628 | |||
629 | offset = *offsetp; | ||
630 | mapping = filp->f_mapping; | ||
631 | inode = mapping->host; | ||
632 | if (!count) | ||
633 | return 0; | ||
634 | |||
635 | mutex_lock(&inode->i_mutex); | ||
636 | retval = filemap_write_and_wait_range(mapping, offset, | ||
637 | offset + count - 1); | ||
638 | if (retval) | ||
639 | goto err_out; | ||
640 | /* | ||
641 | * After a write we want buffered reads to be sure to go to disk to get | ||
642 | * the new data. We invalidate clean cached page from the region we're | ||
643 | * about to write. We do this *before* the write so that if we fail | ||
644 | * here we fall back to buffered write | ||
645 | */ | ||
646 | if (mapping->nrpages) { | ||
647 | pgoff_t pg_start = offset >> PAGE_CACHE_SHIFT; | ||
648 | pgoff_t pg_end = (offset + count - 1) >> PAGE_CACHE_SHIFT; | ||
649 | |||
650 | retval = invalidate_inode_pages2_range(mapping, | ||
651 | pg_start, pg_end); | ||
652 | /* | ||
653 | * If a page can not be invalidated, fall back | ||
654 | * to buffered write. | ||
655 | */ | ||
656 | if (retval) { | ||
657 | if (retval == -EBUSY) | ||
658 | goto buff_write; | ||
659 | goto err_out; | ||
660 | } | ||
661 | } | ||
662 | retval = v9fs_file_write(filp, data, count, offsetp); | ||
663 | err_out: | ||
664 | mutex_unlock(&inode->i_mutex); | ||
665 | return retval; | ||
666 | |||
667 | buff_write: | ||
668 | mutex_unlock(&inode->i_mutex); | ||
669 | return do_sync_write(filp, data, count, offsetp); | ||
670 | } | ||
671 | |||
672 | /** | ||
673 | * v9fs_cached_file_write - write to a file | ||
674 | * @filp: file pointer to write | ||
675 | * @data: data buffer to write data from | ||
676 | * @count: size of buffer | ||
677 | * @offset: offset at which to write data | ||
678 | * | ||
679 | */ | ||
680 | static ssize_t | ||
681 | v9fs_cached_file_write(struct file *filp, const char __user * data, | ||
682 | size_t count, loff_t *offset) | ||
683 | { | ||
684 | |||
685 | if (filp->f_flags & O_DIRECT) | ||
686 | return v9fs_direct_write(filp, data, count, offset); | ||
687 | return do_sync_write(filp, data, count, offset); | ||
688 | } | ||
689 | |||
690 | static const struct vm_operations_struct v9fs_file_vm_ops = { | ||
691 | .fault = filemap_fault, | ||
692 | .page_mkwrite = v9fs_vm_page_mkwrite, | ||
693 | }; | ||
694 | |||
695 | |||
696 | const struct file_operations v9fs_cached_file_operations = { | ||
509 | .llseek = generic_file_llseek, | 697 | .llseek = generic_file_llseek, |
510 | .read = do_sync_read, | 698 | .read = v9fs_cached_file_read, |
699 | .write = v9fs_cached_file_write, | ||
511 | .aio_read = generic_file_aio_read, | 700 | .aio_read = generic_file_aio_read, |
512 | .write = v9fs_file_write, | 701 | .aio_write = generic_file_aio_write, |
513 | .open = v9fs_file_open, | 702 | .open = v9fs_file_open, |
514 | .release = v9fs_dir_release, | 703 | .release = v9fs_dir_release, |
515 | .lock = v9fs_file_lock, | 704 | .lock = v9fs_file_lock, |
516 | .mmap = generic_file_readonly_mmap, | 705 | .mmap = v9fs_file_mmap, |
517 | .fsync = v9fs_file_fsync, | 706 | .fsync = v9fs_file_fsync, |
518 | }; | 707 | }; |
519 | 708 | ||
520 | static const struct file_operations v9fs_cached_file_operations_dotl = { | 709 | const struct file_operations v9fs_cached_file_operations_dotl = { |
521 | .llseek = generic_file_llseek, | 710 | .llseek = generic_file_llseek, |
522 | .read = do_sync_read, | 711 | .read = v9fs_cached_file_read, |
712 | .write = v9fs_cached_file_write, | ||
523 | .aio_read = generic_file_aio_read, | 713 | .aio_read = generic_file_aio_read, |
524 | .write = v9fs_file_write, | 714 | .aio_write = generic_file_aio_write, |
525 | .open = v9fs_file_open, | 715 | .open = v9fs_file_open, |
526 | .release = v9fs_dir_release, | 716 | .release = v9fs_dir_release, |
527 | .lock = v9fs_file_lock_dotl, | 717 | .lock = v9fs_file_lock_dotl, |
528 | .flock = v9fs_file_flock_dotl, | 718 | .flock = v9fs_file_flock_dotl, |
529 | .mmap = generic_file_readonly_mmap, | 719 | .mmap = v9fs_file_mmap, |
530 | .fsync = v9fs_file_fsync_dotl, | 720 | .fsync = v9fs_file_fsync_dotl, |
531 | }; | 721 | }; |
532 | 722 | ||