Commit 7a6b6044 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfsd-5.9' of git://git.linux-nfs.org/projects/cel/cel-2.6

Pull NFS server updates from Chuck Lever:
 "Highlights:
   - Support for user extended attributes on NFS (RFC 8276)
   - Further reduce unnecessary NFSv4 delegation recalls

  Notable fixes:
   - Fix recent krb5p regression
   - Address a few resource leaks and a rare NULL dereference

  Other:
   - De-duplicate RPC/RDMA error handling and other utility functions
   - Replace storage and display of kernel memory addresses by tracepoints"

* tag 'nfsd-5.9' of git://git.linux-nfs.org/projects/cel/cel-2.6: (38 commits)
  svcrdma: CM event handler clean up
  svcrdma: Remove transport reference counting
  svcrdma: Fix another Receive buffer leak
  SUNRPC: Refresh the show_rqstp_flags() macro
  nfsd: netns.h: delete a duplicated word
  SUNRPC: Fix ("SUNRPC: Add "@len" parameter to gss_unwrap()")
  nfsd: avoid a NULL dereference in __cld_pipe_upcall()
  nfsd4: a client's own opens needn't prevent delegations
  nfsd: Use seq_putc() in two functions
  svcrdma: Display chunk completion ID when posting a rw_ctxt
  svcrdma: Record send_ctxt completion ID in trace_svcrdma_post_send()
  svcrdma: Introduce Send completion IDs
  svcrdma: Record Receive completion ID in svc_rdma_decode_rqst
  svcrdma: Introduce Receive completion IDs
  svcrdma: Introduce infrastructure to support completion IDs
  svcrdma: Add common XDR encoders for RDMA and Read segments
  svcrdma: Add common XDR decoders for RDMA and Read segments
  SUNRPC: Add helpers for decoding list discriminators symbolically
  svcrdma: Remove declarations for functions long removed
  svcrdma: Clean up trace_svcrdma_send_failed() tracepoint
  ...
