NFS client updates for Linux 5.20

Highlights include:
 
 Stable fixes:
 - pNFS/flexfiles: Fix infinite looping when the RDMA connection errors out
 
 Bugfixes:
 - NFS: fix port value parsing
 - SUNRPC: Reinitialise the backchannel request buffers before reuse
 - SUNRPC: fix expiry of auth creds
 - NFSv4: Fix races in the legacy idmapper upcall
 - NFS: O_DIRECT fixes from Jeff Layton
 - NFSv4.1: Fix OP_SEQUENCE error handling
 - SUNRPC: Fix an RPC/RDMA performance regression
 - NFS: Fix case insensitive renames
 - NFSv4/pnfs: Fix a use-after-free bug in open
 - NFSv4.1: RECLAIM_COMPLETE must handle EACCES
 
 Features:
 - NFSv4.1: session trunking enhancements
 - NFSv4.2: READ_PLUS performance optimisations
 - NFS: relax the rules for rsize/wsize mount options
 - NFS: don't unhash dentry during unlink/rename
 - SUNRPC: Fail faster on bad verifier
 - NFS/SUNRPC: Various tracing improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEESQctxSBg8JpV8KqEZwvnipYKAPIFAmLzy24ACgkQZwvnipYK
 APJJvhAAnVmv3jeOLjwDm+3X9xBevTq8PXAikWVeJgbSKCdjfqXO1J+XF49MpxXl
 N8PQZMqnwWkF3WvhvYobblwGSl6gJ3RnhVuTgdv4jiPl0ZWyS3XngJY0dCTnNgdL
 5O9AjoOMtnGVZwN5j8agymA8f2TUcel5mED6sAk10t2zDZY7VxuQqjp0m696jYjF
 0PKBmxoC4+6tXtYJS7d2PGRCTjfEUx2BLnnGuLOKsB7X8f63XmxJiu8/AIiY7spr
 M/M+BjAF3ok86dT1LjGlkvSNp23H70Wmsv98udfvxIWBm1l4972oCR/CS+kh3/mU
 dYM+oQ3JxxgKEN7Fdak+zU/+qma9q5z2rPFpSIT1fMEuaKN/7H2cbiHPi5RnEBLa
 AHWilX/lWBIMnZJZd9g3yYcGe6E/pkT6TqW5JY+2510koyfNER4IismAWMx2iYKU
 D0WSZOkmEBS/OYZxpTnqGwvS4L1szo9DN3c+yG2KXLifnmVPpjXZO25wahqSuUo3
 V6eYUCXRJmVg+IuXGsMNdrjxGYxD12xChoYzx5RlXls2lwHGeZr+iG3aL3+XayHa
 I1Kji3500UmfEOUUUr4UiQ428dOdL3QqNzVzdymN8Vh4d7v64LUL0GSseY+10Xrs
 xcbR6l/hwjBIo+I1Bi2mmv3W10tKErFy9eBIKzql3D6VHg7ESOo=
 =U00h
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-5.20-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client updates from Trond Myklebust:
 "Highlights include:

  Stable fixes:
   - pNFS/flexfiles: Fix infinite looping when the RDMA connection
     errors out

  Bugfixes:
   - NFS: fix port value parsing
   - SUNRPC: Reinitialise the backchannel request buffers before reuse
   - SUNRPC: fix expiry of auth creds
   - NFSv4: Fix races in the legacy idmapper upcall
   - NFS: O_DIRECT fixes from Jeff Layton
   - NFSv4.1: Fix OP_SEQUENCE error handling
   - SUNRPC: Fix an RPC/RDMA performance regression
   - NFS: Fix case insensitive renames
   - NFSv4/pnfs: Fix a use-after-free bug in open
   - NFSv4.1: RECLAIM_COMPLETE must handle EACCES

  Features:
   - NFSv4.1: session trunking enhancements
   - NFSv4.2: READ_PLUS performance optimisations
   - NFS: relax the rules for rsize/wsize mount options
   - NFS: don't unhash dentry during unlink/rename
   - SUNRPC: Fail faster on bad verifier
   - NFS/SUNRPC: Various tracing improvements"

* tag 'nfs-for-5.20-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (46 commits)
  NFS: Improve readpage/writepage tracing
  NFS: Improve O_DIRECT tracing
  NFS: Improve write error tracing
  NFS: don't unhash dentry during unlink/rename
  NFSv4/pnfs: Fix a use-after-free bug in open
  NFS: nfs_async_write_reschedule_io must not recurse into the writeback code
  SUNRPC: Don't reuse bvec on retransmission of the request
  SUNRPC: Reinitialise the backchannel request buffers before reuse
  NFSv4.1: RECLAIM_COMPLETE must handle EACCES
  NFSv4.1 probe offline transports for trunking on session creation
  SUNRPC create a function that probes only offline transports
  SUNRPC export xprt_iter_rewind function
  SUNRPC restructure rpc_clnt_setup_test_and_add_xprt
  NFSv4.1 remove xprt from xprt_switch if session trunking test fails
  SUNRPC create an rpc function that allows xprt removal from rpc_clnt
  SUNRPC enable back offline transports in trunking discovery
  SUNRPC create an iterator to list only OFFLINE xprts
  NFSv4.1 offline trunkable transports on DESTROY_SESSION
  SUNRPC add function to offline remove trunkable transports
  SUNRPC expose functions for offline remote xprt functionality
  ...
This commit is contained in:
Linus Torvalds 2022-08-10 14:04:32 -07:00
commit aeb6e6ac18
37 changed files with 1030 additions and 455 deletions

View File

@ -301,18 +301,14 @@ bl_validate_designator(struct pnfs_block_volume *v)
} }
} }
/*
* Try to open the udev path for the WWN. At least on Debian the udev
* by-id path will always point to the dm-multipath device if one exists.
*/
static struct block_device * static struct block_device *
bl_open_udev_path(struct pnfs_block_volume *v) bl_open_path(struct pnfs_block_volume *v, const char *prefix)
{ {
struct block_device *bdev; struct block_device *bdev;
const char *devname; const char *devname;
devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/wwn-0x%*phN", devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/%s%*phN",
v->scsi.designator_len, v->scsi.designator); prefix, v->scsi.designator_len, v->scsi.designator);
if (!devname) if (!devname)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -326,28 +322,6 @@ bl_open_udev_path(struct pnfs_block_volume *v)
return bdev; return bdev;
} }
/*
* Try to open the RH/Fedora specific dm-mpath udev path for this WWN, as the
* wwn- links will only point to the first discovered SCSI device there.
*/
static struct block_device *
bl_open_dm_mpath_udev_path(struct pnfs_block_volume *v)
{
struct block_device *bdev;
const char *devname;
devname = kasprintf(GFP_KERNEL,
"/dev/disk/by-id/dm-uuid-mpath-%d%*phN",
v->scsi.designator_type,
v->scsi.designator_len, v->scsi.designator);
if (!devname)
return ERR_PTR(-ENOMEM);
bdev = blkdev_get_by_path(devname, FMODE_READ | FMODE_WRITE, NULL);
kfree(devname);
return bdev;
}
static int static int
bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d, bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
@ -360,9 +334,15 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
if (!bl_validate_designator(v)) if (!bl_validate_designator(v))
return -EINVAL; return -EINVAL;
bdev = bl_open_dm_mpath_udev_path(v); /*
* Try to open the RH/Fedora specific dm-mpath udev path first, as the
* wwn- links will only point to the first discovered SCSI device there.
* On other distributions like Debian, the default SCSI by-id path will
* point to the dm-multipath device if one exists.
*/
bdev = bl_open_path(v, "dm-uuid-mpath-0x");
if (IS_ERR(bdev)) if (IS_ERR(bdev))
bdev = bl_open_udev_path(v); bdev = bl_open_path(v, "wwn-0x");
if (IS_ERR(bdev)) if (IS_ERR(bdev))
return PTR_ERR(bdev); return PTR_ERR(bdev);
d->bdev = bdev; d->bdev = bdev;

View File

@ -708,9 +708,9 @@ static int nfs_init_server(struct nfs_server *server,
} }
if (ctx->rsize) if (ctx->rsize)
server->rsize = nfs_block_size(ctx->rsize, NULL); server->rsize = nfs_io_size(ctx->rsize, clp->cl_proto);
if (ctx->wsize) if (ctx->wsize)
server->wsize = nfs_block_size(ctx->wsize, NULL); server->wsize = nfs_io_size(ctx->wsize, clp->cl_proto);
server->acregmin = ctx->acregmin * HZ; server->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ; server->acregmax = ctx->acregmax * HZ;
@ -755,18 +755,19 @@ error:
static void nfs_server_set_fsinfo(struct nfs_server *server, static void nfs_server_set_fsinfo(struct nfs_server *server,
struct nfs_fsinfo *fsinfo) struct nfs_fsinfo *fsinfo)
{ {
struct nfs_client *clp = server->nfs_client;
unsigned long max_rpc_payload, raw_max_rpc_payload; unsigned long max_rpc_payload, raw_max_rpc_payload;
/* Work out a lot of parameters */ /* Work out a lot of parameters */
if (server->rsize == 0) if (server->rsize == 0)
server->rsize = nfs_block_size(fsinfo->rtpref, NULL); server->rsize = nfs_io_size(fsinfo->rtpref, clp->cl_proto);
if (server->wsize == 0) if (server->wsize == 0)
server->wsize = nfs_block_size(fsinfo->wtpref, NULL); server->wsize = nfs_io_size(fsinfo->wtpref, clp->cl_proto);
if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax)
server->rsize = nfs_block_size(fsinfo->rtmax, NULL); server->rsize = nfs_io_size(fsinfo->rtmax, clp->cl_proto);
if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax)
server->wsize = nfs_block_size(fsinfo->wtmax, NULL); server->wsize = nfs_io_size(fsinfo->wtmax, clp->cl_proto);
raw_max_rpc_payload = rpc_max_payload(server->client); raw_max_rpc_payload = rpc_max_payload(server->client);
max_rpc_payload = nfs_block_size(raw_max_rpc_payload, NULL); max_rpc_payload = nfs_block_size(raw_max_rpc_payload, NULL);

View File

