diff options
Diffstat (limited to 'fs/afs/file.c')
-rw-r--r-- | fs/afs/file.c | 194 |
1 files changed, 154 insertions, 40 deletions
diff --git a/fs/afs/file.c b/fs/afs/file.c index 510cba15fa56..a39192ced99e 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -19,11 +19,11 @@ #include <linux/task_io_accounting_ops.h> #include "internal.h" +static int afs_file_mmap(struct file *file, struct vm_area_struct *vma); static int afs_readpage(struct file *file, struct page *page); static void afs_invalidatepage(struct page *page, unsigned int offset, unsigned int length); static int afs_releasepage(struct page *page, gfp_t gfp_flags); -static int afs_launder_page(struct page *page); static int afs_readpages(struct file *filp, struct address_space *mapping, struct list_head *pages, unsigned nr_pages); @@ -35,7 +35,7 @@ const struct file_operations afs_file_operations = { .llseek = generic_file_llseek, .read_iter = generic_file_read_iter, .write_iter = afs_file_write, - .mmap = generic_file_readonly_mmap, + .mmap = afs_file_mmap, .splice_read = generic_file_splice_read, .fsync = afs_fsync, .lock = afs_lock, @@ -62,12 +62,63 @@ const struct address_space_operations afs_fs_aops = { .writepages = afs_writepages, }; +static const struct vm_operations_struct afs_vm_ops = { + .fault = filemap_fault, + .map_pages = filemap_map_pages, + .page_mkwrite = afs_page_mkwrite, +}; + +/* + * Discard a pin on a writeback key. + */ +void afs_put_wb_key(struct afs_wb_key *wbk) +{ + if (refcount_dec_and_test(&wbk->usage)) { + key_put(wbk->key); + kfree(wbk); + } +} + +/* + * Cache key for writeback. + */ +int afs_cache_wb_key(struct afs_vnode *vnode, struct afs_file *af) +{ + struct afs_wb_key *wbk, *p; + + wbk = kzalloc(sizeof(struct afs_wb_key), GFP_KERNEL); + if (!wbk) + return -ENOMEM; + refcount_set(&wbk->usage, 2); + wbk->key = af->key; + + spin_lock(&vnode->wb_lock); + list_for_each_entry(p, &vnode->wb_keys, vnode_link) { + if (p->key == wbk->key) + goto found; + } + + key_get(wbk->key); + list_add_tail(&wbk->vnode_link, &vnode->wb_keys); + spin_unlock(&vnode->wb_lock); + af->wb = wbk; + return 0; + +found: + refcount_inc(&p->usage); + spin_unlock(&vnode->wb_lock); + af->wb = p; + kfree(wbk); + return 0; +} + /* * open an AFS file or directory and attach a key to it */ int afs_open(struct inode *inode, struct file *file) { struct afs_vnode *vnode = AFS_FS_I(inode); + struct afs_file *af; struct key *key; int ret; @@ -75,19 +126,38 @@ int afs_open(struct inode *inode, struct file *file) key = afs_request_key(vnode->volume->cell); if (IS_ERR(key)) { - _leave(" = %ld [key]", PTR_ERR(key)); - return PTR_ERR(key); + ret = PTR_ERR(key); + goto error; } - ret = afs_validate(vnode, key); - if (ret < 0) { - _leave(" = %d [val]", ret); - return ret; + af = kzalloc(sizeof(*af), GFP_KERNEL); + if (!af) { + ret = -ENOMEM; + goto error_key; } + af->key = key; + + ret = afs_validate(vnode, key); + if (ret < 0) + goto error_af; - file->private_data = key; + if (file->f_mode & FMODE_WRITE) { + ret = afs_cache_wb_key(vnode, af); + if (ret < 0) + goto error_af; + } + + file->private_data = af; _leave(" = 0"); return 0; + +error_af: + kfree(af); +error_key: + key_put(key); +error: + _leave(" = %d", ret); + return ret; } /* @@ -96,10 +166,16 @@ int afs_open(struct inode *inode, struct file *file) int afs_release(struct inode *inode, struct file *file) { struct afs_vnode *vnode = AFS_FS_I(inode); + struct afs_file *af = file->private_data; _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode); - key_put(file->private_data); + file->private_data = NULL; + if (af->wb) + afs_put_wb_key(af->wb); + key_put(af->key); + kfree(af); + afs_prune_wb_keys(vnode); _leave(" = 0"); return 0; } @@ -138,6 +214,37 @@ static void afs_file_readpage_read_complete(struct page *page, #endif /* + * Fetch file data from the volume. + */ +int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *desc) +{ + struct afs_fs_cursor fc; + int ret; + + _enter("%s{%x:%u.%u},%x,,,", + vnode->volume->name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(key)); + + ret = -ERESTARTSYS; + if (afs_begin_vnode_operation(&fc, vnode, key)) { + while (afs_select_fileserver(&fc)) { + fc.cb_break = vnode->cb_break + vnode->cb_s_break; + afs_fs_fetch_data(&fc, desc); + } + + afs_check_for_remote_deletion(&fc, fc.vnode); + afs_vnode_commit_status(&fc, vnode, fc.cb_break); + ret = afs_end_vnode_operation(&fc); + } + + _leave(" = %d", ret); + return ret; +} + +/* * read page from file, directory or symlink, given a key to use */ int afs_page_filler(void *data, struct page *page) @@ -199,8 +306,13 @@ int afs_page_filler(void *data, struct page *page) /* read the contents of the file from the server into the * page */ - ret = afs_vnode_fetch_data(vnode, key, req); + ret = afs_fetch_data(vnode, key, req); afs_put_read(req); + + if (ret >= 0 && S_ISDIR(inode->i_mode) && + !afs_dir_check_page(inode, page)) + ret = -EIO; + if (ret < 0) { if (ret == -ENOENT) { _debug("got NOENT from server" @@ -259,12 +371,12 @@ static int afs_readpage(struct file *file, struct page *page) int ret; if (file) { - key = file->private_data; + key = afs_file_key(file); ASSERT(key != NULL); ret = afs_page_filler(key, page); } else { struct inode *inode = page->mapping->host; - key = afs_request_key(AFS_FS_S(inode->i_sb)->volume->cell); + key = afs_request_key(AFS_FS_S(inode->i_sb)->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); } else { @@ -281,7 +393,7 @@ static int afs_readpage(struct file *file, struct page *page) static void afs_readpages_page_done(struct afs_call *call, struct afs_read *req) { #ifdef CONFIG_AFS_FSCACHE - struct afs_vnode *vnode = call->reply; + struct afs_vnode *vnode = call->reply[0]; #endif struct page *page = req->pages[req->index]; @@ -310,7 +422,7 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping, struct afs_read *req; struct list_head *p; struct page *first, *page; - struct key *key = file->private_data; + struct key *key = afs_file_key(file); pgoff_t index; int ret, n, i; @@ -369,7 +481,7 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping, return 0; } - ret = afs_vnode_fetch_data(vnode, key, req); + ret = afs_fetch_data(vnode, key, req); if (ret < 0) goto error; @@ -406,7 +518,7 @@ error: static int afs_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { - struct key *key = file->private_data; + struct key *key = afs_file_key(file); struct afs_vnode *vnode; int ret = 0; @@ -464,16 +576,6 @@ static int afs_readpages(struct file *file, struct address_space *mapping, } /* - * write back a dirty page - */ -static int afs_launder_page(struct page *page) -{ - _enter("{%lu}", page->index); - - return 0; -} - -/* * invalidate part or all of a page * - release a page and clean up its private data if offset is 0 (indicating * the entire page) @@ -481,7 +583,8 @@ static int afs_launder_page(struct page *page) static void afs_invalidatepage(struct page *page, unsigned int offset, unsigned int length) { - struct afs_writeback *wb = (struct afs_writeback *) page_private(page); + struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); + unsigned long priv; _enter("{%lu},%u,%u", page->index, offset, length); @@ -498,13 +601,11 @@ static void afs_invalidatepage(struct page *page, unsigned int offset, #endif if (PagePrivate(page)) { - if (wb && !PageWriteback(page)) { - set_page_private(page, 0); - afs_put_writeback(wb); - } - - if (!page_private(page)) - ClearPagePrivate(page); + priv = page_private(page); + trace_afs_page_dirty(vnode, tracepoint_string("inval"), + page->index, priv); + set_page_private(page, 0); + ClearPagePrivate(page); } } @@ -517,8 +618,8 @@ static void afs_invalidatepage(struct page *page, unsigned int offset, */ static int afs_releasepage(struct page *page, gfp_t gfp_flags) { - struct afs_writeback *wb = (struct afs_writeback *) page_private(page); struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); + unsigned long priv; _enter("{{%x:%u}[%lu],%lx},%x", vnode->fid.vid, vnode->fid.vnode, page->index, page->flags, @@ -534,10 +635,10 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags) #endif if (PagePrivate(page)) { - if (wb) { - set_page_private(page, 0); - afs_put_writeback(wb); - } + priv = page_private(page); + trace_afs_page_dirty(vnode, tracepoint_string("rel"), + page->index, priv); + set_page_private(page, 0); ClearPagePrivate(page); } @@ -545,3 +646,16 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags) _leave(" = T"); return 1; } + +/* + * Handle setting up a memory mapping on an AFS file. + */ +static int afs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret; + + ret = generic_file_mmap(file, vma); + if (ret == 0) + vma->vm_ops = &afs_vm_ops; + return ret; +} |