Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c966c5d authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'rxrpc-rewrite-20170106' of...

Merge tag 'rxrpc-rewrite-20170106' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs



David Howells says:

====================
afs: Implement bulk read

This pair of patches implements bulk data reading from an AFS server.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a8386317 91b467e0
Loading
Loading
Loading
Loading
+161 −7
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/pagemap.h>
#include <linux/writeback.h>
#include <linux/gfp.h>
#include <linux/task_io_accounting_ops.h>
#include "internal.h"

static int afs_readpage(struct file *file, struct page *page);
@@ -101,6 +102,21 @@ int afs_release(struct inode *inode, struct file *file)
	return 0;
}

/*
 * Dispose of a ref to a read record.
 */
void afs_put_read(struct afs_read *req)
{
	int i;

	if (atomic_dec_and_test(&req->usage)) {
		for (i = 0; i < req->nr_pages; i++)
			if (req->pages[i])
				put_page(req->pages[i]);
		kfree(req);
	}
}

#ifdef CONFIG_AFS_FSCACHE
/*
 * deal with notification that a page was read from the cache
@@ -126,9 +142,8 @@ int afs_page_filler(void *data, struct page *page)
{
	struct inode *inode = page->mapping->host;
	struct afs_vnode *vnode = AFS_FS_I(inode);
	struct afs_read *req;
	struct key *key = data;
	size_t len;
	off_t offset;
	int ret;

	_enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
@@ -164,12 +179,23 @@ int afs_page_filler(void *data, struct page *page)
		_debug("cache said ENOBUFS");
	default:
	go_on:
		offset = page->index << PAGE_SHIFT;
		len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
		req = kzalloc(sizeof(struct afs_read) + sizeof(struct page *),
			      GFP_KERNEL);
		if (!req)
			goto enomem;

		atomic_set(&req->usage, 1);
		req->pos = (loff_t)page->index << PAGE_SHIFT;
		req->len = min_t(size_t, i_size_read(inode) - req->pos,
				 PAGE_SIZE);
		req->nr_pages = 1;
		req->pages[0] = page;
		get_page(page);

		/* read the contents of the file from the server into the
		 * page */
		ret = afs_vnode_fetch_data(vnode, key, offset, len, page);
		ret = afs_vnode_fetch_data(vnode, key, req);
		afs_put_read(req);
		if (ret < 0) {
			if (ret == -ENOENT) {
				_debug("got NOENT from server"
@@ -201,6 +227,8 @@ int afs_page_filler(void *data, struct page *page)
	_leave(" = 0");
	return 0;

enomem:
	ret = -ENOMEM;
error:
	SetPageError(page);
	unlock_page(page);
@@ -234,6 +262,129 @@ static int afs_readpage(struct file *file, struct page *page)
	return ret;
}

/*
 * Make pages available as they're filled.
 */
static void afs_readpages_page_done(struct afs_call *call, struct afs_read *req)
{
	struct afs_vnode *vnode = call->reply;
	struct page *page = req->pages[req->index];

	req->pages[req->index] = NULL;
	SetPageUptodate(page);

	/* send the page to the cache */
#ifdef CONFIG_AFS_FSCACHE
	if (PageFsCache(page) &&
	    fscache_write_page(vnode->cache, page, GFP_KERNEL) != 0) {
		fscache_uncache_page(vnode->cache, page);
		BUG_ON(PageFsCache(page));
	}
#endif
	unlock_page(page);
	put_page(page);
}

/*
 * 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;
	struct key *key = file->private_data;
	pgoff_t index;
	int ret, n, i;

	/* Count the number of contiguous pages at the front of the list.  Note
	 * that the list goes prev-wards rather than next-wards.
	 */
	first = list_entry(pages->prev, struct page, lru);
	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) + sizeof(struct page *) * n,
		      GFP_NOFS);
	if (!req)
		return -ENOMEM;

	atomic_set(&req->usage, 1);
	req->page_done = afs_readpages_page_done;
	req->pos = first->index;
	req->pos <<= PAGE_SHIFT;

	/* Transfer the pages to the request.  We add them in until one fails
	 * to add to the LRU and then we stop (as that'll make a hole in the
	 * contiguous run.
	 *
	 * 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 = list_entry(pages->prev, struct page, lru);
		list_del(&page->lru);
		index = page->index;
		if (add_to_page_cache_lru(page, mapping, index,
					  readahead_gfp_mask(mapping))) {
#ifdef CONFIG_AFS_FSCACHE
			fscache_uncache_page(vnode->cache, page);
#endif
			put_page(page);
			break;
		}

		req->pages[req->nr_pages++] = page;
		req->len += PAGE_SIZE;
	} while (req->nr_pages < n);

	if (req->nr_pages == 0) {
		kfree(req);
		return 0;
	}

	ret = afs_vnode_fetch_data(vnode, key, req);
	if (ret < 0)
		goto error;

	task_io_account_read(PAGE_SIZE * req->nr_pages);
	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;
	}

	for (i = 0; i < req->nr_pages; i++) {
		page = req->pages[i];
		if (page) {
#ifdef CONFIG_AFS_FSCACHE
			fscache_uncache_page(vnode->cache, page);
#endif
			SetPageError(page);
			unlock_page(page);
		}
	}

	afs_put_read(req);
	return ret;
}

/*
 * read a set of pages
 */