@ -1084,7 +1084,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
struct nfs_cache_array *array; struct nfs_cache_array *array;
unsigned int i; unsigned int i;
array = kmap(desc->page); array = kmap_local_page(desc->page);
for (i = desc->cache_entry_index; i < array->size; i++) { for (i = desc->cache_entry_index; i < array->size; i++) {
struct nfs_cache_array_entry *ent; struct nfs_cache_array_entry *ent;
@ -1110,7 +1110,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
if (array->page_is_eof) if (array->page_is_eof)
desc->eof = !desc->eob; desc->eof = !desc->eob;
kunmap(desc->page); kunmap_local(array);
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %llu\n", dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %llu\n",
(unsigned long long)desc->dir_cookie); (unsigned long long)desc->dir_cookie);
} }
@ -1739,6 +1739,10 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
goto out_bad; goto out_bad;
} }
if ((flags & LOOKUP_RENAME_TARGET) && d_count(dentry) < 2 &&
nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
goto out_bad;
if (nfs_verifier_is_delegated(dentry)) if (nfs_verifier_is_delegated(dentry))
return nfs_lookup_revalidate_delegated(dir, dentry, inode); return nfs_lookup_revalidate_delegated(dir, dentry, inode);
@ -1778,6 +1782,8 @@ __nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
int ret; int ret;
if (flags & LOOKUP_RCU) { if (flags & LOOKUP_RCU) {
if (dentry->d_fsdata == NFS_FSDATA_BLOCKED)
return -ECHILD;
parent = READ_ONCE(dentry->d_parent); parent = READ_ONCE(dentry->d_parent);
dir = d_inode_rcu(parent); dir = d_inode_rcu(parent);
if (!dir) if (!dir)
@ -1786,6 +1792,9 @@ __nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
if (parent != READ_ONCE(dentry->d_parent)) if (parent != READ_ONCE(dentry->d_parent))
return -ECHILD; return -ECHILD;
} else { } else {
/* Wait for unlink to complete */
wait_var_event(&dentry->d_fsdata,
dentry->d_fsdata != NFS_FSDATA_BLOCKED);
parent = dget_parent(dentry); parent = dget_parent(dentry);
ret = reval(d_inode(parent), dentry, flags); ret = reval(d_inode(parent), dentry, flags);
dput(parent); dput(parent);
@ -2454,7 +2463,6 @@ out:
int nfs_unlink(struct inode *dir, struct dentry *dentry) int nfs_unlink(struct inode *dir, struct dentry *dentry)
{ {
int error; int error;
int need_rehash = 0;
dfprintk(VFS, "NFS: unlink(%s/%lu, %pd)\n", dir->i_sb->s_id, dfprintk(VFS, "NFS: unlink(%s/%lu, %pd)\n", dir->i_sb->s_id,
dir->i_ino, dentry); dir->i_ino, dentry);
@ -2469,15 +2477,25 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry)
error = nfs_sillyrename(dir, dentry); error = nfs_sillyrename(dir, dentry);
goto out; goto out;
} }
if (!d_unhashed(dentry)) { /* We must prevent any concurrent open until the unlink
__d_drop(dentry); * completes. ->d_revalidate will wait for ->d_fsdata
need_rehash = 1; * to clear. We set it here to ensure no lookup succeeds until
} * the unlink is complete on the server.
*/
error = -ETXTBSY;
if (WARN_ON(dentry->d_flags & DCACHE_NFSFS_RENAMED) ||
WARN_ON(dentry->d_fsdata == NFS_FSDATA_BLOCKED))
goto out;
if (dentry->d_fsdata)
/* old devname */
kfree(dentry->d_fsdata);
dentry->d_fsdata = NFS_FSDATA_BLOCKED;
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
error = nfs_safe_remove(dentry); error = nfs_safe_remove(dentry);
nfs_dentry_remove_handle_error(dir, dentry, error); nfs_dentry_remove_handle_error(dir, dentry, error);
if (need_rehash) dentry->d_fsdata = NULL;
d_rehash(dentry); wake_up_var(&dentry->d_fsdata);
out: out:
trace_nfs_unlink_exit(dir, dentry, error); trace_nfs_unlink_exit(dir, dentry, error);
return error; return error;
@ -2584,6 +2602,15 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
} }
EXPORT_SYMBOL_GPL(nfs_link); EXPORT_SYMBOL_GPL(nfs_link);
static void
nfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data)
{
struct dentry *new_dentry = data->new_dentry;
new_dentry->d_fsdata = NULL;
wake_up_var(&new_dentry->d_fsdata);
}
/* /*
* RENAME * RENAME
* FIXME: Some nfsds, like the Linux user space nfsd, may generate a * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
@ -2614,8 +2641,9 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
{ {
struct inode *old_inode = d_inode(old_dentry); struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry); struct inode *new_inode = d_inode(new_dentry);
struct dentry *dentry = NULL, *rehash = NULL; struct dentry *dentry = NULL;
struct rpc_task *task; struct rpc_task *task;
bool must_unblock = false;
int error = -EBUSY; int error = -EBUSY;
if (flags) if (flags)
@ -2633,18 +2661,27 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
* the new target. * the new target.
*/ */
if (new_inode && !S_ISDIR(new_inode->i_mode)) { if (new_inode && !S_ISDIR(new_inode->i_mode)) {
/* /* We must prevent any concurrent open until the unlink
* To prevent any new references to the target during the * completes. ->d_revalidate will wait for ->d_fsdata
* rename, we unhash the dentry in advance. * to clear. We set it here to ensure no lookup succeeds until
* the unlink is complete on the server.
*/ */
if (!d_unhashed(new_dentry)) { error = -ETXTBSY;
d_drop(new_dentry); if (WARN_ON(new_dentry->d_flags & DCACHE_NFSFS_RENAMED) ||
rehash = new_dentry; WARN_ON(new_dentry->d_fsdata == NFS_FSDATA_BLOCKED))
goto out;
if (new_dentry->d_fsdata) {
/* old devname */
kfree(new_dentry->d_fsdata);
new_dentry->d_fsdata = NULL;
} }
spin_lock(&new_dentry->d_lock);
if (d_count(new_dentry) > 2) { if (d_count(new_dentry) > 2) {
int err; int err;
spin_unlock(&new_dentry->d_lock);
/* copy the target dentry's name */ /* copy the target dentry's name */
dentry = d_alloc(new_dentry->d_parent, dentry = d_alloc(new_dentry->d_parent,
&new_dentry->d_name); &new_dentry->d_name);
@ -2657,14 +2694,19 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
goto out; goto out;
new_dentry = dentry; new_dentry = dentry;
rehash = NULL;
new_inode = NULL; new_inode = NULL;
} else {
new_dentry->d_fsdata = NFS_FSDATA_BLOCKED;
must_unblock = true;
spin_unlock(&new_dentry->d_lock);
} }
} }
if (S_ISREG(old_inode->i_mode)) if (S_ISREG(old_inode->i_mode))
nfs_sync_inode(old_inode); nfs_sync_inode(old_inode);
task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL); task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry,
must_unblock ? nfs_unblock_rename : NULL);
if (IS_ERR(task)) { if (IS_ERR(task)) {
error = PTR_ERR(task); error = PTR_ERR(task);
goto out; goto out;
@ -2688,8 +2730,6 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
spin_unlock(&old_inode->i_lock); spin_unlock(&old_inode->i_lock);
} }
out: out:
if (rehash)
d_rehash(rehash);
trace_nfs_rename_exit(old_dir, old_dentry, trace_nfs_rename_exit(old_dir, old_dentry,
new_dir, new_dentry, error); new_dir, new_dentry, error);
if (!error) { if (!error) {

View File

@ -60,44 +60,12 @@
#include "iostat.h" #include "iostat.h"
#include "pnfs.h" #include "pnfs.h"
#include "fscache.h" #include "fscache.h"
#include "nfstrace.h"
#define NFSDBG_FACILITY NFSDBG_VFS #define NFSDBG_FACILITY NFSDBG_VFS
static struct kmem_cache *nfs_direct_cachep; static struct kmem_cache *nfs_direct_cachep;
struct nfs_direct_req {
struct kref kref; /* release manager */
/* I/O parameters */
struct nfs_open_context *ctx; /* file open context info */
struct nfs_lock_context *l_ctx; /* Lock context info */
struct kiocb * iocb; /* controlling i/o request */
struct inode * inode; /* target file of i/o */
/* completion state */
atomic_t io_count; /* i/os we're waiting for */
spinlock_t lock; /* protect completion state */
loff_t io_start; /* Start offset for I/O */
ssize_t count, /* bytes actually processed */
max_count, /* max expected count */
bytes_left, /* bytes left to be sent */
error; /* any reported error */
struct completion completion; /* wait for i/o completion */
/* commit state */
struct nfs_mds_commit_info mds_cinfo; /* Storage for cinfo */
struct pnfs_ds_commit_info ds_cinfo; /* Storage for cinfo */
struct work_struct work;
int flags;
/* for write */
#define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */
#define NFS_ODIRECT_RESCHED_WRITES (2) /* write verification failed */
/* for read */
#define NFS_ODIRECT_SHOULD_DIRTY (3) /* dirty user-space page after read */
#define NFS_ODIRECT_DONE INT_MAX /* write verification failed */
};
static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops; static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops;
static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops; static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops;
static void nfs_direct_write_complete(struct nfs_direct_req *dreq); static void nfs_direct_write_complete(struct nfs_direct_req *dreq);
@ -594,14 +562,17 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
struct nfs_page *req; struct nfs_page *req;
int status = data->task.tk_status; int status = data->task.tk_status;
trace_nfs_direct_commit_complete(dreq);
if (status < 0) { if (status < 0) {
/* Errors in commit are fatal */ /* Errors in commit are fatal */
dreq->error = status; dreq->error = status;
dreq->max_count = 0; dreq->max_count = 0;
dreq->count = 0; dreq->count = 0;
dreq->flags = NFS_ODIRECT_DONE; dreq->flags = NFS_ODIRECT_DONE;
} else if (dreq->flags == NFS_ODIRECT_DONE) } else {
status = dreq->error; status = dreq->error;
}
nfs_init_cinfo_from_dreq(&cinfo, dreq); nfs_init_cinfo_from_dreq(&cinfo, dreq);
@ -630,6 +601,8 @@ static void nfs_direct_resched_write(struct nfs_commit_info *cinfo,
{ {
struct nfs_direct_req *dreq = cinfo->dreq; struct nfs_direct_req *dreq = cinfo->dreq;
trace_nfs_direct_resched_write(dreq);
spin_lock(&dreq->lock); spin_lock(&dreq->lock);
if (dreq->flags != NFS_ODIRECT_DONE) if (dreq->flags != NFS_ODIRECT_DONE)
dreq->flags = NFS_ODIRECT_RESCHED_WRITES; dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
@ -694,6 +667,7 @@ static void nfs_direct_write_schedule_work(struct work_struct *work)
static void nfs_direct_write_complete(struct nfs_direct_req *dreq) static void nfs_direct_write_complete(struct nfs_direct_req *dreq)
{ {
trace_nfs_direct_write_complete(dreq);
queue_work(nfsiod_workqueue, &dreq->work); /* Calls nfs_direct_write_schedule_work */ queue_work(nfsiod_workqueue, &dreq->work); /* Calls nfs_direct_write_schedule_work */
} }
@ -704,6 +678,8 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
struct nfs_page *req = nfs_list_entry(hdr->pages.next); struct nfs_page *req = nfs_list_entry(hdr->pages.next);
int flags = NFS_ODIRECT_DONE; int flags = NFS_ODIRECT_DONE;
trace_nfs_direct_write_completion(dreq);
nfs_init_cinfo_from_dreq(&cinfo, dreq); nfs_init_cinfo_from_dreq(&cinfo, dreq);
spin_lock(&dreq->lock); spin_lock(&dreq->lock);
@ -713,7 +689,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
} }
nfs_direct_count_bytes(dreq, hdr); nfs_direct_count_bytes(dreq, hdr);
if (hdr->good_bytes != 0 && nfs_write_need_commit(hdr)) { if (test_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags)) {
if (!dreq->flags) if (!dreq->flags)
dreq->flags = NFS_ODIRECT_DO_COMMIT; dreq->flags = NFS_ODIRECT_DO_COMMIT;
flags = dreq->flags; flags = dreq->flags;
@ -758,6 +734,8 @@ static void nfs_direct_write_reschedule_io(struct nfs_pgio_header *hdr)
{ {
struct nfs_direct_req *dreq = hdr->dreq; struct nfs_direct_req *dreq = hdr->dreq;
trace_nfs_direct_write_reschedule_io(dreq);
spin_lock(&dreq->lock); spin_lock(&dreq->lock);
if (dreq->error == 0) { if (dreq->error == 0) {
dreq->flags = NFS_ODIRECT_RESCHED_WRITES; dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
@ -798,6 +776,8 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
size_t requested_bytes = 0; size_t requested_bytes = 0;
size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE); size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE);
trace_nfs_direct_write_schedule_iovec(dreq);
nfs_pageio_init_write(&desc, inode, ioflags, false, nfs_pageio_init_write(&desc, inode, ioflags, false,
&nfs_direct_write_completion_ops); &nfs_direct_write_completion_ops);
desc.pg_dreq = dreq; desc.pg_dreq = dreq;

View File

@ -661,8 +661,6 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
result = filemap_fdatawait_range(file->f_mapping, result = filemap_fdatawait_range(file->f_mapping,
iocb->ki_pos - written, iocb->ki_pos - written,
iocb->ki_pos - 1); iocb->ki_pos - 1);
if (result < 0)
goto out;
} }
result = generic_write_sync(iocb, written); result = generic_write_sync(iocb, written);
if (result < 0) if (result < 0)

View File

@ -181,6 +181,8 @@ static int filelayout_async_handle_error(struct rpc_task *task,
case -EIO: case -EIO:
case -ETIMEDOUT: case -ETIMEDOUT:
case -EPIPE: case -EPIPE:
case -EPROTO:
case -ENODEV:
dprintk("%s DS connection error %d\n", __func__, dprintk("%s DS connection error %d\n", __func__,
task->tk_status); task->tk_status);
nfs4_mark_deviceid_unavailable(devid); nfs4_mark_deviceid_unavailable(devid);

View File

@ -1131,6 +1131,8 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
case -EIO: case -EIO:
case -ETIMEDOUT: case -ETIMEDOUT:
case -EPIPE: case -EPIPE:
case -EPROTO:
case -ENODEV:
dprintk("%s DS connection error %d\n", __func__, dprintk("%s DS connection error %d\n", __func__,
task->tk_status); task->tk_status);
nfs4_delete_deviceid(devid->ld, devid->nfs_client, nfs4_delete_deviceid(devid->ld, devid->nfs_client,
@ -1236,6 +1238,8 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg,
case -ENOBUFS: case -ENOBUFS:
case -EPIPE: case -EPIPE:
case -EPERM: case -EPERM:
case -EPROTO:
case -ENODEV:
*op_status = status = NFS4ERR_NXIO; *op_status = status = NFS4ERR_NXIO;
break; break;
case -EACCES: case -EACCES:

View File

@ -113,8 +113,10 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
goto out_err_drain_dsaddrs; goto out_err_drain_dsaddrs;
ds_versions[i].version = be32_to_cpup(p++); ds_versions[i].version = be32_to_cpup(p++);
ds_versions[i].minor_version = be32_to_cpup(p++); ds_versions[i].minor_version = be32_to_cpup(p++);
ds_versions[i].rsize = nfs_block_size(be32_to_cpup(p++), NULL); ds_versions[i].rsize = nfs_io_size(be32_to_cpup(p++),
ds_versions[i].wsize = nfs_block_size(be32_to_cpup(p++), NULL); server->nfs_client->cl_proto);
ds_versions[i].wsize = nfs_io_size(be32_to_cpup(p++),
server->nfs_client->cl_proto);
ds_versions[i].tightly_coupled = be32_to_cpup(p); ds_versions[i].tightly_coupled = be32_to_cpup(p);
if (ds_versions[i].rsize > NFS_MAX_FILE_IO_SIZE) if (ds_versions[i].rsize > NFS_MAX_FILE_IO_SIZE)

View File

@ -21,6 +21,8 @@
#include "nfs.h" #include "nfs.h"
#include "internal.h" #include "internal.h"
#include "nfstrace.h"
#define NFSDBG_FACILITY NFSDBG_MOUNT #define NFSDBG_FACILITY NFSDBG_MOUNT
#if IS_ENABLED(CONFIG_NFS_V3) #if IS_ENABLED(CONFIG_NFS_V3)
@ -284,7 +286,6 @@ static int nfs_verify_server_address(struct sockaddr *addr)
} }
} }
dfprintk(MOUNT, "NFS: Invalid IP address specified\n");
return 0; return 0;
} }
@ -378,7 +379,7 @@ static int nfs_parse_security_flavors(struct fs_context *fc,
char *string = param->string, *p; char *string = param->string, *p;
int ret; int ret;
dfprintk(MOUNT, "NFS: parsing %s=%s option\n", param->key, param->string); trace_nfs_mount_assign(param->key, string);
while ((p = strsep(&string, ":")) != NULL) { while ((p = strsep(&string, ":")) != NULL) {
if (!*p) if (!*p)
@ -480,11 +481,11 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
unsigned int len; unsigned int len;
int ret, opt; int ret, opt;
dfprintk(MOUNT, "NFS: parsing nfs mount option '%s'\n", param->key); trace_nfs_mount_option(param);
opt = fs_parse(fc, nfs_fs_parameters, param, &result); opt = fs_parse(fc, nfs_fs_parameters, param, &result);
if (opt < 0) if (opt < 0)
return ctx->sloppy ? 1 : opt; return (opt == -ENOPARAM && ctx->sloppy) ? 1 : opt;
if (fc->security) if (fc->security)
ctx->has_sec_mnt_opts = 1; ctx->has_sec_mnt_opts = 1;
@ -683,6 +684,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
return ret; return ret;
break; break;
case Opt_vers: case Opt_vers:
trace_nfs_mount_assign(param->key, param->string);
ret = nfs_parse_version_string(fc, param->string); ret = nfs_parse_version_string(fc, param->string);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -694,6 +696,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
break; break;
case Opt_proto: case Opt_proto:
trace_nfs_mount_assign(param->key, param->string);
protofamily = AF_INET; protofamily = AF_INET;
switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) { switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
case Opt_xprt_udp6: case Opt_xprt_udp6:
@ -729,6 +732,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
break; break;
case Opt_mountproto: case Opt_mountproto:
trace_nfs_mount_assign(param->key, param->string);
mountfamily = AF_INET; mountfamily = AF_INET;
switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) { switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
case Opt_xprt_udp6: case Opt_xprt_udp6:
@ -751,6 +755,7 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
break; break;
case Opt_addr: case Opt_addr:
trace_nfs_mount_assign(param->key, param->string);
len = rpc_pton(fc->net_ns, param->string, param->size, len = rpc_pton(fc->net_ns, param->string, param->size,
&ctx->nfs_server.address, &ctx->nfs_server.address,
sizeof(ctx->nfs_server._address)); sizeof(ctx->nfs_server._address));
@ -759,16 +764,19 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
ctx->nfs_server.addrlen = len; ctx->nfs_server.addrlen = len;
break; break;
case Opt_clientaddr: case Opt_clientaddr:
trace_nfs_mount_assign(param->key, param->string);
kfree(ctx->client_address); kfree(ctx->client_address);
ctx->client_address = param->string; ctx->client_address = param->string;
param->string = NULL; param->string = NULL;
break; break;
case Opt_mounthost: case Opt_mounthost:
trace_nfs_mount_assign(param->key, param->string);
kfree(ctx->mount_server.hostname); kfree(ctx->mount_server.hostname);
ctx->mount_server.hostname = param->string; ctx->mount_server.hostname = param->string;
param->string = NULL; param->string = NULL;
break; break;
case Opt_mountaddr: case Opt_mountaddr:
trace_nfs_mount_assign(param->key, param->string);
len = rpc_pton(fc->net_ns, param->string, param->size, len = rpc_pton(fc->net_ns, param->string, param->size,
&ctx->mount_server.address, &ctx->mount_server.address,
sizeof(ctx->mount_server._address)); sizeof(ctx->mount_server._address));
@ -846,7 +854,6 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
*/ */
case Opt_sloppy: case Opt_sloppy:
ctx->sloppy = true; ctx->sloppy = true;
dfprintk(MOUNT, "NFS: relaxing parsing rules\n");
break; break;
} }
@ -879,10 +886,8 @@ static int nfs_parse_source(struct fs_context *fc,
size_t len; size_t len;
const char *end; const char *end;
if (unlikely(!dev_name || !*dev_name)) { if (unlikely(!dev_name || !*dev_name))
dfprintk(MOUNT, "NFS: device name not specified\n");
return -EINVAL; return -EINVAL;
}
/* Is the host name protected with square brakcets? */ /* Is the host name protected with square brakcets? */
if (*dev_name == '[') { if (*dev_name == '[') {
@ -922,7 +927,7 @@ static int nfs_parse_source(struct fs_context *fc,
if (!ctx->nfs_server.export_path) if (!ctx->nfs_server.export_path)
goto out_nomem; goto out_nomem;
dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", ctx->nfs_server.export_path); trace_nfs_mount_path(ctx->nfs_server.export_path);
return 0; return 0;
out_bad_devname: out_bad_devname:
@ -1116,7 +1121,6 @@ out_no_sec:
return nfs_invalf(fc, "NFS: nfs_mount_data version supports only AUTH_SYS"); return nfs_invalf(fc, "NFS: nfs_mount_data version supports only AUTH_SYS");
out_nomem: out_nomem:
dfprintk(MOUNT, "NFS: not enough memory to handle mount options");
return -ENOMEM; return -ENOMEM;
out_no_address: out_no_address:
@ -1248,7 +1252,7 @@ static int nfs4_parse_monolithic(struct fs_context *fc,
if (IS_ERR(c)) if (IS_ERR(c))
return PTR_ERR(c); return PTR_ERR(c);
ctx->nfs_server.export_path = c; ctx->nfs_server.export_path = c;
dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c); trace_nfs_mount_path(c);
c = strndup_user(data->client_addr.data, 16); c = strndup_user(data->client_addr.data, 16);
if (IS_ERR(c)) if (IS_ERR(c))

View File

@ -706,6 +706,24 @@ unsigned long nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
return nfs_block_bits(bsize, nrbitsp); return nfs_block_bits(bsize, nrbitsp);
} }
/*
* Compute and set NFS server rsize / wsize
*/
static inline
unsigned long nfs_io_size(unsigned long iosize, enum xprt_transports proto)
{
if (iosize < NFS_MIN_FILE_IO_SIZE)
iosize = NFS_DEF_FILE_IO_SIZE;
else if (iosize >= NFS_MAX_FILE_IO_SIZE)
iosize = NFS_MAX_FILE_IO_SIZE;
else
iosize = iosize & PAGE_MASK;
if (proto == XPRT_TRANSPORT_UDP)
return nfs_block_bits(iosize, NULL);
return iosize;
}
/* /*
* Determine the maximum file size for a superblock * Determine the maximum file size for a superblock
*/ */
@ -861,3 +879,36 @@ static inline void nfs_set_port(struct sockaddr *sap, int *port,
rpc_set_port(sap, *port); rpc_set_port(sap, *port);
} }
struct nfs_direct_req {
struct kref kref; /* release manager */
/* I/O parameters */
struct nfs_open_context *ctx; /* file open context info */
struct nfs_lock_context *l_ctx; /* Lock context info */
struct kiocb * iocb; /* controlling i/o request */
struct inode * inode; /* target file of i/o */
/* completion state */
atomic_t io_count; /* i/os we're waiting for */
spinlock_t lock; /* protect completion state */
loff_t io_start; /* Start offset for I/O */
ssize_t count, /* bytes actually processed */
max_count, /* max expected count */
bytes_left, /* bytes left to be sent */
error; /* any reported error */
struct completion completion; /* wait for i/o completion */
/* commit state */
struct nfs_mds_commit_info mds_cinfo; /* Storage for cinfo */
struct pnfs_ds_commit_info ds_cinfo; /* Storage for cinfo */
struct work_struct work;
int flags;
/* for write */
#define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */
#define NFS_ODIRECT_RESCHED_WRITES (2) /* write verification failed */
/* for read */
#define NFS_ODIRECT_SHOULD_DIRTY (3) /* dirty user-space page after read */
#define NFS_ODIRECT_DONE INT_MAX /* write verification failed */
};

View File

@ -108,7 +108,6 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
if (mds_srv->flags & NFS_MOUNT_NORESVPORT) if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
__set_bit(NFS_CS_NOPING, &cl_init.init_flags);
__set_bit(NFS_CS_DS, &cl_init.init_flags); __set_bit(NFS_CS_DS, &cl_init.init_flags);
/* Use the MDS nfs_client cl_ipaddr. */ /* Use the MDS nfs_client cl_ipaddr. */

View File

@ -1025,73 +1025,84 @@ static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *re
return decode_op_hdr(xdr, OP_DEALLOCATE); return decode_op_hdr(xdr, OP_DEALLOCATE);
} }
static int decode_read_plus_data(struct xdr_stream *xdr, struct read_plus_segment {
struct nfs_pgio_args *args, enum data_content4 type;
struct nfs_pgio_res *res)
{
uint32_t count, recvd;
uint64_t offset; uint64_t offset;
union {
struct {
uint64_t length;
} hole;
struct {
uint32_t length;
unsigned int from;
} data;
};
};
static inline uint64_t read_plus_segment_length(struct read_plus_segment *seg)
{
return seg->type == NFS4_CONTENT_DATA ? seg->data.length : seg->hole.length;
}
static int decode_read_plus_segment(struct xdr_stream *xdr,
struct read_plus_segment *seg)
{
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 8 + 4); p = xdr_inline_decode(xdr, 4);
if (!p) if (!p)
return 1; return -EIO;
seg->type = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &offset); p = xdr_inline_decode(xdr, seg->type == NFS4_CONTENT_DATA ? 12 : 16);
count = be32_to_cpup(p); if (!p)
recvd = xdr_align_data(xdr, res->count, xdr_align_size(count)); return -EIO;
if (recvd > count) p = xdr_decode_hyper(p, &seg->offset);
recvd = count;
if (res->count + recvd > args->count) { if (seg->type == NFS4_CONTENT_DATA) {
if (args->count > res->count) struct xdr_buf buf;
res->count += args->count - res->count; uint32_t len = be32_to_cpup(p);
return 1;
} seg->data.length = len;
res->count += recvd; seg->data.from = xdr_stream_pos(xdr);
if (count > recvd)
return 1; if (!xdr_stream_subsegment(xdr, &buf, xdr_align_size(len)))
return -EIO;
} else if (seg->type == NFS4_CONTENT_HOLE) {
xdr_decode_hyper(p, &seg->hole.length);
} else
return -EINVAL;
return 0; return 0;
} }
static int decode_read_plus_hole(struct xdr_stream *xdr, static int process_read_plus_segment(struct xdr_stream *xdr,
struct nfs_pgio_args *args, struct nfs_pgio_args *args,
struct nfs_pgio_res *res, uint32_t *eof) struct nfs_pgio_res *res,
struct read_plus_segment *seg)
{ {
uint64_t offset, length, recvd; unsigned long offset = seg->offset;
__be32 *p; unsigned long length = read_plus_segment_length(seg);
unsigned int bufpos;
p = xdr_inline_decode(xdr, 8 + 8); if (offset + length < args->offset)
if (!p) return 0;
return 1; else if (offset > args->offset + args->count) {
res->eof = 0;
p = xdr_decode_hyper(p, &offset); return 0;
p = xdr_decode_hyper(p, &length); } else if (offset < args->offset) {
if (offset != args->offset + res->count) { length -= (args->offset - offset);
/* Server returned an out-of-sequence extent */ offset = args->offset;
if (offset > args->offset + res->count || } else if (offset + length > args->offset + args->count) {
offset + length < args->offset + res->count) { length = (args->offset + args->count) - offset;
dprintk("NFS: server returned out of sequence extent: " res->eof = 0;
"offset/size = %llu/%llu != expected %llu\n",
(unsigned long long)offset,
(unsigned long long)length,
(unsigned long long)(args->offset +
res->count));
return 1;
}
length -= args->offset + res->count - offset;
} }
if (length + res->count > args->count) {
*eof = 0;
if (unlikely(res->count >= args->count))
return 1;
length = args->count - res->count;
}
recvd = xdr_expand_hole(xdr, res->count, length);
res->count += recvd;
if (recvd < length) bufpos = xdr->buf->head[0].iov_len + (offset - args->offset);
return 1; if (seg->type == NFS4_CONTENT_HOLE)
return 0; return xdr_stream_zero(xdr, bufpos, length);
else
return xdr_stream_move_subsegment(xdr, seg->data.from, bufpos, length);
} }
static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res) static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
@ -1099,8 +1110,10 @@ static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
struct nfs_pgio_header *hdr = struct nfs_pgio_header *hdr =
container_of(res, struct nfs_pgio_header, res); container_of(res, struct nfs_pgio_header, res);
struct nfs_pgio_args *args = &hdr->args; struct nfs_pgio_args *args = &hdr->args;
uint32_t eof, segments, type; uint32_t segments;
struct read_plus_segment *segs;
int status, i; int status, i;
char scratch_buf[16];
__be32 *p; __be32 *p;
status = decode_op_hdr(xdr, OP_READ_PLUS); status = decode_op_hdr(xdr, OP_READ_PLUS);
@ -1112,38 +1125,31 @@ static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
return -EIO; return -EIO;
res->count = 0; res->count = 0;
eof = be32_to_cpup(p++); res->eof = be32_to_cpup(p++);
segments = be32_to_cpup(p++); segments = be32_to_cpup(p++);
if (segments == 0) if (segments == 0)
goto out; return status;
segs = kmalloc_array(segments, sizeof(*segs), GFP_KERNEL);
if (!segs)
return -ENOMEM;
xdr_set_scratch_buffer(xdr, &scratch_buf, 32);
status = -EIO;
for (i = 0; i < segments; i++) { for (i = 0; i < segments; i++) {
p = xdr_inline_decode(xdr, 4); status = decode_read_plus_segment(xdr, &segs[i]);
if (!p)
goto early_out;
type = be32_to_cpup(p++);
if (type == NFS4_CONTENT_DATA)
status = decode_read_plus_data(xdr, args, res);
else if (type == NFS4_CONTENT_HOLE)
status = decode_read_plus_hole(xdr, args, res, &eof);
else
return -EINVAL;
if (status < 0) if (status < 0)
return status; goto out;
if (status > 0)
goto early_out;
} }
xdr_set_pagelen(xdr, xdr_align_size(args->count));
for (i = segments; i > 0; i--)
res->count += process_read_plus_segment(xdr, args, res, &segs[i-1]);
status = 0;
out: out:
res->eof = eof; kfree(segs);
return 0; return status;
early_out:
if (unlikely(!i))
return -EIO;
res->eof = 0;
return 0;
} }
static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res) static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res)

