2006-05-11 11:03:08 +04:00
/*
* Copyright ( c ) 2004 , 2005 , 2006 Voltaire , Inc . All rights reserved .
2014-04-01 17:28:41 +04:00
* Copyright ( c ) 2013 - 2014 Mellanox Technologies . All rights reserved .
2006-05-11 11:03:08 +04:00
*
* 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
* OpenIB . org BSD 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 .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/mm.h>
2006-10-20 00:08:53 +04:00
# include <linux/highmem.h>
2006-05-11 11:03:08 +04:00
# include <linux/scatterlist.h>
# include "iscsi_iser.h"
2015-04-14 18:08:28 +03:00
static void
iser_free_bounce_sg ( struct iser_data_buf * data )
{
struct scatterlist * sg ;
int count ;
for_each_sg ( data - > sg , sg , data - > size , count )
__free_page ( sg_page ( sg ) ) ;
kfree ( data - > sg ) ;
data - > sg = data - > orig_sg ;
data - > size = data - > orig_size ;
data - > orig_sg = NULL ;
data - > orig_size = 0 ;
}
static int
iser_alloc_bounce_sg ( struct iser_data_buf * data )
{
struct scatterlist * sg ;
struct page * page ;
unsigned long length = data - > data_len ;
int i = 0 , nents = DIV_ROUND_UP ( length , PAGE_SIZE ) ;
sg = kcalloc ( nents , sizeof ( * sg ) , GFP_ATOMIC ) ;
if ( ! sg )
goto err ;
sg_init_table ( sg , nents ) ;
while ( length ) {
u32 page_len = min_t ( u32 , length , PAGE_SIZE ) ;
page = alloc_page ( GFP_ATOMIC ) ;
if ( ! page )
goto err ;
sg_set_page ( & sg [ i ] , page , page_len , 0 ) ;
length - = page_len ;
i + + ;
}
data - > orig_sg = data - > sg ;
data - > orig_size = data - > size ;
data - > sg = sg ;
data - > size = nents ;
return 0 ;
err :
for ( ; i > 0 ; i - - )
__free_page ( sg_page ( & sg [ i - 1 ] ) ) ;
kfree ( sg ) ;
return - ENOMEM ;
}
static void
iser_copy_bounce ( struct iser_data_buf * data , bool to_buffer )
{
struct scatterlist * osg , * bsg = data - > sg ;
void * oaddr , * baddr ;
unsigned int left = data - > data_len ;
unsigned int bsg_off = 0 ;
int i ;
for_each_sg ( data - > orig_sg , osg , data - > orig_size , i ) {
unsigned int copy_len , osg_off = 0 ;
oaddr = kmap_atomic ( sg_page ( osg ) ) + osg - > offset ;
copy_len = min ( left , osg - > length ) ;
while ( copy_len ) {
unsigned int len = min ( copy_len , bsg - > length - bsg_off ) ;
baddr = kmap_atomic ( sg_page ( bsg ) ) + bsg - > offset ;
if ( to_buffer )
memcpy ( baddr + bsg_off , oaddr + osg_off , len ) ;
else
memcpy ( oaddr + osg_off , baddr + bsg_off , len ) ;
kunmap_atomic ( baddr - bsg - > offset ) ;
osg_off + = len ;
bsg_off + = len ;
copy_len - = len ;
if ( bsg_off > = bsg - > length ) {
bsg = sg_next ( bsg ) ;
bsg_off = 0 ;
}
}
kunmap_atomic ( oaddr - osg - > offset ) ;
left - = osg_off ;
}
}
static inline void
iser_copy_from_bounce ( struct iser_data_buf * data )
{
iser_copy_bounce ( data , false ) ;
}
static inline void
iser_copy_to_bounce ( struct iser_data_buf * data )
{
iser_copy_bounce ( data , true ) ;
}
2006-09-11 13:22:30 +04:00
2015-08-06 18:32:54 +03:00
struct iser_fr_desc *
2015-04-14 18:08:21 +03:00
iser_reg_desc_get ( struct ib_conn * ib_conn )
{
2015-08-06 18:32:54 +03:00
struct iser_fr_desc * desc ;
2015-04-14 18:08:21 +03:00
unsigned long flags ;
spin_lock_irqsave ( & ib_conn - > lock , flags ) ;
desc = list_first_entry ( & ib_conn - > fastreg . pool ,
2015-08-06 18:32:54 +03:00
struct iser_fr_desc , list ) ;
2015-04-14 18:08:21 +03:00
list_del ( & desc - > list ) ;
spin_unlock_irqrestore ( & ib_conn - > lock , flags ) ;
return desc ;
}
void
iser_reg_desc_put ( struct ib_conn * ib_conn ,
2015-08-06 18:32:54 +03:00
struct iser_fr_desc * desc )
2015-04-14 18:08:21 +03:00
{
unsigned long flags ;
spin_lock_irqsave ( & ib_conn - > lock , flags ) ;
2015-04-14 18:08:23 +03:00
list_add ( & desc - > list , & ib_conn - > fastreg . pool ) ;
2015-04-14 18:08:21 +03:00
spin_unlock_irqrestore ( & ib_conn - > lock , flags ) ;
}
2006-05-11 11:03:08 +04:00
/**
* iser_start_rdma_unaligned_sg
*/
2008-05-22 00:54:11 +04:00
static int iser_start_rdma_unaligned_sg ( struct iscsi_iser_task * iser_task ,
2014-03-05 21:43:45 +04:00
struct iser_data_buf * data ,
2007-07-18 05:37:42 +04:00
enum iser_data_dir cmd_dir )
2006-05-11 11:03:08 +04:00
{
2014-10-01 15:01:58 +04:00
struct ib_device * dev = iser_task - > iser_conn - > ib_conn . device - > ib_device ;
2015-04-14 18:08:28 +03:00
int rc ;
2006-05-11 11:03:08 +04:00
2015-04-14 18:08:28 +03:00
rc = iser_alloc_bounce_sg ( data ) ;
if ( rc ) {
iser_err ( " Failed to allocate bounce for data len %lu \n " ,
data - > data_len ) ;
return rc ;
2006-05-11 11:03:08 +04:00
}
2015-04-14 18:08:28 +03:00
if ( cmd_dir = = ISER_DIR_OUT )
iser_copy_to_bounce ( data ) ;
data - > dma_nents = ib_dma_map_sg ( dev , data - > sg , data - > size ,
( cmd_dir = = ISER_DIR_OUT ) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
if ( ! data - > dma_nents ) {
iser_err ( " Got dma_nents %d, something went wrong... \n " ,
data - > dma_nents ) ;
rc = - ENOMEM ;
goto err ;
}
2014-03-05 21:43:45 +04:00
2006-05-11 11:03:08 +04:00
return 0 ;
2015-04-14 18:08:28 +03:00
err :
iser_free_bounce_sg ( data ) ;
return rc ;
2006-05-11 11:03:08 +04:00
}
/**
* iser_finalize_rdma_unaligned_sg
*/
2014-03-05 21:43:44 +04:00
2008-05-22 00:54:11 +04:00
void iser_finalize_rdma_unaligned_sg ( struct iscsi_iser_task * iser_task ,
2014-03-05 21:43:44 +04:00
struct iser_data_buf * data ,
enum iser_data_dir cmd_dir )
2006-05-11 11:03:08 +04:00
{
2015-04-14 18:08:28 +03:00
struct ib_device * dev = iser_task - > iser_conn - > ib_conn . device - > ib_device ;
2006-05-11 11:03:08 +04:00
2015-04-14 18:08:28 +03:00
ib_dma_unmap_sg ( dev , data - > sg , data - > size ,
2006-12-13 01:31:00 +03:00
( cmd_dir = = ISER_DIR_OUT ) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
2006-05-11 11:03:08 +04:00
2015-04-14 18:08:28 +03:00
if ( cmd_dir = = ISER_DIR_IN )
iser_copy_from_bounce ( data ) ;
2006-05-11 11:03:08 +04:00
2015-04-14 18:08:28 +03:00
iser_free_bounce_sg ( data ) ;
2006-05-11 11:03:08 +04:00
}
2009-11-12 22:32:27 +03:00
# define IS_4K_ALIGNED(addr) ((((unsigned long)addr) & ~MASK_4K) == 0)
2006-05-11 11:03:08 +04:00
/**
* iser_sg_to_page_vec - Translates scatterlist entries to physical addresses
* and returns the length of resulting physical address array ( may be less than
* the original due to possible compaction ) .
*
* we build a " page vec " under the assumption that the SG meets the RDMA
* alignment requirements . Other then the first and last SG elements , all
* the " internal " elements can be compacted into a list whose elements are
* dma addresses of physical pages . The code supports also the weird case
* where - - few fragments of the same page - - are present in the SG as
* consecutive elements . Also , it handles one entry SG .
*/
2009-11-12 22:32:27 +03:00
2006-05-11 11:03:08 +04:00
static int iser_sg_to_page_vec ( struct iser_data_buf * data ,
2013-07-28 13:35:40 +04:00
struct ib_device * ibdev , u64 * pages ,
int * offset , int * data_size )
2006-05-11 11:03:08 +04:00
{
2015-04-14 18:08:15 +03:00
struct scatterlist * sg , * sgl = data - > sg ;
2009-11-12 22:32:27 +03:00
u64 start_addr , end_addr , page , chunk_start = 0 ;
2006-05-11 11:03:08 +04:00
unsigned long total_sz = 0 ;
2009-11-12 22:32:27 +03:00
unsigned int dma_len ;
int i , new_chunk , cur_page , last_ent = data - > dma_nents - 1 ;
2006-05-11 11:03:08 +04:00
/* compute the offset of first element */
2013-07-28 13:35:40 +04:00
* offset = ( u64 ) sgl [ 0 ] . offset & ~ MASK_4K ;
2006-05-11 11:03:08 +04:00
2009-11-12 22:32:27 +03:00
new_chunk = 1 ;
cur_page = 0 ;
2007-07-24 16:41:13 +04:00
for_each_sg ( sgl , sg , data - > dma_nents , i ) {
2009-11-12 22:32:27 +03:00
start_addr = ib_sg_dma_address ( ibdev , sg ) ;
if ( new_chunk )
chunk_start = start_addr ;
dma_len = ib_sg_dma_len ( ibdev , sg ) ;
end_addr = start_addr + dma_len ;
2006-12-13 01:31:00 +03:00
total_sz + = dma_len ;
2006-05-11 11:03:08 +04:00
2009-11-12 22:32:27 +03:00
/* collect page fragments until aligned or end of SG list */
if ( ! IS_4K_ALIGNED ( end_addr ) & & i < last_ent ) {
new_chunk = 0 ;
continue ;
2006-05-11 11:03:08 +04:00
}
2009-11-12 22:32:27 +03:00
new_chunk = 1 ;
/* address of the first page in the contiguous chunk;
masking relevant for the very first SG entry ,
which might be unaligned */
page = chunk_start & MASK_4K ;
do {
2013-07-28 13:35:40 +04:00
pages [ cur_page + + ] = page ;
2006-09-11 13:22:30 +04:00
page + = SIZE_4K ;
2009-11-12 22:32:27 +03:00
} while ( page < end_addr ) ;
2006-05-11 11:03:08 +04:00
}
2009-11-12 22:32:27 +03:00
2013-07-28 13:35:40 +04:00
* data_size = total_sz ;
iser_dbg ( " page_vec->data_size:%d cur_page %d \n " ,
* data_size , cur_page ) ;
2006-05-11 11:03:08 +04:00
return cur_page ;
}
/**
* iser_data_buf_aligned_len - Tries to determine the maximal correctly aligned
* for RDMA sub - list of a scatter - gather list of memory buffers , and returns
* the number of entries which are aligned correctly . Supports the case where
* consecutive SG elements are actually fragments of the same physcial page .
*/
2009-11-12 22:32:27 +03:00
static int iser_data_buf_aligned_len ( struct iser_data_buf * data ,
struct ib_device * ibdev )
2006-05-11 11:03:08 +04:00
{
2015-04-14 18:08:15 +03:00
struct scatterlist * sg , * sgl , * next_sg = NULL ;
2009-11-12 22:32:27 +03:00
u64 start_addr , end_addr ;
int i , ret_len , start_check = 0 ;
if ( data - > dma_nents = = 1 )
return 1 ;
2006-05-11 11:03:08 +04:00
2015-04-14 18:08:15 +03:00
sgl = data - > sg ;
2009-11-12 22:32:27 +03:00
start_addr = ib_sg_dma_address ( ibdev , sgl ) ;
2006-05-11 11:03:08 +04:00
2007-07-24 16:41:13 +04:00
for_each_sg ( sgl , sg , data - > dma_nents , i ) {
2009-11-12 22:32:27 +03:00
if ( start_check & & ! IS_4K_ALIGNED ( start_addr ) )
break ;
next_sg = sg_next ( sg ) ;
if ( ! next_sg )
break ;
end_addr = start_addr + ib_sg_dma_len ( ibdev , sg ) ;
start_addr = ib_sg_dma_address ( ibdev , next_sg ) ;
if ( end_addr = = start_addr ) {
start_check = 0 ;
continue ;
} else
start_check = 1 ;
if ( ! IS_4K_ALIGNED ( end_addr ) )
break ;
2006-05-11 11:03:08 +04:00
}
2009-11-12 22:32:27 +03:00
ret_len = ( next_sg ) ? i : i + 1 ;
2015-08-06 18:32:52 +03:00
if ( unlikely ( ret_len ! = data - > dma_nents ) )
iser_warn ( " rdma alignment violation (%d/%d aligned) \n " ,
ret_len , data - > dma_nents ) ;
2006-05-11 11:03:08 +04:00
return ret_len ;
}
2006-12-13 01:31:00 +03:00
static void iser_data_buf_dump ( struct iser_data_buf * data ,
struct ib_device * ibdev )
2006-05-11 11:03:08 +04:00
{
2007-07-24 16:41:13 +04:00
struct scatterlist * sg ;
2006-05-11 11:03:08 +04:00
int i ;
2015-04-14 18:08:15 +03:00
for_each_sg ( data - > sg , sg , data - > dma_nents , i )
2013-07-28 13:35:36 +04:00
iser_dbg ( " sg[%d] dma_addr:0x%lX page:0x%p "
2006-09-11 13:24:00 +04:00
" off:0x%x sz:0x%x dma_len:0x%x \n " ,
2007-07-24 16:41:13 +04:00
i , ( unsigned long ) ib_sg_dma_address ( ibdev , sg ) ,
2007-10-22 23:19:53 +04:00
sg_page ( sg ) , sg - > offset ,
2007-07-24 16:41:13 +04:00
sg - > length , ib_sg_dma_len ( ibdev , sg ) ) ;
2006-05-11 11:03:08 +04:00
}
static void iser_dump_page_vec ( struct iser_page_vec * page_vec )
{
int i ;
iser_err ( " page vec length %d data size %d \n " ,
page_vec - > length , page_vec - > data_size ) ;
for ( i = 0 ; i < page_vec - > length ; i + + )
iser_err ( " %d %lx \n " , i , ( unsigned long ) page_vec - > pages [ i ] ) ;
}
2008-05-22 00:54:11 +04:00
int iser_dma_map_task_data ( struct iscsi_iser_task * iser_task ,
struct iser_data_buf * data ,
enum iser_data_dir iser_dir ,
enum dma_data_direction dma_dir )
2006-09-27 17:43:06 +04:00
{
2006-12-13 01:31:00 +03:00
struct ib_device * dev ;
2006-09-27 17:43:06 +04:00
2008-05-22 00:54:11 +04:00
iser_task - > dir [ iser_dir ] = 1 ;
2014-10-01 15:01:58 +04:00
dev = iser_task - > iser_conn - > ib_conn . device - > ib_device ;
2006-09-27 17:43:06 +04:00
2015-04-14 18:08:15 +03:00
data - > dma_nents = ib_dma_map_sg ( dev , data - > sg , data - > size , dma_dir ) ;
2006-09-27 17:43:06 +04:00
if ( data - > dma_nents = = 0 ) {
iser_err ( " dma_map_sg failed!!! \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2014-03-05 21:43:44 +04:00
void iser_dma_unmap_task_data ( struct iscsi_iser_task * iser_task ,
2014-12-28 15:26:11 +03:00
struct iser_data_buf * data ,
enum dma_data_direction dir )
2006-09-27 17:43:06 +04:00
{
2006-12-13 01:31:00 +03:00
struct ib_device * dev ;
2006-09-27 17:43:06 +04:00
2014-10-01 15:01:58 +04:00
dev = iser_task - > iser_conn - > ib_conn . device - > ib_device ;
2015-04-14 18:08:15 +03:00
ib_dma_unmap_sg ( dev , data - > sg , data - > size , dir ) ;
2006-09-27 17:43:06 +04:00
}
2015-04-14 18:08:26 +03:00
static int
iser_reg_dma ( struct iser_device * device , struct iser_data_buf * mem ,
struct iser_mem_reg * reg )
{
struct scatterlist * sg = mem - > sg ;
reg - > sge . lkey = device - > mr - > lkey ;
reg - > rkey = device - > mr - > rkey ;
reg - > sge . addr = ib_sg_dma_address ( device - > ib_device , & sg [ 0 ] ) ;
reg - > sge . length = ib_sg_dma_len ( device - > ib_device , & sg [ 0 ] ) ;
iser_dbg ( " Single DMA entry: lkey=0x%x, rkey=0x%x, addr=0x%llx, "
" length=0x%x \n " , reg - > sge . lkey , reg - > rkey ,
reg - > sge . addr , reg - > sge . length ) ;
return 0 ;
}
2013-07-28 13:35:40 +04:00
static int fall_to_bounce_buf ( struct iscsi_iser_task * iser_task ,
2014-03-05 21:43:45 +04:00
struct iser_data_buf * mem ,
2015-08-06 18:32:52 +03:00
enum iser_data_dir cmd_dir )
2013-07-28 13:35:40 +04:00
{
2015-04-14 18:08:16 +03:00
struct iscsi_conn * iscsi_conn = iser_task - > iser_conn - > iscsi_conn ;
struct iser_device * device = iser_task - > iser_conn - > ib_conn . device ;
2013-07-28 13:35:40 +04:00
iscsi_conn - > fmr_unalign_cnt + + ;
if ( iser_debug_level > 0 )
2015-04-14 18:08:16 +03:00
iser_data_buf_dump ( mem , device - > ib_device ) ;
2013-07-28 13:35:40 +04:00
/* unmap the command data before accessing it */
2014-12-28 15:26:11 +03:00
iser_dma_unmap_task_data ( iser_task , mem ,
( cmd_dir = = ISER_DIR_OUT ) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
2013-07-28 13:35:40 +04:00
/* allocate copy buf, if we are writing, copy the */
/* unaligned scatterlist, dma map the copy */
2015-04-14 18:08:15 +03:00
if ( iser_start_rdma_unaligned_sg ( iser_task , mem , cmd_dir ) ! = 0 )
2014-03-05 21:43:45 +04:00
return - ENOMEM ;
2013-07-28 13:35:40 +04:00
return 0 ;
}
2015-04-14 18:08:17 +03:00
/**
* iser_reg_page_vec - Register physical memory
*
* returns : 0 on success , errno code on failure
*/
static
2015-04-14 18:08:20 +03:00
int iser_reg_page_vec ( struct iscsi_iser_task * iser_task ,
struct iser_data_buf * mem ,
2015-04-14 18:08:17 +03:00
struct iser_page_vec * page_vec ,
2015-04-14 18:08:20 +03:00
struct iser_mem_reg * mem_reg )
2015-04-14 18:08:17 +03:00
{
2015-04-14 18:08:20 +03:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
struct iser_device * device = ib_conn - > device ;
struct ib_pool_fmr * fmr ;
int ret , plen ;
plen = iser_sg_to_page_vec ( mem , device - > ib_device ,
page_vec - > pages ,
& page_vec - > offset ,
& page_vec - > data_size ) ;
page_vec - > length = plen ;
if ( plen * SIZE_4K < page_vec - > data_size ) {
iser_err ( " page vec too short to hold this SG \n " ) ;
iser_data_buf_dump ( mem , device - > ib_device ) ;
iser_dump_page_vec ( page_vec ) ;
return - EINVAL ;
}
2015-04-14 18:08:17 +03:00
2015-04-14 18:08:20 +03:00
fmr = ib_fmr_pool_map_phys ( ib_conn - > fmr . pool ,
page_vec - > pages ,
2015-04-14 18:08:17 +03:00
page_vec - > length ,
2015-04-14 18:08:20 +03:00
page_vec - > pages [ 0 ] ) ;
if ( IS_ERR ( fmr ) ) {
ret = PTR_ERR ( fmr ) ;
iser_err ( " ib_fmr_pool_map_phys failed: %d \n " , ret ) ;
return ret ;
2015-04-14 18:08:17 +03:00
}
2015-04-14 18:08:24 +03:00
mem_reg - > sge . lkey = fmr - > fmr - > lkey ;
2015-04-14 18:08:20 +03:00
mem_reg - > rkey = fmr - > fmr - > rkey ;
2015-04-14 18:08:24 +03:00
mem_reg - > sge . addr = page_vec - > pages [ 0 ] + page_vec - > offset ;
mem_reg - > sge . length = page_vec - > data_size ;
2015-04-14 18:08:20 +03:00
mem_reg - > mem_h = fmr ;
2015-04-14 18:08:17 +03:00
return 0 ;
}
/**
* Unregister ( previosuly registered using FMR ) memory .
* If memory is non - FMR does nothing .
*/
void iser_unreg_mem_fmr ( struct iscsi_iser_task * iser_task ,
enum iser_data_dir cmd_dir )
{
2015-04-14 18:08:19 +03:00
struct iser_mem_reg * reg = & iser_task - > rdma_reg [ cmd_dir ] ;
2015-04-14 18:08:17 +03:00
int ret ;
if ( ! reg - > mem_h )
return ;
iser_dbg ( " PHYSICAL Mem.Unregister mem_h %p \n " , reg - > mem_h ) ;
ret = ib_fmr_pool_unmap ( ( struct ib_pool_fmr * ) reg - > mem_h ) ;
if ( ret )
iser_err ( " ib_fmr_pool_unmap failed %d \n " , ret ) ;
reg - > mem_h = NULL ;
}
void iser_unreg_mem_fastreg ( struct iscsi_iser_task * iser_task ,
enum iser_data_dir cmd_dir )
{
2015-04-14 18:08:19 +03:00
struct iser_mem_reg * reg = & iser_task - > rdma_reg [ cmd_dir ] ;
2015-04-14 18:08:17 +03:00
2015-04-14 18:08:21 +03:00
if ( ! reg - > mem_h )
2015-04-14 18:08:17 +03:00
return ;
2015-04-14 18:08:21 +03:00
iser_reg_desc_put ( & iser_task - > iser_conn - > ib_conn ,
reg - > mem_h ) ;
2015-04-14 18:08:17 +03:00
reg - > mem_h = NULL ;
}
2006-05-11 11:03:08 +04:00
/**
2013-07-28 13:35:41 +04:00
* iser_reg_rdma_mem_fmr - Registers memory intended for RDMA ,
* using FMR ( if possible ) obtaining rkey and va
2006-05-11 11:03:08 +04:00
*
* returns 0 on success , errno code on failure
*/
2013-07-28 13:35:41 +04:00
int iser_reg_rdma_mem_fmr ( struct iscsi_iser_task * iser_task ,
enum iser_data_dir cmd_dir )
2006-05-11 11:03:08 +04:00
{
2014-10-01 15:01:58 +04:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
struct iser_device * device = ib_conn - > device ;
2006-12-13 01:31:00 +03:00
struct ib_device * ibdev = device - > ib_device ;
2008-05-22 00:54:11 +04:00
struct iser_data_buf * mem = & iser_task - > data [ cmd_dir ] ;
2015-04-14 18:08:19 +03:00
struct iser_mem_reg * mem_reg ;
2006-05-11 11:03:08 +04:00
int aligned_len ;
int err ;
2006-09-11 13:24:00 +04:00
int i ;
2006-05-11 11:03:08 +04:00
2015-04-14 18:08:19 +03:00
mem_reg = & iser_task - > rdma_reg [ cmd_dir ] ;
2006-05-11 11:03:08 +04:00
2006-12-13 01:31:00 +03:00
aligned_len = iser_data_buf_aligned_len ( mem , ibdev ) ;
2013-07-28 13:35:42 +04:00
if ( aligned_len ! = mem - > dma_nents ) {
2015-08-06 18:32:52 +03:00
err = fall_to_bounce_buf ( iser_task , mem , cmd_dir ) ;
2013-07-28 13:35:40 +04:00
if ( err ) {
iser_err ( " failed to allocate bounce buffer \n " ) ;
return err ;
}
2006-05-11 11:03:08 +04:00
}
2006-09-11 13:26:33 +04:00
/* if there a single dma entry, FMR is not needed */
if ( mem - > dma_nents = = 1 ) {
2015-04-14 18:08:26 +03:00
return iser_reg_dma ( device , mem , mem_reg ) ;
2006-09-11 13:26:33 +04:00
} else { /* use FMR for multiple dma entries */
2015-04-14 18:08:20 +03:00
err = iser_reg_page_vec ( iser_task , mem , ib_conn - > fmr . page_vec ,
2015-04-14 18:08:19 +03:00
mem_reg ) ;
2013-02-21 18:50:09 +04:00
if ( err & & err ! = - EAGAIN ) {
2006-12-13 01:31:00 +03:00
iser_data_buf_dump ( mem , ibdev ) ;
2008-05-22 00:54:11 +04:00
iser_err ( " mem->dma_nents = %d (dlength = 0x%x) \n " ,
mem - > dma_nents ,
ntoh24 ( iser_task - > desc . iscsi_header . dlength ) ) ;
2006-09-11 13:26:33 +04:00
iser_err ( " page_vec: data_size = 0x%x, length = %d, offset = 0x%x \n " ,
2014-10-01 15:01:58 +04:00
ib_conn - > fmr . page_vec - > data_size ,
ib_conn - > fmr . page_vec - > length ,
ib_conn - > fmr . page_vec - > offset ) ;
for ( i = 0 ; i < ib_conn - > fmr . page_vec - > length ; i + + )
2006-09-11 13:26:33 +04:00
iser_err ( " page_vec[%d] = 0x%llx \n " , i ,
2014-10-01 15:01:58 +04:00
( unsigned long long ) ib_conn - > fmr . page_vec - > pages [ i ] ) ;
2006-09-11 13:24:00 +04:00
}
2013-05-01 17:25:26 +04:00
if ( err )
return err ;
2006-09-11 13:24:00 +04:00
}
2006-05-11 11:03:08 +04:00
return 0 ;
}
2013-07-28 13:35:42 +04:00
2014-12-07 17:10:06 +03:00
static void
2014-08-13 20:54:33 +04:00
iser_set_dif_domain ( struct scsi_cmnd * sc , struct ib_sig_attrs * sig_attrs ,
struct ib_sig_domain * domain )
{
2014-08-13 20:54:35 +04:00
domain - > sig_type = IB_SIG_TYPE_T10_DIF ;
2014-12-07 17:10:06 +03:00
domain - > sig . dif . pi_interval = scsi_prot_interval ( sc ) ;
domain - > sig . dif . ref_tag = scsi_prot_ref_tag ( sc ) ;
2014-08-13 20:54:35 +04:00
/*
* At the moment we hard code those , but in the future
* we will take them from sc .
*/
domain - > sig . dif . apptag_check_mask = 0xffff ;
domain - > sig . dif . app_escape = true ;
domain - > sig . dif . ref_escape = true ;
2014-12-07 17:10:06 +03:00
if ( sc - > prot_flags & SCSI_PROT_REF_INCREMENT )
2014-08-13 20:54:35 +04:00
domain - > sig . dif . ref_remap = true ;
2014-08-13 20:54:33 +04:00
} ;
2014-03-05 21:43:48 +04:00
static int
iser_set_sig_attrs ( struct scsi_cmnd * sc , struct ib_sig_attrs * sig_attrs )
{
switch ( scsi_get_prot_op ( sc ) ) {
case SCSI_PROT_WRITE_INSERT :
case SCSI_PROT_READ_STRIP :
2014-08-13 20:54:35 +04:00
sig_attrs - > mem . sig_type = IB_SIG_TYPE_NONE ;
2014-08-13 20:54:33 +04:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > wire ) ;
2014-03-05 21:43:48 +04:00
sig_attrs - > wire . sig . dif . bg_type = IB_T10DIF_CRC ;
break ;
case SCSI_PROT_READ_INSERT :
case SCSI_PROT_WRITE_STRIP :
2014-08-13 20:54:35 +04:00
sig_attrs - > wire . sig_type = IB_SIG_TYPE_NONE ;
2014-08-13 20:54:33 +04:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > mem ) ;
2014-12-07 17:10:06 +03:00
sig_attrs - > mem . sig . dif . bg_type = sc - > prot_flags & SCSI_PROT_IP_CHECKSUM ?
IB_T10DIF_CSUM : IB_T10DIF_CRC ;
2014-03-05 21:43:48 +04:00
break ;
case SCSI_PROT_READ_PASS :
case SCSI_PROT_WRITE_PASS :
2014-08-13 20:54:33 +04:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > wire ) ;
2014-03-05 21:43:48 +04:00
sig_attrs - > wire . sig . dif . bg_type = IB_T10DIF_CRC ;
2014-08-13 20:54:33 +04:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > mem ) ;
2014-12-07 17:10:06 +03:00
sig_attrs - > mem . sig . dif . bg_type = sc - > prot_flags & SCSI_PROT_IP_CHECKSUM ?
IB_T10DIF_CSUM : IB_T10DIF_CRC ;
2014-03-05 21:43:48 +04:00
break ;
default :
iser_err ( " Unsupported PI operation %d \n " ,
scsi_get_prot_op ( sc ) ) ;
return - EINVAL ;
}
2014-08-13 20:54:35 +04:00
2014-03-05 21:43:48 +04:00
return 0 ;
}
2014-12-07 17:10:06 +03:00
static inline void
2014-03-05 21:43:48 +04:00
iser_set_prot_checks ( struct scsi_cmnd * sc , u8 * mask )
{
2014-12-07 17:10:06 +03:00
* mask = 0 ;
if ( sc - > prot_flags & SCSI_PROT_REF_CHECK )
* mask | = ISER_CHECK_REFTAG ;
if ( sc - > prot_flags & SCSI_PROT_GUARD_CHECK )
* mask | = ISER_CHECK_GUARD ;
2014-03-05 21:43:48 +04:00
}
2014-12-07 17:10:01 +03:00
static void
iser_inv_rkey ( struct ib_send_wr * inv_wr , struct ib_mr * mr )
{
u32 rkey ;
memset ( inv_wr , 0 , sizeof ( * inv_wr ) ) ;
inv_wr - > opcode = IB_WR_LOCAL_INV ;
inv_wr - > wr_id = ISER_FASTREG_LI_WRID ;
inv_wr - > ex . invalidate_rkey = mr - > rkey ;
rkey = ib_inc_rkey ( mr - > rkey ) ;
ib_update_fast_reg_key ( mr , rkey ) ;
}
2014-03-05 21:43:48 +04:00
static int
iser_reg_sig_mr ( struct iscsi_iser_task * iser_task ,
2015-08-06 18:32:53 +03:00
struct iser_pi_context * pi_ctx ,
2015-04-14 18:08:25 +03:00
struct iser_mem_reg * data_reg ,
struct iser_mem_reg * prot_reg ,
struct iser_mem_reg * sig_reg )
2014-03-05 21:43:48 +04:00
{
2014-10-01 15:01:58 +04:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
2014-03-05 21:43:48 +04:00
struct ib_send_wr sig_wr , inv_wr ;
struct ib_send_wr * bad_wr , * wr = NULL ;
struct ib_sig_attrs sig_attrs ;
int ret ;
memset ( & sig_attrs , 0 , sizeof ( sig_attrs ) ) ;
ret = iser_set_sig_attrs ( iser_task - > sc , & sig_attrs ) ;
if ( ret )
goto err ;
2014-12-07 17:10:06 +03:00
iser_set_prot_checks ( iser_task - > sc , & sig_attrs . check_mask ) ;
2014-03-05 21:43:48 +04:00
2015-08-06 18:32:53 +03:00
if ( ! pi_ctx - > sig_mr_valid ) {
2014-12-07 17:10:01 +03:00
iser_inv_rkey ( & inv_wr , pi_ctx - > sig_mr ) ;
2014-03-05 21:43:48 +04:00
wr = & inv_wr ;
}
memset ( & sig_wr , 0 , sizeof ( sig_wr ) ) ;
sig_wr . opcode = IB_WR_REG_SIG_MR ;
sig_wr . wr_id = ISER_FASTREG_LI_WRID ;
2015-04-14 18:08:25 +03:00
sig_wr . sg_list = & data_reg - > sge ;
2014-03-05 21:43:48 +04:00
sig_wr . num_sge = 1 ;
sig_wr . wr . sig_handover . sig_attrs = & sig_attrs ;
sig_wr . wr . sig_handover . sig_mr = pi_ctx - > sig_mr ;
if ( scsi_prot_sg_count ( iser_task - > sc ) )
2015-04-14 18:08:25 +03:00
sig_wr . wr . sig_handover . prot = & prot_reg - > sge ;
2014-03-05 21:43:48 +04:00
sig_wr . wr . sig_handover . access_flags = IB_ACCESS_LOCAL_WRITE |
IB_ACCESS_REMOTE_READ |
IB_ACCESS_REMOTE_WRITE ;
if ( ! wr )
wr = & sig_wr ;
else
wr - > next = & sig_wr ;
2014-10-01 15:01:58 +04:00
ret = ib_post_send ( ib_conn - > qp , wr , & bad_wr ) ;
2014-03-05 21:43:48 +04:00
if ( ret ) {
iser_err ( " reg_sig_mr failed, ret:%d \n " , ret ) ;
goto err ;
}
2015-08-06 18:32:53 +03:00
pi_ctx - > sig_mr_valid = 0 ;
2014-03-05 21:43:48 +04:00
2015-04-14 18:08:25 +03:00
sig_reg - > sge . lkey = pi_ctx - > sig_mr - > lkey ;
sig_reg - > rkey = pi_ctx - > sig_mr - > rkey ;
sig_reg - > sge . addr = 0 ;
sig_reg - > sge . length = scsi_transfer_length ( iser_task - > sc ) ;
2014-03-05 21:43:48 +04:00
2015-04-14 18:08:25 +03:00
iser_dbg ( " sig_sge: lkey: 0x%x, rkey: 0x%x, addr: 0x%llx, length: %u \n " ,
sig_reg - > sge . lkey , sig_reg - > rkey , sig_reg - > sge . addr ,
sig_reg - > sge . length ) ;
2014-03-05 21:43:48 +04:00
err :
return ret ;
}
2014-03-05 21:43:40 +04:00
static int iser_fast_reg_mr ( struct iscsi_iser_task * iser_task ,
struct iser_data_buf * mem ,
2015-08-06 18:32:53 +03:00
struct iser_reg_resources * rsc ,
2015-04-14 18:08:25 +03:00
struct iser_mem_reg * reg )
2013-07-28 13:35:42 +04:00
{
2014-10-01 15:01:58 +04:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
struct iser_device * device = ib_conn - > device ;
2014-03-05 21:43:48 +04:00
struct ib_mr * mr ;
struct ib_fast_reg_page_list * frpl ;
2013-07-28 13:35:42 +04:00
struct ib_send_wr fastreg_wr , inv_wr ;
struct ib_send_wr * bad_wr , * wr = NULL ;
2014-03-05 21:43:40 +04:00
int ret , offset , size , plen ;
/* if there a single dma entry, dma mr suffices */
2015-04-14 18:08:26 +03:00
if ( mem - > dma_nents = = 1 )
return iser_reg_dma ( device , mem , reg ) ;
2014-03-05 21:43:40 +04:00
2015-08-06 18:32:53 +03:00
mr = rsc - > mr ;
frpl = rsc - > frpl ;
2014-03-05 21:43:48 +04:00
plen = iser_sg_to_page_vec ( mem , device - > ib_device , frpl - > page_list ,
2014-03-05 21:43:40 +04:00
& offset , & size ) ;
if ( plen * SIZE_4K < size ) {
iser_err ( " fast reg page_list too short to hold this SG \n " ) ;
return - EINVAL ;
}
2013-07-28 13:35:42 +04:00
2015-08-06 18:32:53 +03:00
if ( ! rsc - > mr_valid ) {
2014-12-07 17:10:01 +03:00
iser_inv_rkey ( & inv_wr , mr ) ;
2013-07-28 13:35:42 +04:00
wr = & inv_wr ;
}
/* Prepare FASTREG WR */
memset ( & fastreg_wr , 0 , sizeof ( fastreg_wr ) ) ;
2014-03-05 21:43:39 +04:00
fastreg_wr . wr_id = ISER_FASTREG_LI_WRID ;
2013-07-28 13:35:42 +04:00
fastreg_wr . opcode = IB_WR_FAST_REG_MR ;
2014-03-05 21:43:48 +04:00
fastreg_wr . wr . fast_reg . iova_start = frpl - > page_list [ 0 ] + offset ;
fastreg_wr . wr . fast_reg . page_list = frpl ;
2014-03-05 21:43:40 +04:00
fastreg_wr . wr . fast_reg . page_list_len = plen ;
2013-07-28 13:35:42 +04:00
fastreg_wr . wr . fast_reg . page_shift = SHIFT_4K ;
2014-03-05 21:43:40 +04:00
fastreg_wr . wr . fast_reg . length = size ;
2014-03-05 21:43:48 +04:00
fastreg_wr . wr . fast_reg . rkey = mr - > rkey ;
2013-07-28 13:35:42 +04:00
fastreg_wr . wr . fast_reg . access_flags = ( IB_ACCESS_LOCAL_WRITE |
IB_ACCESS_REMOTE_WRITE |
IB_ACCESS_REMOTE_READ ) ;
2014-01-23 14:31:28 +04:00
if ( ! wr )
2013-07-28 13:35:42 +04:00
wr = & fastreg_wr ;
2014-01-23 14:31:28 +04:00
else
2013-07-28 13:35:42 +04:00
wr - > next = & fastreg_wr ;
2014-10-01 15:01:58 +04:00
ret = ib_post_send ( ib_conn - > qp , wr , & bad_wr ) ;
2013-07-28 13:35:42 +04:00
if ( ret ) {
iser_err ( " fast registration failed, ret:%d \n " , ret ) ;
return ret ;
}
2015-08-06 18:32:53 +03:00
rsc - > mr_valid = 0 ;
2013-07-28 13:35:42 +04:00
2015-04-14 18:08:25 +03:00
reg - > sge . lkey = mr - > lkey ;
reg - > rkey = mr - > rkey ;
reg - > sge . addr = frpl - > page_list [ 0 ] + offset ;
reg - > sge . length = size ;
2013-07-28 13:35:42 +04:00
return ret ;
}
/**
2014-03-05 21:43:39 +04:00
* iser_reg_rdma_mem_fastreg - Registers memory intended for RDMA ,
2013-07-28 13:35:42 +04:00
* using Fast Registration WR ( if possible ) obtaining rkey and va
*
* returns 0 on success , errno code on failure
*/
2014-03-05 21:43:39 +04:00
int iser_reg_rdma_mem_fastreg ( struct iscsi_iser_task * iser_task ,
enum iser_data_dir cmd_dir )
2013-07-28 13:35:42 +04:00
{
2014-10-01 15:01:58 +04:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
struct iser_device * device = ib_conn - > device ;
2013-07-28 13:35:42 +04:00
struct ib_device * ibdev = device - > ib_device ;
struct iser_data_buf * mem = & iser_task - > data [ cmd_dir ] ;
2015-04-14 18:08:19 +03:00
struct iser_mem_reg * mem_reg = & iser_task - > rdma_reg [ cmd_dir ] ;
2015-08-06 18:32:54 +03:00
struct iser_fr_desc * desc = NULL ;
2013-07-28 13:35:42 +04:00
int err , aligned_len ;
aligned_len = iser_data_buf_aligned_len ( mem , ibdev ) ;
if ( aligned_len ! = mem - > dma_nents ) {
2015-08-06 18:32:52 +03:00
err = fall_to_bounce_buf ( iser_task , mem , cmd_dir ) ;
2013-07-28 13:35:42 +04:00
if ( err ) {
iser_err ( " failed to allocate bounce buffer \n " ) ;
return err ;
}
}
2014-03-05 21:43:48 +04:00
if ( mem - > dma_nents ! = 1 | |
scsi_get_prot_op ( iser_task - > sc ) ! = SCSI_PROT_NORMAL ) {
2015-04-14 18:08:21 +03:00
desc = iser_reg_desc_get ( ib_conn ) ;
2015-04-14 18:08:19 +03:00
mem_reg - > mem_h = desc ;
2014-03-05 21:43:40 +04:00
}
2013-07-28 13:35:42 +04:00
2015-08-06 18:32:53 +03:00
err = iser_fast_reg_mr ( iser_task , mem ,
desc ? & desc - > rsc : NULL , mem_reg ) ;
2014-03-05 21:43:40 +04:00
if ( err )
goto err_reg ;
2014-03-05 21:43:48 +04:00
if ( scsi_get_prot_op ( iser_task - > sc ) ! = SCSI_PROT_NORMAL ) {
2015-04-14 18:08:25 +03:00
struct iser_mem_reg prot_reg ;
2014-03-05 21:43:48 +04:00
2015-04-14 18:08:25 +03:00
memset ( & prot_reg , 0 , sizeof ( prot_reg ) ) ;
2014-03-05 21:43:48 +04:00
if ( scsi_prot_sg_count ( iser_task - > sc ) ) {
mem = & iser_task - > prot [ cmd_dir ] ;
aligned_len = iser_data_buf_aligned_len ( mem , ibdev ) ;
if ( aligned_len ! = mem - > dma_nents ) {
2015-04-14 18:08:16 +03:00
err = fall_to_bounce_buf ( iser_task , mem ,
2015-08-06 18:32:52 +03:00
cmd_dir ) ;
2014-03-05 21:43:48 +04:00
if ( err ) {
iser_err ( " failed to allocate bounce buffer \n " ) ;
return err ;
}
}
2015-08-06 18:32:53 +03:00
err = iser_fast_reg_mr ( iser_task , mem ,
& desc - > pi_ctx - > rsc , & prot_reg ) ;
2014-03-05 21:43:48 +04:00
if ( err )
goto err_reg ;
}
2015-08-06 18:32:53 +03:00
err = iser_reg_sig_mr ( iser_task , desc - > pi_ctx , mem_reg ,
2015-04-14 18:08:25 +03:00
& prot_reg , mem_reg ) ;
2014-03-05 21:43:48 +04:00
if ( err ) {
iser_err ( " Failed to register signature mr \n " ) ;
return err ;
}
2015-08-06 18:32:53 +03:00
desc - > pi_ctx - > sig_protected = 1 ;
2014-03-05 21:43:48 +04:00
}
2014-03-05 21:43:40 +04:00
2013-07-28 13:35:42 +04:00
return 0 ;
err_reg :
2015-04-14 18:08:21 +03:00
if ( desc )
iser_reg_desc_put ( ib_conn , desc ) ;
2014-03-05 21:43:40 +04:00
2013-07-28 13:35:42 +04:00
return err ;
}