Commit 5cbf0398 authored by David Howells's avatar David Howells
Browse files

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: default avatarDavid Howells <dhowells@redhat.com>
Tested-By: default avatarMarc 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
parent dc419184
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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.
+78 −249
Original line number Diff line number Diff line
@@ -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;

	XA_STATE(xas, &vnode->vfs_inode.i_mapping->i_pages, index);

	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));
	struct afs_read *req;

		rcu_read_lock();
		xas_for_each(&xas, page, last) {
			BUG_ON(xa_is_value(page));
			BUG_ON(PageCompound(page));
	req = kzalloc(sizeof(struct afs_read), gfp);
	if (req)
		refcount_set(&req->usage, 1);

			page_endio(page, false, req->error);
			put_page(page);
		}
		rcu_read_unlock();
	}
	return req;
}

/*
@@ -257,15 +213,21 @@ 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;

	_enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
	struct afs_vnode *vnode = AFS_FS_I(subreq->rreq->inode);
	struct afs_read *fsreq;

	BUG_ON(!PageLocked(page));

	ret = -ESTALE;
	if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
		goto error;
	fsreq = afs_alloc_read(GFP_NOFS);
	if (!fsreq)
		return netfs_subreq_terminated(subreq, -ENOMEM, false);

	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;
	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;

	afs_put_read(req);
	_leave(" = 0");
	return 0;
	iov_iter_xarray(&fsreq->def_iter, READ,
			&fsreq->vnode->vfs_inode.i_mapping->i_pages,
			fsreq->pos, fsreq->len);

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;
	afs_fetch_data(fsreq->vnode, fsreq);
}

enomem:
	ret = -ENOMEM;
error:
	unlock_page(page);
	_leave(" = %d", ret);
	return ret;
}

/*
 * 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;
}
	fsreq = afs_alloc_read(GFP_NOFS);
	if (!fsreq)
		return -ENOMEM;

/*
 * 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;
	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);

	/* 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++;
	ret = afs_fetch_data(fsreq->vnode, fsreq);
	page_endio(page, false, ret);
	return ret;
}

	req = kzalloc(sizeof(struct afs_read), GFP_NOFS);
	if (!req)
		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;

	/* 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;
static void afs_init_rreq(struct netfs_read_request *rreq, struct file *file)
{
	rreq->netfs_priv = key_get(afs_file_key(file));
}

		req->nr_pages++;
	} while (req->nr_pages < n);
static int afs_begin_cache_operation(struct netfs_read_request *rreq)
{
	struct afs_vnode *vnode = AFS_FS_I(rreq->inode);

	if (req->nr_pages == 0) {
		afs_put_read(req);
		return 0;
	return fscache_begin_read_operation(rreq, afs_vnode_cache(vnode));
}

	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;
static void afs_priv_cleanup(struct address_space *mapping, void *netfs_priv)
{
	key_put(netfs_priv);
}

	afs_put_read(req);
	return ret;
}
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,
};

/*
 * read a set of pages
 */
static int afs_readpages(struct file *file, struct address_space *mapping,
			 struct list_head *pages, unsigned nr_pages)
static int afs_readpage(struct file *file, struct page *page)
{
	struct key *key = afs_file_key(file);
	struct afs_vnode *vnode;
	int ret = 0;

	_enter("{%d},{%lu},,%d",
	       key_serial(key), mapping->host->i_ino, nr_pages);

	ASSERT(key != NULL);
	if (!file)
		return afs_symlink_readpage(page);

	vnode = AFS_FS_I(mapping->host);
	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
		_leave(" = -ESTALE");
		return -ESTALE;
	return netfs_readpage(file, page, &afs_req_ops, NULL);
}

	/* 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;
	}

	_leave(" = %d [netting]", ret);
	return ret;
static void afs_readahead(struct readahead_control *ractl)
{
	netfs_readahead(ractl, &afs_req_ops, NULL);
}

/*
+1 −0
Original line number Diff line number Diff line
@@ -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"
+3 −0
Original line number Diff line number Diff line
@@ -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)
+5 −2
Original line number Diff line number Diff line
@@ -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);