View File

@ -1161,9 +1161,9 @@ static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
return error; return error;
if (ctx->rsize) if (ctx->rsize)
server->rsize = nfs_block_size(ctx->rsize, NULL); server->rsize = nfs_io_size(ctx->rsize, server->nfs_client->cl_proto);
if (ctx->wsize) if (ctx->wsize)
server->wsize = nfs_block_size(ctx->wsize, NULL); server->wsize = nfs_io_size(ctx->wsize, server->nfs_client->cl_proto);
server->acregmin = ctx->acregmin * HZ; server->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ; server->acregmax = ctx->acregmax * HZ;

View File

@ -561,22 +561,20 @@ nfs_idmap_prepare_pipe_upcall(struct idmap *idmap,
return true; return true;
} }
static void static void nfs_idmap_complete_pipe_upcall(struct idmap_legacy_upcalldata *data,
nfs_idmap_complete_pipe_upcall_locked(struct idmap *idmap, int ret) int ret)
{ {
struct key *authkey = idmap->idmap_upcall_data->authkey; complete_request_key(data->authkey, ret);
key_put(data->authkey);
kfree(idmap->idmap_upcall_data); kfree(data);
idmap->idmap_upcall_data = NULL;
complete_request_key(authkey, ret);
key_put(authkey);
} }
static void static void nfs_idmap_abort_pipe_upcall(struct idmap *idmap,
nfs_idmap_abort_pipe_upcall(struct idmap *idmap, int ret) struct idmap_legacy_upcalldata *data,
int ret)
{ {
if (idmap->idmap_upcall_data != NULL) if (cmpxchg(&idmap->idmap_upcall_data, data, NULL) == data)
nfs_idmap_complete_pipe_upcall_locked(idmap, ret); nfs_idmap_complete_pipe_upcall(data, ret);
} }
static int nfs_idmap_legacy_upcall(struct key *authkey, void *aux) static int nfs_idmap_legacy_upcall(struct key *authkey, void *aux)
@ -613,7 +611,7 @@ static int nfs_idmap_legacy_upcall(struct key *authkey, void *aux)
ret = rpc_queue_upcall(idmap->idmap_pipe, msg); ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
if (ret < 0) if (ret < 0)
nfs_idmap_abort_pipe_upcall(idmap, ret); nfs_idmap_abort_pipe_upcall(idmap, data, ret);
return ret; return ret;
out2: out2:
@ -669,6 +667,7 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
struct request_key_auth *rka; struct request_key_auth *rka;
struct rpc_inode *rpci = RPC_I(file_inode(filp)); struct rpc_inode *rpci = RPC_I(file_inode(filp));
struct idmap *idmap = (struct idmap *)rpci->private; struct idmap *idmap = (struct idmap *)rpci->private;
struct idmap_legacy_upcalldata *data;
struct key *authkey; struct key *authkey;
struct idmap_msg im; struct idmap_msg im;
size_t namelen_in; size_t namelen_in;
@ -678,10 +677,11 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
* will have been woken up and someone else may now have used * will have been woken up and someone else may now have used
* idmap_key_cons - so after this point we may no longer touch it. * idmap_key_cons - so after this point we may no longer touch it.
*/ */
if (idmap->idmap_upcall_data == NULL) data = xchg(&idmap->idmap_upcall_data, NULL);
if (data == NULL)
goto out_noupcall; goto out_noupcall;
authkey = idmap->idmap_upcall_data->authkey; authkey = data->authkey;
rka = get_request_key_auth(authkey); rka = get_request_key_auth(authkey);
if (mlen != sizeof(im)) { if (mlen != sizeof(im)) {
@ -703,18 +703,17 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) { if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
ret = nfs_idmap_read_and_verify_message(&im, ret = nfs_idmap_read_and_verify_message(&im, &data->idmap_msg,
&idmap->idmap_upcall_data->idmap_msg, rka->target_key, authkey);
rka->target_key, authkey);
if (ret >= 0) { if (ret >= 0) {
key_set_timeout(rka->target_key, nfs_idmap_cache_timeout); key_set_timeout(rka->target_key, nfs_idmap_cache_timeout);
ret = mlen; ret = mlen;
} }
out: out:
nfs_idmap_complete_pipe_upcall_locked(idmap, ret); nfs_idmap_complete_pipe_upcall(data, ret);
out_noupcall: out_noupcall:
return ret; return ret;
} }
@ -728,7 +727,7 @@ idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
struct idmap *idmap = data->idmap; struct idmap *idmap = data->idmap;
if (msg->errno) if (msg->errno)
nfs_idmap_abort_pipe_upcall(idmap, msg->errno); nfs_idmap_abort_pipe_upcall(idmap, data, msg->errno);
} }
static void static void
@ -736,8 +735,11 @@ idmap_release_pipe(struct inode *inode)
{ {
struct rpc_inode *rpci = RPC_I(inode); struct rpc_inode *rpci = RPC_I(inode);
struct idmap *idmap = (struct idmap *)rpci->private; struct idmap *idmap = (struct idmap *)rpci->private;
struct idmap_legacy_upcalldata *data;
nfs_idmap_abort_pipe_upcall(idmap, -EPIPE); data = xchg(&idmap->idmap_upcall_data, NULL);
if (data)
nfs_idmap_complete_pipe_upcall(data, -EPIPE);
} }
int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid) int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid)

