2005-04-16 15:20:36 -07:00
/*
2008-06-10 18:20:58 +02:00
* zfcp device driver
2005-04-16 15:20:36 -07:00
*
2008-06-10 18:20:58 +02:00
* Module interface and handling of zfcp data structures .
2005-04-16 15:20:36 -07:00
*
2008-06-10 18:20:58 +02:00
* Copyright IBM Corporation 2002 , 2008
2005-04-16 15:20:36 -07:00
*/
2006-05-22 18:14:08 +02:00
/*
* Driver authors :
* Martin Peschke ( originator of the driver )
* Raimund Schroeder
* Aron Zeh
* Wolfgang Taphorn
* Stefan Bader
* Heiko Carstens ( kernel 2.6 port of the driver )
* Andreas Herrmann
* Maxim Shchetynin
* Volker Sameske
* Ralph Wuerthner
2008-06-10 18:20:58 +02:00
* Michael Loehr
* Swen Schillig
* Christof Schmitt
* Martin Petermann
* Sven Schuetz
2006-05-22 18:14:08 +02:00
*/
2008-06-10 18:20:55 +02:00
# include <linux/miscdevice.h>
2005-04-16 15:20:36 -07:00
# include "zfcp_ext.h"
static char * device ;
/*********************** FUNCTION PROTOTYPES *********************************/
/* written against the module interface */
static int __init zfcp_module_init ( void ) ;
/*********************** KERNEL/MODULE PARAMETERS ***************************/
/* declare driver module init/cleanup functions */
module_init ( zfcp_module_init ) ;
2006-05-22 18:14:08 +02:00
MODULE_AUTHOR ( " IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com " ) ;
2005-04-16 15:20:36 -07:00
MODULE_DESCRIPTION
2006-05-22 18:14:08 +02:00
( " FCP (SCSI over Fibre Channel) HBA driver for IBM System z9 and zSeries " ) ;
2005-04-16 15:20:36 -07:00
MODULE_LICENSE ( " GPL " ) ;
2005-04-10 23:04:28 -05:00
module_param ( device , charp , 0400 ) ;
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( device , " specify initial device " ) ;
/****************************************************************/
/************** Functions without logging ***********************/
/****************************************************************/
void
_zfcp_hex_dump ( char * addr , int count )
{
int i ;
for ( i = 0 ; i < count ; i + + ) {
printk ( " %02x " , addr [ i ] ) ;
if ( ( i % 4 ) = = 3 )
printk ( " " ) ;
if ( ( i % 32 ) = = 31 )
printk ( " \n " ) ;
}
if ( ( ( i - 1 ) % 32 ) ! = 31 )
printk ( " \n " ) ;
}
2006-08-02 11:05:16 +02:00
/****************************************************************/
/****** Functions to handle the request ID hash table ********/
/****************************************************************/
2007-05-08 11:17:54 +02:00
static int zfcp_reqlist_alloc ( struct zfcp_adapter * adapter )
2006-08-02 11:05:16 +02:00
{
2007-05-08 11:17:54 +02:00
int idx ;
2006-08-02 11:05:16 +02:00
adapter - > req_list = kcalloc ( REQUEST_LIST_SIZE , sizeof ( struct list_head ) ,
GFP_KERNEL ) ;
if ( ! adapter - > req_list )
return - ENOMEM ;
2007-05-08 11:17:54 +02:00
for ( idx = 0 ; idx < REQUEST_LIST_SIZE ; idx + + )
INIT_LIST_HEAD ( & adapter - > req_list [ idx ] ) ;
2006-08-02 11:05:16 +02:00
return 0 ;
}
static void zfcp_reqlist_free ( struct zfcp_adapter * adapter )
{
kfree ( adapter - > req_list ) ;
}
int zfcp_reqlist_isempty ( struct zfcp_adapter * adapter )
{
2007-05-08 11:17:54 +02:00
unsigned int idx ;
2006-08-02 11:05:16 +02:00
2007-05-08 11:17:54 +02:00
for ( idx = 0 ; idx < REQUEST_LIST_SIZE ; idx + + )
if ( ! list_empty ( & adapter - > req_list [ idx ] ) )
2006-08-02 11:05:16 +02:00
return 0 ;
return 1 ;
}
2005-04-16 15:20:36 -07:00
/****************************************************************/
/************** Uncategorised Functions *************************/
/****************************************************************/
/**
* zfcp_device_setup - setup function
* @ str : pointer to parameter string
*
* Parse " device=... " parameter string .
*/
static int __init
2005-06-13 13:22:25 +02:00
zfcp_device_setup ( char * devstr )
2005-04-16 15:20:36 -07:00
{
2005-06-13 13:22:25 +02:00
char * tmp , * str ;
size_t len ;
2005-04-16 15:20:36 -07:00
2005-06-13 13:22:25 +02:00
if ( ! devstr )
2005-04-16 15:20:36 -07:00
return 0 ;
2005-06-13 13:22:25 +02:00
len = strlen ( devstr ) + 1 ;
2006-12-13 00:35:56 -08:00
str = kmalloc ( len , GFP_KERNEL ) ;
2008-06-10 18:20:58 +02:00
if ( ! str ) {
pr_err ( " zfcp: Could not allocate memory for "
" device parameter string, device not attached. \n " ) ;
return 0 ;
}
2005-06-13 13:22:25 +02:00
memcpy ( str , devstr , len ) ;
2005-04-16 15:20:36 -07:00
tmp = strchr ( str , ' , ' ) ;
if ( ! tmp )
goto err_out ;
* tmp + + = ' \0 ' ;
strncpy ( zfcp_data . init_busid , str , BUS_ID_SIZE ) ;
zfcp_data . init_busid [ BUS_ID_SIZE - 1 ] = ' \0 ' ;
zfcp_data . init_wwpn = simple_strtoull ( tmp , & tmp , 0 ) ;
if ( * tmp + + ! = ' , ' )
goto err_out ;
if ( * tmp = = ' \0 ' )
goto err_out ;
zfcp_data . init_fcp_lun = simple_strtoull ( tmp , & tmp , 0 ) ;
if ( * tmp ! = ' \0 ' )
goto err_out ;
2005-06-13 13:22:25 +02:00
kfree ( str ) ;
2005-04-16 15:20:36 -07:00
return 1 ;
err_out :
2008-06-10 18:20:58 +02:00
pr_err ( " zfcp: Parse error for device parameter string %s, "
" device not attached. \n " , str ) ;
2005-06-13 13:22:25 +02:00
kfree ( str ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static void __init
zfcp_init_device_configure ( void )
{
struct zfcp_adapter * adapter ;
struct zfcp_port * port ;
struct zfcp_unit * unit ;
down ( & zfcp_data . config_sema ) ;
read_lock_irq ( & zfcp_data . config_lock ) ;
adapter = zfcp_get_adapter_by_busid ( zfcp_data . init_busid ) ;
if ( adapter )
zfcp_adapter_get ( adapter ) ;
read_unlock_irq ( & zfcp_data . config_lock ) ;
if ( adapter = = NULL )
goto out_adapter ;
port = zfcp_port_enqueue ( adapter , zfcp_data . init_wwpn , 0 , 0 ) ;
if ( ! port )
goto out_port ;
unit = zfcp_unit_enqueue ( port , zfcp_data . init_fcp_lun ) ;
if ( ! unit )
goto out_unit ;
up ( & zfcp_data . config_sema ) ;
ccw_device_set_online ( adapter - > ccw_device ) ;
zfcp_erp_wait ( adapter ) ;
down ( & zfcp_data . config_sema ) ;
zfcp_unit_put ( unit ) ;
out_unit :
zfcp_port_put ( port ) ;
out_port :
zfcp_adapter_put ( adapter ) ;
out_adapter :
up ( & zfcp_data . config_sema ) ;
return ;
}
2006-09-18 22:28:49 +02:00
static int calc_alignment ( int size )
{
int align = 1 ;
if ( ! size )
return 0 ;
while ( ( size - align ) > 0 )
align < < = 1 ;
return align ;
}
2005-04-16 15:20:36 -07:00
static int __init
zfcp_module_init ( void )
{
2006-09-18 22:28:49 +02:00
int retval = - ENOMEM ;
int size , align ;
size = sizeof ( struct zfcp_fsf_req_qtcb ) ;
align = calc_alignment ( size ) ;
zfcp_data . fsf_req_qtcb_cache =
2007-07-20 10:11:58 +09:00
kmem_cache_create ( " zfcp_fsf " , size , align , 0 , NULL ) ;
2006-09-18 22:28:49 +02:00
if ( ! zfcp_data . fsf_req_qtcb_cache )
goto out ;
2005-04-16 15:20:36 -07:00
2006-09-18 22:28:49 +02:00
size = sizeof ( struct fsf_status_read_buffer ) ;
align = calc_alignment ( size ) ;
zfcp_data . sr_buffer_cache =
2007-07-20 10:11:58 +09:00
kmem_cache_create ( " zfcp_sr " , size , align , 0 , NULL ) ;
2006-09-18 22:28:49 +02:00
if ( ! zfcp_data . sr_buffer_cache )
goto out_sr_cache ;
size = sizeof ( struct zfcp_gid_pn_data ) ;
align = calc_alignment ( size ) ;
zfcp_data . gid_pn_cache =
2007-07-20 10:11:58 +09:00
kmem_cache_create ( " zfcp_gid " , size , align , 0 , NULL ) ;
2006-09-18 22:28:49 +02:00
if ( ! zfcp_data . gid_pn_cache )
goto out_gid_cache ;
2005-04-16 15:20:36 -07:00
/* initialize adapter list */
INIT_LIST_HEAD ( & zfcp_data . adapter_list_head ) ;
/* initialize adapters to be removed list head */
INIT_LIST_HEAD ( & zfcp_data . adapter_remove_lh ) ;
2006-09-18 22:28:49 +02:00
zfcp_data . scsi_transport_template =
fc_attach_transport ( & zfcp_transport_functions ) ;
if ( ! zfcp_data . scsi_transport_template )
goto out_transport ;
2005-04-16 15:20:36 -07:00
retval = misc_register ( & zfcp_cfdc_misc ) ;
if ( retval ! = 0 ) {
2008-06-10 18:20:58 +02:00
pr_err ( " zfcp: registration of misc device zfcp_cfdc failed \n " ) ;
2006-09-18 22:28:49 +02:00
goto out_misc ;
2005-04-16 15:20:36 -07:00
}
/* Initialise proc semaphores */
sema_init ( & zfcp_data . config_sema , 1 ) ;
/* initialise configuration rw lock */
rwlock_init ( & zfcp_data . config_lock ) ;
/* setup dynamic I/O */
retval = zfcp_ccw_register ( ) ;
if ( retval ) {
2008-06-10 18:20:58 +02:00
pr_err ( " zfcp: Registration with common I/O layer failed. \n " ) ;
2005-04-16 15:20:36 -07:00
goto out_ccw_register ;
}
if ( zfcp_device_setup ( device ) )
zfcp_init_device_configure ( ) ;
goto out ;
out_ccw_register :
misc_deregister ( & zfcp_cfdc_misc ) ;
2006-09-18 22:28:49 +02:00
out_misc :
fc_release_transport ( zfcp_data . scsi_transport_template ) ;
out_transport :
kmem_cache_destroy ( zfcp_data . gid_pn_cache ) ;
out_gid_cache :
kmem_cache_destroy ( zfcp_data . sr_buffer_cache ) ;
out_sr_cache :
kmem_cache_destroy ( zfcp_data . fsf_req_qtcb_cache ) ;
2005-04-16 15:20:36 -07:00
out :
return retval ;
}
/****************************************************************/
/****** Functions for configuration/set-up of structures ********/
/****************************************************************/
/**
* zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN
* @ port : pointer to port to search for unit
* @ fcp_lun : FCP LUN to search for
* Traverse list of all units of a port and return pointer to a unit
* with the given FCP LUN .
*/
struct zfcp_unit *
zfcp_get_unit_by_lun ( struct zfcp_port * port , fcp_lun_t fcp_lun )
{
struct zfcp_unit * unit ;
int found = 0 ;
list_for_each_entry ( unit , & port - > unit_list_head , list ) {
if ( ( unit - > fcp_lun = = fcp_lun ) & &
! atomic_test_mask ( ZFCP_STATUS_COMMON_REMOVE , & unit - > status ) )
{
found = 1 ;
break ;
}
}
return found ? unit : NULL ;
}
/**
* zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn
* @ adapter : pointer to adapter to search for port
* @ wwpn : wwpn to search for
* Traverse list of all ports of an adapter and return pointer to a port
* with the given wwpn .
*/
struct zfcp_port *
zfcp_get_port_by_wwpn ( struct zfcp_adapter * adapter , wwn_t wwpn )
{
struct zfcp_port * port ;
int found = 0 ;
list_for_each_entry ( port , & adapter - > port_list_head , list ) {
if ( ( port - > wwpn = = wwpn ) & &
! ( atomic_read ( & port - > status ) &
( ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE ) ) ) {
found = 1 ;
break ;
}
}
return found ? port : NULL ;
}
/**
* zfcp_get_port_by_did - find port in port list of adapter by d_id
* @ adapter : pointer to adapter to search for port
* @ d_id : d_id to search for
* Traverse list of all ports of an adapter and return pointer to a port
* with the given d_id .
*/
struct zfcp_port *
zfcp_get_port_by_did ( struct zfcp_adapter * adapter , u32 d_id )
{
struct zfcp_port * port ;
int found = 0 ;
list_for_each_entry ( port , & adapter - > port_list_head , list ) {
if ( ( port - > d_id = = d_id ) & &
! atomic_test_mask ( ZFCP_STATUS_COMMON_REMOVE , & port - > status ) )
{
found = 1 ;
break ;
}
}
return found ? port : NULL ;
}
/**
* zfcp_get_adapter_by_busid - find adpater in adapter list by bus_id
* @ bus_id : bus_id to search for
* Traverse list of all adapters and return pointer to an adapter
* with the given bus_id .
*/
struct zfcp_adapter *
zfcp_get_adapter_by_busid ( char * bus_id )
{
struct zfcp_adapter * adapter ;
int found = 0 ;
list_for_each_entry ( adapter , & zfcp_data . adapter_list_head , list ) {
if ( ( strncmp ( bus_id , zfcp_get_busid_by_adapter ( adapter ) ,
BUS_ID_SIZE ) = = 0 ) & &
! atomic_test_mask ( ZFCP_STATUS_COMMON_REMOVE ,
& adapter - > status ) ) {
found = 1 ;
break ;
}
}
return found ? adapter : NULL ;
}
/**
* zfcp_unit_enqueue - enqueue unit to unit list of a port .
* @ port : pointer to port where unit is added
* @ fcp_lun : FCP LUN of unit to be enqueued
* Return : pointer to enqueued unit on success , NULL on error
* Locks : config_sema must be held to serialize changes to the unit list
*
* Sets up some unit internal structures and creates sysfs entry .
*/
struct zfcp_unit *
zfcp_unit_enqueue ( struct zfcp_port * port , fcp_lun_t fcp_lun )
{
2007-06-19 10:25:30 +02:00
struct zfcp_unit * unit ;
2005-04-16 15:20:36 -07:00
/*
* check that there is no unit with this FCP_LUN already in list
* and enqueue it .
* Note : Unlike for the adapter and the port , this is an error
*/
read_lock_irq ( & zfcp_data . config_lock ) ;
unit = zfcp_get_unit_by_lun ( port , fcp_lun ) ;
read_unlock_irq ( & zfcp_data . config_lock ) ;
if ( unit )
return NULL ;
2006-05-22 18:17:30 +02:00
unit = kzalloc ( sizeof ( struct zfcp_unit ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! unit )
return NULL ;
/* initialise reference count stuff */
atomic_set ( & unit - > refcount , 0 ) ;
init_waitqueue_head ( & unit - > remove_wq ) ;
unit - > port = port ;
unit - > fcp_lun = fcp_lun ;
/* setup for sysfs registration */
snprintf ( unit - > sysfs_device . bus_id , BUS_ID_SIZE , " 0x%016llx " , fcp_lun ) ;
unit - > sysfs_device . parent = & port - > sysfs_device ;
unit - > sysfs_device . release = zfcp_sysfs_unit_release ;
dev_set_drvdata ( & unit - > sysfs_device , unit ) ;
/* mark unit unusable as long as sysfs registration is not complete */
atomic_set_mask ( ZFCP_STATUS_COMMON_REMOVE , & unit - > status ) ;
2008-05-06 11:00:05 +02:00
spin_lock_init ( & unit - > latencies . lock ) ;
unit - > latencies . write . channel . min = 0xFFFFFFFF ;
unit - > latencies . write . fabric . min = 0xFFFFFFFF ;
unit - > latencies . read . channel . min = 0xFFFFFFFF ;
unit - > latencies . read . fabric . min = 0xFFFFFFFF ;
unit - > latencies . cmd . channel . min = 0xFFFFFFFF ;
unit - > latencies . cmd . fabric . min = 0xFFFFFFFF ;
2005-04-16 15:20:36 -07:00
if ( device_register ( & unit - > sysfs_device ) ) {
kfree ( unit ) ;
return NULL ;
}
if ( zfcp_sysfs_unit_create_files ( & unit - > sysfs_device ) ) {
device_unregister ( & unit - > sysfs_device ) ;
return NULL ;
}
zfcp_unit_get ( unit ) ;
2007-06-19 10:25:30 +02:00
unit - > scsi_lun = scsilun_to_int ( ( struct scsi_lun * ) & unit - > fcp_lun ) ;
2005-04-16 15:20:36 -07:00
write_lock_irq ( & zfcp_data . config_lock ) ;
2007-06-19 10:25:30 +02:00
list_add_tail ( & unit - > list , & port - > unit_list_head ) ;
2005-04-16 15:20:36 -07:00
atomic_clear_mask ( ZFCP_STATUS_COMMON_REMOVE , & unit - > status ) ;
atomic_set_mask ( ZFCP_STATUS_COMMON_RUNNING , & unit - > status ) ;
write_unlock_irq ( & zfcp_data . config_lock ) ;
port - > units + + ;
zfcp_port_get ( port ) ;
return unit ;
}
void
zfcp_unit_dequeue ( struct zfcp_unit * unit )
{
zfcp_unit_wait ( unit ) ;
write_lock_irq ( & zfcp_data . config_lock ) ;
list_del ( & unit - > list ) ;
write_unlock_irq ( & zfcp_data . config_lock ) ;
unit - > port - > units - - ;
zfcp_port_put ( unit - > port ) ;
zfcp_sysfs_unit_remove_files ( & unit - > sysfs_device ) ;
device_unregister ( & unit - > sysfs_device ) ;
}
/*
* Allocates a combined QTCB / fsf_req buffer for erp actions and fcp / SCSI
* commands .
2007-09-07 09:15:31 +02:00
* It also genrates fcp - nameserver request / response buffer and unsolicited
2005-04-16 15:20:36 -07:00
* status read fsf_req buffers .
*
* locks : must only be called with zfcp_data . config_sema taken
*/
static int
zfcp_allocate_low_mem_buffers ( struct zfcp_adapter * adapter )
{
adapter - > pool . fsf_req_erp =
2006-09-18 22:28:49 +02:00
mempool_create_slab_pool ( ZFCP_POOL_FSF_REQ_ERP_NR ,
zfcp_data . fsf_req_qtcb_cache ) ;
2006-03-26 01:37:47 -08:00
if ( ! adapter - > pool . fsf_req_erp )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
adapter - > pool . fsf_req_scsi =
2006-09-18 22:28:49 +02:00
mempool_create_slab_pool ( ZFCP_POOL_FSF_REQ_SCSI_NR ,
zfcp_data . fsf_req_qtcb_cache ) ;
2006-03-26 01:37:47 -08:00
if ( ! adapter - > pool . fsf_req_scsi )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
adapter - > pool . fsf_req_abort =
2006-09-18 22:28:49 +02:00
mempool_create_slab_pool ( ZFCP_POOL_FSF_REQ_ABORT_NR ,
zfcp_data . fsf_req_qtcb_cache ) ;
2006-03-26 01:37:47 -08:00
if ( ! adapter - > pool . fsf_req_abort )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
adapter - > pool . fsf_req_status_read =
2006-03-26 01:37:47 -08:00
mempool_create_kmalloc_pool ( ZFCP_POOL_STATUS_READ_NR ,
sizeof ( struct zfcp_fsf_req ) ) ;
if ( ! adapter - > pool . fsf_req_status_read )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
adapter - > pool . data_status_read =
2006-09-18 22:28:49 +02:00
mempool_create_slab_pool ( ZFCP_POOL_STATUS_READ_NR ,
zfcp_data . sr_buffer_cache ) ;
2006-03-26 01:37:47 -08:00
if ( ! adapter - > pool . data_status_read )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
adapter - > pool . data_gid_pn =
2006-09-18 22:28:49 +02:00
mempool_create_slab_pool ( ZFCP_POOL_DATA_GID_PN_NR ,
zfcp_data . gid_pn_cache ) ;
2006-03-26 01:37:47 -08:00
if ( ! adapter - > pool . data_gid_pn )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
return 0 ;
}
/**
* zfcp_free_low_mem_buffers - free memory pools of an adapter
* @ adapter : pointer to zfcp_adapter for which memory pools should be freed
* locking : zfcp_data . config_sema must be held
*/
static void
zfcp_free_low_mem_buffers ( struct zfcp_adapter * adapter )
{
if ( adapter - > pool . fsf_req_erp )
mempool_destroy ( adapter - > pool . fsf_req_erp ) ;
if ( adapter - > pool . fsf_req_scsi )
mempool_destroy ( adapter - > pool . fsf_req_scsi ) ;
if ( adapter - > pool . fsf_req_abort )
mempool_destroy ( adapter - > pool . fsf_req_abort ) ;
if ( adapter - > pool . fsf_req_status_read )
mempool_destroy ( adapter - > pool . fsf_req_status_read ) ;
if ( adapter - > pool . data_status_read )
mempool_destroy ( adapter - > pool . data_status_read ) ;
if ( adapter - > pool . data_gid_pn )
mempool_destroy ( adapter - > pool . data_gid_pn ) ;
}
2007-05-10 15:45:46 +02:00
static void zfcp_dummy_release ( struct device * dev )
2005-04-16 15:20:36 -07:00
{
return ;
}
2008-05-19 12:17:37 +02:00
int zfcp_status_read_refill ( struct zfcp_adapter * adapter )
{
while ( atomic_read ( & adapter - > stat_miss ) > 0 )
if ( zfcp_fsf_status_read ( adapter , ZFCP_WAIT_FOR_SBAL ) )
break ;
else
atomic_dec ( & adapter - > stat_miss ) ;
if ( ZFCP_STATUS_READS_RECOM < = atomic_read ( & adapter - > stat_miss ) ) {
zfcp_erp_adapter_reopen ( adapter , 0 , 103 , NULL ) ;
return 1 ;
}
return 0 ;
}
static void _zfcp_status_read_scheduler ( struct work_struct * work )
{
zfcp_status_read_refill ( container_of ( work , struct zfcp_adapter ,
stat_work ) ) ;
}
2008-06-10 18:21:00 +02:00
static int zfcp_nameserver_enqueue ( struct zfcp_adapter * adapter )
{
struct zfcp_port * port ;
port = zfcp_port_enqueue ( adapter , 0 , ZFCP_STATUS_PORT_WKA ,
ZFCP_DID_DIRECTORY_SERVICE ) ;
if ( ! port )
return - ENXIO ;
zfcp_port_put ( port ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
/*
* Enqueues an adapter at the end of the adapter list in the driver data .
* All adapter internal structures are set up .
* Proc - fs entries are also created .
*
2008-06-10 18:20:58 +02:00
* FIXME : Use - ENOMEM as return code for allocation failures
*
2005-04-16 15:20:36 -07:00
* returns : 0 if a new adapter was successfully enqueued
* ZFCP_KNOWN if an adapter with this devno was already present
* - ENOMEM if alloc failed
* locks : config_sema must be held to serialise changes to the adapter list
*/
struct zfcp_adapter *
zfcp_adapter_enqueue ( struct ccw_device * ccw_device )
{
struct zfcp_adapter * adapter ;
/*
2007-09-07 09:15:31 +02:00
* Note : It is safe to release the list_lock , as any list changes
2005-04-16 15:20:36 -07:00
* are protected by the config_sema , which must be held to get here
*/
/* try to allocate new adapter data structure (zeroed) */
2006-05-22 18:17:30 +02:00
adapter = kzalloc ( sizeof ( struct zfcp_adapter ) , GFP_KERNEL ) ;
2008-06-10 18:20:58 +02:00
if ( ! adapter )
2005-04-16 15:20:36 -07:00
goto out ;
ccw_device - > handler = NULL ;
/* save ccw_device pointer */
adapter - > ccw_device = ccw_device ;
2008-06-10 18:20:57 +02:00
if ( zfcp_qdio_allocate ( adapter ) )
2005-04-16 15:20:36 -07:00
goto qdio_allocate_failed ;
2008-06-10 18:20:57 +02:00
if ( zfcp_allocate_low_mem_buffers ( adapter ) )
2005-04-16 15:20:36 -07:00
goto failed_low_mem_buffers ;
/* initialise reference count stuff */
atomic_set ( & adapter - > refcount , 0 ) ;
init_waitqueue_head ( & adapter - > remove_wq ) ;
/* initialise list of ports */
INIT_LIST_HEAD ( & adapter - > port_list_head ) ;
/* initialise list of ports to be removed */
INIT_LIST_HEAD ( & adapter - > port_remove_lh ) ;
/* initialize list of fsf requests */
2006-08-02 11:05:16 +02:00
spin_lock_init ( & adapter - > req_list_lock ) ;
2008-06-10 18:20:57 +02:00
if ( zfcp_reqlist_alloc ( adapter ) )
2006-08-02 11:05:16 +02:00
goto failed_low_mem_buffers ;
2005-04-16 15:20:36 -07:00
2005-12-01 02:46:32 +01:00
/* initialize debug locks */
spin_lock_init ( & adapter - > hba_dbf_lock ) ;
spin_lock_init ( & adapter - > san_dbf_lock ) ;
spin_lock_init ( & adapter - > scsi_dbf_lock ) ;
2008-03-27 14:22:00 +01:00
spin_lock_init ( & adapter - > rec_dbf_lock ) ;
2005-12-01 02:46:32 +01:00
2008-06-10 18:20:57 +02:00
if ( zfcp_adapter_debug_register ( adapter ) )
2007-08-28 09:31:41 +02:00
goto debug_register_failed ;
2005-12-01 02:46:32 +01:00
/* initialize error recovery stuff */
rwlock_init ( & adapter - > erp_lock ) ;
sema_init ( & adapter - > erp_ready_sem , 0 ) ;
INIT_LIST_HEAD ( & adapter - > erp_ready_head ) ;
INIT_LIST_HEAD ( & adapter - > erp_running_head ) ;
2005-04-16 15:20:36 -07:00
/* initialize abort lock */
rwlock_init ( & adapter - > abort_lock ) ;
/* initialise some erp stuff */
init_waitqueue_head ( & adapter - > erp_thread_wqh ) ;
init_waitqueue_head ( & adapter - > erp_done_wqh ) ;
/* initialize lock of associated request queue */
2008-06-10 18:20:57 +02:00
rwlock_init ( & adapter - > req_q . lock ) ;
2008-05-19 12:17:37 +02:00
INIT_WORK ( & adapter - > stat_work , _zfcp_status_read_scheduler ) ;
2008-06-10 18:21:00 +02:00
INIT_WORK ( & adapter - > scan_work , _zfcp_scan_ports_later ) ;
2005-04-16 15:20:36 -07:00
/* mark adapter unusable as long as sysfs registration is not complete */
atomic_set_mask ( ZFCP_STATUS_COMMON_REMOVE , & adapter - > status ) ;
dev_set_drvdata ( & ccw_device - > dev , adapter ) ;
if ( zfcp_sysfs_adapter_create_files ( & ccw_device - > dev ) )
goto sysfs_failed ;
adapter - > generic_services . parent = & adapter - > ccw_device - > dev ;
adapter - > generic_services . release = zfcp_dummy_release ;
snprintf ( adapter - > generic_services . bus_id , BUS_ID_SIZE ,
" generic_services " ) ;
if ( device_register ( & adapter - > generic_services ) )
goto generic_services_failed ;
/* put allocated adapter at list tail */
write_lock_irq ( & zfcp_data . config_lock ) ;
atomic_clear_mask ( ZFCP_STATUS_COMMON_REMOVE , & adapter - > status ) ;
list_add_tail ( & adapter - > list , & zfcp_data . adapter_list_head ) ;
write_unlock_irq ( & zfcp_data . config_lock ) ;
zfcp_data . adapters + + ;
2008-06-10 18:21:00 +02:00
zfcp_nameserver_enqueue ( adapter ) ;
2005-04-16 15:20:36 -07:00
goto out ;
generic_services_failed :
zfcp_sysfs_adapter_remove_files ( & adapter - > ccw_device - > dev ) ;
sysfs_failed :
2007-08-28 09:31:41 +02:00
zfcp_adapter_debug_unregister ( adapter ) ;
debug_register_failed :
2005-04-16 15:20:36 -07:00
dev_set_drvdata ( & ccw_device - > dev , NULL ) ;
2007-05-08 11:17:54 +02:00
zfcp_reqlist_free ( adapter ) ;
2005-04-16 15:20:36 -07:00
failed_low_mem_buffers :
zfcp_free_low_mem_buffers ( adapter ) ;
qdio_allocate_failed :
2008-06-10 18:20:57 +02:00
zfcp_qdio_free ( adapter ) ;
2005-04-16 15:20:36 -07:00
kfree ( adapter ) ;
adapter = NULL ;
out :
return adapter ;
}
/*
* returns : 0 - struct zfcp_adapter data structure successfully removed
* ! 0 - struct zfcp_adapter data structure could not be removed
* ( e . g . still used )
* locks : adapter list write lock is assumed to be held by caller
*/
void
zfcp_adapter_dequeue ( struct zfcp_adapter * adapter )
{
int retval = 0 ;
unsigned long flags ;
2008-06-10 18:21:00 +02:00
cancel_work_sync ( & adapter - > scan_work ) ;
2008-05-19 12:17:37 +02:00
cancel_work_sync ( & adapter - > stat_work ) ;
2007-05-09 11:01:24 +02:00
zfcp_adapter_scsi_unregister ( adapter ) ;
2005-04-16 15:20:36 -07:00
device_unregister ( & adapter - > generic_services ) ;
zfcp_sysfs_adapter_remove_files ( & adapter - > ccw_device - > dev ) ;
dev_set_drvdata ( & adapter - > ccw_device - > dev , NULL ) ;
/* sanity check: no pending FSF requests */
2006-08-02 11:05:16 +02:00
spin_lock_irqsave ( & adapter - > req_list_lock , flags ) ;
retval = zfcp_reqlist_isempty ( adapter ) ;
spin_unlock_irqrestore ( & adapter - > req_list_lock , flags ) ;
if ( ! retval ) {
2005-04-16 15:20:36 -07:00
retval = - EBUSY ;
goto out ;
}
2007-08-28 09:31:41 +02:00
zfcp_adapter_debug_unregister ( adapter ) ;
2005-04-16 15:20:36 -07:00
/* remove specified adapter data structure from list */
write_lock_irq ( & zfcp_data . config_lock ) ;
list_del ( & adapter - > list ) ;
write_unlock_irq ( & zfcp_data . config_lock ) ;
/* decrease number of adapters in list */
zfcp_data . adapters - - ;
2008-06-10 18:20:57 +02:00
zfcp_qdio_free ( adapter ) ;
2005-04-16 15:20:36 -07:00
zfcp_free_low_mem_buffers ( adapter ) ;
2006-08-02 11:05:16 +02:00
zfcp_reqlist_free ( adapter ) ;
2006-01-05 09:59:34 +01:00
kfree ( adapter - > fc_stats ) ;
kfree ( adapter - > stats_reset_data ) ;
2005-04-16 15:20:36 -07:00
kfree ( adapter ) ;
out :
return ;
}
/**
* zfcp_port_enqueue - enqueue port to port list of adapter
* @ adapter : adapter where remote port is added
* @ wwpn : WWPN of the remote port to be enqueued
* @ status : initial status for the port
* @ d_id : destination id of the remote port to be enqueued
* Return : pointer to enqueued port on success , NULL on error
* Locks : config_sema must be held to serialize changes to the port list
*
* All port internal structures are set up and the sysfs entry is generated .
* d_id is used to enqueue ports with a well known address like the Directory
* Service for nameserver lookup .
*/
struct zfcp_port *
zfcp_port_enqueue ( struct zfcp_adapter * adapter , wwn_t wwpn , u32 status ,
u32 d_id )
{
2005-08-27 11:07:54 -07:00
struct zfcp_port * port ;
2005-04-16 15:20:36 -07:00
int check_wwpn ;
check_wwpn = ! ( status & ZFCP_STATUS_PORT_NO_WWPN ) ;
/*
* check that there is no port with this WWPN already in list
*/
if ( check_wwpn ) {
read_lock_irq ( & zfcp_data . config_lock ) ;
port = zfcp_get_port_by_wwpn ( adapter , wwpn ) ;
read_unlock_irq ( & zfcp_data . config_lock ) ;
if ( port )
return NULL ;
}
2006-05-22 18:17:30 +02:00
port = kzalloc ( sizeof ( struct zfcp_port ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! port )
return NULL ;
/* initialise reference count stuff */
atomic_set ( & port - > refcount , 0 ) ;
init_waitqueue_head ( & port - > remove_wq ) ;
INIT_LIST_HEAD ( & port - > unit_list_head ) ;
INIT_LIST_HEAD ( & port - > unit_remove_lh ) ;
port - > adapter = adapter ;
if ( check_wwpn )
port - > wwpn = wwpn ;
atomic_set_mask ( status , & port - > status ) ;
/* setup for sysfs registration */
if ( status & ZFCP_STATUS_PORT_WKA ) {
switch ( d_id ) {
case ZFCP_DID_DIRECTORY_SERVICE :
snprintf ( port - > sysfs_device . bus_id , BUS_ID_SIZE ,
" directory " ) ;
break ;
case ZFCP_DID_MANAGEMENT_SERVICE :
snprintf ( port - > sysfs_device . bus_id , BUS_ID_SIZE ,
" management " ) ;
break ;
case ZFCP_DID_KEY_DISTRIBUTION_SERVICE :
snprintf ( port - > sysfs_device . bus_id , BUS_ID_SIZE ,
" key_distribution " ) ;
break ;
case ZFCP_DID_ALIAS_SERVICE :
snprintf ( port - > sysfs_device . bus_id , BUS_ID_SIZE ,
" alias " ) ;
break ;
case ZFCP_DID_TIME_SERVICE :
snprintf ( port - > sysfs_device . bus_id , BUS_ID_SIZE ,
" time " ) ;
break ;
default :
kfree ( port ) ;
return NULL ;
}
port - > sysfs_device . parent = & adapter - > generic_services ;
} else {
snprintf ( port - > sysfs_device . bus_id ,
BUS_ID_SIZE , " 0x%016llx " , wwpn ) ;
2005-08-27 11:07:54 -07:00
port - > sysfs_device . parent = & adapter - > ccw_device - > dev ;
2005-04-16 15:20:36 -07:00
}
2008-06-10 18:21:00 +02:00
port - > d_id = d_id ;
2005-04-16 15:20:36 -07:00
port - > sysfs_device . release = zfcp_sysfs_port_release ;
dev_set_drvdata ( & port - > sysfs_device , port ) ;
/* mark port unusable as long as sysfs registration is not complete */
atomic_set_mask ( ZFCP_STATUS_COMMON_REMOVE , & port - > status ) ;
if ( device_register ( & port - > sysfs_device ) ) {
kfree ( port ) ;
return NULL ;
}
if ( zfcp_sysfs_port_create_files ( & port - > sysfs_device , status ) ) {
device_unregister ( & port - > sysfs_device ) ;
return NULL ;
}
zfcp_port_get ( port ) ;
write_lock_irq ( & zfcp_data . config_lock ) ;
2005-08-27 11:07:54 -07:00
list_add_tail ( & port - > list , & adapter - > port_list_head ) ;
2005-04-16 15:20:36 -07:00
atomic_clear_mask ( ZFCP_STATUS_COMMON_REMOVE , & port - > status ) ;
atomic_set_mask ( ZFCP_STATUS_COMMON_RUNNING , & port - > status ) ;
if ( d_id = = ZFCP_DID_DIRECTORY_SERVICE )
if ( ! adapter - > nameserver_port )
adapter - > nameserver_port = port ;
adapter - > ports + + ;
write_unlock_irq ( & zfcp_data . config_lock ) ;
zfcp_adapter_get ( adapter ) ;
return port ;
}
void
zfcp_port_dequeue ( struct zfcp_port * port )
{
zfcp_port_wait ( port ) ;
write_lock_irq ( & zfcp_data . config_lock ) ;
list_del ( & port - > list ) ;
port - > adapter - > ports - - ;
write_unlock_irq ( & zfcp_data . config_lock ) ;
2005-08-27 11:07:54 -07:00
if ( port - > rport )
2005-08-28 13:22:37 -07:00
fc_remote_port_delete ( port - > rport ) ;
port - > rport = NULL ;
2005-04-16 15:20:36 -07:00
zfcp_adapter_put ( port - > adapter ) ;
zfcp_sysfs_port_remove_files ( & port - > sysfs_device ,
atomic_read ( & port - > status ) ) ;
device_unregister ( & port - > sysfs_device ) ;
}
2008-06-10 18:20:55 +02:00
void zfcp_sg_free_table ( struct scatterlist * sg , int count )
{
int i ;
for ( i = 0 ; i < count ; i + + , sg + + )
if ( sg )
free_page ( ( unsigned long ) sg_virt ( sg ) ) ;
else
break ;
}
int zfcp_sg_setup_table ( struct scatterlist * sg , int count )
{
void * addr ;
int i ;
sg_init_table ( sg , count ) ;
for ( i = 0 ; i < count ; i + + , sg + + ) {
addr = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! addr ) {
zfcp_sg_free_table ( sg , i ) ;
return - ENOMEM ;
}
sg_set_buf ( sg , addr , PAGE_SIZE ) ;
}
return 0 ;
}