add open iscsi netlink interface to iscsi transport class
Subsystems (4)
PATCH 3/3 add open iscsi netlink interface to iscsi transport class
incorporate the open-iscsi/linux-iscsi netlink interface into the iscsi transport class.
See here for details on the sysfs layout
http://marc.theaimsgroup.com/?l=linux-kernel&m=111526269831567&w=2
drivers/scsi/scsi_transport_iscsi.c +1066 -245
@@ -1,8 +1,10 @@ -/* +/* * iSCSI transport class definitions * * Copyright (C) IBM Corporation, 2004 -* Copyright (C) Mike Christie, 2004 +* Copyright (C) Mike Christie, 2004 - 2005 +* Copyright (C) Dmitry Yusupov, 2004 - 2005 +* Copyright (C) Alex Aizman, 2004 - 2005 * * 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
@@ -19,370 +21,1189 @@* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <linux/module.h> +#include <linux/mempool.h> +#include <net/tcp.h> #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> +#include <scsi/iscsi_ifev.h> -#define ISCSI_SESSION_ATTRS 20 -#define ISCSI_HOST_ATTRS 2 +#define ISCSI_SESSION_ATTRS 8 +#define ISCSI_CONN_ATTRS 6struct iscsi_internal {struct scsi_transport_template t; -struct iscsi_function_template *fnt; +struct iscsi_transport *iscsi_transport; +struct list_head list; +/* +* List of sessions for this transport +*/ +struct list_head sessions; +/* +* lock to serialize access to the sessions list which must +* be taken after the rx_queue_sema +*/ +spinlock_t session_lock; +/* +* based on transport capabilities, at register time we sets these +* bits to tell the transport class it wants the attributes +* displayed in sysfs. +*/ +uint32_t param_mask; +/* * We do not have any private or other attrs. */ +struct transport_container conn_cont; +struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS +1 ]; +struct transport_container session_cont;struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS +1 ]; -struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS +1 ]; }; +/* +* list of registered transports and lock that must +* be held while accessing list. The iscsi_transport_lock must +* be acquired after the rx_queue_sema. +*/ +static LIST_HEAD (iscsi_transports); +static DEFINE_SPINLOCK (iscsi_transport_lock); +#define to_iscsi_internal (tmpl) container_of(tmpl, struct iscsi_internal, t) -static DECLARE_TRANSPORT_CLASS(iscsi_transport_class, -"iscsi_transport" , +static DECLARE_TRANSPORT_CLASS(iscsi_session_class, +"iscsi_session" ,NULL ,NULL , NULL); -static DECLARE_TRANSPORT_CLASS(iscsi_host_class, -"iscsi_host" , +static DECLARE_TRANSPORT_CLASS(iscsi_connection_class, +"iscsi_connection" ,NULL ,NULL , NULL); + +static struct sock *nls; +static int daemon_pid; +static DECLARE_MUTEX (rx_queue_sema); + +struct mempool_zone { +mempool_t *pool; +atomic_t allocated; +int size; +int hiwat; +struct list_head freequeue; +spinlock_t freelock; +}; + +static struct mempool_zone z_reply; +/* -* iSCSI target and session attrs +* 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 */ -#define iscsi_session_show_fn (field, format) \ - \ -static ssize_t \ -show_session_##field(struct class_device *cdev, char *buf) \ -{ \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ - struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ - \ - if (i->fnt->get_##field) \ - i->fnt->get_##field(starget); \ - return snprintf(buf, 20, format"\n", iscsi_##field(starget)); \ -} - -#define iscsi_session_rd_attr (field, format) \ - iscsi_session_show_fn(field, format) \ -static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_##field, NULL); - -iscsi_session_rd_attr (tpgt,"%hu" ); -iscsi_session_rd_attr (tsih,"%2x" ); -iscsi_session_rd_attr (max_recv_data_segment_len,"%u" ); -iscsi_session_rd_attr (max_burst_len,"%u" ); -iscsi_session_rd_attr (first_burst_len,"%u" ); -iscsi_session_rd_attr (def_time2wait,"%hu" ); -iscsi_session_rd_attr (def_time2retain,"%hu" ); -iscsi_session_rd_attr (max_outstanding_r2t,"%hu" ); -iscsi_session_rd_attr (erl,"%d" ); +#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 + +struct iscsi_if_conn { +struct list_head conn_list;/* item in connlist */ +struct list_head session_list;/* item in session->connections */ +iscsi_connh_t connh; +int active;/* must be accessed with the connlock */ +struct Scsi_Host *host;/* originated shost */ +struct device dev;/* sysfs transport/container device */ +struct iscsi_transport *transport; +struct mempool_zone z_error; +struct mempool_zone z_pdu; +struct list_head freequeue; +}; +#define iscsi_dev_to_if_conn (_dev) \ + container_of(_dev, struct iscsi_if_conn, dev) -#define iscsi_session_show_bool_fn (field) \ - \ -static ssize_t \ -show_session_bool_##field(struct class_device *cdev, char *buf) \ -{ \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ - struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ - \ - if (i->fnt->get_##field) \ - i->fnt->get_##field(starget); \ - \ - if (iscsi_##field(starget)) \ - return sprintf(buf, "Yes\n"); \ - return sprintf(buf, "No\n"); \ +#define iscsi_cdev_to_if_conn (_cdev) \ + iscsi_dev_to_if_conn(_cdev->dev) + +static LIST_HEAD (connlist); +static DEFINE_SPINLOCK (connlock); + +struct iscsi_if_session { +struct list_head list;/* item in session_list */ +struct list_head connections; +iscsi_sessionh_t sessionh; +struct iscsi_transport *transport; +struct device dev;/* sysfs transport/container device */ +}; + +#define iscsi_dev_to_if_session (_dev) \ + container_of(_dev, struct iscsi_if_session, dev) + +#define iscsi_cdev_to_if_session (_cdev) \ + iscsi_dev_to_if_session(_cdev->dev) + +#define iscsi_if_session_to_shost (_session) \ + dev_to_shost(_session->dev.parent) + +static struct iscsi_if_conn * +iscsi_if_find_conn (uint64_t key) +{ +unsigned long flags; +struct iscsi_if_conn *conn; + +spin_lock_irqsave (&connlock, flags); +list_for_each_entry (conn, &connlist, conn_list) +if (conn->connh == key) { +spin_unlock_irqrestore (&connlock, flags); +return conn; + } +spin_unlock_irqrestore (&connlock, flags); +return NULL ; } -#define iscsi_session_rd_bool_attr (field) \ - iscsi_session_show_bool_fn(field) \ -static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_bool_##field, NULL); +static struct iscsi_internal * +iscsi_if_transport_lookup (struct iscsi_transport *tt) +{ +struct iscsi_internal *priv; +unsigned long flags; -iscsi_session_rd_bool_attr (initial_r2t); -iscsi_session_rd_bool_attr (immediate_data); -iscsi_session_rd_bool_attr (data_pdu_in_order); -iscsi_session_rd_bool_attr (data_sequence_in_order); +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 ; +} -#define iscsi_session_show_digest_fn (field) \ - \ -static ssize_t \ -show_##field(struct class_device *cdev, char *buf) \ -{ \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ - struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ - \ - if (i->fnt->get_##field) \ - i->fnt->get_##field(starget); \ - \ - if (iscsi_##field(starget)) \ - return sprintf(buf, "CRC32C\n"); \ - return sprintf(buf, "None\n"); \ +static inline struct list_head *skb_to_lh (struct sk_buff *skb) +{ +return (struct list_head *)&skb->cb; } -#define iscsi_session_rd_digest_attr (field) \ - iscsi_session_show_digest_fn(field) \ -static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); +static void * +mempool_zone_alloc_skb (unsigned int gfp_mask,void *pool_data) +{ +struct mempool_zone *zone = pool_data; -iscsi_session_rd_digest_attr (header_digest); -iscsi_session_rd_digest_attr (data_digest); +return alloc_skb (zone->size, gfp_mask); +} -static ssize_t -show_port (struct class_device *cdev,char *buf) +static void +mempool_zone_free_skb (void *element,void *pool_data) { -struct scsi_target *starget =transport_class_to_starget (cdev); -struct Scsi_Host *shost =dev_to_shost (starget->dev.parent); -struct iscsi_internal *i =to_iscsi_internal (shost->transportt); +kfree_skb (element); +} -if (i->fnt->get_port) - i->fnt->get_port (starget); +static void +mempool_zone_complete (struct mempool_zone *zone) +{ +unsigned long flags; +struct list_head *lh, *n; -return snprintf (buf,20 ,"%hu\n" ,ntohs (iscsi_port (starget))); +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); } -static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL); -static ssize_t -show_ip_address (struct class_device *cdev,char *buf) +static int +mempool_zone_init (struct mempool_zone *zp,unsigned max,unsigned size, +unsigned hiwat) { -struct scsi_target *starget =transport_class_to_starget (cdev); -struct Scsi_Host *shost =dev_to_shost (starget->dev.parent); -struct iscsi_internal *i =to_iscsi_internal (shost->transportt); + zp->pool =mempool_create (max, mempool_zone_alloc_skb, + mempool_zone_free_skb, zp); +if (!zp->pool) +return -ENOMEM ; + + zp->size = size; + zp->hiwat = hiwat; + +INIT_LIST_HEAD (&zp->freequeue); +spin_lock_init (&zp->freelock); +atomic_set (&zp->allocated,0 ); + +return 0 ; +} -if (i->fnt->get_ip_address) - i->fnt->get_ip_address (starget); -if (iscsi_addr_type (starget) ==AF_INET ) -return sprintf (buf,"%u.%u.%u.%u\n" , -NIPQUAD (iscsi_sin_addr (starget))); -else if (iscsi_addr_type (starget) ==AF_INET6 ) -return sprintf (buf,"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n" , -NIP6 (iscsi_sin6_addr (starget))); -return -EINVAL ; +static struct sk_buff * +mempool_zone_get_skb (struct mempool_zone *zone) +{ +struct sk_buff *skb; + + skb =mempool_alloc (zone->pool,GFP_ATOMIC ); +if (skb) +atomic_inc (&zone->allocated); +return skb; } -static CLASS_DEVICE_ATTR(ip_address, S_IRUGO, show_ip_address, NULL); -static ssize_t -show_isid (struct class_device *cdev,char *buf) +static int +iscsi_unicast_skb (struct mempool_zone *zone,struct sk_buff *skb) { -struct scsi_target *starget =transport_class_to_starget (cdev); -struct Scsi_Host *shost =dev_to_shost (starget->dev.parent); -struct iscsi_internal *i =to_iscsi_internal (shost->transportt); +unsigned long flags; +int rc; + +skb_get (skb); + rc =netlink_unicast (nls, skb, daemon_pid,MSG_DONTWAIT ); +if (rc <0 ) { +mempool_free (skb, zone->pool); +printk (KERN_ERR "iscsi: can not unicast skb (%d)\n" , rc); +return rc; + } -if (i->fnt->get_isid) - i->fnt->get_isid (starget); +spin_lock_irqsave (&zone->freelock, flags); +list_add (skb_to_lh (skb), &zone->freequeue); +spin_unlock_irqrestore (&zone->freelock, flags); -return sprintf (buf,"%02x%02x%02x%02x%02x%02x\n" , -iscsi_isid (starget)[0 ],iscsi_isid (starget)[1 ], -iscsi_isid (starget)[2 ],iscsi_isid (starget)[3 ], -iscsi_isid (starget)[4 ],iscsi_isid (starget)[5 ]); +return 0 ; } -static CLASS_DEVICE_ATTR(isid, S_IRUGO, show_isid, NULL); -/* -* This is used for iSCSI names. Normally, we follow -* the transport class convention of having the lld -* set the field, but in these cases the value is -* too large. -*/ -#define iscsi_session_show_str_fn (field) \ - \ -static ssize_t \ -show_session_str_##field(struct class_device *cdev, char *buf) \ -{ \ - ssize_t ret = 0; \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ - struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ - \ - if (i->fnt->get_##field) \ - ret = i->fnt->get_##field(starget, buf, PAGE_SIZE); \ - return ret; \ +int iscsi_recv_pdu (iscsi_connh_t connh,struct iscsi_hdr *hdr, +char *data,uint32_t data_size) +{ +struct nlmsghdr *nlh; +struct sk_buff *skb; +struct iscsi_uevent *ev; +struct iscsi_if_conn *conn; +char *pdu; +int len =NLMSG_SPACE (sizeof (*ev) +sizeof (struct iscsi_hdr ) + + data_size); + + conn =iscsi_if_find_conn (connh); +BUG_ON (!conn); + +mempool_zone_complete (&conn->z_pdu); + + skb =mempool_zone_get_skb (&conn->z_pdu); +if (!skb) { +iscsi_conn_error (connh,ISCSI_ERR_CONN_FAILED ); +printk (KERN_ERR "iscsi%d: can not deliver control PDU: OOM\n" , + conn->host->host_no); +return -ENOMEM ; + } + + nlh =__nlmsg_put (skb, daemon_pid,0 ,0 , (len -sizeof (*nlh))); + ev =NLMSG_DATA (nlh); +memset (ev,0 ,sizeof (*ev)); + ev->transport_handle =iscsi_handle (conn->transport); + ev->type =ISCSI_KEVENT_RECV_PDU ; +if (atomic_read (&conn->z_pdu.allocated) >= conn->z_pdu.hiwat) + ev->iferror = -ENOMEM ; + ev->r.recv_req.conn_handle = connh; + pdu = (char *)ev +sizeof (*ev); +memcpy (pdu, hdr,sizeof (struct iscsi_hdr )); +memcpy (pdu +sizeof (struct iscsi_hdr ), data, data_size); + +return iscsi_unicast_skb (&conn->z_pdu, skb); } +EXPORT_SYMBOL_GPL (iscsi_recv_pdu); -#define iscsi_session_rd_str_attr (field) \ - iscsi_session_show_str_fn(field) \ -static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_str_##field, NULL); +void iscsi_conn_error (iscsi_connh_t connh,enum iscsi_err error) +{ +struct nlmsghdr *nlh; +struct sk_buff *skb; +struct iscsi_uevent *ev; +struct iscsi_if_conn *conn; +int len =NLMSG_SPACE (sizeof (*ev)); + + conn =iscsi_if_find_conn (connh); +BUG_ON (!conn); + +mempool_zone_complete (&conn->z_error); + + skb =mempool_zone_get_skb (&conn->z_error); +if (!skb) { +printk (KERN_ERR "iscsi%d: gracefully ignored conn error (%d)\n" , + conn->host->host_no, error); +return ; + } + + nlh =__nlmsg_put (skb, daemon_pid,0 ,0 , (len -sizeof (*nlh))); + ev =NLMSG_DATA (nlh); + ev->transport_handle =iscsi_handle (conn->transport); + ev->type =ISCSI_KEVENT_CONN_ERROR ; +if (atomic_read (&conn->z_error.allocated) >= conn->z_error.hiwat) + ev->iferror = -ENOMEM ; + ev->r.connerror.error = error; + ev->r.connerror.conn_handle = connh; -iscsi_session_rd_str_attr (target_name); -iscsi_session_rd_str_attr (target_alias); +iscsi_unicast_skb (&conn->z_error, skb); + +printk (KERN_INFO "iscsi%d: detected conn error (%d)\n" , + conn->host->host_no, error); +} +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; + +mempool_zone_complete (&z_reply); + + skb =mempool_zone_get_skb (&z_reply); +/* +* 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))); + nlh->nlmsg_flags = flags; +memcpy (NLMSG_DATA (nlh), payload, size); +return iscsi_unicast_skb (&z_reply, skb); +}/* -* iSCSI host attrs +* iSCSI Session's hostdata organization: +* +* *------------------* <== host->hostdata +* | transport | +* |------------------| <== iscsi_hostdata(host->hostdata) +* | transport's data | +* |------------------| <== hostdata_session(host->hostdata) +* | interface's data | +* *------------------* */ +#define hostdata_privsize (_t) (sizeof(unsigned long) + _t->hostdata_size + \ + _t->hostdata_size % sizeof(unsigned long) + \ + sizeof(struct iscsi_if_session)) + +#define hostdata_session (_hostdata) ((void*)_hostdata + sizeof(unsigned long) + \ + ((struct iscsi_transport *) \ + iscsi_ptr(*(uint64_t *)_hostdata))->hostdata_size) + +static void iscsi_if_session_dev_release (struct device *dev) +{ +struct iscsi_if_session *session =iscsi_dev_to_if_session (dev); +struct iscsi_transport *transport = session->transport; +struct Scsi_Host *shost =iscsi_if_session_to_shost (session); +struct iscsi_if_conn *conn, *tmp; +unsigned long flags; + +/* now free connections */ +spin_lock_irqsave (&connlock, flags); +list_for_each_entry_safe (conn, tmp, &session->connections, + session_list) { +list_del (&conn->session_list); +mempool_destroy (conn->z_pdu.pool); +mempool_destroy (conn->z_error.pool); +kfree (conn); + } +spin_unlock_irqrestore (&connlock, flags); +scsi_host_put (shost); +module_put (transport->owner); +} + +static int +iscsi_if_create_session (struct iscsi_internal *priv,struct iscsi_uevent *ev) +{ +struct iscsi_transport *transport = priv->iscsi_transport; +struct iscsi_if_session *session; +struct Scsi_Host *shost; +unsigned long flags; +int error; + +if (!try_module_get (transport->owner)) +return -EPERM ; + + shost =scsi_host_alloc (transport->host_template, +hostdata_privsize (transport)); +if (!shost) { + ev->r.c_session_ret.session_handle =iscsi_handle (NULL ); +printk (KERN_ERR "iscsi: can not allocate SCSI host for " +"session\n" ); + error = -ENOMEM ; + goto out_module_put; + } + shost->max_id =1 ; + shost->max_channel =0 ; + shost->max_lun = transport->max_lun; + shost->max_cmd_len = transport->max_cmd_len; + shost->transportt = &priv->t; + +/* store struct iscsi_transport in hostdata */ + *(uint64_t *)shost->hostdata = ev->transport_handle; + + ev->r.c_session_ret.session_handle = transport->create_session ( + ev->u.c_session.initial_cmdsn, shost); +if (ev->r.c_session_ret.session_handle ==iscsi_handle (NULL )) { + error =0 ; + goto out_host_put; + } + +/* host_no becomes assigned SID */ + ev->r.c_session_ret.sid = shost->host_no; +/* initialize session */ + session =hostdata_session (shost->hostdata); +INIT_LIST_HEAD (&session->connections); +INIT_LIST_HEAD (&session->list); + session->sessionh = ev->r.c_session_ret.session_handle; + session->transport = transport; + + error =scsi_add_host (shost,NULL ); +if (error) + goto out_destroy_session; + +/* +* this is released in the dev's release function) +*/ +scsi_host_get (shost); +snprintf (session->dev.bus_id,BUS_ID_SIZE ,"session%u" , shost->host_no); + session->dev.parent = &shost->shost_gendev; + session->dev.release = iscsi_if_session_dev_release; + error =device_register (&session->dev); +if (error) { +printk (KERN_ERR "iscsi: could not register session%d's dev\n" , + shost->host_no); + goto out_remove_host; + } +transport_register_device (&session->dev); + +/* add this session to the list of active sessions */ +spin_lock_irqsave (&priv->session_lock, flags); +list_add (&session->list, &priv->sessions); +spin_unlock_irqrestore (&priv->session_lock, flags); + +return 0 ; + + out_remove_host: +scsi_remove_host (shost); + out_destroy_session: + transport->destroy_session (ev->r.c_session_ret.session_handle); + ev->r.c_session_ret.session_handle =iscsi_handle (NULL ); + out_host_put: +scsi_host_put (shost); + out_module_put: +module_put (transport->owner); +return error; +} + +static int +iscsi_if_destroy_session (struct iscsi_internal *priv,struct iscsi_uevent *ev) +{ +struct iscsi_transport *transport = priv->iscsi_transport; +struct Scsi_Host *shost; +struct iscsi_if_session *session; +unsigned long flags; +struct iscsi_if_conn *conn; +int error =0 ; + + shost =scsi_host_lookup (ev->u.d_session.sid); +if (shost ==ERR_PTR (-ENXIO )) +return -EEXIST ; + session =hostdata_session (shost->hostdata); + +/* check if we have active connections */ +spin_lock_irqsave (&connlock, flags); +list_for_each_entry (conn, &session->connections, session_list) { +if (conn->active) { +printk (KERN_ERR "iscsi%d: can not destroy session: " +"has active connection (%p)\n" , + shost->host_no,iscsi_ptr (conn->connh)); +spin_unlock_irqrestore (&connlock, flags); + error =EIO ; + goto out_release_ref; + } + } +spin_unlock_irqrestore (&connlock, flags); + +scsi_remove_host (shost); + transport->destroy_session (ev->u.d_session.session_handle); +transport_unregister_device (&session->dev); +device_unregister (&session->dev); + +/* remove this session from the list of active sessions */ +spin_lock_irqsave (&priv->session_lock, flags); +list_del (&session->list); +spin_unlock_irqrestore (&priv->session_lock, flags); + +/* ref from host alloc */ +scsi_host_put (shost); + out_release_ref: +/* ref from host lookup */ +scsi_host_put (shost); +return error; +} + +static void iscsi_if_conn_dev_release (struct device *dev) +{ +struct iscsi_if_conn *conn =iscsi_dev_to_if_conn (dev); +struct Scsi_Host *shost = conn->host; + +scsi_host_put (shost); +} + +static int +iscsi_if_create_conn (struct iscsi_transport *transport,struct iscsi_uevent *ev) +{ +struct iscsi_if_session *session; +struct Scsi_Host *shost; +struct iscsi_if_conn *conn; +unsigned long flags; +int error; + + shost =scsi_host_lookup (ev->u.c_conn.sid); +if (shost ==ERR_PTR (-ENXIO )) +return -EEXIST ; + session =hostdata_session (shost->hostdata); + + conn =kmalloc (sizeof (struct iscsi_if_conn ),GFP_KERNEL ); +if (!conn) { + error = -ENOMEM ; + goto out_release_ref; + } +memset (conn,0 ,sizeof (struct iscsi_if_conn )); +INIT_LIST_HEAD (&conn->session_list); +INIT_LIST_HEAD (&conn->conn_list); + conn->host = shost; + conn->transport = transport; + + error =mempool_zone_init (&conn->z_pdu,Z_MAX_PDU , +NLMSG_SPACE (sizeof (struct iscsi_uevent ) + +sizeof (struct iscsi_hdr ) + +DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH ), +Z_HIWAT_PDU ); +if (error) { +printk (KERN_ERR "iscsi%d: can not allocate pdu zone for new " +"conn\n" , shost->host_no); + goto out_free_conn; + } + error =mempool_zone_init (&conn->z_error,Z_MAX_ERROR , +NLMSG_SPACE (sizeof (struct iscsi_uevent )), +Z_HIWAT_ERROR ); +if (error) { +printk (KERN_ERR "iscsi%d: can not allocate error zone for " +"new conn\n" , shost->host_no); + goto out_free_pdu_pool; + } + + ev->r.handle = transport->create_conn (ev->u.c_conn.session_handle, + ev->u.c_conn.cid); +if (!ev->r.handle) { + error = -ENODEV ; + goto out_free_error_pool; + } + + conn->connh = ev->r.handle; + +/* +* this is released in the dev's release function +*/ +if (!scsi_host_get (shost)) + goto out_destroy_conn; +snprintf (conn->dev.bus_id,BUS_ID_SIZE ,"connection%d:%u" , + shost->host_no, ev->u.c_conn.cid); + conn->dev.parent = &session->dev; + conn->dev.release = iscsi_if_conn_dev_release; + error =device_register (&conn->dev); +if (error) { +printk (KERN_ERR "iscsi%d: could not register connections%u " +"dev\n" , shost->host_no, ev->u.c_conn.cid); + goto out_release_parent_ref; + } +transport_register_device (&conn->dev); + +spin_lock_irqsave (&connlock, flags); +list_add (&conn->conn_list, &connlist); +list_add (&conn->session_list, &session->connections); + conn->active =1 ; +spin_unlock_irqrestore (&connlock, flags); + +scsi_host_put (shost); +return 0 ; + + out_release_parent_ref: +scsi_host_put (shost); + out_destroy_conn: + transport->destroy_conn (ev->r.handle); + out_free_error_pool: +mempool_destroy (conn->z_error.pool); + out_free_pdu_pool: +mempool_destroy (conn->z_pdu.pool); + out_free_conn: +kfree (conn); + out_release_ref: +scsi_host_put (shost); +return error; +} + +static int +iscsi_if_destroy_conn (struct iscsi_transport *transport,struct iscsi_uevent *ev) +{ +unsigned long flags; +struct iscsi_if_conn *conn; + + conn =iscsi_if_find_conn (ev->u.d_conn.conn_handle); +if (!conn) +return -EEXIST ; + + transport->destroy_conn (ev->u.d_conn.conn_handle); + +spin_lock_irqsave (&connlock, flags); + conn->active =0 ; +list_del (&conn->conn_list); +spin_unlock_irqrestore (&connlock, flags); + +transport_unregister_device (&conn->dev); +device_unregister (&conn->dev); +return 0 ; +} + +static int +iscsi_if_get_stats (struct iscsi_transport *transport,struct sk_buff *skb, +struct nlmsghdr *nlh) +{ +struct iscsi_uevent *ev =NLMSG_DATA (nlh); +struct iscsi_stats *stats; +struct sk_buff *skbstat; +struct iscsi_if_conn *conn; +struct nlmsghdr *nlhstat; +struct iscsi_uevent *evstat; +int len =NLMSG_SPACE (sizeof (*ev) + +sizeof (struct iscsi_stats ) + +sizeof (struct iscsi_stats_custom ) * +ISCSI_STATS_CUSTOM_MAX ); +int err =0 ; + + conn =iscsi_if_find_conn (ev->u.get_stats.conn_handle); +if (!conn) +return -EEXIST ; + +do { +int actual_size; + +mempool_zone_complete (&conn->z_pdu); + + skbstat =mempool_zone_get_skb (&conn->z_pdu); +if (!skbstat) { +printk (KERN_ERR "iscsi%d: can not deliver stats: OOM\n" , + conn->host->host_no); +return -ENOMEM ; + } + + nlhstat =__nlmsg_put (skbstat, daemon_pid,0 ,0 , + (len -sizeof (*nlhstat))); + 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 ; + evstat->u.get_stats.conn_handle = + ev->u.get_stats.conn_handle; + stats = (struct iscsi_stats *) + ((char *)evstat +sizeof (*evstat)); +memset (stats,0 ,sizeof (*stats)); + + transport->get_stats (ev->u.get_stats.conn_handle, stats); + 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); +skb_trim (skb,NLMSG_ALIGN (actual_size)); + nlhstat->nlmsg_len = actual_size; + + err =iscsi_unicast_skb (&conn->z_pdu, skbstat); + }while (err <0 && err != -ECONNREFUSED ); + +return err; +} + +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; + +if (NETLINK_CREDS (skb)->uid) +return -EPERM ; + + priv =iscsi_if_transport_lookup (iscsi_ptr (ev->transport_handle)); +if (priv) + transport = priv->iscsi_transport; +else if (nlh->nlmsg_type !=ISCSI_UEVENT_TRANS_LIST ) +return -EEXIST ; + + daemon_pid =NETLINK_CREDS (skb)->pid; + +switch (nlh->nlmsg_type) { +case ISCSI_UEVENT_TRANS_LIST : { +unsigned long flags; + +/* +* this will always succeed for now since there +* is only one transport. We can kill this event +* and just export the info in sysfs when we settle +* the iscsi sysfs layout debate +*/ +spin_lock_irqsave (&iscsi_transport_lock, flags); +list_for_each_entry (priv, &iscsi_transports, list) { + ev->r.t_list.elements[0 ].trans_handle = +iscsi_handle (priv->iscsi_transport); +strncpy (ev->r.t_list.elements[0 ].name, + priv->iscsi_transport->name, +ISCSI_TRANSPORT_NAME_MAXLEN ); + } +spin_unlock_irqrestore (&iscsi_transport_lock, flags); + + }break ; +case ISCSI_UEVENT_CREATE_SESSION : + err =iscsi_if_create_session (priv, ev); +break ; +case ISCSI_UEVENT_DESTROY_SESSION : + err =iscsi_if_destroy_session (priv, ev); +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 : +if (!iscsi_if_find_conn (ev->u.b_conn.conn_handle)) +return -EEXIST ; + ev->r.retcode = transport->bind_conn ( + ev->u.b_conn.session_handle, + ev->u.b_conn.conn_handle, + ev->u.b_conn.transport_fd, + ev->u.b_conn.is_leading); +break ; +case ISCSI_UEVENT_SET_PARAM : +if (!iscsi_if_find_conn (ev->u.set_param.conn_handle)) +return -EEXIST ; + ev->r.retcode = transport->set_param ( + ev->u.set_param.conn_handle, + ev->u.set_param.param, ev->u.set_param.value); +break ; +case ISCSI_UEVENT_START_CONN : +if (!iscsi_if_find_conn (ev->u.start_conn.conn_handle)) +return -EEXIST ; + ev->r.retcode = transport->start_conn ( + ev->u.start_conn.conn_handle); +break ; +case ISCSI_UEVENT_STOP_CONN : +if (!iscsi_if_find_conn (ev->u.stop_conn.conn_handle)) +return -EEXIST ; + transport->stop_conn (ev->u.stop_conn.conn_handle, + ev->u.stop_conn.flag); +break ; +case ISCSI_UEVENT_SEND_PDU : +if (!iscsi_if_find_conn (ev->u.send_pdu.conn_handle)) +return -EEXIST ; + ev->r.retcode = transport->send_pdu ( + ev->u.send_pdu.conn_handle, + (struct iscsi_hdr *)((char *)ev +sizeof (*ev)), + (char *)ev +sizeof (*ev) + ev->u.send_pdu.hdr_size, + ev->u.send_pdu.data_size); +break ; +case ISCSI_UEVENT_GET_STATS : + err =iscsi_if_get_stats (transport, skb, nlh); +break ; +default : + err = -EINVAL ; +break ; + } + +return err; +} + +/* Get message from skb (based on rtnetlink_rcv_skb). Each message is +* processed by iscsi_if_recv_msg. Malformed skbs with wrong length are +* discarded silently. */ +static void +iscsi_if_rx (struct sock *sk,int len) +{ +struct sk_buff *skb; + +down (&rx_queue_sema); +while ((skb =skb_dequeue (&sk->sk_receive_queue)) !=NULL ) { +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 ; + } + ev =NLMSG_DATA (nlh); + rlen =NLMSG_ALIGN (nlh->nlmsg_len); +if (rlen > skb->len) + rlen = skb->len; + 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)); +if (atomic_read (&z_reply.allocated) >= + z_reply.hiwat) + ev->iferror = -ENOMEM ; + }while (err <0 && err != -ECONNREFUSED ); +skb_pull (skb, rlen); + } +kfree_skb (skb); + } +up (&rx_queue_sema); +} +/* -* Again, this is used for iSCSI names. Normally, we follow -* the transport class convention of having the lld set -* the field, but in these cases the value is too large. +* iSCSI connection attrs */ -#define iscsi_host_show_str_fn (field) \ - \ +#define iscsi_conn_int_attr_show (param, format) \ static ssize_t \ -show_host_str_##field(struct class_device *cdev, char *buf) \ +show_conn_int_param_##param(struct class_device *cdev, char *buf) \ { \ - int ret = 0; \ - struct Scsi_Host *shost = transport_class_to_shost(cdev); \ - struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ + uint32_t value = 0; \ + struct iscsi_if_conn *conn = iscsi_cdev_to_if_conn(cdev); \ + struct iscsi_internal *priv; \ \ - if (i->fnt->get_##field) \ - ret = i->fnt->get_##field(shost, buf, PAGE_SIZE); \ - return ret; \ + priv = to_iscsi_internal(conn->host->transportt); \ + if (priv->param_mask & (1 << param)) \ + priv->iscsi_transport->get_param(conn->connh, param, &value); \ + return snprintf(buf, 20, format"\n", value); \ } -#define iscsi_host_rd_str_attr (field) \ - iscsi_host_show_str_fn(field) \ -static CLASS_DEVICE_ATTR(field, S_IRUGO, show_host_str_##field, NULL); +#define iscsi_conn_int_attr (field, param, format) \ + iscsi_conn_int_attr_show(param, format) \ +static CLASS_DEVICE_ATTR(field, S_IRUGO, show_conn_int_param_##param, NULL); + +iscsi_conn_int_attr (max_recv_dlength,ISCSI_PARAM_MAX_RECV_DLENGTH ,"%u" ); +iscsi_conn_int_attr (max_xmit_dlength,ISCSI_PARAM_MAX_XMIT_DLENGTH ,"%u" ); +iscsi_conn_int_attr (header_digest,ISCSI_PARAM_HDRDGST_EN ,"%d" ); +iscsi_conn_int_attr (data_digest,ISCSI_PARAM_DATADGST_EN ,"%d" ); +iscsi_conn_int_attr (ifmarker,ISCSI_PARAM_IFMARKER_EN ,"%d" ); +iscsi_conn_int_attr (ofmarker,ISCSI_PARAM_OFMARKER_EN ,"%d" ); -iscsi_host_rd_str_attr (initiator_name); -iscsi_host_rd_str_attr (initiator_alias); +/* +* iSCSI session attrs +*/ +#define iscsi_session_int_attr_show (param, format) \ +static ssize_t \ +show_session_int_param_##param(struct class_device *cdev, char *buf) \ +{ \ + uint32_t value = 0; \ + struct iscsi_if_session *session = iscsi_cdev_to_if_session(cdev); \ + struct Scsi_Host *shost = iscsi_if_session_to_shost(session); \ + struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \ + struct iscsi_if_conn *conn = NULL; \ + unsigned long flags; \ + \ + spin_lock_irqsave(&connlock, flags); \ + if (!list_empty(&session->connections)) { \ + conn = list_entry(session->connections.next, \ + struct iscsi_if_conn, session_list); \ + spin_unlock_irqrestore(&connlock, flags); \ + \ + if (conn && (priv->param_mask & (1 << param))) \ + priv->iscsi_transport->get_param(conn->connh, \ + param, &value);\ + } \ + return snprintf(buf, 20, format"\n", value); \ +} -#define SETUP_SESSION_RD_ATTR (field) \ - if (i->fnt->show_##field) { \ - i->session_attrs[count] = &class_device_attr_##field; \ +#define iscsi_session_int_attr (field, param, format) \ + iscsi_session_int_attr_show(param, format) \ +static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_int_param_##param, NULL); + +iscsi_session_int_attr (initial_r2t,ISCSI_PARAM_INITIAL_R2T_EN ,"%d" ); +iscsi_session_int_attr (max_outstanding_r2t,ISCSI_PARAM_MAX_R2T ,"%hu" ); +iscsi_session_int_attr (immediate_data,ISCSI_PARAM_IMM_DATA_EN ,"%d" ); +iscsi_session_int_attr (first_burst_len,ISCSI_PARAM_FIRST_BURST ,"%u" ); +iscsi_session_int_attr (max_burst_len,ISCSI_PARAM_MAX_BURST ,"%u" ); +iscsi_session_int_attr (data_pdu_in_order,ISCSI_PARAM_PDU_INORDER_EN ,"%d" ); +iscsi_session_int_attr (data_seq_in_order,ISCSI_PARAM_DATASEQ_INORDER_EN ,"%d" ); +iscsi_session_int_attr (erl,ISCSI_PARAM_ERL ,"%d" ); + +#define SETUP_SESSION_RD_ATTR (field, param) \ + if (priv->param_mask & (1 << param)) { \ + priv->session_attrs[count] = &class_device_attr_##field;\ count++; \ } -#define SETUP_HOST_RD_ATTR (field) \ - if (i->fnt->show_##field) { \ - i->host_attrs[count] = &class_device_attr_##field; \ +#define SETUP_CONN_RD_ATTR (field, param) \ + if (priv->param_mask & (1 << param)) { \ + priv->conn_attrs[count] = &class_device_attr_##field; \ count++; \ } -static int iscsi_host_match (struct attribute_container *cont, -struct device *dev) +static int iscsi_is_session_dev (const struct device *dev) { +return dev->release == iscsi_if_session_dev_release; +} + +static int iscsi_session_match (struct attribute_container *cont, +struct device *dev) +{ +struct iscsi_if_session *session;struct Scsi_Host *shost; -struct iscsi_internal *i; +struct iscsi_internal *priv; + +if (!iscsi_is_session_dev (dev)) +return 0 ; -if (!scsi_is_host_device (dev)) + session =iscsi_dev_to_if_session (dev); + shost =iscsi_if_session_to_shost (session); +if (!shost->transportt)return 0 ; - shost =dev_to_shost (dev); -if (!shost->transportt || shost->transportt->host_attrs.ac.class - != &iscsi_host_class.class) + priv =to_iscsi_internal (shost->transportt); +if (priv->session_cont.ac.class != &iscsi_session_class.class)return 0 ; - i =to_iscsi_internal (shost->transportt); - -return &i->t.host_attrs.ac == cont; +return &priv->session_cont.ac == cont; } -static int iscsi_target_match (struct attribute_container *cont, -struct device *dev) +static int iscsi_is_conn_dev (const struct device *dev) { +return dev->release == iscsi_if_conn_dev_release; +} + +static int iscsi_conn_match (struct attribute_container *cont, +struct device *dev) +{ +struct iscsi_if_conn *conn;struct Scsi_Host *shost; -struct iscsi_internal *i; +struct iscsi_internal *priv; + +if (!iscsi_is_conn_dev (dev)) +return 0 ; -if (!scsi_is_target_device (dev)) + conn =iscsi_dev_to_if_conn (dev); + shost = conn->host; +if (!shost->transportt)return 0 ; - shost =dev_to_shost (dev->parent); -if (!shost->transportt || shost->transportt->host_attrs.ac.class - != &iscsi_host_class.class) + priv =to_iscsi_internal (shost->transportt); +if (priv->conn_cont.ac.class != &iscsi_connection_class.class)return 0 ; - i =to_iscsi_internal (shost->transportt); - -return &i->t.target_attrs.ac == cont; +return &priv->conn_cont.ac == cont; } -struct scsi_transport_template * -iscsi_attach_transport (struct iscsi_function_template *fnt) +int iscsi_register_transport (struct iscsi_transport *tt) { -struct iscsi_internal *i =kmalloc (sizeof (struct iscsi_internal ), -GFP_KERNEL ); +struct iscsi_internal *priv; +unsigned long flags;int count =0 ; -if (unlikely (!i)) -return NULL ; +BUG_ON (!tt); -memset (i,0 ,sizeof (struct iscsi_internal )); - i->fnt = fnt; + priv =iscsi_if_transport_lookup (tt); +if (priv) +return -EEXIST ; + + priv =kmalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; +memset (priv,0 ,sizeof (*priv)); +INIT_LIST_HEAD (&priv->list); +INIT_LIST_HEAD (&priv->sessions); +spin_lock_init (&priv->session_lock); + priv->iscsi_transport = tt; + +/* setup parameters mask */ + priv->param_mask =0xFFFFFFFF ; +if (!(tt->caps &CAP_MULTI_R2T )) + priv->param_mask &= ~(1 <<ISCSI_PARAM_MAX_R2T ); +if (!(tt->caps &CAP_HDRDGST )) + priv->param_mask &= ~(1 <<ISCSI_PARAM_HDRDGST_EN ); +if (!(tt->caps &CAP_DATADGST )) + priv->param_mask &= ~(1 <<ISCSI_PARAM_DATADGST_EN ); +if (!(tt->caps &CAP_MARKERS )) { + priv->param_mask &= ~(1 <<ISCSI_PARAM_IFMARKER_EN ); + priv->param_mask &= ~(1 <<ISCSI_PARAM_OFMARKER_EN ); + } - i->t.target_attrs.ac.attrs = &i->session_attrs[0 ]; - i->t.target_attrs.ac.class = &iscsi_transport_class.class; - i->t.target_attrs.ac.match = iscsi_target_match; -transport_container_register (&i->t.target_attrs); - i->t.target_size =sizeof (struct iscsi_class_session ); - -SETUP_SESSION_RD_ATTR (tsih); -SETUP_SESSION_RD_ATTR (isid); -SETUP_SESSION_RD_ATTR (header_digest); -SETUP_SESSION_RD_ATTR (data_digest); -SETUP_SESSION_RD_ATTR (target_name); -SETUP_SESSION_RD_ATTR (target_alias); -SETUP_SESSION_RD_ATTR (port); -SETUP_SESSION_RD_ATTR (tpgt); -SETUP_SESSION_RD_ATTR (ip_address); -SETUP_SESSION_RD_ATTR (initial_r2t); -SETUP_SESSION_RD_ATTR (immediate_data); -SETUP_SESSION_RD_ATTR (max_recv_data_segment_len); -SETUP_SESSION_RD_ATTR (max_burst_len); -SETUP_SESSION_RD_ATTR (first_burst_len); -SETUP_SESSION_RD_ATTR (def_time2wait); -SETUP_SESSION_RD_ATTR (def_time2retain); -SETUP_SESSION_RD_ATTR (max_outstanding_r2t); -SETUP_SESSION_RD_ATTR (data_pdu_in_order); -SETUP_SESSION_RD_ATTR (data_sequence_in_order); -SETUP_SESSION_RD_ATTR (erl); +/* 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); + +SETUP_CONN_RD_ATTR (max_recv_dlength,ISCSI_PARAM_MAX_RECV_DLENGTH ); +SETUP_CONN_RD_ATTR (max_xmit_dlength,ISCSI_PARAM_MAX_XMIT_DLENGTH ); +SETUP_CONN_RD_ATTR (header_digest,ISCSI_PARAM_HDRDGST_EN ); +SETUP_CONN_RD_ATTR (data_digest,ISCSI_PARAM_DATADGST_EN ); +SETUP_CONN_RD_ATTR (ifmarker,ISCSI_PARAM_IFMARKER_EN ); +SETUP_CONN_RD_ATTR (ofmarker,ISCSI_PARAM_OFMARKER_EN ); -BUG_ON (count >ISCSI_SESSION_ATTRS ); - i->session_attrs[count] =NULL ; +BUG_ON (count >ISCSI_CONN_ATTRS ); + priv->conn_attrs[count] =NULL ; + count =0 ; - i->t.host_attrs.ac.attrs = &i->host_attrs[0 ]; - i->t.host_attrs.ac.class = &iscsi_host_class.class; - i->t.host_attrs.ac.match = iscsi_host_match; -transport_container_register (&i->t.host_attrs); - i->t.host_size =0 ; +/* 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); + +SETUP_SESSION_RD_ATTR (initial_r2t,ISCSI_PARAM_INITIAL_R2T_EN ); +SETUP_SESSION_RD_ATTR (max_outstanding_r2t,ISCSI_PARAM_MAX_R2T ); +SETUP_SESSION_RD_ATTR (immediate_data,ISCSI_PARAM_IMM_DATA_EN ); +SETUP_SESSION_RD_ATTR (first_burst_len,ISCSI_PARAM_FIRST_BURST ); +SETUP_SESSION_RD_ATTR (max_burst_len,ISCSI_PARAM_MAX_BURST ); +SETUP_SESSION_RD_ATTR (data_pdu_in_order,ISCSI_PARAM_PDU_INORDER_EN ); + SETUP_SESSION_RD_ATTR(data_seq_in_order,ISCSI_PARAM_DATASEQ_INORDER_EN) +SETUP_SESSION_RD_ATTR (erl,ISCSI_PARAM_ERL ); - count =0 ; -SETUP_HOST_RD_ATTR (initiator_name); -SETUP_HOST_RD_ATTR (initiator_alias); +BUG_ON (count >ISCSI_SESSION_ATTRS ); + priv->session_attrs[count] =NULL ; -BUG_ON (count >ISCSI_HOST_ATTRS ); - i->host_attrs[count] =NULL ; +spin_lock_irqsave (&iscsi_transport_lock, flags); +list_add (&priv->list, &iscsi_transports); +spin_unlock_irqrestore (&iscsi_transport_lock, flags); -return &i->t; +printk (KERN_NOTICE "iscsi: registered transport (%s)\n" , tt->name); +return 0 ; } +EXPORT_SYMBOL_GPL (iscsi_register_transport); + +int iscsi_unregister_transport (struct iscsi_transport *tt) +{ +struct iscsi_internal *priv; +unsigned long flags; + +BUG_ON (!tt); + +down (&rx_queue_sema); + + priv =iscsi_if_transport_lookup (tt); +BUG_ON (!priv); + +spin_lock_irqsave (&priv->session_lock, flags); +if (!list_empty (&priv->sessions)) { +spin_unlock_irqrestore (&priv->session_lock, flags); +up (&rx_queue_sema); +return -EPERM ; + } +spin_unlock_irqrestore (&priv->session_lock, flags); + +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); +kfree (priv); +up (&rx_queue_sema); -EXPORT_SYMBOL (iscsi_attach_transport); +return 0 ; +} +EXPORT_SYMBOL_GPL (iscsi_unregister_transport); -void iscsi_release_transport (struct scsi_transport_template *t) +static int +iscsi_rcv_nl_event (struct notifier_block *this,unsigned long event,void *ptr) { -struct iscsi_internal *i =to_iscsi_internal (t); +struct netlink_notify *n = ptr; + +if (event ==NETLINK_URELEASE && + n->protocol ==NETLINK_ISCSI && n->pid) { +struct iscsi_if_conn *conn; +unsigned long flags; + +mempool_zone_complete (&z_reply); +spin_lock_irqsave (&connlock, flags); +list_for_each_entry (conn, &connlist, conn_list) { +mempool_zone_complete (&conn->z_error); +mempool_zone_complete (&conn->z_pdu); + } +spin_unlock_irqrestore (&connlock, flags); + } -transport_container_unregister (&i->t.target_attrs); -transport_container_unregister (&i->t.host_attrs); - -kfree (i); +return NOTIFY_DONE ; } -EXPORT_SYMBOL (iscsi_release_transport); +static struct notifier_block iscsi_nl_notifier = { + .notifier_call = iscsi_rcv_nl_event, +};static __init int iscsi_transport_init (void ) { -int err =transport_class_register (&iscsi_transport_class); +int err; + err =transport_class_register (&iscsi_connection_class);if (err)return err; -return transport_class_register (&iscsi_host_class); + + err =transport_class_register (&iscsi_session_class); +if (err) + goto unregister_conn_class; + +netlink_register_notifier (&iscsi_nl_notifier); + nls =netlink_kernel_create (NETLINK_ISCSI , iscsi_if_rx); +if (!nls) { + err = -ENOBUFS ; + goto unregister_notifier; + } + + err =mempool_zone_init (&z_reply,Z_MAX_REPLY , +NLMSG_SPACE (sizeof (struct iscsi_uevent )),Z_HIWAT_REPLY ); +if (!err) +return 0 ; + +sock_release (nls->sk_socket); + unregister_notifier: +netlink_unregister_notifier (&iscsi_nl_notifier); + unregister_conn_class: +transport_class_unregister (&iscsi_connection_class); +return err; } static void __exit iscsi_transport_exit(void) { -transport_class_unregister (&iscsi_host_class); -transport_class_unregister (&iscsi_transport_class); +mempool_destroy (z_reply.pool); +sock_release (nls->sk_socket); +netlink_unregister_notifier (&iscsi_nl_notifier); +transport_class_unregister (&iscsi_connection_class); +transport_class_unregister (&iscsi_session_class); }module_init (iscsi_transport_init);module_exit (iscsi_transport_exit); -MODULE_AUTHOR ("Mike Christie" ); -MODULE_DESCRIPTION ("iSCSI Transport Attributes" ); +MODULE_AUTHOR ("Mike Christie <[email protected]>, " +"Dmitry Yusupov <[email protected]>, " +"Alex Aizman <[email protected]>" ); +MODULE_DESCRIPTION ("iSCSI Transport Interface" );MODULE_LICENSE ("GPL" );
include/scsi/scsi_transport_iscsi.h +57 -147
223 lines not on this page · raw message