X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_transport_iscsi.c;h=2340e2c5c02178452ce79e4fe93ab76a6e86068e;hb=5e7facb77ff4b6961d936773fb1f175f7abf76b7;hp=fac7534f3ec4a88237c7e7b6ca25d48ebb7034d6;hpb=4c851879312702456c7fbd594f19a7a9d991c252;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index fac7534..2340e2c 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -30,23 +30,24 @@ #include #include -#define ISCSI_SESSION_ATTRS 19 +#define ISCSI_SESSION_ATTRS 21 #define ISCSI_CONN_ATTRS 13 #define ISCSI_HOST_ATTRS 4 -#define ISCSI_TRANSPORT_VERSION "2.0-868" + +#define ISCSI_TRANSPORT_VERSION "2.0-870" struct iscsi_internal { int daemon_pid; struct scsi_transport_template t; struct iscsi_transport *iscsi_transport; struct list_head list; - struct class_device cdev; + struct device dev; - struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1]; + struct device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1]; struct transport_container conn_cont; - struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1]; + struct device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1]; struct transport_container session_cont; - struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1]; + struct device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1]; }; static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ @@ -63,12 +64,12 @@ 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) +#define dev_to_iscsi_internal(_dev) \ + container_of(_dev, struct iscsi_internal, dev) -static void iscsi_transport_release(struct class_device *cdev) +static void iscsi_transport_release(struct device *dev) { - struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); + struct iscsi_internal *priv = dev_to_iscsi_internal(dev); kfree(priv); } @@ -78,37 +79,33 @@ static void iscsi_transport_release(struct class_device *cdev) */ static struct class iscsi_transport_class = { .name = "iscsi_transport", - .release = iscsi_transport_release, + .dev_release = iscsi_transport_release, }; static ssize_t -show_transport_handle(struct class_device *cdev, char *buf) +show_transport_handle(struct device *dev, struct device_attribute *attr, + char *buf) { - struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); + struct iscsi_internal *priv = dev_to_iscsi_internal(dev); return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport)); } -static CLASS_DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL); +static 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) \ +show_transport_##name(struct device *dev, \ + struct device_attribute *attr,char *buf) \ { \ - struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); \ + struct iscsi_internal *priv = dev_to_iscsi_internal(dev); \ return sprintf(buf, format"\n", priv->iscsi_transport->name); \ } \ -static CLASS_DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL); +static 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, + &dev_attr_handle.attr, + &dev_attr_caps.attr, NULL, }; @@ -116,42 +113,146 @@ static struct attribute_group iscsi_transport_group = { .attrs = iscsi_transport_attrs, }; +/* + * iSCSI endpoint attrs + */ +#define iscsi_dev_to_endpoint(_dev) \ + container_of(_dev, struct iscsi_endpoint, dev) +#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store) \ +struct device_attribute dev_attr_##_prefix##_##_name = \ + __ATTR(_name,_mode,_show,_store) -static int iscsi_setup_host(struct transport_container *tc, struct device *dev, - struct class_device *cdev) +static void iscsi_endpoint_release(struct device *dev) { - struct Scsi_Host *shost = dev_to_shost(dev); - struct iscsi_host *ihost = shost->shost_data; + struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); + kfree(ep); +} - memset(ihost, 0, sizeof(*ihost)); - INIT_LIST_HEAD(&ihost->sessions); - mutex_init(&ihost->mutex); - atomic_set(&ihost->nr_scans, 0); +static struct class iscsi_endpoint_class = { + .name = "iscsi_endpoint", + .dev_release = iscsi_endpoint_release, +}; - snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d", - shost->host_no); - ihost->scan_workq = create_singlethread_workqueue( - ihost->scan_workq_name); - if (!ihost->scan_workq) - return -ENOMEM; - return 0; +static ssize_t +show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); + return sprintf(buf, "%llu\n", (unsigned long long) ep->id); +} +static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL); + +static struct attribute *iscsi_endpoint_attrs[] = { + &dev_attr_ep_handle.attr, + NULL, +}; + +static struct attribute_group iscsi_endpoint_group = { + .attrs = iscsi_endpoint_attrs, +}; + +#define ISCSI_MAX_EPID -1 + +static int iscsi_match_epid(struct device *dev, void *data) +{ + struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); + uint64_t *epid = (uint64_t *) data; + + return *epid == ep->id; +} + +struct iscsi_endpoint * +iscsi_create_endpoint(int dd_size) +{ + struct device *dev; + struct iscsi_endpoint *ep; + uint64_t id; + int err; + + for (id = 1; id < ISCSI_MAX_EPID; id++) { + dev = class_find_device(&iscsi_endpoint_class, NULL, &id, + iscsi_match_epid); + if (!dev) + break; + } + if (id == ISCSI_MAX_EPID) { + printk(KERN_ERR "Too many connections. Max supported %u\n", + ISCSI_MAX_EPID - 1); + return NULL; + } + + ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL); + if (!ep) + return NULL; + + ep->id = id; + ep->dev.class = &iscsi_endpoint_class; + dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id); + err = device_register(&ep->dev); + if (err) + goto free_ep; + + err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group); + if (err) + goto unregister_dev; + + if (dd_size) + ep->dd_data = &ep[1]; + return ep; + +unregister_dev: + device_unregister(&ep->dev); + return NULL; + +free_ep: + kfree(ep); + return NULL; +} +EXPORT_SYMBOL_GPL(iscsi_create_endpoint); + +void iscsi_destroy_endpoint(struct iscsi_endpoint *ep) +{ + sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group); + device_unregister(&ep->dev); +} +EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint); + +struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) +{ + struct iscsi_endpoint *ep; + struct device *dev; + + dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, + iscsi_match_epid); + if (!dev) + return NULL; + + ep = iscsi_dev_to_endpoint(dev); + /* + * we can drop this now because the interface will prevent + * removals and lookups from racing. + */ + put_device(dev); + return ep; } +EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint); -static int iscsi_remove_host(struct transport_container *tc, struct device *dev, - struct class_device *cdev) +static int iscsi_setup_host(struct transport_container *tc, struct device *dev, + struct device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); - struct iscsi_host *ihost = shost->shost_data; + struct iscsi_cls_host *ihost = shost->shost_data; - destroy_workqueue(ihost->scan_workq); + memset(ihost, 0, sizeof(*ihost)); + atomic_set(&ihost->nr_scans, 0); + mutex_init(&ihost->mutex); return 0; } static DECLARE_TRANSPORT_CLASS(iscsi_host_class, "iscsi_host", iscsi_setup_host, - iscsi_remove_host, + NULL, NULL); static DECLARE_TRANSPORT_CLASS(iscsi_session_class, @@ -231,7 +332,7 @@ static struct { { ISCSI_SESSION_FREE, "FREE" }, }; -const char *iscsi_session_state_name(int state) +static const char *iscsi_session_state_name(int state) { int i; char *name = NULL; @@ -256,10 +357,10 @@ int iscsi_session_chkready(struct iscsi_cls_session *session) err = 0; break; case ISCSI_SESSION_FAILED: - err = DID_IMM_RETRY << 16; + err = DID_TRANSPORT_DISRUPTED << 16; break; case ISCSI_SESSION_FREE: - err = DID_NO_CONNECT << 16; + err = DID_TRANSPORT_FAILFAST << 16; break; default: err = DID_NO_CONNECT << 16; @@ -285,6 +386,24 @@ static int iscsi_is_session_dev(const struct device *dev) return dev->release == iscsi_session_release; } +static int iscsi_iter_session_fn(struct device *dev, void *data) +{ + void (* fn) (struct iscsi_cls_session *) = data; + + if (!iscsi_is_session_dev(dev)) + return 0; + fn(iscsi_dev_to_session(dev)); + return 0; +} + +void iscsi_host_for_each_session(struct Scsi_Host *shost, + void (*fn)(struct iscsi_cls_session *)) +{ + device_for_each_child(&shost->shost_gendev, fn, + iscsi_iter_session_fn); +} +EXPORT_SYMBOL_GPL(iscsi_host_for_each_session); + /** * iscsi_scan_finished - helper to report when running scans are done * @shost: scsi host @@ -295,7 +414,7 @@ static int iscsi_is_session_dev(const struct device *dev) */ int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time) { - struct iscsi_host *ihost = shost->shost_data; + struct iscsi_cls_host *ihost = shost->shost_data; /* * qla4xxx will have kicked off some session unblocks before calling * scsi_scan_host, so just wait for them to complete. @@ -304,42 +423,76 @@ int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time) } EXPORT_SYMBOL_GPL(iscsi_scan_finished); -static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, - uint id, uint lun) +struct iscsi_scan_data { + unsigned int channel; + unsigned int id; + unsigned int lun; +}; + +static int iscsi_user_scan_session(struct device *dev, void *data) { - struct iscsi_host *ihost = shost->shost_data; + struct iscsi_scan_data *scan_data = data; struct iscsi_cls_session *session; + struct Scsi_Host *shost; + struct iscsi_cls_host *ihost; + unsigned long flags; + unsigned int id; + + if (!iscsi_is_session_dev(dev)) + return 0; + + session = iscsi_dev_to_session(dev); + shost = iscsi_session_to_shost(session); + ihost = shost->shost_data; mutex_lock(&ihost->mutex); - list_for_each_entry(session, &ihost->sessions, host_list) { - if ((channel == SCAN_WILD_CARD || channel == 0) && - (id == SCAN_WILD_CARD || id == session->target_id)) - scsi_scan_target(&session->dev, 0, - session->target_id, lun, 1); + spin_lock_irqsave(&session->lock, flags); + if (session->state != ISCSI_SESSION_LOGGED_IN) { + spin_unlock_irqrestore(&session->lock, flags); + mutex_unlock(&ihost->mutex); + return 0; } - mutex_unlock(&ihost->mutex); + id = session->target_id; + spin_unlock_irqrestore(&session->lock, flags); + if (id != ISCSI_MAX_TARGET) { + if ((scan_data->channel == SCAN_WILD_CARD || + scan_data->channel == 0) && + (scan_data->id == SCAN_WILD_CARD || + scan_data->id == id)) + scsi_scan_target(&session->dev, 0, id, + scan_data->lun, 1); + } + mutex_unlock(&ihost->mutex); return 0; } +static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, + uint id, uint lun) +{ + struct iscsi_scan_data scan_data; + + scan_data.channel = channel; + scan_data.id = id; + scan_data.lun = lun; + + return device_for_each_child(&shost->shost_gendev, &scan_data, + iscsi_user_scan_session); +} + static void iscsi_scan_session(struct work_struct *work) { struct iscsi_cls_session *session = container_of(work, struct iscsi_cls_session, scan_work); struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost = shost->shost_data; - unsigned long flags; + struct iscsi_cls_host *ihost = shost->shost_data; + struct iscsi_scan_data scan_data; - spin_lock_irqsave(&session->lock, flags); - if (session->state != ISCSI_SESSION_LOGGED_IN) { - spin_unlock_irqrestore(&session->lock, flags); - goto done; - } - spin_unlock_irqrestore(&session->lock, flags); + scan_data.channel = 0; + scan_data.id = SCAN_WILD_CARD; + scan_data.lun = SCAN_WILD_CARD; - scsi_scan_target(&session->dev, 0, session->target_id, - SCAN_WILD_CARD, 1); -done: + iscsi_user_scan_session(&session->dev, &scan_data); atomic_dec(&ihost->nr_scans); } @@ -373,48 +526,72 @@ static void session_recovery_timedout(struct work_struct *work) scsi_target_unblock(&session->dev); } -void __iscsi_unblock_session(struct iscsi_cls_session *session) -{ - if (!cancel_delayed_work(&session->recovery_work)) - flush_workqueue(iscsi_eh_timer_workq); - scsi_target_unblock(&session->dev); -} - -void iscsi_unblock_session(struct iscsi_cls_session *session) +static void __iscsi_unblock_session(struct work_struct *work) { + struct iscsi_cls_session *session = + container_of(work, struct iscsi_cls_session, + unblock_work); struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost = shost->shost_data; + struct iscsi_cls_host *ihost = shost->shost_data; unsigned long flags; + /* + * The recovery and unblock work get run from the same workqueue, + * so try to cancel it if it was going to run after this unblock. + */ + cancel_delayed_work(&session->recovery_work); spin_lock_irqsave(&session->lock, flags); session->state = ISCSI_SESSION_LOGGED_IN; spin_unlock_irqrestore(&session->lock, flags); - - __iscsi_unblock_session(session); + /* start IO */ + scsi_target_unblock(&session->dev); /* * Only do kernel scanning if the driver is properly hooked into * the async scanning code (drivers like iscsi_tcp do login and * scanning from userspace). */ if (shost->hostt->scan_finished) { - if (queue_work(ihost->scan_workq, &session->scan_work)) + if (scsi_queue_work(shost, &session->scan_work)) atomic_inc(&ihost->nr_scans); } } + +/** + * iscsi_unblock_session - set a session as logged in and start IO. + * @session: iscsi session + * + * Mark a session as ready to accept IO. + */ +void iscsi_unblock_session(struct iscsi_cls_session *session) +{ + queue_work(iscsi_eh_timer_workq, &session->unblock_work); + /* + * make sure all the events have completed before tell the driver + * it is safe + */ + flush_workqueue(iscsi_eh_timer_workq); +} EXPORT_SYMBOL_GPL(iscsi_unblock_session); -void iscsi_block_session(struct iscsi_cls_session *session) +static void __iscsi_block_session(struct work_struct *work) { + struct iscsi_cls_session *session = + container_of(work, struct iscsi_cls_session, + block_work); unsigned long flags; spin_lock_irqsave(&session->lock, flags); session->state = ISCSI_SESSION_FAILED; spin_unlock_irqrestore(&session->lock, flags); - scsi_target_block(&session->dev); queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, session->recovery_tmo * HZ); } + +void iscsi_block_session(struct iscsi_cls_session *session) +{ + queue_work(iscsi_eh_timer_workq, &session->block_work); +} EXPORT_SYMBOL_GPL(iscsi_block_session); static void __iscsi_unbind_session(struct work_struct *work) @@ -423,36 +600,32 @@ static void __iscsi_unbind_session(struct work_struct *work) container_of(work, struct iscsi_cls_session, unbind_work); struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost = shost->shost_data; + struct iscsi_cls_host *ihost = shost->shost_data; + unsigned long flags; /* Prevent new scans and make sure scanning is not in progress */ mutex_lock(&ihost->mutex); - if (list_empty(&session->host_list)) { + spin_lock_irqsave(&session->lock, flags); + if (session->target_id == ISCSI_MAX_TARGET) { + spin_unlock_irqrestore(&session->lock, flags); mutex_unlock(&ihost->mutex); return; } - list_del_init(&session->host_list); + session->target_id = ISCSI_MAX_TARGET; + spin_unlock_irqrestore(&session->lock, flags); mutex_unlock(&ihost->mutex); scsi_remove_target(&session->dev); iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); } -static int iscsi_unbind_session(struct iscsi_cls_session *session) -{ - struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost = shost->shost_data; - - return queue_work(ihost->scan_workq, &session->unbind_work); -} - struct iscsi_cls_session * -iscsi_alloc_session(struct Scsi_Host *shost, - struct iscsi_transport *transport) +iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, + int dd_size) { struct iscsi_cls_session *session; - session = kzalloc(sizeof(*session) + transport->sessiondata_size, + session = kzalloc(sizeof(*session) + dd_size, GFP_KERNEL); if (!session) return NULL; @@ -461,8 +634,9 @@ iscsi_alloc_session(struct Scsi_Host *shost, session->recovery_tmo = 120; session->state = ISCSI_SESSION_FREE; INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); - INIT_LIST_HEAD(&session->host_list); INIT_LIST_HEAD(&session->sess_list); + INIT_WORK(&session->unblock_work, __iscsi_unblock_session); + INIT_WORK(&session->block_work, __iscsi_block_session); INIT_WORK(&session->unbind_work, __iscsi_unbind_session); INIT_WORK(&session->scan_work, iscsi_scan_session); spin_lock_init(&session->lock); @@ -472,25 +646,59 @@ iscsi_alloc_session(struct Scsi_Host *shost, session->dev.parent = &shost->shost_gendev; session->dev.release = iscsi_session_release; device_initialize(&session->dev); - if (transport->sessiondata_size) + if (dd_size) session->dd_data = &session[1]; return session; } EXPORT_SYMBOL_GPL(iscsi_alloc_session); +static int iscsi_get_next_target_id(struct device *dev, void *data) +{ + struct iscsi_cls_session *session; + unsigned long flags; + int err = 0; + + if (!iscsi_is_session_dev(dev)) + return 0; + + session = iscsi_dev_to_session(dev); + spin_lock_irqsave(&session->lock, flags); + if (*((unsigned int *) data) == session->target_id) + err = -EEXIST; + spin_unlock_irqrestore(&session->lock, flags); + return err; +} + int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) { struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost; + struct iscsi_cls_host *ihost; unsigned long flags; + unsigned int id = target_id; int err; ihost = shost->shost_data; session->sid = atomic_add_return(1, &iscsi_session_nr); - session->target_id = target_id; - snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", - session->sid); + if (id == ISCSI_MAX_TARGET) { + for (id = 0; id < ISCSI_MAX_TARGET; id++) { + err = device_for_each_child(&shost->shost_gendev, &id, + iscsi_get_next_target_id); + if (!err) + break; + } + + if (id == ISCSI_MAX_TARGET) { + iscsi_cls_session_printk(KERN_ERR, session, + "Too many iscsi targets. Max " + "number of targets is %d.\n", + ISCSI_MAX_TARGET - 1); + goto release_host; + } + } + session->target_id = id; + + dev_set_name(&session->dev, "session%u", session->sid); err = device_add(&session->dev); if (err) { iscsi_cls_session_printk(KERN_ERR, session, @@ -503,10 +711,6 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) list_add(&session->sess_list, &sesslist); spin_unlock_irqrestore(&sesslock, flags); - mutex_lock(&ihost->mutex); - list_add(&session->host_list, &ihost->sessions); - mutex_unlock(&ihost->mutex); - iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION); return 0; @@ -520,18 +724,18 @@ EXPORT_SYMBOL_GPL(iscsi_add_session); * iscsi_create_session - create iscsi class session * @shost: scsi host * @transport: iscsi transport + * @dd_size: private driver data size * @target_id: which target * * This can be called from a LLD or iscsi_transport. */ struct iscsi_cls_session * -iscsi_create_session(struct Scsi_Host *shost, - struct iscsi_transport *transport, - unsigned int target_id) +iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport, + int dd_size, unsigned int target_id) { struct iscsi_cls_session *session; - session = iscsi_alloc_session(shost, transport); + session = iscsi_alloc_session(shost, transport, dd_size); if (!session) return NULL; @@ -567,7 +771,6 @@ static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data) void iscsi_remove_session(struct iscsi_cls_session *session) { struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost = shost->shost_data; unsigned long flags; int err; @@ -575,24 +778,25 @@ void iscsi_remove_session(struct iscsi_cls_session *session) list_del(&session->sess_list); spin_unlock_irqrestore(&sesslock, flags); + /* make sure there are no blocks/unblocks queued */ + flush_workqueue(iscsi_eh_timer_workq); + /* make sure the timedout callout is not running */ + if (!cancel_delayed_work(&session->recovery_work)) + flush_workqueue(iscsi_eh_timer_workq); /* * If we are blocked let commands flow again. The lld or iscsi * layer should set up the queuecommand to fail commands. + * We assume that LLD will not be calling block/unblock while + * removing the session. */ spin_lock_irqsave(&session->lock, flags); session->state = ISCSI_SESSION_FREE; spin_unlock_irqrestore(&session->lock, flags); - __iscsi_unblock_session(session); - __iscsi_unbind_session(&session->unbind_work); - /* flush running scans */ - flush_workqueue(ihost->scan_workq); - /* - * If the session dropped while removing devices then we need to make - * sure it is not blocked - */ - if (!cancel_delayed_work(&session->recovery_work)) - flush_workqueue(iscsi_eh_timer_workq); + scsi_target_unblock(&session->dev); + /* flush running scans then delete devices */ + scsi_flush_work(shost); + __iscsi_unbind_session(&session->unbind_work); /* hw iscsi may not have removed all connections from session */ err = device_for_each_child(&session->dev, NULL, @@ -632,6 +836,7 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session); /** * iscsi_create_conn - create iscsi class connection * @session: iscsi cls session + * @dd_size: private driver data size * @cid: connection id * * This can be called from a LLD or iscsi_transport. The connection @@ -644,18 +849,17 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session); * non-zero. */ struct iscsi_cls_conn * -iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) +iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) { struct iscsi_transport *transport = session->transport; struct iscsi_cls_conn *conn; unsigned long flags; int err; - conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL); + conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL); if (!conn) return NULL; - - if (transport->conndata_size) + if (dd_size) conn->dd_data = &conn[1]; INIT_LIST_HEAD(&conn->conn_list); @@ -666,8 +870,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) if (!get_device(&session->dev)) goto free_conn; - snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u", - session->sid, cid); + dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid); conn->dev.parent = &session->dev; conn->dev.release = iscsi_conn_release; err = device_register(&conn->dev); @@ -779,7 +982,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, skb = alloc_skb(len, GFP_ATOMIC); if (!skb) { - iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED); + iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED); iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver " "control PDU: OOM\n"); return -ENOMEM; @@ -800,25 +1003,18 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, } EXPORT_SYMBOL_GPL(iscsi_recv_pdu); -void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) +void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) { - struct iscsi_cls_session *session = iscsi_conn_to_session(conn); struct nlmsghdr *nlh; struct sk_buff *skb; struct iscsi_uevent *ev; struct iscsi_internal *priv; int len = NLMSG_SPACE(sizeof(*ev)); - unsigned long flags; priv = iscsi_if_transport_lookup(conn->transport); if (!priv) return; - spin_lock_irqsave(&session->lock, flags); - if (session->state == ISCSI_SESSION_LOGGED_IN) - session->state = ISCSI_SESSION_FAILED; - spin_unlock_irqrestore(&session->lock, flags); - skb = alloc_skb(len, GFP_ATOMIC); if (!skb) { iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored " @@ -839,7 +1035,7 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n", error); } -EXPORT_SYMBOL_GPL(iscsi_conn_error); +EXPORT_SYMBOL_GPL(iscsi_conn_error_event); static int iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, @@ -995,21 +1191,21 @@ int iscsi_session_event(struct iscsi_cls_session *session, EXPORT_SYMBOL_GPL(iscsi_session_event); static int -iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) +iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep, + struct iscsi_uevent *ev, uint32_t initial_cmdsn, + uint16_t cmds_max, uint16_t queue_depth) { struct iscsi_transport *transport = priv->iscsi_transport; struct iscsi_cls_session *session; - uint32_t hostno; + struct Scsi_Host *shost; - session = transport->create_session(transport, &priv->t, - ev->u.c_session.cmds_max, - ev->u.c_session.queue_depth, - ev->u.c_session.initial_cmdsn, - &hostno); + session = transport->create_session(ep, cmds_max, queue_depth, + initial_cmdsn); if (!session) return -ENOMEM; - ev->r.c_session_ret.host_no = hostno; + shost = iscsi_session_to_shost(session); + ev->r.c_session_ret.host_no = shost->host_no; ev->r.c_session_ret.sid = session->sid; return 0; } @@ -1084,6 +1280,7 @@ static int iscsi_if_transport_ep(struct iscsi_transport *transport, struct iscsi_uevent *ev, int msg_type) { + struct iscsi_endpoint *ep; struct sockaddr *dst_addr; int rc = 0; @@ -1093,22 +1290,33 @@ iscsi_if_transport_ep(struct iscsi_transport *transport, 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); + ep = transport->ep_connect(dst_addr, + ev->u.ep_connect.non_blocking); + if (IS_ERR(ep)) + return PTR_ERR(ep); + + ev->r.ep_connect_ret.handle = ep->id; 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, + ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle); + if (!ep) + return -EINVAL; + + ev->r.retcode = transport->ep_poll(ep, 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); + ep = iscsi_lookup_endpoint(ev->u.ep_disconnect.ep_handle); + if (!ep) + return -EINVAL; + + transport->ep_disconnect(ep); break; } return rc; @@ -1126,7 +1334,7 @@ iscsi_tgt_dscvr(struct iscsi_transport *transport, return -EINVAL; shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no); - if (IS_ERR(shost)) { + if (!shost) { printk(KERN_ERR "target discovery could not find host no %u\n", ev->u.tgt_dscvr.host_no); return -ENODEV; @@ -1152,7 +1360,7 @@ iscsi_set_host_param(struct iscsi_transport *transport, return -ENOSYS; shost = scsi_host_lookup(ev->u.set_host_param.host_no); - if (IS_ERR(shost)) { + if (!shost) { printk(KERN_ERR "set_host_param could not find host no %u\n", ev->u.set_host_param.host_no); return -ENODEV; @@ -1173,6 +1381,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) struct iscsi_internal *priv; struct iscsi_cls_session *session; struct iscsi_cls_conn *conn; + struct iscsi_endpoint *ep = NULL; priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); if (!priv) @@ -1186,7 +1395,22 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) switch (nlh->nlmsg_type) { case ISCSI_UEVENT_CREATE_SESSION: - err = iscsi_if_create_session(priv, ev); + err = iscsi_if_create_session(priv, ep, ev, + ev->u.c_session.initial_cmdsn, + ev->u.c_session.cmds_max, + ev->u.c_session.queue_depth); + break; + case ISCSI_UEVENT_CREATE_BOUND_SESSION: + ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle); + if (!ep) { + err = -EINVAL; + break; + } + + err = iscsi_if_create_session(priv, ep, ev, + ev->u.c_bound_session.initial_cmdsn, + ev->u.c_bound_session.cmds_max, + ev->u.c_bound_session.queue_depth); break; case ISCSI_UEVENT_DESTROY_SESSION: session = iscsi_session_lookup(ev->u.d_session.sid); @@ -1198,7 +1422,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case ISCSI_UEVENT_UNBIND_SESSION: session = iscsi_session_lookup(ev->u.d_session.sid); if (session) - iscsi_unbind_session(session); + scsi_queue_work(iscsi_session_to_shost(session), + &session->unbind_work); else err = -EINVAL; break; @@ -1317,11 +1542,8 @@ iscsi_if_rx(struct sk_buff *skb) mutex_unlock(&rx_queue_mutex); } -#define iscsi_cdev_to_conn(_cdev) \ - iscsi_dev_to_conn(_cdev->dev) - #define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store) \ -struct class_device_attribute class_device_attr_##_prefix##_##_name = \ +struct device_attribute dev_attr_##_prefix##_##_name = \ __ATTR(_name,_mode,_show,_store) /* @@ -1329,9 +1551,10 @@ struct class_device_attribute class_device_attr_##_prefix##_##_name = \ */ #define iscsi_conn_attr_show(param) \ static ssize_t \ -show_conn_param_##param(struct class_device *cdev, char *buf) \ +show_conn_param_##param(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ - struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev); \ + struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \ struct iscsi_transport *t = conn->transport; \ return t->get_conn_param(conn, param, buf); \ } @@ -1355,17 +1578,16 @@ iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS); iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO); iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO); -#define iscsi_cdev_to_session(_cdev) \ - iscsi_dev_to_session(_cdev->dev) - /* * iSCSI session attrs */ #define iscsi_session_attr_show(param, perm) \ static ssize_t \ -show_session_param_##param(struct class_device *cdev, char *buf) \ +show_session_param_##param(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ - struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \ + struct iscsi_cls_session *session = \ + iscsi_dev_to_session(dev->parent); \ struct iscsi_transport *t = session->transport; \ \ if (perm && !capable(CAP_SYS_ADMIN)) \ @@ -1395,11 +1617,14 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1); iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0); iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); +iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0); +iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0) static ssize_t -show_priv_session_state(struct class_device *cdev, char *buf) +show_priv_session_state(struct device *dev, struct device_attribute *attr, + char *buf) { - struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); + struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent); return sprintf(buf, "%s\n", iscsi_session_state_name(session->state)); } static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, @@ -1407,9 +1632,11 @@ static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, #define iscsi_priv_session_attr_show(field, format) \ static ssize_t \ -show_priv_session_##field(struct class_device *cdev, char *buf) \ +show_priv_session_##field(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ - struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\ + struct iscsi_cls_session *session = \ + iscsi_dev_to_session(dev->parent); \ return sprintf(buf, format"\n", session->field); \ } @@ -1424,9 +1651,10 @@ iscsi_priv_session_attr(recovery_tmo, "%d"); */ #define iscsi_host_attr_show(param) \ static ssize_t \ -show_host_param_##param(struct class_device *cdev, char *buf) \ +show_host_param_##param(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ - struct Scsi_Host *shost = transport_class_to_shost(cdev); \ + struct Scsi_Host *shost = transport_class_to_shost(dev); \ struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \ return priv->iscsi_transport->get_host_param(shost, param, buf); \ } @@ -1443,7 +1671,7 @@ iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME); #define SETUP_PRIV_SESSION_RD_ATTR(field) \ do { \ - priv->session_attrs[count] = &class_device_attr_priv_sess_##field; \ + priv->session_attrs[count] = &dev_attr_priv_sess_##field; \ count++; \ } while (0) @@ -1451,7 +1679,7 @@ do { \ #define SETUP_SESSION_RD_ATTR(field, param_flag) \ do { \ if (tt->param_mask & param_flag) { \ - priv->session_attrs[count] = &class_device_attr_sess_##field; \ + priv->session_attrs[count] = &dev_attr_sess_##field; \ count++; \ } \ } while (0) @@ -1459,7 +1687,7 @@ do { \ #define SETUP_CONN_RD_ATTR(field, param_flag) \ do { \ if (tt->param_mask & param_flag) { \ - priv->conn_attrs[count] = &class_device_attr_conn_##field; \ + priv->conn_attrs[count] = &dev_attr_conn_##field; \ count++; \ } \ } while (0) @@ -1467,7 +1695,7 @@ do { \ #define SETUP_HOST_RD_ATTR(field, param_flag) \ do { \ if (tt->host_param_mask & param_flag) { \ - priv->host_attrs[count] = &class_device_attr_host_##field; \ + priv->host_attrs[count] = &dev_attr_host_##field; \ count++; \ } \ } while (0) @@ -1557,22 +1785,23 @@ iscsi_register_transport(struct iscsi_transport *tt) priv->daemon_pid = -1; priv->iscsi_transport = tt; priv->t.user_scan = iscsi_user_scan; + priv->t.create_work_queue = 1; - priv->cdev.class = &iscsi_transport_class; - snprintf(priv->cdev.class_id, BUS_ID_SIZE, "%s", tt->name); - err = class_device_register(&priv->cdev); + priv->dev.class = &iscsi_transport_class; + dev_set_name(&priv->dev, "%s", tt->name); + err = device_register(&priv->dev); if (err) goto free_priv; - err = sysfs_create_group(&priv->cdev.kobj, &iscsi_transport_group); + err = sysfs_create_group(&priv->dev.kobj, &iscsi_transport_group); if (err) - goto unregister_cdev; + goto unregister_dev; /* 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->t.host_size = sizeof(struct iscsi_cls_host); transport_container_register(&priv->t.host_attrs); SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME); @@ -1630,6 +1859,8 @@ iscsi_register_transport(struct iscsi_transport *tt) SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT); SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO); SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO); + SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME); + SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME); SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo); SETUP_PRIV_SESSION_RD_ATTR(state); @@ -1643,8 +1874,9 @@ iscsi_register_transport(struct iscsi_transport *tt) printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name); return &priv->t; -unregister_cdev: - class_device_unregister(&priv->cdev); +unregister_dev: + device_unregister(&priv->dev); + return NULL; free_priv: kfree(priv); return NULL; @@ -1671,8 +1903,8 @@ int iscsi_unregister_transport(struct iscsi_transport *tt) transport_container_unregister(&priv->session_cont); transport_container_unregister(&priv->t.host_attrs); - sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group); - class_device_unregister(&priv->cdev); + sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group); + device_unregister(&priv->dev); mutex_unlock(&rx_queue_mutex); return 0; @@ -1692,10 +1924,14 @@ static __init int iscsi_transport_init(void) if (err) return err; - err = transport_class_register(&iscsi_host_class); + err = class_register(&iscsi_endpoint_class); if (err) goto unregister_transport_class; + err = transport_class_register(&iscsi_host_class); + if (err) + goto unregister_endpoint_class; + err = transport_class_register(&iscsi_connection_class); if (err) goto unregister_host_class; @@ -1704,8 +1940,8 @@ static __init int iscsi_transport_init(void) if (err) goto unregister_conn_class; - nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL, - THIS_MODULE); + nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, + NULL, THIS_MODULE); if (!nls) { err = -ENOBUFS; goto unregister_session_class; @@ -1725,6 +1961,8 @@ unregister_conn_class: transport_class_unregister(&iscsi_connection_class); unregister_host_class: transport_class_unregister(&iscsi_host_class); +unregister_endpoint_class: + class_unregister(&iscsi_endpoint_class); unregister_transport_class: class_unregister(&iscsi_transport_class); return err; @@ -1737,6 +1975,7 @@ static void __exit iscsi_transport_exit(void) transport_class_unregister(&iscsi_connection_class); transport_class_unregister(&iscsi_session_class); transport_class_unregister(&iscsi_host_class); + class_unregister(&iscsi_endpoint_class); class_unregister(&iscsi_transport_class); }