parents 8d3e09b4 b297fed6
...@@ -1808,6 +1808,9 @@ check_conflicting_open(struct file *filp, const long arg, int flags) ...@@ -1808,6 +1808,9 @@ check_conflicting_open(struct file *filp, const long arg, int flags)
if (flags & FL_LAYOUT) if (flags & FL_LAYOUT)
return 0; return 0;
if (flags & FL_DELEG)
/* We leave these checks to the caller. */
return 0;
if (arg == F_RDLCK) if (arg == F_RDLCK)
return inode_is_open_for_write(inode) ? -EAGAIN : 0; return inode_is_open_for_write(inode) ? -EAGAIN : 0;
......
...@@ -171,7 +171,7 @@ struct nfsd_net { ...@@ -171,7 +171,7 @@ struct nfsd_net {
unsigned int longest_chain_cachesize; unsigned int longest_chain_cachesize;
struct shrinker nfsd_reply_cache_shrinker; struct shrinker nfsd_reply_cache_shrinker;
/* utsname taken from the the process that starts the server */ /* utsname taken from the process that starts the server */
char nfsd_name[UNX_MAXNODENAME+1]; char nfsd_name[UNX_MAXNODENAME+1];
}; };
......
...@@ -168,7 +168,7 @@ idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) ...@@ -168,7 +168,7 @@ idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
ent->id); ent->id);
if (test_bit(CACHE_VALID, &h->flags)) if (test_bit(CACHE_VALID, &h->flags))
seq_printf(m, " %s", ent->name); seq_printf(m, " %s", ent->name);
seq_printf(m, "\n"); seq_putc(m, '\n');
return 0; return 0;
} }
...@@ -346,7 +346,7 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) ...@@ -346,7 +346,7 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
ent->name); ent->name);
if (test_bit(CACHE_VALID, &h->flags)) if (test_bit(CACHE_VALID, &h->flags))
seq_printf(m, " %u", ent->id); seq_printf(m, " %u", ent->id);
seq_printf(m, "\n"); seq_putc(m, '\n');
return 0; return 0;
} }
......
...@@ -566,8 +566,14 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -566,8 +566,14 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u) union nfsd4_op_u *u)
{ {
struct nfsd4_access *access = &u->access; struct nfsd4_access *access = &u->access;
u32 access_full;
if (access->ac_req_access & ~NFS3_ACCESS_FULL) access_full = NFS3_ACCESS_FULL;
if (cstate->minorversion >= 2)
access_full |= NFS4_ACCESS_XALIST | NFS4_ACCESS_XAREAD |
NFS4_ACCESS_XAWRITE;
if (access->ac_req_access & ~access_full)
return nfserr_inval; return nfserr_inval;
access->ac_resp_access = access->ac_req_access; access->ac_resp_access = access->ac_req_access;
...@@ -2091,6 +2097,68 @@ nfsd4_layoutreturn(struct svc_rqst *rqstp, ...@@ -2091,6 +2097,68 @@ nfsd4_layoutreturn(struct svc_rqst *rqstp,
} }
#endif /* CONFIG_NFSD_PNFS */ #endif /* CONFIG_NFSD_PNFS */
static __be32
nfsd4_getxattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd4_getxattr *getxattr = &u->getxattr;
return nfsd_getxattr(rqstp, &cstate->current_fh,
getxattr->getxa_name, &getxattr->getxa_buf,
&getxattr->getxa_len);
}
static __be32
nfsd4_setxattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd4_setxattr *setxattr = &u->setxattr;
__be32 ret;
if (opens_in_grace(SVC_NET(rqstp)))
return nfserr_grace;
ret = nfsd_setxattr(rqstp, &cstate->current_fh, setxattr->setxa_name,
setxattr->setxa_buf, setxattr->setxa_len,
setxattr->setxa_flags);
if (!ret)
set_change_info(&setxattr->setxa_cinfo, &cstate->current_fh);
return ret;
}
static __be32
nfsd4_listxattrs(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
/*
* Get the entire list, then copy out only the user attributes
* in the encode function.
*/
return nfsd_listxattr(rqstp, &cstate->current_fh,
&u->listxattrs.lsxa_buf, &u->listxattrs.lsxa_len);
}
static __be32
nfsd4_removexattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd4_removexattr *removexattr = &u->removexattr;
__be32 ret;
if (opens_in_grace(SVC_NET(rqstp)))
return nfserr_grace;
ret = nfsd_removexattr(rqstp, &cstate->current_fh,
removexattr->rmxa_name);
if (!ret)
set_change_info(&removexattr->rmxa_cinfo, &cstate->current_fh);
return ret;
}
/* /*
* NULL call. * NULL call.
*/ */
...@@ -2700,6 +2768,42 @@ static inline u32 nfsd4_seek_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) ...@@ -2700,6 +2768,42 @@ static inline u32 nfsd4_seek_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
return (op_encode_hdr_size + 3) * sizeof(__be32); return (op_encode_hdr_size + 3) * sizeof(__be32);
} }
static inline u32 nfsd4_getxattr_rsize(struct svc_rqst *rqstp,
struct nfsd4_op *op)
{
u32 maxcount, rlen;
maxcount = svc_max_payload(rqstp);
rlen = min_t(u32, XATTR_SIZE_MAX, maxcount);
return (op_encode_hdr_size + 1 + XDR_QUADLEN(rlen)) * sizeof(__be32);
}
static inline u32 nfsd4_setxattr_rsize(struct svc_rqst *rqstp,
struct nfsd4_op *op)
{
return (op_encode_hdr_size + op_encode_change_info_maxsz)
* sizeof(__be32);
}
static inline u32 nfsd4_listxattrs_rsize(struct svc_rqst *rqstp,
struct nfsd4_op *op)
{
u32 maxcount, rlen;
maxcount = svc_max_payload(rqstp);
rlen = min(op->u.listxattrs.lsxa_maxcount, maxcount);
return (op_encode_hdr_size + 4 + XDR_QUADLEN(rlen)) * sizeof(__be32);
}
static inline u32 nfsd4_removexattr_rsize(struct svc_rqst *rqstp,
struct nfsd4_op *op)
{
return (op_encode_hdr_size + op_encode_change_info_maxsz)
* sizeof(__be32);
}
static const struct nfsd4_operation nfsd4_ops[] = { static const struct nfsd4_operation nfsd4_ops[] = {
[OP_ACCESS] = { [OP_ACCESS] = {
.op_func = nfsd4_access, .op_func = nfsd4_access,
...@@ -3081,6 +3185,28 @@ static const struct nfsd4_operation nfsd4_ops[] = { ...@@ -3081,6 +3185,28 @@ static const struct nfsd4_operation nfsd4_ops[] = {
.op_name = "OP_COPY_NOTIFY", .op_name = "OP_COPY_NOTIFY",
.op_rsize_bop = nfsd4_copy_notify_rsize, .op_rsize_bop = nfsd4_copy_notify_rsize,
}, },
[OP_GETXATTR] = {
.op_func = nfsd4_getxattr,
.op_name = "OP_GETXATTR",
.op_rsize_bop = nfsd4_getxattr_rsize,
},
[OP_SETXATTR] = {
.op_func = nfsd4_setxattr,
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
.op_name = "OP_SETXATTR",
.op_rsize_bop = nfsd4_setxattr_rsize,
},
[OP_LISTXATTRS] = {
.op_func = nfsd4_listxattrs,
.op_name = "OP_LISTXATTRS",
.op_rsize_bop = nfsd4_listxattrs_rsize,
},
[OP_REMOVEXATTR] = {
.op_func = nfsd4_removexattr,
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
.op_name = "OP_REMOVEXATTR",
.op_rsize_bop = nfsd4_removexattr_rsize,
},
}; };
/** /**
......
...@@ -747,13 +747,11 @@ struct cld_upcall { ...@@ -747,13 +747,11 @@ struct cld_upcall {
}; };
static int static int
__cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg) __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
{ {
int ret; int ret;
struct rpc_pipe_msg msg; struct rpc_pipe_msg msg;
struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u); struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u);
struct nfsd_net *nn = net_generic(pipe->dentry->d_sb->s_fs_info,
nfsd_net_id);
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
msg.data = cmsg; msg.data = cmsg;
...@@ -773,7 +771,7 @@ __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg) ...@@ -773,7 +771,7 @@ __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg)
} }
static int static int
cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg) cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
{ {
int ret; int ret;
...@@ -782,7 +780,7 @@ cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg) ...@@ -782,7 +780,7 @@ cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg)
* upcalls queued. * upcalls queued.
*/ */
do { do {
ret = __cld_pipe_upcall(pipe, cmsg); ret = __cld_pipe_upcall(pipe, cmsg, nn);
} while (ret == -EAGAIN); } while (ret == -EAGAIN);
return ret; return ret;
...@@ -1115,7 +1113,7 @@ nfsd4_cld_create(struct nfs4_client *clp) ...@@ -1115,7 +1113,7 @@ nfsd4_cld_create(struct nfs4_client *clp)
memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
clp->cl_name.len); clp->cl_name.len);
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
if (!ret) { if (!ret) {
ret = cup->cu_u.cu_msg.cm_status; ret = cup->cu_u.cu_msg.cm_status;
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
...@@ -1180,7 +1178,7 @@ nfsd4_cld_create_v2(struct nfs4_client *clp) ...@@ -1180,7 +1178,7 @@ nfsd4_cld_create_v2(struct nfs4_client *clp)
} else } else
cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0; cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0;
ret = cld_pipe_upcall(cn->cn_pipe, cmsg); ret = cld_pipe_upcall(cn->cn_pipe, cmsg, nn);
if (!ret) { if (!ret) {
ret = cmsg->cm_status; ret = cmsg->cm_status;
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
...@@ -1218,7 +1216,7 @@ nfsd4_cld_remove(struct nfs4_client *clp) ...@@ -1218,7 +1216,7 @@ nfsd4_cld_remove(struct nfs4_client *clp)
memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
clp->cl_name.len); clp->cl_name.len);
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
if (!ret) { if (!ret) {
ret = cup->cu_u.cu_msg.cm_status; ret = cup->cu_u.cu_msg.cm_status;
clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
...@@ -1261,7 +1259,7 @@ nfsd4_cld_check_v0(struct nfs4_client *clp) ...@@ -1261,7 +1259,7 @@ nfsd4_cld_check_v0(struct nfs4_client *clp)
memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
clp->cl_name.len); clp->cl_name.len);
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
if (!ret) { if (!ret) {
ret = cup->cu_u.cu_msg.cm_status; ret = cup->cu_u.cu_msg.cm_status;
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
...@@ -1404,7 +1402,7 @@ nfsd4_cld_grace_start(struct nfsd_net *nn) ...@@ -1404,7 +1402,7 @@ nfsd4_cld_grace_start(struct nfsd_net *nn)
} }
cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart; cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
if (!ret) if (!ret)
ret = cup->cu_u.cu_msg.cm_status; ret = cup->cu_u.cu_msg.cm_status;
...@@ -1432,7 +1430,7 @@ nfsd4_cld_grace_done_v0(struct nfsd_net *nn) ...@@ -1432,7 +1430,7 @@ nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone; cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
cup->cu_u.cu_msg.cm_u.cm_gracetime = nn->boot_time; cup->cu_u.cu_msg.cm_u.cm_gracetime = nn->boot_time;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
if (!ret) if (!ret)
ret = cup->cu_u.cu_msg.cm_status; ret = cup->cu_u.cu_msg.cm_status;
...@@ -1460,7 +1458,7 @@ nfsd4_cld_grace_done(struct nfsd_net *nn) ...@@ -1460,7 +1458,7 @@ nfsd4_cld_grace_done(struct nfsd_net *nn)
} }
cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone; cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
if (!ret) if (!ret)
ret = cup->cu_u.cu_msg.cm_status; ret = cup->cu_u.cu_msg.cm_status;
...@@ -1524,7 +1522,7 @@ nfsd4_cld_get_version(struct nfsd_net *nn) ...@@ -1524,7 +1522,7 @@ nfsd4_cld_get_version(struct nfsd_net *nn)
goto out_err; goto out_err;
} }
cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion; cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg); ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
if (!ret) { if (!ret) {
ret = cup->cu_u.cu_msg.cm_status; ret = cup->cu_u.cu_msg.cm_status;
if (ret) if (ret)
......
...@@ -4940,6 +4940,32 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, ...@@ -4940,6 +4940,32 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp,
return fl; return fl;
} }
static int nfsd4_check_conflicting_opens(struct nfs4_client *clp,
struct nfs4_file *fp)
{
struct nfs4_clnt_odstate *co;
struct file *f = fp->fi_deleg_file->nf_file;
struct inode *ino = locks_inode(f);
int writes = atomic_read(&ino->i_writecount);
if (fp->fi_fds[O_WRONLY])
writes--;
if (fp->fi_fds[O_RDWR])
writes--;
WARN_ON_ONCE(writes < 0);
if (writes > 0)
return -EAGAIN;
spin_lock(&fp->fi_lock);
list_for_each_entry(co, &fp->fi_clnt_odstate, co_perfile) {
if (co->co_client != clp) {
spin_unlock(&fp->fi_lock);
return -EAGAIN;
}
}
spin_unlock(&fp->fi_lock);
return 0;
}
static struct nfs4_delegation * static struct nfs4_delegation *
nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate) struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate)
...@@ -4959,9 +4985,12 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, ...@@ -4959,9 +4985,12 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
nf = find_readable_file(fp); nf = find_readable_file(fp);
if (!nf) { if (!nf) {
/* We should always have a readable file here */ /*
WARN_ON_ONCE(1); * We probably could attempt another open and get a read
return ERR_PTR(-EBADF); * delegation, but for now, don't bother until the
* client actually sends us one.
*/
return ERR_PTR(-EAGAIN);
} }
spin_lock(&state_lock); spin_lock(&state_lock);
spin_lock(&fp->fi_lock); spin_lock(&fp->fi_lock);
...@@ -4991,11 +5020,19 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh, ...@@ -4991,11 +5020,19 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
if (!fl) if (!fl)
goto out_clnt_odstate; goto out_clnt_odstate;
status = nfsd4_check_conflicting_opens(clp, fp);
if (status) {
locks_free_lock(fl);
goto out_clnt_odstate;
}
status = vfs_setlease(fp->fi_deleg_file->nf_file, fl->fl_type, &fl, NULL); status = vfs_setlease(fp->fi_deleg_file->nf_file, fl->fl_type, &fl, NULL);
if (fl) if (fl)
locks_free_lock(fl); locks_free_lock(fl);
if (status) if (status)
goto out_clnt_odstate; goto out_clnt_odstate;
status = nfsd4_check_conflicting_opens(clp, fp);
if (status)
goto out_clnt_odstate;
spin_lock(&state_lock); spin_lock(&state_lock);
spin_lock(&fp->fi_lock); spin_lock(&fp->fi_lock);
...@@ -5077,17 +5114,6 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, ...@@ -5077,17 +5114,6 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open,
goto out_no_deleg; goto out_no_deleg;
if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED)) if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED))
goto out_no_deleg; goto out_no_deleg;
/*
* Also, if the file was opened for write or
* create, there's a good chance the client's
* about to write to it, resulting in an
* immediate recall (since we don't support
* write delegations):
*/
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
goto out_no_deleg;
if (open->op_create == NFS4_OPEN_CREATE)
goto out_no_deleg;
break; break;
default: default:
goto out_no_deleg; goto out_no_deleg;
......
This diff is collapsed.
...@@ -286,6 +286,8 @@ void nfsd_lockd_shutdown(void); ...@@ -286,6 +286,8 @@ void nfsd_lockd_shutdown(void);
#define nfserr_wrong_lfs cpu_to_be32(NFS4ERR_WRONG_LFS) #define nfserr_wrong_lfs cpu_to_be32(NFS4ERR_WRONG_LFS)
#define nfserr_badlabel cpu_to_be32(NFS4ERR_BADLABEL) #define nfserr_badlabel cpu_to_be32(NFS4ERR_BADLABEL)
#define nfserr_file_open cpu_to_be32(NFS4ERR_FILE_OPEN) #define nfserr_file_open cpu_to_be32(NFS4ERR_FILE_OPEN)
#define nfserr_xattr2big cpu_to_be32(NFS4ERR_XATTR2BIG)
#define nfserr_noxattr cpu_to_be32(NFS4ERR_NOXATTR)
/* error codes for internal use */ /* error codes for internal use */
/* if a request fails due to kmalloc failure, it gets dropped. /* if a request fails due to kmalloc failure, it gets dropped.
...@@ -387,7 +389,8 @@ void nfsd_lockd_shutdown(void); ...@@ -387,7 +389,8 @@ void nfsd_lockd_shutdown(void);
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \ (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_CHANGE_ATTR_TYPE | \ FATTR4_WORD2_CHANGE_ATTR_TYPE | \
FATTR4_WORD2_MODE_UMASK | \ FATTR4_WORD2_MODE_UMASK | \
NFSD4_2_SECURITY_ATTRS) NFSD4_2_SECURITY_ATTRS | \
FATTR4_WORD2_XATTR_SUPPORT)
extern const u32 nfsd_suppattrs[3][3]; extern const u32 nfsd_suppattrs[3][3];
......
...@@ -612,6 +612,12 @@ static struct accessmap nfs3_regaccess[] = { ...@@ -612,6 +612,12 @@ static struct accessmap nfs3_regaccess[] = {
{ NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC }, { NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE }, { NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE },
#ifdef CONFIG_NFSD_V4
{ NFS4_ACCESS_XAREAD, NFSD_MAY_READ },
{ NFS4_ACCESS_XAWRITE, NFSD_MAY_WRITE },
{ NFS4_ACCESS_XALIST, NFSD_MAY_READ },
#endif
{ 0, 0 } { 0, 0 }
}; };
...@@ -622,6 +628,12 @@ static struct accessmap nfs3_diraccess[] = { ...@@ -622,6 +628,12 @@ static struct accessmap nfs3_diraccess[] = {
{ NFS3_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE }, { NFS3_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE },
{ NFS3_ACCESS_DELETE, NFSD_MAY_REMOVE }, { NFS3_ACCESS_DELETE, NFSD_MAY_REMOVE },
#ifdef CONFIG_NFSD_V4
{ NFS4_ACCESS_XAREAD, NFSD_MAY_READ },
{ NFS4_ACCESS_XAWRITE, NFSD_MAY_WRITE },
{ NFS4_ACCESS_XALIST, NFSD_MAY_READ },
#endif
{ 0, 0 } { 0, 0 }
}; };
...@@ -2065,6 +2077,233 @@ static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp) ...@@ -2065,6 +2077,233 @@ static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp)
return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY; return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY;
} }
#ifdef CONFIG_NFSD_V4
/*
* Helper function to translate error numbers. In the case of xattr operations,
* some error codes need to be translated outside of the standard translations.
*
* ENODATA needs to be translated to nfserr_noxattr.
* E2BIG to nfserr_xattr2big.
*
* Additionally, vfs_listxattr can return -ERANGE. This means that the
* file has too many extended attributes to retrieve inside an
* XATTR_LIST_MAX sized buffer. This is a bug in the xattr implementation:
* filesystems will allow the adding of extended attributes until they hit
* their own internal limit. This limit may be larger than XATTR_LIST_MAX.
* So, at that point, the attributes are present and valid, but can't
* be retrieved using listxattr, since the upper level xattr code enforces
* the XATTR_LIST_MAX limit.
*
* This bug means that we need to deal with listxattr returning -ERANGE. The
* best mapping is to return TOOSMALL.
*/
static __be32
nfsd_xattr_errno(int err)
{
switch (err) {
case -ENODATA:
return nfserr_noxattr;
case -E2BIG:
return nfserr_xattr2big;
case -ERANGE:
return nfserr_toosmall;
}
return nfserrno(err);
}
/*
* Retrieve the specified user extended attribute. To avoid always
* having to allocate the maximum size (since we are not getting
* a maximum size from the RPC), do a probe + alloc. Hold a reader
* lock on i_rwsem to prevent the extended attribute from changing
* size while we're doing this.
*/
__be32
nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
void **bufp, int *lenp)
{
ssize_t len;
__be32 err;
char *buf;
struct inode *inode;
struct dentry *dentry;
err = fh_verify(rqstp, fhp, 0, NFSD_MAY_READ);
if (err)
return err;
err = nfs_ok;
dentry = fhp->fh_dentry;
inode = d_inode(dentry);
inode_lock_shared(inode);
len = vfs_getxattr(dentry, name, NULL, 0);
/*
* Zero-length attribute, just return.
*/
if (len == 0) {
*bufp = NULL;
*lenp = 0;
goto out;
}
if (len < 0) {
err = nfsd_xattr_errno(len);
goto out;
}
if (len > *lenp) {
err = nfserr_toosmall;
goto out;
}
buf = kvmalloc(len, GFP_KERNEL | GFP_NOFS);
if (buf == NULL) {
err = nfserr_jukebox;
goto out;
}
len = vfs_getxattr(dentry, name, buf, len);
if (len <= 0) {
kvfree(buf);
buf = NULL;
err = nfsd_xattr_errno(len);
}
*lenp = len;
*bufp = buf;
out:
inode_unlock_shared(inode);
return err;
}
/*
* Retrieve the xattr names. Since we can't know how many are
* user extended attributes, we must get all attributes here,
* and have the XDR encode filter out the "user." ones.
*
* While this could always just allocate an XATTR_LIST_MAX
* buffer, that's a waste, so do a probe + allocate. To
* avoid any changes between the probe and allocate, wrap
* this in inode_lock.
*/
__be32
nfsd_listxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char **bufp,
int *lenp)
{
ssize_t len;
__be32 err;
char *buf;
struct inode *inode;
struct dentry *dentry;
err = fh_verify(rqstp, fhp, 0, NFSD_MAY_READ);
if (err)
return err;
dentry = fhp->fh_dentry;
inode = d_inode(dentry);
*lenp = 0;
inode_lock_shared(inode);
len = vfs_listxattr(dentry, NULL, 0);
if (len <= 0) {
err = nfsd_xattr_errno(len);
goto out;
}
if (len > XATTR_LIST_MAX) {
err = nfserr_xattr2big;
goto out;
}
/*
* We're holding i_rwsem - use GFP_NOFS.
*/
buf = kvmalloc(len, GFP_KERNEL | GFP_NOFS);
if (buf == NULL) {
err = nfserr_jukebox;
goto out;
}
len = vfs_listxattr(dentry, buf, len);
if (len <= 0) {
kvfree(buf);
err = nfsd_xattr_errno(len);
goto out;
}
*lenp = len;
*bufp = buf;
err = nfs_ok;
out:
inode_unlock_shared(inode);
return err;
}
/*
* Removexattr and setxattr need to call fh_lock to both lock the inode
* and set the change attribute. Since the top-level vfs_removexattr
* and vfs_setxattr calls already do their own inode_lock calls, call
* the _locked variant. Pass in a NULL pointer for delegated_inode,
* and let the client deal with NFS4ERR_DELAY (same as with e.g.
* setattr and remove).
*/
__be32
nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name)
{
int err, ret;
err = fh_verify(rqstp, fhp, 0, NFSD_MAY_WRITE);
if (err)
return err;
ret = fh_want_write(fhp);
if (ret)
return nfserrno(ret);
fh_lock(fhp);
ret = __vfs_removexattr_locked(fhp->fh_dentry, name, NULL);
fh_unlock(fhp);
fh_drop_write(fhp);
return nfsd_xattr_errno(ret);
}
__be32
nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
void *buf, u32 len, u32 flags)
{
int err, ret;
err = fh_verify(rqstp, fhp, 0, NFSD_MAY_WRITE);
if (err)
return err;
ret = fh_want_write(fhp);
if (ret)
return nfserrno(ret);
fh_lock(fhp);
ret = __vfs_setxattr_locked(fhp->fh_dentry, name, buf, len, flags,
NULL);
fh_unlock(fhp);
fh_drop_write(fhp);
return nfsd_xattr_errno(ret);
}
#endif
/* /*
* Check for a user's access permissions to this inode. * Check for a user's access permissions to this inode.
*/ */
......
...@@ -76,6 +76,16 @@ __be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *, ...@@ -76,6 +76,16 @@ __be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *,
__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *, __be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
loff_t, unsigned long, __be32 *verf); loff_t, unsigned long, __be32 *verf);
#endif /* CONFIG_NFSD_V3 */ #endif /* CONFIG_NFSD_V3 */
#ifdef CONFIG_NFSD_V4
__be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *name, void **bufp, int *lenp);
__be32 nfsd_listxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
char **bufp, int *lenp);
__be32 nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *name);
__be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *name, void *buf, u32 len, u32 flags);
#endif
int nfsd_open_break_lease(struct inode *, int); int nfsd_open_break_lease(struct inode *, int);
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t, __be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **); int, struct file **);
......
...@@ -224,6 +224,32 @@ struct nfsd4_putfh { ...@@ -224,6 +224,32 @@ struct nfsd4_putfh {
bool no_verify; /* represents foreigh fh */ bool no_verify; /* represents foreigh fh */
}; };
struct nfsd4_getxattr {
char *getxa_name; /* request */
u32 getxa_len; /* request */
void *getxa_buf;
};
struct nfsd4_setxattr {
u32 setxa_flags; /* request */
char *setxa_name; /* request */
char *setxa_buf; /* request */
u32 setxa_len; /* request */
struct nfsd4_change_info setxa_cinfo; /* response */
};
struct nfsd4_removexattr {
char *rmxa_name; /* request */
struct nfsd4_change_info rmxa_cinfo; /* response */
};
struct nfsd4_listxattrs {
u64 lsxa_cookie; /* request */
u32 lsxa_maxcount; /* request */
char *lsxa_buf; /* unfiltered buffer (reply) */
u32 lsxa_len; /* unfiltered len (reply) */
};
struct nfsd4_open { struct nfsd4_open {
u32 op_claim_type; /* request */ u32 op_claim_type; /* request */
struct xdr_netobj op_fname; /* request - everything but CLAIM_PREV */ struct xdr_netobj op_fname; /* request - everything but CLAIM_PREV */
...@@ -649,6 +675,11 @@ struct nfsd4_op { ...@@ -649,6 +675,11 @@ struct nfsd4_op {
struct nfsd4_offload_status offload_status; struct nfsd4_offload_status offload_status;
struct nfsd4_copy_notify copy_notify; struct nfsd4_copy_notify copy_notify;
struct nfsd4_seek seek; struct nfsd4_seek seek;
struct nfsd4_getxattr getxattr;
struct nfsd4_setxattr setxattr;
struct nfsd4_listxattrs listxattrs;
struct nfsd4_removexattr removexattr;
} u; } u;
struct nfs4_replay * replay; struct nfs4_replay * replay;
}; };
......
...@@ -134,6 +134,33 @@ xattr_permission(struct inode *inode, const char *name, int mask) ...@@ -134,6 +134,33 @@ xattr_permission(struct inode *inode, const char *name, int mask)
return inode_permission(inode, mask); return inode_permission(inode, mask);
} }
/*
* Look for any handler that deals with the specified namespace.
*/
int
xattr_supported_namespace(struct inode *inode, const char *prefix)
{
const struct xattr_handler **handlers = inode->i_sb->s_xattr;
const struct xattr_handler *handler;
size_t preflen;
if (!(inode->i_opflags & IOP_XATTR)) {
if (unlikely(is_bad_inode(inode)))
return -EIO;
return -EOPNOTSUPP;
}
preflen = strlen(prefix);
for_each_xattr_handler(handlers, handler) {
if (!strncmp(xattr_prefix(handler), prefix, preflen))
return 0;
}
return -EOPNOTSUPP;
}
EXPORT_SYMBOL(xattr_supported_namespace);
int int
__vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, __vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
...@@ -204,10 +231,22 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, ...@@ -204,10 +231,22 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
return error; return error;
} }
/**
* __vfs_setxattr_locked: set an extended attribute while holding the inode
* lock
*
* @dentry - object to perform setxattr on
* @name - xattr name to set
* @value - value to set @name to
* @size - size of @value
* @flags - flags to pass into filesystem operations
* @delegated_inode - on return, will contain an inode pointer that
* a delegation was broken on, NULL if none.
*/
int int
vfs_setxattr(struct dentry *dentry, const char *name, const void *value, __vfs_setxattr_locked(struct dentry *dentry, const char *name,
size_t size, int flags) const void *value, size_t size, int flags,
struct inode **delegated_inode)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int error; int error;
...@@ -216,15 +255,40 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value, ...@@ -216,15 +255,40 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
if (error) if (error)
return error; return error;
inode_lock(inode);
error = security_inode_setxattr(dentry, name, value, size, flags); error = security_inode_setxattr(dentry, name, value, size, flags);
if (error) if (error)
goto out; goto out;
error = try_break_deleg(inode, delegated_inode);
if (error)
goto out;
error = __vfs_setxattr_noperm(dentry, name, value, size, flags); error = __vfs_setxattr_noperm(dentry, name, value, size, flags);
out: out:
return error;
}
EXPORT_SYMBOL_GPL(__vfs_setxattr_locked);
int
vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct inode *delegated_inode = NULL;
int error;
retry_deleg:
inode_lock(inode);
error = __vfs_setxattr_locked(dentry, name, value, size, flags,
&delegated_inode);
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
}
return error; return error;
} }
EXPORT_SYMBOL_GPL(vfs_setxattr); EXPORT_SYMBOL_GPL(vfs_setxattr);
...@@ -378,8 +442,18 @@ __vfs_removexattr(struct dentry *dentry, const char *name) ...@@ -378,8 +442,18 @@ __vfs_removexattr(struct dentry *dentry, const char *name)
} }
EXPORT_SYMBOL(__vfs_removexattr); EXPORT_SYMBOL(__vfs_removexattr);
/**
* __vfs_removexattr_locked: set an extended attribute while holding the inode
* lock
*
* @dentry - object to perform setxattr on
* @name - name of xattr to remove
* @delegated_inode - on return, will contain an inode pointer that
* a delegation was broken on, NULL if none.
*/
int int
vfs_removexattr(struct dentry *dentry, const char *name) __vfs_removexattr_locked(struct dentry *dentry, const char *name,
struct inode **delegated_inode)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int error; int error;
...@@ -388,11 +462,14 @@ vfs_removexattr(struct dentry *dentry, const char *name) ...@@ -388,11 +462,14 @@ vfs_removexattr(struct dentry *dentry, const char *name)
if (error) if (error)
return error; return error;
inode_lock(inode);
error = security_inode_removexattr(dentry, name); error = security_inode_removexattr(dentry, name);
if (error) if (error)
goto out; goto out;
error = try_break_deleg(inode, delegated_inode);
if (error)
goto out;
error = __vfs_removexattr(dentry, name); error = __vfs_removexattr(dentry, name);
if (!error) { if (!error) {
...@@ -401,12 +478,32 @@ vfs_removexattr(struct dentry *dentry, const char *name) ...@@ -401,12 +478,32 @@ vfs_removexattr(struct dentry *dentry, const char *name)
} }
out: out:
return error;
}
EXPORT_SYMBOL_GPL(__vfs_removexattr_locked);
int
vfs_removexattr(struct dentry *dentry, const char *name)
{
struct inode *inode = dentry->d_inode;
struct inode *delegated_inode = NULL;
int error;
retry_deleg:
inode_lock(inode);
error = __vfs_removexattr_locked(dentry, name, &delegated_inode);
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
}
return error; return error;
} }
EXPORT_SYMBOL_GPL(vfs_removexattr); EXPORT_SYMBOL_GPL(vfs_removexattr);
/* /*
* Extended attribute SET operations * Extended attribute SET operations
*/ */
......
...@@ -150,6 +150,12 @@ enum nfs_opnum4 { ...@@ -150,6 +150,12 @@ enum nfs_opnum4 {
OP_WRITE_SAME = 70, OP_WRITE_SAME = 70,
OP_CLONE = 71, OP_CLONE = 71,
/* xattr support (RFC8726) */
OP_GETXATTR = 72,
OP_SETXATTR = 73,
OP_LISTXATTRS = 74,
OP_REMOVEXATTR = 75,
OP_ILLEGAL = 10044, OP_ILLEGAL = 10044,
}; };
...@@ -159,7 +165,7 @@ Needs to be updated if more operations are defined in future.*/ ...@@ -159,7 +165,7 @@ Needs to be updated if more operations are defined in future.*/
#define FIRST_NFS4_OP OP_ACCESS #define FIRST_NFS4_OP OP_ACCESS
#define LAST_NFS40_OP OP_RELEASE_LOCKOWNER #define LAST_NFS40_OP OP_RELEASE_LOCKOWNER
#define LAST_NFS41_OP OP_RECLAIM_COMPLETE #define LAST_NFS41_OP OP_RECLAIM_COMPLETE
#define LAST_NFS42_OP OP_CLONE #define LAST_NFS42_OP OP_REMOVEXATTR
#define LAST_NFS4_OP LAST_NFS42_OP #define LAST_NFS4_OP LAST_NFS42_OP
enum nfsstat4 { enum nfsstat4 {
...@@ -280,6 +286,10 @@ enum nfsstat4 { ...@@ -280,6 +286,10 @@ enum nfsstat4 {
NFS4ERR_WRONG_LFS = 10092, NFS4ERR_WRONG_LFS = 10092,
NFS4ERR_BADLABEL = 10093, NFS4ERR_BADLABEL = 10093,
NFS4ERR_OFFLOAD_NO_REQS = 10094, NFS4ERR_OFFLOAD_NO_REQS = 10094,
/* xattr (RFC8276) */
NFS4ERR_NOXATTR = 10095,
NFS4ERR_XATTR2BIG = 10096,
}; };
static inline bool seqid_mutating_err(u32 err) static inline bool seqid_mutating_err(u32 err)
...@@ -452,6 +462,7 @@ enum change_attr_type4 { ...@@ -452,6 +462,7 @@ enum change_attr_type4 {
#define FATTR4_WORD2_CHANGE_ATTR_TYPE (1UL << 15) #define FATTR4_WORD2_CHANGE_ATTR_TYPE (1UL << 15)
#define FATTR4_WORD2_SECURITY_LABEL (1UL << 16) #define FATTR4_WORD2_SECURITY_LABEL (1UL << 16)
#define FATTR4_WORD2_MODE_UMASK (1UL << 17) #define FATTR4_WORD2_MODE_UMASK (1UL << 17)
#define FATTR4_WORD2_XATTR_SUPPORT (1UL << 18)
/* MDS threshold bitmap bits */ /* MDS threshold bitmap bits */
#define THRESHOLD_RD (1UL << 0) #define THRESHOLD_RD (1UL << 0)
...@@ -700,4 +711,13 @@ struct nl4_server { ...@@ -700,4 +711,13 @@ struct nl4_server {
struct nfs42_netaddr nl4_addr; /* NL4_NETADDR */ struct nfs42_netaddr nl4_addr; /* NL4_NETADDR */
} u; } u;
}; };
/*
* Options for setxattr. These match the flags for setxattr(2).
*/
enum nfs4_setxattr_options {
SETXATTR4_EITHER = 0,
SETXATTR4_CREATE = 1,
SETXATTR4_REPLACE = 2,
};
#endif #endif
...@@ -124,4 +124,78 @@ rpcrdma_decode_buffer_size(u8 val) ...@@ -124,4 +124,78 @@ rpcrdma_decode_buffer_size(u8 val)
return ((unsigned int)val + 1) << 10; return ((unsigned int)val + 1) << 10;
} }
/**
* xdr_encode_rdma_segment - Encode contents of an RDMA segment
* @p: Pointer into a send buffer
* @handle: The RDMA handle to encode
* @length: The RDMA length to encode
* @offset: The RDMA offset to encode
*
* Return value:
* Pointer to the XDR position that follows the encoded RDMA segment
*/
static inline __be32 *xdr_encode_rdma_segment(__be32 *p, u32 handle,
u32 length, u64 offset)
{
*p++ = cpu_to_be32(handle);
*p++ = cpu_to_be32(length);
return xdr_encode_hyper(p, offset);
}
/**
* xdr_encode_read_segment - Encode contents of a Read segment
* @p: Pointer into a send buffer
* @position: The position to encode
* @handle: The RDMA handle to encode
* @length: The RDMA length to encode
* @offset: The RDMA offset to encode
*
* Return value:
* Pointer to the XDR position that follows the encoded Read segment
*/
static inline __be32 *xdr_encode_read_segment(__be32 *p, u32 position,
u32 handle, u32 length,
u64 offset)
{
*p++ = cpu_to_be32(position);
return xdr_encode_rdma_segment(p, handle, length, offset);
}
/**
* xdr_decode_rdma_segment - Decode contents of an RDMA segment
* @p: Pointer to the undecoded RDMA segment
* @handle: Upon return, the RDMA handle
* @length: Upon return, the RDMA length
* @offset: Upon return, the RDMA offset
*
* Return value:
* Pointer to the XDR item that follows the RDMA segment
*/
static inline __be32 *xdr_decode_rdma_segment(__be32 *p, u32 *handle,
u32 *length, u64 *offset)
{
*handle = be32_to_cpup(p++);
*length = be32_to_cpup(p++);
return xdr_decode_hyper(p, offset);
}
/**
* xdr_decode_read_segment - Decode contents of a Read segment
* @p: Pointer to the undecoded Read segment
* @position: Upon return, the segment's position
* @handle: Upon return, the RDMA handle
* @length: Upon return, the RDMA length
* @offset: Upon return, the RDMA offset
*
* Return value:
* Pointer to the XDR item that follows the Read segment
*/
static inline __be32 *xdr_decode_read_segment(__be32 *p, u32 *position,
u32 *handle, u32 *length,
u64 *offset)
{
*position = be32_to_cpup(p++);
return xdr_decode_rdma_segment(p, handle, length, offset);
}
#endif /* _LINUX_SUNRPC_RPC_RDMA_H */ #endif /* _LINUX_SUNRPC_RPC_RDMA_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* * Copyright (c) 2020, Oracle and/or its affiliates.
*/
#ifndef RPC_RDMA_CID_H
#define RPC_RDMA_CID_H
/*
* The rpc_rdma_cid struct records completion ID information. A
* completion ID matches an incoming Send or Receive completion
* to a Completion Queue and to a previous ib_post_*(). The ID
* can then be displayed in an error message or recorded in a
* trace record.
*
* This struct is shared between the server and client RPC/RDMA
* transport implementations.
*/
struct rpc_rdma_cid {
u32 ci_queue_id;
int ci_completion_id;
};
#endif /* RPC_RDMA_CID_H */
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/rpc_rdma.h> #include <linux/sunrpc/rpc_rdma.h>
#include <linux/sunrpc/rpc_rdma_cid.h>
#include <rdma/ib_verbs.h> #include <rdma/ib_verbs.h>
#include <rdma/rdma_cm.h> #include <rdma/rdma_cm.h>
...@@ -109,6 +110,8 @@ struct svcxprt_rdma { ...@@ -109,6 +110,8 @@ struct svcxprt_rdma {
struct work_struct sc_work; struct work_struct sc_work;
struct llist_head sc_recv_ctxts; struct llist_head sc_recv_ctxts;
atomic_t sc_completion_ids;
}; };
/* sc_flags */ /* sc_flags */
#define RDMAXPRT_CONN_PENDING 3 #define RDMAXPRT_CONN_PENDING 3
...@@ -129,6 +132,7 @@ struct svc_rdma_recv_ctxt { ...@@ -129,6 +132,7 @@ struct svc_rdma_recv_ctxt {
struct list_head rc_list; struct list_head rc_list;
struct ib_recv_wr rc_recv_wr; struct ib_recv_wr rc_recv_wr;
struct ib_cqe rc_cqe; struct ib_cqe rc_cqe;
struct rpc_rdma_cid rc_cid;
struct ib_sge rc_recv_sge; struct ib_sge rc_recv_sge;
void *rc_recv_buf; void *rc_recv_buf;
struct xdr_buf rc_arg; struct xdr_buf rc_arg;
...@@ -147,6 +151,8 @@ struct svc_rdma_recv_ctxt { ...@@ -147,6 +151,8 @@ struct svc_rdma_recv_ctxt {
struct svc_rdma_send_ctxt { struct svc_rdma_send_ctxt {
struct list_head sc_list; struct list_head sc_list;
struct rpc_rdma_cid sc_cid;
struct ib_send_wr sc_send_wr; struct ib_send_wr sc_send_wr;
struct ib_cqe sc_cqe; struct ib_cqe sc_cqe;
struct xdr_buf sc_hdrbuf; struct xdr_buf sc_hdrbuf;
...@@ -190,20 +196,21 @@ extern struct svc_rdma_send_ctxt * ...@@ -190,20 +196,21 @@ extern struct svc_rdma_send_ctxt *
svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma); svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma);
extern void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, extern void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *ctxt); struct svc_rdma_send_ctxt *ctxt);
extern int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr); extern int svc_rdma_send(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *ctxt);
extern int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, extern int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *sctxt, struct svc_rdma_send_ctxt *sctxt,
const struct svc_rdma_recv_ctxt *rctxt, const struct svc_rdma_recv_ctxt *rctxt,
struct xdr_buf *xdr); struct xdr_buf *xdr);
extern void svc_rdma_send_error_msg(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *sctxt,
struct svc_rdma_recv_ctxt *rctxt,
int status);
extern int svc_rdma_sendto(struct svc_rqst *); extern int svc_rdma_sendto(struct svc_rqst *);
extern int svc_rdma_read_payload(struct svc_rqst *rqstp, unsigned int offset, extern int svc_rdma_read_payload(struct svc_rqst *rqstp, unsigned int offset,
unsigned int length); unsigned int length);
/* svc_rdma_transport.c */ /* svc_rdma_transport.c */
extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *);
extern void svc_sq_reap(struct svcxprt_rdma *);
extern void svc_rq_reap(struct svcxprt_rdma *);
extern struct svc_xprt_class svc_rdma_class; extern struct svc_xprt_class svc_rdma_class;
#ifdef CONFIG_SUNRPC_BACKCHANNEL #ifdef CONFIG_SUNRPC_BACKCHANNEL
extern struct svc_xprt_class svc_rdma_bc_class; extern struct svc_xprt_class svc_rdma_bc_class;
......
...@@ -474,6 +474,32 @@ xdr_stream_encode_uint32_array(struct xdr_stream *xdr, ...@@ -474,6 +474,32 @@ xdr_stream_encode_uint32_array(struct xdr_stream *xdr,
return ret; return ret;
} }
/**
* xdr_item_is_absent - symbolically handle XDR discriminators
* @p: pointer to undecoded discriminator
*
* Return values:
* %true if the following XDR item is absent
* %false if the following XDR item is present
*/
static inline bool xdr_item_is_absent(const __be32 *p)
{
return *p == xdr_zero;
}
/**
* xdr_item_is_present - symbolically handle XDR discriminators
* @p: pointer to undecoded discriminator
*
* Return values:
* %true if the following XDR item is present
* %false if the following XDR item is absent
*/
static inline bool xdr_item_is_present(const __be32 *p)
{
return *p != xdr_zero;
}
/** /**
* xdr_stream_decode_u32 - Decode a 32-bit integer * xdr_stream_decode_u32 - Decode a 32-bit integer
* @xdr: pointer to xdr_stream * @xdr: pointer to xdr_stream
......
...@@ -52,14 +52,18 @@ ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t); ...@@ -52,14 +52,18 @@ ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
int __vfs_setxattr(struct dentry *, struct inode *, const char *, const void *, size_t, int); int __vfs_setxattr(struct dentry *, struct inode *, const char *, const void *, size_t, int);
int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int); int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int);
int __vfs_setxattr_locked(struct dentry *, const char *, const void *, size_t, int, struct inode **);
int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int); int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
int __vfs_removexattr(struct dentry *, const char *); int __vfs_removexattr(struct dentry *, const char *);
int __vfs_removexattr_locked(struct dentry *, const char *, struct inode **);
int vfs_removexattr(struct dentry *, const char *); int vfs_removexattr(struct dentry *, const char *);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name, ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
char **xattr_value, size_t size, gfp_t flags); char **xattr_value, size_t size, gfp_t flags);
int xattr_supported_namespace(struct inode *inode, const char *prefix);
static inline const char *xattr_prefix(const struct xattr_handler *handler) static inline const char *xattr_prefix(const struct xattr_handler *handler)
{ {
return handler->prefix ?: handler->name; return handler->prefix ?: handler->name;
......
...@@ -170,55 +170,144 @@ DECLARE_EVENT_CLASS(rpcgss_ctx_class, ...@@ -170,55 +170,144 @@ DECLARE_EVENT_CLASS(rpcgss_ctx_class,
DEFINE_CTX_EVENT(init); DEFINE_CTX_EVENT(init);
DEFINE_CTX_EVENT(destroy); DEFINE_CTX_EVENT(destroy);
DECLARE_EVENT_CLASS(rpcgss_svc_gssapi_class,
TP_PROTO(
const struct svc_rqst *rqstp,
u32 maj_stat
),
TP_ARGS(rqstp, maj_stat),
TP_STRUCT__entry(
__field(u32, xid)
__field(u32, maj_stat)
__string(addr, rqstp->rq_xprt->xpt_remotebuf)
),
TP_fast_assign(
__entry->xid = __be32_to_cpu(rqstp->rq_xid);
__entry->maj_stat = maj_stat;
__assign_str(addr, rqstp->rq_xprt->xpt_remotebuf);
),
TP_printk("addr=%s xid=0x%08x maj_stat=%s",
__get_str(addr), __entry->xid,
__entry->maj_stat == 0 ?
"GSS_S_COMPLETE" : show_gss_status(__entry->maj_stat))
);
#define DEFINE_SVC_GSSAPI_EVENT(name) \
DEFINE_EVENT(rpcgss_svc_gssapi_class, rpcgss_svc_##name, \
TP_PROTO( \
const struct svc_rqst *rqstp, \
u32 maj_stat \
), \
TP_ARGS(rqstp, maj_stat))
DEFINE_SVC_GSSAPI_EVENT(unwrap);
DEFINE_SVC_GSSAPI_EVENT(mic);
TRACE_EVENT(rpcgss_svc_unwrap_failed,
TP_PROTO(
const struct svc_rqst *rqstp
),
TP_ARGS(rqstp),
TP_STRUCT__entry(
__field(u32, xid)
__string(addr, rqstp->rq_xprt->xpt_remotebuf)
),
TP_fast_assign(
__entry->xid = be32_to_cpu(rqstp->rq_xid);
__assign_str(addr, rqstp->rq_xprt->xpt_remotebuf);
),
TP_printk("addr=%s xid=0x%08x", __get_str(addr), __entry->xid)
);
TRACE_EVENT(rpcgss_svc_seqno_bad,
TP_PROTO(
const struct svc_rqst *rqstp,
u32 expected,
u32 received
),
TP_ARGS(rqstp, expected, received),
TP_STRUCT__entry(
__field(u32, expected)
__field(u32, received)
__field(u32, xid)
__string(addr, rqstp->rq_xprt->xpt_remotebuf)
),
TP_fast_assign(
__entry->expected = expected;
__entry->received = received;
__entry->xid = __be32_to_cpu(rqstp->rq_xid);
__assign_str(addr, rqstp->rq_xprt->xpt_remotebuf);
),
TP_printk("addr=%s xid=0x%08x expected seqno %u, received seqno %u",
__get_str(addr), __entry->xid,
__entry->expected, __entry->received)
);
TRACE_EVENT(rpcgss_svc_accept_upcall, TRACE_EVENT(rpcgss_svc_accept_upcall,
TP_PROTO( TP_PROTO(
__be32 xid, const struct svc_rqst *rqstp,
u32 major_status, u32 major_status,
u32 minor_status u32 minor_status
), ),
TP_ARGS(xid, major_status, minor_status), TP_ARGS(rqstp, major_status, minor_status),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(u32, xid)
__field(u32, minor_status) __field(u32, minor_status)
__field(unsigned long, major_status) __field(unsigned long, major_status)
__field(u32, xid)
__string(addr, rqstp->rq_xprt->xpt_remotebuf)
), ),
TP_fast_assign( TP_fast_assign(
__entry->xid = be32_to_cpu(xid);
__entry->minor_status = minor_status; __entry->minor_status = minor_status;
__entry->major_status = major_status; __entry->major_status = major_status;
__entry->xid = be32_to_cpu(rqstp->rq_xid);
__assign_str(addr, rqstp->rq_xprt->xpt_remotebuf);
), ),
TP_printk("xid=0x%08x major_status=%s (0x%08lx) minor_status=%u", TP_printk("addr=%s xid=0x%08x major_status=%s (0x%08lx) minor_status=%u",
__entry->xid, __entry->major_status == 0 ? "GSS_S_COMPLETE" : __get_str(addr), __entry->xid,
show_gss_status(__entry->major_status), (__entry->major_status == 0) ? "GSS_S_COMPLETE" :
show_gss_status(__entry->major_status),
__entry->major_status, __entry->minor_status __entry->major_status, __entry->minor_status
) )
); );
TRACE_EVENT(rpcgss_svc_accept, TRACE_EVENT(rpcgss_svc_authenticate,
TP_PROTO( TP_PROTO(
__be32 xid, const struct svc_rqst *rqstp,
size_t len const struct rpc_gss_wire_cred *gc
), ),
TP_ARGS(xid, len), TP_ARGS(rqstp, gc),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(u32, seqno)
__field(u32, xid) __field(u32, xid)
__field(size_t, len) __string(addr, rqstp->rq_xprt->xpt_remotebuf)
), ),
TP_fast_assign( TP_fast_assign(
__entry->xid = be32_to_cpu(xid); __entry->xid = be32_to_cpu(rqstp->rq_xid);
__entry->len = len; __entry->seqno = gc->gc_seq;
__assign_str(addr, rqstp->rq_xprt->xpt_remotebuf);
), ),
TP_printk("xid=0x%08x len=%zu", TP_printk("addr=%s xid=0x%08x seqno=%u", __get_str(addr),
__entry->xid, __entry->len __entry->xid, __entry->seqno)
)
); );
...@@ -371,11 +460,11 @@ TRACE_EVENT(rpcgss_update_slack, ...@@ -371,11 +460,11 @@ TRACE_EVENT(rpcgss_update_slack,
DECLARE_EVENT_CLASS(rpcgss_svc_seqno_class, DECLARE_EVENT_CLASS(rpcgss_svc_seqno_class,
TP_PROTO( TP_PROTO(
__be32 xid, const struct svc_rqst *rqstp,
u32 seqno u32 seqno
), ),
TP_ARGS(xid, seqno), TP_ARGS(rqstp, seqno),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(u32, xid) __field(u32, xid)
...@@ -383,25 +472,52 @@ DECLARE_EVENT_CLASS(rpcgss_svc_seqno_class, ...@@ -383,25 +472,52 @@ DECLARE_EVENT_CLASS(rpcgss_svc_seqno_class,
), ),
TP_fast_assign( TP_fast_assign(
__entry->xid = be32_to_cpu(xid); __entry->xid = be32_to_cpu(rqstp->rq_xid);
__entry->seqno = seqno; __entry->seqno = seqno;
), ),
TP_printk("xid=0x%08x seqno=%u, request discarded", TP_printk("xid=0x%08x seqno=%u",
__entry->xid, __entry->seqno) __entry->xid, __entry->seqno)
); );
#define DEFINE_SVC_SEQNO_EVENT(name) \ #define DEFINE_SVC_SEQNO_EVENT(name) \
DEFINE_EVENT(rpcgss_svc_seqno_class, rpcgss_svc_##name, \ DEFINE_EVENT(rpcgss_svc_seqno_class, rpcgss_svc_seqno_##name, \
TP_PROTO( \ TP_PROTO( \
__be32 xid, \ const struct svc_rqst *rqstp, \
u32 seqno \ u32 seqno \
), \ ), \
TP_ARGS(xid, seqno)) TP_ARGS(rqstp, seqno))
DEFINE_SVC_SEQNO_EVENT(large_seqno); DEFINE_SVC_SEQNO_EVENT(large);
DEFINE_SVC_SEQNO_EVENT(old_seqno); DEFINE_SVC_SEQNO_EVENT(seen);
TRACE_EVENT(rpcgss_svc_seqno_low,
TP_PROTO(
const struct svc_rqst *rqstp,
u32 seqno,
u32 min,
u32 max
),
TP_ARGS(rqstp, seqno, min, max),
TP_STRUCT__entry(
__field(u32, xid)
__field(u32, seqno)
__field(u32, min)
__field(u32, max)
),
TP_fast_assign(
__entry->xid = be32_to_cpu(rqstp->rq_xid);
__entry->seqno = seqno;
__entry->min = min;
__entry->max = max;
),
TP_printk("xid=0x%08x seqno=%u window=[%u..%u]",
__entry->xid, __entry->seqno, __entry->min, __entry->max)
);
/** /**
** gssd upcall related trace events ** gssd upcall related trace events
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#define _TRACE_RPCRDMA_H #define _TRACE_RPCRDMA_H
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/sunrpc/rpc_rdma_cid.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <trace/events/rdma.h> #include <trace/events/rdma.h>
...@@ -18,6 +19,46 @@ ...@@ -18,6 +19,46 @@
** Event classes ** Event classes
**/ **/
DECLARE_EVENT_CLASS(rpcrdma_completion_class,
TP_PROTO(
const struct ib_wc *wc,
const struct rpc_rdma_cid *cid
),
TP_ARGS(wc, cid),
TP_STRUCT__entry(
__field(u32, cq_id)
__field(int, completion_id)
__field(unsigned long, status)
__field(unsigned int, vendor_err)
),
TP_fast_assign(
__entry->cq_id = cid->ci_queue_id;
__entry->completion_id = cid->ci_completion_id;
__entry->status = wc->status;
if (wc->status)
__entry->vendor_err = wc->vendor_err;
else
__entry->vendor_err = 0;
),
TP_printk("cq.id=%u cid=%d status=%s (%lu/0x%x)",
__entry->cq_id, __entry->completion_id,
rdma_show_wc_status(__entry->status),
__entry->status, __entry->vendor_err
)
);
#define DEFINE_COMPLETION_EVENT(name) \
DEFINE_EVENT(rpcrdma_completion_class, name, \
TP_PROTO( \
const struct ib_wc *wc, \
const struct rpc_rdma_cid *cid \
), \
TP_ARGS(wc, cid))
DECLARE_EVENT_CLASS(xprtrdma_reply_event, DECLARE_EVENT_CLASS(xprtrdma_reply_event,
TP_PROTO( TP_PROTO(
const struct rpcrdma_rep *rep const struct rpcrdma_rep *rep
...@@ -1328,13 +1369,16 @@ TRACE_DEFINE_ENUM(RDMA_ERROR); ...@@ -1328,13 +1369,16 @@ TRACE_DEFINE_ENUM(RDMA_ERROR);
TRACE_EVENT(svcrdma_decode_rqst, TRACE_EVENT(svcrdma_decode_rqst,
TP_PROTO( TP_PROTO(
const struct svc_rdma_recv_ctxt *ctxt,
__be32 *p, __be32 *p,
unsigned int hdrlen unsigned int hdrlen
), ),
TP_ARGS(p, hdrlen), TP_ARGS(ctxt, p, hdrlen),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(u32, cq_id)
__field(int, completion_id)
__field(u32, xid) __field(u32, xid)
__field(u32, vers) __field(u32, vers)
__field(u32, proc) __field(u32, proc)
...@@ -1343,6 +1387,8 @@ TRACE_EVENT(svcrdma_decode_rqst, ...@@ -1343,6 +1387,8 @@ TRACE_EVENT(svcrdma_decode_rqst,
), ),
TP_fast_assign( TP_fast_assign(
__entry->cq_id = ctxt->rc_cid.ci_queue_id;
__entry->completion_id = ctxt->rc_cid.ci_completion_id;
__entry->xid = be32_to_cpup(p++); __entry->xid = be32_to_cpup(p++);
__entry->vers = be32_to_cpup(p++); __entry->vers = be32_to_cpup(p++);
__entry->credits = be32_to_cpup(p++); __entry->credits = be32_to_cpup(p++);
...@@ -1350,37 +1396,48 @@ TRACE_EVENT(svcrdma_decode_rqst, ...@@ -1350,37 +1396,48 @@ TRACE_EVENT(svcrdma_decode_rqst,
__entry->hdrlen = hdrlen; __entry->hdrlen = hdrlen;
), ),
TP_printk("xid=0x%08x vers=%u credits=%u proc=%s hdrlen=%u", TP_printk("cq.id=%u cid=%d xid=0x%08x vers=%u credits=%u proc=%s hdrlen=%u",
__entry->cq_id, __entry->completion_id,
__entry->xid, __entry->vers, __entry->credits, __entry->xid, __entry->vers, __entry->credits,
show_rpcrdma_proc(__entry->proc), __entry->hdrlen) show_rpcrdma_proc(__entry->proc), __entry->hdrlen)
); );
TRACE_EVENT(svcrdma_decode_short_err, TRACE_EVENT(svcrdma_decode_short_err,
TP_PROTO( TP_PROTO(
const struct svc_rdma_recv_ctxt *ctxt,
unsigned int hdrlen unsigned int hdrlen
), ),
TP_ARGS(hdrlen), TP_ARGS(ctxt, hdrlen),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(u32, cq_id)
__field(int, completion_id)
__field(unsigned int, hdrlen) __field(unsigned int, hdrlen)
), ),
TP_fast_assign( TP_fast_assign(
__entry->cq_id = ctxt->rc_cid.ci_queue_id;
__entry->completion_id = ctxt->rc_cid.ci_completion_id;
__entry->hdrlen = hdrlen; __entry->hdrlen = hdrlen;
), ),
TP_printk("hdrlen=%u", __entry->hdrlen) TP_printk("cq.id=%u cid=%d hdrlen=%u",
__entry->cq_id, __entry->completion_id,
__entry->hdrlen)
); );
DECLARE_EVENT_CLASS(svcrdma_badreq_event, DECLARE_EVENT_CLASS(svcrdma_badreq_event,
TP_PROTO( TP_PROTO(
const struct svc_rdma_recv_ctxt *ctxt,
__be32 *p __be32 *p
), ),
TP_ARGS(p), TP_ARGS(ctxt, p),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(u32, cq_id)
__field(int, completion_id)
__field(u32, xid) __field(u32, xid)
__field(u32, vers) __field(u32, vers)
__field(u32, proc) __field(u32, proc)
...@@ -1388,13 +1445,16 @@ DECLARE_EVENT_CLASS(svcrdma_badreq_event, ...@@ -1388,13 +1445,16 @@ DECLARE_EVENT_CLASS(svcrdma_badreq_event,
), ),
TP_fast_assign( TP_fast_assign(
__entry->cq_id = ctxt->rc_cid.ci_queue_id;
__entry->completion_id = ctxt->rc_cid.ci_completion_id;
__entry->xid = be32_to_cpup(p++); __entry->xid = be32_to_cpup(p++);
__entry->vers = be32_to_cpup(p++); __entry->vers = be32_to_cpup(p++);
__entry->credits = be32_to_cpup(p++); __entry->credits = be32_to_cpup(p++);
__entry->proc = be32_to_cpup(p); __entry->proc = be32_to_cpup(p);
), ),
TP_printk("xid=0x%08x vers=%u credits=%u proc=%u", TP_printk("cq.id=%u cid=%d xid=0x%08x vers=%u credits=%u proc=%u",
__entry->cq_id, __entry->completion_id,
__entry->xid, __entry->vers, __entry->credits, __entry->proc) __entry->xid, __entry->vers, __entry->credits, __entry->proc)
); );
...@@ -1402,9 +1462,10 @@ DECLARE_EVENT_CLASS(svcrdma_badreq_event, ...@@ -1402,9 +1462,10 @@ DECLARE_EVENT_CLASS(svcrdma_badreq_event,
DEFINE_EVENT(svcrdma_badreq_event, \ DEFINE_EVENT(svcrdma_badreq_event, \
svcrdma_decode_##name##_err, \ svcrdma_decode_##name##_err, \
TP_PROTO( \ TP_PROTO( \
const struct svc_rdma_recv_ctxt *ctxt, \
__be32 *p \ __be32 *p \
), \ ), \
TP_ARGS(p)) TP_ARGS(ctxt, p))
DEFINE_BADREQ_EVENT(badvers); DEFINE_BADREQ_EVENT(badvers);
DEFINE_BADREQ_EVENT(drop); DEFINE_BADREQ_EVENT(drop);
...@@ -1716,7 +1777,7 @@ TRACE_EVENT(svcrdma_send_pullup, ...@@ -1716,7 +1777,7 @@ TRACE_EVENT(svcrdma_send_pullup,
TP_printk("len=%u", __entry->len) TP_printk("len=%u", __entry->len)
); );
TRACE_EVENT(svcrdma_send_failed, TRACE_EVENT(svcrdma_send_err,
TP_PROTO( TP_PROTO(
const struct svc_rqst *rqst, const struct svc_rqst *rqst,
int status int status
...@@ -1727,167 +1788,127 @@ TRACE_EVENT(svcrdma_send_failed, ...@@ -1727,167 +1788,127 @@ TRACE_EVENT(svcrdma_send_failed,
TP_STRUCT__entry( TP_STRUCT__entry(
__field(int, status) __field(int, status)
__field(u32, xid) __field(u32, xid)
__field(const void *, xprt)
__string(addr, rqst->rq_xprt->xpt_remotebuf) __string(addr, rqst->rq_xprt->xpt_remotebuf)
), ),
TP_fast_assign( TP_fast_assign(
__entry->status = status; __entry->status = status;
__entry->xid = __be32_to_cpu(rqst->rq_xid); __entry->xid = __be32_to_cpu(rqst->rq_xid);
__entry->xprt = rqst->rq_xprt;
__assign_str(addr, rqst->rq_xprt->xpt_remotebuf); __assign_str(addr, rqst->rq_xprt->xpt_remotebuf);
), ),
TP_printk("xprt=%p addr=%s xid=0x%08x status=%d", TP_printk("addr=%s xid=0x%08x status=%d", __get_str(addr),
__entry->xprt, __get_str(addr),
__entry->xid, __entry->status __entry->xid, __entry->status
) )
); );
DECLARE_EVENT_CLASS(svcrdma_sendcomp_event,
TP_PROTO(
const struct ib_wc *wc
),
TP_ARGS(wc),
TP_STRUCT__entry(
__field(const void *, cqe)
__field(unsigned int, status)
__field(unsigned int, vendor_err)
),
TP_fast_assign(
__entry->cqe = wc->wr_cqe;
__entry->status = wc->status;
if (wc->status)
__entry->vendor_err = wc->vendor_err;
else
__entry->vendor_err = 0;
),
TP_printk("cqe=%p status=%s (%u/0x%x)",
__entry->cqe, rdma_show_wc_status(__entry->status),
__entry->status, __entry->vendor_err
)
);
#define DEFINE_SENDCOMP_EVENT(name) \
DEFINE_EVENT(svcrdma_sendcomp_event, svcrdma_wc_##name, \
TP_PROTO( \
const struct ib_wc *wc \
), \
TP_ARGS(wc))
TRACE_EVENT(svcrdma_post_send, TRACE_EVENT(svcrdma_post_send,
TP_PROTO( TP_PROTO(
const struct ib_send_wr *wr const struct svc_rdma_send_ctxt *ctxt
), ),
TP_ARGS(wr), TP_ARGS(ctxt),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, cqe) __field(u32, cq_id)
__field(int, completion_id)
__field(unsigned int, num_sge) __field(unsigned int, num_sge)
__field(u32, inv_rkey) __field(u32, inv_rkey)
), ),
TP_fast_assign( TP_fast_assign(
__entry->cqe = wr->wr_cqe; const struct ib_send_wr *wr = &ctxt->sc_send_wr;
__entry->cq_id = ctxt->sc_cid.ci_queue_id;
__entry->completion_id = ctxt->sc_cid.ci_completion_id;
__entry->num_sge = wr->num_sge; __entry->num_sge = wr->num_sge;
__entry->inv_rkey = (wr->opcode == IB_WR_SEND_WITH_INV) ? __entry->inv_rkey = (wr->opcode == IB_WR_SEND_WITH_INV) ?
wr->ex.invalidate_rkey : 0; wr->ex.invalidate_rkey : 0;
), ),
TP_printk("cqe=%p num_sge=%u inv_rkey=0x%08x", TP_printk("cq_id=%u cid=%d num_sge=%u inv_rkey=0x%08x",
__entry->cqe, __entry->num_sge, __entry->cq_id, __entry->completion_id,
__entry->inv_rkey __entry->num_sge, __entry->inv_rkey
) )
); );
DEFINE_SENDCOMP_EVENT(send); DEFINE_COMPLETION_EVENT(svcrdma_wc_send);
TRACE_EVENT(svcrdma_post_recv, TRACE_EVENT(svcrdma_post_recv,
TP_PROTO( TP_PROTO(
const struct ib_recv_wr *wr, const struct svc_rdma_recv_ctxt *ctxt
int status
), ),
TP_ARGS(wr, status), TP_ARGS(ctxt),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, cqe) __field(u32, cq_id)
__field(int, status) __field(int, completion_id)
), ),
TP_fast_assign( TP_fast_assign(
__entry->cqe = wr->wr_cqe; __entry->cq_id = ctxt->rc_cid.ci_queue_id;
__entry->status = status; __entry->completion_id = ctxt->rc_cid.ci_completion_id;
), ),
TP_printk("cqe=%p status=%d", TP_printk("cq.id=%d cid=%d",
__entry->cqe, __entry->status __entry->cq_id, __entry->completion_id
) )
); );
TRACE_EVENT(svcrdma_wc_receive, DEFINE_COMPLETION_EVENT(svcrdma_wc_receive);
TRACE_EVENT(svcrdma_rq_post_err,
TP_PROTO( TP_PROTO(
const struct ib_wc *wc const struct svcxprt_rdma *rdma,
int status
), ),
TP_ARGS(wc), TP_ARGS(rdma, status),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, cqe) __field(int, status)
__field(u32, byte_len) __string(addr, rdma->sc_xprt.xpt_remotebuf)
__field(unsigned int, status)
__field(u32, vendor_err)
), ),
TP_fast_assign( TP_fast_assign(
__entry->cqe = wc->wr_cqe; __entry->status = status;
__entry->status = wc->status; __assign_str(addr, rdma->sc_xprt.xpt_remotebuf);
if (wc->status) {
__entry->byte_len = 0;
__entry->vendor_err = wc->vendor_err;
} else {
__entry->byte_len = wc->byte_len;
__entry->vendor_err = 0;
}
), ),
TP_printk("cqe=%p byte_len=%u status=%s (%u/0x%x)", TP_printk("addr=%s status=%d",
__entry->cqe, __entry->byte_len, __get_str(addr), __entry->status
rdma_show_wc_status(__entry->status),
__entry->status, __entry->vendor_err
) )
); );
TRACE_EVENT(svcrdma_post_rw, TRACE_EVENT(svcrdma_post_chunk,
TP_PROTO( TP_PROTO(
const void *cqe, const struct rpc_rdma_cid *cid,
int sqecount int sqecount
), ),
TP_ARGS(cqe, sqecount), TP_ARGS(cid, sqecount),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, cqe) __field(u32, cq_id)
__field(int, completion_id)
__field(int, sqecount) __field(int, sqecount)
), ),
TP_fast_assign( TP_fast_assign(
__entry->cqe = cqe; __entry->cq_id = cid->ci_queue_id;
__entry->completion_id = cid->ci_completion_id;
__entry->sqecount = sqecount; __entry->sqecount = sqecount;
), ),
TP_printk("cqe=%p sqecount=%d", TP_printk("cq.id=%u cid=%d sqecount=%d",
__entry->cqe, __entry->sqecount __entry->cq_id, __entry->completion_id,
__entry->sqecount
) )
); );
DEFINE_SENDCOMP_EVENT(read); DEFINE_COMPLETION_EVENT(svcrdma_wc_read);
DEFINE_SENDCOMP_EVENT(write); DEFINE_COMPLETION_EVENT(svcrdma_wc_write);
TRACE_EVENT(svcrdma_qp_error, TRACE_EVENT(svcrdma_qp_error,
TP_PROTO( TP_PROTO(
......
...@@ -1250,15 +1250,34 @@ DECLARE_EVENT_CLASS(svc_xdr_buf_class, ...@@ -1250,15 +1250,34 @@ DECLARE_EVENT_CLASS(svc_xdr_buf_class,
DEFINE_SVCXDRBUF_EVENT(recvfrom); DEFINE_SVCXDRBUF_EVENT(recvfrom);
DEFINE_SVCXDRBUF_EVENT(sendto); DEFINE_SVCXDRBUF_EVENT(sendto);
/*
* from include/linux/sunrpc/svc.h
*/
#define SVC_RQST_FLAG_LIST \
svc_rqst_flag(SECURE) \
svc_rqst_flag(LOCAL) \
svc_rqst_flag(USEDEFERRAL) \
svc_rqst_flag(DROPME) \
svc_rqst_flag(SPLICE_OK) \
svc_rqst_flag(VICTIM) \
svc_rqst_flag(BUSY) \
svc_rqst_flag(DATA) \
svc_rqst_flag_end(AUTHERR)
#undef svc_rqst_flag
#undef svc_rqst_flag_end
#define svc_rqst_flag(x) TRACE_DEFINE_ENUM(RQ_##x);
#define svc_rqst_flag_end(x) TRACE_DEFINE_ENUM(RQ_##x);
SVC_RQST_FLAG_LIST
#undef svc_rqst_flag
#undef svc_rqst_flag_end
#define svc_rqst_flag(x) { BIT(RQ_##x), #x },
#define svc_rqst_flag_end(x) { BIT(RQ_##x), #x }
#define show_rqstp_flags(flags) \ #define show_rqstp_flags(flags) \
__print_flags(flags, "|", \ __print_flags(flags, "|", SVC_RQST_FLAG_LIST)
{ (1UL << RQ_SECURE), "RQ_SECURE"}, \
{ (1UL << RQ_LOCAL), "RQ_LOCAL"}, \
{ (1UL << RQ_USEDEFERRAL), "RQ_USEDEFERRAL"}, \
{ (1UL << RQ_DROPME), "RQ_DROPME"}, \
{ (1UL << RQ_SPLICE_OK), "RQ_SPLICE_OK"}, \
{ (1UL << RQ_VICTIM), "RQ_VICTIM"}, \
{ (1UL << RQ_BUSY), "RQ_BUSY"})
TRACE_EVENT(svc_recv, TRACE_EVENT(svc_recv,
TP_PROTO(struct svc_rqst *rqst, int len), TP_PROTO(struct svc_rqst *rqst, int len),
......
...@@ -33,6 +33,9 @@ ...@@ -33,6 +33,9 @@
#define NFS4_ACCESS_EXTEND 0x0008 #define NFS4_ACCESS_EXTEND 0x0008
#define NFS4_ACCESS_DELETE 0x0010 #define NFS4_ACCESS_DELETE 0x0010
#define NFS4_ACCESS_EXECUTE 0x0020 #define NFS4_ACCESS_EXECUTE 0x0020
#define NFS4_ACCESS_XAREAD 0x0040
#define NFS4_ACCESS_XAWRITE 0x0080
#define NFS4_ACCESS_XALIST 0x0100
#define NFS4_FH_PERSISTENT 0x0000 #define NFS4_FH_PERSISTENT 0x0000
#define NFS4_FH_NOEXPIRE_WITH_OPEN 0x0001 #define NFS4_FH_NOEXPIRE_WITH_OPEN 0x0001
......
...@@ -584,7 +584,7 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, int len, ...@@ -584,7 +584,7 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, int len,
buf->head[0].iov_len); buf->head[0].iov_len);
memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen); memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip; buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
buf->len = len - GSS_KRB5_TOK_HDR_LEN + headskip; buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
/* Trim off the trailing "extra count" and checksum blob */ /* Trim off the trailing "extra count" and checksum blob */
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip); xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
......
...@@ -332,7 +332,7 @@ static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct r ...@@ -332,7 +332,7 @@ static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct r
struct gss_svc_seq_data { struct gss_svc_seq_data {
/* highest seq number seen so far: */ /* highest seq number seen so far: */
int sd_max; u32 sd_max;
/* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of
* sd_win is nonzero iff sequence number i has been seen already: */ * sd_win is nonzero iff sequence number i has been seen already: */
unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG];
...@@ -613,16 +613,29 @@ gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) ...@@ -613,16 +613,29 @@ gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle)
return found; return found;
} }
/* Implements sequence number algorithm as specified in RFC 2203. */ /**
static int * gss_check_seq_num - GSS sequence number window check
gss_check_seq_num(struct rsc *rsci, int seq_num) * @rqstp: RPC Call to use when reporting errors
* @rsci: cached GSS context state (updated on return)
* @seq_num: sequence number to check
*
* Implements sequence number algorithm as specified in
* RFC 2203, Section 5.3.3.1. "Context Management".
*
* Return values:
* %true: @rqstp's GSS sequence number is inside the window
* %false: @rqstp's GSS sequence number is outside the window
*/
static bool gss_check_seq_num(const struct svc_rqst *rqstp, struct rsc *rsci,
u32 seq_num)
{ {
struct gss_svc_seq_data *sd = &rsci->seqdata; struct gss_svc_seq_data *sd = &rsci->seqdata;
bool result = false;
spin_lock(&sd->sd_lock); spin_lock(&sd->sd_lock);
if (seq_num > sd->sd_max) { if (seq_num > sd->sd_max) {
if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { if (seq_num >= sd->sd_max + GSS_SEQ_WIN) {
memset(sd->sd_win,0,sizeof(sd->sd_win)); memset(sd->sd_win, 0, sizeof(sd->sd_win));
sd->sd_max = seq_num; sd->sd_max = seq_num;
} else while (sd->sd_max < seq_num) { } else while (sd->sd_max < seq_num) {
sd->sd_max++; sd->sd_max++;
...@@ -631,17 +644,25 @@ gss_check_seq_num(struct rsc *rsci, int seq_num) ...@@ -631,17 +644,25 @@ gss_check_seq_num(struct rsc *rsci, int seq_num)
__set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win);
goto ok; goto ok;
} else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) { } else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) {
goto drop; goto toolow;
} }
/* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */
if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win))
goto drop; goto alreadyseen;
ok: ok:
result = true;
out:
spin_unlock(&sd->sd_lock); spin_unlock(&sd->sd_lock);
return 1; return result;
drop:
spin_unlock(&sd->sd_lock); toolow:
return 0; trace_rpcgss_svc_seqno_low(rqstp, seq_num,
sd->sd_max - GSS_SEQ_WIN,
sd->sd_max);
goto out;
alreadyseen:
trace_rpcgss_svc_seqno_seen(rqstp, seq_num);
goto out;
} }
static inline u32 round_up_to_quad(u32 i) static inline u32 round_up_to_quad(u32 i)
...@@ -721,14 +742,12 @@ gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, ...@@ -721,14 +742,12 @@ gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
} }
if (gc->gc_seq > MAXSEQ) { if (gc->gc_seq > MAXSEQ) {
trace_rpcgss_svc_large_seqno(rqstp->rq_xid, gc->gc_seq); trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq);
*authp = rpcsec_gsserr_ctxproblem; *authp = rpcsec_gsserr_ctxproblem;
return SVC_DENIED; return SVC_DENIED;
} }
if (!gss_check_seq_num(rsci, gc->gc_seq)) { if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq))
trace_rpcgss_svc_old_seqno(rqstp->rq_xid, gc->gc_seq);
return SVC_DROP; return SVC_DROP;
}
return SVC_OK; return SVC_OK;
} }
...@@ -866,11 +885,13 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) ...@@ -866,11 +885,13 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
static int static int
unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
{ {
u32 integ_len, rseqno, maj_stat;
int stat = -EINVAL; int stat = -EINVAL;
u32 integ_len, maj_stat;
struct xdr_netobj mic; struct xdr_netobj mic;
struct xdr_buf integ_buf; struct xdr_buf integ_buf;
mic.data = NULL;
/* NFS READ normally uses splice to send data in-place. However /* NFS READ normally uses splice to send data in-place. However
* the data in cache can change after the reply's MIC is computed * the data in cache can change after the reply's MIC is computed
* but before the RPC reply is sent. To prevent the client from * but before the RPC reply is sent. To prevent the client from
...@@ -885,34 +906,44 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g ...@@ -885,34 +906,44 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
integ_len = svc_getnl(&buf->head[0]); integ_len = svc_getnl(&buf->head[0]);
if (integ_len & 3) if (integ_len & 3)
return stat; goto unwrap_failed;
if (integ_len > buf->len) if (integ_len > buf->len)
return stat; goto unwrap_failed;
if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) { if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
WARN_ON_ONCE(1); goto unwrap_failed;
return stat;
}
/* copy out mic... */ /* copy out mic... */
if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
return stat; goto unwrap_failed;
if (mic.len > RPC_MAX_AUTH_SIZE) if (mic.len > RPC_MAX_AUTH_SIZE)
return stat; goto unwrap_failed;
mic.data = kmalloc(mic.len, GFP_KERNEL); mic.data = kmalloc(mic.len, GFP_KERNEL);
if (!mic.data) if (!mic.data)
return stat; goto unwrap_failed;
if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
goto out; goto unwrap_failed;
maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
if (maj_stat != GSS_S_COMPLETE) if (maj_stat != GSS_S_COMPLETE)
goto out; goto bad_mic;
if (svc_getnl(&buf->head[0]) != seq) rseqno = svc_getnl(&buf->head[0]);
goto out; if (rseqno != seq)
goto bad_seqno;
/* trim off the mic and padding at the end before returning */ /* trim off the mic and padding at the end before returning */
xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4); xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
stat = 0; stat = 0;
out: out:
kfree(mic.data); kfree(mic.data);
return stat; return stat;
unwrap_failed:
trace_rpcgss_svc_unwrap_failed(rqstp);
goto out;
bad_seqno:
trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
goto out;
bad_mic:
trace_rpcgss_svc_mic(rqstp, maj_stat);
goto out;
} }
static inline int static inline int
...@@ -937,6 +968,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs ...@@ -937,6 +968,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
{ {
u32 priv_len, maj_stat; u32 priv_len, maj_stat;
int pad, remaining_len, offset; int pad, remaining_len, offset;
u32 rseqno;
clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
...@@ -951,14 +983,13 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs ...@@ -951,14 +983,13 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
* not yet read from the head, so these two values are different: */ * not yet read from the head, so these two values are different: */
remaining_len = total_buf_len(buf); remaining_len = total_buf_len(buf);
if (priv_len > remaining_len) if (priv_len > remaining_len)
return -EINVAL; goto unwrap_failed;
pad = remaining_len - priv_len; pad = remaining_len - priv_len;
buf->len -= pad; buf->len -= pad;
fix_priv_head(buf, pad); fix_priv_head(buf, pad);
maj_stat = gss_unwrap(ctx, 0, priv_len, buf); maj_stat = gss_unwrap(ctx, 0, priv_len, buf);
pad = priv_len - buf->len; pad = priv_len - buf->len;
buf->len -= pad;
/* The upper layers assume the buffer is aligned on 4-byte boundaries. /* The upper layers assume the buffer is aligned on 4-byte boundaries.
* In the krb5p case, at least, the data ends up offset, so we need to * In the krb5p case, at least, the data ends up offset, so we need to
* move it around. */ * move it around. */
...@@ -972,11 +1003,22 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs ...@@ -972,11 +1003,22 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
fix_priv_head(buf, pad); fix_priv_head(buf, pad);
} }
if (maj_stat != GSS_S_COMPLETE) if (maj_stat != GSS_S_COMPLETE)
return -EINVAL; goto bad_unwrap;
out_seq: out_seq:
if (svc_getnl(&buf->head[0]) != seq) rseqno = svc_getnl(&buf->head[0]);
return -EINVAL; if (rseqno != seq)
goto bad_seqno;
return 0; return 0;
unwrap_failed:
trace_rpcgss_svc_unwrap_failed(rqstp);
return -EINVAL;
bad_seqno:
trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
return -EINVAL;
bad_unwrap:
trace_rpcgss_svc_unwrap(rqstp, maj_stat);
return -EINVAL;
} }
struct gss_svc_data { struct gss_svc_data {
...@@ -1314,8 +1356,7 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, ...@@ -1314,8 +1356,7 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
if (status) if (status)
goto out; goto out;
trace_rpcgss_svc_accept_upcall(rqstp->rq_xid, ud.major_status, trace_rpcgss_svc_accept_upcall(rqstp, ud.major_status, ud.minor_status);
ud.minor_status);
switch (ud.major_status) { switch (ud.major_status) {
case GSS_S_CONTINUE_NEEDED: case GSS_S_CONTINUE_NEEDED:
...@@ -1490,8 +1531,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -1490,8 +1531,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
int ret; int ret;
struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
trace_rpcgss_svc_accept(rqstp->rq_xid, argv->iov_len);
*authp = rpc_autherr_badcred; *authp = rpc_autherr_badcred;
if (!svcdata) if (!svcdata)
svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
...@@ -1608,6 +1647,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -1608,6 +1647,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
GSS_C_QOP_DEFAULT, GSS_C_QOP_DEFAULT,
gc->gc_svc); gc->gc_svc);
ret = SVC_OK; ret = SVC_OK;
trace_rpcgss_svc_authenticate(rqstp, gc);
goto out; goto out;
} }
garbage_args: garbage_args:
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/sched.h> #include <linux/sunrpc/sched.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/gss_err.h> #include <linux/sunrpc/gss_err.h>
#include <linux/sunrpc/auth_gss.h> #include <linux/sunrpc/auth_gss.h>
......
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
* New MRs are created on demand. * New MRs are created on demand.
*/ */
#include <linux/sunrpc/rpc_rdma.h>
#include <linux/sunrpc/svc_rdma.h> #include <linux/sunrpc/svc_rdma.h>
#include "xprt_rdma.h" #include "xprt_rdma.h"
......
...@@ -275,14 +275,6 @@ rpcrdma_convert_iovs(struct rpcrdma_xprt *r_xprt, struct xdr_buf *xdrbuf, ...@@ -275,14 +275,6 @@ rpcrdma_convert_iovs(struct rpcrdma_xprt *r_xprt, struct xdr_buf *xdrbuf,
return n; return n;
} }
static void
xdr_encode_rdma_segment(__be32 *iptr, struct rpcrdma_mr *mr)
{
*iptr++ = cpu_to_be32(mr->mr_handle);
*iptr++ = cpu_to_be32(mr->mr_length);
xdr_encode_hyper(iptr, mr->mr_offset);
}
static int static int
encode_rdma_segment(struct xdr_stream *xdr, struct rpcrdma_mr *mr) encode_rdma_segment(struct xdr_stream *xdr, struct rpcrdma_mr *mr)
{ {
...@@ -292,7 +284,7 @@ encode_rdma_segment(struct xdr_stream *xdr, struct rpcrdma_mr *mr) ...@@ -292,7 +284,7 @@ encode_rdma_segment(struct xdr_stream *xdr, struct rpcrdma_mr *mr)
if (unlikely(!p)) if (unlikely(!p))
return -EMSGSIZE; return -EMSGSIZE;
xdr_encode_rdma_segment(p, mr); xdr_encode_rdma_segment(p, mr->mr_handle, mr->mr_length, mr->mr_offset);
return 0; return 0;
} }
...@@ -307,8 +299,8 @@ encode_read_segment(struct xdr_stream *xdr, struct rpcrdma_mr *mr, ...@@ -307,8 +299,8 @@ encode_read_segment(struct xdr_stream *xdr, struct rpcrdma_mr *mr,
return -EMSGSIZE; return -EMSGSIZE;
*p++ = xdr_one; /* Item present */ *p++ = xdr_one; /* Item present */
*p++ = cpu_to_be32(position); xdr_encode_read_segment(p, position, mr->mr_handle, mr->mr_length,
xdr_encode_rdma_segment(p, mr); mr->mr_offset);
return 0; return 0;
} }
...@@ -1133,11 +1125,11 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep) ...@@ -1133,11 +1125,11 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep)
p = xdr_inline_decode(xdr, 0); p = xdr_inline_decode(xdr, 0);
/* Chunk lists */ /* Chunk lists */
if (*p++ != xdr_zero) if (xdr_item_is_present(p++))
return false; return false;
if (*p++ != xdr_zero) if (xdr_item_is_present(p++))
return false; return false;
if (*p++ != xdr_zero) if (xdr_item_is_present(p++))
return false; return false;
/* RPC header */ /* RPC header */
...@@ -1176,10 +1168,7 @@ static int decode_rdma_segment(struct xdr_stream *xdr, u32 *length) ...@@ -1176,10 +1168,7 @@ static int decode_rdma_segment(struct xdr_stream *xdr, u32 *length)
if (unlikely(!p)) if (unlikely(!p))
return -EIO; return -EIO;
handle = be32_to_cpup(p++); xdr_decode_rdma_segment(p, &handle, length, &offset);
*length = be32_to_cpup(p++);
xdr_decode_hyper(p, &offset);
trace_xprtrdma_decode_seg(handle, *length, offset); trace_xprtrdma_decode_seg(handle, *length, offset);
return 0; return 0;
} }
...@@ -1215,7 +1204,7 @@ static int decode_read_list(struct xdr_stream *xdr) ...@@ -1215,7 +1204,7 @@ static int decode_read_list(struct xdr_stream *xdr)
p = xdr_inline_decode(xdr, sizeof(*p)); p = xdr_inline_decode(xdr, sizeof(*p));
if (unlikely(!p)) if (unlikely(!p))
return -EIO; return -EIO;
if (unlikely(*p != xdr_zero)) if (unlikely(xdr_item_is_present(p)))
return -EIO; return -EIO;
return 0; return 0;
} }
...@@ -1234,7 +1223,7 @@ static int decode_write_list(struct xdr_stream *xdr, u32 *length) ...@@ -1234,7 +1223,7 @@ static int decode_write_list(struct xdr_stream *xdr, u32 *length)
p = xdr_inline_decode(xdr, sizeof(*p)); p = xdr_inline_decode(xdr, sizeof(*p));
if (unlikely(!p)) if (unlikely(!p))
return -EIO; return -EIO;
if (*p == xdr_zero) if (xdr_item_is_absent(p))
break; break;
if (!first) if (!first)
return -EIO; return -EIO;
...@@ -1256,7 +1245,7 @@ static int decode_reply_chunk(struct xdr_stream *xdr, u32 *length) ...@@ -1256,7 +1245,7 @@ static int decode_reply_chunk(struct xdr_stream *xdr, u32 *length)
return -EIO; return -EIO;
*length = 0; *length = 0;
if (*p != xdr_zero) if (xdr_item_is_present(p))
if (decode_write_chunk(xdr, length)) if (decode_write_chunk(xdr, length))
return -EIO; return -EIO;
return 0; return 0;
......
...@@ -87,7 +87,7 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma, ...@@ -87,7 +87,7 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma,
*/ */
get_page(virt_to_page(rqst->rq_buffer)); get_page(virt_to_page(rqst->rq_buffer));
ctxt->sc_send_wr.opcode = IB_WR_SEND; ctxt->sc_send_wr.opcode = IB_WR_SEND;
return svc_rdma_send(rdma, &ctxt->sc_send_wr); return svc_rdma_send(rdma, ctxt);
} }
/* Server-side transport endpoint wants a whole page for its send /* Server-side transport endpoint wants a whole page for its send
......
...@@ -117,6 +117,13 @@ svc_rdma_next_recv_ctxt(struct list_head *list) ...@@ -117,6 +117,13 @@ svc_rdma_next_recv_ctxt(struct list_head *list)
rc_list); rc_list);
} }
static void svc_rdma_recv_cid_init(struct svcxprt_rdma *rdma,
struct rpc_rdma_cid *cid)
{
cid->ci_queue_id = rdma->sc_rq_cq->res.id;
cid->ci_completion_id = atomic_inc_return(&rdma->sc_completion_ids);
}
static struct svc_rdma_recv_ctxt * static struct svc_rdma_recv_ctxt *
svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma) svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma)
{ {
...@@ -135,6 +142,8 @@ svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma) ...@@ -135,6 +142,8 @@ svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma)
if (ib_dma_mapping_error(rdma->sc_pd->device, addr)) if (ib_dma_mapping_error(rdma->sc_pd->device, addr))
goto fail2; goto fail2;
svc_rdma_recv_cid_init(rdma, &ctxt->rc_cid);
ctxt->rc_recv_wr.next = NULL; ctxt->rc_recv_wr.next = NULL;
ctxt->rc_recv_wr.wr_cqe = &ctxt->rc_cqe; ctxt->rc_recv_wr.wr_cqe = &ctxt->rc_cqe;
ctxt->rc_recv_wr.sg_list = &ctxt->rc_recv_sge; ctxt->rc_recv_wr.sg_list = &ctxt->rc_recv_sge;
...@@ -248,16 +257,15 @@ static int __svc_rdma_post_recv(struct svcxprt_rdma *rdma, ...@@ -248,16 +257,15 @@ static int __svc_rdma_post_recv(struct svcxprt_rdma *rdma,
{ {
int ret; int ret;
svc_xprt_get(&rdma->sc_xprt); trace_svcrdma_post_recv(ctxt);
ret = ib_post_recv(rdma->sc_qp, &ctxt->rc_recv_wr, NULL); ret = ib_post_recv(rdma->sc_qp, &ctxt->rc_recv_wr, NULL);
trace_svcrdma_post_recv(&ctxt->rc_recv_wr, ret);
if (ret) if (ret)
goto err_post; goto err_post;
return 0; return 0;
err_post: err_post:
trace_svcrdma_rq_post_err(rdma, ret);
svc_rdma_recv_ctxt_put(rdma, ctxt); svc_rdma_recv_ctxt_put(rdma, ctxt);
svc_xprt_put(&rdma->sc_xprt);
return ret; return ret;
} }
...@@ -265,6 +273,8 @@ static int svc_rdma_post_recv(struct svcxprt_rdma *rdma) ...@@ -265,6 +273,8 @@ static int svc_rdma_post_recv(struct svcxprt_rdma *rdma)
{ {
struct svc_rdma_recv_ctxt *ctxt; struct svc_rdma_recv_ctxt *ctxt;
if (test_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags))
return 0;
ctxt = svc_rdma_recv_ctxt_get(rdma); ctxt = svc_rdma_recv_ctxt_get(rdma);
if (!ctxt) if (!ctxt)
return -ENOMEM; return -ENOMEM;
...@@ -309,11 +319,10 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) ...@@ -309,11 +319,10 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
struct ib_cqe *cqe = wc->wr_cqe; struct ib_cqe *cqe = wc->wr_cqe;
struct svc_rdma_recv_ctxt *ctxt; struct svc_rdma_recv_ctxt *ctxt;
trace_svcrdma_wc_receive(wc);
/* WARNING: Only wc->wr_cqe and wc->status are reliable */ /* WARNING: Only wc->wr_cqe and wc->status are reliable */
ctxt = container_of(cqe, struct svc_rdma_recv_ctxt, rc_cqe); ctxt = container_of(cqe, struct svc_rdma_recv_ctxt, rc_cqe);
trace_svcrdma_wc_receive(wc, &ctxt->rc_cid);
if (wc->status != IB_WC_SUCCESS) if (wc->status != IB_WC_SUCCESS)
goto flushed; goto flushed;
...@@ -333,15 +342,13 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) ...@@ -333,15 +342,13 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
spin_unlock(&rdma->sc_rq_dto_lock); spin_unlock(&rdma->sc_rq_dto_lock);
if (!test_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags)) if (!test_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags))
svc_xprt_enqueue(&rdma->sc_xprt); svc_xprt_enqueue(&rdma->sc_xprt);
goto out; return;
flushed: flushed:
post_err: post_err:
svc_rdma_recv_ctxt_put(rdma, ctxt); svc_rdma_recv_ctxt_put(rdma, ctxt);
set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
svc_xprt_enqueue(&rdma->sc_xprt); svc_xprt_enqueue(&rdma->sc_xprt);
out:
svc_xprt_put(&rdma->sc_xprt);
} }
/** /**
...@@ -419,7 +426,7 @@ static bool xdr_check_read_list(struct svc_rdma_recv_ctxt *rctxt) ...@@ -419,7 +426,7 @@ static bool xdr_check_read_list(struct svc_rdma_recv_ctxt *rctxt)
len = 0; len = 0;
first = true; first = true;
while (*p != xdr_zero) { while (xdr_item_is_present(p)) {
p = xdr_inline_decode(&rctxt->rc_stream, p = xdr_inline_decode(&rctxt->rc_stream,
rpcrdma_readseg_maxsz * sizeof(*p)); rpcrdma_readseg_maxsz * sizeof(*p));
if (!p) if (!p)
...@@ -466,9 +473,7 @@ static bool xdr_check_write_chunk(struct svc_rdma_recv_ctxt *rctxt, u32 maxlen) ...@@ -466,9 +473,7 @@ static bool xdr_check_write_chunk(struct svc_rdma_recv_ctxt *rctxt, u32 maxlen)
if (!p) if (!p)
return false; return false;
handle = be32_to_cpup(p++); xdr_decode_rdma_segment(p, &handle, &length, &offset);
length = be32_to_cpup(p++);
xdr_decode_hyper(p, &offset);
trace_svcrdma_decode_wseg(handle, length, offset); trace_svcrdma_decode_wseg(handle, length, offset);
total += length; total += length;
...@@ -500,7 +505,7 @@ static bool xdr_check_write_list(struct svc_rdma_recv_ctxt *rctxt) ...@@ -500,7 +505,7 @@ static bool xdr_check_write_list(struct svc_rdma_recv_ctxt *rctxt)
if (!p) if (!p)
return false; return false;
rctxt->rc_write_list = p; rctxt->rc_write_list = p;
while (*p != xdr_zero) { while (xdr_item_is_present(p)) {
if (!xdr_check_write_chunk(rctxt, MAX_BYTES_WRITE_CHUNK)) if (!xdr_check_write_chunk(rctxt, MAX_BYTES_WRITE_CHUNK))
return false; return false;
++chcount; ++chcount;
...@@ -532,12 +537,11 @@ static bool xdr_check_reply_chunk(struct svc_rdma_recv_ctxt *rctxt) ...@@ -532,12 +537,11 @@ static bool xdr_check_reply_chunk(struct svc_rdma_recv_ctxt *rctxt)
p = xdr_inline_decode(&rctxt->rc_stream, sizeof(*p)); p = xdr_inline_decode(&rctxt->rc_stream, sizeof(*p));
if (!p) if (!p)
return false; return false;
rctxt->rc_reply_chunk = p; rctxt->rc_reply_chunk = NULL;
if (*p != xdr_zero) { if (xdr_item_is_present(p)) {
if (!xdr_check_write_chunk(rctxt, MAX_BYTES_SPECIAL_CHUNK)) if (!xdr_check_write_chunk(rctxt, MAX_BYTES_SPECIAL_CHUNK))
return false; return false;
} else { rctxt->rc_reply_chunk = p;
rctxt->rc_reply_chunk = NULL;
} }
return true; return true;
} }
...@@ -568,7 +572,7 @@ static void svc_rdma_get_inv_rkey(struct svcxprt_rdma *rdma, ...@@ -568,7 +572,7 @@ static void svc_rdma_get_inv_rkey(struct svcxprt_rdma *rdma,
p += rpcrdma_fixed_maxsz; p += rpcrdma_fixed_maxsz;
/* Read list */ /* Read list */
while (*p++ != xdr_zero) { while (xdr_item_is_present(p++)) {
p++; /* position */ p++; /* position */
if (inv_rkey == xdr_zero) if (inv_rkey == xdr_zero)
inv_rkey = *p; inv_rkey = *p;
...@@ -578,7 +582,7 @@ static void svc_rdma_get_inv_rkey(struct svcxprt_rdma *rdma, ...@@ -578,7 +582,7 @@ static void svc_rdma_get_inv_rkey(struct svcxprt_rdma *rdma,
} }
/* Write list */ /* Write list */
while (*p++ != xdr_zero) { while (xdr_item_is_present(p++)) {
segcount = be32_to_cpup(p++); segcount = be32_to_cpup(p++);
for (i = 0; i < segcount; i++) { for (i = 0; i < segcount; i++) {
if (inv_rkey == xdr_zero) if (inv_rkey == xdr_zero)
...@@ -590,7 +594,7 @@ static void svc_rdma_get_inv_rkey(struct svcxprt_rdma *rdma, ...@@ -590,7 +594,7 @@ static void svc_rdma_get_inv_rkey(struct svcxprt_rdma *rdma,
} }
/* Reply chunk */ /* Reply chunk */
if (*p++ != xdr_zero) { if (xdr_item_is_present(p++)) {
segcount = be32_to_cpup(p++); segcount = be32_to_cpup(p++);
for (i = 0; i < segcount; i++) { for (i = 0; i < segcount; i++) {
if (inv_rkey == xdr_zero) if (inv_rkey == xdr_zero)
...@@ -661,27 +665,27 @@ static int svc_rdma_xdr_decode_req(struct xdr_buf *rq_arg, ...@@ -661,27 +665,27 @@ static int svc_rdma_xdr_decode_req(struct xdr_buf *rq_arg,
hdr_len = xdr_stream_pos(&rctxt->rc_stream); hdr_len = xdr_stream_pos(&rctxt->rc_stream);
rq_arg->head[0].iov_len -= hdr_len; rq_arg->head[0].iov_len -= hdr_len;
rq_arg->len -= hdr_len; rq_arg->len -= hdr_len;
trace_svcrdma_decode_rqst(rdma_argp, hdr_len); trace_svcrdma_decode_rqst(rctxt, rdma_argp, hdr_len);
return hdr_len; return hdr_len;
out_short: out_short:
trace_svcrdma_decode_short_err(rq_arg->len); trace_svcrdma_decode_short_err(rctxt, rq_arg->len);
return -EINVAL; return -EINVAL;
out_version: out_version:
trace_svcrdma_decode_badvers_err(rdma_argp); trace_svcrdma_decode_badvers_err(rctxt, rdma_argp);
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
out_drop: out_drop:
trace_svcrdma_decode_drop_err(rdma_argp); trace_svcrdma_decode_drop_err(rctxt, rdma_argp);
return 0; return 0;
out_proc: out_proc:
trace_svcrdma_decode_badproc_err(rdma_argp); trace_svcrdma_decode_badproc_err(rctxt, rdma_argp);
return -EINVAL; return -EINVAL;
out_inval: out_inval:
trace_svcrdma_decode_parse_err(rdma_argp); trace_svcrdma_decode_parse_err(rctxt, rdma_argp);
return -EINVAL; return -EINVAL;
} }
...@@ -714,57 +718,16 @@ static void rdma_read_complete(struct svc_rqst *rqstp, ...@@ -714,57 +718,16 @@ static void rdma_read_complete(struct svc_rqst *rqstp,
rqstp->rq_arg.buflen = head->rc_arg.buflen; rqstp->rq_arg.buflen = head->rc_arg.buflen;
} }
static void svc_rdma_send_error(struct svcxprt_rdma *xprt, static void svc_rdma_send_error(struct svcxprt_rdma *rdma,
__be32 *rdma_argp, int status) struct svc_rdma_recv_ctxt *rctxt,
int status)
{ {
struct svc_rdma_send_ctxt *ctxt; struct svc_rdma_send_ctxt *sctxt;
__be32 *p;
int ret;
ctxt = svc_rdma_send_ctxt_get(xprt); sctxt = svc_rdma_send_ctxt_get(rdma);
if (!ctxt) if (!sctxt)
return; return;
svc_rdma_send_error_msg(rdma, sctxt, rctxt, status);
p = xdr_reserve_space(&ctxt->sc_stream,
rpcrdma_fixed_maxsz * sizeof(*p));
if (!p)
goto put_ctxt;
*p++ = *rdma_argp;
*p++ = *(rdma_argp + 1);
*p++ = xprt->sc_fc_credits;
*p = rdma_error;
switch (status) {
case -EPROTONOSUPPORT:
p = xdr_reserve_space(&ctxt->sc_stream, 3 * sizeof(*p));
if (!p)
goto put_ctxt;
*p++ = err_vers;
*p++ = rpcrdma_version;
*p = rpcrdma_version;
trace_svcrdma_err_vers(*rdma_argp);
break;
default:
p = xdr_reserve_space(&ctxt->sc_stream, sizeof(*p));
if (!p)
goto put_ctxt;
*p = err_chunk;
trace_svcrdma_err_chunk(*rdma_argp);
}
ctxt->sc_send_wr.num_sge = 1;
ctxt->sc_send_wr.opcode = IB_WR_SEND;
ctxt->sc_sges[0].length = ctxt->sc_hdrbuf.len;
ret = svc_rdma_send(xprt, &ctxt->sc_send_wr);
if (ret)
goto put_ctxt;
return;
put_ctxt:
svc_rdma_send_ctxt_put(xprt, ctxt);
} }
/* By convention, backchannel calls arrive via rdma_msg type /* By convention, backchannel calls arrive via rdma_msg type
...@@ -900,13 +863,13 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) ...@@ -900,13 +863,13 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
return 0; return 0;
out_err: out_err:
svc_rdma_send_error(rdma_xprt, p, ret); svc_rdma_send_error(rdma_xprt, ctxt, ret);
svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); svc_rdma_recv_ctxt_put(rdma_xprt, ctxt);
return 0; return 0;
out_postfail: out_postfail:
if (ret == -EINVAL) if (ret == -EINVAL)
svc_rdma_send_error(rdma_xprt, p, ret); svc_rdma_send_error(rdma_xprt, ctxt, ret);
svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); svc_rdma_recv_ctxt_put(rdma_xprt, ctxt);
return ret; return ret;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <rdma/rw.h> #include <rdma/rw.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/rpc_rdma.h> #include <linux/sunrpc/rpc_rdma.h>
#include <linux/sunrpc/svc_rdma.h> #include <linux/sunrpc/svc_rdma.h>
...@@ -144,17 +145,25 @@ static int svc_rdma_rw_ctx_init(struct svcxprt_rdma *rdma, ...@@ -144,17 +145,25 @@ static int svc_rdma_rw_ctx_init(struct svcxprt_rdma *rdma,
* demand, and not cached. * demand, and not cached.
*/ */
struct svc_rdma_chunk_ctxt { struct svc_rdma_chunk_ctxt {
struct rpc_rdma_cid cc_cid;
struct ib_cqe cc_cqe; struct ib_cqe cc_cqe;
struct svcxprt_rdma *cc_rdma; struct svcxprt_rdma *cc_rdma;
struct list_head cc_rwctxts; struct list_head cc_rwctxts;
int cc_sqecount; int cc_sqecount;
}; };
static void svc_rdma_cc_cid_init(struct svcxprt_rdma *rdma,
struct rpc_rdma_cid *cid)
{
cid->ci_queue_id = rdma->sc_sq_cq->res.id;
cid->ci_completion_id = atomic_inc_return(&rdma->sc_completion_ids);
}
static void svc_rdma_cc_init(struct svcxprt_rdma *rdma, static void svc_rdma_cc_init(struct svcxprt_rdma *rdma,
struct svc_rdma_chunk_ctxt *cc) struct svc_rdma_chunk_ctxt *cc)
{ {
svc_rdma_cc_cid_init(rdma, &cc->cc_cid);
cc->cc_rdma = rdma; cc->cc_rdma = rdma;
svc_xprt_get(&rdma->sc_xprt);
INIT_LIST_HEAD(&cc->cc_rwctxts); INIT_LIST_HEAD(&cc->cc_rwctxts);
cc->cc_sqecount = 0; cc->cc_sqecount = 0;
...@@ -174,7 +183,6 @@ static void svc_rdma_cc_release(struct svc_rdma_chunk_ctxt *cc, ...@@ -174,7 +183,6 @@ static void svc_rdma_cc_release(struct svc_rdma_chunk_ctxt *cc,
ctxt->rw_nents, dir); ctxt->rw_nents, dir);
svc_rdma_put_rw_ctxt(rdma, ctxt); svc_rdma_put_rw_ctxt(rdma, ctxt);
} }
svc_xprt_put(&rdma->sc_xprt);
} }
/* State for sending a Write or Reply chunk. /* State for sending a Write or Reply chunk.
...@@ -236,7 +244,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) ...@@ -236,7 +244,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc)
struct svc_rdma_write_info *info = struct svc_rdma_write_info *info =
container_of(cc, struct svc_rdma_write_info, wi_cc); container_of(cc, struct svc_rdma_write_info, wi_cc);
trace_svcrdma_wc_write(wc); trace_svcrdma_wc_write(wc, &cc->cc_cid);
atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail);
wake_up(&rdma->sc_send_wait); wake_up(&rdma->sc_send_wait);
...@@ -294,7 +302,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) ...@@ -294,7 +302,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc)
struct svc_rdma_read_info *info = struct svc_rdma_read_info *info =
container_of(cc, struct svc_rdma_read_info, ri_cc); container_of(cc, struct svc_rdma_read_info, ri_cc);
trace_svcrdma_wc_read(wc); trace_svcrdma_wc_read(wc, &cc->cc_cid);
atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail);
wake_up(&rdma->sc_send_wait); wake_up(&rdma->sc_send_wait);
...@@ -350,6 +358,7 @@ static int svc_rdma_post_chunk_ctxt(struct svc_rdma_chunk_ctxt *cc) ...@@ -350,6 +358,7 @@ static int svc_rdma_post_chunk_ctxt(struct svc_rdma_chunk_ctxt *cc)
do { do {
if (atomic_sub_return(cc->cc_sqecount, if (atomic_sub_return(cc->cc_sqecount,
&rdma->sc_sq_avail) > 0) { &rdma->sc_sq_avail) > 0) {
trace_svcrdma_post_chunk(&cc->cc_cid, cc->cc_sqecount);
ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr); ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr);
if (ret) if (ret)
break; break;
...@@ -441,34 +450,32 @@ svc_rdma_build_writes(struct svc_rdma_write_info *info, ...@@ -441,34 +450,32 @@ svc_rdma_build_writes(struct svc_rdma_write_info *info,
seg = info->wi_segs + info->wi_seg_no * rpcrdma_segment_maxsz; seg = info->wi_segs + info->wi_seg_no * rpcrdma_segment_maxsz;
do { do {
unsigned int write_len; unsigned int write_len;
u32 seg_length, seg_handle; u32 handle, length;
u64 seg_offset; u64 offset;
if (info->wi_seg_no >= info->wi_nsegs) if (info->wi_seg_no >= info->wi_nsegs)
goto out_overflow; goto out_overflow;
seg_handle = be32_to_cpup(seg); xdr_decode_rdma_segment(seg, &handle, &length, &offset);
seg_length = be32_to_cpup(seg + 1); offset += info->wi_seg_off;
xdr_decode_hyper(seg + 2, &seg_offset);
seg_offset += info->wi_seg_off;
write_len = min(remaining, seg_length - info->wi_seg_off); write_len = min(remaining, length - info->wi_seg_off);
ctxt = svc_rdma_get_rw_ctxt(rdma, ctxt = svc_rdma_get_rw_ctxt(rdma,
(write_len >> PAGE_SHIFT) + 2); (write_len >> PAGE_SHIFT) + 2);
if (!ctxt) if (!ctxt)
return -ENOMEM; return -ENOMEM;
constructor(info, write_len, ctxt); constructor(info, write_len, ctxt);
ret = svc_rdma_rw_ctx_init(rdma, ctxt, seg_offset, seg_handle, ret = svc_rdma_rw_ctx_init(rdma, ctxt, offset, handle,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (ret < 0) if (ret < 0)
return -EIO; return -EIO;
trace_svcrdma_send_wseg(seg_handle, write_len, seg_offset); trace_svcrdma_send_wseg(handle, write_len, offset);
list_add(&ctxt->rw_list, &cc->cc_rwctxts); list_add(&ctxt->rw_list, &cc->cc_rwctxts);
cc->cc_sqecount += ret; cc->cc_sqecount += ret;
if (write_len == seg_length - info->wi_seg_off) { if (write_len == length - info->wi_seg_off) {
seg += 4; seg += 4;
info->wi_seg_no++; info->wi_seg_no++;
info->wi_seg_off = 0; info->wi_seg_off = 0;
...@@ -684,35 +691,24 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp, ...@@ -684,35 +691,24 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp,
struct svc_rdma_read_info *info, struct svc_rdma_read_info *info,
__be32 *p) __be32 *p)
{ {
unsigned int i;
int ret; int ret;
ret = -EINVAL; ret = -EINVAL;
info->ri_chunklen = 0; info->ri_chunklen = 0;
while (*p++ != xdr_zero && be32_to_cpup(p++) == info->ri_position) { while (*p++ != xdr_zero && be32_to_cpup(p++) == info->ri_position) {
u32 rs_handle, rs_length; u32 handle, length;
u64 rs_offset; u64 offset;
rs_handle = be32_to_cpup(p++);
rs_length = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &rs_offset);
ret = svc_rdma_build_read_segment(info, rqstp, p = xdr_decode_rdma_segment(p, &handle, &length, &offset);
rs_handle, rs_length, ret = svc_rdma_build_read_segment(info, rqstp, handle, length,
rs_offset); offset);
if (ret < 0) if (ret < 0)
break; break;
trace_svcrdma_send_rseg(rs_handle, rs_length, rs_offset); trace_svcrdma_send_rseg(handle, length, offset);
info->ri_chunklen += rs_length; info->ri_chunklen += length;
} }
/* Pages under I/O have been copied to head->rc_pages.
* Prevent their premature release by svc_xprt_release() .
*/
for (i = 0; i < info->ri_readctxt->rc_page_count; i++)
rqstp->rq_pages[i] = NULL;
return ret; return ret;
} }
...@@ -807,6 +803,26 @@ static int svc_rdma_build_pz_read_chunk(struct svc_rqst *rqstp, ...@@ -807,6 +803,26 @@ static int svc_rdma_build_pz_read_chunk(struct svc_rqst *rqstp,
return ret; return ret;
} }
/* Pages under I/O have been copied to head->rc_pages. Ensure they
* are not released by svc_xprt_release() until the I/O is complete.
*
* This has to be done after all Read WRs are constructed to properly
* handle a page that is part of I/O on behalf of two different RDMA
* segments.
*
* Do this only if I/O has been posted. Otherwise, we do indeed want
* svc_xprt_release() to clean things up properly.
*/
static void svc_rdma_save_io_pages(struct svc_rqst *rqstp,
const unsigned int start,
const unsigned int num_pages)
{
unsigned int i;
for (i = start; i < num_pages + start; i++)
rqstp->rq_pages[i] = NULL;
}
/** /**
* svc_rdma_recv_read_chunk - Pull a Read chunk from the client * svc_rdma_recv_read_chunk - Pull a Read chunk from the client
* @rdma: controlling RDMA transport * @rdma: controlling RDMA transport
...@@ -860,6 +876,7 @@ int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, ...@@ -860,6 +876,7 @@ int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp,
ret = svc_rdma_post_chunk_ctxt(&info->ri_cc); ret = svc_rdma_post_chunk_ctxt(&info->ri_cc);
if (ret < 0) if (ret < 0)
goto out_err; goto out_err;
svc_rdma_save_io_pages(rqstp, 0, head->rc_page_count);
return 0; return 0;
out_err: out_err:
......
...@@ -106,7 +106,6 @@ ...@@ -106,7 +106,6 @@
#include <rdma/rdma_cm.h> #include <rdma/rdma_cm.h>
#include <linux/sunrpc/debug.h> #include <linux/sunrpc/debug.h>
#include <linux/sunrpc/rpc_rdma.h>
#include <linux/sunrpc/svc_rdma.h> #include <linux/sunrpc/svc_rdma.h>
#include "xprt_rdma.h" #include "xprt_rdma.h"
...@@ -123,6 +122,13 @@ svc_rdma_next_send_ctxt(struct list_head *list) ...@@ -123,6 +122,13 @@ svc_rdma_next_send_ctxt(struct list_head *list)
sc_list); sc_list);
} }
static void svc_rdma_send_cid_init(struct svcxprt_rdma *rdma,
struct rpc_rdma_cid *cid)
{
cid->ci_queue_id = rdma->sc_sq_cq->res.id;
cid->ci_completion_id = atomic_inc_return(&rdma->sc_completion_ids);
}
static struct svc_rdma_send_ctxt * static struct svc_rdma_send_ctxt *
svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma)
{ {
...@@ -145,6 +151,8 @@ svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) ...@@ -145,6 +151,8 @@ svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma)
if (ib_dma_mapping_error(rdma->sc_pd->device, addr)) if (ib_dma_mapping_error(rdma->sc_pd->device, addr))
goto fail2; goto fail2;
svc_rdma_send_cid_init(rdma, &ctxt->sc_cid);
ctxt->sc_send_wr.next = NULL; ctxt->sc_send_wr.next = NULL;
ctxt->sc_send_wr.wr_cqe = &ctxt->sc_cqe; ctxt->sc_send_wr.wr_cqe = &ctxt->sc_cqe;
ctxt->sc_send_wr.sg_list = ctxt->sc_sges; ctxt->sc_send_wr.sg_list = ctxt->sc_sges;
...@@ -269,34 +277,33 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc) ...@@ -269,34 +277,33 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc)
{ {
struct svcxprt_rdma *rdma = cq->cq_context; struct svcxprt_rdma *rdma = cq->cq_context;
struct ib_cqe *cqe = wc->wr_cqe; struct ib_cqe *cqe = wc->wr_cqe;
struct svc_rdma_send_ctxt *ctxt; struct svc_rdma_send_ctxt *ctxt =
container_of(cqe, struct svc_rdma_send_ctxt, sc_cqe);
trace_svcrdma_wc_send(wc); trace_svcrdma_wc_send(wc, &ctxt->sc_cid);
atomic_inc(&rdma->sc_sq_avail); atomic_inc(&rdma->sc_sq_avail);
wake_up(&rdma->sc_send_wait); wake_up(&rdma->sc_send_wait);
ctxt = container_of(cqe, struct svc_rdma_send_ctxt, sc_cqe);
svc_rdma_send_ctxt_put(rdma, ctxt); svc_rdma_send_ctxt_put(rdma, ctxt);
if (unlikely(wc->status != IB_WC_SUCCESS)) { if (unlikely(wc->status != IB_WC_SUCCESS)) {
set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
svc_xprt_enqueue(&rdma->sc_xprt); svc_xprt_enqueue(&rdma->sc_xprt);
} }
svc_xprt_put(&rdma->sc_xprt);
} }
/** /**
* svc_rdma_send - Post a single Send WR * svc_rdma_send - Post a single Send WR
* @rdma: transport on which to post the WR * @rdma: transport on which to post the WR
* @wr: prepared Send WR to post * @ctxt: send ctxt with a Send WR ready to post
* *
* Returns zero the Send WR was posted successfully. Otherwise, a * Returns zero the Send WR was posted successfully. Otherwise, a
* negative errno is returned. * negative errno is returned.
*/ */
int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr) int svc_rdma_send(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt)
{ {
struct ib_send_wr *wr = &ctxt->sc_send_wr;
int ret; int ret;
might_sleep(); might_sleep();
...@@ -321,8 +328,7 @@ int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr) ...@@ -321,8 +328,7 @@ int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr)
continue; continue;
} }
svc_xprt_get(&rdma->sc_xprt); trace_svcrdma_post_send(ctxt);
trace_svcrdma_post_send(wr);
ret = ib_post_send(rdma->sc_qp, wr, NULL); ret = ib_post_send(rdma->sc_qp, wr, NULL);
if (ret) if (ret)
break; break;
...@@ -331,7 +337,6 @@ int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr) ...@@ -331,7 +337,6 @@ int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr)
trace_svcrdma_sq_post_err(rdma, ret); trace_svcrdma_sq_post_err(rdma, ret);
set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
svc_xprt_put(&rdma->sc_xprt);
wake_up(&rdma->sc_send_wait); wake_up(&rdma->sc_send_wait);
return ret; return ret;
} }
...@@ -375,11 +380,8 @@ static ssize_t svc_rdma_encode_write_segment(__be32 *src, ...@@ -375,11 +380,8 @@ static ssize_t svc_rdma_encode_write_segment(__be32 *src,
if (!p) if (!p)
return -EMSGSIZE; return -EMSGSIZE;
handle = be32_to_cpup(src++); xdr_decode_rdma_segment(src, &handle, &length, &offset);
length = be32_to_cpup(src++);
xdr_decode_hyper(src, &offset);
*p++ = cpu_to_be32(handle);
if (*remaining < length) { if (*remaining < length) {
/* segment only partly filled */ /* segment only partly filled */
length = *remaining; length = *remaining;
...@@ -388,8 +390,7 @@ static ssize_t svc_rdma_encode_write_segment(__be32 *src, ...@@ -388,8 +390,7 @@ static ssize_t svc_rdma_encode_write_segment(__be32 *src,
/* entire segment was consumed */ /* entire segment was consumed */
*remaining -= length; *remaining -= length;
} }
*p++ = cpu_to_be32(length); xdr_encode_rdma_segment(p, handle, length, offset);
xdr_encode_hyper(p, offset);
trace_svcrdma_encode_wseg(handle, length, offset); trace_svcrdma_encode_wseg(handle, length, offset);
return len; return len;
...@@ -801,45 +802,76 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, ...@@ -801,45 +802,76 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma,
} else { } else {
sctxt->sc_send_wr.opcode = IB_WR_SEND; sctxt->sc_send_wr.opcode = IB_WR_SEND;
} }
return svc_rdma_send(rdma, &sctxt->sc_send_wr); return svc_rdma_send(rdma, sctxt);
} }
/* Given the client-provided Write and Reply chunks, the server was not /**
* able to form a complete reply. Return an RDMA_ERROR message so the * svc_rdma_send_error_msg - Send an RPC/RDMA v1 error response
* client can retire this RPC transaction. As above, the Send completion * @rdma: controlling transport context
* routine releases payload pages that were part of a previous RDMA Write. * @sctxt: Send context for the response
* * @rctxt: Receive context for incoming bad message
* Remote Invalidation is skipped for simplicity. * @status: negative errno indicating error that occurred
*
* Given the client-provided Read, Write, and Reply chunks, the
* server was not able to parse the Call or form a complete Reply.
* Return an RDMA_ERROR message so the client can retire the RPC
* transaction.
*
* The caller does not have to release @sctxt. It is released by
* Send completion, or by this function on error.
*/ */
static int svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, void svc_rdma_send_error_msg(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *ctxt, struct svc_rdma_send_ctxt *sctxt,
struct svc_rqst *rqstp) struct svc_rdma_recv_ctxt *rctxt,
int status)
{ {
struct svc_rdma_recv_ctxt *rctxt = rqstp->rq_xprt_ctxt;
__be32 *rdma_argp = rctxt->rc_recv_buf; __be32 *rdma_argp = rctxt->rc_recv_buf;
__be32 *p; __be32 *p;
rpcrdma_set_xdrlen(&ctxt->sc_hdrbuf, 0); rpcrdma_set_xdrlen(&sctxt->sc_hdrbuf, 0);
xdr_init_encode(&ctxt->sc_stream, &ctxt->sc_hdrbuf, ctxt->sc_xprt_buf, xdr_init_encode(&sctxt->sc_stream, &sctxt->sc_hdrbuf,
NULL); sctxt->sc_xprt_buf, NULL);
p = xdr_reserve_space(&ctxt->sc_stream, RPCRDMA_HDRLEN_ERR); p = xdr_reserve_space(&sctxt->sc_stream,
rpcrdma_fixed_maxsz * sizeof(*p));
if (!p) if (!p)
return -ENOMSG; goto put_ctxt;
*p++ = *rdma_argp; *p++ = *rdma_argp;
*p++ = *(rdma_argp + 1); *p++ = *(rdma_argp + 1);
*p++ = rdma->sc_fc_credits; *p++ = rdma->sc_fc_credits;
*p++ = rdma_error; *p = rdma_error;
*p = err_chunk;
trace_svcrdma_err_chunk(*rdma_argp); switch (status) {
case -EPROTONOSUPPORT:
p = xdr_reserve_space(&sctxt->sc_stream, 3 * sizeof(*p));
if (!p)
goto put_ctxt;
*p++ = err_vers;
*p++ = rpcrdma_version;
*p = rpcrdma_version;
trace_svcrdma_err_vers(*rdma_argp);
break;
default:
p = xdr_reserve_space(&sctxt->sc_stream, sizeof(*p));
if (!p)
goto put_ctxt;
*p = err_chunk;
trace_svcrdma_err_chunk(*rdma_argp);
}
svc_rdma_save_io_pages(rqstp, ctxt); /* Remote Invalidation is skipped for simplicity. */
sctxt->sc_send_wr.num_sge = 1;
sctxt->sc_send_wr.opcode = IB_WR_SEND;
sctxt->sc_sges[0].length = sctxt->sc_hdrbuf.len;
if (svc_rdma_send(rdma, sctxt))
goto put_ctxt;
return;
ctxt->sc_send_wr.num_sge = 1; put_ctxt:
ctxt->sc_send_wr.opcode = IB_WR_SEND; svc_rdma_send_ctxt_put(rdma, sctxt);
ctxt->sc_sges[0].length = ctxt->sc_hdrbuf.len;
return svc_rdma_send(rdma, &ctxt->sc_send_wr);
} }
/** /**
...@@ -930,15 +962,17 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) ...@@ -930,15 +962,17 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
if (ret != -E2BIG && ret != -EINVAL) if (ret != -E2BIG && ret != -EINVAL)
goto err1; goto err1;
ret = svc_rdma_send_error_msg(rdma, sctxt, rqstp); /* Send completion releases payload pages that were part
if (ret < 0) * of previously posted RDMA Writes.
goto err1; */
svc_rdma_save_io_pages(rqstp, sctxt);
svc_rdma_send_error_msg(rdma, sctxt, rctxt, ret);
return 0; return 0;
err1: err1:
svc_rdma_send_ctxt_put(rdma, sctxt); svc_rdma_send_ctxt_put(rdma, sctxt);
err0: err0:
trace_svcrdma_send_failed(rqstp, ret); trace_svcrdma_send_err(rqstp, ret);
set_bit(XPT_CLOSE, &xprt->xpt_flags); set_bit(XPT_CLOSE, &xprt->xpt_flags);
return -ENOTCONN; return -ENOTCONN;
} }
......
...@@ -55,7 +55,6 @@ ...@@ -55,7 +55,6 @@
#include <linux/sunrpc/addr.h> #include <linux/sunrpc/addr.h>
#include <linux/sunrpc/debug.h> #include <linux/sunrpc/debug.h>
#include <linux/sunrpc/rpc_rdma.h>
#include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/svc_rdma.h> #include <linux/sunrpc/svc_rdma.h>
...@@ -238,65 +237,56 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id, ...@@ -238,65 +237,56 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id,
svc_xprt_enqueue(&listen_xprt->sc_xprt); svc_xprt_enqueue(&listen_xprt->sc_xprt);
} }
/* /**
* Handles events generated on the listening endpoint. These events will be * svc_rdma_listen_handler - Handle CM events generated on a listening endpoint
* either be incoming connect requests or adapter removal events. * @cma_id: the server's listener rdma_cm_id
* @event: details of the event
*
* Return values:
* %0: Do not destroy @cma_id
* %1: Destroy @cma_id (never returned here)
*
* NB: There is never a DEVICE_REMOVAL event for INADDR_ANY listeners.
*/ */
static int rdma_listen_handler(struct rdma_cm_id *cma_id, static int svc_rdma_listen_handler(struct rdma_cm_id *cma_id,
struct rdma_cm_event *event) struct rdma_cm_event *event)
{ {
switch (event->event) { switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST: case RDMA_CM_EVENT_CONNECT_REQUEST:
dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, "
"event = %s (%d)\n", cma_id, cma_id->context,
rdma_event_msg(event->event), event->event);
handle_connect_req(cma_id, &event->param.conn); handle_connect_req(cma_id, &event->param.conn);
break; break;
default: default:
/* NB: No device removal upcall for INADDR_ANY listeners */
dprintk("svcrdma: Unexpected event on listening endpoint %p, "
"event = %s (%d)\n", cma_id,
rdma_event_msg(event->event), event->event);
break; break;
} }
return 0; return 0;
} }
static int rdma_cma_handler(struct rdma_cm_id *cma_id, /**
struct rdma_cm_event *event) * svc_rdma_cma_handler - Handle CM events on client connections
* @cma_id: the server's listener rdma_cm_id
* @event: details of the event
*
* Return values:
* %0: Do not destroy @cma_id
* %1: Destroy @cma_id (never returned here)
*/
static int svc_rdma_cma_handler(struct rdma_cm_id *cma_id,
struct rdma_cm_event *event)
{ {
struct svcxprt_rdma *rdma = cma_id->context; struct svcxprt_rdma *rdma = cma_id->context;
struct svc_xprt *xprt = &rdma->sc_xprt; struct svc_xprt *xprt = &rdma->sc_xprt;
switch (event->event) { switch (event->event) {
case RDMA_CM_EVENT_ESTABLISHED: case RDMA_CM_EVENT_ESTABLISHED:
/* Accept complete */
svc_xprt_get(xprt);
dprintk("svcrdma: Connection completed on DTO xprt=%p, "
"cm_id=%p\n", xprt, cma_id);
clear_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags); clear_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags);
svc_xprt_enqueue(xprt); svc_xprt_enqueue(xprt);
break; break;
case RDMA_CM_EVENT_DISCONNECTED: case RDMA_CM_EVENT_DISCONNECTED:
dprintk("svcrdma: Disconnect on DTO xprt=%p, cm_id=%p\n",
xprt, cma_id);
set_bit(XPT_CLOSE, &xprt->xpt_flags);
svc_xprt_enqueue(xprt);
svc_xprt_put(xprt);
break;
case RDMA_CM_EVENT_DEVICE_REMOVAL: case RDMA_CM_EVENT_DEVICE_REMOVAL:
dprintk("svcrdma: Device removal cma_id=%p, xprt = %p, "
"event = %s (%d)\n", cma_id, xprt,
rdma_event_msg(event->event), event->event);
set_bit(XPT_CLOSE, &xprt->xpt_flags); set_bit(XPT_CLOSE, &xprt->xpt_flags);
svc_xprt_enqueue(xprt); svc_xprt_enqueue(xprt);
svc_xprt_put(xprt);
break; break;
default: default:
dprintk("svcrdma: Unexpected event on DTO endpoint %p, "
"event = %s (%d)\n", cma_id,
rdma_event_msg(event->event), event->event);
break; break;
} }
return 0; return 0;
...@@ -322,7 +312,7 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, ...@@ -322,7 +312,7 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
set_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags); set_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags);
strcpy(cma_xprt->sc_xprt.xpt_remotebuf, "listener"); strcpy(cma_xprt->sc_xprt.xpt_remotebuf, "listener");
listen_id = rdma_create_id(net, rdma_listen_handler, cma_xprt, listen_id = rdma_create_id(net, svc_rdma_listen_handler, cma_xprt,
RDMA_PS_TCP, IB_QPT_RC); RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(listen_id)) { if (IS_ERR(listen_id)) {
ret = PTR_ERR(listen_id); ret = PTR_ERR(listen_id);
...@@ -486,7 +476,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) ...@@ -486,7 +476,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
goto errout; goto errout;
/* Swap out the handler */ /* Swap out the handler */
newxprt->sc_cm_id->event_handler = rdma_cma_handler; newxprt->sc_cm_id->event_handler = svc_rdma_cma_handler;
/* Construct RDMA-CM private message */ /* Construct RDMA-CM private message */
pmsg.cp_magic = rpcrdma_cmp_magic; pmsg.cp_magic = rpcrdma_cmp_magic;
...@@ -540,24 +530,11 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) ...@@ -540,24 +530,11 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
return NULL; return NULL;
} }
/*
* When connected, an svc_xprt has at least two references:
*
* - A reference held by the cm_id between the ESTABLISHED and
* DISCONNECTED events. If the remote peer disconnected first, this
* reference could be gone.
*
* - A reference held by the svc_recv code that called this function
* as part of close processing.
*
* At a minimum one references should still be held.
*/
static void svc_rdma_detach(struct svc_xprt *xprt) static void svc_rdma_detach(struct svc_xprt *xprt)
{ {
struct svcxprt_rdma *rdma = struct svcxprt_rdma *rdma =
container_of(xprt, struct svcxprt_rdma, sc_xprt); container_of(xprt, struct svcxprt_rdma, sc_xprt);
/* Disconnect and flush posted WQE */
rdma_disconnect(rdma->sc_cm_id); rdma_disconnect(rdma->sc_cm_id);
} }
...@@ -567,6 +544,7 @@ static void __svc_rdma_free(struct work_struct *work) ...@@ -567,6 +544,7 @@ static void __svc_rdma_free(struct work_struct *work)
container_of(work, struct svcxprt_rdma, sc_work); container_of(work, struct svcxprt_rdma, sc_work);
struct svc_xprt *xprt = &rdma->sc_xprt; struct svc_xprt *xprt = &rdma->sc_xprt;
/* This blocks until the Completion Queues are empty */
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
ib_drain_qp(rdma->sc_qp); ib_drain_qp(rdma->sc_qp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment