[RFC PATCH 70/76] afs: Use the fscache_write_begin() helper
From: David Howells
Date: Fri Nov 20 2020 - 10:20:22 EST
Make AFS use the new fscache_write_begin() helper to do the pre-reading
required before the write. If successful, the helper returns with the
required page filled in and locked. It may read more than just one page,
expanding the read to meet cache granularity requirements as necessary.
Note: A more advanced version of this could be made that does
generic_perform_write() for a whole cache granule. This would make it
easier to avoid doing the download/read for the data to be overwritten.
Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---
fs/afs/file.c | 11 +++++
fs/afs/internal.h | 1
fs/afs/write.c | 108 ++++++-----------------------------------------------
3 files changed, 23 insertions(+), 97 deletions(-)
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 84a222587ca3..21c0ea627bd4 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -368,15 +368,24 @@ static int afs_begin_cache_operation(struct netfs_read_request *rreq)
FSCACHE_WANT_PARAMS);
}
+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 1f7caa560c5c..7a76bedd19d9 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1036,6 +1036,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 e672833c99bc..b2e03de09c24 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"
/*
@@ -22,68 +24,6 @@ int afs_set_page_dirty(struct page *page)
return __set_page_dirty_nobuffers(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
*/
@@ -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;
}
/*