View File

@ -784,10 +784,9 @@ static void nfs4_slot_sequence_record_sent(struct nfs4_slot *slot,
if ((s32)(seqnr - slot->seq_nr_highest_sent) > 0) if ((s32)(seqnr - slot->seq_nr_highest_sent) > 0)
slot->seq_nr_highest_sent = seqnr; slot->seq_nr_highest_sent = seqnr;
} }
static void nfs4_slot_sequence_acked(struct nfs4_slot *slot, static void nfs4_slot_sequence_acked(struct nfs4_slot *slot, u32 seqnr)
u32 seqnr)
{ {
slot->seq_nr_highest_sent = seqnr; nfs4_slot_sequence_record_sent(slot, seqnr);
slot->seq_nr_last_acked = seqnr; slot->seq_nr_last_acked = seqnr;
} }
@ -854,7 +853,6 @@ static int nfs41_sequence_process(struct rpc_task *task,
__func__, __func__,
slot->slot_nr, slot->slot_nr,
slot->seq_nr); slot->seq_nr);
nfs4_slot_sequence_acked(slot, slot->seq_nr);
goto out_retry; goto out_retry;
case -NFS4ERR_RETRY_UNCACHED_REP: case -NFS4ERR_RETRY_UNCACHED_REP:
case -NFS4ERR_SEQ_FALSE_RETRY: case -NFS4ERR_SEQ_FALSE_RETRY:
@ -3098,12 +3096,13 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
} }
out: out:
if (opendata->lgp) { if (!opendata->cancelled) {
nfs4_lgopen_release(opendata->lgp); if (opendata->lgp) {
opendata->lgp = NULL; nfs4_lgopen_release(opendata->lgp);
} opendata->lgp = NULL;
if (!opendata->cancelled) }
nfs4_sequence_free_slot(&opendata->o_res.seq_res); nfs4_sequence_free_slot(&opendata->o_res.seq_res);
}
return ret; return ret;
} }
@ -8924,6 +8923,9 @@ void nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
if (status == 0) if (status == 0)
rpc_clnt_xprt_switch_add_xprt(clnt, xprt); rpc_clnt_xprt_switch_add_xprt(clnt, xprt);
else if (rpc_clnt_xprt_switch_has_addr(clnt,
(struct sockaddr *)&xprt->addr))
rpc_clnt_xprt_switch_remove_xprt(clnt, xprt);
rpc_put_task(task); rpc_put_task(task);
} }
@ -9248,6 +9250,13 @@ int nfs4_proc_create_session(struct nfs_client *clp, const struct cred *cred)
int status; int status;
unsigned *ptr; unsigned *ptr;
struct nfs4_session *session = clp->cl_session; struct nfs4_session *session = clp->cl_session;
struct nfs4_add_xprt_data xprtdata = {
.clp = clp,
};
struct rpc_add_xprt_test rpcdata = {
.add_xprt_test = clp->cl_mvops->session_trunk,
.data = &xprtdata,
};
dprintk("--> %s clp=%p session=%p\n", __func__, clp, session); dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
@ -9264,6 +9273,7 @@ int nfs4_proc_create_session(struct nfs_client *clp, const struct cred *cred)
ptr = (unsigned *)&session->sess_id.data[0]; ptr = (unsigned *)&session->sess_id.data[0];
dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__, dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__,
clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]); clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]);
rpc_clnt_probe_trunked_xprts(clp->cl_rpcclient, &rpcdata);
out: out:
return status; return status;
} }
@ -9293,6 +9303,7 @@ int nfs4_proc_destroy_session(struct nfs4_session *session,
if (status) if (status)
dprintk("NFS: Got error %d from the server on DESTROY_SESSION. " dprintk("NFS: Got error %d from the server on DESTROY_SESSION. "
"Session has been destroyed regardless...\n", status); "Session has been destroyed regardless...\n", status);
rpc_clnt_manage_trunked_xprts(session->clp->cl_rpcclient);
return status; return status;
} }
@ -9477,6 +9488,9 @@ static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nf
rpc_delay(task, NFS4_POLL_RETRY_MAX); rpc_delay(task, NFS4_POLL_RETRY_MAX);
fallthrough; fallthrough;
case -NFS4ERR_RETRY_UNCACHED_REP: case -NFS4ERR_RETRY_UNCACHED_REP:
case -EACCES:
dprintk("%s: failed to reclaim complete error %d for server %s, retrying\n",
__func__, task->tk_status, clp->cl_hostname);
return -EAGAIN; return -EAGAIN;
case -NFS4ERR_BADSESSION: case -NFS4ERR_BADSESSION:
case -NFS4ERR_DEADSESSION: case -NFS4ERR_DEADSESSION:

View File

