diff options
| author | David Howells <dhowells@redhat.com> | 2017-11-02 11:27:52 -0400 |
|---|---|---|
| committer | David Howells <dhowells@redhat.com> | 2017-11-13 10:38:21 -0500 |
| commit | 1cf7a1518aefa69ac6ba0c3f9206073e4221e3c8 (patch) | |
| tree | 17788e9c0e145c336761adedf27460daccc76887 | |
| parent | 4343d00872e1de9a470d951bf09bdd18bc73f555 (diff) | |
afs: Implement shared-writeable mmap
Implement shared-writeable mmap for AFS.
Signed-off-by: David Howells <dhowells@redhat.com>
| -rw-r--r-- | fs/afs/file.c | 22 | ||||
| -rw-r--r-- | fs/afs/internal.h | 1 | ||||
| -rw-r--r-- | fs/afs/write.c | 40 |
3 files changed, 54 insertions, 9 deletions
diff --git a/fs/afs/file.c b/fs/afs/file.c index c3a7bc1281f5..675c5c268a52 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/task_io_accounting_ops.h> | 19 | #include <linux/task_io_accounting_ops.h> |
| 20 | #include "internal.h" | 20 | #include "internal.h" |
| 21 | 21 | ||
| 22 | static int afs_file_mmap(struct file *file, struct vm_area_struct *vma); | ||
| 22 | static int afs_readpage(struct file *file, struct page *page); | 23 | static int afs_readpage(struct file *file, struct page *page); |
| 23 | static void afs_invalidatepage(struct page *page, unsigned int offset, | 24 | static void afs_invalidatepage(struct page *page, unsigned int offset, |
| 24 | unsigned int length); | 25 | unsigned int length); |
| @@ -34,7 +35,7 @@ const struct file_operations afs_file_operations = { | |||
| 34 | .llseek = generic_file_llseek, | 35 | .llseek = generic_file_llseek, |
| 35 | .read_iter = generic_file_read_iter, | 36 | .read_iter = generic_file_read_iter, |
| 36 | .write_iter = afs_file_write, | 37 | .write_iter = afs_file_write, |
| 37 | .mmap = generic_file_readonly_mmap, | 38 | .mmap = afs_file_mmap, |
| 38 | .splice_read = generic_file_splice_read, | 39 | .splice_read = generic_file_splice_read, |
| 39 | .fsync = afs_fsync, | 40 | .fsync = afs_fsync, |
| 40 | .lock = afs_lock, | 41 | .lock = afs_lock, |
| @@ -61,6 +62,12 @@ const struct address_space_operations afs_fs_aops = { | |||
| 61 | .writepages = afs_writepages, | 62 | .writepages = afs_writepages, |
| 62 | }; | 63 | }; |
| 63 | 64 | ||
| 65 | static const struct vm_operations_struct afs_vm_ops = { | ||
| 66 | .fault = filemap_fault, | ||
| 67 | .map_pages = filemap_map_pages, | ||
| 68 | .page_mkwrite = afs_page_mkwrite, | ||
| 69 | }; | ||
| 70 | |||
| 64 | /* | 71 | /* |
| 65 | * Discard a pin on a writeback key. | 72 | * Discard a pin on a writeback key. |
| 66 | */ | 73 | */ |
| @@ -629,3 +636,16 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags) | |||
| 629 | _leave(" = T"); | 636 | _leave(" = T"); |
| 630 | return 1; | 637 | return 1; |
| 631 | } | 638 | } |
| 639 | |||
| 640 | /* | ||
| 641 | * Handle setting up a memory mapping on an AFS file. | ||
| 642 | */ | ||
| 643 | static int afs_file_mmap(struct file *file, struct vm_area_struct *vma) | ||
| 644 | { | ||
| 645 | int ret; | ||
| 646 | |||
| 647 | ret = generic_file_mmap(file, vma); | ||
| 648 | if (ret == 0) | ||
| 649 | vma->vm_ops = &afs_vm_ops; | ||
| 650 | return ret; | ||
| 651 | } | ||
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 688562ae3bf8..1de36e6abd5e 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
| @@ -886,6 +886,7 @@ extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *); | |||
| 886 | extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *); | 886 | extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *); |
| 887 | extern int afs_flush(struct file *, fl_owner_t); | 887 | extern int afs_flush(struct file *, fl_owner_t); |
| 888 | extern int afs_fsync(struct file *, loff_t, loff_t, int); | 888 | extern int afs_fsync(struct file *, loff_t, loff_t, int); |
| 889 | extern int afs_page_mkwrite(struct vm_fault *); | ||
| 889 | extern void afs_prune_wb_keys(struct afs_vnode *); | 890 | extern void afs_prune_wb_keys(struct afs_vnode *); |
| 890 | extern int afs_launder_page(struct page *); | 891 | extern int afs_launder_page(struct page *); |
| 891 | 892 | ||
diff --git a/fs/afs/write.c b/fs/afs/write.c index 4c131371005b..6807277ef956 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c | |||
| @@ -749,21 +749,45 @@ int afs_flush(struct file *file, fl_owner_t id) | |||
| 749 | * notification that a previously read-only page is about to become writable | 749 | * notification that a previously read-only page is about to become writable |
| 750 | * - if it returns an error, the caller will deliver a bus error signal | 750 | * - if it returns an error, the caller will deliver a bus error signal |
| 751 | */ | 751 | */ |
| 752 | int afs_page_mkwrite(struct vm_area_struct *vma, struct page *page) | 752 | int afs_page_mkwrite(struct vm_fault *vmf) |
| 753 | { | 753 | { |
| 754 | struct afs_vnode *vnode = AFS_FS_I(vma->vm_file->f_mapping->host); | 754 | struct file *file = vmf->vma->vm_file; |
| 755 | struct inode *inode = file_inode(file); | ||
| 756 | struct afs_vnode *vnode = AFS_FS_I(inode); | ||
| 757 | unsigned long priv; | ||
| 755 | 758 | ||
| 756 | _enter("{{%x:%u}},{%lx}", | 759 | _enter("{{%x:%u}},{%lx}", |
| 757 | vnode->fid.vid, vnode->fid.vnode, page->index); | 760 | vnode->fid.vid, vnode->fid.vnode, vmf->page->index); |
| 758 | 761 | ||
| 759 | /* wait for the page to be written to the cache before we allow it to | 762 | sb_start_pagefault(inode->i_sb); |
| 760 | * be modified */ | 763 | |
| 764 | /* Wait for the page to be written to the cache before we allow it to | ||
| 765 | * be modified. We then assume the entire page will need writing back. | ||
| 766 | */ | ||
| 761 | #ifdef CONFIG_AFS_FSCACHE | 767 | #ifdef CONFIG_AFS_FSCACHE |
| 762 | fscache_wait_on_page_write(vnode->cache, page); | 768 | fscache_wait_on_page_write(vnode->cache, vmf->page); |
| 763 | #endif | 769 | #endif |
| 764 | 770 | ||
| 765 | _leave(" = 0"); | 771 | if (PageWriteback(vmf->page) && |
| 766 | return 0; | 772 | wait_on_page_bit_killable(vmf->page, PG_writeback) < 0) |
| 773 | return VM_FAULT_RETRY; | ||
| 774 | |||
| 775 | if (lock_page_killable(vmf->page) < 0) | ||
| 776 | return VM_FAULT_RETRY; | ||
| 777 | |||
| 778 | /* We mustn't change page->private until writeback is complete as that | ||
| 779 | * details the portion of the page we need to write back and we might | ||
| 780 | * need to redirty the page if there's a problem. | ||
| 781 | */ | ||
| 782 | wait_on_page_writeback(vmf->page); | ||
| 783 | |||
| 784 | priv = (unsigned long)PAGE_SIZE << AFS_PRIV_SHIFT; /* To */ | ||
| 785 | priv |= 0; /* From */ | ||
| 786 | SetPagePrivate(vmf->page); | ||
| 787 | set_page_private(vmf->page, priv); | ||
| 788 | |||
| 789 | sb_end_pagefault(inode->i_sb); | ||
| 790 | return VM_FAULT_LOCKED; | ||
| 767 | } | 791 | } |
| 768 | 792 | ||
| 769 | /* | 793 | /* |
