|
|
@@ -1,4 +1,5 @@
|
|
|
/*
|
|
|
+ * Copyright (c) 2016 Oracle. All rights reserved.
|
|
|
* Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved.
|
|
|
*
|
|
|
* This software is available to you under a choice of one of two
|
|
|
@@ -47,102 +48,43 @@
|
|
|
|
|
|
#define RPCDBG_FACILITY RPCDBG_SVCXPRT
|
|
|
|
|
|
-/*
|
|
|
- * Decodes a read chunk list. The expected format is as follows:
|
|
|
- * descrim : xdr_one
|
|
|
- * position : __be32 offset into XDR stream
|
|
|
- * handle : __be32 RKEY
|
|
|
- * . . .
|
|
|
- * end-of-list: xdr_zero
|
|
|
- */
|
|
|
-static __be32 *decode_read_list(__be32 *va, __be32 *vaend)
|
|
|
+static __be32 *xdr_check_read_list(__be32 *p, __be32 *end)
|
|
|
{
|
|
|
- struct rpcrdma_read_chunk *ch = (struct rpcrdma_read_chunk *)va;
|
|
|
+ __be32 *next;
|
|
|
|
|
|
- while (ch->rc_discrim != xdr_zero) {
|
|
|
- if (((unsigned long)ch + sizeof(struct rpcrdma_read_chunk)) >
|
|
|
- (unsigned long)vaend) {
|
|
|
- dprintk("svcrdma: vaend=%p, ch=%p\n", vaend, ch);
|
|
|
+ while (*p++ != xdr_zero) {
|
|
|
+ next = p + rpcrdma_readchunk_maxsz - 1;
|
|
|
+ if (next > end)
|
|
|
return NULL;
|
|
|
- }
|
|
|
- ch++;
|
|
|
+ p = next;
|
|
|
}
|
|
|
- return &ch->rc_position;
|
|
|
+ return p;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Decodes a write chunk list. The expected format is as follows:
|
|
|
- * descrim : xdr_one
|
|
|
- * nchunks : <count>
|
|
|
- * handle : __be32 RKEY ---+
|
|
|
- * length : __be32 <len of segment> |
|
|
|
- * offset : remove va + <count>
|
|
|
- * . . . |
|
|
|
- * ---+
|
|
|
- */
|
|
|
-static __be32 *decode_write_list(__be32 *va, __be32 *vaend)
|
|
|
+static __be32 *xdr_check_write_list(__be32 *p, __be32 *end)
|
|
|
{
|
|
|
- unsigned long start, end;
|
|
|
- int nchunks;
|
|
|
-
|
|
|
- struct rpcrdma_write_array *ary =
|
|
|
- (struct rpcrdma_write_array *)va;
|
|
|
+ __be32 *next;
|
|
|
|
|
|
- /* Check for not write-array */
|
|
|
- if (ary->wc_discrim == xdr_zero)
|
|
|
- return &ary->wc_nchunks;
|
|
|
-
|
|
|
- if ((unsigned long)ary + sizeof(struct rpcrdma_write_array) >
|
|
|
- (unsigned long)vaend) {
|
|
|
- dprintk("svcrdma: ary=%p, vaend=%p\n", ary, vaend);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- nchunks = be32_to_cpu(ary->wc_nchunks);
|
|
|
-
|
|
|
- start = (unsigned long)&ary->wc_array[0];
|
|
|
- end = (unsigned long)vaend;
|
|
|
- if (nchunks < 0 ||
|
|
|
- nchunks > (SIZE_MAX - start) / sizeof(struct rpcrdma_write_chunk) ||
|
|
|
- (start + (sizeof(struct rpcrdma_write_chunk) * nchunks)) > end) {
|
|
|
- dprintk("svcrdma: ary=%p, wc_nchunks=%d, vaend=%p\n",
|
|
|
- ary, nchunks, vaend);
|
|
|
- return NULL;
|
|
|
+ while (*p++ != xdr_zero) {
|
|
|
+ next = p + 1 + be32_to_cpup(p) * rpcrdma_segment_maxsz;
|
|
|
+ if (next > end)
|
|
|
+ return NULL;
|
|
|
+ p = next;
|
|
|
}
|
|
|
- /*
|
|
|
- * rs_length is the 2nd 4B field in wc_target and taking its
|
|
|
- * address skips the list terminator
|
|
|
- */
|
|
|
- return &ary->wc_array[nchunks].wc_target.rs_length;
|
|
|
+ return p;
|
|
|
}
|
|
|
|
|
|
-static __be32 *decode_reply_array(__be32 *va, __be32 *vaend)
|
|
|
+static __be32 *xdr_check_reply_chunk(__be32 *p, __be32 *end)
|
|
|
{
|
|
|
- unsigned long start, end;
|
|
|
- int nchunks;
|
|
|
- struct rpcrdma_write_array *ary =
|
|
|
- (struct rpcrdma_write_array *)va;
|
|
|
-
|
|
|
- /* Check for no reply-array */
|
|
|
- if (ary->wc_discrim == xdr_zero)
|
|
|
- return &ary->wc_nchunks;
|
|
|
-
|
|
|
- if ((unsigned long)ary + sizeof(struct rpcrdma_write_array) >
|
|
|
- (unsigned long)vaend) {
|
|
|
- dprintk("svcrdma: ary=%p, vaend=%p\n", ary, vaend);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- nchunks = be32_to_cpu(ary->wc_nchunks);
|
|
|
-
|
|
|
- start = (unsigned long)&ary->wc_array[0];
|
|
|
- end = (unsigned long)vaend;
|
|
|
- if (nchunks < 0 ||
|
|
|
- nchunks > (SIZE_MAX - start) / sizeof(struct rpcrdma_write_chunk) ||
|
|
|
- (start + (sizeof(struct rpcrdma_write_chunk) * nchunks)) > end) {
|
|
|
- dprintk("svcrdma: ary=%p, wc_nchunks=%d, vaend=%p\n",
|
|
|
- ary, nchunks, vaend);
|
|
|
- return NULL;
|
|
|
+ __be32 *next;
|
|
|
+
|
|
|
+ if (*p++ != xdr_zero) {
|
|
|
+ next = p + 1 + be32_to_cpup(p) * rpcrdma_segment_maxsz;
|
|
|
+ if (next > end)
|
|
|
+ return NULL;
|
|
|
+ p = next;
|
|
|
}
|
|
|
- return (__be32 *)&ary->wc_array[nchunks];
|
|
|
+ return p;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -158,87 +100,71 @@ static __be32 *decode_reply_array(__be32 *va, __be32 *vaend)
|
|
|
*/
|
|
|
int svc_rdma_xdr_decode_req(struct xdr_buf *rq_arg)
|
|
|
{
|
|
|
- struct rpcrdma_msg *rmsgp;
|
|
|
- __be32 *va, *vaend;
|
|
|
- unsigned int len;
|
|
|
- u32 hdr_len;
|
|
|
+ __be32 *p, *end, *rdma_argp;
|
|
|
+ unsigned int hdr_len;
|
|
|
|
|
|
/* Verify that there's enough bytes for header + something */
|
|
|
- if (rq_arg->len <= RPCRDMA_HDRLEN_ERR) {
|
|
|
- dprintk("svcrdma: header too short = %d\n",
|
|
|
- rq_arg->len);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ if (rq_arg->len <= RPCRDMA_HDRLEN_ERR)
|
|
|
+ goto out_short;
|
|
|
|
|
|
- rmsgp = (struct rpcrdma_msg *)rq_arg->head[0].iov_base;
|
|
|
- if (rmsgp->rm_vers != rpcrdma_version) {
|
|
|
- dprintk("%s: bad version %u\n", __func__,
|
|
|
- be32_to_cpu(rmsgp->rm_vers));
|
|
|
- return -EPROTONOSUPPORT;
|
|
|
- }
|
|
|
+ rdma_argp = rq_arg->head[0].iov_base;
|
|
|
+ if (*(rdma_argp + 1) != rpcrdma_version)
|
|
|
+ goto out_version;
|
|
|
|
|
|
- switch (be32_to_cpu(rmsgp->rm_type)) {
|
|
|
- case RDMA_MSG:
|
|
|
- case RDMA_NOMSG:
|
|
|
+ switch (*(rdma_argp + 3)) {
|
|
|
+ case rdma_msg:
|
|
|
+ case rdma_nomsg:
|
|
|
break;
|
|
|
|
|
|
- case RDMA_DONE:
|
|
|
- /* Just drop it */
|
|
|
- dprintk("svcrdma: dropping RDMA_DONE message\n");
|
|
|
- return 0;
|
|
|
-
|
|
|
- case RDMA_ERROR:
|
|
|
- /* Possible if this is a backchannel reply.
|
|
|
- * XXX: We should cancel this XID, though.
|
|
|
- */
|
|
|
- dprintk("svcrdma: dropping RDMA_ERROR message\n");
|
|
|
- return 0;
|
|
|
-
|
|
|
- case RDMA_MSGP:
|
|
|
- /* Pull in the extra for the padded case, bump our pointer */
|
|
|
- rmsgp->rm_body.rm_padded.rm_align =
|
|
|
- be32_to_cpu(rmsgp->rm_body.rm_padded.rm_align);
|
|
|
- rmsgp->rm_body.rm_padded.rm_thresh =
|
|
|
- be32_to_cpu(rmsgp->rm_body.rm_padded.rm_thresh);
|
|
|
-
|
|
|
- va = &rmsgp->rm_body.rm_padded.rm_pempty[4];
|
|
|
- rq_arg->head[0].iov_base = va;
|
|
|
- len = (u32)((unsigned long)va - (unsigned long)rmsgp);
|
|
|
- rq_arg->head[0].iov_len -= len;
|
|
|
- if (len > rq_arg->len)
|
|
|
- return -EINVAL;
|
|
|
- return len;
|
|
|
- default:
|
|
|
- dprintk("svcrdma: bad rdma procedure (%u)\n",
|
|
|
- be32_to_cpu(rmsgp->rm_type));
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ case rdma_done:
|
|
|
+ goto out_drop;
|
|
|
|
|
|
- /* The chunk list may contain either a read chunk list or a write
|
|
|
- * chunk list and a reply chunk list.
|
|
|
- */
|
|
|
- va = &rmsgp->rm_body.rm_chunks[0];
|
|
|
- vaend = (__be32 *)((unsigned long)rmsgp + rq_arg->len);
|
|
|
- va = decode_read_list(va, vaend);
|
|
|
- if (!va) {
|
|
|
- dprintk("svcrdma: failed to decode read list\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- va = decode_write_list(va, vaend);
|
|
|
- if (!va) {
|
|
|
- dprintk("svcrdma: failed to decode write list\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- va = decode_reply_array(va, vaend);
|
|
|
- if (!va) {
|
|
|
- dprintk("svcrdma: failed to decode reply chunk\n");
|
|
|
- return -EINVAL;
|
|
|
+ case rdma_error:
|
|
|
+ goto out_drop;
|
|
|
+
|
|
|
+ default:
|
|
|
+ goto out_proc;
|
|
|
}
|
|
|
|
|
|
- rq_arg->head[0].iov_base = va;
|
|
|
- hdr_len = (unsigned long)va - (unsigned long)rmsgp;
|
|
|
+ end = (__be32 *)((unsigned long)rdma_argp + rq_arg->len);
|
|
|
+ p = xdr_check_read_list(rdma_argp + 4, end);
|
|
|
+ if (!p)
|
|
|
+ goto out_inval;
|
|
|
+ p = xdr_check_write_list(p, end);
|
|
|
+ if (!p)
|
|
|
+ goto out_inval;
|
|
|
+ p = xdr_check_reply_chunk(p, end);
|
|
|
+ if (!p)
|
|
|
+ goto out_inval;
|
|
|
+ if (p > end)
|
|
|
+ goto out_inval;
|
|
|
+
|
|
|
+ rq_arg->head[0].iov_base = p;
|
|
|
+ hdr_len = (unsigned long)p - (unsigned long)rdma_argp;
|
|
|
rq_arg->head[0].iov_len -= hdr_len;
|
|
|
return hdr_len;
|
|
|
+
|
|
|
+out_short:
|
|
|
+ dprintk("svcrdma: header too short = %d\n", rq_arg->len);
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+out_version:
|
|
|
+ dprintk("svcrdma: bad xprt version: %u\n",
|
|
|
+ be32_to_cpup(rdma_argp + 1));
|
|
|
+ return -EPROTONOSUPPORT;
|
|
|
+
|
|
|
+out_drop:
|
|
|
+ dprintk("svcrdma: dropping RDMA_DONE/ERROR message\n");
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_proc:
|
|
|
+ dprintk("svcrdma: bad rdma procedure (%u)\n",
|
|
|
+ be32_to_cpup(rdma_argp + 3));
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+out_inval:
|
|
|
+ dprintk("svcrdma: failed to parse transport header\n");
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
int svc_rdma_xdr_encode_error(struct svcxprt_rdma *xprt,
|
|
|
@@ -249,7 +175,7 @@ int svc_rdma_xdr_encode_error(struct svcxprt_rdma *xprt,
|
|
|
|
|
|
*va++ = rmsgp->rm_xid;
|
|
|
*va++ = rmsgp->rm_vers;
|
|
|
- *va++ = cpu_to_be32(xprt->sc_max_requests);
|
|
|
+ *va++ = xprt->sc_fc_credits;
|
|
|
*va++ = rdma_error;
|
|
|
*va++ = cpu_to_be32(err);
|
|
|
if (err == ERR_VERS) {
|
|
|
@@ -260,32 +186,35 @@ int svc_rdma_xdr_encode_error(struct svcxprt_rdma *xprt,
|
|
|
return (int)((unsigned long)va - (unsigned long)startp);
|
|
|
}
|
|
|
|
|
|
-int svc_rdma_xdr_get_reply_hdr_len(struct rpcrdma_msg *rmsgp)
|
|
|
+/**
|
|
|
+ * svc_rdma_xdr_get_reply_hdr_length - Get length of Reply transport header
|
|
|
+ * @rdma_resp: buffer containing Reply transport header
|
|
|
+ *
|
|
|
+ * Returns length of transport header, in bytes.
|
|
|
+ */
|
|
|
+unsigned int svc_rdma_xdr_get_reply_hdr_len(__be32 *rdma_resp)
|
|
|
{
|
|
|
- struct rpcrdma_write_array *wr_ary;
|
|
|
+ unsigned int nsegs;
|
|
|
+ __be32 *p;
|
|
|
|
|
|
- /* There is no read-list in a reply */
|
|
|
+ p = rdma_resp;
|
|
|
|
|
|
- /* skip write list */
|
|
|
- wr_ary = (struct rpcrdma_write_array *)
|
|
|
- &rmsgp->rm_body.rm_chunks[1];
|
|
|
- if (wr_ary->wc_discrim)
|
|
|
- wr_ary = (struct rpcrdma_write_array *)
|
|
|
- &wr_ary->wc_array[be32_to_cpu(wr_ary->wc_nchunks)].
|
|
|
- wc_target.rs_length;
|
|
|
- else
|
|
|
- wr_ary = (struct rpcrdma_write_array *)
|
|
|
- &wr_ary->wc_nchunks;
|
|
|
-
|
|
|
- /* skip reply array */
|
|
|
- if (wr_ary->wc_discrim)
|
|
|
- wr_ary = (struct rpcrdma_write_array *)
|
|
|
- &wr_ary->wc_array[be32_to_cpu(wr_ary->wc_nchunks)];
|
|
|
- else
|
|
|
- wr_ary = (struct rpcrdma_write_array *)
|
|
|
- &wr_ary->wc_nchunks;
|
|
|
-
|
|
|
- return (unsigned long) wr_ary - (unsigned long) rmsgp;
|
|
|
+ /* RPC-over-RDMA V1 replies never have a Read list. */
|
|
|
+ p += rpcrdma_fixed_maxsz + 1;
|
|
|
+
|
|
|
+ /* Skip Write list. */
|
|
|
+ while (*p++ != xdr_zero) {
|
|
|
+ nsegs = be32_to_cpup(p++);
|
|
|
+ p += nsegs * rpcrdma_segment_maxsz;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Skip Reply chunk. */
|
|
|
+ if (*p++ != xdr_zero) {
|
|
|
+ nsegs = be32_to_cpup(p++);
|
|
|
+ p += nsegs * rpcrdma_segment_maxsz;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (unsigned long)p - (unsigned long)rdma_resp;
|
|
|
}
|
|
|
|
|
|
void svc_rdma_xdr_encode_write_list(struct rpcrdma_msg *rmsgp, int chunks)
|
|
|
@@ -326,19 +255,3 @@ void svc_rdma_xdr_encode_array_chunk(struct rpcrdma_write_array *ary,
|
|
|
seg->rs_offset = rs_offset;
|
|
|
seg->rs_length = cpu_to_be32(write_len);
|
|
|
}
|
|
|
-
|
|
|
-void svc_rdma_xdr_encode_reply_header(struct svcxprt_rdma *xprt,
|
|
|
- struct rpcrdma_msg *rdma_argp,
|
|
|
- struct rpcrdma_msg *rdma_resp,
|
|
|
- enum rpcrdma_proc rdma_type)
|
|
|
-{
|
|
|
- rdma_resp->rm_xid = rdma_argp->rm_xid;
|
|
|
- rdma_resp->rm_vers = rdma_argp->rm_vers;
|
|
|
- rdma_resp->rm_credit = cpu_to_be32(xprt->sc_max_requests);
|
|
|
- rdma_resp->rm_type = cpu_to_be32(rdma_type);
|
|
|
-
|
|
|
- /* Encode <nul> chunks lists */
|
|
|
- rdma_resp->rm_body.rm_chunks[0] = xdr_zero;
|
|
|
- rdma_resp->rm_body.rm_chunks[1] = xdr_zero;
|
|
|
- rdma_resp->rm_body.rm_chunks[2] = xdr_zero;
|
|
|
-}
|