@ -1137,7 +1137,7 @@ TRACE_EVENT(nfs_readpage_done,
__field(u32, arg_count) __field(u32, arg_count)
__field(u32, res_count) __field(u32, res_count)
__field(bool, eof) __field(bool, eof)
__field(int, status) __field(int, error)
), ),
TP_fast_assign( TP_fast_assign(
@ -1146,7 +1146,7 @@ TRACE_EVENT(nfs_readpage_done,
const struct nfs_fh *fh = hdr->args.fh ? const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh; hdr->args.fh : &nfsi->fh;
__entry->status = task->tk_status; __entry->error = task->tk_status;
__entry->offset = hdr->args.offset; __entry->offset = hdr->args.offset;
__entry->arg_count = hdr->args.count; __entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count; __entry->res_count = hdr->res.count;
@ -1157,14 +1157,13 @@ TRACE_EVENT(nfs_readpage_done,
), ),
TP_printk( TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x " "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%u res=%u status=%d%s", "offset=%lld count=%u res=%u%s", __entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid, (unsigned long long)__entry->fileid,
__entry->fhandle, __entry->fhandle,
(long long)__entry->offset, __entry->arg_count, (long long)__entry->offset, __entry->arg_count,
__entry->res_count, __entry->status, __entry->res_count, __entry->eof ? " eof" : ""
__entry->eof ? " eof" : ""
) )
); );
@ -1184,7 +1183,7 @@ TRACE_EVENT(nfs_readpage_short,
__field(u32, arg_count) __field(u32, arg_count)
__field(u32, res_count) __field(u32, res_count)
__field(bool, eof) __field(bool, eof)
__field(int, status) __field(int, error)
), ),
TP_fast_assign( TP_fast_assign(
@ -1193,7 +1192,7 @@ TRACE_EVENT(nfs_readpage_short,
const struct nfs_fh *fh = hdr->args.fh ? const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh; hdr->args.fh : &nfsi->fh;
__entry->status = task->tk_status; __entry->error = task->tk_status;
__entry->offset = hdr->args.offset; __entry->offset = hdr->args.offset;
__entry->arg_count = hdr->args.count; __entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count; __entry->res_count = hdr->res.count;
@ -1204,14 +1203,13 @@ TRACE_EVENT(nfs_readpage_short,
), ),
TP_printk( TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x " "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%u res=%u status=%d%s", "offset=%lld count=%u res=%u%s", __entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid, (unsigned long long)__entry->fileid,
__entry->fhandle, __entry->fhandle,
(long long)__entry->offset, __entry->arg_count, (long long)__entry->offset, __entry->arg_count,
__entry->res_count, __entry->status, __entry->res_count, __entry->eof ? " eof" : ""
__entry->eof ? " eof" : ""
) )
); );
@ -1323,7 +1321,7 @@ TRACE_EVENT(nfs_pgio_error,
__field(u32, arg_count) __field(u32, arg_count)
__field(u32, res_count) __field(u32, res_count)
__field(loff_t, pos) __field(loff_t, pos)
__field(int, status) __field(int, error)
), ),
TP_fast_assign( TP_fast_assign(
@ -1332,7 +1330,7 @@ TRACE_EVENT(nfs_pgio_error,
const struct nfs_fh *fh = hdr->args.fh ? const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh; hdr->args.fh : &nfsi->fh;
__entry->status = error; __entry->error = error;
__entry->offset = hdr->args.offset; __entry->offset = hdr->args.offset;
__entry->arg_count = hdr->args.count; __entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count; __entry->res_count = hdr->res.count;
@ -1341,12 +1339,12 @@ TRACE_EVENT(nfs_pgio_error,
__entry->fhandle = nfs_fhandle_hash(fh); __entry->fhandle = nfs_fhandle_hash(fh);
), ),
TP_printk("fileid=%02x:%02x:%llu fhandle=0x%08x " TP_printk("error=%d fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%u res=%u pos=%llu status=%d", "offset=%lld count=%u res=%u pos=%llu", __entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid, __entry->fhandle, (unsigned long long)__entry->fileid, __entry->fhandle,
(long long)__entry->offset, __entry->arg_count, __entry->res_count, (long long)__entry->offset, __entry->arg_count, __entry->res_count,
__entry->pos, __entry->status __entry->pos
) )
); );
@ -1406,7 +1404,7 @@ TRACE_EVENT(nfs_writeback_done,
__field(loff_t, offset) __field(loff_t, offset)
__field(u32, arg_count) __field(u32, arg_count)
__field(u32, res_count) __field(u32, res_count)
__field(int, status) __field(int, error)
__field(unsigned long, stable) __field(unsigned long, stable)
__array(char, verifier, NFS4_VERIFIER_SIZE) __array(char, verifier, NFS4_VERIFIER_SIZE)
), ),
@ -1418,7 +1416,7 @@ TRACE_EVENT(nfs_writeback_done,
hdr->args.fh : &nfsi->fh; hdr->args.fh : &nfsi->fh;
const struct nfs_writeverf *verf = hdr->res.verf; const struct nfs_writeverf *verf = hdr->res.verf;
__entry->status = task->tk_status; __entry->error = task->tk_status;
__entry->offset = hdr->args.offset; __entry->offset = hdr->args.offset;
__entry->arg_count = hdr->args.count; __entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count; __entry->res_count = hdr->res.count;
@ -1432,14 +1430,14 @@ TRACE_EVENT(nfs_writeback_done,
), ),
TP_printk( TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x " "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%u res=%u status=%d stable=%s " "offset=%lld count=%u res=%u stable=%s "
"verifier=%s", "verifier=%s", __entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid, (unsigned long long)__entry->fileid,
__entry->fhandle, __entry->fhandle,
(long long)__entry->offset, __entry->arg_count, (long long)__entry->offset, __entry->arg_count,
__entry->res_count, __entry->status, __entry->res_count,
show_nfs_stable_how(__entry->stable), show_nfs_stable_how(__entry->stable),
show_nfs4_verifier(__entry->verifier) show_nfs4_verifier(__entry->verifier)
) )
@ -1447,44 +1445,50 @@ TRACE_EVENT(nfs_writeback_done,
DECLARE_EVENT_CLASS(nfs_page_error_class, DECLARE_EVENT_CLASS(nfs_page_error_class,
TP_PROTO( TP_PROTO(
const struct inode *inode,
const struct nfs_page *req, const struct nfs_page *req,
int error int error
), ),
TP_ARGS(req, error), TP_ARGS(inode, req, error),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, req) __field(dev_t, dev)
__field(pgoff_t, index) __field(u32, fhandle)
__field(unsigned int, offset) __field(u64, fileid)
__field(unsigned int, pgbase) __field(loff_t, offset)
__field(unsigned int, bytes) __field(unsigned int, count)
__field(int, error) __field(int, error)
), ),
TP_fast_assign( TP_fast_assign(
__entry->req = req; const struct nfs_inode *nfsi = NFS_I(inode);
__entry->index = req->wb_index; __entry->dev = inode->i_sb->s_dev;
__entry->offset = req->wb_offset; __entry->fileid = nfsi->fileid;
__entry->pgbase = req->wb_pgbase; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->bytes = req->wb_bytes; __entry->offset = req_offset(req);
__entry->count = req->wb_bytes;
__entry->error = error; __entry->error = error;
), ),
TP_printk( TP_printk(
"req=%p index=%lu offset=%u pgbase=%u bytes=%u error=%d", "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x "
__entry->req, __entry->index, __entry->offset, "offset=%lld count=%u", __entry->error,
__entry->pgbase, __entry->bytes, __entry->error MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle, __entry->offset,
__entry->count
) )
); );
#define DEFINE_NFS_PAGEERR_EVENT(name) \ #define DEFINE_NFS_PAGEERR_EVENT(name) \
DEFINE_EVENT(nfs_page_error_class, name, \ DEFINE_EVENT(nfs_page_error_class, name, \
TP_PROTO( \ TP_PROTO( \
const struct inode *inode, \
const struct nfs_page *req, \ const struct nfs_page *req, \
int error \ int error \
), \ ), \
TP_ARGS(req, error)) TP_ARGS(inode, req, error))
DEFINE_NFS_PAGEERR_EVENT(nfs_write_error); DEFINE_NFS_PAGEERR_EVENT(nfs_write_error);
DEFINE_NFS_PAGEERR_EVENT(nfs_comp_error); DEFINE_NFS_PAGEERR_EVENT(nfs_comp_error);
@ -1541,7 +1545,7 @@ TRACE_EVENT(nfs_commit_done,
__field(u32, fhandle) __field(u32, fhandle)
__field(u64, fileid) __field(u64, fileid)
__field(loff_t, offset) __field(loff_t, offset)
__field(int, status) __field(int, error)
__field(unsigned long, stable) __field(unsigned long, stable)
__array(char, verifier, NFS4_VERIFIER_SIZE) __array(char, verifier, NFS4_VERIFIER_SIZE)
), ),
@ -1553,7 +1557,7 @@ TRACE_EVENT(nfs_commit_done,
data->args.fh : &nfsi->fh; data->args.fh : &nfsi->fh;
const struct nfs_writeverf *verf = data->res.verf; const struct nfs_writeverf *verf = data->res.verf;
__entry->status = task->tk_status; __entry->error = task->tk_status;
__entry->offset = data->args.offset; __entry->offset = data->args.offset;
__entry->stable = verf->committed; __entry->stable = verf->committed;
memcpy(__entry->verifier, memcpy(__entry->verifier,
@ -1565,17 +1569,83 @@ TRACE_EVENT(nfs_commit_done,
), ),
TP_printk( TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x " "error=%d fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld status=%d stable=%s verifier=%s", "offset=%lld stable=%s verifier=%s", __entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid, (unsigned long long)__entry->fileid,
__entry->fhandle, __entry->fhandle,
(long long)__entry->offset, __entry->status, (long long)__entry->offset,
show_nfs_stable_how(__entry->stable), show_nfs_stable_how(__entry->stable),
show_nfs4_verifier(__entry->verifier) show_nfs4_verifier(__entry->verifier)
) )
); );
#define nfs_show_direct_req_flags(v) \
__print_flags(v, "|", \
{ NFS_ODIRECT_DO_COMMIT, "DO_COMMIT" }, \
{ NFS_ODIRECT_RESCHED_WRITES, "RESCHED_WRITES" }, \
{ NFS_ODIRECT_SHOULD_DIRTY, "SHOULD DIRTY" }, \
{ NFS_ODIRECT_DONE, "DONE" } )
DECLARE_EVENT_CLASS(nfs_direct_req_class,
TP_PROTO(
const struct nfs_direct_req *dreq
),
TP_ARGS(dreq),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(u64, fileid)
__field(u32, fhandle)
__field(loff_t, offset)
__field(ssize_t, count)
__field(ssize_t, bytes_left)
__field(ssize_t, error)
__field(int, flags)
),
TP_fast_assign(
const struct inode *inode = dreq->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = &nfsi->fh;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(fh);
__entry->offset = dreq->io_start;
__entry->count = dreq->count;
__entry->bytes_left = dreq->bytes_left;
__entry->error = dreq->error;
__entry->flags = dreq->flags;
),
TP_printk(
"error=%zd fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%zd bytes_left=%zd flags=%s",
__entry->error, MAJOR(__entry->dev),
MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle, __entry->offset,
__entry->count, __entry->bytes_left,
nfs_show_direct_req_flags(__entry->flags)
)
);
#define DEFINE_NFS_DIRECT_REQ_EVENT(name) \
DEFINE_EVENT(nfs_direct_req_class, name, \
TP_PROTO( \
const struct nfs_direct_req *dreq \
), \
TP_ARGS(dreq))
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_commit_complete);
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_resched_write);
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_complete);
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_completion);
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_schedule_iovec);
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_reschedule_io);
TRACE_EVENT(nfs_fh_to_dentry, TRACE_EVENT(nfs_fh_to_dentry,
TP_PROTO( TP_PROTO(
const struct super_block *sb, const struct super_block *sb,
@ -1609,6 +1679,65 @@ TRACE_EVENT(nfs_fh_to_dentry,
) )
); );
TRACE_EVENT(nfs_mount_assign,
TP_PROTO(
const char *option,
const char *value
),
TP_ARGS(option, value),
TP_STRUCT__entry(
__string(option, option)
__string(value, value)
),
TP_fast_assign(
__assign_str(option, option);
__assign_str(value, value);
),
TP_printk("option %s=%s",
__get_str(option), __get_str(value)
)
);
TRACE_EVENT(nfs_mount_option,
TP_PROTO(
const struct fs_parameter *param
),
TP_ARGS(param),
TP_STRUCT__entry(
__string(option, param->key)
),
TP_fast_assign(
__assign_str(option, param->key);
),
TP_printk("option %s", __get_str(option))
);
TRACE_EVENT(nfs_mount_path,
TP_PROTO(
const char *path
),
TP_ARGS(path),
TP_STRUCT__entry(
__string(path, path)
),
TP_fast_assign(
__assign_str(path, path);
),
TP_printk("path='%s'", __get_str(path))
);
DECLARE_EVENT_CLASS(nfs_xdr_event, DECLARE_EVENT_CLASS(nfs_xdr_event,
TP_PROTO( TP_PROTO(
const struct xdr_stream *xdr, const struct xdr_stream *xdr,

View File

@ -592,7 +592,8 @@ nfs_lock_and_join_requests(struct page *page)
static void nfs_write_error(struct nfs_page *req, int error) static void nfs_write_error(struct nfs_page *req, int error)
{ {
trace_nfs_write_error(req, error); trace_nfs_write_error(page_file_mapping(req->wb_page)->host, req,
error);
nfs_mapping_set_error(req->wb_page, error); nfs_mapping_set_error(req->wb_page, error);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
nfs_end_page_writeback(req); nfs_end_page_writeback(req);
@ -1000,7 +1001,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
nfs_list_remove_request(req); nfs_list_remove_request(req);
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) &&
(hdr->good_bytes < bytes)) { (hdr->good_bytes < bytes)) {
trace_nfs_comp_error(req, hdr->error); trace_nfs_comp_error(hdr->inode, req, hdr->error);
nfs_mapping_set_error(req->wb_page, hdr->error); nfs_mapping_set_error(req->wb_page, hdr->error);
goto remove_req; goto remove_req;
} }
@ -1444,8 +1445,6 @@ static void nfs_async_write_error(struct list_head *head, int error)
static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr) static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr)
{ {
nfs_async_write_error(&hdr->pages, 0); nfs_async_write_error(&hdr->pages, 0);
filemap_fdatawrite_range(hdr->inode->i_mapping, hdr->args.offset,
hdr->args.offset + hdr->args.count - 1);
} }
static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = { static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
@ -1576,25 +1575,37 @@ static int nfs_writeback_done(struct rpc_task *task,
nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count); nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count);
trace_nfs_writeback_done(task, hdr); trace_nfs_writeback_done(task, hdr);
if (hdr->res.verf->committed < hdr->args.stable && if (task->tk_status >= 0) {
task->tk_status >= 0) { enum nfs3_stable_how committed = hdr->res.verf->committed;
/* We tried a write call, but the server did not
* commit data to stable storage even though we
* requested it.
* Note: There is a known bug in Tru64 < 5.0 in which
* the server reports NFS_DATA_SYNC, but performs
* NFS_FILE_SYNC. We therefore implement this checking
* as a dprintk() in order to avoid filling syslog.
*/
static unsigned long complain;
/* Note this will print the MDS for a DS write */ if (committed == NFS_UNSTABLE) {
if (time_before(complain, jiffies)) { /*
dprintk("NFS: faulty NFS server %s:" * We have some uncommitted data on the server at
" (committed = %d) != (stable = %d)\n", * this point, so ensure that we keep track of that
NFS_SERVER(inode)->nfs_client->cl_hostname, * fact irrespective of what later writes do.
hdr->res.verf->committed, hdr->args.stable); */
complain = jiffies + 300 * HZ; set_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags);
}
if (committed < hdr->args.stable) {
/* We tried a write call, but the server did not
* commit data to stable storage even though we
* requested it.
* Note: There is a known bug in Tru64 < 5.0 in which
* the server reports NFS_DATA_SYNC, but performs
* NFS_FILE_SYNC. We therefore implement this checking
* as a dprintk() in order to avoid filling syslog.
*/
static unsigned long complain;
/* Note this will print the MDS for a DS write */
if (time_before(complain, jiffies)) {
dprintk("NFS: faulty NFS server %s:"
" (committed = %d) != (stable = %d)\n",
NFS_SERVER(inode)->nfs_client->cl_hostname,
committed, hdr->args.stable);
complain = jiffies + 300 * HZ;
}
} }
} }
@ -1872,7 +1883,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
(long long)req_offset(req)); (long long)req_offset(req));
if (status < 0) { if (status < 0) {
if (req->wb_page) { if (req->wb_page) {
trace_nfs_commit_error(req, status); trace_nfs_commit_error(data->inode, req,
status);
nfs_mapping_set_error(req->wb_page, status); nfs_mapping_set_error(req->wb_page, status);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
} }

View File

@ -617,6 +617,15 @@ nfs_fileid_to_ino_t(u64 fileid)
#define NFS_JUKEBOX_RETRY_TIME (5 * HZ) #define NFS_JUKEBOX_RETRY_TIME (5 * HZ)
/* We need to block new opens while a file is being unlinked.
* If it is opened *before* we decide to unlink, we will silly-rename
* instead. If it is opened *after*, then we need to create or will fail.
* If we allow the two to race, we could end up with a file that is open
* but deleted on the server resulting in ESTALE.
* So use ->d_fsdata to record when the unlink is happening
* and block dentry revalidation while it is set.
*/
#define NFS_FSDATA_BLOCKED ((void*)1)
# undef ifdebug # undef ifdebug
# ifdef NFS_DEBUG # ifdef NFS_DEBUG

View File

@ -202,8 +202,7 @@ nfs_list_entry(struct list_head *head)
return list_entry(head, struct nfs_page, wb_list); return list_entry(head, struct nfs_page, wb_list);
} }
static inline static inline loff_t req_offset(const struct nfs_page *req)
loff_t req_offset(struct nfs_page *req)
{ {
return (((loff_t)req->wb_index) << PAGE_SHIFT) + req->wb_offset; return (((loff_t)req->wb_index) << PAGE_SHIFT) + req->wb_offset;
} }

View File

@ -1600,6 +1600,7 @@ enum {
NFS_IOHDR_STAT, NFS_IOHDR_STAT,
NFS_IOHDR_RESEND_PNFS, NFS_IOHDR_RESEND_PNFS,
NFS_IOHDR_RESEND_MDS, NFS_IOHDR_RESEND_MDS,
NFS_IOHDR_UNSTABLE_WRITES,
}; };
struct nfs_io_completion; struct nfs_io_completion;

View File

@ -234,13 +234,18 @@ int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *,
struct rpc_xprt_switch *, struct rpc_xprt_switch *,
struct rpc_xprt *, struct rpc_xprt *,
void *); void *);
void rpc_clnt_manage_trunked_xprts(struct rpc_clnt *);
void rpc_clnt_probe_trunked_xprts(struct rpc_clnt *,
struct rpc_add_xprt_test *);
const char *rpc_proc_name(const struct rpc_task *task); const char *rpc_proc_name(const struct rpc_task *task);
void rpc_clnt_xprt_switch_put(struct rpc_clnt *); void rpc_clnt_xprt_switch_put(struct rpc_clnt *);
void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *, struct rpc_xprt *); void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *, struct rpc_xprt *);
void rpc_clnt_xprt_switch_remove_xprt(struct rpc_clnt *, struct rpc_xprt *);
bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt, bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
const struct sockaddr *sap); const struct sockaddr *sap);
void rpc_clnt_xprt_set_online(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
void rpc_cleanup_clids(void); void rpc_cleanup_clids(void);
static inline int rpc_reply_expected(struct rpc_task *task) static inline int rpc_reply_expected(struct rpc_task *task)

View File

@ -61,8 +61,6 @@ struct rpc_task {
struct rpc_wait tk_wait; /* RPC wait */ struct rpc_wait tk_wait; /* RPC wait */
} u; } u;
int tk_rpc_status; /* Result of last RPC operation */
/* /*
* RPC call state * RPC call state
*/ */
@ -82,6 +80,8 @@ struct rpc_task {
ktime_t tk_start; /* RPC task init timestamp */ ktime_t tk_start; /* RPC task init timestamp */
pid_t tk_owner; /* Process id for batching tasks */ pid_t tk_owner; /* Process id for batching tasks */
int tk_rpc_status; /* Result of last RPC operation */
unsigned short tk_flags; /* misc flags */ unsigned short tk_flags; /* misc flags */
unsigned short tk_timeouts; /* maj timeouts */ unsigned short tk_timeouts; /* maj timeouts */

View File

@ -258,10 +258,13 @@ extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
extern int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data); extern int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data);
extern unsigned int xdr_align_data(struct xdr_stream *, unsigned int offset, unsigned int length); extern void xdr_set_pagelen(struct xdr_stream *, unsigned int len);
extern unsigned int xdr_expand_hole(struct xdr_stream *, unsigned int offset, unsigned int length);
extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
unsigned int len); unsigned int len);
extern unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
unsigned int target, unsigned int length);
extern unsigned int xdr_stream_zero(struct xdr_stream *xdr, unsigned int offset,
unsigned int length);
/** /**
* xdr_set_scratch_buffer - Attach a scratch buffer for decoding data. * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data.

View File

@ -144,7 +144,8 @@ struct rpc_xprt_ops {
unsigned short (*get_srcport)(struct rpc_xprt *xprt); unsigned short (*get_srcport)(struct rpc_xprt *xprt);
int (*buf_alloc)(struct rpc_task *task); int (*buf_alloc)(struct rpc_task *task);
void (*buf_free)(struct rpc_task *task); void (*buf_free)(struct rpc_task *task);
int (*prepare_request)(struct rpc_rqst *req); int (*prepare_request)(struct rpc_rqst *req,
struct xdr_buf *buf);
int (*send_request)(struct rpc_rqst *req); int (*send_request)(struct rpc_rqst *req);
void (*wait_for_reply_request)(struct rpc_task *task); void (*wait_for_reply_request)(struct rpc_task *task);
void (*timer)(struct rpc_xprt *xprt, struct rpc_task *task); void (*timer)(struct rpc_xprt *xprt, struct rpc_task *task);
@ -505,4 +506,7 @@ static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt)
return test_and_set_bit(XPRT_BINDING, &xprt->state); return test_and_set_bit(XPRT_BINDING, &xprt->state);
} }
void xprt_set_offline_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps);
void xprt_set_online_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps);
void xprt_delete_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps);
#endif /* _LINUX_SUNRPC_XPRT_H */ #endif /* _LINUX_SUNRPC_XPRT_H */

View File

