summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/afs/file.c19
-rw-r--r--fs/afs/internal.h1
-rw-r--r--fs/afs/write.c108
3 files changed, 31 insertions, 97 deletions
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 10c6eaaac2cc..db035ae2a134 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -333,6 +333,13 @@ static void afs_init_rreq(struct netfs_read_request *rreq, struct file *file)
rreq->netfs_priv = key_get(afs_file_key(file));
}
+static bool afs_is_cache_enabled(struct inode *inode)
+{
+ struct fscache_cookie *cookie = afs_vnode_cache(AFS_FS_I(inode));
+
+ return fscache_cookie_enabled(cookie) && !hlist_empty(&cookie->backing_objects);
+}
+
static int afs_begin_cache_operation(struct netfs_read_request *rreq)
{
struct afs_vnode *vnode = AFS_FS_I(rreq->inode);
@@ -340,14 +347,24 @@ static int afs_begin_cache_operation(struct netfs_read_request *rreq)
return fscache_begin_read_operation(rreq, afs_vnode_cache(vnode));
}
+static int afs_check_write_begin(struct file *file, loff_t pos, unsigned len,
+ struct page *page, void **_fsdata)
+{
+ struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
+
+ return test_bit(AFS_VNODE_DELETED, &vnode->flags) ? -ESTALE : 0;
+}
+
static void afs_priv_cleanup(struct address_space *mapping, void *netfs_priv)
{
key_put(netfs_priv);
}
-static const struct netfs_read_request_ops afs_req_ops = {
+const struct netfs_read_request_ops afs_req_ops = {
.init_rreq = afs_init_rreq,
+ .is_cache_enabled = afs_is_cache_enabled,
.begin_cache_operation = afs_begin_cache_operation,
+ .check_write_begin = afs_check_write_begin,
.issue_op = afs_req_issue_op,
.cleanup = afs_priv_cleanup,
};
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index f9a692fc08f4..52157a05796a 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1045,6 +1045,7 @@ extern void afs_dynroot_depopulate(struct super_block *);
extern const struct address_space_operations afs_fs_aops;
extern const struct inode_operations afs_file_inode_operations;
extern const struct file_operations afs_file_operations;
+extern const struct netfs_read_request_ops afs_req_ops;
extern int afs_cache_wb_key(struct afs_vnode *, struct afs_file *);
extern void afs_put_wb_key(struct afs_wb_key *);
diff --git a/fs/afs/write.c b/fs/afs/write.c
index bc84c771b0fd..dc66ff15dd16 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -11,6 +11,8 @@
#include <linux/pagemap.h>
#include <linux/writeback.h>
#include <linux/pagevec.h>
+#include <linux/netfs.h>
+#include <linux/fscache.h>
#include "internal.h"
/*
@@ -23,68 +25,6 @@ int afs_set_page_dirty(struct page *page)
}
/*
- * Handle completion of a read operation to fill a page.
- */
-static void afs_fill_hole(struct afs_read *req)
-{
- if (iov_iter_count(req->iter) > 0)
- /* The read was short - clear the excess buffer. */
- iov_iter_zero(iov_iter_count(req->iter), req->iter);
-}
-
-/*
- * partly or wholly fill a page that's under preparation for writing
- */
-static int afs_fill_page(struct file *file,
- loff_t pos, unsigned int len, struct page *page)
-{
- struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
- struct afs_read *req;
- size_t p;
- void *data;
- int ret;
-
- _enter(",,%llu", (unsigned long long)pos);
-
- if (pos >= vnode->vfs_inode.i_size) {
- p = pos & ~PAGE_MASK;
- ASSERTCMP(p + len, <=, PAGE_SIZE);
- data = kmap(page);
- memset(data + p, 0, len);
- kunmap(page);
- return 0;
- }
-
- req = kzalloc(sizeof(struct afs_read), GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- refcount_set(&req->usage, 1);
- req->vnode = vnode;
- req->done = afs_fill_hole;
- req->key = key_get(afs_file_key(file));
- req->pos = pos;
- req->len = len;
- req->nr_pages = 1;
- req->iter = &req->def_iter;
- iov_iter_xarray(&req->def_iter, READ, &file->f_mapping->i_pages, pos, len);
-
- ret = afs_fetch_data(vnode, req);
- afs_put_read(req);
- if (ret < 0) {
- if (ret == -ENOENT) {
- _debug("got NOENT from server"
- " - marking file deleted and stale");
- set_bit(AFS_VNODE_DELETED, &vnode->flags);
- ret = -ESTALE;
- }
- }
-
- _leave(" = %d", ret);
- return ret;
-}
-
-/*
* prepare to perform part of a write to a page
*/
int afs_write_begin(struct file *file, struct address_space *mapping,
@@ -102,24 +42,14 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
_enter("{%llx:%llu},%llx,%x",
vnode->fid.vid, vnode->fid.vnode, pos, len);
- page = grab_cache_page_write_begin(mapping, pos / PAGE_SIZE, flags);
- if (!page)
- return -ENOMEM;
-
- if (!PageUptodate(page) && len != PAGE_SIZE) {
- ret = afs_fill_page(file, pos & PAGE_MASK, PAGE_SIZE, page);
- if (ret < 0) {
- unlock_page(page);
- put_page(page);
- _leave(" = %d [prep]", ret);
- return ret;
- }
- SetPageUptodate(page);
- }
-
-#ifdef CONFIG_AFS_FSCACHE
- wait_on_page_fscache(page);
-#endif
+ /* Prefetch area to be written into the cache if we're caching this
+ * file. We need to do this before we get a lock on the page in case
+ * there's more than one writer competing for the same cache block.
+ */
+ ret = netfs_write_begin(file, mapping, pos, len, flags, &page, fsdata,
+ &afs_req_ops, NULL);
+ if (ret < 0)
+ return ret;
index = page->index;
from = pos - index * PAGE_SIZE;
@@ -184,7 +114,6 @@ int afs_write_end(struct file *file, struct address_space *mapping,
unsigned int f, from = pos & (thp_size(page) - 1);
unsigned int t, to = from + copied;
loff_t i_size, maybe_i_size;
- int ret = 0;
_enter("{%llx:%llu},{%lx}",
vnode->fid.vid, vnode->fid.vnode, page->index);
@@ -203,19 +132,7 @@ int afs_write_end(struct file *file, struct address_space *mapping,
write_sequnlock(&vnode->cb_lock);
}
- if (!PageUptodate(page)) {
- if (copied < len) {
- /* Try and load any missing data from the server. The
- * unmarshalling routine will take care of clearing any
- * bits that are beyond the EOF.
- */
- ret = afs_fill_page(file, pos + copied,
- len - copied, page);
- if (ret < 0)
- goto out;
- }
- SetPageUptodate(page);
- }
+ ASSERT(PageUptodate(page));
if (PagePrivate(page)) {
priv = page_private(page);
@@ -236,12 +153,11 @@ int afs_write_end(struct file *file, struct address_space *mapping,
if (set_page_dirty(page))
_debug("dirtied %lx", page->index);
- ret = copied;
out:
unlock_page(page);
put_page(page);
- return ret;
+ return copied;
}
/*