2015-08-14 05:47:53 +03:00
/*
* CXL Flash Device Driver
*
* Written by : Manoj N . Kumar < manoj @ linux . vnet . ibm . com > , IBM Corporation
* Matthew R . Ochs < mrochs @ linux . vnet . ibm . com > , IBM Corporation
*
* Copyright ( C ) 2015 IBM Corporation
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/syscalls.h>
# include <misc/cxl.h>
# include <asm/unaligned.h>
# include <asm/bitsperlong.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_host.h>
# include <uapi/scsi/cxlflash_ioctl.h>
# include "sislite.h"
# include "common.h"
# include "vlun.h"
# include "superpipe.h"
/**
* marshal_virt_to_resize ( ) - translate uvirtual to resize structure
* @ virt : Source structure from which to translate / copy .
* @ resize : Destination structure for the translate / copy .
*/
static void marshal_virt_to_resize ( struct dk_cxlflash_uvirtual * virt ,
struct dk_cxlflash_resize * resize )
{
resize - > hdr = virt - > hdr ;
resize - > context_id = virt - > context_id ;
resize - > rsrc_handle = virt - > rsrc_handle ;
resize - > req_size = virt - > lun_size ;
resize - > last_lba = virt - > last_lba ;
}
/**
* marshal_clone_to_rele ( ) - translate clone to release structure
* @ clone : Source structure from which to translate / copy .
* @ rele : Destination structure for the translate / copy .
*/
static void marshal_clone_to_rele ( struct dk_cxlflash_clone * clone ,
struct dk_cxlflash_release * release )
{
release - > hdr = clone - > hdr ;
release - > context_id = clone - > context_id_dst ;
}
/**
* ba_init ( ) - initializes a block allocator
* @ ba_lun : Block allocator to initialize .
*
* Return : 0 on success , - errno on failure
*/
static int ba_init ( struct ba_lun * ba_lun )
{
struct ba_lun_info * bali = NULL ;
int lun_size_au = 0 , i = 0 ;
int last_word_underflow = 0 ;
u64 * lam ;
pr_debug ( " %s: Initializing LUN: lun_id = %llX, "
" ba_lun->lsize = %lX, ba_lun->au_size = %lX \n " ,
__func__ , ba_lun - > lun_id , ba_lun - > lsize , ba_lun - > au_size ) ;
/* Calculate bit map size */
lun_size_au = ba_lun - > lsize / ba_lun - > au_size ;
if ( lun_size_au = = 0 ) {
pr_debug ( " %s: Requested LUN size of 0! \n " , __func__ ) ;
return - EINVAL ;
}
/* Allocate lun information container */
bali = kzalloc ( sizeof ( struct ba_lun_info ) , GFP_KERNEL ) ;
if ( unlikely ( ! bali ) ) {
pr_err ( " %s: Failed to allocate lun_info for lun_id %llX \n " ,
__func__ , ba_lun - > lun_id ) ;
return - ENOMEM ;
}
bali - > total_aus = lun_size_au ;
bali - > lun_bmap_size = lun_size_au / BITS_PER_LONG ;
if ( lun_size_au % BITS_PER_LONG )
bali - > lun_bmap_size + + ;
/* Allocate bitmap space */
bali - > lun_alloc_map = kzalloc ( ( bali - > lun_bmap_size * sizeof ( u64 ) ) ,
GFP_KERNEL ) ;
if ( unlikely ( ! bali - > lun_alloc_map ) ) {
pr_err ( " %s: Failed to allocate lun allocation map: "
" lun_id = %llX \n " , __func__ , ba_lun - > lun_id ) ;
kfree ( bali ) ;
return - ENOMEM ;
}
/* Initialize the bit map size and set all bits to '1' */
bali - > free_aun_cnt = lun_size_au ;
for ( i = 0 ; i < bali - > lun_bmap_size ; i + + )
bali - > lun_alloc_map [ i ] = 0xFFFFFFFFFFFFFFFFULL ;
/* If the last word not fully utilized, mark extra bits as allocated */
last_word_underflow = ( bali - > lun_bmap_size * BITS_PER_LONG ) ;
last_word_underflow - = bali - > free_aun_cnt ;
if ( last_word_underflow > 0 ) {
lam = & bali - > lun_alloc_map [ bali - > lun_bmap_size - 1 ] ;
for ( i = ( HIBIT - last_word_underflow + 1 ) ;
i < BITS_PER_LONG ;
i + + )
clear_bit ( i , ( ulong * ) lam ) ;
}
/* Initialize high elevator index, low/curr already at 0 from kzalloc */
bali - > free_high_idx = bali - > lun_bmap_size ;
/* Allocate clone map */
bali - > aun_clone_map = kzalloc ( ( bali - > total_aus * sizeof ( u8 ) ) ,
GFP_KERNEL ) ;
if ( unlikely ( ! bali - > aun_clone_map ) ) {
pr_err ( " %s: Failed to allocate clone map: lun_id = %llX \n " ,
__func__ , ba_lun - > lun_id ) ;
kfree ( bali - > lun_alloc_map ) ;
kfree ( bali ) ;
return - ENOMEM ;
}
/* Pass the allocated lun info as a handle to the user */
ba_lun - > ba_lun_handle = bali ;
pr_debug ( " %s: Successfully initialized the LUN: "
" lun_id = %llX, bitmap size = %X, free_aun_cnt = %llX \n " ,
__func__ , ba_lun - > lun_id , bali - > lun_bmap_size ,
bali - > free_aun_cnt ) ;
return 0 ;
}
/**
* find_free_range ( ) - locates a free bit within the block allocator
* @ low : First word in block allocator to start search .
* @ high : Last word in block allocator to search .
* @ bali : LUN information structure owning the block allocator to search .
* @ bit_word : Passes back the word in the block allocator owning the free bit .
*
* Return : The bit position within the passed back word , - 1 on failure
*/
static int find_free_range ( u32 low ,
u32 high ,
struct ba_lun_info * bali , int * bit_word )
{
int i ;
u64 bit_pos = - 1 ;
ulong * lam , num_bits ;
for ( i = low ; i < high ; i + + )
if ( bali - > lun_alloc_map [ i ] ! = 0 ) {
lam = ( ulong * ) & bali - > lun_alloc_map [ i ] ;
num_bits = ( sizeof ( * lam ) * BITS_PER_BYTE ) ;
bit_pos = find_first_bit ( lam , num_bits ) ;
pr_devel ( " %s: Found free bit %llX in lun "
" map entry %llX at bitmap index = %X \n " ,
__func__ , bit_pos , bali - > lun_alloc_map [ i ] ,
i ) ;
* bit_word = i ;
bali - > free_aun_cnt - - ;
clear_bit ( bit_pos , lam ) ;
break ;
}
return bit_pos ;
}
/**
* ba_alloc ( ) - allocates a block from the block allocator
* @ ba_lun : Block allocator from which to allocate a block .
*
* Return : The allocated block , - 1 on failure
*/
static u64 ba_alloc ( struct ba_lun * ba_lun )
{
u64 bit_pos = - 1 ;
int bit_word = 0 ;
struct ba_lun_info * bali = NULL ;
bali = ba_lun - > ba_lun_handle ;
pr_debug ( " %s: Received block allocation request: "
" lun_id = %llX, free_aun_cnt = %llX \n " ,
__func__ , ba_lun - > lun_id , bali - > free_aun_cnt ) ;
if ( bali - > free_aun_cnt = = 0 ) {
pr_debug ( " %s: No space left on LUN: lun_id = %llX \n " ,
__func__ , ba_lun - > lun_id ) ;
return - 1ULL ;
}
/* Search to find a free entry, curr->high then low->curr */
bit_pos = find_free_range ( bali - > free_curr_idx ,
bali - > free_high_idx , bali , & bit_word ) ;
if ( bit_pos = = - 1 ) {
bit_pos = find_free_range ( bali - > free_low_idx ,
bali - > free_curr_idx ,
bali , & bit_word ) ;
if ( bit_pos = = - 1 ) {
pr_debug ( " %s: Could not find an allocation unit on LUN: "
" lun_id = %llX \n " , __func__ , ba_lun - > lun_id ) ;
return - 1ULL ;
}
}
/* Update the free_curr_idx */
if ( bit_pos = = HIBIT )
bali - > free_curr_idx = bit_word + 1 ;
else
bali - > free_curr_idx = bit_word ;
pr_debug ( " %s: Allocating AU number %llX, on lun_id %llX, "
" free_aun_cnt = %llX \n " , __func__ ,
( ( bit_word * BITS_PER_LONG ) + bit_pos ) , ba_lun - > lun_id ,
bali - > free_aun_cnt ) ;
return ( u64 ) ( ( bit_word * BITS_PER_LONG ) + bit_pos ) ;
}
/**
* validate_alloc ( ) - validates the specified block has been allocated
* @ ba_lun_info : LUN info owning the block allocator .
* @ aun : Block to validate .
*
* Return : 0 on success , - 1 on failure
*/
static int validate_alloc ( struct ba_lun_info * bali , u64 aun )
{
int idx = 0 , bit_pos = 0 ;
idx = aun / BITS_PER_LONG ;
bit_pos = aun % BITS_PER_LONG ;
if ( test_bit ( bit_pos , ( ulong * ) & bali - > lun_alloc_map [ idx ] ) )
return - 1 ;
return 0 ;
}
/**
* ba_free ( ) - frees a block from the block allocator
* @ ba_lun : Block allocator from which to allocate a block .
* @ to_free : Block to free .
*
* Return : 0 on success , - 1 on failure
*/
static int ba_free ( struct ba_lun * ba_lun , u64 to_free )
{
int idx = 0 , bit_pos = 0 ;
struct ba_lun_info * bali = NULL ;
bali = ba_lun - > ba_lun_handle ;
if ( validate_alloc ( bali , to_free ) ) {
pr_debug ( " %s: The AUN %llX is not allocated on lun_id %llX \n " ,
__func__ , to_free , ba_lun - > lun_id ) ;
return - 1 ;
}
pr_debug ( " %s: Received a request to free AU %llX on lun_id %llX, "
" free_aun_cnt = %llX \n " , __func__ , to_free , ba_lun - > lun_id ,
bali - > free_aun_cnt ) ;
if ( bali - > aun_clone_map [ to_free ] > 0 ) {
pr_debug ( " %s: AUN %llX on lun_id %llX has been cloned. Clone "
" count = %X \n " , __func__ , to_free , ba_lun - > lun_id ,
bali - > aun_clone_map [ to_free ] ) ;
bali - > aun_clone_map [ to_free ] - - ;
return 0 ;
}
idx = to_free / BITS_PER_LONG ;
bit_pos = to_free % BITS_PER_LONG ;
set_bit ( bit_pos , ( ulong * ) & bali - > lun_alloc_map [ idx ] ) ;
bali - > free_aun_cnt + + ;
if ( idx < bali - > free_low_idx )
bali - > free_low_idx = idx ;
else if ( idx > bali - > free_high_idx )
bali - > free_high_idx = idx ;
pr_debug ( " %s: Successfully freed AU at bit_pos %X, bit map index %X on "
" lun_id %llX, free_aun_cnt = %llX \n " , __func__ , bit_pos , idx ,
ba_lun - > lun_id , bali - > free_aun_cnt ) ;
return 0 ;
}
/**
* ba_clone ( ) - Clone a chunk of the block allocation table
* @ ba_lun : Block allocator from which to allocate a block .
* @ to_free : Block to free .
*
* Return : 0 on success , - 1 on failure
*/
static int ba_clone ( struct ba_lun * ba_lun , u64 to_clone )
{
struct ba_lun_info * bali = ba_lun - > ba_lun_handle ;
if ( validate_alloc ( bali , to_clone ) ) {
pr_debug ( " %s: AUN %llX is not allocated on lun_id %llX \n " ,
__func__ , to_clone , ba_lun - > lun_id ) ;
return - 1 ;
}
pr_debug ( " %s: Received a request to clone AUN %llX on lun_id %llX \n " ,
__func__ , to_clone , ba_lun - > lun_id ) ;
if ( bali - > aun_clone_map [ to_clone ] = = MAX_AUN_CLONE_CNT ) {
pr_debug ( " %s: AUN %llX on lun_id %llX hit max clones already \n " ,
__func__ , to_clone , ba_lun - > lun_id ) ;
return - 1 ;
}
bali - > aun_clone_map [ to_clone ] + + ;
return 0 ;
}
/**
* ba_space ( ) - returns the amount of free space left in the block allocator
* @ ba_lun : Block allocator .
*
* Return : Amount of free space in block allocator
*/
static u64 ba_space ( struct ba_lun * ba_lun )
{
struct ba_lun_info * bali = ba_lun - > ba_lun_handle ;
return bali - > free_aun_cnt ;
}
/**
* cxlflash_ba_terminate ( ) - frees resources associated with the block allocator
* @ ba_lun : Block allocator .
*
* Safe to call in a partially allocated state .
*/
void cxlflash_ba_terminate ( struct ba_lun * ba_lun )
{
struct ba_lun_info * bali = ba_lun - > ba_lun_handle ;
if ( bali ) {
kfree ( bali - > aun_clone_map ) ;
kfree ( bali - > lun_alloc_map ) ;
kfree ( bali ) ;
ba_lun - > ba_lun_handle = NULL ;
}
}
/**
* init_vlun ( ) - initializes a LUN for virtual use
* @ lun_info : LUN information structure that owns the block allocator .
*
* Return : 0 on success , - errno on failure
*/
static int init_vlun ( struct llun_info * lli )
{
int rc = 0 ;
struct glun_info * gli = lli - > parent ;
struct blka * blka = & gli - > blka ;
memset ( blka , 0 , sizeof ( * blka ) ) ;
mutex_init ( & blka - > mutex ) ;
/* LUN IDs are unique per port, save the index instead */
blka - > ba_lun . lun_id = lli - > lun_index ;
blka - > ba_lun . lsize = gli - > max_lba + 1 ;
blka - > ba_lun . lba_size = gli - > blk_len ;
blka - > ba_lun . au_size = MC_CHUNK_SIZE ;
blka - > nchunk = blka - > ba_lun . lsize / MC_CHUNK_SIZE ;
rc = ba_init ( & blka - > ba_lun ) ;
if ( unlikely ( rc ) )
pr_debug ( " %s: cannot init block_alloc, rc=%d \n " , __func__ , rc ) ;
pr_debug ( " %s: returning rc=%d lli=%p \n " , __func__ , rc , lli ) ;
return rc ;
}
/**
* write_same16 ( ) - sends a SCSI WRITE_SAME16 ( 0 ) command to specified LUN
* @ sdev : SCSI device associated with LUN .
* @ lba : Logical block address to start write same .
* @ nblks : Number of logical blocks to write same .
*
* Return : 0 on success , - errno on failure
*/
static int write_same16 ( struct scsi_device * sdev ,
u64 lba ,
u32 nblks )
{
u8 * cmd_buf = NULL ;
u8 * scsi_cmd = NULL ;
u8 * sense_buf = NULL ;
int rc = 0 ;
int result = 0 ;
int ws_limit = SISLITE_MAX_WS_BLOCKS ;
u64 offset = lba ;
int left = nblks ;
2015-10-21 23:11:10 +03:00
u32 to = sdev - > request_queue - > rq_timeout ;
2015-08-14 05:47:53 +03:00
struct cxlflash_cfg * cfg = ( struct cxlflash_cfg * ) sdev - > host - > hostdata ;
struct device * dev = & cfg - > dev - > dev ;
cmd_buf = kzalloc ( CMD_BUFSIZE , GFP_KERNEL ) ;
scsi_cmd = kzalloc ( MAX_COMMAND_SIZE , GFP_KERNEL ) ;
sense_buf = kzalloc ( SCSI_SENSE_BUFFERSIZE , GFP_KERNEL ) ;
if ( unlikely ( ! cmd_buf | | ! scsi_cmd | | ! sense_buf ) ) {
rc = - ENOMEM ;
goto out ;
}
while ( left > 0 ) {
scsi_cmd [ 0 ] = WRITE_SAME_16 ;
put_unaligned_be64 ( offset , & scsi_cmd [ 2 ] ) ;
put_unaligned_be32 ( ws_limit < left ? ws_limit : left ,
& scsi_cmd [ 10 ] ) ;
result = scsi_execute ( sdev , scsi_cmd , DMA_TO_DEVICE , cmd_buf ,
2015-10-21 23:11:10 +03:00
CMD_BUFSIZE , sense_buf , to , CMD_RETRIES ,
2015-10-21 23:11:00 +03:00
0 , NULL ) ;
2015-08-14 05:47:53 +03:00
if ( result ) {
dev_err_ratelimited ( dev , " %s: command failed for "
" offset %lld result=0x%x \n " ,
__func__ , offset , result ) ;
rc = - EIO ;
goto out ;
}
left - = ws_limit ;
offset + = ws_limit ;
}
out :
kfree ( cmd_buf ) ;
kfree ( scsi_cmd ) ;
kfree ( sense_buf ) ;
pr_debug ( " %s: returning rc=%d \n " , __func__ , rc ) ;
return rc ;
}
/**
* grow_lxt ( ) - expands the translation table associated with the specified RHTE
* @ afu : AFU associated with the host .
* @ sdev : SCSI device associated with LUN .
* @ ctxid : Context ID of context owning the RHTE .
* @ rhndl : Resource handle associated with the RHTE .
* @ rhte : Resource handle entry ( RHTE ) .
* @ new_size : Number of translation entries associated with RHTE .
*
* By design , this routine employs a ' best attempt ' allocation and will
* truncate the requested size down if there is not sufficient space in
* the block allocator to satisfy the request but there does exist some
* amount of space . The user is made aware of this by returning the size
* allocated .
*
* Return : 0 on success , - errno on failure
*/
static int grow_lxt ( struct afu * afu ,
struct scsi_device * sdev ,
ctx_hndl_t ctxid ,
res_hndl_t rhndl ,
struct sisl_rht_entry * rhte ,
u64 * new_size )
{
struct sisl_lxt_entry * lxt = NULL , * lxt_old = NULL ;
struct llun_info * lli = sdev - > hostdata ;
struct glun_info * gli = lli - > parent ;
struct blka * blka = & gli - > blka ;
u32 av_size ;
u32 ngrps , ngrps_old ;
u64 aun ; /* chunk# allocated by block allocator */
u64 delta = * new_size - rhte - > lxt_cnt ;
u64 my_new_size ;
int i , rc = 0 ;
/*
* Check what is available in the block allocator before re - allocating
* LXT array . This is done up front under the mutex which must not be
* released until after allocation is complete .
*/
mutex_lock ( & blka - > mutex ) ;
av_size = ba_space ( & blka - > ba_lun ) ;
if ( unlikely ( av_size < = 0 ) ) {
pr_debug ( " %s: ba_space error: av_size %d \n " , __func__ , av_size ) ;
mutex_unlock ( & blka - > mutex ) ;
rc = - ENOSPC ;
goto out ;
}
if ( av_size < delta )
delta = av_size ;
lxt_old = rhte - > lxt_start ;
ngrps_old = LXT_NUM_GROUPS ( rhte - > lxt_cnt ) ;
ngrps = LXT_NUM_GROUPS ( rhte - > lxt_cnt + delta ) ;
if ( ngrps ! = ngrps_old ) {
/* reallocate to fit new size */
lxt = kzalloc ( ( sizeof ( * lxt ) * LXT_GROUP_SIZE * ngrps ) ,
GFP_KERNEL ) ;
if ( unlikely ( ! lxt ) ) {
mutex_unlock ( & blka - > mutex ) ;
rc = - ENOMEM ;
goto out ;
}
/* copy over all old entries */
memcpy ( lxt , lxt_old , ( sizeof ( * lxt ) * rhte - > lxt_cnt ) ) ;
} else
lxt = lxt_old ;
/* nothing can fail from now on */
my_new_size = rhte - > lxt_cnt + delta ;
/* add new entries to the end */
for ( i = rhte - > lxt_cnt ; i < my_new_size ; i + + ) {
/*
* Due to the earlier check of available space , ba_alloc
* cannot fail here . If it did due to internal error ,
* leave a rlba_base of - 1u which will likely be a
* invalid LUN ( too large ) .
*/
aun = ba_alloc ( & blka - > ba_lun ) ;
if ( ( aun = = - 1ULL ) | | ( aun > = blka - > nchunk ) )
pr_debug ( " %s: ba_alloc error: allocated chunk# %llX, "
" max %llX \n " , __func__ , aun , blka - > nchunk - 1 ) ;
/* select both ports, use r/w perms from RHT */
lxt [ i ] . rlba_base = ( ( aun < < MC_CHUNK_SHIFT ) |
( lli - > lun_index < < LXT_LUNIDX_SHIFT ) |
( RHT_PERM_RW < < LXT_PERM_SHIFT |
lli - > port_sel ) ) ;
}
mutex_unlock ( & blka - > mutex ) ;
/*
* The following sequence is prescribed in the SISlite spec
* for syncing up with the AFU when adding LXT entries .
*/
dma_wmb ( ) ; /* Make LXT updates are visible */
rhte - > lxt_start = lxt ;
dma_wmb ( ) ; /* Make RHT entry's LXT table update visible */
rhte - > lxt_cnt = my_new_size ;
dma_wmb ( ) ; /* Make RHT entry's LXT table size update visible */
cxlflash_afu_sync ( afu , ctxid , rhndl , AFU_LW_SYNC ) ;
/* free old lxt if reallocated */
if ( lxt ! = lxt_old )
kfree ( lxt_old ) ;
* new_size = my_new_size ;
out :
pr_debug ( " %s: returning rc=%d \n " , __func__ , rc ) ;
return rc ;
}
/**
* shrink_lxt ( ) - reduces translation table associated with the specified RHTE
* @ afu : AFU associated with the host .
* @ sdev : SCSI device associated with LUN .
* @ rhndl : Resource handle associated with the RHTE .
* @ rhte : Resource handle entry ( RHTE ) .
* @ ctxi : Context owning resources .
* @ new_size : Number of translation entries associated with RHTE .
*
* Return : 0 on success , - errno on failure
*/
static int shrink_lxt ( struct afu * afu ,
struct scsi_device * sdev ,
res_hndl_t rhndl ,
struct sisl_rht_entry * rhte ,
struct ctx_info * ctxi ,
u64 * new_size )
{
struct sisl_lxt_entry * lxt , * lxt_old ;
struct llun_info * lli = sdev - > hostdata ;
struct glun_info * gli = lli - > parent ;
struct blka * blka = & gli - > blka ;
ctx_hndl_t ctxid = DECODE_CTXID ( ctxi - > ctxid ) ;
bool needs_ws = ctxi - > rht_needs_ws [ rhndl ] ;
bool needs_sync = ! ctxi - > err_recovery_active ;
u32 ngrps , ngrps_old ;
u64 aun ; /* chunk# allocated by block allocator */
u64 delta = rhte - > lxt_cnt - * new_size ;
u64 my_new_size ;
int i , rc = 0 ;
lxt_old = rhte - > lxt_start ;
ngrps_old = LXT_NUM_GROUPS ( rhte - > lxt_cnt ) ;
ngrps = LXT_NUM_GROUPS ( rhte - > lxt_cnt - delta ) ;
if ( ngrps ! = ngrps_old ) {
/* Reallocate to fit new size unless new size is 0 */
if ( ngrps ) {
lxt = kzalloc ( ( sizeof ( * lxt ) * LXT_GROUP_SIZE * ngrps ) ,
GFP_KERNEL ) ;
if ( unlikely ( ! lxt ) ) {
rc = - ENOMEM ;
goto out ;
}
/* Copy over old entries that will remain */
memcpy ( lxt , lxt_old ,
( sizeof ( * lxt ) * ( rhte - > lxt_cnt - delta ) ) ) ;
} else
lxt = NULL ;
} else
lxt = lxt_old ;
/* Nothing can fail from now on */
my_new_size = rhte - > lxt_cnt - delta ;
/*
* The following sequence is prescribed in the SISlite spec
* for syncing up with the AFU when removing LXT entries .
*/
rhte - > lxt_cnt = my_new_size ;
dma_wmb ( ) ; /* Make RHT entry's LXT table size update visible */
rhte - > lxt_start = lxt ;
dma_wmb ( ) ; /* Make RHT entry's LXT table update visible */
if ( needs_sync )
cxlflash_afu_sync ( afu , ctxid , rhndl , AFU_HW_SYNC ) ;
if ( needs_ws ) {
/*
* Mark the context as unavailable , so that we can release
* the mutex safely .
*/
ctxi - > unavail = true ;
mutex_unlock ( & ctxi - > mutex ) ;
}
/* Free LBAs allocated to freed chunks */
mutex_lock ( & blka - > mutex ) ;
for ( i = delta - 1 ; i > = 0 ; i - - ) {
/* Mask the higher 48 bits before shifting, even though
* it is a noop
*/
aun = ( lxt_old [ my_new_size + i ] . rlba_base & SISL_ASTATUS_MASK ) ;
aun = ( aun > > MC_CHUNK_SHIFT ) ;
if ( needs_ws )
write_same16 ( sdev , aun , MC_CHUNK_SIZE ) ;
ba_free ( & blka - > ba_lun , aun ) ;
}
mutex_unlock ( & blka - > mutex ) ;
if ( needs_ws ) {
/* Make the context visible again */
mutex_lock ( & ctxi - > mutex ) ;
ctxi - > unavail = false ;
}
/* Free old lxt if reallocated */
if ( lxt ! = lxt_old )
kfree ( lxt_old ) ;
* new_size = my_new_size ;
out :
pr_debug ( " %s: returning rc=%d \n " , __func__ , rc ) ;
return rc ;
}
/**
* _cxlflash_vlun_resize ( ) - changes the size of a virtual lun
* @ sdev : SCSI device associated with LUN owning virtual LUN .
* @ ctxi : Context owning resources .
* @ resize : Resize ioctl data structure .
*
* On successful return , the user is informed of the new size ( in blocks )
* of the virtual lun in last LBA format . When the size of the virtual
* lun is zero , the last LBA is reflected as - 1. See comment in the
* prologue for _cxlflash_disk_release ( ) regarding AFU syncs and contexts
* on the error recovery list .
*
* Return : 0 on success , - errno on failure
*/
int _cxlflash_vlun_resize ( struct scsi_device * sdev ,
struct ctx_info * ctxi ,
struct dk_cxlflash_resize * resize )
{
struct cxlflash_cfg * cfg = ( struct cxlflash_cfg * ) sdev - > host - > hostdata ;
struct llun_info * lli = sdev - > hostdata ;
struct glun_info * gli = lli - > parent ;
struct afu * afu = cfg - > afu ;
bool put_ctx = false ;
res_hndl_t rhndl = resize - > rsrc_handle ;
u64 new_size ;
u64 nsectors ;
u64 ctxid = DECODE_CTXID ( resize - > context_id ) ,
rctxid = resize - > context_id ;
struct sisl_rht_entry * rhte ;
int rc = 0 ;
/*
* The requested size ( req_size ) is always assumed to be in 4 k blocks ,
* so we have to convert it here from 4 k to chunk size .
*/
nsectors = ( resize - > req_size * CXLFLASH_BLOCK_SIZE ) / gli - > blk_len ;
new_size = DIV_ROUND_UP ( nsectors , MC_CHUNK_SIZE ) ;
pr_debug ( " %s: ctxid=%llu rhndl=0x%llx, req_size=0x%llx, "
" new_size=%llx \n " , __func__ , ctxid , resize - > rsrc_handle ,
resize - > req_size , new_size ) ;
if ( unlikely ( gli - > mode ! = MODE_VIRTUAL ) ) {
pr_debug ( " %s: LUN mode does not support resize! (%d) \n " ,
__func__ , gli - > mode ) ;
rc = - EINVAL ;
goto out ;
}
if ( ! ctxi ) {
ctxi = get_context ( cfg , rctxid , lli , CTX_CTRL_ERR_FALLBACK ) ;
if ( unlikely ( ! ctxi ) ) {
pr_debug ( " %s: Bad context! (%llu) \n " , __func__ , ctxid ) ;
rc = - EINVAL ;
goto out ;
}
put_ctx = true ;
}
rhte = get_rhte ( ctxi , rhndl , lli ) ;
if ( unlikely ( ! rhte ) ) {
pr_debug ( " %s: Bad resource handle! (%u) \n " , __func__ , rhndl ) ;
rc = - EINVAL ;
goto out ;
}
if ( new_size > rhte - > lxt_cnt )
rc = grow_lxt ( afu , sdev , ctxid , rhndl , rhte , & new_size ) ;
else if ( new_size < rhte - > lxt_cnt )
rc = shrink_lxt ( afu , sdev , rhndl , rhte , ctxi , & new_size ) ;
resize - > hdr . return_flags = 0 ;
resize - > last_lba = ( new_size * MC_CHUNK_SIZE * gli - > blk_len ) ;
resize - > last_lba / = CXLFLASH_BLOCK_SIZE ;
resize - > last_lba - - ;
out :
if ( put_ctx )
put_context ( ctxi ) ;
pr_debug ( " %s: resized to %lld returning rc=%d \n " ,
__func__ , resize - > last_lba , rc ) ;
return rc ;
}
int cxlflash_vlun_resize ( struct scsi_device * sdev ,
struct dk_cxlflash_resize * resize )
{
return _cxlflash_vlun_resize ( sdev , NULL , resize ) ;
}
/**
* cxlflash_restore_luntable ( ) - Restore LUN table to prior state
* @ cfg : Internal structure associated with the host .
*/
void cxlflash_restore_luntable ( struct cxlflash_cfg * cfg )
{
struct llun_info * lli , * temp ;
u32 chan ;
u32 lind ;
struct afu * afu = cfg - > afu ;
2015-10-21 23:14:48 +03:00
struct sisl_global_map __iomem * agm = & afu - > afu_map - > global ;
2015-08-14 05:47:53 +03:00
mutex_lock ( & global . mutex ) ;
list_for_each_entry_safe ( lli , temp , & cfg - > lluns , list ) {
if ( ! lli - > in_table )
continue ;
lind = lli - > lun_index ;
if ( lli - > port_sel = = BOTH_PORTS ) {
writeq_be ( lli - > lun_id [ 0 ] , & agm - > fc_port [ 0 ] [ lind ] ) ;
writeq_be ( lli - > lun_id [ 1 ] , & agm - > fc_port [ 1 ] [ lind ] ) ;
pr_debug ( " %s: Virtual LUN on slot %d id0=%llx, "
" id1=%llx \n " , __func__ , lind ,
lli - > lun_id [ 0 ] , lli - > lun_id [ 1 ] ) ;
} else {
chan = PORT2CHAN ( lli - > port_sel ) ;
writeq_be ( lli - > lun_id [ chan ] , & agm - > fc_port [ chan ] [ lind ] ) ;
pr_debug ( " %s: Virtual LUN on slot %d chan=%d, "
" id=%llx \n " , __func__ , lind , chan ,
lli - > lun_id [ chan ] ) ;
}
}
mutex_unlock ( & global . mutex ) ;
}
/**
* init_luntable ( ) - write an entry in the LUN table
* @ cfg : Internal structure associated with the host .
* @ lli : Per adapter LUN information structure .
*
* On successful return , a LUN table entry is created .
* At the top for LUNs visible on both ports .
* At the bottom for LUNs visible only on one port .
*
* Return : 0 on success , - errno on failure
*/
static int init_luntable ( struct cxlflash_cfg * cfg , struct llun_info * lli )
{
u32 chan ;
u32 lind ;
int rc = 0 ;
struct afu * afu = cfg - > afu ;
2015-10-21 23:14:48 +03:00
struct sisl_global_map __iomem * agm = & afu - > afu_map - > global ;
2015-08-14 05:47:53 +03:00
mutex_lock ( & global . mutex ) ;
if ( lli - > in_table )
goto out ;
if ( lli - > port_sel = = BOTH_PORTS ) {
/*
* If this LUN is visible from both ports , we will put
* it in the top half of the LUN table .
*/
if ( ( cfg - > promote_lun_index = = cfg - > last_lun_index [ 0 ] ) | |
( cfg - > promote_lun_index = = cfg - > last_lun_index [ 1 ] ) ) {
rc = - ENOSPC ;
goto out ;
}
lind = lli - > lun_index = cfg - > promote_lun_index ;
writeq_be ( lli - > lun_id [ 0 ] , & agm - > fc_port [ 0 ] [ lind ] ) ;
writeq_be ( lli - > lun_id [ 1 ] , & agm - > fc_port [ 1 ] [ lind ] ) ;
cfg - > promote_lun_index + + ;
pr_debug ( " %s: Virtual LUN on slot %d id0=%llx, id1=%llx \n " ,
__func__ , lind , lli - > lun_id [ 0 ] , lli - > lun_id [ 1 ] ) ;
} else {
/*
* If this LUN is visible only from one port , we will put
* it in the bottom half of the LUN table .
*/
chan = PORT2CHAN ( lli - > port_sel ) ;
if ( cfg - > promote_lun_index = = cfg - > last_lun_index [ chan ] ) {
rc = - ENOSPC ;
goto out ;
}
lind = lli - > lun_index = cfg - > last_lun_index [ chan ] ;
writeq_be ( lli - > lun_id [ chan ] , & agm - > fc_port [ chan ] [ lind ] ) ;
cfg - > last_lun_index [ chan ] - - ;
pr_debug ( " %s: Virtual LUN on slot %d chan=%d, id=%llx \n " ,
__func__ , lind , chan , lli - > lun_id [ chan ] ) ;
}
lli - > in_table = true ;
out :
mutex_unlock ( & global . mutex ) ;
pr_debug ( " %s: returning rc=%d \n " , __func__ , rc ) ;
return rc ;
}
/**
* cxlflash_disk_virtual_open ( ) - open a virtual disk of specified size
* @ sdev : SCSI device associated with LUN owning virtual LUN .
* @ arg : UVirtual ioctl data structure .
*
* On successful return , the user is informed of the resource handle
* to be used to identify the virtual lun and the size ( in blocks ) of
* the virtual lun in last LBA format . When the size of the virtual lun
* is zero , the last LBA is reflected as - 1.
*
* Return : 0 on success , - errno on failure
*/
int cxlflash_disk_virtual_open ( struct scsi_device * sdev , void * arg )
{
struct cxlflash_cfg * cfg = ( struct cxlflash_cfg * ) sdev - > host - > hostdata ;
struct device * dev = & cfg - > dev - > dev ;
struct llun_info * lli = sdev - > hostdata ;
struct glun_info * gli = lli - > parent ;
struct dk_cxlflash_uvirtual * virt = ( struct dk_cxlflash_uvirtual * ) arg ;
struct dk_cxlflash_resize resize ;
u64 ctxid = DECODE_CTXID ( virt - > context_id ) ,
rctxid = virt - > context_id ;
u64 lun_size = virt - > lun_size ;
u64 last_lba = 0 ;
u64 rsrc_handle = - 1 ;
int rc = 0 ;
struct ctx_info * ctxi = NULL ;
struct sisl_rht_entry * rhte = NULL ;
pr_debug ( " %s: ctxid=%llu ls=0x%llx \n " , __func__ , ctxid , lun_size ) ;
2015-10-21 23:11:26 +03:00
/* Setup the LUNs block allocator on first call */
2015-08-14 05:47:53 +03:00
mutex_lock ( & gli - > mutex ) ;
if ( gli - > mode = = MODE_NONE ) {
rc = init_vlun ( lli ) ;
if ( rc ) {
dev_err ( dev , " %s: call to init_vlun failed rc=%d! \n " ,
__func__ , rc ) ;
rc = - ENOMEM ;
goto err0 ;
}
}
rc = cxlflash_lun_attach ( gli , MODE_VIRTUAL , true ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: Failed to attach to LUN! (VIRTUAL) \n " ,
__func__ ) ;
goto err0 ;
}
mutex_unlock ( & gli - > mutex ) ;
2015-10-21 23:11:26 +03:00
rc = init_luntable ( cfg , lli ) ;
if ( rc ) {
dev_err ( dev , " %s: call to init_luntable failed rc=%d! \n " ,
__func__ , rc ) ;
goto err1 ;
}
2015-08-14 05:47:53 +03:00
ctxi = get_context ( cfg , rctxid , lli , 0 ) ;
if ( unlikely ( ! ctxi ) ) {
dev_err ( dev , " %s: Bad context! (%llu) \n " , __func__ , ctxid ) ;
rc = - EINVAL ;
goto err1 ;
}
rhte = rhte_checkout ( ctxi , lli ) ;
if ( unlikely ( ! rhte ) ) {
dev_err ( dev , " %s: too many opens for this context \n " , __func__ ) ;
rc = - EMFILE ; /* too many opens */
goto err1 ;
}
rsrc_handle = ( rhte - ctxi - > rht_start ) ;
/* Populate RHT format 0 */
rhte - > nmask = MC_RHT_NMASK ;
rhte - > fp = SISL_RHT_FP ( 0U , ctxi - > rht_perms ) ;
/* Resize even if requested size is 0 */
marshal_virt_to_resize ( virt , & resize ) ;
resize . rsrc_handle = rsrc_handle ;
rc = _cxlflash_vlun_resize ( sdev , ctxi , & resize ) ;
if ( rc ) {
dev_err ( dev , " %s: resize failed rc %d \n " , __func__ , rc ) ;
goto err2 ;
}
last_lba = resize . last_lba ;
if ( virt - > hdr . flags & DK_CXLFLASH_UVIRTUAL_NEED_WRITE_SAME )
ctxi - > rht_needs_ws [ rsrc_handle ] = true ;
virt - > hdr . return_flags = 0 ;
virt - > last_lba = last_lba ;
virt - > rsrc_handle = rsrc_handle ;
out :
if ( likely ( ctxi ) )
put_context ( ctxi ) ;
pr_debug ( " %s: returning handle 0x%llx rc=%d llba %lld \n " ,
__func__ , rsrc_handle , rc , last_lba ) ;
return rc ;
err2 :
rhte_checkin ( ctxi , rhte ) ;
err1 :
cxlflash_lun_detach ( gli ) ;
goto out ;
err0 :
/* Special common cleanup prior to successful LUN attach */
cxlflash_ba_terminate ( & gli - > blka . ba_lun ) ;
mutex_unlock ( & gli - > mutex ) ;
goto out ;
}
/**
* clone_lxt ( ) - copies translation tables from source to destination RHTE
* @ afu : AFU associated with the host .
* @ blka : Block allocator associated with LUN .
* @ ctxid : Context ID of context owning the RHTE .
* @ rhndl : Resource handle associated with the RHTE .
* @ rhte : Destination resource handle entry ( RHTE ) .
* @ rhte_src : Source resource handle entry ( RHTE ) .
*
* Return : 0 on success , - errno on failure
*/
static int clone_lxt ( struct afu * afu ,
struct blka * blka ,
ctx_hndl_t ctxid ,
res_hndl_t rhndl ,
struct sisl_rht_entry * rhte ,
struct sisl_rht_entry * rhte_src )
{
struct sisl_lxt_entry * lxt ;
u32 ngrps ;
u64 aun ; /* chunk# allocated by block allocator */
int i , j ;
ngrps = LXT_NUM_GROUPS ( rhte_src - > lxt_cnt ) ;
if ( ngrps ) {
/* allocate new LXTs for clone */
lxt = kzalloc ( ( sizeof ( * lxt ) * LXT_GROUP_SIZE * ngrps ) ,
GFP_KERNEL ) ;
if ( unlikely ( ! lxt ) )
return - ENOMEM ;
/* copy over */
memcpy ( lxt , rhte_src - > lxt_start ,
( sizeof ( * lxt ) * rhte_src - > lxt_cnt ) ) ;
/* clone the LBAs in block allocator via ref_cnt */
mutex_lock ( & blka - > mutex ) ;
for ( i = 0 ; i < rhte_src - > lxt_cnt ; i + + ) {
aun = ( lxt [ i ] . rlba_base > > MC_CHUNK_SHIFT ) ;
if ( ba_clone ( & blka - > ba_lun , aun ) = = - 1ULL ) {
/* free the clones already made */
for ( j = 0 ; j < i ; j + + ) {
aun = ( lxt [ j ] . rlba_base > >
MC_CHUNK_SHIFT ) ;
ba_free ( & blka - > ba_lun , aun ) ;
}
mutex_unlock ( & blka - > mutex ) ;
kfree ( lxt ) ;
return - EIO ;
}
}
mutex_unlock ( & blka - > mutex ) ;
} else {
lxt = NULL ;
}
/*
* The following sequence is prescribed in the SISlite spec
* for syncing up with the AFU when adding LXT entries .
*/
dma_wmb ( ) ; /* Make LXT updates are visible */
rhte - > lxt_start = lxt ;
dma_wmb ( ) ; /* Make RHT entry's LXT table update visible */
rhte - > lxt_cnt = rhte_src - > lxt_cnt ;
dma_wmb ( ) ; /* Make RHT entry's LXT table size update visible */
cxlflash_afu_sync ( afu , ctxid , rhndl , AFU_LW_SYNC ) ;
pr_debug ( " %s: returning \n " , __func__ ) ;
return 0 ;
}
/**
* cxlflash_disk_clone ( ) - clone a context by making snapshot of another
* @ sdev : SCSI device associated with LUN owning virtual LUN .
* @ clone : Clone ioctl data structure .
*
* This routine effectively performs cxlflash_disk_open operation for each
* in - use virtual resource in the source context . Note that the destination
* context must be in pristine state and cannot have any resource handles
* open at the time of the clone .
*
* Return : 0 on success , - errno on failure
*/
int cxlflash_disk_clone ( struct scsi_device * sdev ,
struct dk_cxlflash_clone * clone )
{
struct cxlflash_cfg * cfg = ( struct cxlflash_cfg * ) sdev - > host - > hostdata ;
struct llun_info * lli = sdev - > hostdata ;
struct glun_info * gli = lli - > parent ;
struct blka * blka = & gli - > blka ;
struct afu * afu = cfg - > afu ;
struct dk_cxlflash_release release = { { 0 } , 0 } ;
struct ctx_info * ctxi_src = NULL ,
* ctxi_dst = NULL ;
struct lun_access * lun_access_src , * lun_access_dst ;
u32 perms ;
u64 ctxid_src = DECODE_CTXID ( clone - > context_id_src ) ,
ctxid_dst = DECODE_CTXID ( clone - > context_id_dst ) ,
rctxid_src = clone - > context_id_src ,
rctxid_dst = clone - > context_id_dst ;
int adap_fd_src = clone - > adap_fd_src ;
int i , j ;
int rc = 0 ;
bool found ;
LIST_HEAD ( sidecar ) ;
pr_debug ( " %s: ctxid_src=%llu ctxid_dst=%llu adap_fd_src=%d \n " ,
__func__ , ctxid_src , ctxid_dst , adap_fd_src ) ;
/* Do not clone yourself */
if ( unlikely ( rctxid_src = = rctxid_dst ) ) {
rc = - EINVAL ;
goto out ;
}
if ( unlikely ( gli - > mode ! = MODE_VIRTUAL ) ) {
rc = - EINVAL ;
pr_debug ( " %s: Clone not supported on physical LUNs! (%d) \n " ,
__func__ , gli - > mode ) ;
goto out ;
}
ctxi_src = get_context ( cfg , rctxid_src , lli , CTX_CTRL_CLONE ) ;
ctxi_dst = get_context ( cfg , rctxid_dst , lli , 0 ) ;
if ( unlikely ( ! ctxi_src | | ! ctxi_dst ) ) {
pr_debug ( " %s: Bad context! (%llu,%llu) \n " , __func__ ,
ctxid_src , ctxid_dst ) ;
rc = - EINVAL ;
goto out ;
}
if ( unlikely ( adap_fd_src ! = ctxi_src - > lfd ) ) {
pr_debug ( " %s: Invalid source adapter fd! (%d) \n " ,
__func__ , adap_fd_src ) ;
rc = - EINVAL ;
goto out ;
}
/* Verify there is no open resource handle in the destination context */
for ( i = 0 ; i < MAX_RHT_PER_CONTEXT ; i + + )
if ( ctxi_dst - > rht_start [ i ] . nmask ! = 0 ) {
rc = - EINVAL ;
goto out ;
}
/* Clone LUN access list */
list_for_each_entry ( lun_access_src , & ctxi_src - > luns , list ) {
found = false ;
list_for_each_entry ( lun_access_dst , & ctxi_dst - > luns , list )
if ( lun_access_dst - > sdev = = lun_access_src - > sdev ) {
found = true ;
break ;
}
if ( ! found ) {
lun_access_dst = kzalloc ( sizeof ( * lun_access_dst ) ,
GFP_KERNEL ) ;
if ( unlikely ( ! lun_access_dst ) ) {
pr_err ( " %s: Unable to allocate lun_access! \n " ,
__func__ ) ;
rc = - ENOMEM ;
goto out ;
}
* lun_access_dst = * lun_access_src ;
list_add ( & lun_access_dst - > list , & sidecar ) ;
}
}
if ( unlikely ( ! ctxi_src - > rht_out ) ) {
pr_debug ( " %s: Nothing to clone! \n " , __func__ ) ;
goto out_success ;
}
/* User specified permission on attach */
perms = ctxi_dst - > rht_perms ;
/*
* Copy over checked - out RHT ( and their associated LXT ) entries by
* hand , stopping after we ' ve copied all outstanding entries and
* cleaning up if the clone fails .
*
* Note : This loop is equivalent to performing cxlflash_disk_open and
* cxlflash_vlun_resize . As such , LUN accounting needs to be taken into
* account by attaching after each successful RHT entry clone . In the
* event that a clone failure is experienced , the LUN detach is handled
* via the cleanup performed by _cxlflash_disk_release .
*/
for ( i = 0 ; i < MAX_RHT_PER_CONTEXT ; i + + ) {
if ( ctxi_src - > rht_out = = ctxi_dst - > rht_out )
break ;
if ( ctxi_src - > rht_start [ i ] . nmask = = 0 )
continue ;
/* Consume a destination RHT entry */
ctxi_dst - > rht_out + + ;
ctxi_dst - > rht_start [ i ] . nmask = ctxi_src - > rht_start [ i ] . nmask ;
ctxi_dst - > rht_start [ i ] . fp =
SISL_RHT_FP_CLONE ( ctxi_src - > rht_start [ i ] . fp , perms ) ;
ctxi_dst - > rht_lun [ i ] = ctxi_src - > rht_lun [ i ] ;
rc = clone_lxt ( afu , blka , ctxid_dst , i ,
& ctxi_dst - > rht_start [ i ] ,
& ctxi_src - > rht_start [ i ] ) ;
if ( rc ) {
marshal_clone_to_rele ( clone , & release ) ;
for ( j = 0 ; j < i ; j + + ) {
release . rsrc_handle = j ;
_cxlflash_disk_release ( sdev , ctxi_dst ,
& release ) ;
}
/* Put back the one we failed on */
rhte_checkin ( ctxi_dst , & ctxi_dst - > rht_start [ i ] ) ;
goto err ;
}
cxlflash_lun_attach ( gli , gli - > mode , false ) ;
}
out_success :
list_splice ( & sidecar , & ctxi_dst - > luns ) ;
sys_close ( adap_fd_src ) ;
/* fall through */
out :
if ( ctxi_src )
put_context ( ctxi_src ) ;
if ( ctxi_dst )
put_context ( ctxi_dst ) ;
pr_debug ( " %s: returning rc=%d \n " , __func__ , rc ) ;
return rc ;
err :
list_for_each_entry_safe ( lun_access_src , lun_access_dst , & sidecar , list )
kfree ( lun_access_src ) ;
goto out ;
}