2008-07-17 16:53:21 -07:00
/*
* Generic SCSI-3 ALUA SCSI Device Handler
*
2010-09-24 15:57:04 +02:00
* Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH.
2008-07-17 16:53:21 -07:00
* All rights reserved.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
2010-03-24 17:04:11 +09:00
# include <linux/slab.h>
2010-09-24 15:57:04 +02:00
# include <linux/delay.h>
2011-05-27 09:47:43 -04:00
# include <linux/module.h>
2008-07-17 16:53:21 -07:00
# include <scsi/scsi.h>
# include <scsi/scsi_eh.h>
# include <scsi/scsi_dh.h>
# define ALUA_DH_NAME "alua"
2010-09-24 15:57:04 +02:00
# define ALUA_DH_VER "1.3"
2008-07-17 16:53:21 -07:00
# define TPGS_STATE_OPTIMIZED 0x0
# define TPGS_STATE_NONOPTIMIZED 0x1
# define TPGS_STATE_STANDBY 0x2
# define TPGS_STATE_UNAVAILABLE 0x3
2010-09-24 15:57:04 +02:00
# define TPGS_STATE_LBA_DEPENDENT 0x4
2008-07-17 16:53:21 -07:00
# define TPGS_STATE_OFFLINE 0xe
# define TPGS_STATE_TRANSITIONING 0xf
# define TPGS_SUPPORT_NONE 0x00
# define TPGS_SUPPORT_OPTIMIZED 0x01
# define TPGS_SUPPORT_NONOPTIMIZED 0x02
# define TPGS_SUPPORT_STANDBY 0x04
# define TPGS_SUPPORT_UNAVAILABLE 0x08
2010-09-24 15:57:04 +02:00
# define TPGS_SUPPORT_LBA_DEPENDENT 0x10
2008-07-17 16:53:21 -07:00
# define TPGS_SUPPORT_OFFLINE 0x40
# define TPGS_SUPPORT_TRANSITION 0x80
2012-05-18 14:08:54 -04:00
# define RTPG_FMT_MASK 0x70
# define RTPG_FMT_EXT_HDR 0x10
2008-07-17 16:53:21 -07:00
# define TPGS_MODE_UNINITIALIZED -1
# define TPGS_MODE_NONE 0x0
# define TPGS_MODE_IMPLICIT 0x1
# define TPGS_MODE_EXPLICIT 0x2
# define ALUA_INQUIRY_SIZE 36
2012-05-18 14:08:54 -04:00
# define ALUA_FAILOVER_TIMEOUT 60
2008-07-17 16:53:21 -07:00
# define ALUA_FAILOVER_RETRIES 5
2012-03-27 20:55:49 +00:00
/* flags passed from user level */
# define ALUA_OPTIMIZE_STPG 1
2008-07-17 16:53:21 -07:00
struct alua_dh_data {
int group_id ;
int rel_port ;
int tpgs ;
int state ;
2012-03-27 20:56:08 +00:00
int pref ;
2012-03-27 20:55:49 +00:00
unsigned flags ; /* used for optimizing STPG */
2008-07-17 16:53:21 -07:00
unsigned char inq [ ALUA_INQUIRY_SIZE ] ;
unsigned char * buff ;
int bufflen ;
2012-05-18 14:08:54 -04:00
unsigned char transition_tmo ;
2008-07-17 16:53:21 -07:00
unsigned char sense [ SCSI_SENSE_BUFFERSIZE ] ;
int senselen ;
2009-10-21 09:23:04 -07:00
struct scsi_device * sdev ;
activate_complete callback_fn ;
void * callback_data ;
2008-07-17 16:53:21 -07:00
} ;
# define ALUA_POLICY_SWITCH_CURRENT 0
# define ALUA_POLICY_SWITCH_ALL 1
2009-10-21 09:23:04 -07:00
static char print_alua_state ( int ) ;
static int alua_check_sense ( struct scsi_device * , struct scsi_sense_hdr * ) ;
2008-07-17 16:53:21 -07:00
static inline struct alua_dh_data * get_alua_data ( struct scsi_device * sdev )
{
struct scsi_dh_data * scsi_dh_data = sdev - > scsi_dh_data ;
BUG_ON ( scsi_dh_data = = NULL ) ;
return ( ( struct alua_dh_data * ) scsi_dh_data - > buf ) ;
}
static int realloc_buffer ( struct alua_dh_data * h , unsigned len )
{
if ( h - > buff & & h - > buff ! = h - > inq )
kfree ( h - > buff ) ;
h - > buff = kmalloc ( len , GFP_NOIO ) ;
if ( ! h - > buff ) {
h - > buff = h - > inq ;
h - > bufflen = ALUA_INQUIRY_SIZE ;
return 1 ;
}
h - > bufflen = len ;
return 0 ;
}
static struct request * get_alua_req ( struct scsi_device * sdev ,
void * buffer , unsigned buflen , int rw )
{
struct request * rq ;
struct request_queue * q = sdev - > request_queue ;
rq = blk_get_request ( q , rw , GFP_NOIO ) ;
2014-08-28 08:15:21 -06:00
if ( IS_ERR ( rq ) ) {
2008-07-17 16:53:21 -07:00
sdev_printk ( KERN_INFO , sdev ,
2008-07-03 23:47:27 -07:00
" %s: blk_get_request failed \n " , __func__ ) ;
2008-07-17 16:53:21 -07:00
return NULL ;
}
2014-06-06 07:57:37 -06:00
blk_rq_set_block_pc ( rq ) ;
2008-07-17 16:53:21 -07:00
if ( buflen & & blk_rq_map_kern ( q , rq , buffer , buflen , GFP_NOIO ) ) {
blk_put_request ( rq ) ;
sdev_printk ( KERN_INFO , sdev ,
2008-07-03 23:47:27 -07:00
" %s: blk_rq_map_kern failed \n " , __func__ ) ;
2008-07-17 16:53:21 -07:00
return NULL ;
}
2008-08-19 18:45:30 -05:00
rq - > cmd_flags | = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
2008-09-06 08:39:16 -05:00
REQ_FAILFAST_DRIVER ;
2008-07-17 16:53:21 -07:00
rq - > retries = ALUA_FAILOVER_RETRIES ;
2012-05-18 14:08:54 -04:00
rq - > timeout = ALUA_FAILOVER_TIMEOUT * HZ ;
2008-07-17 16:53:21 -07:00
return rq ;
}
/*
* submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command
* @sdev: sdev the command should be sent to
*/
static int submit_vpd_inquiry ( struct scsi_device * sdev , struct alua_dh_data * h )
{
struct request * rq ;
int err = SCSI_DH_RES_TEMP_UNAVAIL ;
rq = get_alua_req ( sdev , h - > buff , h - > bufflen , READ ) ;
if ( ! rq )
goto done ;
/* Prepare the command. */
rq - > cmd [ 0 ] = INQUIRY ;
rq - > cmd [ 1 ] = 1 ;
rq - > cmd [ 2 ] = 0x83 ;
rq - > cmd [ 4 ] = h - > bufflen ;
rq - > cmd_len = COMMAND_SIZE ( INQUIRY ) ;
rq - > sense = h - > sense ;
memset ( rq - > sense , 0 , SCSI_SENSE_BUFFERSIZE ) ;
rq - > sense_len = h - > senselen = 0 ;
err = blk_execute_rq ( rq - > q , NULL , rq , 1 ) ;
if ( err = = - EIO ) {
sdev_printk ( KERN_INFO , sdev ,
" %s: evpd inquiry failed with %x \n " ,
ALUA_DH_NAME , rq - > errors ) ;
h - > senselen = rq - > sense_len ;
err = SCSI_DH_IO ;
}
blk_put_request ( rq ) ;
done :
return err ;
}
/*
* submit_rtpg - Issue a REPORT TARGET GROUP STATES command
* @sdev: sdev the command should be sent to
*/
2012-05-18 14:08:55 -04:00
static unsigned submit_rtpg ( struct scsi_device * sdev , struct alua_dh_data * h ,
bool rtpg_ext_hdr_req )
2008-07-17 16:53:21 -07:00
{
struct request * rq ;
int err = SCSI_DH_RES_TEMP_UNAVAIL ;
rq = get_alua_req ( sdev , h - > buff , h - > bufflen , READ ) ;
if ( ! rq )
goto done ;
/* Prepare the command. */
rq - > cmd [ 0 ] = MAINTENANCE_IN ;
2012-05-18 14:08:55 -04:00
if ( rtpg_ext_hdr_req )
rq - > cmd [ 1 ] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT ;
else
rq - > cmd [ 1 ] = MI_REPORT_TARGET_PGS ;
2008-07-17 16:53:21 -07:00
rq - > cmd [ 6 ] = ( h - > bufflen > > 24 ) & 0xff ;
rq - > cmd [ 7 ] = ( h - > bufflen > > 16 ) & 0xff ;
rq - > cmd [ 8 ] = ( h - > bufflen > > 8 ) & 0xff ;
rq - > cmd [ 9 ] = h - > bufflen & 0xff ;
rq - > cmd_len = COMMAND_SIZE ( MAINTENANCE_IN ) ;
rq - > sense = h - > sense ;
memset ( rq - > sense , 0 , SCSI_SENSE_BUFFERSIZE ) ;
rq - > sense_len = h - > senselen = 0 ;
err = blk_execute_rq ( rq - > q , NULL , rq , 1 ) ;
if ( err = = - EIO ) {
sdev_printk ( KERN_INFO , sdev ,
" %s: rtpg failed with %x \n " ,
ALUA_DH_NAME , rq - > errors ) ;
h - > senselen = rq - > sense_len ;
err = SCSI_DH_IO ;
}
blk_put_request ( rq ) ;
done :
return err ;
}
2009-10-21 09:23:04 -07:00
/*
* alua_stpg - Evaluate SET TARGET GROUP STATES
* @sdev: the device to be evaluated
* @state: the new target group state
*
* Send a SET TARGET GROUP STATES command to the device.
* We only have to test here if we should resubmit the command;
* any other error is assumed as a failure.
*/
static void stpg_endio ( struct request * req , int error )
{
struct alua_dh_data * h = req - > end_io_data ;
struct scsi_sense_hdr sense_hdr ;
2011-01-05 16:00:22 -05:00
unsigned err = SCSI_DH_OK ;
2009-10-21 09:23:04 -07:00
2013-03-05 22:40:24 -06:00
if ( host_byte ( req - > errors ) ! = DID_OK | |
msg_byte ( req - > errors ) ! = COMMAND_COMPLETE ) {
2011-01-05 16:00:22 -05:00
err = SCSI_DH_IO ;
2009-10-21 09:23:04 -07:00
goto done ;
2011-01-05 16:00:22 -05:00
}
2009-10-21 09:23:04 -07:00
2013-03-05 22:40:24 -06:00
if ( req - > sense_len > 0 ) {
2009-10-21 09:23:04 -07:00
err = scsi_normalize_sense ( h - > sense , SCSI_SENSE_BUFFERSIZE ,
& sense_hdr ) ;
if ( ! err ) {
err = SCSI_DH_IO ;
goto done ;
}
err = alua_check_sense ( h - > sdev , & sense_hdr ) ;
if ( err = = ADD_TO_MLQUEUE ) {
err = SCSI_DH_RETRY ;
goto done ;
}
sdev_printk ( KERN_INFO , h - > sdev ,
" %s: stpg sense code: %02x/%02x/%02x \n " ,
ALUA_DH_NAME , sense_hdr . sense_key ,
sense_hdr . asc , sense_hdr . ascq ) ;
err = SCSI_DH_IO ;
2013-03-05 22:40:24 -06:00
} else if ( error )
err = SCSI_DH_IO ;
2009-10-21 09:23:04 -07:00
if ( err = = SCSI_DH_OK ) {
h - > state = TPGS_STATE_OPTIMIZED ;
sdev_printk ( KERN_INFO , h - > sdev ,
" %s: port group %02x switched to state %c \n " ,
ALUA_DH_NAME , h - > group_id ,
print_alua_state ( h - > state ) ) ;
}
done :
2011-01-05 16:00:21 -05:00
req - > end_io_data = NULL ;
__blk_put_request ( req - > q , req ) ;
2009-10-21 09:23:04 -07:00
if ( h - > callback_fn ) {
h - > callback_fn ( h - > callback_data , err ) ;
h - > callback_fn = h - > callback_data = NULL ;
}
return ;
}
2008-07-17 16:53:21 -07:00
/*
* submit_stpg - Issue a SET TARGET GROUP STATES command
*
* Currently we're only setting the current target port group state
* to 'active/optimized' and let the array firmware figure out
* the states of the remaining groups.
*/
2009-10-21 09:23:04 -07:00
static unsigned submit_stpg ( struct alua_dh_data * h )
2008-07-17 16:53:21 -07:00
{
struct request * rq ;
int stpg_len = 8 ;
2009-10-21 09:23:04 -07:00
struct scsi_device * sdev = h - > sdev ;
2008-07-17 16:53:21 -07:00
/* Prepare the data buffer */
memset ( h - > buff , 0 , stpg_len ) ;
h - > buff [ 4 ] = TPGS_STATE_OPTIMIZED & 0x0f ;
2009-01-30 17:00:09 -06:00
h - > buff [ 6 ] = ( h - > group_id > > 8 ) & 0xff ;
h - > buff [ 7 ] = h - > group_id & 0xff ;
2008-07-17 16:53:21 -07:00
rq = get_alua_req ( sdev , h - > buff , stpg_len , WRITE ) ;
if ( ! rq )
2009-10-21 09:23:04 -07:00
return SCSI_DH_RES_TEMP_UNAVAIL ;
2008-07-17 16:53:21 -07:00
/* Prepare the command. */
rq - > cmd [ 0 ] = MAINTENANCE_OUT ;
rq - > cmd [ 1 ] = MO_SET_TARGET_PGS ;
rq - > cmd [ 6 ] = ( stpg_len > > 24 ) & 0xff ;
rq - > cmd [ 7 ] = ( stpg_len > > 16 ) & 0xff ;
rq - > cmd [ 8 ] = ( stpg_len > > 8 ) & 0xff ;
rq - > cmd [ 9 ] = stpg_len & 0xff ;
rq - > cmd_len = COMMAND_SIZE ( MAINTENANCE_OUT ) ;
rq - > sense = h - > sense ;
memset ( rq - > sense , 0 , SCSI_SENSE_BUFFERSIZE ) ;
rq - > sense_len = h - > senselen = 0 ;
2009-10-21 09:23:04 -07:00
rq - > end_io_data = h ;
2008-07-17 16:53:21 -07:00
2009-10-21 09:23:04 -07:00
blk_execute_rq_nowait ( rq - > q , NULL , rq , 1 , stpg_endio ) ;
2011-01-05 16:00:20 -05:00
return SCSI_DH_OK ;
2008-07-17 16:53:21 -07:00
}
/*
2011-08-24 10:51:13 +02:00
* alua_check_tpgs - Evaluate TPGS setting
2008-07-17 16:53:21 -07:00
* @sdev: device to be checked
*
2011-08-24 10:51:13 +02:00
* Examine the TPGS setting of the sdev to find out if ALUA
2008-07-17 16:53:21 -07:00
* is supported.
*/
2011-08-24 10:51:13 +02:00
static int alua_check_tpgs ( struct scsi_device * sdev , struct alua_dh_data * h )
2008-07-17 16:53:21 -07:00
{
2011-08-24 10:51:13 +02:00
int err = SCSI_DH_OK ;
2008-07-17 16:53:21 -07:00
2011-08-24 10:51:13 +02:00
h - > tpgs = scsi_device_tpgs ( sdev ) ;
2008-07-17 16:53:21 -07:00
switch ( h - > tpgs ) {
case TPGS_MODE_EXPLICIT | TPGS_MODE_IMPLICIT :
sdev_printk ( KERN_INFO , sdev ,
" %s: supports implicit and explicit TPGS \n " ,
ALUA_DH_NAME ) ;
break ;
case TPGS_MODE_EXPLICIT :
sdev_printk ( KERN_INFO , sdev , " %s: supports explicit TPGS \n " ,
ALUA_DH_NAME ) ;
break ;
case TPGS_MODE_IMPLICIT :
sdev_printk ( KERN_INFO , sdev , " %s: supports implicit TPGS \n " ,
ALUA_DH_NAME ) ;
break ;
default :
h - > tpgs = TPGS_MODE_NONE ;
sdev_printk ( KERN_INFO , sdev , " %s: not supported \n " ,
ALUA_DH_NAME ) ;
err = SCSI_DH_DEV_UNSUPP ;
break ;
}
return err ;
}
/*
* alua_vpd_inquiry - Evaluate INQUIRY vpd page 0x83
* @sdev: device to be checked
*
* Extract the relative target port and the target port group
* descriptor from the list of identificators.
*/
static int alua_vpd_inquiry ( struct scsi_device * sdev , struct alua_dh_data * h )
{
int len ;
unsigned err ;
unsigned char * d ;
retry :
err = submit_vpd_inquiry ( sdev , h ) ;
if ( err ! = SCSI_DH_OK )
return err ;
/* Check if vpd page exceeds initial buffer */
len = ( h - > buff [ 2 ] < < 8 ) + h - > buff [ 3 ] + 4 ;
if ( len > h - > bufflen ) {
/* Resubmit with the correct length */
if ( realloc_buffer ( h , len ) ) {
sdev_printk ( KERN_WARNING , sdev ,
" %s: kmalloc buffer failed \n " ,
ALUA_DH_NAME ) ;
/* Temporary failure, bypass */
return SCSI_DH_DEV_TEMP_BUSY ;
}
goto retry ;
}
/*
* Now look for the correct descriptor.
*/
d = h - > buff + 4 ;
while ( d < h - > buff + len ) {
switch ( d [ 1 ] & 0xf ) {
case 0x4 :
/* Relative target port */
h - > rel_port = ( d [ 6 ] < < 8 ) + d [ 7 ] ;
break ;
case 0x5 :
/* Target port group */
h - > group_id = ( d [ 6 ] < < 8 ) + d [ 7 ] ;
break ;
default :
break ;
}
d + = d [ 3 ] + 4 ;
}
if ( h - > group_id = = - 1 ) {
/*
* Internal error; TPGS supported but required
* VPD identification descriptors not present.
* Disable ALUA support
*/
sdev_printk ( KERN_INFO , sdev ,
" %s: No target port descriptors found \n " ,
ALUA_DH_NAME ) ;
h - > state = TPGS_STATE_OPTIMIZED ;
h - > tpgs = TPGS_MODE_NONE ;
err = SCSI_DH_DEV_UNSUPP ;
} else {
sdev_printk ( KERN_INFO , sdev ,
" %s: port group %02x rel port %02x \n " ,
ALUA_DH_NAME , h - > group_id , h - > rel_port ) ;
}
return err ;
}
static char print_alua_state ( int state )
{
switch ( state ) {
case TPGS_STATE_OPTIMIZED :
return ' A ' ;
case TPGS_STATE_NONOPTIMIZED :
return ' N ' ;
case TPGS_STATE_STANDBY :
return ' S ' ;
case TPGS_STATE_UNAVAILABLE :
return ' U ' ;
2010-09-24 15:57:04 +02:00
case TPGS_STATE_LBA_DEPENDENT :
return ' L ' ;
2008-07-17 16:53:21 -07:00
case TPGS_STATE_OFFLINE :
return ' O ' ;
case TPGS_STATE_TRANSITIONING :
return ' T ' ;
default :
return ' X ' ;
}
}
static int alua_check_sense ( struct scsi_device * sdev ,
struct scsi_sense_hdr * sense_hdr )
{
switch ( sense_hdr - > sense_key ) {
case NOT_READY :
if ( sense_hdr - > asc = = 0x04 & & sense_hdr - > ascq = = 0x0a )
/*
* LUN Not Accessible - ALUA state transition
*/
2008-08-12 12:11:57 -07:00
return ADD_TO_MLQUEUE ;
2008-07-17 16:53:21 -07:00
if ( sense_hdr - > asc = = 0x04 & & sense_hdr - > ascq = = 0x0b )
/*
* LUN Not Accessible -- Target port in standby state
*/
return SUCCESS ;
if ( sense_hdr - > asc = = 0x04 & & sense_hdr - > ascq = = 0x0c )
/*
* LUN Not Accessible -- Target port in unavailable state
*/
return SUCCESS ;
if ( sense_hdr - > asc = = 0x04 & & sense_hdr - > ascq = = 0x12 )
/*
* LUN Not Ready -- Offline
*/
return SUCCESS ;
break ;
case UNIT_ATTENTION :
if ( sense_hdr - > asc = = 0x29 & & sense_hdr - > ascq = = 0x00 )
/*
* Power On, Reset, or Bus Device Reset, just retry.
*/
2008-08-12 12:11:57 -07:00
return ADD_TO_MLQUEUE ;
2013-10-15 15:52:39 +00:00
if ( sense_hdr - > asc = = 0x29 & & sense_hdr - > ascq = = 0x04 )
/*
* Device internal reset
*/
return ADD_TO_MLQUEUE ;
2011-12-21 18:01:37 -05:00
if ( sense_hdr - > asc = = 0x2a & & sense_hdr - > ascq = = 0x01 )
/*
* Mode Parameters Changed
*/
return ADD_TO_MLQUEUE ;
2011-10-06 13:22:07 -04:00
if ( sense_hdr - > asc = = 0x2a & & sense_hdr - > ascq = = 0x06 )
2008-07-17 16:53:21 -07:00
/*
* ALUA state changed
*/
2008-08-12 12:11:57 -07:00
return ADD_TO_MLQUEUE ;
2011-10-06 13:22:07 -04:00
if ( sense_hdr - > asc = = 0x2a & & sense_hdr - > ascq = = 0x07 )
2008-07-17 16:53:21 -07:00
/*
* Implicit ALUA state transition failed
*/
2008-08-12 12:11:57 -07:00
return ADD_TO_MLQUEUE ;
2011-10-06 13:22:07 -04:00
if ( sense_hdr - > asc = = 0x3f & & sense_hdr - > ascq = = 0x03 )
/*
* Inquiry data has changed
*/
return ADD_TO_MLQUEUE ;
if ( sense_hdr - > asc = = 0x3f & & sense_hdr - > ascq = = 0x0e )
2009-01-30 17:00:11 -06:00
/*
* REPORTED_LUNS_DATA_HAS_CHANGED is reported
* when switching controllers on targets like
* Intel Multi-Flex. We can just retry.
*/
return ADD_TO_MLQUEUE ;
2008-07-17 16:53:21 -07:00
break ;
}
return SCSI_RETURN_NOT_HANDLED ;
}
/*
* alua_rtpg - Evaluate REPORT TARGET GROUP STATES
* @sdev: the device to be evaluated.
2013-10-15 15:52:54 +00:00
* @wait_for_transition: if nonzero, wait ALUA_FAILOVER_TIMEOUT seconds for device to exit transitioning state
2008-07-17 16:53:21 -07:00
*
* Evaluate the Target Port Group State.
* Returns SCSI_DH_DEV_OFFLINED if the path is
2011-03-30 22:57:33 -03:00
* found to be unusable.
2008-07-17 16:53:21 -07:00
*/
2013-10-15 15:52:54 +00:00
static int alua_rtpg ( struct scsi_device * sdev , struct alua_dh_data * h , int wait_for_transition )
2008-07-17 16:53:21 -07:00
{
struct scsi_sense_hdr sense_hdr ;
int len , k , off , valid_states = 0 ;
2011-08-24 10:51:18 +02:00
unsigned char * ucp ;
2008-07-17 16:53:21 -07:00
unsigned err ;
2012-05-18 14:08:55 -04:00
bool rtpg_ext_hdr_req = 1 ;
2012-05-18 14:08:56 -04:00
unsigned long expiry , interval = 0 ;
2012-05-18 14:08:54 -04:00
unsigned int tpg_desc_tbl_off ;
unsigned char orig_transition_tmo ;
if ( ! h - > transition_tmo )
expiry = round_jiffies_up ( jiffies + ALUA_FAILOVER_TIMEOUT * HZ ) ;
else
expiry = round_jiffies_up ( jiffies + h - > transition_tmo * HZ ) ;
2008-07-17 16:53:21 -07:00
retry :
2012-05-18 14:08:55 -04:00
err = submit_rtpg ( sdev , h , rtpg_ext_hdr_req ) ;
2008-07-17 16:53:21 -07:00
if ( err = = SCSI_DH_IO & & h - > senselen > 0 ) {
err = scsi_normalize_sense ( h - > sense , SCSI_SENSE_BUFFERSIZE ,
& sense_hdr ) ;
if ( ! err )
return SCSI_DH_IO ;
2012-05-18 14:08:55 -04:00
/*
* submit_rtpg() has failed on existing arrays
* when requesting extended header info, and
* the array doesn't support extended headers,
* even though it shouldn't according to T10.
* The retry without rtpg_ext_hdr_req set
* handles this.
*/
if ( rtpg_ext_hdr_req = = 1 & &
sense_hdr . sense_key = = ILLEGAL_REQUEST & &
sense_hdr . asc = = 0x24 & & sense_hdr . ascq = = 0 ) {
rtpg_ext_hdr_req = 0 ;
goto retry ;
}
2008-07-17 16:53:21 -07:00
err = alua_check_sense ( sdev , & sense_hdr ) ;
2010-09-24 15:57:04 +02:00
if ( err = = ADD_TO_MLQUEUE & & time_before ( jiffies , expiry ) )
2008-07-17 16:53:21 -07:00
goto retry ;
sdev_printk ( KERN_INFO , sdev ,
" %s: rtpg sense code %02x/%02x/%02x \n " ,
ALUA_DH_NAME , sense_hdr . sense_key ,
sense_hdr . asc , sense_hdr . ascq ) ;
err = SCSI_DH_IO ;
}
if ( err ! = SCSI_DH_OK )
return err ;
len = ( h - > buff [ 0 ] < < 24 ) + ( h - > buff [ 1 ] < < 16 ) +
( h - > buff [ 2 ] < < 8 ) + h - > buff [ 3 ] + 4 ;
if ( len > h - > bufflen ) {
/* Resubmit with the correct length */
if ( realloc_buffer ( h , len ) ) {
sdev_printk ( KERN_WARNING , sdev ,
2008-07-03 23:47:27 -07:00
" %s: kmalloc buffer failed \n " , __func__ ) ;
2008-07-17 16:53:21 -07:00
/* Temporary failure, bypass */
return SCSI_DH_DEV_TEMP_BUSY ;
}
goto retry ;
}
2012-05-18 14:08:54 -04:00
orig_transition_tmo = h - > transition_tmo ;
if ( ( h - > buff [ 4 ] & RTPG_FMT_MASK ) = = RTPG_FMT_EXT_HDR & & h - > buff [ 5 ] ! = 0 )
h - > transition_tmo = h - > buff [ 5 ] ;
else
h - > transition_tmo = ALUA_FAILOVER_TIMEOUT ;
2013-10-15 15:52:54 +00:00
if ( wait_for_transition & & ( orig_transition_tmo ! = h - > transition_tmo ) ) {
2012-05-18 14:08:54 -04:00
sdev_printk ( KERN_INFO , sdev ,
" %s: transition timeout set to %d seconds \n " ,
ALUA_DH_NAME , h - > transition_tmo ) ;
expiry = jiffies + h - > transition_tmo * HZ ;
}
if ( ( h - > buff [ 4 ] & RTPG_FMT_MASK ) = = RTPG_FMT_EXT_HDR )
tpg_desc_tbl_off = 8 ;
else
tpg_desc_tbl_off = 4 ;
for ( k = tpg_desc_tbl_off , ucp = h - > buff + tpg_desc_tbl_off ;
k < len ;
k + = off , ucp + = off ) {
2008-07-17 16:53:21 -07:00
if ( h - > group_id = = ( ucp [ 2 ] < < 8 ) + ucp [ 3 ] ) {
h - > state = ucp [ 0 ] & 0x0f ;
2012-03-27 20:56:08 +00:00
h - > pref = ucp [ 0 ] > > 7 ;
2008-07-17 16:53:21 -07:00
valid_states = ucp [ 1 ] ;
}
off = 8 + ( ucp [ 7 ] * 4 ) ;
}
sdev_printk ( KERN_INFO , sdev ,
2012-03-27 20:56:08 +00:00
" %s: port group %02x state %c %s supports %c%c%c%c%c%c%c \n " ,
2008-07-17 16:53:21 -07:00
ALUA_DH_NAME , h - > group_id , print_alua_state ( h - > state ) ,
2012-03-27 20:56:08 +00:00
h - > pref ? " preferred " : " non-preferred " ,
2008-07-17 16:53:21 -07:00
valid_states & TPGS_SUPPORT_TRANSITION ? ' T ' : ' t ' ,
valid_states & TPGS_SUPPORT_OFFLINE ? ' O ' : ' o ' ,
2010-09-24 15:57:04 +02:00
valid_states & TPGS_SUPPORT_LBA_DEPENDENT ? ' L ' : ' l ' ,
2008-07-17 16:53:21 -07:00
valid_states & TPGS_SUPPORT_UNAVAILABLE ? ' U ' : ' u ' ,
valid_states & TPGS_SUPPORT_STANDBY ? ' S ' : ' s ' ,
valid_states & TPGS_SUPPORT_NONOPTIMIZED ? ' N ' : ' n ' ,
valid_states & TPGS_SUPPORT_OPTIMIZED ? ' A ' : ' a ' ) ;
2010-09-24 15:57:04 +02:00
switch ( h - > state ) {
case TPGS_STATE_TRANSITIONING :
2013-10-15 15:52:54 +00:00
if ( wait_for_transition ) {
if ( time_before ( jiffies , expiry ) ) {
/* State transition, retry */
interval + = 2000 ;
msleep ( interval ) ;
goto retry ;
}
err = SCSI_DH_RETRY ;
} else {
err = SCSI_DH_OK ;
2008-07-17 16:53:21 -07:00
}
2013-10-15 15:52:54 +00:00
2010-09-24 15:57:04 +02:00
/* Transitioning time exceeded, set port to standby */
h - > state = TPGS_STATE_STANDBY ;
break ;
case TPGS_STATE_OFFLINE :
2012-08-24 09:08:41 +00:00
/* Path unusable */
2010-09-24 15:57:04 +02:00
err = SCSI_DH_DEV_OFFLINED ;
break ;
default :
/* Useable path if active */
err = SCSI_DH_OK ;
break ;
2008-07-17 16:53:21 -07:00
}
return err ;
}
/*
* alua_initialize - Initialize ALUA state
* @sdev: the device to be initialized
*
* For the prep_fn to work correctly we have
* to initialize the ALUA state for the device.
*/
static int alua_initialize ( struct scsi_device * sdev , struct alua_dh_data * h )
{
int err ;
2011-08-24 10:51:13 +02:00
err = alua_check_tpgs ( sdev , h ) ;
2008-07-17 16:53:21 -07:00
if ( err ! = SCSI_DH_OK )
goto out ;
err = alua_vpd_inquiry ( sdev , h ) ;
if ( err ! = SCSI_DH_OK )
goto out ;
2013-10-15 15:52:54 +00:00
err = alua_rtpg ( sdev , h , 0 ) ;
2008-07-17 16:53:21 -07:00
if ( err ! = SCSI_DH_OK )
goto out ;
out :
return err ;
}
2012-03-27 20:55:49 +00:00
/*
* alua_set_params - set/unset the optimize flag
* @sdev: device on the path to be activated
* params - parameters in the following format
* "no_of_params\0param1\0param2\0param3\0...\0"
* For example, to set the flag pass the following parameters
* from multipath.conf
* hardware_handler "2 alua 1"
*/
static int alua_set_params ( struct scsi_device * sdev , const char * params )
{
struct alua_dh_data * h = get_alua_data ( sdev ) ;
unsigned int optimize = 0 , argc ;
const char * p = params ;
int result = SCSI_DH_OK ;
if ( ( sscanf ( params , " %u " , & argc ) ! = 1 ) | | ( argc ! = 1 ) )
return - EINVAL ;
while ( * p + + )
;
if ( ( sscanf ( p , " %u " , & optimize ) ! = 1 ) | | ( optimize > 1 ) )
return - EINVAL ;
if ( optimize )
h - > flags | = ALUA_OPTIMIZE_STPG ;
else
h - > flags & = ~ ALUA_OPTIMIZE_STPG ;
return result ;
}
2008-07-17 16:53:21 -07:00
2013-04-04 14:54:47 +00:00
static uint optimize_stpg ;
module_param ( optimize_stpg , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( optimize_stpg , " Allow use of a non-optimized path, rather than sending a STPG, when implicit TPGS is supported (0=No,1=Yes). Default is 0. " ) ;
2008-07-17 16:53:21 -07:00
/*
* alua_activate - activate a path
* @sdev: device on the path to be activated
*
* We're currently switching the port group to be activated only and
* let the array figure out the rest.
* There may be other arrays which require us to switch all port groups
* based on a certain policy. But until we actually encounter them it
* should be okay.
*/
2009-10-21 09:22:46 -07:00
static int alua_activate ( struct scsi_device * sdev ,
activate_complete fn , void * data )
2008-07-17 16:53:21 -07:00
{
struct alua_dh_data * h = get_alua_data ( sdev ) ;
int err = SCSI_DH_OK ;
2012-03-27 20:56:20 +00:00
int stpg = 0 ;
2008-07-17 16:53:21 -07:00
2013-10-15 15:52:54 +00:00
err = alua_rtpg ( sdev , h , 1 ) ;
2011-08-24 10:51:16 +02:00
if ( err ! = SCSI_DH_OK )
goto out ;
2008-07-17 16:53:21 -07:00
2013-04-04 14:54:47 +00:00
if ( optimize_stpg )
h - > flags | = ALUA_OPTIMIZE_STPG ;
2012-03-27 20:56:20 +00:00
if ( h - > tpgs & TPGS_MODE_EXPLICIT ) {
switch ( h - > state ) {
case TPGS_STATE_NONOPTIMIZED :
stpg = 1 ;
if ( ( h - > flags & ALUA_OPTIMIZE_STPG ) & &
( ! h - > pref ) & &
( h - > tpgs & TPGS_MODE_IMPLICIT ) )
stpg = 0 ;
break ;
case TPGS_STATE_STANDBY :
2012-06-22 08:49:38 +00:00
case TPGS_STATE_UNAVAILABLE :
2012-03-27 20:56:20 +00:00
stpg = 1 ;
break ;
case TPGS_STATE_OFFLINE :
err = SCSI_DH_IO ;
break ;
case TPGS_STATE_TRANSITIONING :
err = SCSI_DH_RETRY ;
break ;
default :
break ;
}
}
if ( stpg ) {
2009-10-21 09:23:04 -07:00
h - > callback_fn = fn ;
h - > callback_data = data ;
err = submit_stpg ( h ) ;
if ( err = = SCSI_DH_OK )
return 0 ;
h - > callback_fn = h - > callback_data = NULL ;
}
2008-07-17 16:53:21 -07:00
out :
2009-10-21 09:22:46 -07:00
if ( fn )
fn ( data , err ) ;
return 0 ;
2008-07-17 16:53:21 -07:00
}
/*
* alua_prep_fn - request callback
*
* Fail I/O to all paths not in state
* active/optimized or active/non-optimized.
*/
static int alua_prep_fn ( struct scsi_device * sdev , struct request * req )
{
struct alua_dh_data * h = get_alua_data ( sdev ) ;
int ret = BLKPREP_OK ;
2010-09-24 15:57:04 +02:00
if ( h - > state = = TPGS_STATE_TRANSITIONING )
ret = BLKPREP_DEFER ;
else if ( h - > state ! = TPGS_STATE_OPTIMIZED & &
h - > state ! = TPGS_STATE_NONOPTIMIZED & &
h - > state ! = TPGS_STATE_LBA_DEPENDENT ) {
2008-07-17 16:53:21 -07:00
ret = BLKPREP_KILL ;
req - > cmd_flags | = REQ_QUIET ;
}
return ret ;
}
2011-08-24 10:51:15 +02:00
static bool alua_match ( struct scsi_device * sdev )
{
return ( scsi_device_tpgs ( sdev ) ! = 0 ) ;
}
2008-07-17 16:53:21 -07:00
static int alua_bus_attach ( struct scsi_device * sdev ) ;
static void alua_bus_detach ( struct scsi_device * sdev ) ;
static struct scsi_device_handler alua_dh = {
. name = ALUA_DH_NAME ,
. module = THIS_MODULE ,
. attach = alua_bus_attach ,
. detach = alua_bus_detach ,
. prep_fn = alua_prep_fn ,
. check_sense = alua_check_sense ,
. activate = alua_activate ,
2012-03-27 20:55:49 +00:00
. set_params = alua_set_params ,
2011-08-24 10:51:15 +02:00
. match = alua_match ,
2008-07-17 16:53:21 -07:00
} ;
/*
* alua_bus_attach - Attach device handler
* @sdev: device to be attached to
*/
static int alua_bus_attach ( struct scsi_device * sdev )
{
struct scsi_dh_data * scsi_dh_data ;
struct alua_dh_data * h ;
unsigned long flags ;
int err = SCSI_DH_OK ;
2011-02-11 15:17:33 -07:00
scsi_dh_data = kzalloc ( sizeof ( * scsi_dh_data )
2008-07-17 16:53:21 -07:00
+ sizeof ( * h ) , GFP_KERNEL ) ;
if ( ! scsi_dh_data ) {
sdev_printk ( KERN_ERR , sdev , " %s: Attach failed \n " ,
ALUA_DH_NAME ) ;
return - ENOMEM ;
}
scsi_dh_data - > scsi_dh = & alua_dh ;
h = ( struct alua_dh_data * ) scsi_dh_data - > buf ;
h - > tpgs = TPGS_MODE_UNINITIALIZED ;
h - > state = TPGS_STATE_OPTIMIZED ;
h - > group_id = - 1 ;
h - > rel_port = - 1 ;
h - > buff = h - > inq ;
h - > bufflen = ALUA_INQUIRY_SIZE ;
2009-10-21 09:23:04 -07:00
h - > sdev = sdev ;
2008-07-17 16:53:21 -07:00
err = alua_initialize ( sdev , h ) ;
2011-04-26 18:27:05 +05:30
if ( ( err ! = SCSI_DH_OK ) & & ( err ! = SCSI_DH_DEV_OFFLINED ) )
2008-07-17 16:53:21 -07:00
goto failed ;
if ( ! try_module_get ( THIS_MODULE ) )
goto failed ;
spin_lock_irqsave ( sdev - > request_queue - > queue_lock , flags ) ;
sdev - > scsi_dh_data = scsi_dh_data ;
spin_unlock_irqrestore ( sdev - > request_queue - > queue_lock , flags ) ;
2011-10-27 14:36:32 -04:00
sdev_printk ( KERN_NOTICE , sdev , " %s: Attached \n " , ALUA_DH_NAME ) ;
2008-07-17 16:53:21 -07:00
return 0 ;
failed :
kfree ( scsi_dh_data ) ;
sdev_printk ( KERN_ERR , sdev , " %s: not attached \n " , ALUA_DH_NAME ) ;
return - EINVAL ;
}
/*
* alua_bus_detach - Detach device handler
* @sdev: device to be detached from
*/
static void alua_bus_detach ( struct scsi_device * sdev )
{
struct scsi_dh_data * scsi_dh_data ;
struct alua_dh_data * h ;
unsigned long flags ;
spin_lock_irqsave ( sdev - > request_queue - > queue_lock , flags ) ;
scsi_dh_data = sdev - > scsi_dh_data ;
sdev - > scsi_dh_data = NULL ;
spin_unlock_irqrestore ( sdev - > request_queue - > queue_lock , flags ) ;
h = ( struct alua_dh_data * ) scsi_dh_data - > buf ;
if ( h - > buff & & h - > inq ! = h - > buff )
kfree ( h - > buff ) ;
kfree ( scsi_dh_data ) ;
module_put ( THIS_MODULE ) ;
sdev_printk ( KERN_NOTICE , sdev , " %s: Detached \n " , ALUA_DH_NAME ) ;
}
static int __init alua_init ( void )
{
int r ;
r = scsi_register_device_handler ( & alua_dh ) ;
if ( r ! = 0 )
printk ( KERN_ERR " %s: Failed to register scsi device handler " ,
ALUA_DH_NAME ) ;
return r ;
}
static void __exit alua_exit ( void )
{
scsi_unregister_device_handler ( & alua_dh ) ;
}
module_init ( alua_init ) ;
module_exit ( alua_exit ) ;
MODULE_DESCRIPTION ( " DM Multipath ALUA support " ) ;
MODULE_AUTHOR ( " Hannes Reinecke <hare@suse.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( ALUA_DH_VER ) ;