2005-08-05 06:33:07 +04:00
/*
2005-04-17 02:20:36 +04:00
* iSCSI transport class definitions
*
* Copyright ( C ) IBM Corporation , 2004
2005-08-05 06:33:07 +04:00
* Copyright ( C ) Mike Christie , 2004 - 2005
* Copyright ( C ) Dmitry Yusupov , 2004 - 2005
* Copyright ( C ) Alex Aizman , 2004 - 2005
2005-04-17 02:20:36 +04:00
*
* 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 .
*/
# include <linux/module.h>
2005-08-05 06:33:07 +04:00
# include <linux/mempool.h>
2006-01-11 15:16:10 +03:00
# include <linux/mutex.h>
2005-08-05 06:33:07 +04:00
# include <net/tcp.h>
2005-04-17 02:20:36 +04:00
# include <scsi/scsi.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_transport.h>
# include <scsi/scsi_transport_iscsi.h>
2005-08-05 06:33:07 +04:00
# include <scsi/iscsi_if.h>
2005-04-17 02:20:36 +04:00
2006-04-07 06:13:39 +04:00
# define ISCSI_SESSION_ATTRS 11
2006-05-03 04:46:47 +04:00
# define ISCSI_CONN_ATTRS 11
2006-04-07 06:13:39 +04:00
# define ISCSI_HOST_ATTRS 0
2005-04-17 02:20:36 +04:00
struct iscsi_internal {
2006-05-19 05:31:39 +04:00
int daemon_pid ;
2005-04-17 02:20:36 +04:00
struct scsi_transport_template t ;
2005-08-05 06:33:07 +04:00
struct iscsi_transport * iscsi_transport ;
struct list_head list ;
struct class_device cdev ;
2006-04-07 06:13:39 +04:00
struct class_device_attribute * host_attrs [ ISCSI_HOST_ATTRS + 1 ] ;
2005-08-05 06:33:07 +04:00
struct transport_container conn_cont ;
struct class_device_attribute * conn_attrs [ ISCSI_CONN_ATTRS + 1 ] ;
struct transport_container session_cont ;
2005-04-17 02:20:36 +04:00
struct class_device_attribute * session_attrs [ ISCSI_SESSION_ATTRS + 1 ] ;
} ;
2006-04-07 06:13:33 +04:00
static int iscsi_session_nr ; /* sysfs session id for next new session */
2005-08-05 06:33:07 +04:00
/*
* list of registered transports and lock that must
* be held while accessing list . The iscsi_transport_lock must
2006-01-11 15:16:10 +03:00
* be acquired after the rx_queue_mutex .
2005-08-05 06:33:07 +04:00
*/
static LIST_HEAD ( iscsi_transports ) ;
static DEFINE_SPINLOCK ( iscsi_transport_lock ) ;
# define to_iscsi_internal(tmpl) \
container_of ( tmpl , struct iscsi_internal , t )
# define cdev_to_iscsi_internal(_cdev) \
container_of ( _cdev , struct iscsi_internal , cdev )
static void iscsi_transport_release ( struct class_device * cdev )
{
struct iscsi_internal * priv = cdev_to_iscsi_internal ( cdev ) ;
kfree ( priv ) ;
}
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
/*
* iscsi_transport_class represents the iscsi_transports that are
* registered .
*/
static struct class iscsi_transport_class = {
. name = " iscsi_transport " ,
. release = iscsi_transport_release ,
} ;
static ssize_t
show_transport_handle ( struct class_device * cdev , char * buf )
{
struct iscsi_internal * priv = cdev_to_iscsi_internal ( cdev ) ;
2005-09-13 06:01:46 +04:00
return sprintf ( buf , " %llu \n " , ( unsigned long long ) iscsi_handle ( priv - > iscsi_transport ) ) ;
2005-08-05 06:33:07 +04:00
}
static CLASS_DEVICE_ATTR ( handle , S_IRUGO , show_transport_handle , NULL ) ;
# define show_transport_attr(name, format) \
static ssize_t \
show_transport_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct iscsi_internal * priv = cdev_to_iscsi_internal ( cdev ) ; \
return sprintf ( buf , format " \n " , priv - > iscsi_transport - > name ) ; \
} \
static CLASS_DEVICE_ATTR ( name , S_IRUGO , show_transport_ # # name , NULL ) ;
show_transport_attr ( caps , " 0x%x " ) ;
show_transport_attr ( max_lun , " %d " ) ;
show_transport_attr ( max_conn , " %d " ) ;
show_transport_attr ( max_cmd_len , " %d " ) ;
static struct attribute * iscsi_transport_attrs [ ] = {
& class_device_attr_handle . attr ,
& class_device_attr_caps . attr ,
& class_device_attr_max_lun . attr ,
& class_device_attr_max_conn . attr ,
& class_device_attr_max_cmd_len . attr ,
NULL ,
} ;
static struct attribute_group iscsi_transport_group = {
. attrs = iscsi_transport_attrs ,
} ;
2006-04-07 06:13:39 +04:00
static int iscsi_setup_host ( struct transport_container * tc , struct device * dev ,
struct class_device * cdev )
{
struct Scsi_Host * shost = dev_to_shost ( dev ) ;
struct iscsi_host * ihost = shost - > shost_data ;
memset ( ihost , 0 , sizeof ( * ihost ) ) ;
INIT_LIST_HEAD ( & ihost - > sessions ) ;
mutex_init ( & ihost - > mutex ) ;
return 0 ;
}
static DECLARE_TRANSPORT_CLASS ( iscsi_host_class ,
" iscsi_host " ,
iscsi_setup_host ,
NULL ,
NULL ) ;
2005-08-05 06:33:07 +04:00
static DECLARE_TRANSPORT_CLASS ( iscsi_session_class ,
" iscsi_session " ,
2005-04-17 02:20:36 +04:00
NULL ,
NULL ,
NULL ) ;
2005-08-05 06:33:07 +04:00
static DECLARE_TRANSPORT_CLASS ( iscsi_connection_class ,
" iscsi_connection " ,
2005-04-17 02:20:36 +04:00
NULL ,
NULL ,
NULL ) ;
2005-08-05 06:33:07 +04:00
static struct sock * nls ;
2006-01-11 15:16:10 +03:00
static DEFINE_MUTEX ( rx_queue_mutex ) ;
2005-08-05 06:33:07 +04:00
struct mempool_zone {
mempool_t * pool ;
atomic_t allocated ;
int size ;
int hiwat ;
struct list_head freequeue ;
spinlock_t freelock ;
} ;
2006-01-14 03:05:50 +03:00
static struct mempool_zone * z_reply ;
2005-08-05 06:33:07 +04:00
2005-04-17 02:20:36 +04:00
/*
2005-08-05 06:33:07 +04:00
* Z_MAX_ * - actual mempool size allocated at the mempool_zone_init ( ) time
* Z_HIWAT_ * - zone ' s high watermark when if_error bit will be set to - ENOMEM
* so daemon will notice OOM on NETLINK tranposrt level and will
* be able to predict or change operational behavior
2005-04-17 02:20:36 +04:00
*/
2005-08-05 06:33:07 +04:00
# define Z_MAX_REPLY 8
# define Z_HIWAT_REPLY 6
# define Z_MAX_PDU 8
# define Z_HIWAT_PDU 6
# define Z_MAX_ERROR 16
# define Z_HIWAT_ERROR 12
2006-02-02 06:06:49 +03:00
static LIST_HEAD ( sesslist ) ;
static DEFINE_SPINLOCK ( sesslock ) ;
2006-01-14 03:05:50 +03:00
static LIST_HEAD ( connlist ) ;
static DEFINE_SPINLOCK ( connlock ) ;
2005-08-05 06:33:07 +04:00
2006-04-07 06:13:33 +04:00
static uint32_t iscsi_conn_get_sid ( struct iscsi_cls_conn * conn )
{
struct iscsi_cls_session * sess = iscsi_dev_to_session ( conn - > dev . parent ) ;
return sess - > sid ;
}
/*
* Returns the matching session to a given sid
*/
static struct iscsi_cls_session * iscsi_session_lookup ( uint32_t sid )
2006-02-02 06:06:49 +03:00
{
unsigned long flags ;
struct iscsi_cls_session * sess ;
spin_lock_irqsave ( & sesslock , flags ) ;
list_for_each_entry ( sess , & sesslist , sess_list ) {
2006-04-07 06:13:33 +04:00
if ( sess - > sid = = sid ) {
2006-02-02 06:06:49 +03:00
spin_unlock_irqrestore ( & sesslock , flags ) ;
return sess ;
}
}
spin_unlock_irqrestore ( & sesslock , flags ) ;
return NULL ;
}
2006-04-07 06:13:33 +04:00
/*
* Returns the matching connection to a given sid / cid tuple
*/
static struct iscsi_cls_conn * iscsi_conn_lookup ( uint32_t sid , uint32_t cid )
2006-02-02 06:06:49 +03:00
{
unsigned long flags ;
struct iscsi_cls_conn * conn ;
spin_lock_irqsave ( & connlock , flags ) ;
list_for_each_entry ( conn , & connlist , conn_list ) {
2006-04-07 06:13:33 +04:00
if ( ( conn - > cid = = cid ) & & ( iscsi_conn_get_sid ( conn ) = = sid ) ) {
2006-02-02 06:06:49 +03:00
spin_unlock_irqrestore ( & connlock , flags ) ;
return conn ;
}
}
spin_unlock_irqrestore ( & connlock , flags ) ;
return NULL ;
}
2006-01-14 03:05:50 +03:00
/*
* The following functions can be used by LLDs that allocate
* their own scsi_hosts or by software iscsi LLDs
*/
static void iscsi_session_release ( struct device * dev )
{
struct iscsi_cls_session * session = iscsi_dev_to_session ( dev ) ;
struct Scsi_Host * shost ;
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
shost = iscsi_session_to_shost ( session ) ;
scsi_host_put ( shost ) ;
kfree ( session ) ;
}
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
static int iscsi_is_session_dev ( const struct device * dev )
{
return dev - > release = = iscsi_session_release ;
}
2005-08-05 06:33:07 +04:00
2006-04-07 06:13:39 +04:00
static int iscsi_user_scan ( struct Scsi_Host * shost , uint channel ,
uint id , uint lun )
{
struct iscsi_host * ihost = shost - > shost_data ;
struct iscsi_cls_session * session ;
mutex_lock ( & ihost - > mutex ) ;
list_for_each_entry ( session , & ihost - > sessions , host_list ) {
2006-06-28 21:00:29 +04:00
if ( ( channel = = SCAN_WILD_CARD | | channel = = 0 ) & &
2006-04-07 06:13:39 +04:00
( id = = SCAN_WILD_CARD | | id = = session - > target_id ) )
2006-06-28 21:00:29 +04:00
scsi_scan_target ( & session - > dev , 0 ,
2006-04-07 06:13:39 +04:00
session - > target_id , lun , 1 ) ;
}
mutex_unlock ( & ihost - > mutex ) ;
return 0 ;
}
static void session_recovery_timedout ( void * data )
{
struct iscsi_cls_session * session = data ;
2006-05-03 04:46:43 +04:00
dev_printk ( KERN_INFO , & session - > dev , " iscsi: session recovery timed "
" out after %d secs \n " , session - > recovery_tmo ) ;
2006-04-07 06:13:39 +04:00
if ( session - > transport - > session_recovery_timedout )
session - > transport - > session_recovery_timedout ( session ) ;
scsi_target_unblock ( & session - > dev ) ;
}
void iscsi_unblock_session ( struct iscsi_cls_session * session )
{
if ( ! cancel_delayed_work ( & session - > recovery_work ) )
flush_scheduled_work ( ) ;
scsi_target_unblock ( & session - > dev ) ;
}
EXPORT_SYMBOL_GPL ( iscsi_unblock_session ) ;
void iscsi_block_session ( struct iscsi_cls_session * session )
{
scsi_target_block ( & session - > dev ) ;
schedule_delayed_work ( & session - > recovery_work ,
session - > recovery_tmo * HZ ) ;
}
EXPORT_SYMBOL_GPL ( iscsi_block_session ) ;
2006-01-14 03:05:50 +03:00
struct iscsi_cls_session *
2006-06-28 21:00:30 +04:00
iscsi_alloc_session ( struct Scsi_Host * shost ,
struct iscsi_transport * transport )
2006-01-14 03:05:50 +03:00
{
struct iscsi_cls_session * session ;
2006-04-07 06:13:33 +04:00
session = kzalloc ( sizeof ( * session ) + transport - > sessiondata_size ,
GFP_KERNEL ) ;
2006-01-14 03:05:50 +03:00
if ( ! session )
2006-06-28 21:00:27 +04:00
return NULL ;
2006-01-14 03:05:50 +03:00
session - > transport = transport ;
2006-04-07 06:13:39 +04:00
session - > recovery_tmo = 120 ;
INIT_WORK ( & session - > recovery_work , session_recovery_timedout , session ) ;
INIT_LIST_HEAD ( & session - > host_list ) ;
INIT_LIST_HEAD ( & session - > sess_list ) ;
2006-01-14 03:05:50 +03:00
2006-06-28 21:00:31 +04:00
/* this is released in the dev's release function */
scsi_host_get ( shost ) ;
2006-06-28 21:00:30 +04:00
session - > dev . parent = & shost - > shost_gendev ;
session - > dev . release = iscsi_session_release ;
device_initialize ( & session - > dev ) ;
2006-04-07 06:13:33 +04:00
if ( transport - > sessiondata_size )
session - > dd_data = & session [ 1 ] ;
2006-06-28 21:00:30 +04:00
return session ;
}
EXPORT_SYMBOL_GPL ( iscsi_alloc_session ) ;
2006-06-28 21:00:31 +04:00
int iscsi_add_session ( struct iscsi_cls_session * session , unsigned int target_id )
2006-06-28 21:00:30 +04:00
{
struct Scsi_Host * shost = iscsi_session_to_shost ( session ) ;
struct iscsi_host * ihost ;
int err ;
2006-04-07 06:13:33 +04:00
2006-04-07 06:13:39 +04:00
ihost = shost - > shost_data ;
2006-04-07 06:13:33 +04:00
session - > sid = iscsi_session_nr + + ;
2006-06-28 21:00:31 +04:00
session - > target_id = target_id ;
2006-04-07 06:13:39 +04:00
2006-04-07 06:13:33 +04:00
snprintf ( session - > dev . bus_id , BUS_ID_SIZE , " session%u " ,
session - > sid ) ;
2006-06-28 21:00:30 +04:00
err = device_add ( & session - > dev ) ;
2006-01-14 03:05:50 +03:00
if ( err ) {
dev_printk ( KERN_ERR , & session - > dev , " iscsi: could not "
" register session's dev \n " ) ;
2006-06-28 21:00:30 +04:00
goto release_host ;
2006-01-14 03:05:50 +03:00
}
transport_register_device ( & session - > dev ) ;
2006-04-07 06:13:39 +04:00
mutex_lock ( & ihost - > mutex ) ;
list_add ( & session - > host_list , & ihost - > sessions ) ;
mutex_unlock ( & ihost - > mutex ) ;
2006-06-28 21:00:30 +04:00
return 0 ;
2006-04-07 06:13:39 +04:00
2006-06-28 21:00:30 +04:00
release_host :
scsi_host_put ( shost ) ;
return err ;
2006-01-14 03:05:50 +03:00
}
2006-06-28 21:00:30 +04:00
EXPORT_SYMBOL_GPL ( iscsi_add_session ) ;
2006-01-14 03:05:50 +03:00
/**
2006-06-28 21:00:30 +04:00
* iscsi_create_session - create iscsi class session
* @ shost : scsi host
* @ transport : iscsi transport
2006-01-14 03:05:50 +03:00
*
2006-06-28 21:00:30 +04:00
* This can be called from a LLD or iscsi_transport .
2006-01-14 03:05:50 +03:00
* */
2006-06-28 21:00:30 +04:00
struct iscsi_cls_session *
iscsi_create_session ( struct Scsi_Host * shost ,
2006-06-28 21:00:31 +04:00
struct iscsi_transport * transport ,
unsigned int target_id )
2006-06-28 21:00:30 +04:00
{
struct iscsi_cls_session * session ;
session = iscsi_alloc_session ( shost , transport ) ;
if ( ! session )
return NULL ;
2006-06-28 21:00:31 +04:00
if ( iscsi_add_session ( session , target_id ) ) {
2006-06-28 21:00:30 +04:00
iscsi_free_session ( session ) ;
return NULL ;
}
return session ;
}
EXPORT_SYMBOL_GPL ( iscsi_create_session ) ;
void iscsi_remove_session ( struct iscsi_cls_session * session )
2006-01-14 03:05:50 +03:00
{
2006-04-07 06:13:39 +04:00
struct Scsi_Host * shost = iscsi_session_to_shost ( session ) ;
struct iscsi_host * ihost = shost - > shost_data ;
if ( ! cancel_delayed_work ( & session - > recovery_work ) )
flush_scheduled_work ( ) ;
mutex_lock ( & ihost - > mutex ) ;
list_del ( & session - > host_list ) ;
mutex_unlock ( & ihost - > mutex ) ;
2006-06-28 21:00:30 +04:00
scsi_remove_target ( & session - > dev ) ;
2006-01-14 03:05:50 +03:00
transport_unregister_device ( & session - > dev ) ;
2006-06-28 21:00:30 +04:00
device_del ( & session - > dev ) ;
}
EXPORT_SYMBOL_GPL ( iscsi_remove_session ) ;
void iscsi_free_session ( struct iscsi_cls_session * session )
{
put_device ( & session - > dev ) ;
2006-01-14 03:05:50 +03:00
}
2006-06-28 21:00:30 +04:00
EXPORT_SYMBOL_GPL ( iscsi_free_session ) ;
/**
* iscsi_destroy_session - destroy iscsi session
* @ session : iscsi_session
*
* Can be called by a LLD or iscsi_transport . There must not be
* any running connections .
* */
int iscsi_destroy_session ( struct iscsi_cls_session * session )
{
iscsi_remove_session ( session ) ;
iscsi_free_session ( session ) ;
return 0 ;
}
2006-01-14 03:05:50 +03:00
EXPORT_SYMBOL_GPL ( iscsi_destroy_session ) ;
2006-06-28 21:00:32 +04:00
static void mempool_zone_destroy ( struct mempool_zone * zp )
{
mempool_destroy ( zp - > pool ) ;
kfree ( zp ) ;
}
static void *
mempool_zone_alloc_skb ( gfp_t gfp_mask , void * pool_data )
{
struct mempool_zone * zone = pool_data ;
return alloc_skb ( zone - > size , gfp_mask ) ;
}
static void
mempool_zone_free_skb ( void * element , void * pool_data )
{
kfree_skb ( element ) ;
}
static struct mempool_zone *
mempool_zone_init ( unsigned max , unsigned size , unsigned hiwat )
{
struct mempool_zone * zp ;
zp = kzalloc ( sizeof ( * zp ) , GFP_KERNEL ) ;
if ( ! zp )
return NULL ;
zp - > size = size ;
zp - > hiwat = hiwat ;
INIT_LIST_HEAD ( & zp - > freequeue ) ;
spin_lock_init ( & zp - > freelock ) ;
atomic_set ( & zp - > allocated , 0 ) ;
zp - > pool = mempool_create ( max , mempool_zone_alloc_skb ,
mempool_zone_free_skb , zp ) ;
if ( ! zp - > pool ) {
kfree ( zp ) ;
return NULL ;
}
return zp ;
}
2006-01-14 03:05:50 +03:00
static void iscsi_conn_release ( struct device * dev )
{
struct iscsi_cls_conn * conn = iscsi_dev_to_conn ( dev ) ;
struct device * parent = conn - > dev . parent ;
2006-06-28 21:00:32 +04:00
mempool_zone_destroy ( conn - > z_pdu ) ;
mempool_zone_destroy ( conn - > z_error ) ;
2006-01-14 03:05:50 +03:00
kfree ( conn ) ;
put_device ( parent ) ;
}
static int iscsi_is_conn_dev ( const struct device * dev )
{
return dev - > release = = iscsi_conn_release ;
}
2006-06-28 21:00:32 +04:00
static int iscsi_create_event_pools ( struct iscsi_cls_conn * conn )
{
conn - > z_pdu = mempool_zone_init ( Z_MAX_PDU ,
NLMSG_SPACE ( sizeof ( struct iscsi_uevent ) +
sizeof ( struct iscsi_hdr ) +
DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH ) ,
Z_HIWAT_PDU ) ;
if ( ! conn - > z_pdu ) {
dev_printk ( KERN_ERR , & conn - > dev , " iscsi: can not allocate "
" pdu zone for new conn \n " ) ;
return - ENOMEM ;
}
conn - > z_error = mempool_zone_init ( Z_MAX_ERROR ,
NLMSG_SPACE ( sizeof ( struct iscsi_uevent ) ) ,
Z_HIWAT_ERROR ) ;
if ( ! conn - > z_error ) {
dev_printk ( KERN_ERR , & conn - > dev , " iscsi: can not allocate "
" error zone for new conn \n " ) ;
mempool_zone_destroy ( conn - > z_pdu ) ;
return - ENOMEM ;
}
return 0 ;
}
2006-01-14 03:05:50 +03:00
/**
* iscsi_create_conn - create iscsi class connection
* @ session : iscsi cls session
* @ cid : connection id
*
* This can be called from a LLD or iscsi_transport . The connection
* is child of the session so cid must be unique for all connections
* on the session .
2006-04-07 06:13:33 +04:00
*
* Since we do not support MCS , cid will normally be zero . In some cases
* for software iscsi we could be trying to preallocate a connection struct
* in which case there could be two connection structs and cid would be
* non - zero .
2006-01-14 03:05:50 +03:00
* */
struct iscsi_cls_conn *
iscsi_create_conn ( struct iscsi_cls_session * session , uint32_t cid )
{
struct iscsi_transport * transport = session - > transport ;
struct iscsi_cls_conn * conn ;
int err ;
conn = kzalloc ( sizeof ( * conn ) + transport - > conndata_size , GFP_KERNEL ) ;
if ( ! conn )
return NULL ;
if ( transport - > conndata_size )
conn - > dd_data = & conn [ 1 ] ;
INIT_LIST_HEAD ( & conn - > conn_list ) ;
conn - > transport = transport ;
2006-04-07 06:13:33 +04:00
conn - > cid = cid ;
2006-01-14 03:05:50 +03:00
2006-06-28 21:00:32 +04:00
if ( iscsi_create_event_pools ( conn ) )
goto free_conn ;
2006-01-14 03:05:50 +03:00
/* this is released in the dev's release function */
if ( ! get_device ( & session - > dev ) )
2006-06-28 21:00:32 +04:00
goto free_conn_pools ;
2006-04-07 06:13:33 +04:00
2006-01-14 03:05:50 +03:00
snprintf ( conn - > dev . bus_id , BUS_ID_SIZE , " connection%d:%u " ,
2006-04-07 06:13:33 +04:00
session - > sid , cid ) ;
2006-01-14 03:05:50 +03:00
conn - > dev . parent = & session - > dev ;
conn - > dev . release = iscsi_conn_release ;
err = device_register ( & conn - > dev ) ;
if ( err ) {
dev_printk ( KERN_ERR , & conn - > dev , " iscsi: could not register "
" connection's dev \n " ) ;
goto release_parent_ref ;
}
transport_register_device ( & conn - > dev ) ;
return conn ;
release_parent_ref :
put_device ( & session - > dev ) ;
2006-06-28 21:00:32 +04:00
free_conn_pools :
2006-01-14 03:05:50 +03:00
free_conn :
kfree ( conn ) ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( iscsi_create_conn ) ;
/**
* iscsi_destroy_conn - destroy iscsi class connection
* @ session : iscsi cls session
*
* This can be called from a LLD or iscsi_transport .
* */
int iscsi_destroy_conn ( struct iscsi_cls_conn * conn )
{
transport_unregister_device ( & conn - > dev ) ;
device_unregister ( & conn - > dev ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( iscsi_destroy_conn ) ;
/*
* iscsi interface functions
*/
2005-08-05 06:33:07 +04:00
static struct iscsi_internal *
iscsi_if_transport_lookup ( struct iscsi_transport * tt )
{
struct iscsi_internal * priv ;
unsigned long flags ;
spin_lock_irqsave ( & iscsi_transport_lock , flags ) ;
list_for_each_entry ( priv , & iscsi_transports , list ) {
if ( tt = = priv - > iscsi_transport ) {
spin_unlock_irqrestore ( & iscsi_transport_lock , flags ) ;
return priv ;
}
}
spin_unlock_irqrestore ( & iscsi_transport_lock , flags ) ;
return NULL ;
}
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
static inline struct list_head * skb_to_lh ( struct sk_buff * skb )
{
return ( struct list_head * ) & skb - > cb ;
}
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
static void
mempool_zone_complete ( struct mempool_zone * zone )
{
unsigned long flags ;
struct list_head * lh , * n ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
spin_lock_irqsave ( & zone - > freelock , flags ) ;
list_for_each_safe ( lh , n , & zone - > freequeue ) {
struct sk_buff * skb = ( struct sk_buff * ) ( ( char * ) lh -
offsetof ( struct sk_buff , cb ) ) ;
if ( ! skb_shared ( skb ) ) {
list_del ( skb_to_lh ( skb ) ) ;
mempool_free ( skb , zone - > pool ) ;
atomic_dec ( & zone - > allocated ) ;
}
}
spin_unlock_irqrestore ( & zone - > freelock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 06:33:07 +04:00
static struct sk_buff *
mempool_zone_get_skb ( struct mempool_zone * zone )
2005-04-17 02:20:36 +04:00
{
2005-08-05 06:33:07 +04:00
struct sk_buff * skb ;
skb = mempool_alloc ( zone - > pool , GFP_ATOMIC ) ;
if ( skb )
atomic_inc ( & zone - > allocated ) ;
return skb ;
}
2005-04-17 02:20:36 +04:00
2006-06-28 21:00:32 +04:00
static int
iscsi_broadcast_skb ( struct mempool_zone * zone , struct sk_buff * skb )
{
unsigned long flags ;
int rc ;
skb_get ( skb ) ;
rc = netlink_broadcast ( nls , skb , 0 , 1 , GFP_KERNEL ) ;
if ( rc < 0 ) {
mempool_free ( skb , zone - > pool ) ;
printk ( KERN_ERR " iscsi: can not broadcast skb (%d) \n " , rc ) ;
return rc ;
}
spin_lock_irqsave ( & zone - > freelock , flags ) ;
INIT_LIST_HEAD ( skb_to_lh ( skb ) ) ;
list_add ( skb_to_lh ( skb ) , & zone - > freequeue ) ;
spin_unlock_irqrestore ( & zone - > freelock , flags ) ;
return 0 ;
}
2005-08-05 06:33:07 +04:00
static int
2006-05-19 05:31:39 +04:00
iscsi_unicast_skb ( struct mempool_zone * zone , struct sk_buff * skb , int pid )
2005-08-05 06:33:07 +04:00
{
unsigned long flags ;
int rc ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
skb_get ( skb ) ;
2006-05-19 05:31:39 +04:00
rc = netlink_unicast ( nls , skb , pid , MSG_DONTWAIT ) ;
2005-08-05 06:33:07 +04:00
if ( rc < 0 ) {
mempool_free ( skb , zone - > pool ) ;
printk ( KERN_ERR " iscsi: can not unicast skb (%d) \n " , rc ) ;
return rc ;
}
spin_lock_irqsave ( & zone - > freelock , flags ) ;
2006-04-07 06:13:39 +04:00
INIT_LIST_HEAD ( skb_to_lh ( skb ) ) ;
2005-08-05 06:33:07 +04:00
list_add ( skb_to_lh ( skb ) , & zone - > freequeue ) ;
spin_unlock_irqrestore ( & zone - > freelock , flags ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-02-02 06:06:49 +03:00
int iscsi_recv_pdu ( struct iscsi_cls_conn * conn , struct iscsi_hdr * hdr ,
2005-08-05 06:33:07 +04:00
char * data , uint32_t data_size )
2005-04-17 02:20:36 +04:00
{
2005-08-05 06:33:07 +04:00
struct nlmsghdr * nlh ;
struct sk_buff * skb ;
struct iscsi_uevent * ev ;
char * pdu ;
2006-05-19 05:31:39 +04:00
struct iscsi_internal * priv ;
2005-08-05 06:33:07 +04:00
int len = NLMSG_SPACE ( sizeof ( * ev ) + sizeof ( struct iscsi_hdr ) +
data_size ) ;
2006-05-19 05:31:39 +04:00
priv = iscsi_if_transport_lookup ( conn - > transport ) ;
if ( ! priv )
return - EINVAL ;
2006-01-14 03:05:50 +03:00
mempool_zone_complete ( conn - > z_pdu ) ;
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
skb = mempool_zone_get_skb ( conn - > z_pdu ) ;
2005-08-05 06:33:07 +04:00
if ( ! skb ) {
2006-02-02 06:06:49 +03:00
iscsi_conn_error ( conn , ISCSI_ERR_CONN_FAILED ) ;
2006-01-14 03:05:50 +03:00
dev_printk ( KERN_ERR , & conn - > dev , " iscsi: can not deliver "
" control PDU: OOM \n " ) ;
2005-08-05 06:33:07 +04:00
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
2006-05-19 05:31:39 +04:00
nlh = __nlmsg_put ( skb , priv - > daemon_pid , 0 , 0 , ( len - sizeof ( * nlh ) ) , 0 ) ;
2005-08-05 06:33:07 +04:00
ev = NLMSG_DATA ( nlh ) ;
memset ( ev , 0 , sizeof ( * ev ) ) ;
ev - > transport_handle = iscsi_handle ( conn - > transport ) ;
ev - > type = ISCSI_KEVENT_RECV_PDU ;
2006-01-14 03:05:50 +03:00
if ( atomic_read ( & conn - > z_pdu - > allocated ) > = conn - > z_pdu - > hiwat )
2005-08-05 06:33:07 +04:00
ev - > iferror = - ENOMEM ;
2006-04-07 06:13:33 +04:00
ev - > r . recv_req . cid = conn - > cid ;
ev - > r . recv_req . sid = iscsi_conn_get_sid ( conn ) ;
2005-08-05 06:33:07 +04:00
pdu = ( char * ) ev + sizeof ( * ev ) ;
memcpy ( pdu , hdr , sizeof ( struct iscsi_hdr ) ) ;
memcpy ( pdu + sizeof ( struct iscsi_hdr ) , data , data_size ) ;
2005-04-17 02:20:36 +04:00
2006-05-19 05:31:39 +04:00
return iscsi_unicast_skb ( conn - > z_pdu , skb , priv - > daemon_pid ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 06:33:07 +04:00
EXPORT_SYMBOL_GPL ( iscsi_recv_pdu ) ;
2005-04-17 02:20:36 +04:00
2006-02-02 06:06:49 +03:00
void iscsi_conn_error ( struct iscsi_cls_conn * conn , enum iscsi_err error )
2005-04-17 02:20:36 +04:00
{
2005-08-05 06:33:07 +04:00
struct nlmsghdr * nlh ;
struct sk_buff * skb ;
struct iscsi_uevent * ev ;
2006-05-19 05:31:39 +04:00
struct iscsi_internal * priv ;
2005-08-05 06:33:07 +04:00
int len = NLMSG_SPACE ( sizeof ( * ev ) ) ;
2006-05-19 05:31:39 +04:00
priv = iscsi_if_transport_lookup ( conn - > transport ) ;
if ( ! priv )
return ;
2006-01-14 03:05:50 +03:00
mempool_zone_complete ( conn - > z_error ) ;
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
skb = mempool_zone_get_skb ( conn - > z_error ) ;
2005-08-05 06:33:07 +04:00
if ( ! skb ) {
2006-01-14 03:05:50 +03:00
dev_printk ( KERN_ERR , & conn - > dev , " iscsi: gracefully ignored "
" conn error (%d) \n " , error ) ;
2005-08-05 06:33:07 +04:00
return ;
}
2006-05-19 05:31:39 +04:00
nlh = __nlmsg_put ( skb , priv - > daemon_pid , 0 , 0 , ( len - sizeof ( * nlh ) ) , 0 ) ;
2005-08-05 06:33:07 +04:00
ev = NLMSG_DATA ( nlh ) ;
ev - > transport_handle = iscsi_handle ( conn - > transport ) ;
ev - > type = ISCSI_KEVENT_CONN_ERROR ;
2006-01-14 03:05:50 +03:00
if ( atomic_read ( & conn - > z_error - > allocated ) > = conn - > z_error - > hiwat )
2005-08-05 06:33:07 +04:00
ev - > iferror = - ENOMEM ;
ev - > r . connerror . error = error ;
2006-04-07 06:13:33 +04:00
ev - > r . connerror . cid = conn - > cid ;
ev - > r . connerror . sid = iscsi_conn_get_sid ( conn ) ;
2005-04-17 02:20:36 +04:00
2006-06-28 21:00:32 +04:00
iscsi_broadcast_skb ( conn - > z_error , skb ) ;
2005-04-17 02:20:36 +04:00
2006-01-14 03:05:50 +03:00
dev_printk ( KERN_INFO , & conn - > dev , " iscsi: detected conn error (%d) \n " ,
error ) ;
2005-08-05 06:33:07 +04:00
}
EXPORT_SYMBOL_GPL ( iscsi_conn_error ) ;
static int
iscsi_if_send_reply ( int pid , int seq , int type , int done , int multi ,
void * payload , int size )
{
struct sk_buff * skb ;
struct nlmsghdr * nlh ;
int len = NLMSG_SPACE ( size ) ;
int flags = multi ? NLM_F_MULTI : 0 ;
int t = done ? NLMSG_DONE : type ;
2006-01-14 03:05:50 +03:00
mempool_zone_complete ( z_reply ) ;
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
skb = mempool_zone_get_skb ( z_reply ) ;
2005-08-05 06:33:07 +04:00
/*
* FIXME :
* user is supposed to react on iferror = = - ENOMEM ;
* see iscsi_if_rx ( ) .
*/
BUG_ON ( ! skb ) ;
nlh = __nlmsg_put ( skb , pid , seq , t , ( len - sizeof ( * nlh ) ) , 0 ) ;
nlh - > nlmsg_flags = flags ;
memcpy ( NLMSG_DATA ( nlh ) , payload , size ) ;
2006-05-19 05:31:39 +04:00
return iscsi_unicast_skb ( z_reply , skb , pid ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-14 03:05:50 +03:00
static int
2006-02-02 06:06:56 +03:00
iscsi_if_get_stats ( struct iscsi_transport * transport , struct nlmsghdr * nlh )
2006-01-14 03:05:50 +03:00
{
struct iscsi_uevent * ev = NLMSG_DATA ( nlh ) ;
struct iscsi_stats * stats ;
struct sk_buff * skbstat ;
struct iscsi_cls_conn * conn ;
struct nlmsghdr * nlhstat ;
struct iscsi_uevent * evstat ;
2006-05-19 05:31:39 +04:00
struct iscsi_internal * priv ;
2006-01-14 03:05:50 +03:00
int len = NLMSG_SPACE ( sizeof ( * ev ) +
sizeof ( struct iscsi_stats ) +
sizeof ( struct iscsi_stats_custom ) *
ISCSI_STATS_CUSTOM_MAX ) ;
int err = 0 ;
2005-08-05 06:33:07 +04:00
2006-05-19 05:31:39 +04:00
priv = iscsi_if_transport_lookup ( transport ) ;
if ( ! priv )
return - EINVAL ;
2006-04-07 06:13:33 +04:00
conn = iscsi_conn_lookup ( ev - > u . get_stats . sid , ev - > u . get_stats . cid ) ;
2006-01-14 03:05:50 +03:00
if ( ! conn )
return - EEXIST ;
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
do {
int actual_size ;
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
mempool_zone_complete ( conn - > z_pdu ) ;
2005-08-05 06:33:07 +04:00
2006-01-14 03:05:50 +03:00
skbstat = mempool_zone_get_skb ( conn - > z_pdu ) ;
if ( ! skbstat ) {
dev_printk ( KERN_ERR , & conn - > dev , " iscsi: can not "
" deliver stats: OOM \n " ) ;
return - ENOMEM ;
}
2006-05-19 05:31:39 +04:00
nlhstat = __nlmsg_put ( skbstat , priv - > daemon_pid , 0 , 0 ,
2006-01-14 03:05:50 +03:00
( len - sizeof ( * nlhstat ) ) , 0 ) ;
evstat = NLMSG_DATA ( nlhstat ) ;
memset ( evstat , 0 , sizeof ( * evstat ) ) ;
evstat - > transport_handle = iscsi_handle ( conn - > transport ) ;
evstat - > type = nlh - > nlmsg_type ;
if ( atomic_read ( & conn - > z_pdu - > allocated ) > = conn - > z_pdu - > hiwat )
evstat - > iferror = - ENOMEM ;
2006-04-07 06:13:33 +04:00
evstat - > u . get_stats . cid =
ev - > u . get_stats . cid ;
evstat - > u . get_stats . sid =
ev - > u . get_stats . sid ;
2006-01-14 03:05:50 +03:00
stats = ( struct iscsi_stats * )
( ( char * ) evstat + sizeof ( * evstat ) ) ;
memset ( stats , 0 , sizeof ( * stats ) ) ;
2006-02-02 06:06:49 +03:00
transport - > get_stats ( conn , stats ) ;
2006-01-14 03:05:50 +03:00
actual_size = NLMSG_SPACE ( sizeof ( struct iscsi_uevent ) +
sizeof ( struct iscsi_stats ) +
sizeof ( struct iscsi_stats_custom ) *
stats - > custom_length ) ;
actual_size - = sizeof ( * nlhstat ) ;
actual_size = NLMSG_LENGTH ( actual_size ) ;
2006-02-02 06:06:56 +03:00
skb_trim ( skbstat , NLMSG_ALIGN ( actual_size ) ) ;
2006-01-14 03:05:50 +03:00
nlhstat - > nlmsg_len = actual_size ;
2006-05-19 05:31:39 +04:00
err = iscsi_unicast_skb ( conn - > z_pdu , skbstat , priv - > daemon_pid ) ;
2006-01-14 03:05:50 +03:00
} while ( err < 0 & & err ! = - ECONNREFUSED ) ;
return err ;
2005-08-05 06:33:07 +04:00
}
2006-06-28 21:00:32 +04:00
/**
* iscsi_if_destroy_session_done - send session destr . completion event
* @ conn : last connection for session
*
* This is called by HW iscsi LLDs to notify userpsace that its HW has
* removed a session .
* */
int iscsi_if_destroy_session_done ( struct iscsi_cls_conn * conn )
{
struct iscsi_internal * priv ;
struct iscsi_cls_session * session ;
struct Scsi_Host * shost ;
struct iscsi_uevent * ev ;
struct sk_buff * skb ;
struct nlmsghdr * nlh ;
unsigned long flags ;
int rc , len = NLMSG_SPACE ( sizeof ( * ev ) ) ;
priv = iscsi_if_transport_lookup ( conn - > transport ) ;
if ( ! priv )
return - EINVAL ;
session = iscsi_dev_to_session ( conn - > dev . parent ) ;
shost = iscsi_session_to_shost ( session ) ;
mempool_zone_complete ( conn - > z_pdu ) ;
skb = mempool_zone_get_skb ( conn - > z_pdu ) ;
if ( ! skb ) {
dev_printk ( KERN_ERR , & conn - > dev , " Cannot notify userspace of "
" session creation event \n " ) ;
return - ENOMEM ;
}
nlh = __nlmsg_put ( skb , priv - > daemon_pid , 0 , 0 , ( len - sizeof ( * nlh ) ) , 0 ) ;
ev = NLMSG_DATA ( nlh ) ;
ev - > transport_handle = iscsi_handle ( conn - > transport ) ;
ev - > type = ISCSI_KEVENT_DESTROY_SESSION ;
ev - > r . d_session . host_no = shost - > host_no ;
ev - > r . d_session . sid = session - > sid ;
/*
* this will occur if the daemon is not up , so we just warn
* the user and when the daemon is restarted it will handle it
*/
rc = iscsi_broadcast_skb ( conn - > z_pdu , skb ) ;
if ( rc < 0 )
dev_printk ( KERN_ERR , & conn - > dev , " Cannot notify userspace of "
" session destruction event. Check iscsi daemon \n " ) ;
spin_lock_irqsave ( & sesslock , flags ) ;
list_del ( & session - > sess_list ) ;
spin_unlock_irqrestore ( & sesslock , flags ) ;
spin_lock_irqsave ( & connlock , flags ) ;
conn - > active = 0 ;
list_del ( & conn - > conn_list ) ;
spin_unlock_irqrestore ( & connlock , flags ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( iscsi_if_destroy_session_done ) ;
/**
* iscsi_if_create_session_done - send session creation completion event
* @ conn : leading connection for session
*
* This is called by HW iscsi LLDs to notify userpsace that its HW has
* created a session or a existing session is back in the logged in state .
* */
int iscsi_if_create_session_done ( struct iscsi_cls_conn * conn )
{
struct iscsi_internal * priv ;
struct iscsi_cls_session * session ;
struct Scsi_Host * shost ;
struct iscsi_uevent * ev ;
struct sk_buff * skb ;
struct nlmsghdr * nlh ;
unsigned long flags ;
int rc , len = NLMSG_SPACE ( sizeof ( * ev ) ) ;
priv = iscsi_if_transport_lookup ( conn - > transport ) ;
if ( ! priv )
return - EINVAL ;
session = iscsi_dev_to_session ( conn - > dev . parent ) ;
shost = iscsi_session_to_shost ( session ) ;
mempool_zone_complete ( conn - > z_pdu ) ;
skb = mempool_zone_get_skb ( conn - > z_pdu ) ;
if ( ! skb ) {
dev_printk ( KERN_ERR , & conn - > dev , " Cannot notify userspace of "
" session creation event \n " ) ;
return - ENOMEM ;
}
nlh = __nlmsg_put ( skb , priv - > daemon_pid , 0 , 0 , ( len - sizeof ( * nlh ) ) , 0 ) ;
ev = NLMSG_DATA ( nlh ) ;
ev - > transport_handle = iscsi_handle ( conn - > transport ) ;
ev - > type = ISCSI_UEVENT_CREATE_SESSION ;
ev - > r . c_session_ret . host_no = shost - > host_no ;
ev - > r . c_session_ret . sid = session - > sid ;
/*
* this will occur if the daemon is not up , so we just warn
* the user and when the daemon is restarted it will handle it
*/
rc = iscsi_broadcast_skb ( conn - > z_pdu , skb ) ;
if ( rc < 0 )
dev_printk ( KERN_ERR , & conn - > dev , " Cannot notify userspace of "
" session creation event. Check iscsi daemon \n " ) ;
spin_lock_irqsave ( & sesslock , flags ) ;
list_add ( & session - > sess_list , & sesslist ) ;
spin_unlock_irqrestore ( & sesslock , flags ) ;
spin_lock_irqsave ( & connlock , flags ) ;
list_add ( & conn - > conn_list , & connlist ) ;
conn - > active = 1 ;
spin_unlock_irqrestore ( & connlock , flags ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( iscsi_if_create_session_done ) ;
2005-08-05 06:33:07 +04:00
static int
iscsi_if_create_session ( struct iscsi_internal * priv , struct iscsi_uevent * ev )
{
struct iscsi_transport * transport = priv - > iscsi_transport ;
2006-02-02 06:06:49 +03:00
struct iscsi_cls_session * session ;
2006-04-07 06:13:41 +04:00
unsigned long flags ;
2006-04-07 06:13:33 +04:00
uint32_t hostno ;
2005-08-05 06:33:07 +04:00
2006-04-07 06:13:41 +04:00
session = transport - > create_session ( transport , & priv - > t ,
2006-02-02 06:06:49 +03:00
ev - > u . c_session . initial_cmdsn ,
2006-04-07 06:13:33 +04:00
& hostno ) ;
2006-02-02 06:06:49 +03:00
if ( ! session )
2006-01-14 03:05:50 +03:00
return - ENOMEM ;
2005-08-05 06:33:07 +04:00
2006-04-07 06:13:41 +04:00
spin_lock_irqsave ( & sesslock , flags ) ;
list_add ( & session - > sess_list , & sesslist ) ;
spin_unlock_irqrestore ( & sesslock , flags ) ;
2006-04-07 06:13:33 +04:00
ev - > r . c_session_ret . host_no = hostno ;
ev - > r . c_session_ret . sid = session - > sid ;
2005-08-05 06:33:07 +04:00
return 0 ;
}
static int
2006-02-02 06:06:49 +03:00
iscsi_if_create_conn ( struct iscsi_transport * transport , struct iscsi_uevent * ev )
2005-08-05 06:33:07 +04:00
{
2006-01-14 03:05:50 +03:00
struct iscsi_cls_conn * conn ;
2006-02-02 06:06:49 +03:00
struct iscsi_cls_session * session ;
2005-08-05 06:33:07 +04:00
unsigned long flags ;
2006-01-14 03:05:50 +03:00
2006-04-07 06:13:33 +04:00
session = iscsi_session_lookup ( ev - > u . c_conn . sid ) ;
if ( ! session ) {
printk ( KERN_ERR " iscsi: invalid session %d \n " ,
ev - > u . c_conn . sid ) ;
2006-01-14 03:05:50 +03:00
return - EINVAL ;
2006-04-07 06:13:33 +04:00
}
2005-08-05 06:33:07 +04:00
2006-02-02 06:06:49 +03:00
conn = transport - > create_conn ( session , ev - > u . c_conn . cid ) ;
2006-04-07 06:13:33 +04:00
if ( ! conn ) {
printk ( KERN_ERR " iscsi: couldn't create a new "
" connection for session %d \n " ,
session - > sid ) ;
2006-02-02 06:06:49 +03:00
return - ENOMEM ;
2006-04-07 06:13:33 +04:00
}
2005-08-05 06:33:07 +04:00
2006-04-07 06:13:33 +04:00
ev - > r . c_conn_ret . sid = session - > sid ;
ev - > r . c_conn_ret . cid = conn - > cid ;
2005-08-05 06:33:07 +04:00
spin_lock_irqsave ( & connlock , flags ) ;
list_add ( & conn - > conn_list , & connlist ) ;
conn - > active = 1 ;
spin_unlock_irqrestore ( & connlock , flags ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 06:33:07 +04:00
static int
iscsi_if_destroy_conn ( struct iscsi_transport * transport , struct iscsi_uevent * ev )
{
unsigned long flags ;
2006-01-14 03:05:50 +03:00
struct iscsi_cls_conn * conn ;
2005-08-05 06:33:07 +04:00
2006-04-07 06:13:33 +04:00
conn = iscsi_conn_lookup ( ev - > u . d_conn . sid , ev - > u . d_conn . cid ) ;
2005-08-05 06:33:07 +04:00
if ( ! conn )
2006-01-14 03:05:50 +03:00
return - EINVAL ;
2005-08-05 06:33:07 +04:00
spin_lock_irqsave ( & connlock , flags ) ;
conn - > active = 0 ;
list_del ( & conn - > conn_list ) ;
spin_unlock_irqrestore ( & connlock , flags ) ;
2006-01-14 03:05:50 +03:00
if ( transport - > destroy_conn )
transport - > destroy_conn ( conn ) ;
return 0 ;
2005-08-05 06:33:07 +04:00
}
2006-04-07 06:13:36 +04:00
static int
iscsi_set_param ( struct iscsi_transport * transport , struct iscsi_uevent * ev )
{
char * data = ( char * ) ev + sizeof ( * ev ) ;
struct iscsi_cls_conn * conn ;
struct iscsi_cls_session * session ;
2006-06-28 21:00:23 +04:00
int err = 0 , value = 0 ;
2006-04-07 06:13:36 +04:00
session = iscsi_session_lookup ( ev - > u . set_param . sid ) ;
conn = iscsi_conn_lookup ( ev - > u . set_param . sid , ev - > u . set_param . cid ) ;
if ( ! conn | | ! session )
return - EINVAL ;
switch ( ev - > u . set_param . param ) {
2006-04-07 06:13:39 +04:00
case ISCSI_PARAM_SESS_RECOVERY_TMO :
2006-06-28 21:00:23 +04:00
sscanf ( data , " %d " , & value ) ;
2006-04-07 06:13:39 +04:00
if ( value ! = 0 )
session - > recovery_tmo = value ;
break ;
2006-04-07 06:13:36 +04:00
default :
2006-06-28 21:00:23 +04:00
err = transport - > set_param ( conn , ev - > u . set_param . param ,
data , ev - > u . set_param . len ) ;
2006-04-07 06:13:36 +04:00
}
return err ;
}
2006-05-03 04:46:36 +04:00
static int
iscsi_if_transport_ep ( struct iscsi_transport * transport ,
struct iscsi_uevent * ev , int msg_type )
{
struct sockaddr * dst_addr ;
int rc = 0 ;
switch ( msg_type ) {
case ISCSI_UEVENT_TRANSPORT_EP_CONNECT :
if ( ! transport - > ep_connect )
return - EINVAL ;
dst_addr = ( struct sockaddr * ) ( ( char * ) ev + sizeof ( * ev ) ) ;
rc = transport - > ep_connect ( dst_addr ,
ev - > u . ep_connect . non_blocking ,
& ev - > r . ep_connect_ret . handle ) ;
break ;
case ISCSI_UEVENT_TRANSPORT_EP_POLL :
if ( ! transport - > ep_poll )
return - EINVAL ;
ev - > r . retcode = transport - > ep_poll ( ev - > u . ep_poll . ep_handle ,
ev - > u . ep_poll . timeout_ms ) ;
break ;
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT :
if ( ! transport - > ep_disconnect )
return - EINVAL ;
transport - > ep_disconnect ( ev - > u . ep_disconnect . ep_handle ) ;
break ;
}
return rc ;
}
2006-06-28 21:00:22 +04:00
static int
iscsi_tgt_dscvr ( struct iscsi_transport * transport ,
struct iscsi_uevent * ev )
{
struct sockaddr * dst_addr ;
if ( ! transport - > tgt_dscvr )
return - EINVAL ;
dst_addr = ( struct sockaddr * ) ( ( char * ) ev + sizeof ( * ev ) ) ;
return transport - > tgt_dscvr ( ev - > u . tgt_dscvr . type ,
ev - > u . tgt_dscvr . host_no ,
ev - > u . tgt_dscvr . enable , dst_addr ) ;
}
2005-08-05 06:33:07 +04:00
static int
iscsi_if_recv_msg ( struct sk_buff * skb , struct nlmsghdr * nlh )
{
int err = 0 ;
struct iscsi_uevent * ev = NLMSG_DATA ( nlh ) ;
struct iscsi_transport * transport = NULL ;
struct iscsi_internal * priv ;
2006-02-02 06:06:49 +03:00
struct iscsi_cls_session * session ;
struct iscsi_cls_conn * conn ;
2006-04-07 06:13:41 +04:00
unsigned long flags ;
2005-08-05 06:33:07 +04:00
priv = iscsi_if_transport_lookup ( iscsi_ptr ( ev - > transport_handle ) ) ;
if ( ! priv )
return - EINVAL ;
transport = priv - > iscsi_transport ;
2006-02-02 06:06:49 +03:00
if ( ! try_module_get ( transport - > owner ) )
return - EINVAL ;
2006-05-19 05:31:39 +04:00
priv - > daemon_pid = NETLINK_CREDS ( skb ) - > pid ;
2005-08-05 06:33:07 +04:00
switch ( nlh - > nlmsg_type ) {
case ISCSI_UEVENT_CREATE_SESSION :
err = iscsi_if_create_session ( priv , ev ) ;
break ;
case ISCSI_UEVENT_DESTROY_SESSION :
2006-04-07 06:13:33 +04:00
session = iscsi_session_lookup ( ev - > u . d_session . sid ) ;
2006-04-07 06:13:41 +04:00
if ( session ) {
spin_lock_irqsave ( & sesslock , flags ) ;
list_del ( & session - > sess_list ) ;
spin_unlock_irqrestore ( & sesslock , flags ) ;
2006-02-02 06:06:49 +03:00
transport - > destroy_session ( session ) ;
2006-04-07 06:13:41 +04:00
} else
2006-02-02 06:06:49 +03:00
err = - EINVAL ;
2005-08-05 06:33:07 +04:00
break ;
case ISCSI_UEVENT_CREATE_CONN :
err = iscsi_if_create_conn ( transport , ev ) ;
break ;
case ISCSI_UEVENT_DESTROY_CONN :
err = iscsi_if_destroy_conn ( transport , ev ) ;
break ;
case ISCSI_UEVENT_BIND_CONN :
2006-04-07 06:13:33 +04:00
session = iscsi_session_lookup ( ev - > u . b_conn . sid ) ;
conn = iscsi_conn_lookup ( ev - > u . b_conn . sid , ev - > u . b_conn . cid ) ;
2006-02-02 06:06:49 +03:00
if ( session & & conn )
ev - > r . retcode = transport - > bind_conn ( session , conn ,
2006-05-03 04:46:36 +04:00
ev - > u . b_conn . transport_eph ,
2006-02-02 06:06:49 +03:00
ev - > u . b_conn . is_leading ) ;
else
err = - EINVAL ;
2005-08-05 06:33:07 +04:00
break ;
case ISCSI_UEVENT_SET_PARAM :
2006-04-07 06:13:36 +04:00
err = iscsi_set_param ( transport , ev ) ;
2005-08-05 06:33:07 +04:00
break ;
case ISCSI_UEVENT_START_CONN :
2006-04-07 06:13:33 +04:00
conn = iscsi_conn_lookup ( ev - > u . start_conn . sid , ev - > u . start_conn . cid ) ;
2006-02-02 06:06:49 +03:00
if ( conn )
ev - > r . retcode = transport - > start_conn ( conn ) ;
else
err = - EINVAL ;
2005-08-05 06:33:07 +04:00
break ;
case ISCSI_UEVENT_STOP_CONN :
2006-04-07 06:13:33 +04:00
conn = iscsi_conn_lookup ( ev - > u . stop_conn . sid , ev - > u . stop_conn . cid ) ;
2006-02-02 06:06:49 +03:00
if ( conn )
transport - > stop_conn ( conn , ev - > u . stop_conn . flag ) ;
else
err = - EINVAL ;
2005-08-05 06:33:07 +04:00
break ;
case ISCSI_UEVENT_SEND_PDU :
2006-04-07 06:13:33 +04:00
conn = iscsi_conn_lookup ( ev - > u . send_pdu . sid , ev - > u . send_pdu . cid ) ;
2006-02-02 06:06:49 +03:00
if ( conn )
ev - > r . retcode = transport - > send_pdu ( conn ,
( struct iscsi_hdr * ) ( ( char * ) ev + sizeof ( * ev ) ) ,
( char * ) ev + sizeof ( * ev ) + ev - > u . send_pdu . hdr_size ,
ev - > u . send_pdu . data_size ) ;
else
err = - EINVAL ;
2005-08-05 06:33:07 +04:00
break ;
case ISCSI_UEVENT_GET_STATS :
2006-02-02 06:06:56 +03:00
err = iscsi_if_get_stats ( transport , nlh ) ;
2005-08-05 06:33:07 +04:00
break ;
2006-05-03 04:46:36 +04:00
case ISCSI_UEVENT_TRANSPORT_EP_CONNECT :
case ISCSI_UEVENT_TRANSPORT_EP_POLL :
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT :
err = iscsi_if_transport_ep ( transport , ev , nlh - > nlmsg_type ) ;
break ;
2006-06-28 21:00:22 +04:00
case ISCSI_UEVENT_TGT_DSCVR :
err = iscsi_tgt_dscvr ( transport , ev ) ;
break ;
2005-08-05 06:33:07 +04:00
default :
err = - EINVAL ;
break ;
}
2006-02-02 06:06:49 +03:00
module_put ( transport - > owner ) ;
2005-08-05 06:33:07 +04:00
return err ;
}
2006-04-07 06:13:33 +04:00
/*
* Get message from skb ( based on rtnetlink_rcv_skb ) . Each message is
* processed by iscsi_if_recv_msg . Malformed skbs with wrong lengths or
* invalid creds are discarded silently .
*/
2005-08-05 06:33:07 +04:00
static void
iscsi_if_rx ( struct sock * sk , int len )
{
struct sk_buff * skb ;
2005-04-17 02:20:36 +04:00
2006-01-11 15:16:10 +03:00
mutex_lock ( & rx_queue_mutex ) ;
2005-08-05 06:33:07 +04:00
while ( ( skb = skb_dequeue ( & sk - > sk_receive_queue ) ) ! = NULL ) {
2006-02-02 06:07:01 +03:00
if ( NETLINK_CREDS ( skb ) - > uid ) {
skb_pull ( skb , skb - > len ) ;
goto free_skb ;
}
2005-08-05 06:33:07 +04:00
while ( skb - > len > = NLMSG_SPACE ( 0 ) ) {
int err ;
uint32_t rlen ;
struct nlmsghdr * nlh ;
struct iscsi_uevent * ev ;
nlh = ( struct nlmsghdr * ) skb - > data ;
if ( nlh - > nlmsg_len < sizeof ( * nlh ) | |
skb - > len < nlh - > nlmsg_len ) {
break ;
}
2006-02-02 06:07:01 +03:00
2005-08-05 06:33:07 +04:00
ev = NLMSG_DATA ( nlh ) ;
rlen = NLMSG_ALIGN ( nlh - > nlmsg_len ) ;
if ( rlen > skb - > len )
rlen = skb - > len ;
2006-02-02 06:07:01 +03:00
2005-08-05 06:33:07 +04:00
err = iscsi_if_recv_msg ( skb , nlh ) ;
if ( err ) {
ev - > type = ISCSI_KEVENT_IF_ERROR ;
ev - > iferror = err ;
}
do {
/*
* special case for GET_STATS :
* on success - sending reply and stats from
* inside of if_recv_msg ( ) ,
* on error - fall through .
*/
if ( ev - > type = = ISCSI_UEVENT_GET_STATS & & ! err )
break ;
err = iscsi_if_send_reply (
NETLINK_CREDS ( skb ) - > pid , nlh - > nlmsg_seq ,
nlh - > nlmsg_type , 0 , 0 , ev , sizeof ( * ev ) ) ;
2006-01-14 03:05:50 +03:00
if ( atomic_read ( & z_reply - > allocated ) > =
z_reply - > hiwat )
2005-08-05 06:33:07 +04:00
ev - > iferror = - ENOMEM ;
} while ( err < 0 & & err ! = - ECONNREFUSED ) ;
skb_pull ( skb , rlen ) ;
}
2006-02-02 06:07:01 +03:00
free_skb :
2005-08-05 06:33:07 +04:00
kfree_skb ( skb ) ;
}
2006-01-11 15:16:10 +03:00
mutex_unlock ( & rx_queue_mutex ) ;
2005-08-05 06:33:07 +04:00
}
2005-04-17 02:20:36 +04:00
2006-01-14 03:05:50 +03:00
# define iscsi_cdev_to_conn(_cdev) \
iscsi_dev_to_conn ( _cdev - > dev )
2006-04-07 06:13:36 +04:00
# define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store) \
struct class_device_attribute class_device_attr_ # # _prefix # # _ # # _name = \
__ATTR ( _name , _mode , _show , _store )
2005-04-17 02:20:36 +04:00
/*
2005-08-05 06:33:07 +04:00
* iSCSI connection attrs
2005-04-17 02:20:36 +04:00
*/
2006-06-28 21:00:23 +04:00
# define iscsi_conn_attr_show(param) \
2005-08-05 06:33:07 +04:00
static ssize_t \
2006-06-28 21:00:23 +04:00
show_conn_param_ # # param ( struct class_device * cdev , char * buf ) \
2005-08-05 06:33:07 +04:00
{ \
2006-01-14 03:05:50 +03:00
struct iscsi_cls_conn * conn = iscsi_cdev_to_conn ( cdev ) ; \
struct iscsi_transport * t = conn - > transport ; \
2006-06-28 21:00:23 +04:00
return t - > get_conn_param ( conn , param , buf ) ; \
2005-08-05 06:33:07 +04:00
}
2006-06-28 21:00:23 +04:00
# define iscsi_conn_attr(field, param) \
iscsi_conn_attr_show ( param ) \
static ISCSI_CLASS_ATTR ( conn , field , S_IRUGO , show_conn_param_ # # param , \
2006-04-07 06:13:36 +04:00
NULL ) ;
2005-08-05 06:33:07 +04:00
2006-06-28 21:00:23 +04:00
iscsi_conn_attr ( max_recv_dlength , ISCSI_PARAM_MAX_RECV_DLENGTH ) ;
iscsi_conn_attr ( max_xmit_dlength , ISCSI_PARAM_MAX_XMIT_DLENGTH ) ;
iscsi_conn_attr ( header_digest , ISCSI_PARAM_HDRDGST_EN ) ;
iscsi_conn_attr ( data_digest , ISCSI_PARAM_DATADGST_EN ) ;
iscsi_conn_attr ( ifmarker , ISCSI_PARAM_IFMARKER_EN ) ;
iscsi_conn_attr ( ofmarker , ISCSI_PARAM_OFMARKER_EN ) ;
iscsi_conn_attr ( persistent_port , ISCSI_PARAM_PERSISTENT_PORT ) ;
iscsi_conn_attr ( port , ISCSI_PARAM_CONN_PORT ) ;
iscsi_conn_attr ( exp_statsn , ISCSI_PARAM_EXP_STATSN ) ;
iscsi_conn_attr ( persistent_address , ISCSI_PARAM_PERSISTENT_ADDRESS ) ;
iscsi_conn_attr ( address , ISCSI_PARAM_CONN_ADDRESS ) ;
2005-04-17 02:20:36 +04:00
2006-01-14 03:05:50 +03:00
# define iscsi_cdev_to_session(_cdev) \
iscsi_dev_to_session ( _cdev - > dev )
2005-04-17 02:20:36 +04:00
/*
2005-08-05 06:33:07 +04:00
* iSCSI session attrs
2005-04-17 02:20:36 +04:00
*/
2006-06-28 21:00:23 +04:00
# define iscsi_session_attr_show(param) \
2006-04-07 06:13:36 +04:00
static ssize_t \
2006-06-28 21:00:23 +04:00
show_session_param_ # # param ( struct class_device * cdev , char * buf ) \
2006-04-07 06:13:36 +04:00
{ \
struct iscsi_cls_session * session = iscsi_cdev_to_session ( cdev ) ; \
struct iscsi_transport * t = session - > transport ; \
2006-06-28 21:00:23 +04:00
return t - > get_session_param ( session , param , buf ) ; \
2006-04-07 06:13:36 +04:00
}
2006-06-28 21:00:23 +04:00
# define iscsi_session_attr(field, param) \
iscsi_session_attr_show ( param ) \
static ISCSI_CLASS_ATTR ( sess , field , S_IRUGO , show_session_param_ # # param , \
2006-04-07 06:13:36 +04:00
NULL ) ;
2006-06-28 21:00:23 +04:00
iscsi_session_attr ( targetname , ISCSI_PARAM_TARGET_NAME ) ;
iscsi_session_attr ( initial_r2t , ISCSI_PARAM_INITIAL_R2T_EN ) ;
iscsi_session_attr ( max_outstanding_r2t , ISCSI_PARAM_MAX_R2T ) ;
iscsi_session_attr ( immediate_data , ISCSI_PARAM_IMM_DATA_EN ) ;
iscsi_session_attr ( first_burst_len , ISCSI_PARAM_FIRST_BURST ) ;
iscsi_session_attr ( max_burst_len , ISCSI_PARAM_MAX_BURST ) ;
iscsi_session_attr ( data_pdu_in_order , ISCSI_PARAM_PDU_INORDER_EN ) ;
iscsi_session_attr ( data_seq_in_order , ISCSI_PARAM_DATASEQ_INORDER_EN ) ;
iscsi_session_attr ( erl , ISCSI_PARAM_ERL ) ;
iscsi_session_attr ( tpgt , ISCSI_PARAM_TPGT ) ;
2005-04-17 02:20:36 +04:00
2006-04-07 06:13:36 +04:00
# define iscsi_priv_session_attr_show(field, format) \
static ssize_t \
2006-06-28 21:00:23 +04:00
show_priv_session_ # # field ( struct class_device * cdev , char * buf ) \
2006-04-07 06:13:36 +04:00
{ \
2006-06-28 21:00:23 +04:00
struct iscsi_cls_session * session = iscsi_cdev_to_session ( cdev ) ; \
2006-04-07 06:13:36 +04:00
return sprintf ( buf , format " \n " , session - > field ) ; \
}
# define iscsi_priv_session_attr(field, format) \
iscsi_priv_session_attr_show ( field , format ) \
static ISCSI_CLASS_ATTR ( priv_sess , field , S_IRUGO , show_priv_session_ # # field , \
NULL )
2006-04-07 06:13:39 +04:00
iscsi_priv_session_attr ( recovery_tmo , " %d " ) ;
2006-04-07 06:13:36 +04:00
# define SETUP_PRIV_SESSION_RD_ATTR(field) \
do { \
priv - > session_attrs [ count ] = & class_device_attr_priv_sess_ # # field ; \
count + + ; \
} while ( 0 )
2006-06-28 21:00:23 +04:00
2006-04-07 06:13:36 +04:00
# define SETUP_SESSION_RD_ATTR(field, param_flag) \
do { \
if ( tt - > param_mask & param_flag ) { \
priv - > session_attrs [ count ] = & class_device_attr_sess_ # # field ; \
2005-04-17 02:20:36 +04:00
count + + ; \
2006-04-07 06:13:36 +04:00
} \
} while ( 0 )
# define SETUP_CONN_RD_ATTR(field, param_flag) \
do { \
if ( tt - > param_mask & param_flag ) { \
priv - > conn_attrs [ count ] = & class_device_attr_conn_ # # field ; \
count + + ; \
} \
} while ( 0 )
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
static int iscsi_session_match ( struct attribute_container * cont ,
struct device * dev )
2005-04-17 02:20:36 +04:00
{
2006-01-14 03:05:50 +03:00
struct iscsi_cls_session * session ;
2005-04-17 02:20:36 +04:00
struct Scsi_Host * shost ;
2005-08-05 06:33:07 +04:00
struct iscsi_internal * priv ;
if ( ! iscsi_is_session_dev ( dev ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-01-14 03:05:50 +03:00
session = iscsi_dev_to_session ( dev ) ;
shost = iscsi_session_to_shost ( session ) ;
2005-08-05 06:33:07 +04:00
if ( ! shost - > transportt )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-08-05 06:33:07 +04:00
priv = to_iscsi_internal ( shost - > transportt ) ;
if ( priv - > session_cont . ac . class ! = & iscsi_session_class . class )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-08-05 06:33:07 +04:00
return & priv - > session_cont . ac = = cont ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 06:33:07 +04:00
static int iscsi_conn_match ( struct attribute_container * cont ,
struct device * dev )
{
2006-01-14 03:05:50 +03:00
struct iscsi_cls_session * session ;
struct iscsi_cls_conn * conn ;
2005-04-17 02:20:36 +04:00
struct Scsi_Host * shost ;
2005-08-05 06:33:07 +04:00
struct iscsi_internal * priv ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
if ( ! iscsi_is_conn_dev ( dev ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2006-01-14 03:05:50 +03:00
conn = iscsi_dev_to_conn ( dev ) ;
session = iscsi_dev_to_session ( conn - > dev . parent ) ;
shost = iscsi_session_to_shost ( session ) ;
2005-08-05 06:33:07 +04:00
if ( ! shost - > transportt )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-08-05 06:33:07 +04:00
priv = to_iscsi_internal ( shost - > transportt ) ;
if ( priv - > conn_cont . ac . class ! = & iscsi_connection_class . class )
return 0 ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
return & priv - > conn_cont . ac = = cont ;
}
2006-04-07 06:13:39 +04:00
static int iscsi_host_match ( struct attribute_container * cont ,
struct device * dev )
{
struct Scsi_Host * shost ;
struct iscsi_internal * priv ;
if ( ! scsi_is_host_device ( dev ) )
return 0 ;
shost = dev_to_shost ( dev ) ;
if ( ! shost - > transportt | |
shost - > transportt - > host_attrs . ac . class ! = & iscsi_host_class . class )
return 0 ;
priv = to_iscsi_internal ( shost - > transportt ) ;
return & priv - > t . host_attrs . ac = = cont ;
}
2006-01-14 03:05:50 +03:00
struct scsi_transport_template *
iscsi_register_transport ( struct iscsi_transport * tt )
2005-08-05 06:33:07 +04:00
{
struct iscsi_internal * priv ;
unsigned long flags ;
int count = 0 , err ;
BUG_ON ( ! tt ) ;
priv = iscsi_if_transport_lookup ( tt ) ;
if ( priv )
2006-01-14 03:05:50 +03:00
return NULL ;
2005-08-05 06:33:07 +04:00
2006-01-16 18:31:18 +03:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
2005-08-05 06:33:07 +04:00
if ( ! priv )
2006-01-14 03:05:50 +03:00
return NULL ;
2005-08-05 06:33:07 +04:00
INIT_LIST_HEAD ( & priv - > list ) ;
2006-06-28 21:00:32 +04:00
priv - > daemon_pid = - 1 ;
2005-08-05 06:33:07 +04:00
priv - > iscsi_transport = tt ;
2006-04-07 06:13:39 +04:00
priv - > t . user_scan = iscsi_user_scan ;
2005-08-05 06:33:07 +04:00
priv - > cdev . class = & iscsi_transport_class ;
snprintf ( priv - > cdev . class_id , BUS_ID_SIZE , " %s " , tt - > name ) ;
err = class_device_register ( & priv - > cdev ) ;
if ( err )
goto free_priv ;
err = sysfs_create_group ( & priv - > cdev . kobj , & iscsi_transport_group ) ;
if ( err )
goto unregister_cdev ;
2006-04-07 06:13:39 +04:00
/* host parameters */
priv - > t . host_attrs . ac . attrs = & priv - > host_attrs [ 0 ] ;
priv - > t . host_attrs . ac . class = & iscsi_host_class . class ;
priv - > t . host_attrs . ac . match = iscsi_host_match ;
priv - > t . host_size = sizeof ( struct iscsi_host ) ;
priv - > host_attrs [ 0 ] = NULL ;
transport_container_register ( & priv - > t . host_attrs ) ;
2005-08-05 06:33:07 +04:00
/* connection parameters */
priv - > conn_cont . ac . attrs = & priv - > conn_attrs [ 0 ] ;
priv - > conn_cont . ac . class = & iscsi_connection_class . class ;
priv - > conn_cont . ac . match = iscsi_conn_match ;
transport_container_register ( & priv - > conn_cont ) ;
2005-04-17 02:20:36 +04:00
2006-04-07 06:13:36 +04:00
SETUP_CONN_RD_ATTR ( max_recv_dlength , ISCSI_MAX_RECV_DLENGTH ) ;
SETUP_CONN_RD_ATTR ( max_xmit_dlength , ISCSI_MAX_XMIT_DLENGTH ) ;
SETUP_CONN_RD_ATTR ( header_digest , ISCSI_HDRDGST_EN ) ;
SETUP_CONN_RD_ATTR ( data_digest , ISCSI_DATADGST_EN ) ;
SETUP_CONN_RD_ATTR ( ifmarker , ISCSI_IFMARKER_EN ) ;
SETUP_CONN_RD_ATTR ( ofmarker , ISCSI_OFMARKER_EN ) ;
SETUP_CONN_RD_ATTR ( address , ISCSI_CONN_ADDRESS ) ;
SETUP_CONN_RD_ATTR ( port , ISCSI_CONN_PORT ) ;
2006-05-03 04:46:47 +04:00
SETUP_CONN_RD_ATTR ( exp_statsn , ISCSI_EXP_STATSN ) ;
2006-06-28 21:00:23 +04:00
SETUP_CONN_RD_ATTR ( persistent_address , ISCSI_PERSISTENT_ADDRESS ) ;
SETUP_CONN_RD_ATTR ( persistent_port , ISCSI_PERSISTENT_PORT ) ;
2005-08-05 06:33:07 +04:00
BUG_ON ( count > ISCSI_CONN_ATTRS ) ;
priv - > conn_attrs [ count ] = NULL ;
2005-04-17 02:20:36 +04:00
count = 0 ;
2005-08-05 06:33:07 +04:00
/* session parameters */
priv - > session_cont . ac . attrs = & priv - > session_attrs [ 0 ] ;
priv - > session_cont . ac . class = & iscsi_session_class . class ;
priv - > session_cont . ac . match = iscsi_session_match ;
transport_container_register ( & priv - > session_cont ) ;
2006-04-07 06:13:36 +04:00
SETUP_SESSION_RD_ATTR ( initial_r2t , ISCSI_INITIAL_R2T_EN ) ;
SETUP_SESSION_RD_ATTR ( max_outstanding_r2t , ISCSI_MAX_R2T ) ;
SETUP_SESSION_RD_ATTR ( immediate_data , ISCSI_IMM_DATA_EN ) ;
SETUP_SESSION_RD_ATTR ( first_burst_len , ISCSI_FIRST_BURST ) ;
SETUP_SESSION_RD_ATTR ( max_burst_len , ISCSI_MAX_BURST ) ;
SETUP_SESSION_RD_ATTR ( data_pdu_in_order , ISCSI_PDU_INORDER_EN ) ;
SETUP_SESSION_RD_ATTR ( data_seq_in_order , ISCSI_DATASEQ_INORDER_EN ) ;
SETUP_SESSION_RD_ATTR ( erl , ISCSI_ERL ) ;
2006-06-28 21:00:23 +04:00
SETUP_SESSION_RD_ATTR ( targetname , ISCSI_TARGET_NAME ) ;
SETUP_SESSION_RD_ATTR ( tpgt , ISCSI_TPGT ) ;
2006-04-07 06:13:39 +04:00
SETUP_PRIV_SESSION_RD_ATTR ( recovery_tmo ) ;
2006-04-07 06:13:36 +04:00
2005-08-05 06:33:07 +04:00
BUG_ON ( count > ISCSI_SESSION_ATTRS ) ;
priv - > session_attrs [ count ] = NULL ;
spin_lock_irqsave ( & iscsi_transport_lock , flags ) ;
list_add ( & priv - > list , & iscsi_transports ) ;
spin_unlock_irqrestore ( & iscsi_transport_lock , flags ) ;
printk ( KERN_NOTICE " iscsi: registered transport (%s) \n " , tt - > name ) ;
2006-01-14 03:05:50 +03:00
return & priv - > t ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
unregister_cdev :
class_device_unregister ( & priv - > cdev ) ;
free_priv :
kfree ( priv ) ;
2006-01-14 03:05:50 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 06:33:07 +04:00
EXPORT_SYMBOL_GPL ( iscsi_register_transport ) ;
int iscsi_unregister_transport ( struct iscsi_transport * tt )
{
struct iscsi_internal * priv ;
unsigned long flags ;
BUG_ON ( ! tt ) ;
2006-01-11 15:16:10 +03:00
mutex_lock ( & rx_queue_mutex ) ;
2005-08-05 06:33:07 +04:00
priv = iscsi_if_transport_lookup ( tt ) ;
BUG_ON ( ! priv ) ;
spin_lock_irqsave ( & iscsi_transport_lock , flags ) ;
list_del ( & priv - > list ) ;
spin_unlock_irqrestore ( & iscsi_transport_lock , flags ) ;
transport_container_unregister ( & priv - > conn_cont ) ;
transport_container_unregister ( & priv - > session_cont ) ;
2006-04-07 06:13:39 +04:00
transport_container_unregister ( & priv - > t . host_attrs ) ;
2005-08-05 06:33:07 +04:00
sysfs_remove_group ( & priv - > cdev . kobj , & iscsi_transport_group ) ;
class_device_unregister ( & priv - > cdev ) ;
2006-01-11 15:16:10 +03:00
mutex_unlock ( & rx_queue_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( iscsi_unregister_transport ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
static int
iscsi_rcv_nl_event ( struct notifier_block * this , unsigned long event , void * ptr )
2005-04-17 02:20:36 +04:00
{
2005-08-05 06:33:07 +04:00
struct netlink_notify * n = ptr ;
if ( event = = NETLINK_URELEASE & &
n - > protocol = = NETLINK_ISCSI & & n - > pid ) {
2006-01-14 03:05:50 +03:00
struct iscsi_cls_conn * conn ;
2005-08-05 06:33:07 +04:00
unsigned long flags ;
2006-01-14 03:05:50 +03:00
mempool_zone_complete ( z_reply ) ;
2005-08-05 06:33:07 +04:00
spin_lock_irqsave ( & connlock , flags ) ;
list_for_each_entry ( conn , & connlist , conn_list ) {
2006-01-14 03:05:50 +03:00
mempool_zone_complete ( conn - > z_error ) ;
mempool_zone_complete ( conn - > z_pdu ) ;
2005-08-05 06:33:07 +04:00
}
spin_unlock_irqrestore ( & connlock , flags ) ;
}
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
return NOTIFY_DONE ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 06:33:07 +04:00
static struct notifier_block iscsi_nl_notifier = {
. notifier_call = iscsi_rcv_nl_event ,
} ;
2005-04-17 02:20:36 +04:00
static __init int iscsi_transport_init ( void )
{
2005-08-05 06:33:07 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2005-08-05 06:33:07 +04:00
err = class_register ( & iscsi_transport_class ) ;
2005-04-17 02:20:36 +04:00
if ( err )
return err ;
2005-08-05 06:33:07 +04:00
2006-04-07 06:13:39 +04:00
err = transport_class_register ( & iscsi_host_class ) ;
2005-08-05 06:33:07 +04:00
if ( err )
goto unregister_transport_class ;
2006-04-07 06:13:39 +04:00
err = transport_class_register ( & iscsi_connection_class ) ;
if ( err )
goto unregister_host_class ;
2005-08-05 06:33:07 +04:00
err = transport_class_register ( & iscsi_session_class ) ;
if ( err )
goto unregister_conn_class ;
err = netlink_register_notifier ( & iscsi_nl_notifier ) ;
if ( err )
goto unregister_session_class ;
2005-09-08 19:14:11 +04:00
nls = netlink_kernel_create ( NETLINK_ISCSI , 1 , iscsi_if_rx ,
2006-01-14 03:05:50 +03:00
THIS_MODULE ) ;
2005-08-05 06:33:07 +04:00
if ( ! nls ) {
err = - ENOBUFS ;
goto unregister_notifier ;
}
2006-01-14 03:05:50 +03:00
z_reply = mempool_zone_init ( Z_MAX_REPLY ,
2005-08-05 06:33:07 +04:00
NLMSG_SPACE ( sizeof ( struct iscsi_uevent ) ) , Z_HIWAT_REPLY ) ;
2006-01-14 03:05:50 +03:00
if ( z_reply )
2005-08-05 06:33:07 +04:00
return 0 ;
sock_release ( nls - > sk_socket ) ;
unregister_notifier :
netlink_unregister_notifier ( & iscsi_nl_notifier ) ;
unregister_session_class :
transport_class_unregister ( & iscsi_session_class ) ;
unregister_conn_class :
transport_class_unregister ( & iscsi_connection_class ) ;
2006-04-07 06:13:39 +04:00
unregister_host_class :
transport_class_unregister ( & iscsi_host_class ) ;
2005-08-05 06:33:07 +04:00
unregister_transport_class :
class_unregister ( & iscsi_transport_class ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
static void __exit iscsi_transport_exit ( void )
{
2006-01-14 03:05:50 +03:00
mempool_zone_destroy ( z_reply ) ;
2005-08-05 06:33:07 +04:00
sock_release ( nls - > sk_socket ) ;
netlink_unregister_notifier ( & iscsi_nl_notifier ) ;
transport_class_unregister ( & iscsi_connection_class ) ;
transport_class_unregister ( & iscsi_session_class ) ;
2006-04-07 06:13:39 +04:00
transport_class_unregister ( & iscsi_host_class ) ;
2005-08-05 06:33:07 +04:00
class_unregister ( & iscsi_transport_class ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( iscsi_transport_init ) ;
module_exit ( iscsi_transport_exit ) ;
2005-08-05 06:33:07 +04:00
MODULE_AUTHOR ( " Mike Christie <michaelc@cs.wisc.edu>, "
" Dmitry Yusupov <dmitry_yus@yahoo.com>, "
" Alex Aizman <itn780@yahoo.com> " ) ;
MODULE_DESCRIPTION ( " iSCSI Transport Interface " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;