@ -55,7 +55,7 @@ extern void rpc_xprt_switch_set_roundrobin(struct rpc_xprt_switch *xps);
extern void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps, extern void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt); struct rpc_xprt *xprt);
extern void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps, extern void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt); struct rpc_xprt *xprt, bool offline);
extern void xprt_iter_init(struct rpc_xprt_iter *xpi, extern void xprt_iter_init(struct rpc_xprt_iter *xpi,
struct rpc_xprt_switch *xps); struct rpc_xprt_switch *xps);
@ -63,8 +63,13 @@ extern void xprt_iter_init(struct rpc_xprt_iter *xpi,
extern void xprt_iter_init_listall(struct rpc_xprt_iter *xpi, extern void xprt_iter_init_listall(struct rpc_xprt_iter *xpi,
struct rpc_xprt_switch *xps); struct rpc_xprt_switch *xps);
extern void xprt_iter_init_listoffline(struct rpc_xprt_iter *xpi,
struct rpc_xprt_switch *xps);
extern void xprt_iter_destroy(struct rpc_xprt_iter *xpi); extern void xprt_iter_destroy(struct rpc_xprt_iter *xpi);
extern void xprt_iter_rewind(struct rpc_xprt_iter *xpi);
extern struct rpc_xprt_switch *xprt_iter_xchg_switch( extern struct rpc_xprt_switch *xprt_iter_xchg_switch(
struct rpc_xprt_iter *xpi, struct rpc_xprt_iter *xpi,
struct rpc_xprt_switch *newswitch); struct rpc_xprt_switch *newswitch);

View File

@ -1266,6 +1266,26 @@ TRACE_EVENT(xprt_reserve,
) )
); );
TRACE_EVENT(xs_data_ready,
TP_PROTO(
const struct rpc_xprt *xprt
),
TP_ARGS(xprt),
TP_STRUCT__entry(
__string(addr, xprt->address_strings[RPC_DISPLAY_ADDR])
__string(port, xprt->address_strings[RPC_DISPLAY_PORT])
),
TP_fast_assign(
__assign_str(addr, xprt->address_strings[RPC_DISPLAY_ADDR]);
__assign_str(port, xprt->address_strings[RPC_DISPLAY_PORT]);
),
TP_printk("peer=[%s]:%s", __get_str(addr), __get_str(port))
);
TRACE_EVENT(xs_stream_read_data, TRACE_EVENT(xs_stream_read_data,
TP_PROTO(struct rpc_xprt *xprt, ssize_t err, size_t total), TP_PROTO(struct rpc_xprt *xprt, ssize_t err, size_t total),

View File

@ -445,7 +445,7 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
* Enforce a 60 second garbage collection moratorium * Enforce a 60 second garbage collection moratorium
* Note that the cred_unused list must be time-ordered. * Note that the cred_unused list must be time-ordered.
*/ */
if (!time_in_range(cred->cr_expire, expired, jiffies)) if (time_in_range(cred->cr_expire, expired, jiffies))
continue; continue;
if (!rpcauth_unhash_cred(cred)) if (!rpcauth_unhash_cred(cred))
continue; continue;

View File

@ -1340,14 +1340,11 @@ gss_hash_cred(struct auth_cred *acred, unsigned int hashbits)
/* /*
* Lookup RPCSEC_GSS cred for the current process * Lookup RPCSEC_GSS cred for the current process
*/ */
static struct rpc_cred * static struct rpc_cred *gss_lookup_cred(struct rpc_auth *auth,
gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) struct auth_cred *acred, int flags)
{ {
gfp_t gfp = GFP_KERNEL; return rpcauth_lookup_credcache(auth, acred, flags,
rpc_task_gfp_mask());
if (flags & RPCAUTH_LOOKUP_ASYNC)
gfp = GFP_NOWAIT | __GFP_NOWARN;
return rpcauth_lookup_credcache(auth, acred, flags, gfp);
} }
static struct rpc_cred * static struct rpc_cred *

View File

@ -50,6 +50,17 @@ static void xprt_free_allocation(struct rpc_rqst *req)
kfree(req); kfree(req);
} }
static void xprt_bc_reinit_xdr_buf(struct xdr_buf *buf)
{
buf->head[0].iov_len = PAGE_SIZE;
buf->tail[0].iov_len = 0;
buf->pages = NULL;
buf->page_len = 0;
buf->flags = 0;
buf->len = 0;
buf->buflen = PAGE_SIZE;
}
static int xprt_alloc_xdr_buf(struct xdr_buf *buf, gfp_t gfp_flags) static int xprt_alloc_xdr_buf(struct xdr_buf *buf, gfp_t gfp_flags)
{ {
struct page *page; struct page *page;
@ -278,6 +289,9 @@ void xprt_free_bc_rqst(struct rpc_rqst *req)
*/ */
spin_lock_bh(&xprt->bc_pa_lock); spin_lock_bh(&xprt->bc_pa_lock);
if (xprt_need_to_requeue(xprt)) { if (xprt_need_to_requeue(xprt)) {
xprt_bc_reinit_xdr_buf(&req->rq_snd_buf);
xprt_bc_reinit_xdr_buf(&req->rq_rcv_buf);
req->rq_rcv_buf.len = PAGE_SIZE;
list_add_tail(&req->rq_bc_pa_list, &xprt->bc_pa_list); list_add_tail(&req->rq_bc_pa_list, &xprt->bc_pa_list);
xprt->bc_alloc_count++; xprt->bc_alloc_count++;
atomic_inc(&xprt->bc_slot_count); atomic_inc(&xprt->bc_slot_count);

View File

@ -786,7 +786,8 @@ out_revert:
EXPORT_SYMBOL_GPL(rpc_switch_client_transport); EXPORT_SYMBOL_GPL(rpc_switch_client_transport);
static static
int rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi) int _rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi,
void func(struct rpc_xprt_iter *xpi, struct rpc_xprt_switch *xps))
{ {
struct rpc_xprt_switch *xps; struct rpc_xprt_switch *xps;
@ -795,11 +796,24 @@ int rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi)
rcu_read_unlock(); rcu_read_unlock();
if (xps == NULL) if (xps == NULL)
return -EAGAIN; return -EAGAIN;
xprt_iter_init_listall(xpi, xps); func(xpi, xps);
xprt_switch_put(xps); xprt_switch_put(xps);
return 0; return 0;
} }
static
int rpc_clnt_xprt_iter_init(struct rpc_clnt *clnt, struct rpc_xprt_iter *xpi)
{
return _rpc_clnt_xprt_iter_init(clnt, xpi, xprt_iter_init_listall);
}
static
int rpc_clnt_xprt_iter_offline_init(struct rpc_clnt *clnt,
struct rpc_xprt_iter *xpi)
{
return _rpc_clnt_xprt_iter_init(clnt, xpi, xprt_iter_init_listoffline);
}
/** /**
* rpc_clnt_iterate_for_each_xprt - Apply a function to all transports * rpc_clnt_iterate_for_each_xprt - Apply a function to all transports
* @clnt: pointer to client * @clnt: pointer to client
@ -1856,7 +1870,6 @@ rpc_xdr_encode(struct rpc_task *task)
req->rq_snd_buf.head[0].iov_len = 0; req->rq_snd_buf.head[0].iov_len = 0;
xdr_init_encode(&xdr, &req->rq_snd_buf, xdr_init_encode(&xdr, &req->rq_snd_buf,
req->rq_snd_buf.head[0].iov_base, req); req->rq_snd_buf.head[0].iov_base, req);
xdr_free_bvec(&req->rq_snd_buf);
if (rpc_encode_header(task, &xdr)) if (rpc_encode_header(task, &xdr))
return; return;
@ -2137,7 +2150,8 @@ call_connect_status(struct rpc_task *task)
xprt_release(task); xprt_release(task);
value = atomic_long_dec_return(&xprt->queuelen); value = atomic_long_dec_return(&xprt->queuelen);
if (value == 0) if (value == 0)
rpc_xprt_switch_remove_xprt(xps, saved); rpc_xprt_switch_remove_xprt(xps, saved,
true);
xprt_put(saved); xprt_put(saved);
task->tk_xprt = NULL; task->tk_xprt = NULL;
task->tk_action = call_start; task->tk_action = call_start;
@ -2650,7 +2664,7 @@ out_unparsable:
out_verifier: out_verifier:
trace_rpc_bad_verifier(task); trace_rpc_bad_verifier(task);
goto out_garbage; goto out_err;
out_msg_denied: out_msg_denied:
error = -EACCES; error = -EACCES;
@ -2866,6 +2880,30 @@ success:
} }
EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt); EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt);
static int rpc_clnt_add_xprt_helper(struct rpc_clnt *clnt,
struct rpc_xprt *xprt,
struct rpc_add_xprt_test *data)
{
struct rpc_task *task;
int status = -EADDRINUSE;
/* Test the connection */
task = rpc_call_null_helper(clnt, xprt, NULL, 0, NULL, NULL);
if (IS_ERR(task))
return PTR_ERR(task);
status = task->tk_status;
rpc_put_task(task);
if (status < 0)
return status;
/* rpc_xprt_switch and rpc_xprt are deferrenced by add_xprt_test() */
data->add_xprt_test(clnt, xprt, data->data);
return 0;
}
/** /**
* rpc_clnt_setup_test_and_add_xprt() * rpc_clnt_setup_test_and_add_xprt()
* *
@ -2889,8 +2927,6 @@ int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *clnt,
struct rpc_xprt *xprt, struct rpc_xprt *xprt,
void *data) void *data)
{ {
struct rpc_task *task;
struct rpc_add_xprt_test *xtest = (struct rpc_add_xprt_test *)data;
int status = -EADDRINUSE; int status = -EADDRINUSE;
xprt = xprt_get(xprt); xprt = xprt_get(xprt);
@ -2899,31 +2935,19 @@ int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *clnt,
if (rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr)) if (rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr))
goto out_err; goto out_err;
/* Test the connection */ status = rpc_clnt_add_xprt_helper(clnt, xprt, data);
task = rpc_call_null_helper(clnt, xprt, NULL, 0, NULL, NULL);
if (IS_ERR(task)) {
status = PTR_ERR(task);
goto out_err;
}
status = task->tk_status;
rpc_put_task(task);
if (status < 0) if (status < 0)
goto out_err; goto out_err;
/* rpc_xprt_switch and rpc_xprt are deferrenced by add_xprt_test() */ status = 1;
xtest->add_xprt_test(clnt, xprt, xtest->data);
xprt_put(xprt);
xprt_switch_put(xps);
/* so that rpc_clnt_add_xprt does not call rpc_xprt_switch_add_xprt */
return 1;
out_err: out_err:
xprt_put(xprt); xprt_put(xprt);
xprt_switch_put(xps); xprt_switch_put(xps);
pr_info("RPC: rpc_clnt_test_xprt failed: %d addr %s not added\n", if (status < 0)
status, xprt->address_strings[RPC_DISPLAY_ADDR]); pr_info("RPC: rpc_clnt_test_xprt failed: %d addr %s not "
"added\n", status,
xprt->address_strings[RPC_DISPLAY_ADDR]);
/* so that rpc_clnt_add_xprt does not call rpc_xprt_switch_add_xprt */
return status; return status;
} }
EXPORT_SYMBOL_GPL(rpc_clnt_setup_test_and_add_xprt); EXPORT_SYMBOL_GPL(rpc_clnt_setup_test_and_add_xprt);
@ -3000,6 +3024,110 @@ out_put_switch:
} }
EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt); EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt);
static int rpc_xprt_probe_trunked(struct rpc_clnt *clnt,
struct rpc_xprt *xprt,
struct rpc_add_xprt_test *data)
{
struct rpc_xprt_switch *xps;
struct rpc_xprt *main_xprt;
int status = 0;
xprt_get(xprt);
rcu_read_lock();
main_xprt = xprt_get(rcu_dereference(clnt->cl_xprt));
xps = xprt_switch_get(rcu_dereference(clnt->cl_xpi.xpi_xpswitch));
status = rpc_cmp_addr_port((struct sockaddr *)&xprt->addr,
(struct sockaddr *)&main_xprt->addr);
rcu_read_unlock();
xprt_put(main_xprt);
if (status || !test_bit(XPRT_OFFLINE, &xprt->state))
goto out;
status = rpc_clnt_add_xprt_helper(clnt, xprt, data);
out:
xprt_put(xprt);
xprt_switch_put(xps);
return status;
}
/* rpc_clnt_probe_trunked_xprt -- probe offlined transport for session trunking
* @clnt rpc_clnt structure
*
* For each offlined transport found in the rpc_clnt structure call
* the function rpc_xprt_probe_trunked() which will determine if this
* transport still belongs to the trunking group.
*/
void rpc_clnt_probe_trunked_xprts(struct rpc_clnt *clnt,
struct rpc_add_xprt_test *data)
{
struct rpc_xprt_iter xpi;
int ret;
ret = rpc_clnt_xprt_iter_offline_init(clnt, &xpi);
if (ret)
return;
for (;;) {
struct rpc_xprt *xprt = xprt_iter_get_next(&xpi);
if (!xprt)
break;
ret = rpc_xprt_probe_trunked(clnt, xprt, data);
xprt_put(xprt);
if (ret < 0)
break;
xprt_iter_rewind(&xpi);
}
xprt_iter_destroy(&xpi);
}
EXPORT_SYMBOL_GPL(rpc_clnt_probe_trunked_xprts);
static int rpc_xprt_offline(struct rpc_clnt *clnt,
struct rpc_xprt *xprt,
void *data)
{
struct rpc_xprt *main_xprt;
struct rpc_xprt_switch *xps;
int err = 0;
xprt_get(xprt);
rcu_read_lock();
main_xprt = xprt_get(rcu_dereference(clnt->cl_xprt));
xps = xprt_switch_get(rcu_dereference(clnt->cl_xpi.xpi_xpswitch));
err = rpc_cmp_addr_port((struct sockaddr *)&xprt->addr,
(struct sockaddr *)&main_xprt->addr);
rcu_read_unlock();
xprt_put(main_xprt);
if (err)
goto out;
if (wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_KILLABLE)) {
err = -EINTR;
goto out;
}
xprt_set_offline_locked(xprt, xps);
xprt_release_write(xprt, NULL);
out:
xprt_put(xprt);
xprt_switch_put(xps);
return err;
}
/* rpc_clnt_manage_trunked_xprts -- offline trunked transports
* @clnt rpc_clnt structure
*
* For each active transport found in the rpc_clnt structure call
* the function rpc_xprt_offline() which will identify trunked transports
* and will mark them offline.
*/
void rpc_clnt_manage_trunked_xprts(struct rpc_clnt *clnt)
{
rpc_clnt_iterate_for_each_xprt(clnt, rpc_xprt_offline, NULL);
}
EXPORT_SYMBOL_GPL(rpc_clnt_manage_trunked_xprts);
struct connect_timeout_data { struct connect_timeout_data {
unsigned long connect_timeout; unsigned long connect_timeout;
unsigned long reconnect_timeout; unsigned long reconnect_timeout;
@ -3042,8 +3170,22 @@ void rpc_clnt_xprt_switch_put(struct rpc_clnt *clnt)
} }
EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_put); EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_put);
void rpc_clnt_xprt_set_online(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
{
struct rpc_xprt_switch *xps;
rcu_read_lock();
xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
rcu_read_unlock();
xprt_set_online_locked(xprt, xps);
}
void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt) void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
{ {
if (rpc_clnt_xprt_switch_has_addr(clnt,
(const struct sockaddr *)&xprt->addr)) {
return rpc_clnt_xprt_set_online(clnt, xprt);
}
rcu_read_lock(); rcu_read_lock();
rpc_xprt_switch_add_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch), rpc_xprt_switch_add_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch),
xprt); xprt);
@ -3051,6 +3193,19 @@ void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
} }
EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_add_xprt); EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_add_xprt);
void rpc_clnt_xprt_switch_remove_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
{
struct rpc_xprt_switch *xps;
rcu_read_lock();
xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
rpc_xprt_switch_remove_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch),
xprt, 0);
xps->xps_nunique_destaddr_xprts--;
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_remove_xprt);
bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt, bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
const struct sockaddr *sap) const struct sockaddr *sap)
{ {

View File

@ -63,6 +63,7 @@ gfp_t rpc_task_gfp_mask(void)
return GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN; return GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN;
return GFP_KERNEL; return GFP_KERNEL;
} }
EXPORT_SYMBOL_GPL(rpc_task_gfp_mask);
unsigned long unsigned long
rpc_task_timeout(const struct rpc_task *task) rpc_task_timeout(const struct rpc_task *task)

