diff options
-rw-r--r-- | include/iscsi_if.h | 7 | ||||
-rw-r--r-- | kernel/Makefile | 2 | ||||
-rw-r--r-- | kernel/libiscsi.c | 4 | ||||
-rw-r--r-- | kernel/scsi_transport_iscsi.c | 257 | ||||
-rw-r--r-- | kernel/scsi_transport_iscsi.h | 7 | ||||
-rw-r--r-- | usr/initiator.c | 64 | ||||
-rw-r--r-- | usr/initiator.h | 5 | ||||
-rw-r--r-- | usr/iscsi_ipc.h | 2 | ||||
-rw-r--r-- | usr/iscsiadm.c | 2 | ||||
-rw-r--r-- | usr/netlink.c | 72 |
10 files changed, 267 insertions, 155 deletions
diff --git a/include/iscsi_if.h b/include/iscsi_if.h index 803e8d9..7820294 100644 --- a/include/iscsi_if.h +++ b/include/iscsi_if.h @@ -49,12 +49,15 @@ enum iscsi_uevent_e { ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15, ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16, + ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17, /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3, ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4, + ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5, + ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6, }; enum iscsi_tgt_dscvr { @@ -156,6 +159,10 @@ struct iscsi_uevent { uint32_t sid; uint32_t cid; } c_conn_ret; + struct msg_unbind_session { + uint32_t sid; + uint32_t host_no; + } unbind_session; struct msg_recv_req { uint32_t sid; uint32_t cid; diff --git a/kernel/Makefile b/kernel/Makefile index 80a4fdb..ba8a25d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -88,6 +88,8 @@ linux_2_6_21: has_20to21_patch linux_2_6_22: $(unpatch_code) +linux_2_6_23: $(unpatch_code) + do_unpatch_code: echo "Un-patching source code for use with linux-2.6.20 and up ..." patch -R -E -p1 < $(cur_patched) diff --git a/kernel/libiscsi.c b/kernel/libiscsi.c index 6e56974..7f94987 100644 --- a/kernel/libiscsi.c +++ b/kernel/libiscsi.c @@ -1625,7 +1625,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) struct iscsi_session *session = iscsi_hostdata(shost->hostdata); struct module *owner = cls_session->transport->owner; - iscsi_unblock_session(cls_session); + iscsi_remove_session(cls_session); scsi_remove_host(shost); iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); @@ -1640,7 +1640,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) kfree(session->hwaddress); kfree(session->initiatorname); - iscsi_destroy_session(cls_session); + iscsi_free_session(cls_session); scsi_host_put(shost); module_put(owner); } diff --git a/kernel/scsi_transport_iscsi.c b/kernel/scsi_transport_iscsi.c index 38a3cd6..078ea9b 100644 --- a/kernel/scsi_transport_iscsi.c +++ b/kernel/scsi_transport_iscsi.c @@ -116,6 +116,8 @@ static struct attribute_group iscsi_transport_group = { .attrs = iscsi_transport_attrs, }; + + static int iscsi_setup_host(struct transport_container *tc, struct device *dev, struct class_device *cdev) { @@ -125,13 +127,30 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev, memset(ihost, 0, sizeof(*ihost)); INIT_LIST_HEAD(&ihost->sessions); mutex_init(&ihost->mutex); + + snprintf(ihost->unbind_workq_name, KOBJ_NAME_LEN, "iscsi_unbind_%d", + shost->host_no); + ihost->unbind_workq = create_singlethread_workqueue( + ihost->unbind_workq_name); + if (!ihost->unbind_workq) + return -ENOMEM; + return 0; +} + +static int iscsi_remove_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; + + destroy_workqueue(ihost->unbind_workq); return 0; } static DECLARE_TRANSPORT_CLASS(iscsi_host_class, "iscsi_host", iscsi_setup_host, - NULL, + iscsi_remove_host, NULL); static DECLARE_TRANSPORT_CLASS(iscsi_session_class, @@ -266,6 +285,35 @@ void iscsi_block_session(struct iscsi_cls_session *session) } EXPORT_SYMBOL_GPL(iscsi_block_session); +static void __iscsi_unbind_session(struct work_struct *work) +{ + struct iscsi_cls_session *session = + 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; + + /* Prevent new scans and make sure scanning is not in progress */ + mutex_lock(&ihost->mutex); + if (list_empty(&session->host_list)) { + mutex_unlock(&ihost->mutex); + return; + } + list_del_init(&session->host_list); + 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->unbind_workq, &session->unbind_work); +} + struct iscsi_cls_session * iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport) @@ -282,6 +330,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); INIT_LIST_HEAD(&session->host_list); INIT_LIST_HEAD(&session->sess_list); + INIT_WORK(&session->unbind_work, __iscsi_unbind_session); /* this is released in the dev's release function */ scsi_host_get(shost); @@ -298,6 +347,7 @@ 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; + unsigned long flags; int err; ihost = shost->shost_data; @@ -314,9 +364,15 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) } transport_register_device(&session->dev); + spin_lock_irqsave(&sesslock, flags); + 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; release_host: @@ -351,19 +407,42 @@ iscsi_create_session(struct Scsi_Host *shost, } EXPORT_SYMBOL_GPL(iscsi_create_session); +static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data) +{ + return iscsi_destroy_conn(iscsi_dev_to_conn(dev)); +} + 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; + + spin_lock_irqsave(&sesslock, flags); + list_del(&session->sess_list); + spin_unlock_irqrestore(&sesslock, flags); + /* + * If we are blocked let commands flow again. The lld or iscsi + * layer should set up the queuecommand to fail commands. + */ + iscsi_unblock_session(session); + iscsi_unbind_session(session); + /* + * 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); + flush_workqueue(ihost->unbind_workq); - mutex_lock(&ihost->mutex); - list_del(&session->host_list); - mutex_unlock(&ihost->mutex); - - scsi_remove_target(&session->dev); + /* hw iscsi may not have removed all connections from session */ + err = device_for_each_child(&session->dev, NULL, + iscsi_iter_destroy_conn_fn); + if (err) + dev_printk(KERN_ERR, &session->dev, "iscsi: Could not delete " + "all connections for session. Error %d.\n", err); transport_unregister_device(&session->dev); device_del(&session->dev); @@ -372,9 +451,9 @@ EXPORT_SYMBOL_GPL(iscsi_remove_session); void iscsi_free_session(struct iscsi_cls_session *session) { + iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION); put_device(&session->dev); } - EXPORT_SYMBOL_GPL(iscsi_free_session); /** @@ -425,6 +504,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, 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); @@ -453,6 +533,11 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) goto release_parent_ref; } transport_register_device(&conn->dev); + + spin_lock_irqsave(&connlock, flags); + list_add(&conn->conn_list, &connlist); + conn->active = 1; + spin_unlock_irqrestore(&connlock, flags); return conn; release_parent_ref: @@ -472,11 +557,17 @@ EXPORT_SYMBOL_GPL(iscsi_create_conn); **/ int iscsi_destroy_conn(struct iscsi_cls_conn *conn) { + unsigned long flags; + + 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; } - EXPORT_SYMBOL_GPL(iscsi_destroy_conn); /* @@ -686,132 +777,74 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) } /** - * 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. + * iscsi_session_event - send session destr. completion event + * @session: iscsi class session + * @event: type of event **/ -int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn) +int iscsi_session_event(struct iscsi_cls_session *session, + enum iscsi_uevent_e event) { 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); + priv = iscsi_if_transport_lookup(session->transport); if (!priv) return -EINVAL; - - session = iscsi_dev_to_session(conn->dev.parent); shost = iscsi_session_to_shost(session); skb = alloc_skb(len, GFP_KERNEL); if (!skb) { - dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " - "session creation event\n"); + dev_printk(KERN_ERR, &session->dev, "Cannot notify userspace " + "of session event %u\n", event); 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(skb, GFP_KERNEL); - if (rc < 0) - dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " - "session destruction event. Check iscsi daemon\n"); + ev->transport_handle = iscsi_handle(session->transport); - 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) + ev->type = event; + switch (event) { + case ISCSI_KEVENT_DESTROY_SESSION: + ev->r.d_session.host_no = shost->host_no; + ev->r.d_session.sid = session->sid; + break; + case ISCSI_KEVENT_CREATE_SESSION: + ev->r.c_session_ret.host_no = shost->host_no; + ev->r.c_session_ret.sid = session->sid; + break; + case ISCSI_KEVENT_UNBIND_SESSION: + ev->r.unbind_session.host_no = shost->host_no; + ev->r.unbind_session.sid = session->sid; + break; + default: + dev_printk(KERN_ERR, &session->dev, "Invalid event %u.\n", + event); + kfree_skb(skb); return -EINVAL; - - session = iscsi_dev_to_session(conn->dev.parent); - shost = iscsi_session_to_shost(session); - - skb = alloc_skb(len, GFP_KERNEL); - 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(skb, GFP_KERNEL); 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); + dev_printk(KERN_ERR, &session->dev, "Cannot notify userspace " + "of session event %u. Check iscsi daemon\n", event); return rc; } -EXPORT_SYMBOL_GPL(iscsi_if_create_session_done); +EXPORT_SYMBOL_GPL(iscsi_session_event); static int iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) { struct iscsi_transport *transport = priv->iscsi_transport; struct iscsi_cls_session *session; - unsigned long flags; uint32_t hostno; session = transport->create_session(transport, &priv->t, @@ -822,10 +855,6 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) if (!session) return -ENOMEM; - spin_lock_irqsave(&sesslock, flags); - list_add(&session->sess_list, &sesslist); - spin_unlock_irqrestore(&sesslock, flags); - ev->r.c_session_ret.host_no = hostno; ev->r.c_session_ret.sid = session->sid; return 0; @@ -836,7 +865,6 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) { struct iscsi_cls_conn *conn; struct iscsi_cls_session *session; - unsigned long flags; session = iscsi_session_lookup(ev->u.c_conn.sid); if (!session) { @@ -855,28 +883,17 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) ev->r.c_conn_ret.sid = session->sid; ev->r.c_conn_ret.cid = conn->cid; - - spin_lock_irqsave(&connlock, flags); - list_add(&conn->conn_list, &connlist); - conn->active = 1; - spin_unlock_irqrestore(&connlock, flags); - return 0; } static int iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) { - unsigned long flags; struct iscsi_cls_conn *conn; conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); if (!conn) return -EINVAL; - spin_lock_irqsave(&connlock, flags); - conn->active = 0; - list_del(&conn->conn_list); - spin_unlock_irqrestore(&connlock, flags); if (transport->destroy_conn) transport->destroy_conn(conn); @@ -1003,7 +1020,6 @@ 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; - unsigned long flags; priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); if (!priv) @@ -1021,13 +1037,16 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) break; case ISCSI_UEVENT_DESTROY_SESSION: session = iscsi_session_lookup(ev->u.d_session.sid); - if (session) { - spin_lock_irqsave(&sesslock, flags); - list_del(&session->sess_list); - spin_unlock_irqrestore(&sesslock, flags); - + if (session) transport->destroy_session(session); - } else + else + err = -EINVAL; + break; + case ISCSI_UEVENT_UNBIND_SESSION: + session = iscsi_session_lookup(ev->u.d_session.sid); + if (session) + iscsi_unbind_session(session); + else err = -EINVAL; break; case ISCSI_UEVENT_CREATE_CONN: diff --git a/kernel/scsi_transport_iscsi.h b/kernel/scsi_transport_iscsi.h index e65ca85..bc1ce21 100644 --- a/kernel/scsi_transport_iscsi.h +++ b/kernel/scsi_transport_iscsi.h @@ -184,6 +184,7 @@ struct iscsi_cls_session { /* recovery fields */ int recovery_tmo; struct delayed_work recovery_work; + struct work_struct unbind_work; int target_id; @@ -204,6 +205,8 @@ struct iscsi_cls_session { struct iscsi_host { struct list_head sessions; struct mutex mutex; + struct workqueue_struct *unbind_workq; + char unbind_workq_name[KOBJ_NAME_LEN]; }; /* @@ -213,8 +216,8 @@ extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport); extern int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id); -extern int iscsi_if_create_session_done(struct iscsi_cls_conn *conn); -extern int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn); +extern int iscsi_session_event(struct iscsi_cls_session *session, + enum iscsi_uevent_e event); extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *t, unsigned int target_id); diff --git a/usr/initiator.c b/usr/initiator.c index 3a933e5..70ac050 100644 --- a/usr/initiator.c +++ b/usr/initiator.c @@ -1425,6 +1425,34 @@ static int iscsi_send_logout(iscsi_conn_t *conn) return 0; } +static void iscsi_logout(void *data) +{ + struct iscsi_conn_context *conn_context = data; + struct iscsi_conn *conn = conn_context->conn; + int rc = 0; + + iscsi_conn_context_put(conn_context); + + if (!iscsi_send_logout(conn)) + return; + + switch (conn->state) { + case STATE_IN_LOGIN: + case STATE_IN_LOGOUT: + case STATE_LOGGED_IN: + /* we have pdus in flight clean them up */ + rc = session_conn_shutdown(conn, conn->logout_qtask, + MGMT_IPC_OK); + break; + default: + rc = __session_conn_shutdown(conn, conn->logout_qtask, + MGMT_IPC_OK); + break; + } + if (rc) + log_error("BUG: Could not shutdown session."); +} + static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct iscsi_hdr *hdr) { if (hdr->ttt == ISCSI_RESERVED_TAG) { @@ -1718,6 +1746,11 @@ void iscsi_sched_conn_context(struct iscsi_conn_context *conn_context, actor_timer(&conn_context->actor, tmo * 1000, iscsi_logout_timedout, conn_context); break; + case EV_CONN_LOGOUT: + actor_new(&conn_context->actor, iscsi_logout, + conn_context); + actor_schedule(&conn_context->actor); + break; default: log_error("Invalid event type %d.", event); return; @@ -1949,6 +1982,17 @@ destroy_session: return err; } +static int session_unbind(struct iscsi_session *session) +{ + int err; + + err = ipc->unbind_session(session->t->handle, session->id); + if (err) + /* older kernels did not support unbind */ + log_debug(2, "Could not unbind session %d.\n", err); + return err; +} + int session_logout_task(iscsi_session_t *session, queue_task_t *qtask) { @@ -1960,6 +2004,7 @@ session_logout_task(iscsi_session_t *session, queue_task_t *qtask) (conn->state == STATE_XPT_WAIT && (session->r_stage == R_STAGE_NO_CHANGE || session->r_stage == R_STAGE_SESSION_REDIRECT))) { +invalid_state: log_error("session in invalid state for logout. " "Try again later\n"); return MGMT_IPC_ERR_INTERNAL; @@ -1968,6 +2013,8 @@ session_logout_task(iscsi_session_t *session, queue_task_t *qtask) /* FIXME: logout all active connections */ conn = &session->conn[0]; /* FIXME: implement Logout Request */ + if (conn->logout_qtask) + goto invalid_state; qtask->conn = conn; qtask->rsp.command = MGMT_IPC_SESSION_LOGOUT; @@ -1975,15 +2022,17 @@ session_logout_task(iscsi_session_t *session, queue_task_t *qtask) switch (conn->state) { case STATE_LOGGED_IN: + if (!session_unbind(session)) + return MGMT_IPC_OK; + + /* unbind is not supported so just do old logout */ if (!iscsi_send_logout(conn)) return MGMT_IPC_OK; log_error("Could not send logout pdu. Dropping session\n"); /* fallthrough */ case STATE_IN_LOGIN: - rc = session_conn_shutdown(conn, qtask, MGMT_IPC_OK); - break; case STATE_IN_LOGOUT: - rc = MGMT_IPC_ERR_LOGOUT_FAILURE; + rc = session_conn_shutdown(conn, qtask, MGMT_IPC_OK); break; default: rc = __session_conn_shutdown(conn, qtask, MGMT_IPC_OK); @@ -2022,6 +2071,15 @@ iscsi_host_send_targets(queue_task_t *qtask, int host_no, int do_login, */ void iscsi_async_session_creation(uint32_t host_no, uint32_t sid) { + struct iscsi_transport *transport; + + transport = get_transport_by_hba(host_no); + if (!transport) + return; + + if (!(transport->caps & CAP_FW_DB)) + return; + log_debug(3, "session created sid %u host no %d", sid, host_no); session_online_devs(host_no, sid); session_scan_host(host_no, NULL); diff --git a/usr/initiator.h b/usr/initiator.h index 7008845..9bfc43d 100644 --- a/usr/initiator.h +++ b/usr/initiator.h @@ -70,6 +70,7 @@ typedef enum iscsi_event_e { EV_CONN_POLL, EV_CONN_ERROR, EV_CONN_LOGOUT_TIMER, + EV_CONN_LOGOUT, } iscsi_event_e; struct queue_task; @@ -203,8 +204,6 @@ typedef struct iscsi_session { int portal_group_tag; uint8_t isid[6]; uint16_t tsih; - int channel; - int target_id; char target_name[TARGET_NAME_MAXLEN + 1]; char *target_alias; char *initiator_name; @@ -234,6 +233,8 @@ typedef struct iscsi_session { iscsi_session_r_stage_e r_stage; uint32_t replacement_timeout; + int unbinding; + /* sync up fields */ queue_task_t *sync_qtask; } iscsi_session_t; diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h index 2172174..76c485f 100644 --- a/usr/iscsi_ipc.h +++ b/usr/iscsi_ipc.h @@ -62,6 +62,8 @@ struct iscsi_ipc { int (*destroy_session) (uint64_t transport_handle, uint32_t sid); + int (*unbind_session) (uint64_t transport_handle, uint32_t sid); + int (*create_conn) (uint64_t transport_handle, uint32_t sid, uint32_t cid, uint32_t *out_cid); diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c index a7f278e..694150a 100644 --- a/usr/iscsiadm.c +++ b/usr/iscsiadm.c @@ -239,7 +239,7 @@ session_logout(node_rec_t *rec) req.command = MGMT_IPC_SESSION_LOGOUT; memcpy(&req.u.session.rec, rec, sizeof(node_rec_t)); - sysfs_for_each_session(rec, &num_found, __delete_target); + //sysfs_for_each_session(rec, &num_found, __delete_target); return do_iscsid(&req, &rsp); } diff --git a/usr/netlink.c b/usr/netlink.c index 65800ab..5eabccf 100644 --- a/usr/netlink.c +++ b/usr/netlink.c @@ -388,6 +388,27 @@ kdestroy_session(uint64_t transport_handle, uint32_t sid) } static int +kunbind_session(uint64_t transport_handle, uint32_t sid) +{ + int rc; + struct iscsi_uevent ev; + + log_debug(7, "in %s", __FUNCTION__); + + memset(&ev, 0, sizeof(struct iscsi_uevent)); + + ev.type = ISCSI_UEVENT_UNBIND_SESSION; + ev.transport_handle = transport_handle; + ev.u.d_session.sid = sid; + + if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) { + return rc; + } + + return 0; +} + +static int kcreate_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid, uint32_t *out_cid) { @@ -845,7 +866,7 @@ static void drop_data(struct nlmsghdr *nlh) static int ctldev_handle(void) { - int rc; + int rc, ev_size; struct iscsi_uevent *ev; struct iscsi_transport *t; iscsi_session_t *session = NULL; @@ -853,7 +874,7 @@ static int ctldev_handle(void) char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))]; struct nlmsghdr *nlh; struct iscsi_conn_context *conn_context; - int ev_size; + uint32_t sid = 0, cid = 0; log_debug(7, "in %s", __FUNCTION__); @@ -868,6 +889,8 @@ static int ctldev_handle(void) log_debug(7, "%s got event type %u\n", __FUNCTION__, ev->type); /* drivers like qla4xxx can be inserted after iscsid is started */ switch (ev->type) { + case ISCSI_KEVENT_CREATE_SESSION: + /* old kernels sent ISCSI_UEVENT_CREATE_SESSION on creation */ case ISCSI_UEVENT_CREATE_SESSION: drop_data(nlh); iscsi_async_session_creation(ev->r.c_session_ret.host_no, @@ -878,39 +901,31 @@ static int ctldev_handle(void) iscsi_async_session_destruction(ev->r.d_session.host_no, ev->r.d_session.sid); return 0; + case ISCSI_KEVENT_RECV_PDU: + sid = ev->r.recv_req.sid; + cid = ev->r.recv_req.cid; + break; + case ISCSI_KEVENT_CONN_ERROR: + sid = ev->r.connerror.sid; + cid = ev->r.connerror.cid; + case ISCSI_KEVENT_UNBIND_SESSION: + sid = ev->r.unbind_session.sid; + /* session wide event so cid is 0 */ + cid = 0; + break; default: ; /* fall through */ } /* verify connection */ - list_for_each_entry(t, &transports, list) { - list_for_each_entry(session, &t->sessions, list) { - int i; - - for (i=0; i<ISCSI_CONN_MAX; i++) { - if (ev->type == ISCSI_KEVENT_RECV_PDU && - session->id == ev->r.recv_req.sid && - session->conn[i].id == ev->r.recv_req.cid) { - conn = &session->conn[i]; - goto verify_conn; - } - if (ev->type == ISCSI_KEVENT_CONN_ERROR && - session->id == ev->r.connerror.sid && - session->conn[i].id == ev->r.connerror.cid) { - conn = &session->conn[i]; - goto verify_conn; - } - } - } - } - -verify_conn: - if (conn == NULL) { + session = session_find_by_sid(sid); + if (!session) { log_error("Could not verify connection %d:%d. Dropping " - "event.\n", ev->r.recv_req.sid, ev->r.recv_req.cid); + "event.\n", sid, cid); drop_data(nlh); return -ENXIO; } + conn = &session->conn[0]; ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); conn_context = iscsi_conn_context_get(conn, ev_size); @@ -946,6 +961,10 @@ verify_conn: iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_ERROR); break; + case ISCSI_KEVENT_UNBIND_SESSION: + iscsi_sched_conn_context(conn_context, conn, 0, + EV_CONN_LOGOUT); + break; default: iscsi_conn_context_put(conn_context); log_error("unknown kernel event %d", ev->type); @@ -1043,6 +1062,7 @@ struct iscsi_ipc nl_ipc = { .sendtargets = ksendtargets, .create_session = kcreate_session, .destroy_session = kdestroy_session, + .unbind_session = kunbind_session, .create_conn = kcreate_conn, .destroy_conn = kdestroy_conn, .bind_conn = kbind_conn, |