Implement shared-writable mmap for AFS. The key with which to access the file is obtained from the VMA at the point where the PTE is made writable by the page_mkwrite() VMA op and cached in the affected page. If there's an outstanding write on the page made with a different key, then page_mkwrite() will flush it before attaching a record of the new key. [try #2] Only flush the page if the page is still part of the mapping (truncate may have discarded it). Signed-off-by: David Howells <dhowells@xxxxxxxxxx> --- fs/afs/file.c | 22 ++++++++++++++++++++-- fs/afs/internal.h | 1 + fs/afs/write.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/fs/afs/file.c b/fs/afs/file.c index 9c0e721..da2a18b 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -22,6 +22,7 @@ static int afs_readpage(struct file *file, struct page *page); static void afs_invalidatepage(struct page *page, unsigned long offset); static int afs_releasepage(struct page *page, gfp_t gfp_flags); static int afs_launder_page(struct page *page); +static int afs_mmap(struct file *file, struct vm_area_struct *vma); const struct file_operations afs_file_operations = { .open = afs_open, @@ -31,7 +32,7 @@ const struct file_operations afs_file_operations = { .write = do_sync_write, .aio_read = generic_file_aio_read, .aio_write = afs_file_write, - .mmap = generic_file_readonly_mmap, + .mmap = afs_mmap, .sendfile = generic_file_sendfile, .fsync = afs_fsync, }; @@ -54,6 +55,12 @@ const struct address_space_operations afs_fs_aops = { .writepages = afs_writepages, }; +static struct vm_operations_struct afs_file_vm_ops = { + .nopage = filemap_nopage, + .populate = filemap_populate, + .page_mkwrite = afs_page_mkwrite, +}; + /* * open an AFS file or directory and attach a key to it */ @@ -266,7 +273,6 @@ static void afs_invalidatepage(struct page *page, unsigned long offset) static int afs_launder_page(struct page *page) { _enter("{%lu}", page->index); - return 0; } @@ -293,3 +299,15 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags) _leave(" = 0"); return 0; } + +/* + * memory map part of an AFS file + */ +static int afs_mmap(struct file *file, struct vm_area_struct *vma) +{ + _enter(""); + + file_accessed(file); + vma->vm_ops = &afs_file_vm_ops; + return 0; +} diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 4953ba5..bf4ef07 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -709,6 +709,7 @@ extern ssize_t afs_file_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern int afs_writeback_all(struct afs_vnode *); extern int afs_fsync(struct file *, struct dentry *, int); +extern int afs_page_mkwrite(struct vm_area_struct *, struct page *); /*****************************************************************************/ diff --git a/fs/afs/write.c b/fs/afs/write.c index a03b92a..208fbc0 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -174,6 +174,8 @@ static int afs_prepare_page(struct afs_vnode *vnode, struct page *page, * prepare to perform part of a write to a page * - the caller holds the page locked, preventing it from being written out or * modified by anyone else + * - may be called from afs_page_mkwrite() to set up a page for modification + * through shared-writable mmap */ int afs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) @@ -825,3 +827,33 @@ int afs_fsync(struct file *file, struct dentry *dentry, int datasync) _leave(" = %d", ret); return ret; } + +/* + * notification that a previously read-only page is about to become writable + * - if it returns an error, the caller will deliver a bus error signal + * + * we use this to make a record of the key with which the writeback should be + * performed and to flush any outstanding writes made with a different key + * + * the key to be used is attached to the file pinned by the VMA + */ +int afs_page_mkwrite(struct vm_area_struct *vma, struct page *page) +{ + struct afs_vnode *vnode = AFS_FS_I(vma->vm_file->f_mapping->host); + struct key *key = vma->vm_file->private_data; + int ret; + + _enter("{{%x:%u},%x},{%lx}", + vnode->fid.vid, vnode->fid.vnode, key_serial(key), page->index); + + lock_page(page); + if (page->mapping == vma->vm_file->f_mapping) + ret = afs_prepare_write(vma->vm_file, page, 0, 0); + else + ret = 0; /* seems truncate interfered - let the caller deal + * with it (presumably the PTE changed too) */ + unlock_page(page); + + _leave(" = %d", ret); + return ret; +} - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html