2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
*
* This file is released under the GPL .
*
* Multipath hardware handler registration .
*/
# include "dm.h"
# include "dm-hw-handler.h"
# include <linux/slab.h>
struct hwh_internal {
struct hw_handler_type hwht ;
struct list_head list ;
long use ;
} ;
# define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
static LIST_HEAD ( _hw_handlers ) ;
static DECLARE_RWSEM ( _hwh_lock ) ;
2005-05-06 03:16:09 +04:00
static struct hwh_internal * __find_hw_handler_type ( const char * name )
2005-04-17 02:20:36 +04:00
{
struct hwh_internal * hwhi ;
list_for_each_entry ( hwhi , & _hw_handlers , list ) {
if ( ! strcmp ( name , hwhi - > hwht . name ) )
return hwhi ;
}
return NULL ;
}
static struct hwh_internal * get_hw_handler ( const char * name )
{
struct hwh_internal * hwhi ;
down_read ( & _hwh_lock ) ;
hwhi = __find_hw_handler_type ( name ) ;
if ( hwhi ) {
if ( ( hwhi - > use = = 0 ) & & ! try_module_get ( hwhi - > hwht . module ) )
hwhi = NULL ;
else
hwhi - > use + + ;
}
up_read ( & _hwh_lock ) ;
return hwhi ;
}
struct hw_handler_type * dm_get_hw_handler ( const char * name )
{
struct hwh_internal * hwhi ;
if ( ! name )
return NULL ;
hwhi = get_hw_handler ( name ) ;
if ( ! hwhi ) {
request_module ( " dm-%s " , name ) ;
hwhi = get_hw_handler ( name ) ;
}
return hwhi ? & hwhi - > hwht : NULL ;
}
void dm_put_hw_handler ( struct hw_handler_type * hwht )
{
struct hwh_internal * hwhi ;
if ( ! hwht )
return ;
down_read ( & _hwh_lock ) ;
hwhi = __find_hw_handler_type ( hwht - > name ) ;
if ( ! hwhi )
goto out ;
if ( - - hwhi - > use = = 0 )
module_put ( hwhi - > hwht . module ) ;
2006-03-24 20:36:27 +03:00
BUG_ON ( hwhi - > use < 0 ) ;
2005-04-17 02:20:36 +04:00
out :
up_read ( & _hwh_lock ) ;
}
static struct hwh_internal * _alloc_hw_handler ( struct hw_handler_type * hwht )
{
2007-10-20 01:38:51 +04:00
struct hwh_internal * hwhi = kzalloc ( sizeof ( * hwhi ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 01:38:51 +04:00
if ( hwhi )
2005-04-17 02:20:36 +04:00
hwhi - > hwht = * hwht ;
return hwhi ;
}
int dm_register_hw_handler ( struct hw_handler_type * hwht )
{
int r = 0 ;
struct hwh_internal * hwhi = _alloc_hw_handler ( hwht ) ;
if ( ! hwhi )
return - ENOMEM ;
down_write ( & _hwh_lock ) ;
if ( __find_hw_handler_type ( hwht - > name ) ) {
kfree ( hwhi ) ;
r = - EEXIST ;
} else
list_add ( & hwhi - > list , & _hw_handlers ) ;
up_write ( & _hwh_lock ) ;
return r ;
}
int dm_unregister_hw_handler ( struct hw_handler_type * hwht )
{
struct hwh_internal * hwhi ;
down_write ( & _hwh_lock ) ;
hwhi = __find_hw_handler_type ( hwht - > name ) ;
if ( ! hwhi ) {
up_write ( & _hwh_lock ) ;
return - EINVAL ;
}
if ( hwhi - > use ) {
up_write ( & _hwh_lock ) ;
return - ETXTBSY ;
}
list_del ( & hwhi - > list ) ;
up_write ( & _hwh_lock ) ;
kfree ( hwhi ) ;
return 0 ;
}
unsigned dm_scsi_err_handler ( struct hw_handler * hwh , struct bio * bio )
{
#if 0
int sense_key , asc , ascq ;
if ( bio - > bi_error & BIO_SENSE ) {
/* FIXME: This is just an initial guess. */
/* key / asc / ascq */
sense_key = ( bio - > bi_error > > 16 ) & 0xff ;
asc = ( bio - > bi_error > > 8 ) & 0xff ;
ascq = bio - > bi_error & 0xff ;
switch ( sense_key ) {
/* This block as a whole comes from the device.
* So no point retrying on another path . */
case 0x03 : /* Medium error */
case 0x05 : /* Illegal request */
case 0x07 : /* Data protect */
case 0x08 : /* Blank check */
case 0x0a : /* copy aborted */
case 0x0c : /* obsolete - no clue ;-) */
case 0x0d : /* volume overflow */
case 0x0e : /* data miscompare */
case 0x0f : /* reserved - no idea either. */
return MP_ERROR_IO ;
/* For these errors it's unclear whether they
* come from the device or the controller .
* So just lets try a different path , and if
* it eventually succeeds , user - space will clear
* the paths again . . . */
case 0x02 : /* Not ready */
case 0x04 : /* Hardware error */
case 0x09 : /* vendor specific */
case 0x0b : /* Aborted command */
return MP_FAIL_PATH ;
case 0x06 : /* Unit attention - might want to decode */
if ( asc = = 0x04 & & ascq = = 0x01 )
/* "Unit in the process of
* becoming ready " */
return 0 ;
return MP_FAIL_PATH ;
/* FIXME: For Unit Not Ready we may want
* to have a generic pg activation
* feature ( START_UNIT ) . */
/* Should these two ever end up in the
* error path ? I don ' t think so . */
case 0x00 : /* No sense */
case 0x01 : /* Recovered error */
return 0 ;
}
}
# endif
/* We got no idea how to decode the other kinds of errors ->
* assume generic error condition . */
return MP_FAIL_PATH ;
}
EXPORT_SYMBOL_GPL ( dm_register_hw_handler ) ;
EXPORT_SYMBOL_GPL ( dm_unregister_hw_handler ) ;
EXPORT_SYMBOL_GPL ( dm_scsi_err_handler ) ;