@@ -287,8 +438,11 @@ static int afs_readpages(struct file *file, struct address_space *mapping,
		return ret;
	}

	/* load the missing pages from the network */
	ret = read_cache_pages(mapping, pages, afs_page_filler, key);
	while (!list_empty(pages)) {
		ret = afs_readpages_one(file, mapping, pages);
		if (ret < 0)
			break;
	}

	_leave(" = %d [netting]", ret);
	return ret;
+76 −41
Original line number Diff line number Diff line
@@ -309,15 +309,19 @@ int afs_fs_fetch_file_status(struct afs_server *server,
static int afs_deliver_fs_fetch_data(struct afs_call *call)
{
	struct afs_vnode *vnode = call->reply;
	struct afs_read *req = call->reply3;
	const __be32 *bp;
	struct page *page;
	unsigned int size;
	void *buffer;
	int ret;

	_enter("{%u}", call->unmarshall);
	_enter("{%u,%zu/%u;%u/%llu}",
	       call->unmarshall, call->offset, call->count,
	       req->remain, req->actual_len);

	switch (call->unmarshall) {
	case 0:
		req->actual_len = 0;
		call->offset = 0;
		call->unmarshall++;
		if (call->operation_ID != FSFETCHDATA64) {
@@ -334,10 +338,8 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
		if (ret < 0)
			return ret;

		call->count = ntohl(call->tmp);
		_debug("DATA length MSW: %u", call->count);
		if (call->count > 0)
			return -EBADMSG;
		req->actual_len = ntohl(call->tmp);
		req->actual_len <<= 32;
		call->offset = 0;
		call->unmarshall++;

@@ -349,26 +351,52 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
		if (ret < 0)
			return ret;

		call->count = ntohl(call->tmp);
		_debug("DATA length: %u", call->count);
		if (call->count > PAGE_SIZE)
		req->actual_len |= ntohl(call->tmp);
		_debug("DATA length: %llu", req->actual_len);
		/* Check that the server didn't want to send us extra.  We
		 * might want to just discard instead, but that requires
		 * cooperation from AF_RXRPC.
		 */
		if (req->actual_len > req->len)
			return -EBADMSG;
		call->offset = 0;

		req->remain = req->actual_len;
		call->offset = req->pos & (PAGE_SIZE - 1);
		req->index = 0;
		if (req->actual_len == 0)
			goto no_more_data;
		call->unmarshall++;

	begin_page:
		if (req->remain > PAGE_SIZE - call->offset)
			size = PAGE_SIZE - call->offset;
		else
			size = req->remain;
		call->count = call->offset + size;
		ASSERTCMP(call->count, <=, PAGE_SIZE);
		req->remain -= size;

		/* extract the returned data */
	case 3:
		_debug("extract data");
		if (call->count > 0) {
			page = call->reply3;
			buffer = kmap(page);
			ret = afs_extract_data(call, buffer,
					       call->count, true);
			kunmap(page);
		_debug("extract data %u/%llu %zu/%u",
		       req->remain, req->actual_len, call->offset, call->count);

		buffer = kmap(req->pages[req->index]);
		ret = afs_extract_data(call, buffer, call->count, true);
		kunmap(req->pages[req->index]);
		if (ret < 0)
			return ret;
		if (call->offset == PAGE_SIZE) {
			if (req->page_done)
				req->page_done(call, req);
			if (req->remain > 0) {
				req->index++;
				call->offset = 0;
				goto begin_page;
			}
		}

	no_more_data:
		call->offset = 0;
		call->unmarshall++;

@@ -393,17 +421,25 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
	}

	if (call->count < PAGE_SIZE) {
		_debug("clear");
		page = call->reply3;
		buffer = kmap(page);
		buffer = kmap(req->pages[req->index]);
		memset(buffer + call->count, 0, PAGE_SIZE - call->count);
		kunmap(page);
		kunmap(req->pages[req->index]);
		if (req->page_done)
			req->page_done(call, req);
	}

	_leave(" = 0 [done]");
	return 0;
}

static void afs_fetch_data_destructor(struct afs_call *call)
{
	struct afs_read *req = call->reply3;

	afs_put_read(req);
	afs_flat_call_destructor(call);
}

/*
 * FS.FetchData operation type
 */
@@ -411,14 +447,14 @@ static const struct afs_call_type afs_RXFSFetchData = {
	.name		= "FS.FetchData",
	.deliver	= afs_deliver_fs_fetch_data,
	.abort_to_error	= afs_abort_to_error,
	.destructor	= afs_flat_call_destructor,
	.destructor	= afs_fetch_data_destructor,
};

static const struct afs_call_type afs_RXFSFetchData64 = {
	.name		= "FS.FetchData64",
	.deliver	= afs_deliver_fs_fetch_data,
	.abort_to_error	= afs_abort_to_error,
	.destructor	= afs_flat_call_destructor,
	.destructor	= afs_fetch_data_destructor,
};

/*
@@ -427,8 +463,7 @@ static const struct afs_call_type afs_RXFSFetchData64 = {
static int afs_fs_fetch_data64(struct afs_server *server,
			       struct key *key,
			       struct afs_vnode *vnode,
			       off_t offset, size_t length,
			       struct page *buffer,
			       struct afs_read *req,
			       const struct afs_wait_mode *wait_mode)
{
	struct afs_call *call;
@@ -436,8 +471,6 @@ static int afs_fs_fetch_data64(struct afs_server *server,

	_enter("");

	ASSERTCMP(length, <, ULONG_MAX);

	call = afs_alloc_flat_call(&afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
	if (!call)
		return -ENOMEM;
@@ -445,7 +478,7 @@ static int afs_fs_fetch_data64(struct afs_server *server,
	call->key = key;
	call->reply = vnode;
	call->reply2 = NULL; /* volsync */
	call->reply3 = buffer;
	call->reply3 = req;
	call->service_id = FS_SERVICE;
	call->port = htons(AFS_FS_PORT);
	call->operation_ID = FSFETCHDATA64;
@@ -456,11 +489,12 @@ static int afs_fs_fetch_data64(struct afs_server *server,
	bp[1] = htonl(vnode->fid.vid);
	bp[2] = htonl(vnode->fid.vnode);
	bp[3] = htonl(vnode->fid.unique);
	bp[4] = htonl(upper_32_bits(offset));
	bp[5] = htonl((u32) offset);
	bp[4] = htonl(upper_32_bits(req->pos));
	bp[5] = htonl(lower_32_bits(req->pos));
	bp[6] = 0;
	bp[7] = htonl((u32) length);
	bp[7] = htonl(lower_32_bits(req->len));

	atomic_inc(&req->usage);
	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
}

@@ -470,16 +504,16 @@ static int afs_fs_fetch_data64(struct afs_server *server,
int afs_fs_fetch_data(struct afs_server *server,
		      struct key *key,
		      struct afs_vnode *vnode,
		      off_t offset, size_t length,
		      struct page *buffer,
		      struct afs_read *req,
		      const struct afs_wait_mode *wait_mode)
{
	struct afs_call *call;
	__be32 *bp;

	if (upper_32_bits(offset) || upper_32_bits(offset + length))
		return afs_fs_fetch_data64(server, key, vnode, offset, length,
					   buffer, wait_mode);
	if (upper_32_bits(req->pos) ||
	    upper_32_bits(req->len) ||
	    upper_32_bits(req->pos + req->len))
		return afs_fs_fetch_data64(server, key, vnode, req, wait_mode);

	_enter("");

@@ -490,7 +524,7 @@ int afs_fs_fetch_data(struct afs_server *server,
	call->key = key;
	call->reply = vnode;
	call->reply2 = NULL; /* volsync */
	call->reply3 = buffer;
	call->reply3 = req;
	call->service_id = FS_SERVICE;
	call->port = htons(AFS_FS_PORT);
	call->operation_ID = FSFETCHDATA;
@@ -501,9 +535,10 @@ int afs_fs_fetch_data(struct afs_server *server,
	bp[1] = htonl(vnode->fid.vid);
	bp[2] = htonl(vnode->fid.vnode);
	bp[3] = htonl(vnode->fid.unique);
	bp[4] = htonl(offset);
	bp[5] = htonl(length);
	bp[4] = htonl(lower_32_bits(req->pos));
	bp[5] = htonl(lower_32_bits(req->len));

	atomic_inc(&req->usage);
	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
}

+19 −2
Original line number Diff line number Diff line
@@ -133,6 +133,22 @@ struct afs_call_type {
	void (*destructor)(struct afs_call *call);
};

/*
 * Record of an outstanding read operation on a vnode.
 */
struct afs_read {
	loff_t			pos;		/* Where to start reading */
	loff_t			len;		/* How much to read */
	loff_t			actual_len;	/* How much we're actually getting */
	atomic_t		usage;
	unsigned int		remain;		/* Amount remaining */
	unsigned int		index;		/* Which page we're reading into */
	unsigned int		pg_offset;	/* Offset in page we're at */
	unsigned int		nr_pages;
	void (*page_done)(struct afs_call *, struct afs_read *);
	struct page		*pages[];
};

/*
 * record of an outstanding writeback on a vnode
 */
@@ -494,6 +510,7 @@ extern const struct file_operations afs_file_operations;
extern int afs_open(struct inode *, struct file *);
extern int afs_release(struct inode *, struct file *);
extern int afs_page_filler(void *, struct page *);
extern void afs_put_read(struct afs_read *);

/*
 * flock.c
@@ -513,7 +530,7 @@ extern int afs_fs_fetch_file_status(struct afs_server *, struct key *,
extern int afs_fs_give_up_callbacks(struct afs_server *,
				    const struct afs_wait_mode *);
extern int afs_fs_fetch_data(struct afs_server *, struct key *,
			     struct afs_vnode *, off_t, size_t, struct page *,
			     struct afs_vnode *, struct afs_read *,
			     const struct afs_wait_mode *);
extern int afs_fs_create(struct afs_server *, struct key *,
			 struct afs_vnode *, const char *, umode_t,
@@ -699,7 +716,7 @@ extern void afs_vnode_finalise_status_update(struct afs_vnode *,
extern int afs_vnode_fetch_status(struct afs_vnode *, struct afs_vnode *,
				  struct key *);
extern int afs_vnode_fetch_data(struct afs_vnode *, struct key *,
				off_t, size_t, struct page *);
				struct afs_read *);
extern int afs_vnode_create(struct afs_vnode *, struct key *, const char *,
			    umode_t, struct afs_fid *, struct afs_file_status *,
			    struct afs_callback *, struct afs_server **);
+3 −3
Original line number Diff line number Diff line
@@ -393,7 +393,7 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode,
 * - TODO implement caching
 */
int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
			 off_t offset, size_t length, struct page *page)
			 struct afs_read *desc)
{
	struct afs_server *server;
	int ret;
@@ -420,8 +420,8 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,

		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));

		ret = afs_fs_fetch_data(server, key, vnode, offset, length,
					page, &afs_sync_call);
		ret = afs_fs_fetch_data(server, key, vnode, desc,
					&afs_sync_call);

	} while (!afs_volume_release_fileserver(vnode, server, ret));

+1 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
	volume->cell		= params->cell;
	volume->vid		= vlocation->vldb.vid[params->type];

	volume->bdi.ra_pages	= VM_MAX_READAHEAD*1024/PAGE_SIZE; 
	ret = bdi_setup_and_register(&volume->bdi, "afs");
	if (ret)
		goto error_bdi;
Loading