2007-12-13 01:13:25 +03:00
/*
2014-05-29 00:12:01 +04:00
* Copyright ( c ) 2014 Open Grid Computing , Inc . All rights reserved .
2007-12-13 01:13:25 +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/debug.h>
# include <linux/sunrpc/rpc_rdma.h>
# include <linux/spinlock.h>
# include <asm/unaligned.h>
# include <rdma/ib_verbs.h>
# include <rdma/rdma_cm.h>
# include <linux/sunrpc/svc_rdma.h>
# define RPCDBG_FACILITY RPCDBG_SVCXPRT
2016-03-01 21:05:45 +03:00
static u32 xdr_padsize ( u32 len )
{
return ( len & 3 ) ? ( 4 - ( len & 3 ) ) : 0 ;
}
2016-01-07 22:49:53 +03:00
int svc_rdma_map_xdr ( struct svcxprt_rdma * xprt ,
struct xdr_buf * xdr ,
2016-03-01 21:05:54 +03:00
struct svc_rdma_req_map * vec ,
bool write_chunk_present )
2007-12-13 01:13:25 +03:00
{
int sge_no ;
u32 sge_bytes ;
u32 page_bytes ;
2008-07-02 23:56:13 +04:00
u32 page_off ;
2007-12-13 01:13:25 +03:00
int page_no ;
2015-01-13 19:03:03 +03:00
if ( xdr - > len ! =
( xdr - > head [ 0 ] . iov_len + xdr - > page_len + xdr - > tail [ 0 ] . iov_len ) ) {
2016-01-07 22:49:53 +03:00
pr_err ( " svcrdma: %s: XDR buffer length error \n " , __func__ ) ;
2015-01-13 19:03:03 +03:00
return - EIO ;
}
2008-07-02 23:56:13 +04:00
2007-12-13 01:13:25 +03:00
/* Skip the first sge, this is for the RPCRDMA header */
sge_no = 1 ;
/* Head SGE */
2008-07-02 23:56:13 +04:00
vec - > sge [ sge_no ] . iov_base = xdr - > head [ 0 ] . iov_base ;
vec - > sge [ sge_no ] . iov_len = xdr - > head [ 0 ] . iov_len ;
2007-12-13 01:13:25 +03:00
sge_no + + ;
/* pages SGE */
page_no = 0 ;
page_bytes = xdr - > page_len ;
page_off = xdr - > page_base ;
2008-07-02 23:56:13 +04:00
while ( page_bytes ) {
vec - > sge [ sge_no ] . iov_base =
page_address ( xdr - > pages [ page_no ] ) + page_off ;
sge_bytes = min_t ( u32 , page_bytes , ( PAGE_SIZE - page_off ) ) ;
2007-12-13 01:13:25 +03:00
page_bytes - = sge_bytes ;
2008-07-02 23:56:13 +04:00
vec - > sge [ sge_no ] . iov_len = sge_bytes ;
2007-12-13 01:13:25 +03:00
sge_no + + ;
page_no + + ;
page_off = 0 ; /* reset for next time through loop */
}
/* Tail SGE */
2008-07-02 23:56:13 +04:00
if ( xdr - > tail [ 0 ] . iov_len ) {
2016-03-01 21:05:54 +03:00
unsigned char * base = xdr - > tail [ 0 ] . iov_base ;
size_t len = xdr - > tail [ 0 ] . iov_len ;
u32 xdr_pad = xdr_padsize ( xdr - > page_len ) ;
if ( write_chunk_present & & xdr_pad ) {
base + = xdr_pad ;
len - = xdr_pad ;
}
if ( len ) {
vec - > sge [ sge_no ] . iov_base = base ;
vec - > sge [ sge_no ] . iov_len = len ;
sge_no + + ;
}
2007-12-13 01:13:25 +03:00
}
2016-01-07 22:49:53 +03:00
dprintk ( " svcrdma: %s: sge_no %d page_no %d "
2009-03-13 05:21:21 +03:00
" page_base %u page_len %u head_len %zu tail_len %zu \n " ,
2016-01-07 22:49:53 +03:00
__func__ , sge_no , page_no , xdr - > page_base , xdr - > page_len ,
2009-03-11 21:37:55 +03:00
xdr - > head [ 0 ] . iov_len , xdr - > tail [ 0 ] . iov_len ) ;
2008-07-02 23:56:13 +04:00
vec - > count = sge_no ;
2008-10-04 00:45:03 +04:00
return 0 ;
2007-12-13 01:13:25 +03:00
}
2010-10-13 00:33:52 +04:00
static dma_addr_t dma_map_xdr ( struct svcxprt_rdma * xprt ,
struct xdr_buf * xdr ,
u32 xdr_off , size_t len , int dir )
{
struct page * page ;
dma_addr_t dma_addr ;
if ( xdr_off < xdr - > head [ 0 ] . iov_len ) {
/* This offset is in the head */
xdr_off + = ( unsigned long ) xdr - > head [ 0 ] . iov_base & ~ PAGE_MASK ;
page = virt_to_page ( xdr - > head [ 0 ] . iov_base ) ;
} else {
xdr_off - = xdr - > head [ 0 ] . iov_len ;
if ( xdr_off < xdr - > page_len ) {
/* This offset is in the page list */
2014-03-17 21:10:05 +04:00
xdr_off + = xdr - > page_base ;
2010-10-13 00:33:52 +04:00
page = xdr - > pages [ xdr_off > > PAGE_SHIFT ] ;
xdr_off & = ~ PAGE_MASK ;
} else {
/* This offset is in the tail */
xdr_off - = xdr - > page_len ;
xdr_off + = ( unsigned long )
xdr - > tail [ 0 ] . iov_base & ~ PAGE_MASK ;
page = virt_to_page ( xdr - > tail [ 0 ] . iov_base ) ;
}
}
dma_addr = ib_dma_map_page ( xprt - > sc_cm_id - > device , page , xdr_off ,
min_t ( size_t , PAGE_SIZE , len ) , dir ) ;
return dma_addr ;
}
2016-11-29 19:04:42 +03:00
/* Parse the RPC Call's transport header.
2015-07-09 23:45:28 +03:00
*/
2016-11-29 19:04:42 +03:00
static void svc_rdma_get_write_arrays ( struct rpcrdma_msg * rmsgp ,
struct rpcrdma_write_array * * write ,
struct rpcrdma_write_array * * reply )
2015-07-09 23:45:28 +03:00
{
2016-11-29 19:04:42 +03:00
__be32 * p ;
2015-07-09 23:45:28 +03:00
2016-11-29 19:04:42 +03:00
p = ( __be32 * ) & rmsgp - > rm_body . rm_chunks [ 0 ] ;
2015-07-09 23:45:28 +03:00
2016-11-29 19:04:42 +03:00
/* Read list */
while ( * p + + ! = xdr_zero )
p + = 5 ;
2015-07-09 23:45:28 +03:00
2016-11-29 19:04:42 +03:00
/* Write list */
if ( * p ! = xdr_zero ) {
* write = ( struct rpcrdma_write_array * ) p ;
while ( * p + + ! = xdr_zero )
p + = 1 + be32_to_cpu ( * p ) * 4 ;
} else {
* write = NULL ;
p + + ;
2015-07-09 23:45:28 +03:00
}
2016-11-29 19:04:42 +03:00
/* Reply chunk */
if ( * p ! = xdr_zero )
* reply = ( struct rpcrdma_write_array * ) p ;
else
* reply = NULL ;
2015-07-09 23:45:28 +03:00
}
2016-09-13 17:53:23 +03:00
/* RPC-over-RDMA Version One private extension: Remote Invalidation.
* Responder ' s choice : requester signals it can handle Send With
* Invalidate , and responder chooses one rkey to invalidate .
*
* Find a candidate rkey to invalidate when sending a reply . Picks the
2017-04-09 20:06:08 +03:00
* first R_key it finds in the chunk lists .
2016-09-13 17:53:23 +03:00
*
* Returns zero if RPC ' s chunk lists are empty .
*/
2017-04-09 20:06:08 +03:00
static u32 svc_rdma_get_inv_rkey ( __be32 * rdma_argp ,
__be32 * wr_lst , __be32 * rp_ch )
2016-09-13 17:53:23 +03:00
{
2017-04-09 20:06:08 +03:00
__be32 * p ;
2016-09-13 17:53:23 +03:00
2017-04-09 20:06:08 +03:00
p = rdma_argp + rpcrdma_fixed_maxsz ;
if ( * p ! = xdr_zero )
p + = 2 ;
else if ( wr_lst & & be32_to_cpup ( wr_lst + 1 ) )
p = wr_lst + 2 ;
else if ( rp_ch & & be32_to_cpup ( rp_ch + 1 ) )
p = rp_ch + 2 ;
else
return 0 ;
return be32_to_cpup ( p ) ;
2016-09-13 17:53:23 +03:00
}
2017-04-09 20:05:44 +03:00
static int svc_rdma_dma_map_page ( struct svcxprt_rdma * rdma ,
struct svc_rdma_op_ctxt * ctxt ,
unsigned int sge_no ,
struct page * page ,
unsigned int offset ,
unsigned int len )
{
struct ib_device * dev = rdma - > sc_cm_id - > device ;
dma_addr_t dma_addr ;
dma_addr = ib_dma_map_page ( dev , page , offset , len , DMA_TO_DEVICE ) ;
if ( ib_dma_mapping_error ( dev , dma_addr ) )
return - EIO ;
ctxt - > sge [ sge_no ] . addr = dma_addr ;
ctxt - > sge [ sge_no ] . length = len ;
ctxt - > sge [ sge_no ] . lkey = rdma - > sc_pd - > local_dma_lkey ;
svc_rdma_count_mappings ( rdma , ctxt ) ;
return 0 ;
}
/**
* svc_rdma_map_reply_hdr - DMA map the transport header buffer
* @ rdma : controlling transport
* @ ctxt : op_ctxt for the Send WR
* @ rdma_resp : buffer containing transport header
* @ len : length of transport header
*
* Returns :
* % 0 if the header is DMA mapped ,
* % - EIO if DMA mapping failed .
*/
int svc_rdma_map_reply_hdr ( struct svcxprt_rdma * rdma ,
struct svc_rdma_op_ctxt * ctxt ,
__be32 * rdma_resp ,
unsigned int len )
{
ctxt - > direction = DMA_TO_DEVICE ;
ctxt - > pages [ 0 ] = virt_to_page ( rdma_resp ) ;
ctxt - > count = 1 ;
return svc_rdma_dma_map_page ( rdma , ctxt , 0 , ctxt - > pages [ 0 ] , 0 , len ) ;
}
2007-12-13 01:13:25 +03:00
/* Assumptions:
* - The specified write_len can be represented in sc_max_sge * PAGE_SIZE
*/
static int send_write ( struct svcxprt_rdma * xprt , struct svc_rqst * rqstp ,
u32 rmr , u64 to ,
u32 xdr_off , int write_len ,
2008-07-02 23:56:13 +04:00
struct svc_rdma_req_map * vec )
2007-12-13 01:13:25 +03:00
{
2015-10-08 11:16:33 +03:00
struct ib_rdma_wr write_wr ;
2007-12-13 01:13:25 +03:00
struct ib_sge * sge ;
int xdr_sge_no ;
int sge_no ;
int sge_bytes ;
int sge_off ;
int bc ;
struct svc_rdma_op_ctxt * ctxt ;
2015-01-13 19:03:03 +03:00
if ( vec - > count > RPCSVC_MAXPAGES ) {
pr_err ( " svcrdma: Too many pages (%lu) \n " , vec - > count ) ;
return - EIO ;
}
2007-12-13 01:13:25 +03:00
dprintk ( " svcrdma: RDMA_WRITE rmr=%x, to=%llx, xdr_off=%d, "
2008-07-02 23:56:13 +04:00
" write_len=%d, vec->sge=%p, vec->count=%lu \n " ,
2008-02-09 03:02:04 +03:00
rmr , ( unsigned long long ) to , xdr_off ,
2008-07-02 23:56:13 +04:00
write_len , vec - > sge , vec - > count ) ;
2007-12-13 01:13:25 +03:00
ctxt = svc_rdma_get_context ( xprt ) ;
2008-07-02 23:56:13 +04:00
ctxt - > direction = DMA_TO_DEVICE ;
sge = ctxt - > sge ;
2007-12-13 01:13:25 +03:00
/* Find the SGE associated with xdr_off */
2008-07-02 23:56:13 +04:00
for ( bc = xdr_off , xdr_sge_no = 1 ; bc & & xdr_sge_no < vec - > count ;
2007-12-13 01:13:25 +03:00
xdr_sge_no + + ) {
2008-07-02 23:56:13 +04:00
if ( vec - > sge [ xdr_sge_no ] . iov_len > bc )
2007-12-13 01:13:25 +03:00
break ;
2008-07-02 23:56:13 +04:00
bc - = vec - > sge [ xdr_sge_no ] . iov_len ;
2007-12-13 01:13:25 +03:00
}
sge_off = bc ;
bc = write_len ;
sge_no = 0 ;
/* Copy the remaining SGE */
2008-10-04 00:45:03 +04:00
while ( bc ! = 0 ) {
sge_bytes = min_t ( size_t ,
bc , vec - > sge [ xdr_sge_no ] . iov_len - sge_off ) ;
2007-12-13 01:13:25 +03:00
sge [ sge_no ] . length = sge_bytes ;
2014-05-29 00:12:01 +04:00
sge [ sge_no ] . addr =
dma_map_xdr ( xprt , & rqstp - > rq_res , xdr_off ,
sge_bytes , DMA_TO_DEVICE ) ;
xdr_off + = sge_bytes ;
if ( ib_dma_mapping_error ( xprt - > sc_cm_id - > device ,
sge [ sge_no ] . addr ) )
goto err ;
2016-09-13 17:52:50 +03:00
svc_rdma_count_mappings ( xprt , ctxt ) ;
2016-01-08 10:53:41 +03:00
sge [ sge_no ] . lkey = xprt - > sc_pd - > local_dma_lkey ;
2008-10-04 00:45:03 +04:00
ctxt - > count + + ;
2007-12-13 01:13:25 +03:00
sge_off = 0 ;
sge_no + + ;
xdr_sge_no + + ;
2015-01-13 19:03:03 +03:00
if ( xdr_sge_no > vec - > count ) {
pr_err ( " svcrdma: Too many sges (%d) \n " , xdr_sge_no ) ;
goto err ;
}
2007-12-13 01:13:25 +03:00
bc - = sge_bytes ;
2014-07-09 22:49:15 +04:00
if ( sge_no = = xprt - > sc_max_sge )
break ;
2007-12-13 01:13:25 +03:00
}
/* Prepare WRITE WR */
memset ( & write_wr , 0 , sizeof write_wr ) ;
2016-03-01 21:07:22 +03:00
ctxt - > cqe . done = svc_rdma_wc_write ;
write_wr . wr . wr_cqe = & ctxt - > cqe ;
2015-10-08 11:16:33 +03:00
write_wr . wr . sg_list = & sge [ 0 ] ;
write_wr . wr . num_sge = sge_no ;
write_wr . wr . opcode = IB_WR_RDMA_WRITE ;
write_wr . wr . send_flags = IB_SEND_SIGNALED ;
write_wr . rkey = rmr ;
write_wr . remote_addr = to ;
2007-12-13 01:13:25 +03:00
/* Post It */
atomic_inc ( & rdma_stat_write ) ;
2015-10-08 11:16:33 +03:00
if ( svc_rdma_send ( xprt , & write_wr . wr ) )
2008-07-02 23:56:13 +04:00
goto err ;
2014-07-09 22:49:15 +04:00
return write_len - bc ;
2008-07-02 23:56:13 +04:00
err :
2010-10-13 00:33:57 +04:00
svc_rdma_unmap_dma ( ctxt ) ;
2008-07-02 23:56:13 +04:00
svc_rdma_put_context ( ctxt , 0 ) ;
return - EIO ;
2007-12-13 01:13:25 +03:00
}
2016-03-01 21:05:36 +03:00
noinline
2007-12-13 01:13:25 +03:00
static int send_write_chunks ( struct svcxprt_rdma * xprt ,
2016-03-01 21:05:36 +03:00
struct rpcrdma_write_array * wr_ary ,
2007-12-13 01:13:25 +03:00
struct rpcrdma_msg * rdma_resp ,
struct svc_rqst * rqstp ,
2008-07-02 23:56:13 +04:00
struct svc_rdma_req_map * vec )
2007-12-13 01:13:25 +03:00
{
2016-03-01 21:05:45 +03:00
u32 xfer_len = rqstp - > rq_res . page_len ;
2007-12-13 01:13:25 +03:00
int write_len ;
u32 xdr_off ;
int chunk_off ;
int chunk_no ;
2015-06-04 18:20:39 +03:00
int nchunks ;
2007-12-13 01:13:25 +03:00
struct rpcrdma_write_array * res_ary ;
int ret ;
res_ary = ( struct rpcrdma_write_array * )
& rdma_resp - > rm_body . rm_chunks [ 1 ] ;
/* Write chunks start at the pagelist */
2016-03-01 21:05:36 +03:00
nchunks = be32_to_cpu ( wr_ary - > wc_nchunks ) ;
2007-12-13 01:13:25 +03:00
for ( xdr_off = rqstp - > rq_res . head [ 0 ] . iov_len , chunk_no = 0 ;
2015-06-04 18:20:39 +03:00
xfer_len & & chunk_no < nchunks ;
2007-12-13 01:13:25 +03:00
chunk_no + + ) {
struct rpcrdma_segment * arg_ch ;
u64 rs_offset ;
2016-03-01 21:05:36 +03:00
arg_ch = & wr_ary - > wc_array [ chunk_no ] . wc_target ;
2015-06-04 18:20:39 +03:00
write_len = min ( xfer_len , be32_to_cpu ( arg_ch - > rs_length ) ) ;
2007-12-13 01:13:25 +03:00
/* Prepare the response chunk given the length actually
* written */
2012-02-15 21:30:00 +04:00
xdr_decode_hyper ( ( __be32 * ) & arg_ch - > rs_offset , & rs_offset ) ;
2007-12-13 01:13:25 +03:00
svc_rdma_xdr_encode_array_chunk ( res_ary , chunk_no ,
2012-02-15 21:30:00 +04:00
arg_ch - > rs_handle ,
arg_ch - > rs_offset ,
write_len ) ;
2007-12-13 01:13:25 +03:00
chunk_off = 0 ;
while ( write_len ) {
ret = send_write ( xprt , rqstp ,
2015-06-04 18:20:39 +03:00
be32_to_cpu ( arg_ch - > rs_handle ) ,
2007-12-13 01:13:25 +03:00
rs_offset + chunk_off ,
xdr_off ,
2014-07-09 22:49:15 +04:00
write_len ,
2008-07-02 23:56:13 +04:00
vec ) ;
2016-03-01 21:05:36 +03:00
if ( ret < = 0 )
goto out_err ;
2014-07-09 22:49:15 +04:00
chunk_off + = ret ;
xdr_off + = ret ;
xfer_len - = ret ;
write_len - = ret ;
2007-12-13 01:13:25 +03:00
}
}
/* Update the req with the number of chunks actually used */
svc_rdma_xdr_encode_write_list ( rdma_resp , chunk_no ) ;
2016-03-01 21:05:45 +03:00
return rqstp - > rq_res . page_len ;
2016-03-01 21:05:36 +03:00
out_err :
pr_err ( " svcrdma: failed to send write chunks, rc=%d \n " , ret ) ;
return - EIO ;
2007-12-13 01:13:25 +03:00
}
2016-03-01 21:05:36 +03:00
noinline
2007-12-13 01:13:25 +03:00
static int send_reply_chunks ( struct svcxprt_rdma * xprt ,
2016-03-01 21:05:36 +03:00
struct rpcrdma_write_array * rp_ary ,
2007-12-13 01:13:25 +03:00
struct rpcrdma_msg * rdma_resp ,
struct svc_rqst * rqstp ,
2008-07-02 23:56:13 +04:00
struct svc_rdma_req_map * vec )
2007-12-13 01:13:25 +03:00
{
u32 xfer_len = rqstp - > rq_res . len ;
int write_len ;
u32 xdr_off ;
int chunk_no ;
int chunk_off ;
2012-02-15 21:30:00 +04:00
int nchunks ;
2007-12-13 01:13:25 +03:00
struct rpcrdma_segment * ch ;
struct rpcrdma_write_array * res_ary ;
int ret ;
/* XXX: need to fix when reply lists occur with read-list and or
* write - list */
res_ary = ( struct rpcrdma_write_array * )
& rdma_resp - > rm_body . rm_chunks [ 2 ] ;
/* xdr offset starts at RPC message */
2016-03-01 21:05:36 +03:00
nchunks = be32_to_cpu ( rp_ary - > wc_nchunks ) ;
2007-12-13 01:13:25 +03:00
for ( xdr_off = 0 , chunk_no = 0 ;
2012-02-15 21:30:00 +04:00
xfer_len & & chunk_no < nchunks ;
2007-12-13 01:13:25 +03:00
chunk_no + + ) {
u64 rs_offset ;
2016-03-01 21:05:36 +03:00
ch = & rp_ary - > wc_array [ chunk_no ] . wc_target ;
2015-06-04 18:20:39 +03:00
write_len = min ( xfer_len , be32_to_cpu ( ch - > rs_length ) ) ;
2007-12-13 01:13:25 +03:00
/* Prepare the reply chunk given the length actually
* written */
2012-02-15 21:30:00 +04:00
xdr_decode_hyper ( ( __be32 * ) & ch - > rs_offset , & rs_offset ) ;
2007-12-13 01:13:25 +03:00
svc_rdma_xdr_encode_array_chunk ( res_ary , chunk_no ,
2012-02-15 21:30:00 +04:00
ch - > rs_handle , ch - > rs_offset ,
write_len ) ;
2007-12-13 01:13:25 +03:00
chunk_off = 0 ;
while ( write_len ) {
ret = send_write ( xprt , rqstp ,
2015-06-04 18:20:39 +03:00
be32_to_cpu ( ch - > rs_handle ) ,
2007-12-13 01:13:25 +03:00
rs_offset + chunk_off ,
xdr_off ,
2014-07-09 22:49:15 +04:00
write_len ,
2008-07-02 23:56:13 +04:00
vec ) ;
2016-03-01 21:05:36 +03:00
if ( ret < = 0 )
goto out_err ;
2014-07-09 22:49:15 +04:00
chunk_off + = ret ;
xdr_off + = ret ;
xfer_len - = ret ;
write_len - = ret ;
2007-12-13 01:13:25 +03:00
}
}
/* Update the req with the number of chunks actually used */
svc_rdma_xdr_encode_reply_array ( res_ary , chunk_no ) ;
return rqstp - > rq_res . len ;
2016-03-01 21:05:36 +03:00
out_err :
pr_err ( " svcrdma: failed to send reply chunks, rc=%d \n " , ret ) ;
return - EIO ;
2007-12-13 01:13:25 +03:00
}
2017-04-09 20:06:00 +03:00
/* The svc_rqst and all resources it owns are released as soon as
* svc_rdma_sendto returns . Transfer pages under I / O to the ctxt
* so they are released by the Send completion handler .
*/
static void svc_rdma_save_io_pages ( struct svc_rqst * rqstp ,
struct svc_rdma_op_ctxt * ctxt )
{
int i , pages = rqstp - > rq_next_page - rqstp - > rq_respages ;
ctxt - > count + = pages ;
for ( i = 0 ; i < pages ; i + + ) {
ctxt - > pages [ i + 1 ] = rqstp - > rq_respages [ i ] ;
rqstp - > rq_respages [ i ] = NULL ;
}
rqstp - > rq_next_page = rqstp - > rq_respages + 1 ;
}
2017-04-09 20:05:36 +03:00
/**
* svc_rdma_post_send_wr - Set up and post one Send Work Request
* @ rdma : controlling transport
* @ ctxt : op_ctxt for transmitting the Send WR
* @ num_sge : number of SGEs to send
* @ inv_rkey : R_key argument to Send With Invalidate , or zero
*
* Returns :
* % 0 if the Send * was posted successfully ,
* % - ENOTCONN if the connection was lost or dropped ,
* % - EINVAL if there was a problem with the Send we built ,
* % - ENOMEM if ib_post_send failed .
*/
int svc_rdma_post_send_wr ( struct svcxprt_rdma * rdma ,
struct svc_rdma_op_ctxt * ctxt , int num_sge ,
u32 inv_rkey )
{
struct ib_send_wr * send_wr = & ctxt - > send_wr ;
dprintk ( " svcrdma: posting Send WR with %u sge(s) \n " , num_sge ) ;
send_wr - > next = NULL ;
ctxt - > cqe . done = svc_rdma_wc_send ;
send_wr - > wr_cqe = & ctxt - > cqe ;
send_wr - > sg_list = ctxt - > sge ;
send_wr - > num_sge = num_sge ;
send_wr - > send_flags = IB_SEND_SIGNALED ;
if ( inv_rkey ) {
send_wr - > opcode = IB_WR_SEND_WITH_INV ;
send_wr - > ex . invalidate_rkey = inv_rkey ;
} else {
send_wr - > opcode = IB_WR_SEND ;
}
return svc_rdma_send ( rdma , send_wr ) ;
}
2007-12-13 01:13:25 +03:00
/* This function prepares the portion of the RPCRDMA message to be
* sent in the RDMA_SEND . This function is called after data sent via
* RDMA has already been transmitted . There are three cases :
* - The RPCRDMA header , RPC header , and payload are all sent in a
* single RDMA_SEND . This is the " inline " case .
* - The RPCRDMA header and some portion of the RPC header and data
* are sent via this RDMA_SEND and another portion of the data is
* sent via RDMA .
* - The RPCRDMA header [ NOMSG ] is sent in this RDMA_SEND and the RPC
* header and data are all transmitted via RDMA .
* In all three cases , this function prepares the RPCRDMA header in
* sge [ 0 ] , the ' type ' parameter indicates the type to place in the
* RPCRDMA header , and the ' byte_count ' field indicates how much of
2010-10-13 00:33:52 +04:00
* the XDR to include in this RDMA_SEND . NB : The offset of the payload
* to send is zero in the XDR .
2007-12-13 01:13:25 +03:00
*/
static int send_reply ( struct svcxprt_rdma * rdma ,
struct svc_rqst * rqstp ,
struct page * page ,
struct rpcrdma_msg * rdma_resp ,
2008-07-02 23:56:13 +04:00
struct svc_rdma_req_map * vec ,
2016-09-13 17:53:23 +03:00
int byte_count ,
u32 inv_rkey )
2007-12-13 01:13:25 +03:00
{
2016-05-04 17:53:05 +03:00
struct svc_rdma_op_ctxt * ctxt ;
2015-07-09 23:45:18 +03:00
u32 xdr_off ;
2007-12-13 01:13:25 +03:00
int sge_no ;
int sge_bytes ;
2016-05-04 17:53:05 +03:00
int ret = - EIO ;
2008-04-24 01:49:54 +04:00
2007-12-13 01:13:25 +03:00
/* Prepare the context */
2016-05-04 17:53:05 +03:00
ctxt = svc_rdma_get_context ( rdma ) ;
ctxt - > direction = DMA_TO_DEVICE ;
2007-12-13 01:13:25 +03:00
ctxt - > pages [ 0 ] = page ;
ctxt - > count = 1 ;
/* Prepare the SGE for the RPCRDMA Header */
2016-01-08 10:53:41 +03:00
ctxt - > sge [ 0 ] . lkey = rdma - > sc_pd - > local_dma_lkey ;
2017-02-07 19:58:15 +03:00
ctxt - > sge [ 0 ] . length =
svc_rdma_xdr_get_reply_hdr_len ( ( __be32 * ) rdma_resp ) ;
2007-12-13 01:13:25 +03:00
ctxt - > sge [ 0 ] . addr =
2010-10-13 00:33:52 +04:00
ib_dma_map_page ( rdma - > sc_cm_id - > device , page , 0 ,
ctxt - > sge [ 0 ] . length , DMA_TO_DEVICE ) ;
2008-10-04 00:45:03 +04:00
if ( ib_dma_mapping_error ( rdma - > sc_cm_id - > device , ctxt - > sge [ 0 ] . addr ) )
goto err ;
2016-09-13 17:52:50 +03:00
svc_rdma_count_mappings ( rdma , ctxt ) ;
2008-10-04 00:45:03 +04:00
2007-12-13 01:13:25 +03:00
ctxt - > direction = DMA_TO_DEVICE ;
2008-10-04 00:45:03 +04:00
2010-10-13 00:33:52 +04:00
/* Map the payload indicated by 'byte_count' */
2015-07-09 23:45:18 +03:00
xdr_off = 0 ;
2008-07-02 23:56:13 +04:00
for ( sge_no = 1 ; byte_count & & sge_no < vec - > count ; sge_no + + ) {
sge_bytes = min_t ( size_t , vec - > sge [ sge_no ] . iov_len , byte_count ) ;
2007-12-13 01:13:25 +03:00
byte_count - = sge_bytes ;
2014-05-29 00:12:01 +04:00
ctxt - > sge [ sge_no ] . addr =
dma_map_xdr ( rdma , & rqstp - > rq_res , xdr_off ,
sge_bytes , DMA_TO_DEVICE ) ;
xdr_off + = sge_bytes ;
if ( ib_dma_mapping_error ( rdma - > sc_cm_id - > device ,
ctxt - > sge [ sge_no ] . addr ) )
goto err ;
2016-09-13 17:52:50 +03:00
svc_rdma_count_mappings ( rdma , ctxt ) ;
2016-01-08 10:53:41 +03:00
ctxt - > sge [ sge_no ] . lkey = rdma - > sc_pd - > local_dma_lkey ;
2008-07-02 23:56:13 +04:00
ctxt - > sge [ sge_no ] . length = sge_bytes ;
2007-12-13 01:13:25 +03:00
}
2015-01-13 19:03:03 +03:00
if ( byte_count ! = 0 ) {
pr_err ( " svcrdma: Could not map %d bytes \n " , byte_count ) ;
goto err ;
}
2007-12-13 01:13:25 +03:00
2017-04-09 20:06:00 +03:00
svc_rdma_save_io_pages ( rqstp , ctxt ) ;
2014-05-29 00:12:01 +04:00
2015-01-13 19:03:03 +03:00
if ( sge_no > rdma - > sc_max_sge ) {
pr_err ( " svcrdma: Too many sges (%d) \n " , sge_no ) ;
goto err ;
}
2007-12-13 01:13:25 +03:00
2017-04-09 20:05:36 +03:00
ret = svc_rdma_post_send_wr ( rdma , ctxt , sge_no , inv_rkey ) ;
2007-12-13 01:13:25 +03:00
if ( ret )
2008-10-04 00:45:03 +04:00
goto err ;
2007-12-13 01:13:25 +03:00
2008-10-04 00:45:03 +04:00
return 0 ;
err :
2009-04-29 23:14:00 +04:00
svc_rdma_unmap_dma ( ctxt ) ;
2008-10-04 00:45:03 +04:00
svc_rdma_put_context ( ctxt , 1 ) ;
2016-05-04 17:53:05 +03:00
return ret ;
2007-12-13 01:13:25 +03:00
}
void svc_rdma_prep_reply_hdr ( struct svc_rqst * rqstp )
{
}
int svc_rdma_sendto ( struct svc_rqst * rqstp )
{
struct svc_xprt * xprt = rqstp - > rq_xprt ;
struct svcxprt_rdma * rdma =
container_of ( xprt , struct svcxprt_rdma , sc_xprt ) ;
struct rpcrdma_msg * rdma_argp ;
struct rpcrdma_msg * rdma_resp ;
2016-03-01 21:05:36 +03:00
struct rpcrdma_write_array * wr_ary , * rp_ary ;
2007-12-13 01:13:25 +03:00
int ret ;
int inline_bytes ;
struct page * res_page ;
2008-07-02 23:56:13 +04:00
struct svc_rdma_req_map * vec ;
2016-09-13 17:53:23 +03:00
u32 inv_rkey ;
2017-02-07 19:58:23 +03:00
__be32 * p ;
2007-12-13 01:13:25 +03:00
dprintk ( " svcrdma: sending response for rqstp=%p \n " , rqstp ) ;
2015-01-13 19:03:11 +03:00
/* Get the RDMA request header. The receive logic always
* places this at the start of page 0.
*/
rdma_argp = page_address ( rqstp - > rq_pages [ 0 ] ) ;
2016-11-29 19:04:42 +03:00
svc_rdma_get_write_arrays ( rdma_argp , & wr_ary , & rp_ary ) ;
2007-12-13 01:13:25 +03:00
2016-09-13 17:53:23 +03:00
inv_rkey = 0 ;
if ( rdma - > sc_snd_w_inv )
2017-04-09 20:06:08 +03:00
inv_rkey = svc_rdma_get_inv_rkey ( & rdma_argp - > rm_xid ,
( __be32 * ) wr_ary ,
( __be32 * ) rp_ary ) ;
2016-09-13 17:53:23 +03:00
2008-07-02 23:56:13 +04:00
/* Build an req vec for the XDR */
2016-01-07 22:49:20 +03:00
vec = svc_rdma_get_req_map ( rdma ) ;
2016-03-01 21:05:54 +03:00
ret = svc_rdma_map_xdr ( rdma , & rqstp - > rq_res , vec , wr_ary ! = NULL ) ;
2008-10-04 00:45:03 +04:00
if ( ret )
goto err0 ;
2007-12-13 01:13:25 +03:00
inline_bytes = rqstp - > rq_res . len ;
2016-11-29 19:04:50 +03:00
/* Create the RDMA response header. xprt->xpt_mutex,
* acquired in svc_send ( ) , serializes RPC replies . The
* code path below that inserts the credit grant value
* into each transport header runs only inside this
* critical section .
*/
2016-01-07 22:49:45 +03:00
ret = - ENOMEM ;
res_page = alloc_page ( GFP_KERNEL ) ;
if ( ! res_page )
goto err0 ;
2007-12-13 01:13:25 +03:00
rdma_resp = page_address ( res_page ) ;
2017-02-07 19:58:23 +03:00
p = & rdma_resp - > rm_xid ;
* p + + = rdma_argp - > rm_xid ;
* p + + = rdma_argp - > rm_vers ;
* p + + = rdma - > sc_fc_credits ;
* p + + = rp_ary ? rdma_nomsg : rdma_msg ;
/* Start with empty chunks */
* p + + = xdr_zero ;
* p + + = xdr_zero ;
* p = xdr_zero ;
2007-12-13 01:13:25 +03:00
/* Send any write-chunk data and build resp write-list */
2016-03-01 21:05:36 +03:00
if ( wr_ary ) {
ret = send_write_chunks ( rdma , wr_ary , rdma_resp , rqstp , vec ) ;
if ( ret < 0 )
goto err1 ;
2016-03-01 21:05:45 +03:00
inline_bytes - = ret + xdr_padsize ( ret ) ;
2007-12-13 01:13:25 +03:00
}
/* Send any reply-list data and update resp reply-list */
2016-03-01 21:05:36 +03:00
if ( rp_ary ) {
ret = send_reply_chunks ( rdma , rp_ary , rdma_resp , rqstp , vec ) ;
if ( ret < 0 )
goto err1 ;
inline_bytes - = ret ;
2007-12-13 01:13:25 +03:00
}
2016-05-04 17:53:05 +03:00
/* Post a fresh Receive buffer _before_ sending the reply */
ret = svc_rdma_post_recv ( rdma , GFP_KERNEL ) ;
if ( ret )
goto err1 ;
ret = send_reply ( rdma , rqstp , res_page , rdma_resp , vec ,
2016-09-13 17:53:23 +03:00
inline_bytes , inv_rkey ) ;
2016-03-01 21:06:11 +03:00
if ( ret < 0 )
2016-09-13 17:52:59 +03:00
goto err0 ;
2016-03-01 21:06:11 +03:00
2016-01-07 22:49:20 +03:00
svc_rdma_put_req_map ( rdma , vec ) ;
2007-12-13 01:13:25 +03:00
dprintk ( " svcrdma: send_reply returns %d \n " , ret ) ;
return ret ;
2008-10-04 00:45:03 +04:00
err1 :
put_page ( res_page ) ;
err0 :
2016-01-07 22:49:20 +03:00
svc_rdma_put_req_map ( rdma , vec ) ;
2016-05-04 17:53:05 +03:00
pr_err ( " svcrdma: Could not send reply, err=%d. Closing transport. \n " ,
ret ) ;
2016-03-01 21:06:11 +03:00
set_bit ( XPT_CLOSE , & rdma - > sc_xprt . xpt_flags ) ;
return - ENOTCONN ;
2007-12-13 01:13:25 +03:00
}
2016-03-01 21:06:38 +03:00
void svc_rdma_send_error ( struct svcxprt_rdma * xprt , struct rpcrdma_msg * rmsgp ,
int status )
{
struct page * p ;
struct svc_rdma_op_ctxt * ctxt ;
enum rpcrdma_errcode err ;
__be32 * va ;
int length ;
int ret ;
ret = svc_rdma_repost_recv ( xprt , GFP_KERNEL ) ;
if ( ret )
return ;
p = alloc_page ( GFP_KERNEL ) ;
if ( ! p )
return ;
va = page_address ( p ) ;
/* XDR encode an error reply */
err = ERR_CHUNK ;
if ( status = = - EPROTONOSUPPORT )
err = ERR_VERS ;
length = svc_rdma_xdr_encode_error ( xprt , rmsgp , err , va ) ;
2017-04-09 20:05:44 +03:00
/* Map transport header; no RPC message payload */
2016-03-01 21:06:38 +03:00
ctxt = svc_rdma_get_context ( xprt ) ;
2017-04-09 20:05:44 +03:00
ret = svc_rdma_map_reply_hdr ( xprt , ctxt , & rmsgp - > rm_xid , length ) ;
if ( ret ) {
dprintk ( " svcrdma: Error %d mapping send for protocol error \n " ,
ret ) ;
2016-03-01 21:06:38 +03:00
return ;
}
2017-04-09 20:05:36 +03:00
ret = svc_rdma_post_send_wr ( xprt , ctxt , 1 , 0 ) ;
2016-03-01 21:06:38 +03:00
if ( ret ) {
dprintk ( " svcrdma: Error %d posting send for protocol error \n " ,
ret ) ;
svc_rdma_unmap_dma ( ctxt ) ;
svc_rdma_put_context ( ctxt , 1 ) ;
}
}