summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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,