View File

@ -314,32 +314,14 @@ static ssize_t rpc_sysfs_xprt_state_change(struct kobject *kobj,
goto release_tasks; goto release_tasks;
} }
if (offline) { if (offline) {
if (!test_and_set_bit(XPRT_OFFLINE, &xprt->state)) { xprt_set_offline_locked(xprt, xps);
spin_lock(&xps->xps_lock);
xps->xps_nactive--;
spin_unlock(&xps->xps_lock);
}
} else if (online) { } else if (online) {
if (test_and_clear_bit(XPRT_OFFLINE, &xprt->state)) { xprt_set_online_locked(xprt, xps);
spin_lock(&xps->xps_lock);
xps->xps_nactive++;
spin_unlock(&xps->xps_lock);
}
} else if (remove) { } else if (remove) {
if (test_bit(XPRT_OFFLINE, &xprt->state)) { if (test_bit(XPRT_OFFLINE, &xprt->state))
if (!test_and_set_bit(XPRT_REMOVE, &xprt->state)) { xprt_delete_locked(xprt, xps);
xprt_force_disconnect(xprt); else
if (test_bit(XPRT_CONNECTED, &xprt->state)) {
if (!xprt->sending.qlen &&
!xprt->pending.qlen &&
!xprt->backlog.qlen &&
!atomic_long_read(&xprt->queuelen))
rpc_xprt_switch_remove_xprt(xps, xprt);
}
}
} else {
count = -EINVAL; count = -EINVAL;
}
} }
release_tasks: release_tasks:

View File

@ -775,6 +775,34 @@ static void xdr_buf_pages_shift_left(const struct xdr_buf *buf,
xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift); xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift);
} }
static void xdr_buf_head_shift_left(const struct xdr_buf *buf,
unsigned int base, unsigned int len,
unsigned int shift)
{
const struct kvec *head = buf->head;
unsigned int bytes;
if (!shift || !len)
return;
if (shift > base) {
bytes = (shift - base);
if (bytes >= len)
return;
base += bytes;
len -= bytes;
}
if (base < head->iov_len) {
bytes = min_t(unsigned int, len, head->iov_len - base);
memmove(head->iov_base + (base - shift),
head->iov_base + base, bytes);
base += bytes;
len -= bytes;
}
xdr_buf_pages_shift_left(buf, base - head->iov_len, len, shift);
}
/** /**
* xdr_shrink_bufhead * xdr_shrink_bufhead
* @buf: xdr_buf * @buf: xdr_buf
@ -1472,71 +1500,35 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
} }
EXPORT_SYMBOL_GPL(xdr_read_pages); EXPORT_SYMBOL_GPL(xdr_read_pages);
unsigned int xdr_align_data(struct xdr_stream *xdr, unsigned int offset, /**
unsigned int length) * xdr_set_pagelen - Sets the length of the XDR pages
* @xdr: pointer to xdr_stream struct
* @len: new length of the XDR page data
*
* Either grows or shrinks the length of the xdr pages by setting pagelen to
* @len bytes. When shrinking, any extra data is moved into buf->tail, whereas
* when growing any data beyond the current pointer is moved into the tail.
*
* Returns True if the operation was successful, and False otherwise.
*/
void xdr_set_pagelen(struct xdr_stream *xdr, unsigned int len)
{ {
struct xdr_buf *buf = xdr->buf; struct xdr_buf *buf = xdr->buf;
unsigned int from, bytes, len; size_t remaining = xdr_stream_remaining(xdr);
unsigned int shift; size_t base = 0;
xdr_realign_pages(xdr); if (len < buf->page_len) {
from = xdr_page_pos(xdr); base = buf->page_len - len;
xdr_shrink_pagelen(buf, len);
if (from >= buf->page_len + buf->tail->iov_len) } else {
return 0; xdr_buf_head_shift_right(buf, xdr_stream_pos(xdr),
if (from + buf->head->iov_len >= buf->len) buf->page_len, remaining);
return 0; if (len > buf->page_len)
xdr_buf_try_expand(buf, len - buf->page_len);
len = buf->len - buf->head->iov_len; }
xdr_set_tail_base(xdr, base, remaining);
/* We only shift data left! */
if (WARN_ONCE(from < offset, "SUNRPC: misaligned data src=%u dst=%u\n",
from, offset))
return 0;
if (WARN_ONCE(offset > buf->page_len,
"SUNRPC: buffer overflow. offset=%u, page_len=%u\n",
offset, buf->page_len))
return 0;
/* Move page data to the left */
shift = from - offset;
xdr_buf_pages_shift_left(buf, from, len, shift);
bytes = xdr_stream_remaining(xdr);
if (length > bytes)
length = bytes;
bytes -= length;
xdr->buf->len -= shift;
xdr_set_page(xdr, offset + length, bytes);
return length;
} }
EXPORT_SYMBOL_GPL(xdr_align_data); EXPORT_SYMBOL_GPL(xdr_set_pagelen);
unsigned int xdr_expand_hole(struct xdr_stream *xdr, unsigned int offset,
unsigned int length)
{
struct xdr_buf *buf = xdr->buf;
unsigned int from, to, shift;
xdr_realign_pages(xdr);
from = xdr_page_pos(xdr);
to = xdr_align_size(offset + length);
/* Could the hole be behind us? */
if (to > from) {
unsigned int buflen = buf->len - buf->head->iov_len;
shift = to - from;
xdr_buf_try_expand(buf, shift);
xdr_buf_pages_shift_right(buf, from, buflen, shift);
xdr_set_page(xdr, to, xdr_stream_remaining(xdr));
} else if (to != from)
xdr_align_data(xdr, to, 0);
xdr_buf_pages_zero(buf, offset, length);
return length;
}
EXPORT_SYMBOL_GPL(xdr_expand_hole);
/** /**
* xdr_enter_page - decode data from the XDR page * xdr_enter_page - decode data from the XDR page
@ -1680,6 +1672,60 @@ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
} }
EXPORT_SYMBOL_GPL(xdr_stream_subsegment); EXPORT_SYMBOL_GPL(xdr_stream_subsegment);
/**
* xdr_stream_move_subsegment - Move part of a stream to another position
* @xdr: the source xdr_stream
* @offset: the source offset of the segment
* @target: the target offset of the segment
* @length: the number of bytes to move
*
* Moves @length bytes from @offset to @target in the xdr_stream, overwriting
* anything in its space. Returns the number of bytes in the segment.
*/
unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
unsigned int target, unsigned int length)
{
struct xdr_buf buf;
unsigned int shift;
if (offset < target) {
shift = target - offset;
if (xdr_buf_subsegment(xdr->buf, &buf, offset, shift + length) < 0)
return 0;
xdr_buf_head_shift_right(&buf, 0, length, shift);
} else if (offset > target) {
shift = offset - target;
if (xdr_buf_subsegment(xdr->buf, &buf, target, shift + length) < 0)
return 0;
xdr_buf_head_shift_left(&buf, shift, length, shift);
}
return length;
}
EXPORT_SYMBOL_GPL(xdr_stream_move_subsegment);
/**
* xdr_stream_zero - zero out a portion of an xdr_stream
* @xdr: an xdr_stream to zero out
* @offset: the starting point in the stream
* @length: the number of bytes to zero
*/
unsigned int xdr_stream_zero(struct xdr_stream *xdr, unsigned int offset,
unsigned int length)
{
struct xdr_buf buf;
if (xdr_buf_subsegment(xdr->buf, &buf, offset, length) < 0)
return 0;
if (buf.head[0].iov_len)
xdr_buf_iov_zero(buf.head, 0, buf.head[0].iov_len);
if (buf.page_len > 0)
xdr_buf_pages_zero(&buf, 0, buf.page_len);
if (buf.tail[0].iov_len)
xdr_buf_iov_zero(buf.tail, 0, buf.tail[0].iov_len);
return length;
}
EXPORT_SYMBOL_GPL(xdr_stream_zero);
/** /**
* xdr_buf_trim - lop at most "len" bytes off the end of "buf" * xdr_buf_trim - lop at most "len" bytes off the end of "buf"
* @buf: buf to be trimmed * @buf: buf to be trimmed

View File

@ -73,7 +73,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net);
static __be32 xprt_alloc_xid(struct rpc_xprt *xprt); static __be32 xprt_alloc_xid(struct rpc_xprt *xprt);
static void xprt_destroy(struct rpc_xprt *xprt); static void xprt_destroy(struct rpc_xprt *xprt);
static void xprt_request_init(struct rpc_task *task); static void xprt_request_init(struct rpc_task *task);
static int xprt_request_prepare(struct rpc_rqst *req); static int xprt_request_prepare(struct rpc_rqst *req, struct xdr_buf *buf);
static DEFINE_SPINLOCK(xprt_list_lock); static DEFINE_SPINLOCK(xprt_list_lock);
static LIST_HEAD(xprt_list); static LIST_HEAD(xprt_list);
@ -1149,7 +1149,7 @@ xprt_request_enqueue_receive(struct rpc_task *task)
if (!xprt_request_need_enqueue_receive(task, req)) if (!xprt_request_need_enqueue_receive(task, req))
return 0; return 0;
ret = xprt_request_prepare(task->tk_rqstp); ret = xprt_request_prepare(task->tk_rqstp, &req->rq_rcv_buf);
if (ret) if (ret)
return ret; return ret;
spin_lock(&xprt->queue_lock); spin_lock(&xprt->queue_lock);
@ -1179,8 +1179,11 @@ xprt_request_dequeue_receive_locked(struct rpc_task *task)
{ {
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
if (test_and_clear_bit(RPC_TASK_NEED_RECV, &task->tk_runstate)) if (test_and_clear_bit(RPC_TASK_NEED_RECV, &task->tk_runstate)) {
xprt_request_rb_remove(req->rq_xprt, req); xprt_request_rb_remove(req->rq_xprt, req);
xdr_free_bvec(&req->rq_rcv_buf);
req->rq_private_buf.bvec = NULL;
}
} }
/** /**
@ -1336,8 +1339,14 @@ xprt_request_enqueue_transmit(struct rpc_task *task)
{ {
struct rpc_rqst *pos, *req = task->tk_rqstp; struct rpc_rqst *pos, *req = task->tk_rqstp;
struct rpc_xprt *xprt = req->rq_xprt; struct rpc_xprt *xprt = req->rq_xprt;
int ret;
if (xprt_request_need_enqueue_transmit(task, req)) { if (xprt_request_need_enqueue_transmit(task, req)) {
ret = xprt_request_prepare(task->tk_rqstp, &req->rq_snd_buf);
if (ret) {
task->tk_status = ret;
return;
}
req->rq_bytes_sent = 0; req->rq_bytes_sent = 0;
spin_lock(&xprt->queue_lock); spin_lock(&xprt->queue_lock);
/* /*
@ -1397,6 +1406,7 @@ xprt_request_dequeue_transmit_locked(struct rpc_task *task)
} else } else
list_del(&req->rq_xmit2); list_del(&req->rq_xmit2);
atomic_long_dec(&req->rq_xprt->xmit_queuelen); atomic_long_dec(&req->rq_xprt->xmit_queuelen);
xdr_free_bvec(&req->rq_snd_buf);
} }
/** /**
@ -1433,8 +1443,6 @@ xprt_request_dequeue_xprt(struct rpc_task *task)
test_bit(RPC_TASK_NEED_RECV, &task->tk_runstate) || test_bit(RPC_TASK_NEED_RECV, &task->tk_runstate) ||
xprt_is_pinned_rqst(req)) { xprt_is_pinned_rqst(req)) {
spin_lock(&xprt->queue_lock); spin_lock(&xprt->queue_lock);
xprt_request_dequeue_transmit_locked(task);
xprt_request_dequeue_receive_locked(task);
while (xprt_is_pinned_rqst(req)) { while (xprt_is_pinned_rqst(req)) {
set_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate); set_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate);
spin_unlock(&xprt->queue_lock); spin_unlock(&xprt->queue_lock);
@ -1442,6 +1450,8 @@ xprt_request_dequeue_xprt(struct rpc_task *task)
spin_lock(&xprt->queue_lock); spin_lock(&xprt->queue_lock);
clear_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate); clear_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate);
} }
xprt_request_dequeue_transmit_locked(task);
xprt_request_dequeue_receive_locked(task);
spin_unlock(&xprt->queue_lock); spin_unlock(&xprt->queue_lock);
} }
} }
@ -1449,18 +1459,19 @@ xprt_request_dequeue_xprt(struct rpc_task *task)
/** /**
* xprt_request_prepare - prepare an encoded request for transport * xprt_request_prepare - prepare an encoded request for transport
* @req: pointer to rpc_rqst * @req: pointer to rpc_rqst
* @buf: pointer to send/rcv xdr_buf
* *
* Calls into the transport layer to do whatever is needed to prepare * Calls into the transport layer to do whatever is needed to prepare
* the request for transmission or receive. * the request for transmission or receive.
* Returns error, or zero. * Returns error, or zero.
*/ */
static int static int
xprt_request_prepare(struct rpc_rqst *req) xprt_request_prepare(struct rpc_rqst *req, struct xdr_buf *buf)
{ {
struct rpc_xprt *xprt = req->rq_xprt; struct rpc_xprt *xprt = req->rq_xprt;
if (xprt->ops->prepare_request) if (xprt->ops->prepare_request)
return xprt->ops->prepare_request(req); return xprt->ops->prepare_request(req, buf);
return 0; return 0;
} }
@ -1961,8 +1972,6 @@ void xprt_release(struct rpc_task *task)
spin_unlock(&xprt->transport_lock); spin_unlock(&xprt->transport_lock);
if (req->rq_buffer) if (req->rq_buffer)
xprt->ops->buf_free(task); xprt->ops->buf_free(task);
xdr_free_bvec(&req->rq_rcv_buf);
xdr_free_bvec(&req->rq_snd_buf);
if (req->rq_cred != NULL) if (req->rq_cred != NULL)
put_rpccred(req->rq_cred); put_rpccred(req->rq_cred);
if (req->rq_release_snd_buf) if (req->rq_release_snd_buf)
@ -2152,3 +2161,35 @@ void xprt_put(struct rpc_xprt *xprt)
kref_put(&xprt->kref, xprt_destroy_kref); kref_put(&xprt->kref, xprt_destroy_kref);
} }
EXPORT_SYMBOL_GPL(xprt_put); EXPORT_SYMBOL_GPL(xprt_put);
void xprt_set_offline_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps)
{
if (!test_and_set_bit(XPRT_OFFLINE, &xprt->state)) {
spin_lock(&xps->xps_lock);
xps->xps_nactive--;
spin_unlock(&xps->xps_lock);
}
}
void xprt_set_online_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps)
{
if (test_and_clear_bit(XPRT_OFFLINE, &xprt->state)) {
spin_lock(&xps->xps_lock);
xps->xps_nactive++;
spin_unlock(&xps->xps_lock);
}
}
void xprt_delete_locked(struct rpc_xprt *xprt, struct rpc_xprt_switch *xps)
{
if (test_and_set_bit(XPRT_REMOVE, &xprt->state))
return;
xprt_force_disconnect(xprt);
if (!test_bit(XPRT_CONNECTED, &xprt->state))
return;
if (!xprt->sending.qlen && !xprt->pending.qlen &&
!xprt->backlog.qlen && !atomic_long_read(&xprt->queuelen))
rpc_xprt_switch_remove_xprt(xps, xprt, true);
}

