Commit fafe1e39 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'afs-netfs-lib-20210426' of...

Merge tag 'afs-netfs-lib-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull AFS updates from David Howells:
 "Use the new netfs lib.

  Begin the process of overhauling the use of the fscache API by AFS and
  the introduction of support for features such as Transparent Huge
  Pages (THPs).

   - Add some support for THPs, including using core VM helper functions
     to find details of pages.

   - Use the ITER_XARRAY I/O iterator to mediate access to the pagecache
     as this handles THPs and doesn't require allocation of large bvec
     arrays.

   - Delegate address_space read/pre-write I/O methods for AFS to the
     netfs helper library. A method is provided to the library that
     allows it to issue a read against the server.

     This includes a change in use for PG_fscache (it now indicates a
     DIO write in progress from the marked page), so a number of waits
     need to be deployed for it.

   - Split the core AFS writeback function to make it easier to modify
     in future patches to handle writing to the cache. [This might
     feasibly make more sense moved out into my fscache-iter branch].

  I've tested these with "xfstests -g quick" against an AFS volume
  (xfstests needs patching to make it work). With this, AFS without a
  cache passes all expected xfstests; with a cache, there's an extra
  failure, but that's also there before these patches. Fixing that
  probably requires a greater overhaul (as can be found on my
  fscache-iter branch, but that's for a later time).

  Thanks should go to Marc Dionne and Jeff Altman of AuriStor for
  exercising the patches in their test farm also"

Link: https://lore.kernel.org/lkml/3785063.1619482429@warthog.procyon.org.uk/

* tag 'afs-netfs-lib-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Use the netfs_write_begin() helper
  afs: Use new netfs lib read helper API
  afs: Use the fs operation ops to handle FetchData completion
  afs: Prepare for use of THPs
  afs: Extract writeback extension into its own function
  afs: Wait on PG_fscache before modifying/releasing a page
  afs: Use ITER_XARRAY for writing
  afs: Set up the iov_iter before calling afs_extract_data()
  afs: Log remote unmarshalling errors
  afs: Don't truncate iter during data fetch
  afs: Move key to afs_read struct
  afs: Print the operation debug_id when logging an unexpected data version
  afs: Pass page into dirty region helpers to provide THP size
  afs: Disable use of the fscache I/O routines
parents 820c4bae 3003bbd0
...@@ -4,6 +4,7 @@ config AFS_FS ...@@ -4,6 +4,7 @@ config AFS_FS
depends on INET depends on INET
select AF_RXRPC select AF_RXRPC
select DNS_RESOLVER select DNS_RESOLVER
select NETFS_SUPPORT
help help
If you say Y here, you will get an experimental Andrew File System If you say Y here, you will get an experimental Andrew File System
driver. It currently only supports unsecured read-only AFS access. driver. It currently only supports unsecured read-only AFS access.
......
...@@ -102,6 +102,35 @@ struct afs_lookup_cookie { ...@@ -102,6 +102,35 @@ struct afs_lookup_cookie {
struct afs_fid fids[50]; struct afs_fid fids[50];
}; };
/*
* Drop the refs that we're holding on the pages we were reading into. We've
* got refs on the first nr_pages pages.
*/
static void afs_dir_read_cleanup(struct afs_read *req)
{
struct address_space *mapping = req->vnode->vfs_inode.i_mapping;
struct page *page;
pgoff_t last = req->nr_pages - 1;
XA_STATE(xas, &mapping->i_pages, 0);
if (unlikely(!req->nr_pages))
return;
rcu_read_lock();
xas_for_each(&xas, page, last) {
if (xas_retry(&xas, page))
continue;
BUG_ON(xa_is_value(page));
BUG_ON(PageCompound(page));
ASSERTCMP(page->mapping, ==, mapping);
put_page(page);
}
rcu_read_unlock();
}
/* /*
* check that a directory page is valid * check that a directory page is valid
*/ */
...@@ -127,7 +156,7 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page, ...@@ -127,7 +156,7 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page,
qty /= sizeof(union afs_xdr_dir_block); qty /= sizeof(union afs_xdr_dir_block);
/* check them */ /* check them */
dbuf = kmap(page); dbuf = kmap_atomic(page);
for (tmp = 0; tmp < qty; tmp++) { for (tmp = 0; tmp < qty; tmp++) {
if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) { if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) {
printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n", printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n",
...@@ -146,7 +175,7 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page, ...@@ -146,7 +175,7 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page,
((u8 *)&dbuf->blocks[tmp])[AFS_DIR_BLOCK_SIZE - 1] = 0; ((u8 *)&dbuf->blocks[tmp])[AFS_DIR_BLOCK_SIZE - 1] = 0;
} }
kunmap(page); kunmap_atomic(dbuf);
checked: checked:
afs_stat_v(dvnode, n_read_dir); afs_stat_v(dvnode, n_read_dir);
...@@ -157,35 +186,74 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page, ...@@ -157,35 +186,74 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page,
} }
/* /*
* Check the contents of a directory that we've just read. * Dump the contents of a directory.
*/ */
static bool afs_dir_check_pages(struct afs_vnode *dvnode, struct afs_read *req) static void afs_dir_dump(struct afs_vnode *dvnode, struct afs_read *req)
{ {
struct afs_xdr_dir_page *dbuf; struct afs_xdr_dir_page *dbuf;
unsigned int i, j, qty = PAGE_SIZE / sizeof(union afs_xdr_dir_block); struct address_space *mapping = dvnode->vfs_inode.i_mapping;
struct page *page;
unsigned int i, qty = PAGE_SIZE / sizeof(union afs_xdr_dir_block);
pgoff_t last = req->nr_pages - 1;
for (i = 0; i < req->nr_pages; i++) XA_STATE(xas, &mapping->i_pages, 0);
if (!afs_dir_check_page(dvnode, req->pages[i], req->actual_len))
goto bad;
return true;
bad: pr_warn("DIR %llx:%llx f=%llx l=%llx al=%llx\n",
pr_warn("DIR %llx:%llx f=%llx l=%llx al=%llx r=%llx\n",
dvnode->fid.vid, dvnode->fid.vnode, dvnode->fid.vid, dvnode->fid.vnode,
req->file_size, req->len, req->actual_len, req->remain); req->file_size, req->len, req->actual_len);
pr_warn("DIR %llx %x %x %x\n", pr_warn("DIR %llx %x %zx %zx\n",
req->pos, req->index, req->nr_pages, req->offset); req->pos, req->nr_pages,
req->iter->iov_offset, iov_iter_count(req->iter));
for (i = 0; i < req->nr_pages; i++) { xas_for_each(&xas, page, last) {
dbuf = kmap(req->pages[i]); if (xas_retry(&xas, page))
for (j = 0; j < qty; j++) { continue;
union afs_xdr_dir_block *block = &dbuf->blocks[j];
BUG_ON(PageCompound(page));
BUG_ON(page->mapping != mapping);
dbuf = kmap_atomic(page);
for (i = 0; i < qty; i++) {
union afs_xdr_dir_block *block = &dbuf->blocks[i];
pr_warn("[%02x] %32phN\n", i * qty + j, block); pr_warn("[%02lx] %32phN\n", page->index * qty + i, block);
} }
kunmap(req->pages[i]); kunmap_atomic(dbuf);
} }
return false; }
/*
* Check all the pages in a directory. All the pages are held pinned.
*/
static int afs_dir_check(struct afs_vnode *dvnode, struct afs_read *req)
{
struct address_space *mapping = dvnode->vfs_inode.i_mapping;
struct page *page;
pgoff_t last = req->nr_pages - 1;
int ret = 0;
XA_STATE(xas, &mapping->i_pages, 0);
if (unlikely(!req->nr_pages))
return 0;
rcu_read_lock();
xas_for_each(&xas, page, last) {
if (xas_retry(&xas, page))
continue;
BUG_ON(PageCompound(page));
BUG_ON(page->mapping != mapping);
if (!afs_dir_check_page(dvnode, page, req->file_size)) {
afs_dir_dump(dvnode, req);
ret = -EIO;
break;
}
}
rcu_read_unlock();
return ret;
} }
/* /*
...@@ -214,57 +282,57 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key) ...@@ -214,57 +282,57 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
{ {
struct afs_read *req; struct afs_read *req;
loff_t i_size; loff_t i_size;
int nr_pages, nr_inline, i, n; int nr_pages, i, n;
int ret = -ENOMEM; int ret;
_enter("");
retry: req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return ERR_PTR(-ENOMEM);
refcount_set(&req->usage, 1);
req->vnode = dvnode;
req->key = key_get(key);
req->cleanup = afs_dir_read_cleanup;
expand:
i_size = i_size_read(&dvnode->vfs_inode); i_size = i_size_read(&dvnode->vfs_inode);
if (i_size < 2048) if (i_size < 2048) {
return ERR_PTR(afs_bad(dvnode, afs_file_error_dir_small)); ret = afs_bad(dvnode, afs_file_error_dir_small);
goto error;
}
if (i_size > 2048 * 1024) { if (i_size > 2048 * 1024) {
trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big); trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big);
return ERR_PTR(-EFBIG); ret = -EFBIG;
goto error;
} }
_enter("%llu", i_size); _enter("%llu", i_size);
/* Get a request record to hold the page list. We want to hold it
* inline if we can, but we don't want to make an order 1 allocation.
*/
nr_pages = (i_size + PAGE_SIZE - 1) / PAGE_SIZE; nr_pages = (i_size + PAGE_SIZE - 1) / PAGE_SIZE;
nr_inline = nr_pages;
if (nr_inline > (PAGE_SIZE - sizeof(*req)) / sizeof(struct page *))
nr_inline = 0;
req = kzalloc(struct_size(req, array, nr_inline), GFP_KERNEL);
if (!req)
return ERR_PTR(-ENOMEM);
refcount_set(&req->usage, 1);
req->nr_pages = nr_pages;
req->actual_len = i_size; /* May change */ req->actual_len = i_size; /* May change */
req->len = nr_pages * PAGE_SIZE; /* We can ask for more than there is */ req->len = nr_pages * PAGE_SIZE; /* We can ask for more than there is */
req->data_version = dvnode->status.data_version; /* May change */ req->data_version = dvnode->status.data_version; /* May change */
if (nr_inline > 0) { iov_iter_xarray(&req->def_iter, READ, &dvnode->vfs_inode.i_mapping->i_pages,
req->pages = req->array; 0, i_size);
} else { req->iter = &req->def_iter;
req->pages = kcalloc(nr_pages, sizeof(struct page *),
GFP_KERNEL);
if (!req->pages)
goto error;
}
/* Get a list of all the pages that hold or will hold the directory /* Fill in any gaps that we might find where the memory reclaimer has
* content. We need to fill in any gaps that we might find where the * been at work and pin all the pages. If there are any gaps, we will
* memory reclaimer has been at work. If there are any gaps, we will
* need to reread the entire directory contents. * need to reread the entire directory contents.
*/ */
i = 0; i = req->nr_pages;
do { while (i < nr_pages) {
struct page *pages[8], *page;
n = find_get_pages_contig(dvnode->vfs_inode.i_mapping, i, n = find_get_pages_contig(dvnode->vfs_inode.i_mapping, i,
req->nr_pages - i, min_t(unsigned int, nr_pages - i,
req->pages + i); ARRAY_SIZE(pages)),
_debug("find %u at %u/%u", n, i, req->nr_pages); pages);
_debug("find %u at %u/%u", n, i, nr_pages);
if (n == 0) { if (n == 0) {
gfp_t gfp = dvnode->vfs_inode.i_mapping->gfp_mask; gfp_t gfp = dvnode->vfs_inode.i_mapping->gfp_mask;
...@@ -272,22 +340,24 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key) ...@@ -272,22 +340,24 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
afs_stat_v(dvnode, n_inval); afs_stat_v(dvnode, n_inval);
ret = -ENOMEM; ret = -ENOMEM;
req->pages[i] = __page_cache_alloc(gfp); page = __page_cache_alloc(gfp);
if (!req->pages[i]) if (!page)
goto error; goto error;
ret = add_to_page_cache_lru(req->pages[i], ret = add_to_page_cache_lru(page,
dvnode->vfs_inode.i_mapping, dvnode->vfs_inode.i_mapping,
i, gfp); i, gfp);
if (ret < 0) if (ret < 0)
goto error; goto error;
attach_page_private(req->pages[i], (void *)1); attach_page_private(page, (void *)1);
unlock_page(req->pages[i]); unlock_page(page);
req->nr_pages++;
i++; i++;
} else { } else {
req->nr_pages += n;
i += n; i += n;
} }
} while (i < req->nr_pages); }
/* If we're going to reload, we need to lock all the pages to prevent /* If we're going to reload, we need to lock all the pages to prevent
* races. * races.
...@@ -305,18 +375,23 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key) ...@@ -305,18 +375,23 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) { if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
trace_afs_reload_dir(dvnode); trace_afs_reload_dir(dvnode);
ret = afs_fetch_data(dvnode, key, req); ret = afs_fetch_data(dvnode, req);
if (ret < 0) if (ret < 0)
goto error_unlock; goto error_unlock;
task_io_account_read(PAGE_SIZE * req->nr_pages); task_io_account_read(PAGE_SIZE * req->nr_pages);
if (req->len < req->file_size) if (req->len < req->file_size) {
goto content_has_grown; /* The content has grown, so we need to expand the
* buffer.
*/
up_write(&dvnode->validate_lock);
goto expand;
}
/* Validate the data we just read. */ /* Validate the data we just read. */
ret = -EIO; ret = afs_dir_check(dvnode, req);
if (!afs_dir_check_pages(dvnode, req)) if (ret < 0)
goto error_unlock; goto error_unlock;
// TODO: Trim excess pages // TODO: Trim excess pages
...@@ -334,11 +409,6 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key) ...@@ -334,11 +409,6 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
afs_put_read(req); afs_put_read(req);
_leave(" = %d", ret); _leave(" = %d", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
content_has_grown:
up_write(&dvnode->validate_lock);
afs_put_read(req);
goto retry;
} }
/* /*
...@@ -448,6 +518,7 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx, ...@@ -448,6 +518,7 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx,
struct afs_read *req; struct afs_read *req;
struct page *page; struct page *page;
unsigned blkoff, limit; unsigned blkoff, limit;
void __rcu **slot;
int ret; int ret;
_enter("{%lu},%u,,", dir->i_ino, (unsigned)ctx->pos); _enter("{%lu},%u,,", dir->i_ino, (unsigned)ctx->pos);
...@@ -472,9 +543,15 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx, ...@@ -472,9 +543,15 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx,
blkoff = ctx->pos & ~(sizeof(union afs_xdr_dir_block) - 1); blkoff = ctx->pos & ~(sizeof(union afs_xdr_dir_block) - 1);
/* Fetch the appropriate page from the directory and re-add it /* Fetch the appropriate page from the directory and re-add it
* to the LRU. * to the LRU. We have all the pages pinned with an extra ref.
*/ */
page = req->pages[blkoff / PAGE_SIZE]; rcu_read_lock();
page = NULL;
slot = radix_tree_lookup_slot(&dvnode->vfs_inode.i_mapping->i_pages,
blkoff / PAGE_SIZE);
if (slot)
page = radix_tree_deref_slot(slot);
rcu_read_unlock();
if (!page) { if (!page) {
ret = afs_bad(dvnode, afs_file_error_dir_missing_page); ret = afs_bad(dvnode, afs_file_error_dir_missing_page);
break; break;
...@@ -2006,6 +2083,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset, ...@@ -2006,6 +2083,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
afs_stat_v(dvnode, n_inval); afs_stat_v(dvnode, n_inval);
/* we clean up only if the entire page is being invalidated */ /* we clean up only if the entire page is being invalidated */
if (offset == 0 && length == PAGE_SIZE) if (offset == 0 && length == thp_size(page))
detach_page_private(page); detach_page_private(page);
} }
This diff is collapsed.
...@@ -198,8 +198,10 @@ void afs_wait_for_operation(struct afs_operation *op) ...@@ -198,8 +198,10 @@ void afs_wait_for_operation(struct afs_operation *op)
case -ECONNABORTED: case -ECONNABORTED:
if (op->ops->aborted) if (op->ops->aborted)
op->ops->aborted(op); op->ops->aborted(op);
break; fallthrough;
default: default:
if (op->ops->failed)
op->ops->failed(op);
break; break;
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/circ_buf.h> #include <linux/circ_buf.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include <linux/netfs.h>
#include "internal.h" #include "internal.h"
#include "afs_fs.h" #include "afs_fs.h"
#include "xdr_fs.h" #include "xdr_fs.h"
...@@ -302,17 +303,15 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) ...@@ -302,17 +303,15 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
struct afs_vnode_param *vp = &op->file[0]; struct afs_vnode_param *vp = &op->file[0];
struct afs_read *req = op->fetch.req; struct afs_read *req = op->fetch.req;
const __be32 *bp; const __be32 *bp;
unsigned int size;
int ret; int ret;
_enter("{%u,%zu/%llu}", _enter("{%u,%zu,%zu/%llu}",
call->unmarshall, iov_iter_count(call->iter), req->actual_len); call->unmarshall, call->iov_len, iov_iter_count(call->iter),
req->actual_len);
switch (call->unmarshall) { switch (call->unmarshall) {
case 0: case 0:
req->actual_len = 0; req->actual_len = 0;
req->index = 0;
req->offset = req->pos & (PAGE_SIZE - 1);
call->unmarshall++; call->unmarshall++;
if (call->operation_ID == FSFETCHDATA64) { if (call->operation_ID == FSFETCHDATA64) {
afs_extract_to_tmp64(call); afs_extract_to_tmp64(call);
...@@ -322,7 +321,10 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) ...@@ -322,7 +321,10 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
} }
fallthrough; fallthrough;
/* extract the returned data length */ /* Extract the returned data length into
* ->actual_len. This may indicate more or less data than was
* requested will be returned.
*/
case 1: case 1:
_debug("extract data length"); _debug("extract data length");
ret = afs_extract_data(call, true); ret = afs_extract_data(call, true);
...@@ -331,44 +333,25 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) ...@@ -331,44 +333,25 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
req->actual_len = be64_to_cpu(call->tmp64); req->actual_len = be64_to_cpu(call->tmp64);
_debug("DATA length: %llu", req->actual_len); _debug("DATA length: %llu", req->actual_len);
req->remain = min(req->len, req->actual_len);
if (req->remain == 0) if (req->actual_len == 0)
goto no_more_data; goto no_more_data;
call->iter = req->iter;
call->iov_len = min(req->actual_len, req->len);
call->unmarshall++; call->unmarshall++;
begin_page:
ASSERTCMP(req->index, <, req->nr_pages);
if (req->remain > PAGE_SIZE - req->offset)
size = PAGE_SIZE - req->offset;
else
size = req->remain;
call->bvec[0].bv_len = size;
call->bvec[0].bv_offset = req->offset;
call->bvec[0].bv_page = req->pages[req->index];
iov_iter_bvec(&call->def_iter, READ, call->bvec, 1, size);
ASSERTCMP(size, <=, PAGE_SIZE);
fallthrough; fallthrough;
/* extract the returned data */ /* extract the returned data */
case 2: case 2:
_debug("extract data %zu/%llu", _debug("extract data %zu/%llu",
iov_iter_count(call->iter), req->remain); iov_iter_count(call->iter), req->actual_len);
ret = afs_extract_data(call, true); ret = afs_extract_data(call, true);
if (ret < 0) if (ret < 0)
return ret; return ret;
req->remain -= call->bvec[0].bv_len;
req->offset += call->bvec[0].bv_len;
ASSERTCMP(req->offset, <=, PAGE_SIZE);
if (req->offset == PAGE_SIZE) {
req->offset = 0;
req->index++;
if (req->remain > 0)
goto begin_page;
}
ASSERTCMP(req->remain, ==, 0); call->iter = &call->def_iter;
if (req->actual_len <= req->len) if (req->actual_len <= req->len)
goto no_more_data; goto no_more_data;
...@@ -410,17 +393,6 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) ...@@ -410,17 +393,6 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
break; break;
} }
for (; req->index < req->nr_pages; req->index++) {
if (req->offset < PAGE_SIZE)
zero_user_segment(req->pages[req->index],
req->offset, PAGE_SIZE);
req->offset = 0;
}
if (req->page_done)
for (req->index = 0; req->index < req->nr_pages; req->index++)
req->page_done(req);
_leave(" = 0 [done]"); _leave(" = 0 [done]");
return 0; return 0;
} }
...@@ -494,6 +466,8 @@ void afs_fs_fetch_data(struct afs_operation *op) ...@@ -494,6 +466,8 @@ void afs_fs_fetch_data(struct afs_operation *op)
if (!call) if (!call)
return afs_op_nomem(op); return afs_op_nomem(op);
req->call_debug_id = call->debug_id;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
bp[0] = htonl(FSFETCHDATA); bp[0] = htonl(FSFETCHDATA);
...@@ -1079,8 +1053,7 @@ static const struct afs_call_type afs_RXFSStoreData64 = { ...@@ -1079,8 +1053,7 @@ static const struct afs_call_type afs_RXFSStoreData64 = {
/* /*
* store a set of pages to a very large file * store a set of pages to a very large file
*/ */
static void afs_fs_store_data64(struct afs_operation *op, static void afs_fs_store_data64(struct afs_operation *op)
loff_t pos, loff_t size, loff_t i_size)
{ {
struct afs_vnode_param *vp = &op->file[0]; struct afs_vnode_param *vp = &op->file[0];
struct afs_call *call; struct afs_call *call;
...@@ -1095,7 +1068,7 @@ static void afs_fs_store_data64(struct afs_operation *op, ...@@ -1095,7 +1068,7 @@ static void afs_fs_store_data64(struct afs_operation *op,
if (!call) if (!call)
return afs_op_nomem(op); return afs_op_nomem(op);
call->send_pages = true; call->write_iter = op->store.write_iter;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1111,47 +1084,38 @@ static void afs_fs_store_data64(struct afs_operation *op, ...@@ -1111,47 +1084,38 @@ static void afs_fs_store_data64(struct afs_operation *op,
*bp++ = 0; /* unix mode */ *bp++ = 0; /* unix mode */
*bp++ = 0; /* segment size */ *bp++ = 0; /* segment size */
*bp++ = htonl(upper_32_bits(pos)); *bp++ = htonl(upper_32_bits(op->store.pos));
*bp++ = htonl(lower_32_bits(pos)); *bp++ = htonl(lower_32_bits(op->store.pos));
*bp++ = htonl(upper_32_bits(size)); *bp++ = htonl(upper_32_bits(op->store.size));
*bp++ = htonl(lower_32_bits(size)); *bp++ = htonl(lower_32_bits(op->store.size));
*bp++ = htonl(upper_32_bits(i_size)); *bp++ = htonl(upper_32_bits(op->store.i_size));
*bp++ = htonl(lower_32_bits(i_size)); *bp++ = htonl(lower_32_bits(op->store.i_size));
trace_afs_make_fs_call(call, &vp->fid); trace_afs_make_fs_call(call, &vp->fid);
afs_make_op_call(op, call, GFP_NOFS); afs_make_op_call(op, call, GFP_NOFS);
} }
/* /*
* store a set of pages * Write data to a file on the server.
*/ */
void afs_fs_store_data(struct afs_operation *op) void afs_fs_store_data(struct afs_operation *op)
{ {
struct afs_vnode_param *vp = &op->file[0]; struct afs_vnode_param *vp = &op->file[0];
struct afs_call *call; struct afs_call *call;
loff_t size, pos, i_size;
__be32 *bp; __be32 *bp;
_enter(",%x,{%llx:%llu},,", _enter(",%x,{%llx:%llu},,",
key_serial(op->key), vp->fid.vid, vp->fid.vnode); key_serial(op->key), vp->fid.vid, vp->fid.vnode);
size = (loff_t)op->store.last_to - (loff_t)op->store.first_offset;
if (op->store.first != op->store.last)
size += (loff_t)(op->store.last - op->store.first) << PAGE_SHIFT;
pos = (loff_t)op->store.first << PAGE_SHIFT;
pos += op->store.first_offset;
i_size = i_size_read(&vp->vnode->vfs_inode);
if (pos + size > i_size)
i_size = size + pos;
_debug("size %llx, at %llx, i_size %llx", _debug("size %llx, at %llx, i_size %llx",
(unsigned long long) size, (unsigned long long) pos, (unsigned long long)op->store.size,
(unsigned long long) i_size); (unsigned long long)op->store.pos,
(unsigned long long)op->store.i_size);
if (upper_32_bits(pos) || upper_32_bits(i_size) || upper_32_bits(size) || if (upper_32_bits(op->store.pos) ||
upper_32_bits(pos + size)) upper_32_bits(op->store.size) ||
return afs_fs_store_data64(op, pos, size, i_size); upper_32_bits(op->store.i_size))
return afs_fs_store_data64(op);
call = afs_alloc_flat_call(op->net, &afs_RXFSStoreData, call = afs_alloc_flat_call(op->net, &afs_RXFSStoreData,
(4 + 6 + 3) * 4, (4 + 6 + 3) * 4,
...@@ -1159,7 +1123,7 @@ void afs_fs_store_data(struct afs_operation *op) ...@@ -1159,7 +1123,7 @@ void afs_fs_store_data(struct afs_operation *op)
if (!call) if (!call)
return afs_op_nomem(op); return afs_op_nomem(op);
call->send_pages = true; call->write_iter = op->store.write_iter;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1175,9 +1139,9 @@ void afs_fs_store_data(struct afs_operation *op) ...@@ -1175,9 +1139,9 @@ void afs_fs_store_data(struct afs_operation *op)
*bp++ = 0; /* unix mode */ *bp++ = 0; /* unix mode */
*bp++ = 0; /* segment size */ *bp++ = 0; /* segment size */
*bp++ = htonl(lower_32_bits(pos)); *bp++ = htonl(lower_32_bits(op->store.pos));
*bp++ = htonl(lower_32_bits(size)); *bp++ = htonl(lower_32_bits(op->store.size));
*bp++ = htonl(lower_32_bits(i_size)); *bp++ = htonl(lower_32_bits(op->store.i_size));
trace_afs_make_fs_call(call, &vp->fid); trace_afs_make_fs_call(call, &vp->fid);
afs_make_op_call(op, call, GFP_NOFS); afs_make_op_call(op, call, GFP_NOFS);
......
...@@ -214,11 +214,12 @@ static void afs_apply_status(struct afs_operation *op, ...@@ -214,11 +214,12 @@ static void afs_apply_status(struct afs_operation *op,
if (vp->dv_before + vp->dv_delta != status->data_version) { if (vp->dv_before + vp->dv_delta != status->data_version) {
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
pr_warn("kAFS: vnode modified {%llx:%llu} %llx->%llx %s\n", pr_warn("kAFS: vnode modified {%llx:%llu} %llx->%llx %s (op=%x)\n",
vnode->fid.vid, vnode->fid.vnode, vnode->fid.vid, vnode->fid.vnode,
(unsigned long long)vp->dv_before + vp->dv_delta, (unsigned long long)vp->dv_before + vp->dv_delta,
(unsigned long long)status->data_version, (unsigned long long)status->data_version,
op->type ? op->type->name : "???"); op->type ? op->type->name : "???",
op->debug_id);
vnode->invalid_before = status->data_version; vnode->invalid_before = status->data_version;
if (vnode->status.type == AFS_FTYPE_DIR) { if (vnode->status.type == AFS_FTYPE_DIR) {
...@@ -427,7 +428,7 @@ static void afs_get_inode_cache(struct afs_vnode *vnode) ...@@ -427,7 +428,7 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
} __packed key; } __packed key;
struct afs_vnode_cache_aux aux; struct afs_vnode_cache_aux aux;
if (vnode->status.type == AFS_FTYPE_DIR) { if (vnode->status.type != AFS_FTYPE_FILE) {
vnode->cache = NULL; vnode->cache = NULL;
return; return;
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/key.h> #include <linux/key.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/sched.h> #include <linux/sched.h>
#define FSCACHE_USE_NEW_IO_API
#include <linux/fscache.h> #include <linux/fscache.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/uuid.h> #include <linux/uuid.h>
...@@ -31,6 +32,7 @@ ...@@ -31,6 +32,7 @@
struct pagevec; struct pagevec;
struct afs_call; struct afs_call;
struct afs_vnode;
/* /*
* Partial file-locking emulation mode. (The problem being that AFS3 only * Partial file-locking emulation mode. (The problem being that AFS3 only
...@@ -104,7 +106,9 @@ struct afs_call { ...@@ -104,7 +106,9 @@ struct afs_call {
struct afs_server *server; /* The fileserver record if fs op (pins ref) */ struct afs_server *server; /* The fileserver record if fs op (pins ref) */
struct afs_vlserver *vlserver; /* The vlserver record if vl op */ struct afs_vlserver *vlserver; /* The vlserver record if vl op */
void *request; /* request data (first part) */ void *request; /* request data (first part) */
size_t iov_len; /* Size of *iter to be used */
struct iov_iter def_iter; /* Default buffer/data iterator */ struct iov_iter def_iter; /* Default buffer/data iterator */
struct iov_iter *write_iter; /* Iterator defining write to be made */
struct iov_iter *iter; /* Iterator currently in use */ struct iov_iter *iter; /* Iterator currently in use */
union { /* Convenience for ->def_iter */ union { /* Convenience for ->def_iter */
struct kvec kvec[1]; struct kvec kvec[1];
...@@ -131,7 +135,6 @@ struct afs_call { ...@@ -131,7 +135,6 @@ struct afs_call {
unsigned char unmarshall; /* unmarshalling phase */ unsigned char unmarshall; /* unmarshalling phase */
unsigned char addr_ix; /* Address in ->alist */ unsigned char addr_ix; /* Address in ->alist */
bool drop_ref; /* T if need to drop ref for incoming call */ bool drop_ref; /* T if need to drop ref for incoming call */
bool send_pages; /* T if data from mapping should be sent */
bool need_attention; /* T if RxRPC poked us */ bool need_attention; /* T if RxRPC poked us */
bool async; /* T if asynchronous */ bool async; /* T if asynchronous */
bool upgrade; /* T to request service upgrade */ bool upgrade; /* T to request service upgrade */
...@@ -202,17 +205,19 @@ struct afs_read { ...@@ -202,17 +205,19 @@ struct afs_read {
loff_t pos; /* Where to start reading */ loff_t pos; /* Where to start reading */
loff_t len; /* How much we're asking for */ loff_t len; /* How much we're asking for */
loff_t actual_len; /* How much we're actually getting */ loff_t actual_len; /* How much we're actually getting */
loff_t remain; /* Amount remaining */
loff_t file_size; /* File size returned by server */ 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 */ afs_dataversion_t data_version; /* Version number returned by server */
refcount_t usage; refcount_t usage;
unsigned int index; /* Which page we're reading into */ unsigned int call_debug_id;
unsigned int nr_pages; unsigned int nr_pages;
unsigned int offset; /* offset into current page */ int error;
struct afs_vnode *vnode; void (*done)(struct afs_read *);
void (*page_done)(struct afs_read *); void (*cleanup)(struct afs_read *);
struct page **pages; struct iov_iter *iter; /* Iterator representing the buffer */
struct page *array[]; struct iov_iter def_iter; /* Default iterator */
}; };
/* /*
...@@ -739,6 +744,7 @@ struct afs_operation_ops { ...@@ -739,6 +744,7 @@ struct afs_operation_ops {
void (*issue_yfs_rpc)(struct afs_operation *op); void (*issue_yfs_rpc)(struct afs_operation *op);
void (*success)(struct afs_operation *op); void (*success)(struct afs_operation *op);
void (*aborted)(struct afs_operation *op); void (*aborted)(struct afs_operation *op);
void (*failed)(struct afs_operation *op);
void (*edit_dir)(struct afs_operation *op); void (*edit_dir)(struct afs_operation *op);
void (*put)(struct afs_operation *op); void (*put)(struct afs_operation *op);
}; };
...@@ -808,12 +814,11 @@ struct afs_operation { ...@@ -808,12 +814,11 @@ struct afs_operation {
afs_lock_type_t type; afs_lock_type_t type;
} lock; } lock;
struct { struct {
struct address_space *mapping; /* Pages being written from */ struct iov_iter *write_iter;
pgoff_t first; /* first page in mapping to deal with */ loff_t pos;
pgoff_t last; /* last page in mapping to deal with */ loff_t size;
unsigned first_offset; /* offset into mapping[first] */ loff_t i_size;
unsigned last_to; /* amount of mapping[last] */ bool laundering; /* Laundering page, PG_writeback not set */
bool laundering; /* Laundering page, PG_writeback not set */
} store; } store;
struct { struct {
struct iattr *attr; struct iattr *attr;
...@@ -875,31 +880,31 @@ struct afs_vnode_cache_aux { ...@@ -875,31 +880,31 @@ struct afs_vnode_cache_aux {
#define __AFS_PAGE_PRIV_MMAPPED 0x8000UL #define __AFS_PAGE_PRIV_MMAPPED 0x8000UL
#endif #endif
static inline unsigned int afs_page_dirty_resolution(void) static inline unsigned int afs_page_dirty_resolution(struct page *page)
{ {
int shift = PAGE_SHIFT - (__AFS_PAGE_PRIV_SHIFT - 1); int shift = thp_order(page) + PAGE_SHIFT - (__AFS_PAGE_PRIV_SHIFT - 1);
return (shift > 0) ? shift : 0; return (shift > 0) ? shift : 0;
} }
static inline size_t afs_page_dirty_from(unsigned long priv) static inline size_t afs_page_dirty_from(struct page *page, unsigned long priv)
{ {
unsigned long x = priv & __AFS_PAGE_PRIV_MASK; unsigned long x = priv & __AFS_PAGE_PRIV_MASK;
/* The lower bound is inclusive */ /* The lower bound is inclusive */
return x << afs_page_dirty_resolution(); return x << afs_page_dirty_resolution(page);
} }
static inline size_t afs_page_dirty_to(unsigned long priv) static inline size_t afs_page_dirty_to(struct page *page, unsigned long priv)
{ {
unsigned long x = (priv >> __AFS_PAGE_PRIV_SHIFT) & __AFS_PAGE_PRIV_MASK; unsigned long x = (priv >> __AFS_PAGE_PRIV_SHIFT) & __AFS_PAGE_PRIV_MASK;
/* The upper bound is immediately beyond the region */ /* The upper bound is immediately beyond the region */
return (x + 1) << afs_page_dirty_resolution(); return (x + 1) << afs_page_dirty_resolution(page);
} }
static inline unsigned long afs_page_dirty(size_t from, size_t to) static inline unsigned long afs_page_dirty(struct page *page, size_t from, size_t to)
{ {
unsigned int res = afs_page_dirty_resolution(); unsigned int res = afs_page_dirty_resolution(page);
from >>= res; from >>= res;
to = (to - 1) >> res; to = (to - 1) >> res;
return (to << __AFS_PAGE_PRIV_SHIFT) | from; return (to << __AFS_PAGE_PRIV_SHIFT) | from;
...@@ -1040,13 +1045,14 @@ extern void afs_dynroot_depopulate(struct super_block *); ...@@ -1040,13 +1045,14 @@ extern void afs_dynroot_depopulate(struct super_block *);
extern const struct address_space_operations afs_fs_aops; extern const struct address_space_operations afs_fs_aops;
extern const struct inode_operations afs_file_inode_operations; extern const struct inode_operations afs_file_inode_operations;
extern const struct file_operations afs_file_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 int afs_cache_wb_key(struct afs_vnode *, struct afs_file *);
extern void afs_put_wb_key(struct afs_wb_key *); extern void afs_put_wb_key(struct afs_wb_key *);
extern int afs_open(struct inode *, struct file *); extern int afs_open(struct inode *, struct file *);
extern int afs_release(struct inode *, struct file *); extern int afs_release(struct inode *, struct file *);
extern int afs_fetch_data(struct afs_vnode *, struct key *, struct afs_read *); extern int afs_fetch_data(struct afs_vnode *, struct afs_read *);
extern int afs_page_filler(void *, struct page *); extern struct afs_read *afs_alloc_read(gfp_t);
extern void afs_put_read(struct afs_read *); extern void afs_put_read(struct afs_read *);
static inline struct afs_read *afs_get_read(struct afs_read *req) static inline struct afs_read *afs_get_read(struct afs_read *req)
...@@ -1270,6 +1276,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c ...@@ -1270,6 +1276,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c
static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size) static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size)
{ {
call->iov_len = size;
call->kvec[0].iov_base = buf; call->kvec[0].iov_base = buf;
call->kvec[0].iov_len = size; call->kvec[0].iov_len = size;
iov_iter_kvec(&call->def_iter, READ, call->kvec, 1, size); iov_iter_kvec(&call->def_iter, READ, call->kvec, 1, size);
...@@ -1277,21 +1284,25 @@ static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t si ...@@ -1277,21 +1284,25 @@ static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t si
static inline void afs_extract_to_tmp(struct afs_call *call) static inline void afs_extract_to_tmp(struct afs_call *call)
{ {
call->iov_len = sizeof(call->tmp);
afs_extract_begin(call, &call->tmp, sizeof(call->tmp)); afs_extract_begin(call, &call->tmp, sizeof(call->tmp));
} }
static inline void afs_extract_to_tmp64(struct afs_call *call) static inline void afs_extract_to_tmp64(struct afs_call *call)
{ {
call->iov_len = sizeof(call->tmp64);
afs_extract_begin(call, &call->tmp64, sizeof(call->tmp64)); afs_extract_begin(call, &call->tmp64, sizeof(call->tmp64));
} }
static inline void afs_extract_discard(struct afs_call *call, size_t size) static inline void afs_extract_discard(struct afs_call *call, size_t size)
{ {
call->iov_len = size;
iov_iter_discard(&call->def_iter, READ, size); iov_iter_discard(&call->def_iter, READ, size);
} }
static inline void afs_extract_to_buf(struct afs_call *call, size_t size) static inline void afs_extract_to_buf(struct afs_call *call, size_t size)
{ {
call->iov_len = size;
afs_extract_begin(call, call->buffer, size); afs_extract_begin(call, call->buffer, size);
} }
......
...@@ -271,40 +271,6 @@ void afs_flat_call_destructor(struct afs_call *call) ...@@ -271,40 +271,6 @@ void afs_flat_call_destructor(struct afs_call *call)
call->buffer = NULL; call->buffer = NULL;
} }
#define AFS_BVEC_MAX 8
/*
* Load the given bvec with the next few pages.
*/
static void afs_load_bvec(struct afs_call *call, struct msghdr *msg,
struct bio_vec *bv, pgoff_t first, pgoff_t last,
unsigned offset)
{
struct afs_operation *op = call->op;
struct page *pages[AFS_BVEC_MAX];
unsigned int nr, n, i, to, bytes = 0;
nr = min_t(pgoff_t, last - first + 1, AFS_BVEC_MAX);
n = find_get_pages_contig(op->store.mapping, first, nr, pages);
ASSERTCMP(n, ==, nr);
msg->msg_flags |= MSG_MORE;
for (i = 0; i < nr; i++) {
to = PAGE_SIZE;
if (first + i >= last) {
to = op->store.last_to;
msg->msg_flags &= ~MSG_MORE;
}
bv[i].bv_page = pages[i];
bv[i].bv_len = to - offset;
bv[i].bv_offset = offset;
bytes += to - offset;
offset = 0;
}
iov_iter_bvec(&msg->msg_iter, WRITE, bv, nr, bytes);
}
/* /*
* Advance the AFS call state when the RxRPC call ends the transmit phase. * Advance the AFS call state when the RxRPC call ends the transmit phase.
*/ */
...@@ -317,42 +283,6 @@ static void afs_notify_end_request_tx(struct sock *sock, ...@@ -317,42 +283,6 @@ static void afs_notify_end_request_tx(struct sock *sock,
afs_set_call_state(call, AFS_CALL_CL_REQUESTING, AFS_CALL_CL_AWAIT_REPLY); afs_set_call_state(call, AFS_CALL_CL_REQUESTING, AFS_CALL_CL_AWAIT_REPLY);
} }
/*
* attach the data from a bunch of pages on an inode to a call
*/
static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
{
struct afs_operation *op = call->op;
struct bio_vec bv[AFS_BVEC_MAX];
unsigned int bytes, nr, loop, offset;
pgoff_t first = op->store.first, last = op->store.last;
int ret;
offset = op->store.first_offset;
op->store.first_offset = 0;
do {
afs_load_bvec(call, msg, bv, first, last, offset);
trace_afs_send_pages(call, msg, first, last, offset);
offset = 0;
bytes = msg->msg_iter.count;
nr = msg->msg_iter.nr_segs;
ret = rxrpc_kernel_send_data(op->net->socket, call->rxcall, msg,
bytes, afs_notify_end_request_tx);
for (loop = 0; loop < nr; loop++)
put_page(bv[loop].bv_page);
if (ret < 0)
break;
first += nr;
} while (first <= last);
trace_afs_sent_pages(call, op->store.first, last, first, ret);
return ret;
}
/* /*
* Initiate a call and synchronously queue up the parameters for dispatch. Any * Initiate a call and synchronously queue up the parameters for dispatch. Any
* error is stored into the call struct, which the caller must check for. * error is stored into the call struct, which the caller must check for.
...@@ -363,6 +293,7 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) ...@@ -363,6 +293,7 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
struct rxrpc_call *rxcall; struct rxrpc_call *rxcall;
struct msghdr msg; struct msghdr msg;
struct kvec iov[1]; struct kvec iov[1];
size_t len;
s64 tx_total_len; s64 tx_total_len;
int ret; int ret;
...@@ -383,21 +314,8 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) ...@@ -383,21 +314,8 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
* after the initial fixed part. * after the initial fixed part.
*/ */
tx_total_len = call->request_size; tx_total_len = call->request_size;
if (call->send_pages) { if (call->write_iter)
struct afs_operation *op = call->op; tx_total_len += iov_iter_count(call->write_iter);
if (op->store.last == op->store.first) {
tx_total_len += op->store.last_to - op->store.first_offset;
} else {
/* It looks mathematically like you should be able to
* combine the following lines with the ones above, but
* unsigned arithmetic is fun when it wraps...
*/
tx_total_len += PAGE_SIZE - op->store.first_offset;
tx_total_len += op->store.last_to;
tx_total_len += (op->store.last - op->store.first - 1) * PAGE_SIZE;
}
}
/* If the call is going to be asynchronous, we need an extra ref for /* If the call is going to be asynchronous, we need an extra ref for
* the call to hold itself so the caller need not hang on to its ref. * the call to hold itself so the caller need not hang on to its ref.
...@@ -439,7 +357,7 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) ...@@ -439,7 +357,7 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, call->request_size); iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, call->request_size);
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = MSG_WAITALL | (call->send_pages ? MSG_MORE : 0); msg.msg_flags = MSG_WAITALL | (call->write_iter ? MSG_MORE : 0);
ret = rxrpc_kernel_send_data(call->net->socket, rxcall, ret = rxrpc_kernel_send_data(call->net->socket, rxcall,
&msg, call->request_size, &msg, call->request_size,
...@@ -447,8 +365,18 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) ...@@ -447,8 +365,18 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
if (ret < 0) if (ret < 0)
goto error_do_abort; goto error_do_abort;
if (call->send_pages) { if (call->write_iter) {
ret = afs_send_pages(call, &msg); msg.msg_iter = *call->write_iter;
msg.msg_flags &= ~MSG_MORE;
trace_afs_send_data(call, &msg);
ret = rxrpc_kernel_send_data(call->net->socket,
call->rxcall, &msg,
iov_iter_count(&msg.msg_iter),
afs_notify_end_request_tx);
*call->write_iter = msg.msg_iter;
trace_afs_sent_data(call, &msg, ret);
if (ret < 0) if (ret < 0)
goto error_do_abort; goto error_do_abort;
} }
...@@ -466,9 +394,10 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) ...@@ -466,9 +394,10 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
rxrpc_kernel_abort_call(call->net->socket, rxcall, rxrpc_kernel_abort_call(call->net->socket, rxcall,
RX_USER_ABORT, ret, "KSD"); RX_USER_ABORT, ret, "KSD");
} else { } else {
len = 0;
iov_iter_kvec(&msg.msg_iter, READ, NULL, 0, 0); iov_iter_kvec(&msg.msg_iter, READ, NULL, 0, 0);
rxrpc_kernel_recv_data(call->net->socket, rxcall, rxrpc_kernel_recv_data(call->net->socket, rxcall,
&msg.msg_iter, false, &msg.msg_iter, &len, false,
&call->abort_code, &call->service_id); &call->abort_code, &call->service_id);
ac->abort_code = call->abort_code; ac->abort_code = call->abort_code;
ac->responded = true; ac->responded = true;
...@@ -498,12 +427,46 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) ...@@ -498,12 +427,46 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
_leave(" = %d", ret); _leave(" = %d", ret);
} }
/*
* Log remote abort codes that indicate that we have a protocol disagreement
* with the server.
*/
static void afs_log_error(struct afs_call *call, s32 remote_abort)
{
static int max = 0;
const char *msg;
int m;
switch (remote_abort) {
case RX_EOF: msg = "unexpected EOF"; break;
case RXGEN_CC_MARSHAL: msg = "client marshalling"; break;
case RXGEN_CC_UNMARSHAL: msg = "client unmarshalling"; break;
case RXGEN_SS_MARSHAL: msg = "server marshalling"; break;
case RXGEN_SS_UNMARSHAL: msg = "server unmarshalling"; break;
case RXGEN_DECODE: msg = "opcode decode"; break;
case RXGEN_SS_XDRFREE: msg = "server XDR cleanup"; break;
case RXGEN_CC_XDRFREE: msg = "client XDR cleanup"; break;
case -32: msg = "insufficient data"; break;
default:
return;
}
m = max;
if (m < 3) {
max = m + 1;
pr_notice("kAFS: Peer reported %s failure on %s [%pISp]\n",
msg, call->type->name,
&call->alist->addrs[call->addr_ix].transport);
}
}
/* /*
* deliver messages to a call * deliver messages to a call
*/ */
static void afs_deliver_to_call(struct afs_call *call) static void afs_deliver_to_call(struct afs_call *call)
{ {
enum afs_call_state state; enum afs_call_state state;
size_t len;
u32 abort_code, remote_abort = 0; u32 abort_code, remote_abort = 0;
int ret; int ret;
...@@ -516,10 +479,11 @@ static void afs_deliver_to_call(struct afs_call *call) ...@@ -516,10 +479,11 @@ static void afs_deliver_to_call(struct afs_call *call)
state == AFS_CALL_SV_AWAIT_ACK state == AFS_CALL_SV_AWAIT_ACK
) { ) {
if (state == AFS_CALL_SV_AWAIT_ACK) { if (state == AFS_CALL_SV_AWAIT_ACK) {
len = 0;
iov_iter_kvec(&call->def_iter, READ, NULL, 0, 0); iov_iter_kvec(&call->def_iter, READ, NULL, 0, 0);
ret = rxrpc_kernel_recv_data(call->net->socket, ret = rxrpc_kernel_recv_data(call->net->socket,
call->rxcall, &call->def_iter, call->rxcall, &call->def_iter,
false, &remote_abort, &len, false, &remote_abort,
&call->service_id); &call->service_id);
trace_afs_receive_data(call, &call->def_iter, false, ret); trace_afs_receive_data(call, &call->def_iter, false, ret);
...@@ -559,6 +523,7 @@ static void afs_deliver_to_call(struct afs_call *call) ...@@ -559,6 +523,7 @@ static void afs_deliver_to_call(struct afs_call *call)
goto out; goto out;
case -ECONNABORTED: case -ECONNABORTED:
ASSERTCMP(state, ==, AFS_CALL_COMPLETE); ASSERTCMP(state, ==, AFS_CALL_COMPLETE);
afs_log_error(call, call->abort_code);
goto done; goto done;
case -ENOTSUPP: case -ENOTSUPP:
abort_code = RXGEN_OPCODE; abort_code = RXGEN_OPCODE;
...@@ -929,10 +894,11 @@ int afs_extract_data(struct afs_call *call, bool want_more) ...@@ -929,10 +894,11 @@ int afs_extract_data(struct afs_call *call, bool want_more)
u32 remote_abort = 0; u32 remote_abort = 0;
int ret; int ret;
_enter("{%s,%zu},%d", call->type->name, iov_iter_count(iter), want_more); _enter("{%s,%zu,%zu},%d",
call->type->name, call->iov_len, iov_iter_count(iter), want_more);
ret = rxrpc_kernel_recv_data(net->socket, call->rxcall, iter, ret = rxrpc_kernel_recv_data(net->socket, call->rxcall, iter,
want_more, &remote_abort, &call->iov_len, want_more, &remote_abort,
&call->service_id); &call->service_id);
if (ret == 0 || ret == -EAGAIN) if (ret == 0 || ret == -EAGAIN)
return ret; return ret;
......
This diff is collapsed.
...@@ -360,22 +360,23 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) ...@@ -360,22 +360,23 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
struct afs_vnode_param *vp = &op->file[0]; struct afs_vnode_param *vp = &op->file[0];
struct afs_read *req = op->fetch.req; struct afs_read *req = op->fetch.req;
const __be32 *bp; const __be32 *bp;
unsigned int size;
int ret; int ret;
_enter("{%u,%zu/%llu}", _enter("{%u,%zu, %zu/%llu}",
call->unmarshall, iov_iter_count(call->iter), req->actual_len); call->unmarshall, call->iov_len, iov_iter_count(call->iter),
req->actual_len);
switch (call->unmarshall) { switch (call->unmarshall) {
case 0: case 0:
req->actual_len = 0; req->actual_len = 0;
req->index = 0;
req->offset = req->pos & (PAGE_SIZE - 1);
afs_extract_to_tmp64(call); afs_extract_to_tmp64(call);
call->unmarshall++; call->unmarshall++;
fallthrough; fallthrough;
/* extract the returned data length */ /* Extract the returned data length into ->actual_len. This
* may indicate more or less data than was requested will be
* returned.
*/
case 1: case 1:
_debug("extract data length"); _debug("extract data length");
ret = afs_extract_data(call, true); ret = afs_extract_data(call, true);
...@@ -384,44 +385,25 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) ...@@ -384,44 +385,25 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
req->actual_len = be64_to_cpu(call->tmp64); req->actual_len = be64_to_cpu(call->tmp64);
_debug("DATA length: %llu", req->actual_len); _debug("DATA length: %llu", req->actual_len);
req->remain = min(req->len, req->actual_len);
if (req->remain == 0) if (req->actual_len == 0)
goto no_more_data; goto no_more_data;
call->iter = req->iter;
call->iov_len = min(req->actual_len, req->len);
call->unmarshall++; call->unmarshall++;
begin_page:
ASSERTCMP(req->index, <, req->nr_pages);
if (req->remain > PAGE_SIZE - req->offset)
size = PAGE_SIZE - req->offset;
else
size = req->remain;
call->bvec[0].bv_len = size;
call->bvec[0].bv_offset = req->offset;
call->bvec[0].bv_page = req->pages[req->index];
iov_iter_bvec(&call->def_iter, READ, call->bvec, 1, size);
ASSERTCMP(size, <=, PAGE_SIZE);
fallthrough; fallthrough;
/* extract the returned data */ /* extract the returned data */
case 2: case 2:
_debug("extract data %zu/%llu", _debug("extract data %zu/%llu",
iov_iter_count(call->iter), req->remain); iov_iter_count(call->iter), req->actual_len);
ret = afs_extract_data(call, true); ret = afs_extract_data(call, true);
if (ret < 0) if (ret < 0)
return ret; return ret;
req->remain -= call->bvec[0].bv_len;
req->offset += call->bvec[0].bv_len;
ASSERTCMP(req->offset, <=, PAGE_SIZE);
if (req->offset == PAGE_SIZE) {
req->offset = 0;
req->index++;
if (req->remain > 0)
goto begin_page;
}
ASSERTCMP(req->remain, ==, 0); call->iter = &call->def_iter;
if (req->actual_len <= req->len) if (req->actual_len <= req->len)
goto no_more_data; goto no_more_data;
...@@ -467,17 +449,6 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call) ...@@ -467,17 +449,6 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
break; break;
} }
for (; req->index < req->nr_pages; req->index++) {
if (req->offset < PAGE_SIZE)
zero_user_segment(req->pages[req->index],
req->offset, PAGE_SIZE);
req->offset = 0;
}
if (req->page_done)
for (req->index = 0; req->index < req->nr_pages; req->index++)
req->page_done(req);
_leave(" = 0 [done]"); _leave(" = 0 [done]");
return 0; return 0;
} }
...@@ -516,6 +487,8 @@ void yfs_fs_fetch_data(struct afs_operation *op) ...@@ -516,6 +487,8 @@ void yfs_fs_fetch_data(struct afs_operation *op)
if (!call) if (!call)
return afs_op_nomem(op); return afs_op_nomem(op);
req->call_debug_id = call->debug_id;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
bp = xdr_encode_u32(bp, YFSFETCHDATA64); bp = xdr_encode_u32(bp, YFSFETCHDATA64);
...@@ -1102,25 +1075,15 @@ void yfs_fs_store_data(struct afs_operation *op) ...@@ -1102,25 +1075,15 @@ void yfs_fs_store_data(struct afs_operation *op)
{ {
struct afs_vnode_param *vp = &op->file[0]; struct afs_vnode_param *vp = &op->file[0];
struct afs_call *call; struct afs_call *call;
loff_t size, pos, i_size;
__be32 *bp; __be32 *bp;
_enter(",%x,{%llx:%llu},,", _enter(",%x,{%llx:%llu},,",
key_serial(op->key), vp->fid.vid, vp->fid.vnode); key_serial(op->key), vp->fid.vid, vp->fid.vnode);
size = (loff_t)op->store.last_to - (loff_t)op->store.first_offset;
if (op->store.first != op->store.last)
size += (loff_t)(op->store.last - op->store.first) << PAGE_SHIFT;
pos = (loff_t)op->store.first << PAGE_SHIFT;
pos += op->store.first_offset;
i_size = i_size_read(&vp->vnode->vfs_inode);
if (pos + size > i_size)
i_size = size + pos;
_debug("size %llx, at %llx, i_size %llx", _debug("size %llx, at %llx, i_size %llx",
(unsigned long long)size, (unsigned long long)pos, (unsigned long long)op->store.size,
(unsigned long long)i_size); (unsigned long long)op->store.pos,
(unsigned long long)op->store.i_size);
call = afs_alloc_flat_call(op->net, &yfs_RXYFSStoreData64, call = afs_alloc_flat_call(op->net, &yfs_RXYFSStoreData64,
sizeof(__be32) + sizeof(__be32) +
...@@ -1133,8 +1096,7 @@ void yfs_fs_store_data(struct afs_operation *op) ...@@ -1133,8 +1096,7 @@ void yfs_fs_store_data(struct afs_operation *op)
if (!call) if (!call)
return afs_op_nomem(op); return afs_op_nomem(op);
call->key = op->key; call->write_iter = op->store.write_iter;
call->send_pages = true;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1142,9 +1104,9 @@ void yfs_fs_store_data(struct afs_operation *op) ...@@ -1142,9 +1104,9 @@ void yfs_fs_store_data(struct afs_operation *op)
bp = xdr_encode_u32(bp, 0); /* RPC flags */ bp = xdr_encode_u32(bp, 0); /* RPC flags */
bp = xdr_encode_YFSFid(bp, &vp->fid); bp = xdr_encode_YFSFid(bp, &vp->fid);
bp = xdr_encode_YFSStoreStatus_mtime(bp, &op->mtime); bp = xdr_encode_YFSStoreStatus_mtime(bp, &op->mtime);
bp = xdr_encode_u64(bp, pos); bp = xdr_encode_u64(bp, op->store.pos);
bp = xdr_encode_u64(bp, size); bp = xdr_encode_u64(bp, op->store.size);
bp = xdr_encode_u64(bp, i_size); bp = xdr_encode_u64(bp, op->store.i_size);
yfs_check_req(call, bp); yfs_check_req(call, bp);
trace_afs_make_fs_call(call, &vp->fid); trace_afs_make_fs_call(call, &vp->fid);
......
...@@ -53,7 +53,7 @@ int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *, ...@@ -53,7 +53,7 @@ int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *,
struct msghdr *, size_t, struct msghdr *, size_t,
rxrpc_notify_end_tx_t); rxrpc_notify_end_tx_t);
int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *, int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *,
struct iov_iter *, bool, u32 *, u16 *); struct iov_iter *, size_t *, bool, u32 *, u16 *);
bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *, bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *,
u32, int, const char *); u32, int, const char *);
void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *); void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *);
......
...@@ -886,65 +886,52 @@ TRACE_EVENT(afs_call_done, ...@@ -886,65 +886,52 @@ TRACE_EVENT(afs_call_done,
__entry->rx_call) __entry->rx_call)
); );
TRACE_EVENT(afs_send_pages, TRACE_EVENT(afs_send_data,
TP_PROTO(struct afs_call *call, struct msghdr *msg, TP_PROTO(struct afs_call *call, struct msghdr *msg),
pgoff_t first, pgoff_t last, unsigned int offset),
TP_ARGS(call, msg, first, last, offset), TP_ARGS(call, msg),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(unsigned int, call ) __field(unsigned int, call )
__field(pgoff_t, first )
__field(pgoff_t, last )
__field(unsigned int, nr )
__field(unsigned int, bytes )
__field(unsigned int, offset )
__field(unsigned int, flags ) __field(unsigned int, flags )
__field(loff_t, offset )
__field(loff_t, count )
), ),
TP_fast_assign( TP_fast_assign(
__entry->call = call->debug_id; __entry->call = call->debug_id;
__entry->first = first;
__entry->last = last;
__entry->nr = msg->msg_iter.nr_segs;
__entry->bytes = msg->msg_iter.count;
__entry->offset = offset;
__entry->flags = msg->msg_flags; __entry->flags = msg->msg_flags;
__entry->offset = msg->msg_iter.xarray_start + msg->msg_iter.iov_offset;
__entry->count = iov_iter_count(&msg->msg_iter);
), ),
TP_printk(" c=%08x %lx-%lx-%lx b=%x o=%x f=%x", TP_printk(" c=%08x o=%llx n=%llx f=%x",
__entry->call, __entry->call, __entry->offset, __entry->count,
__entry->first, __entry->first + __entry->nr - 1, __entry->last,
__entry->bytes, __entry->offset,
__entry->flags) __entry->flags)
); );
TRACE_EVENT(afs_sent_pages, TRACE_EVENT(afs_sent_data,
TP_PROTO(struct afs_call *call, pgoff_t first, pgoff_t last, TP_PROTO(struct afs_call *call, struct msghdr *msg, int ret),
pgoff_t cursor, int ret),
TP_ARGS(call, first, last, cursor, ret), TP_ARGS(call, msg, ret),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(unsigned int, call ) __field(unsigned int, call )
__field(pgoff_t, first )
__field(pgoff_t, last )
__field(pgoff_t, cursor )
__field(int, ret ) __field(int, ret )
__field(loff_t, offset )
__field(loff_t, count )
), ),
TP_fast_assign( TP_fast_assign(
__entry->call = call->debug_id; __entry->call = call->debug_id;
__entry->first = first;
__entry->last = last;
__entry->cursor = cursor;
__entry->ret = ret; __entry->ret = ret;
__entry->offset = msg->msg_iter.xarray_start + msg->msg_iter.iov_offset;
__entry->count = iov_iter_count(&msg->msg_iter);
), ),
TP_printk(" c=%08x %lx-%lx c=%lx r=%d", TP_printk(" c=%08x o=%llx n=%llx r=%x",
__entry->call, __entry->call, __entry->offset, __entry->count,
__entry->first, __entry->last, __entry->ret)
__entry->cursor, __entry->ret)
); );
TRACE_EVENT(afs_dir_check_failed, TRACE_EVENT(afs_dir_check_failed,
...@@ -969,30 +956,33 @@ TRACE_EVENT(afs_dir_check_failed, ...@@ -969,30 +956,33 @@ TRACE_EVENT(afs_dir_check_failed,
); );
TRACE_EVENT(afs_page_dirty, TRACE_EVENT(afs_page_dirty,
TP_PROTO(struct afs_vnode *vnode, const char *where, TP_PROTO(struct afs_vnode *vnode, const char *where, struct page *page),
pgoff_t page, unsigned long priv),
TP_ARGS(vnode, where, page, priv), TP_ARGS(vnode, where, page),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(struct afs_vnode *, vnode ) __field(struct afs_vnode *, vnode )
__field(const char *, where ) __field(const char *, where )
__field(pgoff_t, page ) __field(pgoff_t, page )
__field(unsigned long, priv ) __field(unsigned long, from )
__field(unsigned long, to )
), ),
TP_fast_assign( TP_fast_assign(
__entry->vnode = vnode; __entry->vnode = vnode;
__entry->where = where; __entry->where = where;
__entry->page = page; __entry->page = page->index;
__entry->priv = priv; __entry->from = afs_page_dirty_from(page, page->private);
__entry->to = afs_page_dirty_to(page, page->private);
__entry->to |= (afs_is_page_dirty_mmapped(page->private) ?
(1UL << (BITS_PER_LONG - 1)) : 0);
), ),
TP_printk("vn=%p %lx %s %zx-%zx%s", TP_printk("vn=%p %lx %s %lx-%lx%s",
__entry->vnode, __entry->page, __entry->where, __entry->vnode, __entry->page, __entry->where,
afs_page_dirty_from(__entry->priv), __entry->from,
afs_page_dirty_to(__entry->priv), __entry->to & ~(1UL << (BITS_PER_LONG - 1)),
afs_is_page_dirty_mmapped(__entry->priv) ? " M" : "") __entry->to & (1UL << (BITS_PER_LONG - 1)) ? " M" : "")
); );
TRACE_EVENT(afs_call_state, TRACE_EVENT(afs_call_state,
......
...@@ -669,6 +669,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, ...@@ -669,6 +669,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
* @sock: The socket that the call exists on * @sock: The socket that the call exists on
* @call: The call to send data through * @call: The call to send data through
* @iter: The buffer to receive into * @iter: The buffer to receive into
* @_len: The amount of data we want to receive (decreased on return)
* @want_more: True if more data is expected to be read * @want_more: True if more data is expected to be read
* @_abort: Where the abort code is stored if -ECONNABORTED is returned * @_abort: Where the abort code is stored if -ECONNABORTED is returned
* @_service: Where to store the actual service ID (may be upgraded) * @_service: Where to store the actual service ID (may be upgraded)
...@@ -684,7 +685,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, ...@@ -684,7 +685,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
* *_abort should also be initialised to 0. * *_abort should also be initialised to 0.
*/ */
int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call,
struct iov_iter *iter, struct iov_iter *iter, size_t *_len,
bool want_more, u32 *_abort, u16 *_service) bool want_more, u32 *_abort, u16 *_service)
{ {
size_t offset = 0; size_t offset = 0;
...@@ -692,7 +693,7 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, ...@@ -692,7 +693,7 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call,
_enter("{%d,%s},%zu,%d", _enter("{%d,%s},%zu,%d",
call->debug_id, rxrpc_call_states[call->state], call->debug_id, rxrpc_call_states[call->state],
iov_iter_count(iter), want_more); *_len, want_more);
ASSERTCMP(call->state, !=, RXRPC_CALL_SERVER_SECURING); ASSERTCMP(call->state, !=, RXRPC_CALL_SERVER_SECURING);
...@@ -703,8 +704,8 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, ...@@ -703,8 +704,8 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call,
case RXRPC_CALL_SERVER_RECV_REQUEST: case RXRPC_CALL_SERVER_RECV_REQUEST:
case RXRPC_CALL_SERVER_ACK_REQUEST: case RXRPC_CALL_SERVER_ACK_REQUEST:
ret = rxrpc_recvmsg_data(sock, call, NULL, iter, ret = rxrpc_recvmsg_data(sock, call, NULL, iter,
iov_iter_count(iter), 0, *_len, 0, &offset);
&offset); *_len -= offset;
if (ret < 0) if (ret < 0)
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment