afs: Use new netfs lib read helper API

Make AFS use the new netfs read helpers to implement the VM read
operations:

 - afs_readpage() now hands off responsibility to netfs_readpage().

 - afs_readpages() is gone and replaced with afs_readahead().

 - afs_readahead() just hands off responsibility to netfs_readahead().

These make use of the cache if a cookie is supplied, otherwise just call
the ->issue_op() method a sufficient number of times to complete the entire
request.

Changes:
v5:
- Use proper wait function for PG_fscache in afs_page_mkwrite()[1].
- Use killable wait for PG_writeback in afs_page_mkwrite()[1].

v4:
- Folded in error handling fixes to afs_req_issue_op().
- Added flag to netfs_subreq_terminated() to indicate that the caller may
  have been running async and stuff that might sleep needs punting to a
  workqueue.

Signed-off-by: David Howells <dhowells@redhat.com>
Tested-By: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: linux-cachefs@redhat.com
cc: linux-fsdevel@vger.kernel.org
Link: https://lore.kernel.org/r/2499407.1616505440@warthog.procyon.org.uk [1]
Link: https://lore.kernel.org/r/160588542733.3465195.7526541422073350302.stgit@warthog.procyon.org.uk/ # rfc
Link: https://lore.kernel.org/r/161118158436.1232039.3884845981224091996.stgit@warthog.procyon.org.uk/ # rfc
Link: https://lore.kernel.org/r/161161053540.2537118.14904446369309535330.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/161340418739.1303470.5908092911600241280.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/161539561926.286939.5729036262354802339.stgit@warthog.procyon.org.uk/ # v4
Link: https://lore.kernel.org/r/161653817977.2770958.17696456811587237197.stgit@warthog.procyon.org.uk/ # v5
Link: https://lore.kernel.org/r/161789101258.6155.3879271028895121537.stgit@warthog.procyon.org.uk/ # v6
This commit is contained in:
David Howells 2020-02-06 14:22:29 +00:00
parent dc4191841d
commit 5cbf03985c
5 changed files with 89 additions and 252 deletions

View File

@ -4,6 +4,7 @@ config AFS_FS
depends on INET
select AF_RXRPC
select DNS_RESOLVER
select NETFS_SUPPORT
help
If you say Y here, you will get an experimental Andrew File System
driver. It currently only supports unsecured read-only AFS access.

View File

@ -14,6 +14,7 @@
#include <linux/gfp.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/mm.h>
#include <linux/netfs.h>
#include "internal.h"
static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
@ -22,8 +23,7 @@ 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_readpages(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages);
static void afs_readahead(struct readahead_control *ractl);
const struct file_operations afs_file_operations = {
.open = afs_open,
@ -47,7 +47,7 @@ const struct inode_operations afs_file_inode_operations = {
const struct address_space_operations afs_fs_aops = {
.readpage = afs_readpage,
.readpages = afs_readpages,
.readahead = afs_readahead,
.set_page_dirty = afs_set_page_dirty,
.launder_page = afs_launder_page,
.releasepage = afs_releasepage,
@ -184,61 +184,17 @@ int afs_release(struct inode *inode, struct file *file)
}
/*
* Handle completion of a read operation.
* Allocate a new read record.
*/
static void afs_file_read_done(struct afs_read *req)
struct afs_read *afs_alloc_read(gfp_t gfp)
{
struct afs_vnode *vnode = req->vnode;
struct page *page;
pgoff_t index = req->pos >> PAGE_SHIFT;
pgoff_t last = index + req->nr_pages - 1;
struct afs_read *req;
XA_STATE(xas, &vnode->vfs_inode.i_mapping->i_pages, index);
req = kzalloc(sizeof(struct afs_read), gfp);
if (req)
refcount_set(&req->usage, 1);
if (iov_iter_count(req->iter) > 0) {
/* The read was short - clear the excess buffer. */
_debug("afterclear %zx %zx %llx/%llx",
req->iter->iov_offset,
iov_iter_count(req->iter),
req->actual_len, req->len);
iov_iter_zero(iov_iter_count(req->iter), req->iter);
}
rcu_read_lock();
xas_for_each(&xas, page, last) {
page_endio(page, false, 0);
put_page(page);
}
rcu_read_unlock();
task_io_account_read(req->len);
req->cleanup = NULL;
}
/*
* Dispose of our locks and refs on the pages if the read failed.
*/
static void afs_file_read_cleanup(struct afs_read *req)
{
struct page *page;
pgoff_t index = req->pos >> PAGE_SHIFT;
pgoff_t last = index + req->nr_pages - 1;
if (req->iter) {
XA_STATE(xas, &req->vnode->vfs_inode.i_mapping->i_pages, index);
_enter("%lu,%u,%zu", index, req->nr_pages, iov_iter_count(req->iter));
rcu_read_lock();
xas_for_each(&xas, page, last) {
BUG_ON(xa_is_value(page));
BUG_ON(PageCompound(page));
page_endio(page, false, req->error);
put_page(page);
}
rcu_read_unlock();
}
return req;
}
/*
@ -257,14 +213,20 @@ void afs_put_read(struct afs_read *req)
static void afs_fetch_data_notify(struct afs_operation *op)
{
struct afs_read *req = op->fetch.req;
struct netfs_read_subrequest *subreq = req->subreq;
int error = op->error;
if (error == -ECONNABORTED)
error = afs_abort_to_error(op->ac.abort_code);
req->error = error;
if (req->done)
if (subreq) {
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
netfs_subreq_terminated(subreq, error ?: req->actual_len, false);
req->subreq = NULL;
} else if (req->done) {
req->done(req);
}
}
static void afs_fetch_data_success(struct afs_operation *op)
@ -308,8 +270,11 @@ int afs_fetch_data(struct afs_vnode *vnode, struct afs_read *req)
key_serial(req->key));
op = afs_alloc_operation(req->key, vnode->volume);
if (IS_ERR(op))
if (IS_ERR(op)) {
if (req->subreq)
netfs_subreq_terminated(req->subreq, PTR_ERR(op), false);
return PTR_ERR(op);
}
afs_op_set_vnode(op, 0, vnode);
@ -318,222 +283,86 @@ int afs_fetch_data(struct afs_vnode *vnode, struct afs_read *req)
return afs_do_sync_operation(op);
}
/*
* read page from file, directory or symlink, given a key to use
*/
static int afs_page_filler(struct key *key, struct page *page)
static void afs_req_issue_op(struct netfs_read_subrequest *subreq)
{
struct inode *inode = page->mapping->host;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_read *req;
int ret;
struct afs_vnode *vnode = AFS_FS_I(subreq->rreq->inode);
struct afs_read *fsreq;
_enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
fsreq = afs_alloc_read(GFP_NOFS);
if (!fsreq)
return netfs_subreq_terminated(subreq, -ENOMEM, false);
BUG_ON(!PageLocked(page));
fsreq->subreq = subreq;
fsreq->pos = subreq->start + subreq->transferred;
fsreq->len = subreq->len - subreq->transferred;
fsreq->key = subreq->rreq->netfs_priv;
fsreq->vnode = vnode;
fsreq->iter = &fsreq->def_iter;
ret = -ESTALE;
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
goto error;
iov_iter_xarray(&fsreq->def_iter, READ,
&fsreq->vnode->vfs_inode.i_mapping->i_pages,
fsreq->pos, fsreq->len);
req = kzalloc(sizeof(struct afs_read), GFP_KERNEL);
if (!req)
goto enomem;
refcount_set(&req->usage, 1);
req->vnode = vnode;
req->key = key_get(key);
req->pos = (loff_t)page->index << PAGE_SHIFT;
req->len = thp_size(page);
req->nr_pages = thp_nr_pages(page);
req->done = afs_file_read_done;
req->cleanup = afs_file_read_cleanup;
get_page(page);
iov_iter_xarray(&req->def_iter, READ, &page->mapping->i_pages,
req->pos, req->len);
req->iter = &req->def_iter;
ret = afs_fetch_data(vnode, req);
if (ret < 0)
goto fetch_error;
afs_put_read(req);
_leave(" = 0");
return 0;
fetch_error:
switch (ret) {
case -EINTR:
case -ENOMEM:
case -ERESTARTSYS:
case -EAGAIN:
afs_put_read(req);
goto error;
case -ENOENT:
_debug("got NOENT from server - marking file deleted and stale");
set_bit(AFS_VNODE_DELETED, &vnode->flags);
ret = -ESTALE;
/* Fall through */
default:
page_endio(page, false, ret);
afs_put_read(req);
_leave(" = %d", ret);
return ret;
}
enomem:
ret = -ENOMEM;
error:
unlock_page(page);
_leave(" = %d", ret);
return ret;
afs_fetch_data(fsreq->vnode, fsreq);
}
/*
* read page from file, directory or symlink, given a file to nominate the key
* to be used
*/
static int afs_readpage(struct file *file, struct page *page)
static int afs_symlink_readpage(struct page *page)
{
struct key *key;
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
struct afs_read *fsreq;
int ret;
if (file) {
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)->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
} else {
ret = afs_page_filler(key, page);
key_put(key);
}
}
return ret;
}
/*
* Read a contiguous set of pages.
*/
static int afs_readpages_one(struct file *file, struct address_space *mapping,
struct list_head *pages)
{
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct afs_read *req;
struct list_head *p;
struct page *first, *page;
pgoff_t index;
int ret, n;
/* Count the number of contiguous pages at the front of the list. Note
* that the list goes prev-wards rather than next-wards.
*/
first = lru_to_page(pages);
index = first->index + 1;
n = 1;
for (p = first->lru.prev; p != pages; p = p->prev) {
page = list_entry(p, struct page, lru);
if (page->index != index)
break;
index++;
n++;
}
req = kzalloc(sizeof(struct afs_read), GFP_NOFS);
if (!req)
fsreq = afs_alloc_read(GFP_NOFS);
if (!fsreq)
return -ENOMEM;
refcount_set(&req->usage, 1);
req->vnode = vnode;
req->key = key_get(afs_file_key(file));
req->done = afs_file_read_done;
req->cleanup = afs_file_read_cleanup;
req->pos = first->index;
req->pos <<= PAGE_SHIFT;
fsreq->pos = page->index * PAGE_SIZE;
fsreq->len = PAGE_SIZE;
fsreq->vnode = vnode;
fsreq->iter = &fsreq->def_iter;
iov_iter_xarray(&fsreq->def_iter, READ, &page->mapping->i_pages,
fsreq->pos, fsreq->len);
/* Add pages to the LRU until it fails. We keep the pages ref'd and
* locked until the read is complete.
*
* Note that it's possible for the file size to change whilst we're
* doing this, but we rely on the server returning less than we asked
* for if the file shrank. We also rely on this to deal with a partial
* page at the end of the file.
*/
do {
page = lru_to_page(pages);
list_del(&page->lru);
index = page->index;
if (add_to_page_cache_lru(page, mapping, index,
readahead_gfp_mask(mapping))) {
put_page(page);
break;
}
req->nr_pages++;
} while (req->nr_pages < n);
if (req->nr_pages == 0) {
afs_put_read(req);
return 0;
}
req->len = req->nr_pages * PAGE_SIZE;
iov_iter_xarray(&req->def_iter, READ, &file->f_mapping->i_pages,
req->pos, req->len);
req->iter = &req->def_iter;
ret = afs_fetch_data(vnode, req);
if (ret < 0)
goto error;
afs_put_read(req);
return 0;
error:
if (ret == -ENOENT) {
_debug("got NOENT from server - marking file deleted and stale");
set_bit(AFS_VNODE_DELETED, &vnode->flags);
ret = -ESTALE;
}
afs_put_read(req);
ret = afs_fetch_data(fsreq->vnode, fsreq);
page_endio(page, false, ret);
return ret;
}
/*
* read a set of pages
*/
static int afs_readpages(struct file *file, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages)
static void afs_init_rreq(struct netfs_read_request *rreq, struct file *file)
{
struct key *key = afs_file_key(file);
struct afs_vnode *vnode;
int ret = 0;
rreq->netfs_priv = key_get(afs_file_key(file));
}
_enter("{%d},{%lu},,%d",
key_serial(key), mapping->host->i_ino, nr_pages);
static int afs_begin_cache_operation(struct netfs_read_request *rreq)
{
struct afs_vnode *vnode = AFS_FS_I(rreq->inode);
ASSERT(key != NULL);
return fscache_begin_read_operation(rreq, afs_vnode_cache(vnode));
}
vnode = AFS_FS_I(mapping->host);
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
_leave(" = -ESTALE");
return -ESTALE;
}
static void afs_priv_cleanup(struct address_space *mapping, void *netfs_priv)
{
key_put(netfs_priv);
}
/* attempt to read as many of the pages as possible */
while (!list_empty(pages)) {
ret = afs_readpages_one(file, mapping, pages);
if (ret < 0)
break;
}
static const struct netfs_read_request_ops afs_req_ops = {
.init_rreq = afs_init_rreq,
.begin_cache_operation = afs_begin_cache_operation,
.issue_op = afs_req_issue_op,
.cleanup = afs_priv_cleanup,
};
_leave(" = %d [netting]", ret);
return ret;
static int afs_readpage(struct file *file, struct page *page)
{
if (!file)
return afs_symlink_readpage(page);
return netfs_readpage(file, page, &afs_req_ops, NULL);
}
static void afs_readahead(struct readahead_control *ractl)
{
netfs_readahead(ractl, &afs_req_ops, NULL);
}
/*

View File

@ -10,6 +10,7 @@
#include <linux/sched.h>
#include <linux/circ_buf.h>
#include <linux/iversion.h>
#include <linux/netfs.h>
#include "internal.h"
#include "afs_fs.h"
#include "xdr_fs.h"

View File

@ -14,6 +14,7 @@
#include <linux/key.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#define FSCACHE_USE_NEW_IO_API
#include <linux/fscache.h>
#include <linux/backing-dev.h>
#include <linux/uuid.h>
@ -207,6 +208,7 @@ struct afs_read {
loff_t file_size; /* File size returned by server */
struct key *key; /* The key to use to reissue the read */
struct afs_vnode *vnode; /* The file being read into. */
struct netfs_read_subrequest *subreq; /* Fscache helper read request this belongs to */
afs_dataversion_t data_version; /* Version number returned by server */
refcount_t usage;
unsigned int call_debug_id;
@ -1049,6 +1051,7 @@ extern void afs_put_wb_key(struct afs_wb_key *);
extern int afs_open(struct inode *, struct file *);
extern int afs_release(struct inode *, struct file *);
extern int afs_fetch_data(struct afs_vnode *, struct afs_read *);
extern struct afs_read *afs_alloc_read(gfp_t);
extern void afs_put_read(struct afs_read *);
static inline struct afs_read *afs_get_read(struct afs_read *req)

View File

@ -930,7 +930,7 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
*/
#ifdef CONFIG_AFS_FSCACHE
if (PageFsCache(page) &&
wait_on_page_bit_killable(page, PG_fscache) < 0)
wait_on_page_fscache_killable(page) < 0)
return VM_FAULT_RETRY;
#endif
@ -944,7 +944,10 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
* details the portion of the page we need to write back and we might
* need to redirty the page if there's a problem.
*/
wait_on_page_writeback(page);
if (wait_on_page_writeback_killable(page) < 0) {
unlock_page(page);
return VM_FAULT_RETRY;
}
priv = afs_page_dirty(page, 0, thp_size(page));
priv = afs_page_dirty_mmapped(priv);