View File

@ -27,6 +27,7 @@ typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct rpc_xprt_switch *xps,
static const struct rpc_xprt_iter_ops rpc_xprt_iter_singular; static const struct rpc_xprt_iter_ops rpc_xprt_iter_singular;
static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin; static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin;
static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall; static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall;
static const struct rpc_xprt_iter_ops rpc_xprt_iter_listoffline;
static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps, static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt) struct rpc_xprt *xprt)
@ -61,11 +62,11 @@ void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps,
} }
static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps, static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt) struct rpc_xprt *xprt, bool offline)
{ {
if (unlikely(xprt == NULL)) if (unlikely(xprt == NULL))
return; return;
if (!test_bit(XPRT_OFFLINE, &xprt->state)) if (!test_bit(XPRT_OFFLINE, &xprt->state) && offline)
xps->xps_nactive--; xps->xps_nactive--;
xps->xps_nxprts--; xps->xps_nxprts--;
if (xps->xps_nxprts == 0) if (xps->xps_nxprts == 0)
@ -78,14 +79,15 @@ static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps,
* rpc_xprt_switch_remove_xprt - Removes an rpc_xprt from a rpc_xprt_switch * rpc_xprt_switch_remove_xprt - Removes an rpc_xprt from a rpc_xprt_switch
* @xps: pointer to struct rpc_xprt_switch * @xps: pointer to struct rpc_xprt_switch
* @xprt: pointer to struct rpc_xprt * @xprt: pointer to struct rpc_xprt
* @offline: indicates if the xprt that's being removed is in an offline state
* *
* Removes xprt from the list of struct rpc_xprt in xps. * Removes xprt from the list of struct rpc_xprt in xps.
*/ */
void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps, void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt) struct rpc_xprt *xprt, bool offline)
{ {
spin_lock(&xps->xps_lock); spin_lock(&xps->xps_lock);
xprt_switch_remove_xprt_locked(xps, xprt); xprt_switch_remove_xprt_locked(xps, xprt, offline);
spin_unlock(&xps->xps_lock); spin_unlock(&xps->xps_lock);
xprt_put(xprt); xprt_put(xprt);
} }
@ -154,7 +156,7 @@ static void xprt_switch_free_entries(struct rpc_xprt_switch *xps)
xprt = list_first_entry(&xps->xps_xprt_list, xprt = list_first_entry(&xps->xps_xprt_list,
struct rpc_xprt, xprt_switch); struct rpc_xprt, xprt_switch);
xprt_switch_remove_xprt_locked(xps, xprt); xprt_switch_remove_xprt_locked(xps, xprt, true);
spin_unlock(&xps->xps_lock); spin_unlock(&xps->xps_lock);
xprt_put(xprt); xprt_put(xprt);
spin_lock(&xps->xps_lock); spin_lock(&xps->xps_lock);
@ -248,6 +250,18 @@ struct rpc_xprt *xprt_switch_find_first_entry(struct list_head *head)
return NULL; return NULL;
} }
static
struct rpc_xprt *xprt_switch_find_first_entry_offline(struct list_head *head)
{
struct rpc_xprt *pos;
list_for_each_entry_rcu(pos, head, xprt_switch) {
if (!xprt_is_active(pos))
return pos;
}
return NULL;
}
static static
struct rpc_xprt *xprt_iter_first_entry(struct rpc_xprt_iter *xpi) struct rpc_xprt *xprt_iter_first_entry(struct rpc_xprt_iter *xpi)
{ {
@ -259,8 +273,9 @@ struct rpc_xprt *xprt_iter_first_entry(struct rpc_xprt_iter *xpi)
} }
static static
struct rpc_xprt *xprt_switch_find_current_entry(struct list_head *head, struct rpc_xprt *_xprt_switch_find_current_entry(struct list_head *head,
const struct rpc_xprt *cur) const struct rpc_xprt *cur,
bool find_active)
{ {
struct rpc_xprt *pos; struct rpc_xprt *pos;
bool found = false; bool found = false;
@ -268,14 +283,25 @@ struct rpc_xprt *xprt_switch_find_current_entry(struct list_head *head,
list_for_each_entry_rcu(pos, head, xprt_switch) { list_for_each_entry_rcu(pos, head, xprt_switch) {
if (cur == pos) if (cur == pos)
found = true; found = true;
if (found && xprt_is_active(pos)) if (found && ((find_active && xprt_is_active(pos)) ||
(!find_active && xprt_is_active(pos))))
return pos; return pos;
} }
return NULL; return NULL;
} }
static static
struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi) struct rpc_xprt *xprt_switch_find_current_entry(struct list_head *head,
const struct rpc_xprt *cur)
{
return _xprt_switch_find_current_entry(head, cur, true);
}
static
struct rpc_xprt * _xprt_iter_current_entry(struct rpc_xprt_iter *xpi,
struct rpc_xprt *first_entry(struct list_head *head),
struct rpc_xprt *current_entry(struct list_head *head,
const struct rpc_xprt *cur))
{ {
struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch); struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch);
struct list_head *head; struct list_head *head;
@ -284,8 +310,30 @@ struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi)
return NULL; return NULL;
head = &xps->xps_xprt_list; head = &xps->xps_xprt_list;
if (xpi->xpi_cursor == NULL || xps->xps_nxprts < 2) if (xpi->xpi_cursor == NULL || xps->xps_nxprts < 2)
return xprt_switch_find_first_entry(head); return first_entry(head);
return xprt_switch_find_current_entry(head, xpi->xpi_cursor); return current_entry(head, xpi->xpi_cursor);
}
static
struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi)
{
return _xprt_iter_current_entry(xpi, xprt_switch_find_first_entry,
xprt_switch_find_current_entry);
}
static
struct rpc_xprt *xprt_switch_find_current_entry_offline(struct list_head *head,
const struct rpc_xprt *cur)
{
return _xprt_switch_find_current_entry(head, cur, false);
}
static
struct rpc_xprt *xprt_iter_current_entry_offline(struct rpc_xprt_iter *xpi)
{
return _xprt_iter_current_entry(xpi,
xprt_switch_find_first_entry_offline,
xprt_switch_find_current_entry_offline);
} }
bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps, bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
@ -310,7 +358,7 @@ bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
static static
struct rpc_xprt *xprt_switch_find_next_entry(struct list_head *head, struct rpc_xprt *xprt_switch_find_next_entry(struct list_head *head,
const struct rpc_xprt *cur) const struct rpc_xprt *cur, bool check_active)
{ {
struct rpc_xprt *pos, *prev = NULL; struct rpc_xprt *pos, *prev = NULL;
bool found = false; bool found = false;
@ -318,7 +366,12 @@ struct rpc_xprt *xprt_switch_find_next_entry(struct list_head *head,
list_for_each_entry_rcu(pos, head, xprt_switch) { list_for_each_entry_rcu(pos, head, xprt_switch) {
if (cur == prev) if (cur == prev)
found = true; found = true;
if (found && xprt_is_active(pos)) /* for request to return active transports return only
* active, for request to return offline transports
* return only offline
*/
if (found && ((check_active && xprt_is_active(pos)) ||
(!check_active && !xprt_is_active(pos))))
return pos; return pos;
prev = pos; prev = pos;
} }
@ -355,7 +408,7 @@ struct rpc_xprt *__xprt_switch_find_next_entry_roundrobin(struct list_head *head
{ {
struct rpc_xprt *ret; struct rpc_xprt *ret;
ret = xprt_switch_find_next_entry(head, cur); ret = xprt_switch_find_next_entry(head, cur, true);
if (ret != NULL) if (ret != NULL)
return ret; return ret;
return xprt_switch_find_first_entry(head); return xprt_switch_find_first_entry(head);
@ -397,7 +450,14 @@ static
struct rpc_xprt *xprt_switch_find_next_entry_all(struct rpc_xprt_switch *xps, struct rpc_xprt *xprt_switch_find_next_entry_all(struct rpc_xprt_switch *xps,
const struct rpc_xprt *cur) const struct rpc_xprt *cur)
{ {
return xprt_switch_find_next_entry(&xps->xps_xprt_list, cur); return xprt_switch_find_next_entry(&xps->xps_xprt_list, cur, true);
}
static
struct rpc_xprt *xprt_switch_find_next_entry_offline(struct rpc_xprt_switch *xps,
const struct rpc_xprt *cur)
{
return xprt_switch_find_next_entry(&xps->xps_xprt_list, cur, false);
} }
static static
@ -407,6 +467,13 @@ struct rpc_xprt *xprt_iter_next_entry_all(struct rpc_xprt_iter *xpi)
xprt_switch_find_next_entry_all); xprt_switch_find_next_entry_all);
} }
static
struct rpc_xprt *xprt_iter_next_entry_offline(struct rpc_xprt_iter *xpi)
{
return xprt_iter_next_entry_multiple(xpi,
xprt_switch_find_next_entry_offline);
}
/* /*
* xprt_iter_rewind - Resets the xprt iterator * xprt_iter_rewind - Resets the xprt iterator
* @xpi: pointer to rpc_xprt_iter * @xpi: pointer to rpc_xprt_iter
@ -414,7 +481,6 @@ struct rpc_xprt *xprt_iter_next_entry_all(struct rpc_xprt_iter *xpi)
* Resets xpi to ensure that it points to the first entry in the list * Resets xpi to ensure that it points to the first entry in the list
* of transports. * of transports.
*/ */
static
void xprt_iter_rewind(struct rpc_xprt_iter *xpi) void xprt_iter_rewind(struct rpc_xprt_iter *xpi)
{ {
rcu_read_lock(); rcu_read_lock();
@ -460,6 +526,12 @@ void xprt_iter_init_listall(struct rpc_xprt_iter *xpi,
__xprt_iter_init(xpi, xps, &rpc_xprt_iter_listall); __xprt_iter_init(xpi, xps, &rpc_xprt_iter_listall);
} }
void xprt_iter_init_listoffline(struct rpc_xprt_iter *xpi,
struct rpc_xprt_switch *xps)
{
__xprt_iter_init(xpi, xps, &rpc_xprt_iter_listoffline);
}
/** /**
* xprt_iter_xchg_switch - Atomically swap out the rpc_xprt_switch * xprt_iter_xchg_switch - Atomically swap out the rpc_xprt_switch
* @xpi: pointer to rpc_xprt_iter * @xpi: pointer to rpc_xprt_iter
@ -574,3 +646,10 @@ const struct rpc_xprt_iter_ops rpc_xprt_iter_listall = {
.xpi_xprt = xprt_iter_current_entry, .xpi_xprt = xprt_iter_current_entry,
.xpi_next = xprt_iter_next_entry_all, .xpi_next = xprt_iter_next_entry_all,
}; };
static
const struct rpc_xprt_iter_ops rpc_xprt_iter_listoffline = {
.xpi_rewind = xprt_iter_default_rewind,
.xpi_xprt = xprt_iter_current_entry_offline,
.xpi_next = xprt_iter_next_entry_offline,
};

View File

@ -571,11 +571,7 @@ xprt_rdma_allocate(struct rpc_task *task)
struct rpc_rqst *rqst = task->tk_rqstp; struct rpc_rqst *rqst = task->tk_rqstp;
struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt); struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
struct rpcrdma_req *req = rpcr_to_rdmar(rqst); struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
gfp_t flags; gfp_t flags = rpc_task_gfp_mask();
flags = RPCRDMA_DEF_GFP;
if (RPC_IS_ASYNC(task))
flags = GFP_NOWAIT | __GFP_NOWARN;
if (!rpcrdma_check_regbuf(r_xprt, req->rl_sendbuf, rqst->rq_callsize, if (!rpcrdma_check_regbuf(r_xprt, req->rl_sendbuf, rqst->rq_callsize,
flags)) flags))

View File

@ -822,17 +822,9 @@ static int xs_stream_nospace(struct rpc_rqst *req, bool vm_wait)
return ret; return ret;
} }
static int static int xs_stream_prepare_request(struct rpc_rqst *req, struct xdr_buf *buf)
xs_stream_prepare_request(struct rpc_rqst *req)
{ {
gfp_t gfp = rpc_task_gfp_mask(); return xdr_alloc_bvec(buf, rpc_task_gfp_mask());
int ret;
ret = xdr_alloc_bvec(&req->rq_snd_buf, gfp);
if (ret < 0)
return ret;
xdr_free_bvec(&req->rq_rcv_buf);
return xdr_alloc_bvec(&req->rq_rcv_buf, gfp);
} }
/* /*
@ -1378,7 +1370,7 @@ static void xs_udp_data_receive_workfn(struct work_struct *work)
} }
/** /**
* xs_data_ready - "data ready" callback for UDP sockets * xs_data_ready - "data ready" callback for sockets
* @sk: socket with data to read * @sk: socket with data to read
* *
*/ */
@ -1386,11 +1378,13 @@ static void xs_data_ready(struct sock *sk)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
dprintk("RPC: xs_data_ready...\n");
xprt = xprt_from_sock(sk); xprt = xprt_from_sock(sk);
if (xprt != NULL) { if (xprt != NULL) {
struct sock_xprt *transport = container_of(xprt, struct sock_xprt *transport = container_of(xprt,
struct sock_xprt, xprt); struct sock_xprt, xprt);
trace_xs_data_ready(xprt);
transport->old_data_ready(sk); transport->old_data_ready(sk);
/* Any data means we had a useful conversation, so /* Any data means we had a useful conversation, so
* then we don't need to delay the next reconnect * then we don't need to delay the next reconnect