Path: blob/master/net/sunrpc/xprtrdma/svc_rdma_marshal.c
15111 views
/*1* Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved.2*3* This software is available to you under a choice of one of two4* licenses. You may choose to be licensed under the terms of the GNU5* General Public License (GPL) Version 2, available from the file6* COPYING in the main directory of this source tree, or the BSD-type7* license below:8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12*13* Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15*16* Redistributions in binary form must reproduce the above17* copyright notice, this list of conditions and the following18* disclaimer in the documentation and/or other materials provided19* with the distribution.20*21* Neither the name of the Network Appliance, Inc. nor the names of22* its contributors may be used to endorse or promote products23* derived from this software without specific prior written24* permission.25*26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS27* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT28* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR29* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT30* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,31* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT32* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,33* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY34* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT35* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE36* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.37*38* Author: Tom Tucker <[email protected]>39*/4041#include <linux/sunrpc/xdr.h>42#include <linux/sunrpc/debug.h>43#include <asm/unaligned.h>44#include <linux/sunrpc/rpc_rdma.h>45#include <linux/sunrpc/svc_rdma.h>4647#define RPCDBG_FACILITY RPCDBG_SVCXPRT4849/*50* Decodes a read chunk list. The expected format is as follows:51* descrim : xdr_one52* position : u32 offset into XDR stream53* handle : u32 RKEY54* . . .55* end-of-list: xdr_zero56*/57static u32 *decode_read_list(u32 *va, u32 *vaend)58{59struct rpcrdma_read_chunk *ch = (struct rpcrdma_read_chunk *)va;6061while (ch->rc_discrim != xdr_zero) {62u64 ch_offset;6364if (((unsigned long)ch + sizeof(struct rpcrdma_read_chunk)) >65(unsigned long)vaend) {66dprintk("svcrdma: vaend=%p, ch=%p\n", vaend, ch);67return NULL;68}6970ch->rc_discrim = ntohl(ch->rc_discrim);71ch->rc_position = ntohl(ch->rc_position);72ch->rc_target.rs_handle = ntohl(ch->rc_target.rs_handle);73ch->rc_target.rs_length = ntohl(ch->rc_target.rs_length);74va = (u32 *)&ch->rc_target.rs_offset;75xdr_decode_hyper(va, &ch_offset);76put_unaligned(ch_offset, (u64 *)va);77ch++;78}79return (u32 *)&ch->rc_position;80}8182/*83* Determine number of chunks and total bytes in chunk list. The chunk84* list has already been verified to fit within the RPCRDMA header.85*/86void svc_rdma_rcl_chunk_counts(struct rpcrdma_read_chunk *ch,87int *ch_count, int *byte_count)88{89/* compute the number of bytes represented by read chunks */90*byte_count = 0;91*ch_count = 0;92for (; ch->rc_discrim != 0; ch++) {93*byte_count = *byte_count + ch->rc_target.rs_length;94*ch_count = *ch_count + 1;95}96}9798/*99* Decodes a write chunk list. The expected format is as follows:100* descrim : xdr_one101* nchunks : <count>102* handle : u32 RKEY ---+103* length : u32 <len of segment> |104* offset : remove va + <count>105* . . . |106* ---+107*/108static u32 *decode_write_list(u32 *va, u32 *vaend)109{110int ch_no;111struct rpcrdma_write_array *ary =112(struct rpcrdma_write_array *)va;113114/* Check for not write-array */115if (ary->wc_discrim == xdr_zero)116return (u32 *)&ary->wc_nchunks;117118if ((unsigned long)ary + sizeof(struct rpcrdma_write_array) >119(unsigned long)vaend) {120dprintk("svcrdma: ary=%p, vaend=%p\n", ary, vaend);121return NULL;122}123ary->wc_discrim = ntohl(ary->wc_discrim);124ary->wc_nchunks = ntohl(ary->wc_nchunks);125if (((unsigned long)&ary->wc_array[0] +126(sizeof(struct rpcrdma_write_chunk) * ary->wc_nchunks)) >127(unsigned long)vaend) {128dprintk("svcrdma: ary=%p, wc_nchunks=%d, vaend=%p\n",129ary, ary->wc_nchunks, vaend);130return NULL;131}132for (ch_no = 0; ch_no < ary->wc_nchunks; ch_no++) {133u64 ch_offset;134135ary->wc_array[ch_no].wc_target.rs_handle =136ntohl(ary->wc_array[ch_no].wc_target.rs_handle);137ary->wc_array[ch_no].wc_target.rs_length =138ntohl(ary->wc_array[ch_no].wc_target.rs_length);139va = (u32 *)&ary->wc_array[ch_no].wc_target.rs_offset;140xdr_decode_hyper(va, &ch_offset);141put_unaligned(ch_offset, (u64 *)va);142}143144/*145* rs_length is the 2nd 4B field in wc_target and taking its146* address skips the list terminator147*/148return (u32 *)&ary->wc_array[ch_no].wc_target.rs_length;149}150151static u32 *decode_reply_array(u32 *va, u32 *vaend)152{153int ch_no;154struct rpcrdma_write_array *ary =155(struct rpcrdma_write_array *)va;156157/* Check for no reply-array */158if (ary->wc_discrim == xdr_zero)159return (u32 *)&ary->wc_nchunks;160161if ((unsigned long)ary + sizeof(struct rpcrdma_write_array) >162(unsigned long)vaend) {163dprintk("svcrdma: ary=%p, vaend=%p\n", ary, vaend);164return NULL;165}166ary->wc_discrim = ntohl(ary->wc_discrim);167ary->wc_nchunks = ntohl(ary->wc_nchunks);168if (((unsigned long)&ary->wc_array[0] +169(sizeof(struct rpcrdma_write_chunk) * ary->wc_nchunks)) >170(unsigned long)vaend) {171dprintk("svcrdma: ary=%p, wc_nchunks=%d, vaend=%p\n",172ary, ary->wc_nchunks, vaend);173return NULL;174}175for (ch_no = 0; ch_no < ary->wc_nchunks; ch_no++) {176u64 ch_offset;177178ary->wc_array[ch_no].wc_target.rs_handle =179ntohl(ary->wc_array[ch_no].wc_target.rs_handle);180ary->wc_array[ch_no].wc_target.rs_length =181ntohl(ary->wc_array[ch_no].wc_target.rs_length);182va = (u32 *)&ary->wc_array[ch_no].wc_target.rs_offset;183xdr_decode_hyper(va, &ch_offset);184put_unaligned(ch_offset, (u64 *)va);185}186187return (u32 *)&ary->wc_array[ch_no];188}189190int svc_rdma_xdr_decode_req(struct rpcrdma_msg **rdma_req,191struct svc_rqst *rqstp)192{193struct rpcrdma_msg *rmsgp = NULL;194u32 *va;195u32 *vaend;196u32 hdr_len;197198rmsgp = (struct rpcrdma_msg *)rqstp->rq_arg.head[0].iov_base;199200/* Verify that there's enough bytes for header + something */201if (rqstp->rq_arg.len <= RPCRDMA_HDRLEN_MIN) {202dprintk("svcrdma: header too short = %d\n",203rqstp->rq_arg.len);204return -EINVAL;205}206207/* Decode the header */208rmsgp->rm_xid = ntohl(rmsgp->rm_xid);209rmsgp->rm_vers = ntohl(rmsgp->rm_vers);210rmsgp->rm_credit = ntohl(rmsgp->rm_credit);211rmsgp->rm_type = ntohl(rmsgp->rm_type);212213if (rmsgp->rm_vers != RPCRDMA_VERSION)214return -ENOSYS;215216/* Pull in the extra for the padded case and bump our pointer */217if (rmsgp->rm_type == RDMA_MSGP) {218int hdrlen;219rmsgp->rm_body.rm_padded.rm_align =220ntohl(rmsgp->rm_body.rm_padded.rm_align);221rmsgp->rm_body.rm_padded.rm_thresh =222ntohl(rmsgp->rm_body.rm_padded.rm_thresh);223224va = &rmsgp->rm_body.rm_padded.rm_pempty[4];225rqstp->rq_arg.head[0].iov_base = va;226hdrlen = (u32)((unsigned long)va - (unsigned long)rmsgp);227rqstp->rq_arg.head[0].iov_len -= hdrlen;228if (hdrlen > rqstp->rq_arg.len)229return -EINVAL;230return hdrlen;231}232233/* The chunk list may contain either a read chunk list or a write234* chunk list and a reply chunk list.235*/236va = &rmsgp->rm_body.rm_chunks[0];237vaend = (u32 *)((unsigned long)rmsgp + rqstp->rq_arg.len);238va = decode_read_list(va, vaend);239if (!va)240return -EINVAL;241va = decode_write_list(va, vaend);242if (!va)243return -EINVAL;244va = decode_reply_array(va, vaend);245if (!va)246return -EINVAL;247248rqstp->rq_arg.head[0].iov_base = va;249hdr_len = (unsigned long)va - (unsigned long)rmsgp;250rqstp->rq_arg.head[0].iov_len -= hdr_len;251252*rdma_req = rmsgp;253return hdr_len;254}255256int svc_rdma_xdr_decode_deferred_req(struct svc_rqst *rqstp)257{258struct rpcrdma_msg *rmsgp = NULL;259struct rpcrdma_read_chunk *ch;260struct rpcrdma_write_array *ary;261u32 *va;262u32 hdrlen;263264dprintk("svcrdma: processing deferred RDMA header on rqstp=%p\n",265rqstp);266rmsgp = (struct rpcrdma_msg *)rqstp->rq_arg.head[0].iov_base;267268/* Pull in the extra for the padded case and bump our pointer */269if (rmsgp->rm_type == RDMA_MSGP) {270va = &rmsgp->rm_body.rm_padded.rm_pempty[4];271rqstp->rq_arg.head[0].iov_base = va;272hdrlen = (u32)((unsigned long)va - (unsigned long)rmsgp);273rqstp->rq_arg.head[0].iov_len -= hdrlen;274return hdrlen;275}276277/*278* Skip all chunks to find RPC msg. These were previously processed279*/280va = &rmsgp->rm_body.rm_chunks[0];281282/* Skip read-list */283for (ch = (struct rpcrdma_read_chunk *)va;284ch->rc_discrim != xdr_zero; ch++);285va = (u32 *)&ch->rc_position;286287/* Skip write-list */288ary = (struct rpcrdma_write_array *)va;289if (ary->wc_discrim == xdr_zero)290va = (u32 *)&ary->wc_nchunks;291else292/*293* rs_length is the 2nd 4B field in wc_target and taking its294* address skips the list terminator295*/296va = (u32 *)&ary->wc_array[ary->wc_nchunks].wc_target.rs_length;297298/* Skip reply-array */299ary = (struct rpcrdma_write_array *)va;300if (ary->wc_discrim == xdr_zero)301va = (u32 *)&ary->wc_nchunks;302else303va = (u32 *)&ary->wc_array[ary->wc_nchunks];304305rqstp->rq_arg.head[0].iov_base = va;306hdrlen = (unsigned long)va - (unsigned long)rmsgp;307rqstp->rq_arg.head[0].iov_len -= hdrlen;308309return hdrlen;310}311312int svc_rdma_xdr_encode_error(struct svcxprt_rdma *xprt,313struct rpcrdma_msg *rmsgp,314enum rpcrdma_errcode err, u32 *va)315{316u32 *startp = va;317318*va++ = htonl(rmsgp->rm_xid);319*va++ = htonl(rmsgp->rm_vers);320*va++ = htonl(xprt->sc_max_requests);321*va++ = htonl(RDMA_ERROR);322*va++ = htonl(err);323if (err == ERR_VERS) {324*va++ = htonl(RPCRDMA_VERSION);325*va++ = htonl(RPCRDMA_VERSION);326}327328return (int)((unsigned long)va - (unsigned long)startp);329}330331int svc_rdma_xdr_get_reply_hdr_len(struct rpcrdma_msg *rmsgp)332{333struct rpcrdma_write_array *wr_ary;334335/* There is no read-list in a reply */336337/* skip write list */338wr_ary = (struct rpcrdma_write_array *)339&rmsgp->rm_body.rm_chunks[1];340if (wr_ary->wc_discrim)341wr_ary = (struct rpcrdma_write_array *)342&wr_ary->wc_array[ntohl(wr_ary->wc_nchunks)].343wc_target.rs_length;344else345wr_ary = (struct rpcrdma_write_array *)346&wr_ary->wc_nchunks;347348/* skip reply array */349if (wr_ary->wc_discrim)350wr_ary = (struct rpcrdma_write_array *)351&wr_ary->wc_array[ntohl(wr_ary->wc_nchunks)];352else353wr_ary = (struct rpcrdma_write_array *)354&wr_ary->wc_nchunks;355356return (unsigned long) wr_ary - (unsigned long) rmsgp;357}358359void svc_rdma_xdr_encode_write_list(struct rpcrdma_msg *rmsgp, int chunks)360{361struct rpcrdma_write_array *ary;362363/* no read-list */364rmsgp->rm_body.rm_chunks[0] = xdr_zero;365366/* write-array discrim */367ary = (struct rpcrdma_write_array *)368&rmsgp->rm_body.rm_chunks[1];369ary->wc_discrim = xdr_one;370ary->wc_nchunks = htonl(chunks);371372/* write-list terminator */373ary->wc_array[chunks].wc_target.rs_handle = xdr_zero;374375/* reply-array discriminator */376ary->wc_array[chunks].wc_target.rs_length = xdr_zero;377}378379void svc_rdma_xdr_encode_reply_array(struct rpcrdma_write_array *ary,380int chunks)381{382ary->wc_discrim = xdr_one;383ary->wc_nchunks = htonl(chunks);384}385386void svc_rdma_xdr_encode_array_chunk(struct rpcrdma_write_array *ary,387int chunk_no,388u32 rs_handle, u64 rs_offset,389u32 write_len)390{391struct rpcrdma_segment *seg = &ary->wc_array[chunk_no].wc_target;392seg->rs_handle = htonl(rs_handle);393seg->rs_length = htonl(write_len);394xdr_encode_hyper((u32 *) &seg->rs_offset, rs_offset);395}396397void svc_rdma_xdr_encode_reply_header(struct svcxprt_rdma *xprt,398struct rpcrdma_msg *rdma_argp,399struct rpcrdma_msg *rdma_resp,400enum rpcrdma_proc rdma_type)401{402rdma_resp->rm_xid = htonl(rdma_argp->rm_xid);403rdma_resp->rm_vers = htonl(rdma_argp->rm_vers);404rdma_resp->rm_credit = htonl(xprt->sc_max_requests);405rdma_resp->rm_type = htonl(rdma_type);406407/* Encode <nul> chunks lists */408rdma_resp->rm_body.rm_chunks[0] = xdr_zero;409rdma_resp->rm_body.rm_chunks[1] = xdr_zero;410rdma_resp->rm_body.rm_chunks[2] = xdr_zero;411}412413414