2007-10-19 22:47:54 +01:00
/*
* Copyright ( C ) 2005 Mike Christie , All rights reserved .
* Copyright ( C ) 2007 Red Hat , Inc . All rights reserved .
* Authors : Mike Christie
* Dave Wysochanski
*
* This file is released under the GPL .
*
* This module implements the specific path activation code for
* HP StorageWorks and FSC FibreCat Asymmetric ( Active / Passive )
* storage arrays .
* These storage arrays have controller - based failover , not
* LUN - based failover . However , LUN - based failover is the design
* of dm - multipath . Thus , this module is written for LUN - based failover .
*/
# include <linux/blkdev.h>
# include <linux/list.h>
# include <linux/types.h>
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
2007-10-19 22:47:55 +01:00
# include <scsi/scsi_dbg.h>
2007-10-19 22:47:54 +01:00
# include "dm.h"
# include "dm-hw-handler.h"
# define DM_MSG_PREFIX "multipath hp-sw"
# define DM_HP_HWH_NAME "hp-sw"
2007-10-19 22:47:55 +01:00
# define DM_HP_HWH_VER "1.0.0"
2007-10-19 22:47:54 +01:00
struct hp_sw_context {
unsigned char sense [ SCSI_SENSE_BUFFERSIZE ] ;
} ;
2007-10-19 22:47:55 +01:00
/*
* hp_sw_error_is_retryable - Is an HP - specific check condition retryable ?
* @ req : path activation request
*
* Examine error codes of request and determine whether the error is retryable .
* Some error codes are already retried by scsi - ml ( see
* scsi_decide_disposition ) , but some HP specific codes are not .
* The intent of this routine is to supply the logic for the HP specific
* check conditions .
*
* Returns :
* 1 - command completed with retryable error
* 0 - command completed with non - retryable error
*
* Possible optimizations
* 1. More hardware - specific error codes
*/
static int hp_sw_error_is_retryable ( struct request * req )
{
/*
* NOT_READY is known to be retryable
* For now we just dump out the sense data and call it retryable
*/
if ( status_byte ( req - > errors ) = = CHECK_CONDITION )
__scsi_print_sense ( DM_HP_HWH_NAME , req - > sense , req - > sense_len ) ;
/*
* At this point we don ' t have complete information about all the error
* codes from this hardware , so we are just conservative and retry
* when in doubt .
*/
return 1 ;
}
2007-10-19 22:47:54 +01:00
/*
* hp_sw_end_io - Completion handler for HP path activation .
* @ req : path activation request
* @ error : scsi - ml error
*
* Check sense data , free request structure , and notify dm that
* pg initialization has completed .
*
* Context : scsi - ml softirq
*
*/
static void hp_sw_end_io ( struct request * req , int error )
{
struct dm_path * path = req - > end_io_data ;
unsigned err_flags = 0 ;
2007-10-19 22:47:55 +01:00
if ( ! error ) {
2007-10-19 22:47:54 +01:00
DMDEBUG ( " %s path activation command - success " ,
path - > dev - > name ) ;
2007-10-19 22:47:55 +01:00
goto out ;
2007-10-19 22:47:54 +01:00
}
2007-10-19 22:47:55 +01:00
if ( hp_sw_error_is_retryable ( req ) ) {
DMDEBUG ( " %s path activation command - retry " ,
path - > dev - > name ) ;
err_flags = MP_RETRY ;
goto out ;
}
DMWARN ( " %s path activation fail - error=0x%x " ,
path - > dev - > name , error ) ;
err_flags = MP_FAIL_PATH ;
out :
2007-10-19 22:47:54 +01:00
req - > end_io_data = NULL ;
__blk_put_request ( req - > q , req ) ;
dm_pg_init_complete ( path , err_flags ) ;
}
/*
* hp_sw_get_request - Allocate an HP specific path activation request
* @ path : path on which request will be sent ( needed for request queue )
*
* The START command is used for path activation request .
* These arrays are controller - based failover , not LUN based .
* One START command issued to a single path will fail over all
* LUNs for the same controller .
*
* Possible optimizations
* 1. Make timeout configurable
* 2. Preallocate request
*/
static struct request * hp_sw_get_request ( struct dm_path * path )
{
struct request * req ;
struct block_device * bdev = path - > dev - > bdev ;
struct request_queue * q = bdev_get_queue ( bdev ) ;
struct hp_sw_context * h = path - > hwhcontext ;
req = blk_get_request ( q , WRITE , GFP_NOIO ) ;
if ( ! req )
goto out ;
req - > timeout = 60 * HZ ;
req - > errors = 0 ;
req - > cmd_type = REQ_TYPE_BLOCK_PC ;
req - > cmd_flags | = REQ_FAILFAST | REQ_NOMERGE ;
req - > end_io_data = path ;
req - > sense = h - > sense ;
memset ( req - > sense , 0 , SCSI_SENSE_BUFFERSIZE ) ;
memset ( & req - > cmd , 0 , BLK_MAX_CDB ) ;
req - > cmd [ 0 ] = START_STOP ;
req - > cmd [ 4 ] = 1 ;
req - > cmd_len = COMMAND_SIZE ( req - > cmd [ 0 ] ) ;
out :
return req ;
}
/*
* hp_sw_pg_init - HP path activation implementation .
* @ hwh : hardware handler specific data
* @ bypassed : unused ; is the path group bypassed ? ( see dm - mpath . c )
* @ path : path to send initialization command
*
* Send an HP - specific path activation command on ' path ' .
* Do not try to optimize in any way , just send the activation command .
* More than one path activation command may be sent to the same controller .
* This seems to work fine for basic failover support .
*
* Possible optimizations
* 1. Detect an in - progress activation request and avoid submitting another one
* 2. Model the controller and only send a single activation request at a time
* 3. Determine the state of a path before sending an activation request
*
* Context : kmpathd ( see process_queued_ios ( ) in dm - mpath . c )
*/
static void hp_sw_pg_init ( struct hw_handler * hwh , unsigned bypassed ,
struct dm_path * path )
{
struct request * req ;
struct hp_sw_context * h ;
path - > hwhcontext = hwh - > context ;
h = hwh - > context ;
req = hp_sw_get_request ( path ) ;
if ( ! req ) {
DMERR ( " %s path activation command - allocation fail " ,
path - > dev - > name ) ;
2007-10-19 22:47:55 +01:00
goto retry ;
2007-10-19 22:47:54 +01:00
}
DMDEBUG ( " %s path activation command - sent " , path - > dev - > name ) ;
blk_execute_rq_nowait ( req - > q , NULL , req , 1 , hp_sw_end_io ) ;
return ;
2007-10-19 22:47:55 +01:00
retry :
dm_pg_init_complete ( path , MP_RETRY ) ;
2007-10-19 22:47:54 +01:00
}
static int hp_sw_create ( struct hw_handler * hwh , unsigned argc , char * * argv )
{
struct hp_sw_context * h ;
h = kmalloc ( sizeof ( * h ) , GFP_KERNEL ) ;
if ( ! h )
return - ENOMEM ;
hwh - > context = h ;
return 0 ;
}
static void hp_sw_destroy ( struct hw_handler * hwh )
{
struct hp_sw_context * h = hwh - > context ;
kfree ( h ) ;
}
static struct hw_handler_type hp_sw_hwh = {
. name = DM_HP_HWH_NAME ,
. module = THIS_MODULE ,
. create = hp_sw_create ,
. destroy = hp_sw_destroy ,
. pg_init = hp_sw_pg_init ,
} ;
static int __init hp_sw_init ( void )
{
int r ;
r = dm_register_hw_handler ( & hp_sw_hwh ) ;
if ( r < 0 )
DMERR ( " register failed %d " , r ) ;
else
DMINFO ( " version " DM_HP_HWH_VER " loaded " ) ;
return r ;
}
static void __exit hp_sw_exit ( void )
{
int r ;
r = dm_unregister_hw_handler ( & hp_sw_hwh ) ;
if ( r < 0 )
DMERR ( " unregister failed %d " , r ) ;
}
module_init ( hp_sw_init ) ;
module_exit ( hp_sw_exit ) ;
MODULE_DESCRIPTION ( " DM Multipath HP StorageWorks / FSC FibreCat (A/P) support " ) ;
MODULE_AUTHOR ( " Mike Christie, Dave Wysochanski <dm-devel@redhat.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DM_HP_HWH_VER ) ;