2006-05-11 10:03:08 +03:00
/*
* Copyright ( c ) 2004 , 2005 , 2006 Voltaire , Inc . All rights reserved .
2014-04-01 16:28:41 +03:00
* Copyright ( c ) 2013 - 2014 Mellanox Technologies . All rights reserved .
2006-05-11 10:03:08 +03: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-19 16:08:53 -04:00
# include <linux/highmem.h>
2006-05-11 10:03:08 +03:00
# include <linux/scatterlist.h>
# include "iscsi_iser.h"
# define ISER_KMALLOC_THRESHOLD 0x20000 /* 128K - kmalloc limit */
2006-09-11 12:22:30 +03:00
2006-05-11 10:03:08 +03:00
/**
* iser_start_rdma_unaligned_sg
*/
2008-05-21 15:54:11 -05:00
static int iser_start_rdma_unaligned_sg ( struct iscsi_iser_task * iser_task ,
2014-03-05 19:43:45 +02:00
struct iser_data_buf * data ,
2007-07-17 18:37:42 -07:00
enum iser_data_dir cmd_dir )
2006-05-11 10:03:08 +03:00
{
2014-10-01 14:01:58 +03:00
struct ib_device * dev = iser_task - > iser_conn - > ib_conn . device - > ib_device ;
2015-04-14 18:08:15 +03:00
struct scatterlist * sgl = data - > sg ;
2014-03-05 19:43:45 +02:00
struct scatterlist * sg ;
2006-05-11 10:03:08 +03:00
char * mem = NULL ;
2015-04-14 18:08:14 +03:00
unsigned long cmd_data_len = data - > data_len ;
2014-03-05 19:43:45 +02:00
int dma_nents , i ;
2006-05-11 10:03:08 +03:00
if ( cmd_data_len > ISER_KMALLOC_THRESHOLD )
2010-02-08 13:20:43 +00:00
mem = ( void * ) __get_free_pages ( GFP_ATOMIC ,
2006-12-08 02:37:49 -08:00
ilog2 ( roundup_pow_of_two ( cmd_data_len ) ) - PAGE_SHIFT ) ;
2006-05-11 10:03:08 +03:00
else
2010-02-08 13:20:43 +00:00
mem = kmalloc ( cmd_data_len , GFP_ATOMIC ) ;
2006-05-11 10:03:08 +03:00
if ( mem = = NULL ) {
iser_err ( " Failed to allocate mem size %d %d for copying sglist \n " ,
2014-03-05 19:43:45 +02:00
data - > size , ( int ) cmd_data_len ) ;
2006-05-11 10:03:08 +03:00
return - ENOMEM ;
}
if ( cmd_dir = = ISER_DIR_OUT ) {
/* copy the unaligned sg the buffer which is used for RDMA */
char * p , * from ;
2015-04-14 18:08:15 +03:00
sgl = data - > sg ;
2007-07-24 14:41:13 +02:00
p = mem ;
for_each_sg ( sgl , sg , data - > size , i ) {
2011-11-25 23:14:20 +08:00
from = kmap_atomic ( sg_page ( sg ) ) ;
2006-05-11 10:03:08 +03:00
memcpy ( p ,
2007-07-24 14:41:13 +02:00
from + sg - > offset ,
sg - > length ) ;
2011-11-25 23:14:20 +08:00
kunmap_atomic ( from ) ;
2007-07-24 14:41:13 +02:00
p + = sg - > length ;
2006-05-11 10:03:08 +03:00
}
}
2015-04-14 18:08:15 +03:00
sg_init_one ( & data - > sg_single , mem , cmd_data_len ) ;
data - > orig_sg = data - > sg ;
data - > sg = & data - > sg_single ;
data - > copy_buf = mem ;
dma_nents = ib_dma_map_sg ( dev , data - > sg , 1 ,
2006-12-12 14:31:00 -08:00
( cmd_dir = = ISER_DIR_OUT ) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
2006-05-11 10:03:08 +03:00
BUG_ON ( dma_nents = = 0 ) ;
2015-04-14 18:08:15 +03:00
data - > dma_nents = dma_nents ;
2014-03-05 19:43:45 +02:00
2006-05-11 10:03:08 +03:00
return 0 ;
}
/**
* iser_finalize_rdma_unaligned_sg
*/
2014-03-05 19:43:44 +02:00
2008-05-21 15:54:11 -05:00
void iser_finalize_rdma_unaligned_sg ( struct iscsi_iser_task * iser_task ,
2014-03-05 19:43:44 +02:00
struct iser_data_buf * data ,
enum iser_data_dir cmd_dir )
2006-05-11 10:03:08 +03:00
{
2006-12-12 14:31:00 -08:00
struct ib_device * dev ;
2006-05-11 10:03:08 +03:00
unsigned long cmd_data_len ;
2014-10-01 14:01:58 +03:00
dev = iser_task - > iser_conn - > ib_conn . device - > ib_device ;
2006-05-11 10:03:08 +03:00
2015-04-14 18:08:15 +03:00
ib_dma_unmap_sg ( dev , data - > sg , 1 ,
2006-12-12 14:31:00 -08:00
( cmd_dir = = ISER_DIR_OUT ) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
2006-05-11 10:03:08 +03:00
if ( cmd_dir = = ISER_DIR_IN ) {
char * mem ;
2007-07-24 14:41:13 +02:00
struct scatterlist * sgl , * sg ;
2006-05-11 10:03:08 +03:00
unsigned char * p , * to ;
unsigned int sg_size ;
int i ;
/* copy back read RDMA to unaligned sg */
2015-04-14 18:08:15 +03:00
mem = data - > copy_buf ;
2006-05-11 10:03:08 +03:00
2015-04-14 18:08:15 +03:00
sgl = data - > sg ;
2014-03-05 19:43:44 +02:00
sg_size = data - > size ;
2006-05-11 10:03:08 +03:00
2007-07-24 14:41:13 +02:00
p = mem ;
for_each_sg ( sgl , sg , sg_size , i ) {
2011-11-25 23:14:20 +08:00
to = kmap_atomic ( sg_page ( sg ) ) ;
2007-07-24 14:41:13 +02:00
memcpy ( to + sg - > offset ,
2006-05-11 10:03:08 +03:00
p ,
2007-07-24 14:41:13 +02:00
sg - > length ) ;
2011-11-25 23:14:20 +08:00
kunmap_atomic ( to ) ;
2007-07-24 14:41:13 +02:00
p + = sg - > length ;
2006-05-11 10:03:08 +03:00
}
}
2014-03-05 19:43:44 +02:00
cmd_data_len = data - > data_len ;
2006-05-11 10:03:08 +03:00
if ( cmd_data_len > ISER_KMALLOC_THRESHOLD )
2015-04-14 18:08:15 +03:00
free_pages ( ( unsigned long ) data - > copy_buf ,
2006-12-08 02:37:49 -08:00
ilog2 ( roundup_pow_of_two ( cmd_data_len ) ) - PAGE_SHIFT ) ;
2006-05-11 10:03:08 +03:00
else
2015-04-14 18:08:15 +03:00
kfree ( data - > copy_buf ) ;
2006-05-11 10:03:08 +03:00
2015-04-14 18:08:15 +03:00
data - > copy_buf = NULL ;
2006-05-11 10:03:08 +03:00
}
2009-11-12 11:32:27 -08:00
# define IS_4K_ALIGNED(addr) ((((unsigned long)addr) & ~MASK_4K) == 0)
2006-05-11 10:03:08 +03: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 11:32:27 -08:00
2006-05-11 10:03:08 +03:00
static int iser_sg_to_page_vec ( struct iser_data_buf * data ,
2013-07-28 12:35:40 +03:00
struct ib_device * ibdev , u64 * pages ,
int * offset , int * data_size )
2006-05-11 10:03:08 +03:00
{
2015-04-14 18:08:15 +03:00
struct scatterlist * sg , * sgl = data - > sg ;
2009-11-12 11:32:27 -08:00
u64 start_addr , end_addr , page , chunk_start = 0 ;
2006-05-11 10:03:08 +03:00
unsigned long total_sz = 0 ;
2009-11-12 11:32:27 -08:00
unsigned int dma_len ;
int i , new_chunk , cur_page , last_ent = data - > dma_nents - 1 ;
2006-05-11 10:03:08 +03:00
/* compute the offset of first element */
2013-07-28 12:35:40 +03:00
* offset = ( u64 ) sgl [ 0 ] . offset & ~ MASK_4K ;
2006-05-11 10:03:08 +03:00
2009-11-12 11:32:27 -08:00
new_chunk = 1 ;
cur_page = 0 ;
2007-07-24 14:41:13 +02:00
for_each_sg ( sgl , sg , data - > dma_nents , i ) {
2009-11-12 11:32:27 -08: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-12 14:31:00 -08:00
total_sz + = dma_len ;
2006-05-11 10:03:08 +03:00
2009-11-12 11:32:27 -08: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 10:03:08 +03:00
}
2009-11-12 11:32:27 -08: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 12:35:40 +03:00
pages [ cur_page + + ] = page ;
2006-09-11 12:22:30 +03:00
page + = SIZE_4K ;
2009-11-12 11:32:27 -08:00
} while ( page < end_addr ) ;
2006-05-11 10:03:08 +03:00
}
2009-11-12 11:32:27 -08:00
2013-07-28 12:35:40 +03:00
* data_size = total_sz ;
iser_dbg ( " page_vec->data_size:%d cur_page %d \n " ,
* data_size , cur_page ) ;
2006-05-11 10:03:08 +03: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 11:32:27 -08:00
static int iser_data_buf_aligned_len ( struct iser_data_buf * data ,
struct ib_device * ibdev )
2006-05-11 10:03:08 +03:00
{
2015-04-14 18:08:15 +03:00
struct scatterlist * sg , * sgl , * next_sg = NULL ;
2009-11-12 11:32:27 -08:00
u64 start_addr , end_addr ;
int i , ret_len , start_check = 0 ;
if ( data - > dma_nents = = 1 )
return 1 ;
2006-05-11 10:03:08 +03:00
2015-04-14 18:08:15 +03:00
sgl = data - > sg ;
2009-11-12 11:32:27 -08:00
start_addr = ib_sg_dma_address ( ibdev , sgl ) ;
2006-05-11 10:03:08 +03:00
2007-07-24 14:41:13 +02:00
for_each_sg ( sgl , sg , data - > dma_nents , i ) {
2009-11-12 11:32:27 -08: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 10:03:08 +03:00
}
2009-11-12 11:32:27 -08:00
ret_len = ( next_sg ) ? i : i + 1 ;
2006-05-11 10:03:08 +03:00
iser_dbg ( " Found %d aligned entries out of %d in sg:0x%p \n " ,
ret_len , data - > dma_nents , data ) ;
return ret_len ;
}
2006-12-12 14:31:00 -08:00
static void iser_data_buf_dump ( struct iser_data_buf * data ,
struct ib_device * ibdev )
2006-05-11 10:03:08 +03:00
{
2007-07-24 14:41:13 +02:00
struct scatterlist * sg ;
2006-05-11 10:03:08 +03:00
int i ;
2015-04-14 18:08:15 +03:00
for_each_sg ( data - > sg , sg , data - > dma_nents , i )
2013-07-28 12:35:36 +03:00
iser_dbg ( " sg[%d] dma_addr:0x%lX page:0x%p "
2006-09-11 12:24:00 +03:00
" off:0x%x sz:0x%x dma_len:0x%x \n " ,
2007-07-24 14:41:13 +02:00
i , ( unsigned long ) ib_sg_dma_address ( ibdev , sg ) ,
2007-10-22 21:19:53 +02:00
sg_page ( sg ) , sg - > offset ,
2007-07-24 14:41:13 +02:00
sg - > length , ib_sg_dma_len ( ibdev , sg ) ) ;
2006-05-11 10:03:08 +03: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 ] ) ;
}
static void iser_page_vec_build ( struct iser_data_buf * data ,
2006-12-12 14:31:00 -08:00
struct iser_page_vec * page_vec ,
struct ib_device * ibdev )
2006-05-11 10:03:08 +03:00
{
int page_vec_len = 0 ;
page_vec - > length = 0 ;
page_vec - > offset = 0 ;
iser_dbg ( " Translating sg sz: %d \n " , data - > dma_nents ) ;
2013-07-28 12:35:40 +03:00
page_vec_len = iser_sg_to_page_vec ( data , ibdev , page_vec - > pages ,
& page_vec - > offset ,
& page_vec - > data_size ) ;
iser_dbg ( " sg len %d page_vec_len %d \n " , data - > dma_nents , page_vec_len ) ;
2006-05-11 10:03:08 +03:00
page_vec - > length = page_vec_len ;
2006-09-11 12:22:30 +03:00
if ( page_vec_len * SIZE_4K < page_vec - > data_size ) {
2006-05-11 10:03:08 +03:00
iser_err ( " page_vec too short to hold this SG \n " ) ;
2006-12-12 14:31:00 -08:00
iser_data_buf_dump ( data , ibdev ) ;
2006-05-11 10:03:08 +03:00
iser_dump_page_vec ( page_vec ) ;
BUG ( ) ;
}
}
2008-05-21 15:54:11 -05: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 16:43:06 +03:00
{
2006-12-12 14:31:00 -08:00
struct ib_device * dev ;
2006-09-27 16:43:06 +03:00
2008-05-21 15:54:11 -05:00
iser_task - > dir [ iser_dir ] = 1 ;
2014-10-01 14:01:58 +03:00
dev = iser_task - > iser_conn - > ib_conn . device - > ib_device ;
2006-09-27 16:43:06 +03: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 16:43:06 +03:00
if ( data - > dma_nents = = 0 ) {
iser_err ( " dma_map_sg failed!!! \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2014-03-05 19:43:44 +02:00
void iser_dma_unmap_task_data ( struct iscsi_iser_task * iser_task ,
2014-12-28 14:26:11 +02:00
struct iser_data_buf * data ,
enum dma_data_direction dir )
2006-09-27 16:43:06 +03:00
{
2006-12-12 14:31:00 -08:00
struct ib_device * dev ;
2006-09-27 16:43:06 +03:00
2014-10-01 14:01:58 +03: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 16:43:06 +03:00
}
2013-07-28 12:35:40 +03:00
static int fall_to_bounce_buf ( struct iscsi_iser_task * iser_task ,
struct ib_device * ibdev ,
2014-03-05 19:43:45 +02:00
struct iser_data_buf * mem ,
2013-07-28 12:35:40 +03:00
enum iser_data_dir cmd_dir ,
int aligned_len )
{
2014-10-01 14:01:57 +03:00
struct iscsi_conn * iscsi_conn = iser_task - > iser_conn - > iscsi_conn ;
2013-07-28 12:35:40 +03:00
iscsi_conn - > fmr_unalign_cnt + + ;
iser_warn ( " rdma alignment violation (%d/%d aligned) or FMR not supported \n " ,
aligned_len , mem - > size ) ;
if ( iser_debug_level > 0 )
iser_data_buf_dump ( mem , ibdev ) ;
/* unmap the command data before accessing it */
2014-12-28 14:26:11 +02:00
iser_dma_unmap_task_data ( iser_task , mem ,
( cmd_dir = = ISER_DIR_OUT ) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
2013-07-28 12:35:40 +03: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 19:43:45 +02:00
return - ENOMEM ;
2013-07-28 12:35:40 +03:00
return 0 ;
}
2006-05-11 10:03:08 +03:00
/**
2013-07-28 12:35:41 +03:00
* iser_reg_rdma_mem_fmr - Registers memory intended for RDMA ,
* using FMR ( if possible ) obtaining rkey and va
2006-05-11 10:03:08 +03:00
*
* returns 0 on success , errno code on failure
*/
2013-07-28 12:35:41 +03:00
int iser_reg_rdma_mem_fmr ( struct iscsi_iser_task * iser_task ,
enum iser_data_dir cmd_dir )
2006-05-11 10:03:08 +03:00
{
2014-10-01 14:01:58 +03:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
struct iser_device * device = ib_conn - > device ;
2006-12-12 14:31:00 -08:00
struct ib_device * ibdev = device - > ib_device ;
2008-05-21 15:54:11 -05:00
struct iser_data_buf * mem = & iser_task - > data [ cmd_dir ] ;
2006-05-11 10:03:08 +03:00
struct iser_regd_buf * regd_buf ;
int aligned_len ;
int err ;
2006-09-11 12:24:00 +03:00
int i ;
2006-09-11 12:26:33 +03:00
struct scatterlist * sg ;
2006-05-11 10:03:08 +03:00
2008-05-21 15:54:11 -05:00
regd_buf = & iser_task - > rdma_regd [ cmd_dir ] ;
2006-05-11 10:03:08 +03:00
2006-12-12 14:31:00 -08:00
aligned_len = iser_data_buf_aligned_len ( mem , ibdev ) ;
2013-07-28 12:35:42 +03:00
if ( aligned_len ! = mem - > dma_nents ) {
2014-03-05 19:43:45 +02:00
err = fall_to_bounce_buf ( iser_task , ibdev , mem ,
2013-07-28 12:35:40 +03:00
cmd_dir , aligned_len ) ;
if ( err ) {
iser_err ( " failed to allocate bounce buffer \n " ) ;
return err ;
}
2006-05-11 10:03:08 +03:00
}
2006-09-11 12:26:33 +03:00
/* if there a single dma entry, FMR is not needed */
if ( mem - > dma_nents = = 1 ) {
2015-04-14 18:08:15 +03:00
sg = mem - > sg ;
2006-09-11 12:26:33 +03:00
regd_buf - > reg . lkey = device - > mr - > lkey ;
regd_buf - > reg . rkey = device - > mr - > rkey ;
2006-12-12 14:31:00 -08:00
regd_buf - > reg . len = ib_sg_dma_len ( ibdev , & sg [ 0 ] ) ;
regd_buf - > reg . va = ib_sg_dma_address ( ibdev , & sg [ 0 ] ) ;
2006-09-11 12:26:33 +03:00
iser_dbg ( " PHYSICAL Mem.register: lkey: 0x%08X rkey: 0x%08X "
" va: 0x%08lX sz: %ld] \n " ,
( unsigned int ) regd_buf - > reg . lkey ,
( unsigned int ) regd_buf - > reg . rkey ,
( unsigned long ) regd_buf - > reg . va ,
( unsigned long ) regd_buf - > reg . len ) ;
} else { /* use FMR for multiple dma entries */
2014-10-01 14:01:58 +03:00
iser_page_vec_build ( mem , ib_conn - > fmr . page_vec , ibdev ) ;
err = iser_reg_page_vec ( ib_conn , ib_conn - > fmr . page_vec ,
2013-07-28 12:35:41 +03:00
& regd_buf - > reg ) ;
2013-02-21 14:50:09 +00:00
if ( err & & err ! = - EAGAIN ) {
2006-12-12 14:31:00 -08:00
iser_data_buf_dump ( mem , ibdev ) ;
2008-05-21 15:54:11 -05:00
iser_err ( " mem->dma_nents = %d (dlength = 0x%x) \n " ,
mem - > dma_nents ,
ntoh24 ( iser_task - > desc . iscsi_header . dlength ) ) ;
2006-09-11 12:26:33 +03:00
iser_err ( " page_vec: data_size = 0x%x, length = %d, offset = 0x%x \n " ,
2014-10-01 14:01:58 +03: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 12:26:33 +03:00
iser_err ( " page_vec[%d] = 0x%llx \n " , i ,
2014-10-01 14:01:58 +03:00
( unsigned long long ) ib_conn - > fmr . page_vec - > pages [ i ] ) ;
2006-09-11 12:24:00 +03:00
}
2013-05-01 13:25:26 +00:00
if ( err )
return err ;
2006-09-11 12:24:00 +03:00
}
2006-05-11 10:03:08 +03:00
return 0 ;
}
2013-07-28 12:35:42 +03:00
2014-12-07 16:10:06 +02:00
static void
2014-08-13 19:54:33 +03:00
iser_set_dif_domain ( struct scsi_cmnd * sc , struct ib_sig_attrs * sig_attrs ,
struct ib_sig_domain * domain )
{
2014-08-13 19:54:35 +03:00
domain - > sig_type = IB_SIG_TYPE_T10_DIF ;
2014-12-07 16:10:06 +02:00
domain - > sig . dif . pi_interval = scsi_prot_interval ( sc ) ;
domain - > sig . dif . ref_tag = scsi_prot_ref_tag ( sc ) ;
2014-08-13 19:54:35 +03: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 16:10:06 +02:00
if ( sc - > prot_flags & SCSI_PROT_REF_INCREMENT )
2014-08-13 19:54:35 +03:00
domain - > sig . dif . ref_remap = true ;
2014-08-13 19:54:33 +03:00
} ;
2014-03-05 19:43:48 +02: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 19:54:35 +03:00
sig_attrs - > mem . sig_type = IB_SIG_TYPE_NONE ;
2014-08-13 19:54:33 +03:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > wire ) ;
2014-03-05 19:43:48 +02: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 19:54:35 +03:00
sig_attrs - > wire . sig_type = IB_SIG_TYPE_NONE ;
2014-08-13 19:54:33 +03:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > mem ) ;
2014-12-07 16:10:06 +02:00
sig_attrs - > mem . sig . dif . bg_type = sc - > prot_flags & SCSI_PROT_IP_CHECKSUM ?
IB_T10DIF_CSUM : IB_T10DIF_CRC ;
2014-03-05 19:43:48 +02:00
break ;
case SCSI_PROT_READ_PASS :
case SCSI_PROT_WRITE_PASS :
2014-08-13 19:54:33 +03:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > wire ) ;
2014-03-05 19:43:48 +02:00
sig_attrs - > wire . sig . dif . bg_type = IB_T10DIF_CRC ;
2014-08-13 19:54:33 +03:00
iser_set_dif_domain ( sc , sig_attrs , & sig_attrs - > mem ) ;
2014-12-07 16:10:06 +02:00
sig_attrs - > mem . sig . dif . bg_type = sc - > prot_flags & SCSI_PROT_IP_CHECKSUM ?
IB_T10DIF_CSUM : IB_T10DIF_CRC ;
2014-03-05 19:43:48 +02:00
break ;
default :
iser_err ( " Unsupported PI operation %d \n " ,
scsi_get_prot_op ( sc ) ) ;
return - EINVAL ;
}
2014-08-13 19:54:35 +03:00
2014-03-05 19:43:48 +02:00
return 0 ;
}
2014-12-07 16:10:06 +02:00
static inline void
2014-03-05 19:43:48 +02:00
iser_set_prot_checks ( struct scsi_cmnd * sc , u8 * mask )
{
2014-12-07 16:10:06 +02: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 19:43:48 +02:00
}
2014-12-07 16:10:01 +02: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 19:43:48 +02:00
static int
iser_reg_sig_mr ( struct iscsi_iser_task * iser_task ,
struct fast_reg_descriptor * desc , struct ib_sge * data_sge ,
struct ib_sge * prot_sge , struct ib_sge * sig_sge )
{
2014-10-01 14:01:58 +03:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
2014-03-05 19:43:48 +02:00
struct iser_pi_context * pi_ctx = desc - > pi_ctx ;
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 16:10:06 +02:00
iser_set_prot_checks ( iser_task - > sc , & sig_attrs . check_mask ) ;
2014-03-05 19:43:48 +02:00
if ( ! ( desc - > reg_indicators & ISER_SIG_KEY_VALID ) ) {
2014-12-07 16:10:01 +02:00
iser_inv_rkey ( & inv_wr , pi_ctx - > sig_mr ) ;
2014-03-05 19:43:48 +02: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 ;
sig_wr . sg_list = data_sge ;
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 ) )
sig_wr . wr . sig_handover . prot = prot_sge ;
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 14:01:58 +03:00
ret = ib_post_send ( ib_conn - > qp , wr , & bad_wr ) ;
2014-03-05 19:43:48 +02:00
if ( ret ) {
iser_err ( " reg_sig_mr failed, ret:%d \n " , ret ) ;
goto err ;
}
desc - > reg_indicators & = ~ ISER_SIG_KEY_VALID ;
sig_sge - > lkey = pi_ctx - > sig_mr - > lkey ;
sig_sge - > addr = 0 ;
2014-12-07 16:10:06 +02:00
sig_sge - > length = scsi_transfer_length ( iser_task - > sc ) ;
2014-03-05 19:43:48 +02:00
iser_dbg ( " sig_sge: addr: 0x%llx length: %u lkey: 0x%x \n " ,
sig_sge - > addr , sig_sge - > length ,
sig_sge - > lkey ) ;
err :
return ret ;
}
2014-03-05 19:43:40 +02:00
static int iser_fast_reg_mr ( struct iscsi_iser_task * iser_task ,
2013-07-28 12:35:42 +03:00
struct iser_regd_buf * regd_buf ,
2014-03-05 19:43:40 +02:00
struct iser_data_buf * mem ,
2014-03-05 19:43:48 +02:00
enum iser_reg_indicator ind ,
2014-03-05 19:43:40 +02:00
struct ib_sge * sge )
2013-07-28 12:35:42 +03:00
{
2014-03-05 19:43:40 +02:00
struct fast_reg_descriptor * desc = regd_buf - > reg . mem_h ;
2014-10-01 14:01:58 +03:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
struct iser_device * device = ib_conn - > device ;
2014-03-05 19:43:40 +02:00
struct ib_device * ibdev = device - > ib_device ;
2014-03-05 19:43:48 +02:00
struct ib_mr * mr ;
struct ib_fast_reg_page_list * frpl ;
2013-07-28 12:35:42 +03:00
struct ib_send_wr fastreg_wr , inv_wr ;
struct ib_send_wr * bad_wr , * wr = NULL ;
2014-03-05 19:43:40 +02:00
int ret , offset , size , plen ;
/* if there a single dma entry, dma mr suffices */
if ( mem - > dma_nents = = 1 ) {
2015-04-14 18:08:15 +03:00
struct scatterlist * sg = mem - > sg ;
2014-03-05 19:43:40 +02:00
sge - > lkey = device - > mr - > lkey ;
sge - > addr = ib_sg_dma_address ( ibdev , & sg [ 0 ] ) ;
sge - > length = ib_sg_dma_len ( ibdev , & sg [ 0 ] ) ;
iser_dbg ( " Single DMA entry: lkey=0x%x, addr=0x%llx, length=0x%x \n " ,
sge - > lkey , sge - > addr , sge - > length ) ;
return 0 ;
}
2014-03-05 19:43:48 +02:00
if ( ind = = ISER_DATA_KEY_VALID ) {
mr = desc - > data_mr ;
frpl = desc - > data_frpl ;
} else {
mr = desc - > pi_ctx - > prot_mr ;
frpl = desc - > pi_ctx - > prot_frpl ;
}
plen = iser_sg_to_page_vec ( mem , device - > ib_device , frpl - > page_list ,
2014-03-05 19:43:40 +02: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 12:35:42 +03:00
2014-03-05 19:43:48 +02:00
if ( ! ( desc - > reg_indicators & ind ) ) {
2014-12-07 16:10:01 +02:00
iser_inv_rkey ( & inv_wr , mr ) ;
2013-07-28 12:35:42 +03:00
wr = & inv_wr ;
}
/* Prepare FASTREG WR */
memset ( & fastreg_wr , 0 , sizeof ( fastreg_wr ) ) ;
2014-03-05 19:43:39 +02:00
fastreg_wr . wr_id = ISER_FASTREG_LI_WRID ;
2013-07-28 12:35:42 +03:00
fastreg_wr . opcode = IB_WR_FAST_REG_MR ;
2014-03-05 19:43:48 +02:00
fastreg_wr . wr . fast_reg . iova_start = frpl - > page_list [ 0 ] + offset ;
fastreg_wr . wr . fast_reg . page_list = frpl ;
2014-03-05 19:43:40 +02:00
fastreg_wr . wr . fast_reg . page_list_len = plen ;
2013-07-28 12:35:42 +03:00
fastreg_wr . wr . fast_reg . page_shift = SHIFT_4K ;
2014-03-05 19:43:40 +02:00
fastreg_wr . wr . fast_reg . length = size ;
2014-03-05 19:43:48 +02:00
fastreg_wr . wr . fast_reg . rkey = mr - > rkey ;
2013-07-28 12:35:42 +03:00
fastreg_wr . wr . fast_reg . access_flags = ( IB_ACCESS_LOCAL_WRITE |
IB_ACCESS_REMOTE_WRITE |
IB_ACCESS_REMOTE_READ ) ;
2014-01-23 12:31:28 +02:00
if ( ! wr )
2013-07-28 12:35:42 +03:00
wr = & fastreg_wr ;
2014-01-23 12:31:28 +02:00
else
2013-07-28 12:35:42 +03:00
wr - > next = & fastreg_wr ;
2014-10-01 14:01:58 +03:00
ret = ib_post_send ( ib_conn - > qp , wr , & bad_wr ) ;
2013-07-28 12:35:42 +03:00
if ( ret ) {
iser_err ( " fast registration failed, ret:%d \n " , ret ) ;
return ret ;
}
2014-03-05 19:43:48 +02:00
desc - > reg_indicators & = ~ ind ;
2013-07-28 12:35:42 +03:00
2014-03-05 19:43:48 +02:00
sge - > lkey = mr - > lkey ;
sge - > addr = frpl - > page_list [ 0 ] + offset ;
2014-03-05 19:43:40 +02:00
sge - > length = size ;
2013-07-28 12:35:42 +03:00
return ret ;
}
/**
2014-03-05 19:43:39 +02:00
* iser_reg_rdma_mem_fastreg - Registers memory intended for RDMA ,
2013-07-28 12:35:42 +03:00
* using Fast Registration WR ( if possible ) obtaining rkey and va
*
* returns 0 on success , errno code on failure
*/
2014-03-05 19:43:39 +02:00
int iser_reg_rdma_mem_fastreg ( struct iscsi_iser_task * iser_task ,
enum iser_data_dir cmd_dir )
2013-07-28 12:35:42 +03:00
{
2014-10-01 14:01:58 +03:00
struct ib_conn * ib_conn = & iser_task - > iser_conn - > ib_conn ;
struct iser_device * device = ib_conn - > device ;
2013-07-28 12:35:42 +03:00
struct ib_device * ibdev = device - > ib_device ;
struct iser_data_buf * mem = & iser_task - > data [ cmd_dir ] ;
struct iser_regd_buf * regd_buf = & iser_task - > rdma_regd [ cmd_dir ] ;
2014-03-05 19:43:40 +02:00
struct fast_reg_descriptor * desc = NULL ;
struct ib_sge data_sge ;
2013-07-28 12:35:42 +03:00
int err , aligned_len ;
unsigned long flags ;
aligned_len = iser_data_buf_aligned_len ( mem , ibdev ) ;
if ( aligned_len ! = mem - > dma_nents ) {
2014-03-05 19:43:45 +02:00
err = fall_to_bounce_buf ( iser_task , ibdev , mem ,
2013-07-28 12:35:42 +03:00
cmd_dir , aligned_len ) ;
if ( err ) {
iser_err ( " failed to allocate bounce buffer \n " ) ;
return err ;
}
}
2014-03-05 19:43:48 +02:00
if ( mem - > dma_nents ! = 1 | |
scsi_get_prot_op ( iser_task - > sc ) ! = SCSI_PROT_NORMAL ) {
2014-10-01 14:01:58 +03:00
spin_lock_irqsave ( & ib_conn - > lock , flags ) ;
desc = list_first_entry ( & ib_conn - > fastreg . pool ,
2013-07-28 12:35:42 +03:00
struct fast_reg_descriptor , list ) ;
list_del ( & desc - > list ) ;
2014-10-01 14:01:58 +03:00
spin_unlock_irqrestore ( & ib_conn - > lock , flags ) ;
2014-03-05 19:43:40 +02:00
regd_buf - > reg . mem_h = desc ;
}
2013-07-28 12:35:42 +03:00
2014-03-05 19:43:48 +02:00
err = iser_fast_reg_mr ( iser_task , regd_buf , mem ,
ISER_DATA_KEY_VALID , & data_sge ) ;
2014-03-05 19:43:40 +02:00
if ( err )
goto err_reg ;
2014-03-05 19:43:48 +02:00
if ( scsi_get_prot_op ( iser_task - > sc ) ! = SCSI_PROT_NORMAL ) {
struct ib_sge prot_sge , sig_sge ;
memset ( & prot_sge , 0 , sizeof ( prot_sge ) ) ;
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 ) {
err = fall_to_bounce_buf ( iser_task , ibdev , mem ,
cmd_dir , aligned_len ) ;
if ( err ) {
iser_err ( " failed to allocate bounce buffer \n " ) ;
return err ;
}
}
err = iser_fast_reg_mr ( iser_task , regd_buf , mem ,
ISER_PROT_KEY_VALID , & prot_sge ) ;
if ( err )
goto err_reg ;
}
err = iser_reg_sig_mr ( iser_task , desc , & data_sge ,
& prot_sge , & sig_sge ) ;
if ( err ) {
iser_err ( " Failed to register signature mr \n " ) ;
return err ;
}
desc - > reg_indicators | = ISER_FASTREG_PROTECTED ;
regd_buf - > reg . lkey = sig_sge . lkey ;
regd_buf - > reg . rkey = desc - > pi_ctx - > sig_mr - > rkey ;
regd_buf - > reg . va = sig_sge . addr ;
regd_buf - > reg . len = sig_sge . length ;
2014-03-05 19:43:40 +02:00
} else {
2014-12-07 16:10:02 +02:00
if ( desc )
2014-03-05 19:43:48 +02:00
regd_buf - > reg . rkey = desc - > data_mr - > rkey ;
2014-12-07 16:10:02 +02:00
else
2014-03-05 19:43:48 +02:00
regd_buf - > reg . rkey = device - > mr - > rkey ;
2013-07-28 12:35:42 +03:00
2014-03-05 19:43:48 +02:00
regd_buf - > reg . lkey = data_sge . lkey ;
regd_buf - > reg . va = data_sge . addr ;
regd_buf - > reg . len = data_sge . length ;
}
2014-03-05 19:43:40 +02:00
2013-07-28 12:35:42 +03:00
return 0 ;
err_reg :
2014-03-05 19:43:40 +02:00
if ( desc ) {
2014-10-01 14:01:58 +03:00
spin_lock_irqsave ( & ib_conn - > lock , flags ) ;
list_add_tail ( & desc - > list , & ib_conn - > fastreg . pool ) ;
spin_unlock_irqrestore ( & ib_conn - > lock , flags ) ;
2014-03-05 19:43:40 +02:00
}
2013-07-28 12:35:42 +03:00
return err ;
}