2007-12-13 01:13:27 +03:00
/*
* Copyright ( c ) 2005 - 2006 Network Appliance , Inc . All rights reserved .
*
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the BSD - type
* license below :
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials provided
* with the distribution .
*
* Neither the name of the Network Appliance , Inc . nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* Author : Tom Tucker < tom @ opengridcomputing . com >
*/
# include <linux/sunrpc/xdr.h>
# include <linux/sunrpc/debug.h>
# include <asm/unaligned.h>
# include <linux/sunrpc/rpc_rdma.h>
# include <linux/sunrpc/svc_rdma.h>
# define RPCDBG_FACILITY RPCDBG_SVCXPRT
/*
* Decodes a read chunk list . The expected format is as follows :
* descrim : xdr_one
* position : u32 offset into XDR stream
* handle : u32 RKEY
* . . .
* end - of - list : xdr_zero
*/
static u32 * decode_read_list ( u32 * va , u32 * vaend )
{
struct rpcrdma_read_chunk * ch = ( struct rpcrdma_read_chunk * ) va ;
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 ) ;
return NULL ;
}
ch + + ;
}
return ( u32 * ) & ch - > rc_position ;
}
/*
* Determine number of chunks and total bytes in chunk list . The chunk
* list has already been verified to fit within the RPCRDMA header .
*/
void svc_rdma_rcl_chunk_counts ( struct rpcrdma_read_chunk * ch ,
int * ch_count , int * byte_count )
{
/* compute the number of bytes represented by read chunks */
* byte_count = 0 ;
* ch_count = 0 ;
for ( ; ch - > rc_discrim ! = 0 ; ch + + ) {
2012-02-15 21:30:00 +04:00
* byte_count = * byte_count + ntohl ( ch - > rc_target . rs_length ) ;
2007-12-13 01:13:27 +03:00
* ch_count = * ch_count + 1 ;
}
}
/*
* Decodes a write chunk list . The expected format is as follows :
* descrim : xdr_one
* nchunks : < count >
* handle : u32 RKEY - - - +
* length : u32 < len of segment > |
* offset : remove va + < count >
* . . . |
* - - - +
*/
static u32 * decode_write_list ( u32 * va , u32 * vaend )
{
2012-02-15 21:30:00 +04:00
int nchunks ;
2007-12-13 01:13:27 +03:00
struct rpcrdma_write_array * ary =
( struct rpcrdma_write_array * ) va ;
/* Check for not write-array */
if ( ary - > wc_discrim = = xdr_zero )
return ( u32 * ) & 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 ;
}
2012-02-15 21:30:00 +04:00
nchunks = ntohl ( ary - > wc_nchunks ) ;
2007-12-13 01:13:27 +03:00
if ( ( ( unsigned long ) & ary - > wc_array [ 0 ] +
2012-02-15 21:30:00 +04:00
( sizeof ( struct rpcrdma_write_chunk ) * nchunks ) ) >
2007-12-13 01:13:27 +03:00
( unsigned long ) vaend ) {
dprintk ( " svcrdma: ary=%p, wc_nchunks=%d, vaend=%p \n " ,
2012-02-15 21:30:00 +04:00
ary , nchunks , vaend ) ;
2007-12-13 01:13:27 +03:00
return NULL ;
}
/*
* rs_length is the 2 nd 4 B field in wc_target and taking its
* address skips the list terminator
*/
2012-02-15 21:30:00 +04:00
return ( u32 * ) & ary - > wc_array [ nchunks ] . wc_target . rs_length ;
2007-12-13 01:13:27 +03:00
}
static u32 * decode_reply_array ( u32 * va , u32 * vaend )
{
2012-02-15 21:30:00 +04:00
int nchunks ;
2007-12-13 01:13:27 +03:00
struct rpcrdma_write_array * ary =
( struct rpcrdma_write_array * ) va ;
/* Check for no reply-array */
if ( ary - > wc_discrim = = xdr_zero )
return ( u32 * ) & 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 ;
}
2012-02-15 21:30:00 +04:00
nchunks = ntohl ( ary - > wc_nchunks ) ;
2007-12-13 01:13:27 +03:00
if ( ( ( unsigned long ) & ary - > wc_array [ 0 ] +
2012-02-15 21:30:00 +04:00
( sizeof ( struct rpcrdma_write_chunk ) * nchunks ) ) >
2007-12-13 01:13:27 +03:00
( unsigned long ) vaend ) {
dprintk ( " svcrdma: ary=%p, wc_nchunks=%d, vaend=%p \n " ,
2012-02-15 21:30:00 +04:00
ary , nchunks , vaend ) ;
2007-12-13 01:13:27 +03:00
return NULL ;
}
2012-02-15 21:30:00 +04:00
return ( u32 * ) & ary - > wc_array [ nchunks ] ;
2007-12-13 01:13:27 +03:00
}
int svc_rdma_xdr_decode_req ( struct rpcrdma_msg * * rdma_req ,
struct svc_rqst * rqstp )
{
struct rpcrdma_msg * rmsgp = NULL ;
u32 * va ;
u32 * vaend ;
u32 hdr_len ;
rmsgp = ( struct rpcrdma_msg * ) rqstp - > rq_arg . head [ 0 ] . iov_base ;
/* Verify that there's enough bytes for header + something */
if ( rqstp - > rq_arg . len < = RPCRDMA_HDRLEN_MIN ) {
dprintk ( " svcrdma: header too short = %d \n " ,
rqstp - > rq_arg . len ) ;
return - EINVAL ;
}
/* Decode the header */
rmsgp - > rm_xid = ntohl ( rmsgp - > rm_xid ) ;
rmsgp - > rm_vers = ntohl ( rmsgp - > rm_vers ) ;
rmsgp - > rm_credit = ntohl ( rmsgp - > rm_credit ) ;
rmsgp - > rm_type = ntohl ( rmsgp - > rm_type ) ;
if ( rmsgp - > rm_vers ! = RPCRDMA_VERSION )
return - ENOSYS ;
/* Pull in the extra for the padded case and bump our pointer */
if ( rmsgp - > rm_type = = RDMA_MSGP ) {
int hdrlen ;
rmsgp - > rm_body . rm_padded . rm_align =
ntohl ( rmsgp - > rm_body . rm_padded . rm_align ) ;
rmsgp - > rm_body . rm_padded . rm_thresh =
ntohl ( rmsgp - > rm_body . rm_padded . rm_thresh ) ;
va = & rmsgp - > rm_body . rm_padded . rm_pempty [ 4 ] ;
rqstp - > rq_arg . head [ 0 ] . iov_base = va ;
hdrlen = ( u32 ) ( ( unsigned long ) va - ( unsigned long ) rmsgp ) ;
rqstp - > rq_arg . head [ 0 ] . iov_len - = hdrlen ;
if ( hdrlen > rqstp - > rq_arg . len )
return - EINVAL ;
return hdrlen ;
}
/* 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 = ( u32 * ) ( ( unsigned long ) rmsgp + rqstp - > rq_arg . len ) ;
va = decode_read_list ( va , vaend ) ;
if ( ! va )
return - EINVAL ;
va = decode_write_list ( va , vaend ) ;
if ( ! va )
return - EINVAL ;
va = decode_reply_array ( va , vaend ) ;
if ( ! va )
return - EINVAL ;
rqstp - > rq_arg . head [ 0 ] . iov_base = va ;
hdr_len = ( unsigned long ) va - ( unsigned long ) rmsgp ;
rqstp - > rq_arg . head [ 0 ] . iov_len - = hdr_len ;
* rdma_req = rmsgp ;
return hdr_len ;
}
int svc_rdma_xdr_decode_deferred_req ( struct svc_rqst * rqstp )
{
struct rpcrdma_msg * rmsgp = NULL ;
struct rpcrdma_read_chunk * ch ;
struct rpcrdma_write_array * ary ;
u32 * va ;
u32 hdrlen ;
dprintk ( " svcrdma: processing deferred RDMA header on rqstp=%p \n " ,
rqstp ) ;
rmsgp = ( struct rpcrdma_msg * ) rqstp - > rq_arg . head [ 0 ] . iov_base ;
/* Pull in the extra for the padded case and bump our pointer */
if ( rmsgp - > rm_type = = RDMA_MSGP ) {
va = & rmsgp - > rm_body . rm_padded . rm_pempty [ 4 ] ;
rqstp - > rq_arg . head [ 0 ] . iov_base = va ;
hdrlen = ( u32 ) ( ( unsigned long ) va - ( unsigned long ) rmsgp ) ;
rqstp - > rq_arg . head [ 0 ] . iov_len - = hdrlen ;
return hdrlen ;
}
/*
* Skip all chunks to find RPC msg . These were previously processed
*/
va = & rmsgp - > rm_body . rm_chunks [ 0 ] ;
/* Skip read-list */
for ( ch = ( struct rpcrdma_read_chunk * ) va ;
ch - > rc_discrim ! = xdr_zero ; ch + + ) ;
va = ( u32 * ) & ch - > rc_position ;
/* Skip write-list */
ary = ( struct rpcrdma_write_array * ) va ;
if ( ary - > wc_discrim = = xdr_zero )
va = ( u32 * ) & ary - > wc_nchunks ;
else
/*
* rs_length is the 2 nd 4 B field in wc_target and taking its
* address skips the list terminator
*/
va = ( u32 * ) & ary - > wc_array [ ary - > wc_nchunks ] . wc_target . rs_length ;
/* Skip reply-array */
ary = ( struct rpcrdma_write_array * ) va ;
if ( ary - > wc_discrim = = xdr_zero )
va = ( u32 * ) & ary - > wc_nchunks ;
else
va = ( u32 * ) & ary - > wc_array [ ary - > wc_nchunks ] ;
rqstp - > rq_arg . head [ 0 ] . iov_base = va ;
hdrlen = ( unsigned long ) va - ( unsigned long ) rmsgp ;
rqstp - > rq_arg . head [ 0 ] . iov_len - = hdrlen ;
return hdrlen ;
}
int svc_rdma_xdr_encode_error ( struct svcxprt_rdma * xprt ,
struct rpcrdma_msg * rmsgp ,
enum rpcrdma_errcode err , u32 * va )
{
u32 * startp = va ;
* va + + = htonl ( rmsgp - > rm_xid ) ;
* va + + = htonl ( rmsgp - > rm_vers ) ;
* va + + = htonl ( xprt - > sc_max_requests ) ;
* va + + = htonl ( RDMA_ERROR ) ;
* va + + = htonl ( err ) ;
if ( err = = ERR_VERS ) {
* va + + = htonl ( RPCRDMA_VERSION ) ;
* va + + = htonl ( RPCRDMA_VERSION ) ;
}
return ( int ) ( ( unsigned long ) va - ( unsigned long ) startp ) ;
}
int svc_rdma_xdr_get_reply_hdr_len ( struct rpcrdma_msg * rmsgp )
{
struct rpcrdma_write_array * wr_ary ;
/* There is no read-list in a reply */
/* 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 [ ntohl ( 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 [ ntohl ( wr_ary - > wc_nchunks ) ] ;
else
wr_ary = ( struct rpcrdma_write_array * )
& wr_ary - > wc_nchunks ;
return ( unsigned long ) wr_ary - ( unsigned long ) rmsgp ;
}
void svc_rdma_xdr_encode_write_list ( struct rpcrdma_msg * rmsgp , int chunks )
{
struct rpcrdma_write_array * ary ;
/* no read-list */
rmsgp - > rm_body . rm_chunks [ 0 ] = xdr_zero ;
/* write-array discrim */
ary = ( struct rpcrdma_write_array * )
& rmsgp - > rm_body . rm_chunks [ 1 ] ;
ary - > wc_discrim = xdr_one ;
ary - > wc_nchunks = htonl ( chunks ) ;
/* write-list terminator */
ary - > wc_array [ chunks ] . wc_target . rs_handle = xdr_zero ;
/* reply-array discriminator */
ary - > wc_array [ chunks ] . wc_target . rs_length = xdr_zero ;
}
void svc_rdma_xdr_encode_reply_array ( struct rpcrdma_write_array * ary ,
int chunks )
{
ary - > wc_discrim = xdr_one ;
ary - > wc_nchunks = htonl ( chunks ) ;
}
void svc_rdma_xdr_encode_array_chunk ( struct rpcrdma_write_array * ary ,
int chunk_no ,
2012-02-15 21:30:00 +04:00
__be32 rs_handle ,
__be64 rs_offset ,
2007-12-13 01:13:27 +03:00
u32 write_len )
{
struct rpcrdma_segment * seg = & ary - > wc_array [ chunk_no ] . wc_target ;
2012-02-15 21:30:00 +04:00
seg - > rs_handle = rs_handle ;
seg - > rs_offset = rs_offset ;
2007-12-13 01:13:27 +03:00
seg - > rs_length = htonl ( 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 = htonl ( rdma_argp - > rm_xid ) ;
rdma_resp - > rm_vers = htonl ( rdma_argp - > rm_vers ) ;
rdma_resp - > rm_credit = htonl ( xprt - > sc_max_requests ) ;
rdma_resp - > rm_type = htonl ( 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 ;
}