summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2007-08-22 04:35:07 -0500
committerMike Christie <michaelc@cs.wisc.edu>2007-08-27 19:37:51 -0500
commita3e4c5202918409d54a893216a1d6555dc38cfe9 (patch)
tree7a150c2145b9f3418a9d75c31cb6202ff1120169
parent440c057de0d5661bf4ec82911f2e494eeb7912ca (diff)
downloadopen-iscsi-a3e4c5202918409d54a893216a1d6555dc38cfe9.tar.gz
fix shutdown
We were using the device delete sysfs file to remove each device then logout. Now in 2.6.21 and .22 this will not work because the sysfs delete file returns immediately and does not wait for the device removal to complete. This causes a hang if a cache sync is needed during shutdown. This patch fixes the shutdown code so that we remove the target and unbind the session before logging out and shuttdown the session. Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
-rw-r--r--include/iscsi_if.h7
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/libiscsi.c4
-rw-r--r--kernel/scsi_transport_iscsi.c257
-rw-r--r--kernel/scsi_transport_iscsi.h7
-rw-r--r--usr/initiator.c64
-rw-r--r--usr/initiator.h5
-rw-r--r--usr/iscsi_ipc.h2
-rw-r--r--usr/iscsiadm.c2
-rw-r--r--usr/netlink.c72
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,