On 02/22/2015 09:25 PM, Mike Christie wrote:
> I just hit a bug in the userspace code. Will send that later.

Hey Sagi,

Attached is the userspace patch, user-mq6.patch. It is made over
0001-iscsid-make-sure-actor-is-delated-before-reschedulin.patch (the
patch to fix that double schedule bug you guys found).

I am also attaching a updated kernel patch. It has some fixes for logout
and iscsi_tcp mq setup.

To use the patches, just set the new iscsid.conf setting
"node.session.queue_ids". It is just a string of ints:

node.session.queue_ids = 1 2 4 8

that get passed in to the kernel. For each id, iscsid will create a
session and have the LLD map whatever they want to that id value. Login
is the same:

iscsiadm -m node -T yourtargget -p ip --login

However, after you login you have to manually scan

iscsiadm -m session --rescan


For logout, you currently have to make sure you logout all the sessions,
so use:

iscsiadm -m node -T yourtargget -p ip --logout

or

iscsiadm -m session --logout

If you just pass in a specific session id like here:

iscsiadm -m session -r SID --logout

then that will wait for all the other sessions in the group to be logged
out before completion the task. I did this because I was not yet sure
how to handle dynamic hctx updates in the kernel.


For the LLD implementation, I hooked in iscsi_tcp to the session/group
creation code. Like I said before, I was not sure what every
driver/fw/hw was going to map to, so the queue id that is getting passed
into the session/connection/ep creation functions is really generic and
you can map it to whatever you like right now.

For ib_iser, you should look at iscsi_tcp.c's create_session_grp and
destroy_session_grp callouts to see how to allocate the host in a
backward compatible way. Sofware iscsi/iser is doing a host per session
still, then doing a session_grp per host and multiple sessions per
group. HW iscsi offload will continue to do a host per some hw/fw
resource, then it can have multiple groups and multiple sessions per group.

I am passing in the queue_id to bind to in every object callout
(ep_connect, conn_create, session_create), because I was not sure at
what time all the drivers needed to bind/setup-mappings at. So pick
which ever makes sense and let me know.

I have not had time to break this into a proper patchset. Was not ready
to send as a RFC set. There is debugging and // comments in places, but
feel free to give me any feedback.

If you did get my other mails/patches a while back then make sure you
are using the new userspace patches/tools in this mail with the updated
kernel patch in this mail. I have not yet added kernel/user compat code,
so you will hit hangs/crashes if you mix and match.

-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/open-iscsi.
For more options, visit https://groups.google.com/d/optout.
diff --git a/include/iscsi_if.h b/include/iscsi_if.h
index 9d15811..aaaae96 100644
--- a/include/iscsi_if.h
+++ b/include/iscsi_if.h
@@ -76,7 +76,15 @@ enum iscsi_uevent_e {
 	ISCSI_UEVENT_LOGOUT_FLASHNODE_SID	= UEVENT_BASE + 30,
 	ISCSI_UEVENT_SET_CHAP		= UEVENT_BASE + 31,
 	ISCSI_UEVENT_GET_HOST_STATS	= UEVENT_BASE + 32,
-	ISCSI_UEVENT_MAX		= ISCSI_UEVENT_GET_HOST_STATS,
+
+	ISCSI_UEVENT_MQ_CREATE_SESSION		= UEVENT_BASE + 33,
+	ISCSI_UEVENT_MQ_CREATE_SESSION_GRP	= UEVENT_BASE + 34,
+	ISCSI_UEVENT_MQ_DESTROY_SESSION_GRP	= UEVENT_BASE + 35,
+	ISCSI_UEVENT_MQ_UNBIND_SESSION_GRP	= UEVENT_BASE + 36,
+	ISCSI_UEVENT_MQ_CREATE_CONN		= UEVENT_BASE + 37,
+	ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT	= UEVENT_BASE + 38,
+
+	ISCSI_UEVENT_MAX		= ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT,
 
 	/* up events */
 	ISCSI_KEVENT_RECV_PDU		= KEVENT_BASE + 1,
@@ -92,7 +100,8 @@ enum iscsi_uevent_e {
 	ISCSI_KEVENT_HOST_EVENT		= KEVENT_BASE + 10,
 	ISCSI_KEVENT_PING_COMP		= KEVENT_BASE + 11,
 
-	ISCSI_KEVENT_MAX		= ISCSI_KEVENT_PING_COMP,
+	ISCSI_KEVENT_MQ_UNBIND_SESSION_GRP = KEVENT_BASE + 12,
+	ISCSI_KEVENT_MAX		= ISCSI_KEVENT_MQ_UNBIND_SESSION_GRP,
 };
 
 enum iscsi_tgt_dscvr {
@@ -108,10 +117,13 @@ enum iscsi_host_event_code {
 	ISCSI_EVENT_MAX,
 };
 
+#define ISCSI_UEVENT_FLAG_EP_BOUND	0x1
+#define ISCSI_UEVENT_FLAG_HOST_BOUND	0x1
+
 struct iscsi_uevent {
 	uint32_t type; /* k/u events type */
 	uint32_t iferror; /* carries interface or resource errors */
-	uint64_t transport_handle;
+	__aligned_u64 transport_handle;
 
 	union {
 		/* messages u -> k */
@@ -121,11 +133,21 @@ struct iscsi_uevent {
 			uint16_t	queue_depth;
 		} c_session;
 		struct msg_create_bound_session {
-			uint64_t	ep_handle;
+			__aligned_u64	ep_handle;
 			uint32_t	initial_cmdsn;
 			uint16_t	cmds_max;
 			uint16_t	queue_depth;
 		} c_bound_session;
+		struct msg_mq_create_session {
+			__aligned_u64	ep_handle;
+			uint32_t	flags;
+			uint32_t	queue_id;
+			uint32_t	initial_cmdsn;
+			uint32_t	gid;
+			uint32_t	host_no;
+			uint16_t	cmds_max;
+			uint16_t	queue_depth;
+		} c_mq_session;
 		struct msg_destroy_session {
 			uint32_t	sid;
 		} d_session;
@@ -133,10 +155,15 @@ struct iscsi_uevent {
 			uint32_t	sid;
 			uint32_t	cid;
 		} c_conn;
+		struct msg_mq_create_conn {
+			uint32_t	sid;
+			uint32_t	cid;
+			uint32_t	queue_id;
+		} c_mq_conn;
 		struct msg_bind_conn {
 			uint32_t	sid;
 			uint32_t	cid;
-			uint64_t	transport_eph;
+			__aligned_u64	transport_eph;
 			uint32_t	is_leading;
 		} b_conn;
 		struct msg_destroy_conn {
@@ -162,7 +189,7 @@ struct iscsi_uevent {
 		struct msg_stop_conn {
 			uint32_t	sid;
 			uint32_t	cid;
-			uint64_t	conn_handle;
+			__aligned_u64	conn_handle;
 			uint32_t	flag;
 		} stop_conn;
 		struct msg_get_stats {
@@ -176,12 +203,21 @@ struct iscsi_uevent {
 			uint32_t	host_no;
 			uint32_t	non_blocking;
 		} ep_connect_through_host;
+		struct msg_mq_transport_connect {
+			uint32_t	flags;
+			uint32_t	host_no;
+			uint32_t	non_blocking;
+			uint32_t	queue_id;
+			/*
+			 * TODO: Sagi/Or, there were some new fields we wanted for iser multipath right (from a past issue and not related to mq)? Let's add them here now while we are at it.
+			 */
+		} ep_mq_connect;
 		struct msg_transport_poll {
-			uint64_t	ep_handle;
+			__aligned_u64	ep_handle;
 			uint32_t	timeout_ms;
 		} ep_poll;
 		struct msg_transport_disconnect {
-			uint64_t	ep_handle;
+			__aligned_u64	ep_handle;
 		} ep_disconnect;
 		struct msg_tgt_dscvr {
 			enum iscsi_tgt_dscvr	type;
@@ -254,11 +290,22 @@ struct iscsi_uevent {
 		struct msg_get_host_stats {
 			uint32_t	host_no;
 		} get_host_stats;
-
+		struct msg_mq_create_session_grp {
+			uint32_t	host_no;
+			uint32_t	nr_sessions;
+		} c_mq_session_grp;
+		struct msg_mq_destroy_session_grp {
+			uint32_t	host_no;
+			uint32_t	gid;
+		} d_mq_session_grp;
 	} u;
 	union {
 		/* messages k -> u */
 		int			retcode;
+		struct msg_mq_create_session_grp_ret {
+			uint32_t	gid;
+			uint32_t	host_no;
+		} c_mq_session_grp_ret;
 		struct msg_create_session_ret {
 			uint32_t	sid;
 			uint32_t	host_no;
@@ -274,7 +321,7 @@ struct iscsi_uevent {
 		struct msg_recv_req {
 			uint32_t	sid;
 			uint32_t	cid;
-			uint64_t	recv_handle;
+			__aligned_u64	recv_handle;
 		} recv_req;
 		struct msg_conn_login {
 			uint32_t	sid;
@@ -291,7 +338,7 @@ struct iscsi_uevent {
 			uint32_t	sid;
 		} d_session;
 		struct msg_transport_connect_ret {
-			uint64_t	handle;
+			__aligned_u64	handle;
 		} ep_connect_ret;
 		struct msg_req_path {
 			uint32_t	host_no;
diff --git a/usr/config.h b/usr/config.h
index fd31a54..39786cd 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -38,6 +38,8 @@
 #define ISCSI_CONN_MAX		1
 /* max len of interface */
 #define ISCSI_MAX_IFACE_LEN	65
+/* TODO: make this dynamic or at least configurable */
+#define QUEUE_IDS_MAXLEN		1024
 
 /* the following structures store the options set in the config file.
  * a structure is defined for each logically-related group of options.
@@ -201,6 +203,7 @@ typedef struct session_rec {
 	 * allowed to be initiated on this record
 	 */
 	unsigned char                           multiple;
+	char					queue_ids[QUEUE_IDS_MAXLEN];
 	char					boot_root[BOOT_NAME_MAXLEN];
 	char					boot_nic[BOOT_NAME_MAXLEN];
 	char					boot_target[BOOT_NAME_MAXLEN];
diff --git a/usr/discovery.c b/usr/discovery.c
index 565a919..5348d8e 100644
--- a/usr/discovery.c
+++ b/usr/discovery.c
@@ -1142,9 +1142,7 @@ static int iscsi_create_leading_conn(struct iscsi_session *session)
 
 	log_debug(2, "%s discovery create session\n", __FUNCTION__);
 	/* create kernel structs */
-        rc = ipc->create_session(session->t->handle,
-				 conn->transport_ep_handle, 1, 32, 1,
-				 &session->id, &host_no);
+        rc = ipc->create_session(session);
 	if (rc) {
 		log_error("Could not create kernel session (err %d).\n", rc);
 		rc = ISCSI_ERR_INTERNAL;
@@ -1155,7 +1153,7 @@ static int iscsi_create_leading_conn(struct iscsi_session *session)
 	session->isid[3] = session->id;
 
 	log_debug(2, "%s discovery create conn\n", __FUNCTION__);
-	rc = ipc->create_conn(t->handle, session->id, conn->id, &conn->id);
+	rc = ipc->create_conn(conn);
 	if (rc) {
 		log_error("Could not create connection (err %d)", rc);
 		rc = ISCSI_ERR_INTERNAL;
@@ -1574,6 +1572,7 @@ int discovery_sendtargets(void *fndata, struct iface_rec *iface,
 	session = iscsi_alloc_session(config, iface, &rc);
 	if (rc)
 		return rc;
+	idbm_node_setup_defaults(&session->nrec);
 
 	ipc_ev_context.conn = &session->conn[0];
 	ipc_register_ev_callback(&ipc_clbk);
diff --git a/usr/idbm.c b/usr/idbm.c
index cd5462a..89b3276 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -462,6 +462,8 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
 		      session.iscsi.MaxOutstandingR2T, IDBM_SHOW, num, 1);
 	__recinfo_int(SESSION_ERL, ri, r,
 		      session.iscsi.ERL, IDBM_SHOW, num, 1);
+	__recinfo_str(SESSION_MQ_QUEUE_IDS, ri, r,
+		      session.queue_ids, IDBM_SHOW, num, 1);
 
 	for (i = 0; i < ISCSI_CONN_MAX; i++) {
 		char key[NAME_MAXVAL];
diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h
index 5790a03..79d2c45 100644
--- a/usr/idbm_fields.h
+++ b/usr/idbm_fields.h
@@ -45,6 +45,7 @@
 #define SESSION_MAX_CONNS	"node.session.iscsi.MaxConnections"
 #define SESSION_MAX_R2T		"node.session.iscsi.MaxOutstandingR2T"
 #define SESSION_ERL		"node.session.iscsi.ERL"
+#define SESSION_MQ_QUEUE_IDS	"node.session.queue_ids"
 
 /* connections fields */
 #define CONN_ADDR		"node.conn[%d].address"
diff --git a/usr/initiator.c b/usr/initiator.c
index f70db4d..6b13d00 100644
--- a/usr/initiator.c
+++ b/usr/initiator.c
@@ -349,6 +349,7 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t, int *rc)
 	}
 	log_debug(2, "Allocted session %p", session);
 
+	INIT_LIST_HEAD(&session->grp_list);
 	INIT_LIST_HEAD(&session->list);
 	session->t = t;
 	session->reopen_qtask.mgmt_ipc_fd = -1;
@@ -424,6 +425,19 @@ free_session:
 	return NULL;
 }
 
+static void session_grp_destroy(struct iscsi_session_grp *grp)
+{
+	int rc;
+
+	if (!list_empty(&grp->sessions))
+		return;
+
+	rc = ipc->destroy_session_grp(grp, grp->t->handle);
+	if (rc)
+		log_error("Could not destroy group %u. Error %d", grp->id, rc);
+	free(grp);
+}
+
 static void iscsi_flush_context_pool(struct iscsi_session *session)
 {
 	struct iscsi_ev_context *ev_context;
@@ -506,11 +520,17 @@ cleanup:
 	}
 
 	log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
-		    "through [iface: %s] is shutdown.",
+		    "through [iface: %s] on queue %u:%u is shutdown.",
 		    session->id, conn->id, session->nrec.name,
 		    session->nrec.conn[conn->id].address,
 		    session->nrec.conn[conn->id].port,
-		    session->nrec.iface.name);
+		    session->nrec.iface.name,
+		    session->grp ? session->grp->id : 0, session->qid);
+
+	if (!list_empty(&session->grp_list)) {
+		list_del(&session->grp_list);
+		session_grp_destroy(session->grp);
+	}
 
 	mgmt_ipc_write_rsp(qtask, err);
 	conn_delete_timers(conn);
@@ -995,8 +1015,12 @@ void free_initiator(void)
 static void session_scan_host(struct iscsi_session *session, int hostno,
 			      queue_task_t *qtask)
 {
-	pid_t pid;
+	//pid_t pid;
 
+	/* TODO: fix of course */
+	
+	mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
+/*
 	pid = iscsi_sysfs_scan_host(hostno, 1);
 	if (pid == 0) {
 		mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
@@ -1015,6 +1039,7 @@ static void session_scan_host(struct iscsi_session *session, int hostno,
 		}
 	} else
 		mgmt_ipc_write_rsp(qtask, ISCSI_ERR_INTERNAL);
+*/
 }
 
 static void
@@ -1050,11 +1075,13 @@ setup_full_feature_phase(iscsi_conn_t *conn)
 			session_scan_host(session, session->hostno, c->qtask);
 
 		log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
-			    "through [iface: %s] is operational now",
+			    "through [iface: %s] on queue %u:%u is "
+			    "operational now",
 			    session->id, conn->id, session->nrec.name,
 			    session->nrec.conn[conn->id].address,
 			    session->nrec.conn[conn->id].port,
-			    session->nrec.iface.name);
+			    session->nrec.iface.name,
+			    session->grp ? session->grp->id : 0, session->qid);
 	} else {
 		session->notify_qtask = NULL;
 
@@ -1434,39 +1461,6 @@ fail:
 		  "affected.\n", session->id);
 }
 
-static int session_ipc_create(struct iscsi_session *session)
-{
-	struct iscsi_conn *conn = &session->conn[0];
-	int err = 0, pass_ep = 1;
-	uint32_t host_no = -1;
-
-	if (session->t->template->ep_connect != ktransport_ep_connect)
-		pass_ep = 0;
-retry_create:
-	err = ipc->create_session(session->t->handle,
-				  pass_ep ? conn->transport_ep_handle : 0,
-				  session->nrec.session.initial_cmdsn,
-				  session->nrec.session.cmds_max,
-				  session->nrec.session.queue_depth,
-				  &session->id, &host_no);
-	/*
-	 * Older kernels were not passed the sessions's leading conn ep,
-	 * so we will get -EINVAL || -ENOSYS for iser.
-	 *
-	 * 2.6.22 and earlier would send -EINVAL instead of -ENOSYS.
-	 */
-	if (pass_ep && (err == -ENOSYS || err == -EINVAL)) {
-		pass_ep = 0;
-		goto retry_create;
-	}
-
-	if (!err) {
-		session->hostno = host_no;
-		session_increase_wq_priority(session);
-	}
-	return err;
-}
-
 static void setup_offload_login_phase(iscsi_conn_t *conn)
 {
 	iscsi_session_t *session = conn->session;
@@ -1544,16 +1538,16 @@ static void session_conn_poll(void *data)
 
 		/* do not allocate new connection in case of reopen */
 		if (session->id == -1) {
-			if (conn->id == 0 && session_ipc_create(session)) {
+			if (conn->id == 0 && ipc->create_session(session)) {
 				log_error("Can't create session.");
 				err = ISCSI_ERR_INTERNAL;
 				goto cleanup;
 			}
+			session_increase_wq_priority(session);
 			log_debug(3, "created new iSCSI session sid %d host "
 				  "no %u", session->id, session->hostno);
 
-			err = ipc->create_conn(session->t->handle,
-					session->id, conn->id, &conn->id);
+			err = ipc->create_conn(conn);
 			if (err) {
 				log_error("Can't create connection.");
 				err = ISCSI_ERR_INTERNAL;
@@ -1666,11 +1660,13 @@ static void session_conn_process_login(void *data)
 		session->notify_qtask = NULL;
 
 		log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
-			    "through [iface: %s] is operational now",
+			    "through [iface: %s] on queue %u:%u is "
+			    "operational now",
 			    session->id, conn->id, session->nrec.name,
 			    session->nrec.conn[conn->id].address,
 			    session->nrec.conn[conn->id].port,
-			    session->nrec.iface.name);
+			    session->nrec.iface.name,
+			    session->grp ? 0 : session->grp->id, session->qid);
 	} else {
 		session->notify_qtask = NULL;
 		mgmt_ipc_write_rsp(c->qtask, ISCSI_SUCCESS);
@@ -1788,7 +1784,8 @@ static int session_is_running(node_rec_t *rec)
 	return 0;
 }
 
-static int __session_login_task(node_rec_t *rec, queue_task_t *qtask)
+static int __session_login_task(node_rec_t *rec, queue_task_t *qtask,
+				struct iscsi_session_grp *grp, uint32_t qid)
 {
 	iscsi_session_t *session;
 	iscsi_conn_t *conn;
@@ -1796,7 +1793,7 @@ static int __session_login_task(node_rec_t *rec, queue_task_t *qtask)
 	int rc;
 
 	if (session_is_running(rec)) {
-		if (rec->session.multiple)
+		if (rec->session.multiple || grp)
 			log_debug(2, "Adding a copy of an existing session");
 		else
 			return ISCSI_ERR_SESS_EXISTS;
@@ -1885,6 +1882,12 @@ static int __session_login_task(node_rec_t *rec, queue_task_t *qtask)
 		return ISCSI_ERR_LOGIN;
 	}
 
+	if (grp) {
+		session->qid = qid;
+		session->grp = grp;
+		list_add_tail(&session->grp_list, &grp->sessions);
+	}
+
 	if (gettimeofday(&conn->initial_connect_time, NULL))
 		log_error("Could not get initial connect time. If "
 			  "login errors iscsid may give up the initial "
@@ -1904,22 +1907,118 @@ static int __session_login_task(node_rec_t *rec, queue_task_t *qtask)
 	return ISCSI_SUCCESS;
 }
 
+static int session_grp_login_task(node_rec_t *rec, queue_task_t *qtask)
+{
+	struct iscsi_session_grp *grp = NULL;
+	struct iscsi_transport *t;
+	struct iscsi_session *session;
+	char *queue, *queues;
+	uint32_t qid, hostno = 0;
+	int rc;
+
+	t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name);
+	if (!t)
+		return ISCSI_ERR_TRANS_NOT_FOUND;
+
+	if (t->template->bind_ep_required) {
+		hostno = iscsi_sysfs_get_host_no_from_hwinfo(&rec->iface, &rc);
+		if (!rc) {
+			log_debug(4, "Matched to host %u\n", rc); 
+		} else {
+			return rc;
+		}
+	}
+
+	queues = strdup(rec->session.queue_ids);
+	if (!queues)
+		return ISCSI_ERR_NOMEM;
+
+	/*
+	 * TODO: handle ISCSI_ERR_HOST_NOT_FOUND retries. Was passing in
+	 * already made group here. Need to finish.
+	 */
+	if (!grp) {
+		grp = calloc(1, sizeof(*grp));
+		if (!grp) {
+			 rc = ISCSI_ERR_NOMEM;
+			 goto free_queues;
+		}
+		INIT_LIST_HEAD(&grp->sessions);
+		grp->hostno = hostno;
+		grp->t = t;
+
+		rc = ipc->create_session_grp(grp, t->handle, queues);
+		if (rc) {
+			log_error("Could not create session group. Error %d: %s\n",
+				  rc, strerror(-rc));
+			rc = ISCSI_ERR_INTERNAL;
+			goto free_grp;
+		}
+	}
+
+	while ((queue = strsep(&queues, " "))) {
+		int exists = 0;
+
+		qid = atoi(queue);
+		if (qid < 0) {
+			log_error("Could not create session for queue %s in group %u.\n",
+				  queue, grp->id);
+			continue;
+		}
+
+		list_for_each_entry(session, &grp->sessions, grp_list) {
+			if (session->qid == qid) {
+				exists = 1;
+				break;
+			}
+		}
+
+		if (exists)
+			continue;
+
+		rc = __session_login_task(rec, qtask, grp, qid);
+		if (rc == ISCSI_ERR_HOST_NOT_FOUND) {
+			goto free_queues;
+		} else if (rc) {
+			/* Ignore for now */
+			log_error("Could not create session for queue %s in group %u.\n",
+				  queue, grp->id);
+		} else {
+			qtask->refcount++;
+		}
+	}
+
+	if (list_empty(&grp->sessions)) {
+		log_error("No sessions created for group %u.\n", grp->id);
+		rc = ISCSI_ERR_INTERNAL;
+		goto free_queues;
+	}
+
+	/* release ref from allocation since we took a ref per session */
+	qtask->refcount--;
+	free(queues);
+	return ISCSI_SUCCESS;
+
+free_grp:
+	free(grp);
+free_queues:
+	free(queues);
+	return rc;
+}
+
 int
 session_login_task(node_rec_t *rec, queue_task_t *qtask)
 {
 	int rc;
 
-	rc = __session_login_task(rec, qtask);
-	if (rc == ISCSI_ERR_HOST_NOT_FOUND) {
-		rc = queue_session_login_task_retry(NULL, rec, qtask);
-		if (rc)
-			return rc;
-		/*
-		 * we are going to internally retry. Will return final rc
-		 * when completed
-		 */
-		return ISCSI_SUCCESS;
+	if (rec->session.queue_ids[0] == '\0') {
+		rc = __session_login_task(rec, qtask, NULL, 0);
+	} else {
+		rc = session_grp_login_task(rec, qtask);
 	}
+
+	if (rc == ISCSI_ERR_HOST_NOT_FOUND)
+		rc = queue_session_login_task_retry(NULL, rec, qtask);
 	return rc;
 }
 
@@ -1929,7 +2028,11 @@ static void session_login_task_retry(void *data)
 	struct node_rec *rec = info->rec;
 	int rc;
 
-	rc = __session_login_task(rec, info->qtask);
+	if (rec->session.queue_ids[0] == '\0') {
+		rc = __session_login_task(rec, info->qtask, NULL, 0);
+	} else {
+		rc = session_grp_login_task(rec, info->qtask);
+	}
 	if (rc == ISCSI_ERR_HOST_NOT_FOUND) {
 		if (info->retry_count == rec->conn[0].timeo.login_timeout) {
 			/* give up */
@@ -1969,7 +2072,7 @@ static int queue_session_login_task_retry(struct login_task_retry_info *info,
 	log_debug(4, "queue session setup attempt in %d secs, retries %d\n",
 		  3, info->retry_count);
 	actor_timer(&info->retry_actor, 3, session_login_task_retry, info);
-	return 0;
+	return ISCSI_SUCCESS;
 }
 
 static int
diff --git a/usr/initiator.h b/usr/initiator.h
index c11d77f..a4a44eb 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -185,6 +185,7 @@ typedef struct queue_task {
 	iscsiadm_req_t req;
 	iscsiadm_rsp_t rsp;
 	int mgmt_ipc_fd;
+	int refcount;
 	int allocated : 1;
 	/* Newer request types include a
 	 * variable-length payload */
@@ -193,6 +194,7 @@ typedef struct queue_task {
 
 struct iscsi_transport_template;
 struct iscsi_transport;
+struct iscsi_session_grp;
 
 /* daemon's session structure */
 typedef struct iscsi_session {
@@ -245,6 +247,10 @@ typedef struct iscsi_session {
 	iscsi_conn_t conn[ISCSI_CONN_MAX];
 	uint64_t param_mask;
 
+	struct iscsi_session_grp *grp;
+	uint32_t qid;
+	struct list_head grp_list;
+
 	/* connection reopens during recovery */
 	int reopen_cnt;
 	queue_task_t reopen_qtask;
@@ -263,6 +269,13 @@ typedef struct iscsi_session {
 	queue_task_t *notify_qtask;
 } iscsi_session_t;
 
+struct iscsi_session_grp {
+	struct list_head sessions;
+	uint32_t id;
+	uint32_t hostno;
+	struct iscsi_transport *t;
+};
+
 /* login.c */
 
 #define ISCSI_SESSION_TYPE_NORMAL 0
diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h
index 5087b5c..cbefa77 100644
--- a/usr/iscsi_ipc.h
+++ b/usr/iscsi_ipc.h
@@ -35,7 +35,9 @@ enum {
 };
 
 struct iscsi_conn;
+struct iscsi_session;
 struct iscsi_ev_context;
+struct iscsi_session_grp;
 
 /*
  * When handling async events, the initiator may not be able to
@@ -76,17 +78,18 @@ struct iscsi_ipc {
 	int (*sendtargets) (uint64_t transport_handle, uint32_t host_no,
 			    struct sockaddr *addr);
 
-	int (*create_session) (uint64_t transport_handle, uint64_t ep_handle,
-			       uint32_t initial_cmdsn, uint16_t cmds_max,
-			       uint16_t qdepth, uint32_t *out_sid,
-			       uint32_t *hostno);
-
+	int (*create_session_grp) (struct iscsi_session_grp *grp,
+				   uint64_t transport_handle, char *queues);
+	int (*destroy_session_grp) (struct iscsi_session_grp *grp,
+				    uint64_t transport_handle);
+	int (*unbind_session_grp) (struct iscsi_session_grp *grp,
+				   uint64_t transport_handle);
+	int (*create_session) (struct iscsi_session *session);
 	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);
+	int (*create_conn) (struct iscsi_conn *conn);
 
 	int (*destroy_conn) (uint64_t transport_handle, uint32_t sid,
 			     uint32_t cid);
diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c
index c16bce9..4171e75 100644
--- a/usr/mgmt_ipc.c
+++ b/usr/mgmt_ipc.c
@@ -453,8 +453,12 @@ mgmt_ipc_write_rsp(queue_task_t *qtask, int err)
 {
 	if (!qtask)
 		return;
-	log_debug(4, "%s: rsp to fd %d", __FUNCTION__,
-		 qtask->mgmt_ipc_fd);
+
+	log_debug(4, "%s: rsp to fd %d refcount %d", __FUNCTION__,
+		 qtask->mgmt_ipc_fd, qtask->refcount);
+
+	if (--qtask->refcount)
+		return;
 
 	if (qtask->mgmt_ipc_fd < 0) {
 		mgmt_ipc_destroy_queue_task(qtask);
@@ -564,6 +568,7 @@ void mgmt_ipc_handle(int accept_fd)
 
 	command = qtask->req.command;
 	qtask->rsp.command = command;
+	qtask->refcount = 1;
 
 	if (0 <= command && command < __MGMT_IPC_MAX_COMMAND)
 		handler = mgmt_ipc_functions[command];
diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h
index 55972ed..97295d4 100644
--- a/usr/mgmt_ipc.h
+++ b/usr/mgmt_ipc.h
@@ -23,7 +23,7 @@
 #include "iscsi_if.h"
 #include "config.h"
 
-#define ISCSIADM_NAMESPACE	"ISCSIADM_ABSTRACT_NAMESPACE"
+#define ISCSIADM_NAMESPACE	"ISCSIADM_ABSTRACT_NAMESPACEXYZ"
 #define PEERUSER_MAX		64
 
 typedef enum iscsiadm_cmd {
diff --git a/usr/netlink.c b/usr/netlink.c
index 3984727..f3dece0 100644
--- a/usr/netlink.c
+++ b/usr/netlink.c
@@ -291,7 +291,7 @@ __kipc_call(struct iovec *iovp, int count)
 	struct iscsi_uevent *ev = iovp[1].iov_base;
 	enum iscsi_uevent_e type = ev->type;
 
-	log_debug(7, "in %s", __FUNCTION__);
+	log_debug(7, "in %s event %u", __FUNCTION__, type);
 
 	rc = kwritev(type, iovp, count);
 
@@ -393,27 +393,111 @@ ksendtargets(uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr)
 }
 
 static int
-kcreate_session(uint64_t transport_handle, uint64_t ep_handle,
-		uint32_t initial_cmdsn, uint16_t cmds_max, uint16_t qdepth,
-		uint32_t *out_sid, uint32_t *hostno)
+kcreate_session_grp(struct iscsi_session_grp *grp, uint64_t transport_handle,
+		    char *queues)
+{
+	int rc, nr_queues = 0;
+	struct iscsi_uevent ev;
+	struct iovec iov[2];
+	char *queue_string, *queue;
+
+	log_debug(7, "in %s", __FUNCTION__);
+
+	queue_string = strdup(queues);
+	if (!queue_string)
+		return -ENOMEM;
+
+	while ((queue = strsep(&queue_string, " "))) {
+		if (atoi(queue) >= 0)
+			nr_queues++;
+	}
+	free(queue_string);
+	if (!nr_queues)
+		return -EINVAL;
+
+	memset(&ev, 0, sizeof(struct iscsi_uevent));
+
+	ev.type = ISCSI_UEVENT_MQ_CREATE_SESSION_GRP;
+	ev.transport_handle = transport_handle;
+	ev.u.c_mq_session_grp.host_no = grp->hostno;
+	ev.u.c_mq_session_grp.nr_sessions = nr_queues;
+	
+	iov[1].iov_base = &ev;
+	iov[1].iov_len = sizeof(ev);
+	rc = __kipc_call(iov, 2);
+	if (rc < 0)
+		return rc;
+
+	grp->hostno = ev.r.c_mq_session_grp_ret.host_no;
+	grp->id = ev.r.c_mq_session_grp_ret.gid;
+	return 0;
+}
+
+static int kdestroy_session_grp(struct iscsi_session_grp *grp,
+				uint64_t transport_handle)
 {
-	int rc;
 	struct iscsi_uevent ev;
 	struct iovec iov[2];
+	int rc;
 
 	log_debug(7, "in %s", __FUNCTION__);
 
 	memset(&ev, 0, sizeof(struct iscsi_uevent));
 
-	if (ep_handle == 0) {
+	ev.type = ISCSI_UEVENT_MQ_DESTROY_SESSION_GRP;
+	ev.transport_handle = transport_handle;
+
+	ev.u.d_mq_session_grp.host_no = grp->hostno;
+	ev.u.d_mq_session_grp.gid = grp->id;
+	
+	iov[1].iov_base = &ev;
+	iov[1].iov_len = sizeof(ev);
+	rc = __kipc_call(iov, 2);
+	if (rc  < 0)
+		return rc;
+	return 0;
+}
+
+static int
+kcreate_session(struct iscsi_session *session)
+{
+	uint32_t initial_cmdsn = session->nrec.session.initial_cmdsn;
+	uint16_t cmds_max = session->nrec.session.cmds_max;
+	int16_t qdepth = session->nrec.session.queue_depth;
+	int rc;
+	struct iscsi_uevent ev;
+	struct iovec iov[2];
+	uint64_t ep_handle = 0;
+
+	log_debug(7, "in %s", __FUNCTION__);
+
+	memset(&ev, 0, sizeof(struct iscsi_uevent));
+	ev.transport_handle = session->t->handle;
+
+	if (session->conn[0].transport_ep_handle &&
+	    session->t->template->ep_connect == ktransport_ep_connect)
+		ep_handle = session->conn[0].transport_ep_handle;
+
+retry_create:
+	if (session->grp) {
+		ev.type = ISCSI_UEVENT_MQ_CREATE_SESSION;
+		ev.u.c_mq_session.gid = session->grp->id;
+		ev.u.c_mq_session.host_no = session->grp->hostno;
+		ev.u.c_mq_session.queue_id = session->qid;
+		ev.u.c_mq_session.initial_cmdsn = initial_cmdsn;
+		ev.u.c_mq_session.cmds_max = cmds_max;
+		ev.u.c_mq_session.queue_depth = qdepth;
+		if (ep_handle != 0) {
+			ev.u.c_mq_session.ep_handle = ep_handle;
+			ev.u.c_mq_session.flags &= ISCSI_UEVENT_FLAG_EP_BOUND;
+		}
+	} else if (ep_handle == 0) {
 		ev.type = ISCSI_UEVENT_CREATE_SESSION;
-		ev.transport_handle = transport_handle;
 		ev.u.c_session.initial_cmdsn = initial_cmdsn;
 		ev.u.c_session.cmds_max = cmds_max;
 		ev.u.c_session.queue_depth = qdepth;
 	} else {
 		ev.type = ISCSI_UEVENT_CREATE_BOUND_SESSION;
-		ev.transport_handle = transport_handle;
 		ev.u.c_bound_session.initial_cmdsn = initial_cmdsn;
 		ev.u.c_bound_session.cmds_max = cmds_max;
 		ev.u.c_bound_session.queue_depth = qdepth;
@@ -423,11 +507,22 @@ kcreate_session(uint64_t transport_handle, uint64_t ep_handle,
 	iov[1].iov_base = &ev;
 	iov[1].iov_len = sizeof(ev);
 	rc = __kipc_call(iov, 2);
-	if (rc < 0)
+	if (rc < 0) {
+		/*
+		* Older kernels were not passed the sessions's leading conn ep,
+		* so we will get -EINVAL || -ENOSYS for iser.
+		*
+		* 2.6.22 and earlier would send -EINVAL instead of -ENOSYS.
+		*/
+		if (ep_handle && (rc == -ENOSYS || rc == -EINVAL)) {
+			ep_handle = 0;
+			goto retry_create;
+		}
 		return rc;
+	}
 
-	*hostno = ev.r.c_session_ret.host_no;
-	*out_sid = ev.r.c_session_ret.sid;
+	session->hostno = ev.r.c_session_ret.host_no;
+	session->id = ev.r.c_session_ret.sid;
 
 	return 0;
 }
@@ -481,9 +576,8 @@ kunbind_session(uint64_t transport_handle, uint32_t sid)
 }
 
 static int
-kcreate_conn(uint64_t transport_handle, uint32_t sid,
-	    uint32_t cid, uint32_t *out_cid)
-{
+kcreate_conn(struct iscsi_conn *conn)
+		{
 	int rc;
 	struct iscsi_uevent ev;
 	struct iovec iov[2];
@@ -491,11 +585,18 @@ kcreate_conn(uint64_t transport_handle, uint32_t sid,
 	log_debug(7, "in %s", __FUNCTION__);
 
 	memset(&ev, 0, sizeof(struct iscsi_uevent));
+	ev.transport_handle = conn->session->t->handle;
 
-	ev.type = ISCSI_UEVENT_CREATE_CONN;
-	ev.transport_handle = transport_handle;
-	ev.u.c_conn.cid = cid;
-	ev.u.c_conn.sid = sid;
+	if (conn->session->grp) {
+		ev.type = ISCSI_UEVENT_MQ_CREATE_CONN;
+		ev.u.c_mq_conn.cid = conn->id;
+		ev.u.c_mq_conn.sid = conn->session->id;
+		ev.u.c_mq_conn.queue_id = conn->session->qid;
+	} else {
+		ev.type = ISCSI_UEVENT_CREATE_CONN;
+		ev.u.c_conn.cid = conn->id;
+		ev.u.c_conn.sid = conn->session->id;
+	}
 
 	iov[1].iov_base = &ev;
 	iov[1].iov_len = sizeof(ev);
@@ -508,7 +609,7 @@ kcreate_conn(uint64_t transport_handle, uint32_t sid,
 	if ((int)ev.r.c_conn_ret.cid == -1)
 		return -EIO;
 
-	*out_cid = ev.r.c_conn_ret.cid;
+	conn->id = ev.r.c_conn_ret.cid;
 	return 0;
 }
 
@@ -863,6 +964,13 @@ ktransport_ep_connect(iscsi_conn_t *conn, int non_blocking)
 		ev->type = ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST;
 		ev->u.ep_connect_through_host.non_blocking = non_blocking;
 		ev->u.ep_connect_through_host.host_no = conn->session->hostno;
+	} else if (conn->session->grp) {
+		ev->type = ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT;
+		ev->u.ep_mq_connect.host_no = conn->session->hostno;
+		ev->u.ep_mq_connect.non_blocking = non_blocking;
+		ev->u.ep_mq_connect.queue_id = conn->session->qid;
+		if (conn->bind_ep)
+			ev->u.ep_mq_connect.flags &= ISCSI_UEVENT_FLAG_HOST_BOUND;
 	} else {
 		ev->type = ISCSI_UEVENT_TRANSPORT_EP_CONNECT;
 		ev->u.ep_connect.non_blocking = non_blocking;
@@ -1759,6 +1867,8 @@ struct iscsi_ipc nl_ipc = {
 	.ctldev_close		= ctldev_close,
 	.ctldev_handle		= ctldev_handle,
 	.sendtargets		= ksendtargets,
+	.create_session_grp	= kcreate_session_grp,
+	.destroy_session_grp	= kdestroy_session_grp,
 	.create_session         = kcreate_session,
 	.destroy_session        = kdestroy_session,
 	.unbind_session		= kunbind_session,
diff --git a/usr/types.h b/usr/types.h
index 9d9ba86..837666f 100644
--- a/usr/types.h
+++ b/usr/types.h
@@ -19,4 +19,9 @@
 typedef uint16_t __be16;
 typedef uint32_t __be32;
 
+#ifndef __aligned_u64
+/* this is a special 64bit data type that is 8-byte aligned */
+#define __aligned_u64 __u64 __attribute__((aligned(8)))
+#endif
+
 #endif	/* TYPES_H */
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 6a594aa..b0d2e73 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -410,13 +410,14 @@ iscsi_iser_check_protection(struct iscsi_task *task, sector_t *sector)
  * iscsi_iser_conn_create() - create a new iscsi-iser connection
  * @cls_session: iscsi class connection
  * @conn_idx:    connection index within the session (for MCS)
+ * @queue_idx:   mq queue id
  *
  * Return: iscsi_cls_conn when iscsi_conn_setup succeeds or NULL
  *         otherwise.
  */
 static struct iscsi_cls_conn *
 iscsi_iser_conn_create(struct iscsi_cls_session *cls_session,
-		       uint32_t conn_idx)
+		       uint32_t conn_idx, uint32_t queue_id)
 {
 	struct iscsi_conn *conn;
 	struct iscsi_cls_conn *cls_conn;
@@ -564,7 +565,7 @@ iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
 static void
 iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&cls_session->dev);
 
 	iscsi_session_teardown(cls_session);
 	iscsi_host_remove(shost);
@@ -585,18 +586,21 @@ iser_dif_prot_caps(int prot_caps)
 
 /**
  * iscsi_iser_session_create() - create an iscsi-iser session
+ * @grp:            grp to add session to if mq is enabled
  * @ep:             iscsi end-point handle
  * @cmds_max:       maximum commands in this session
  * @qdepth:         session command queue depth
  * @initial_cmdsn:  initiator command sequnce number
+ * @queue_id:       mq queue id
  *
  * Allocates and adds a scsi host, expose DIF supprot if
  * exists, and sets up an iscsi session.
  */
 static struct iscsi_cls_session *
-iscsi_iser_session_create(struct iscsi_endpoint *ep,
+iscsi_iser_session_create(struct iscsi_session_grp *grp,
+			  struct iscsi_endpoint *ep,
 			  uint16_t cmds_max, uint16_t qdepth,
-			  uint32_t initial_cmdsn)
+			  uint32_t initial_cmdsn, uint32_t queue_id)
 {
 	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
@@ -658,10 +662,10 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
 		cmds_max = max_cmds;
 	}
 
-	cls_session = iscsi_session_setup(&iscsi_iser_transport, shost,
+	cls_session = iscsi_session_setup(&iscsi_iser_transport, shost, grp,
 					  cmds_max, 0,
 					  sizeof(struct iscsi_iser_task),
-					  initial_cmdsn, 0);
+					  initial_cmdsn, queue_id, 0);
 	if (!cls_session)
 		goto remove_host;
 	session = cls_session->dd_data;
@@ -781,6 +785,7 @@ static int iscsi_iser_get_ep_param(struct iscsi_endpoint *ep,
  * @shost:          scsi_host
  * @dst_addr:       destination address
  * @non-blocking:   indicate if routine can block
+ * @queue_id:       mq queue id
  *
  * Allocate an iscsi endpoint, an iser_conn structure and bind them.
  * After that start RDMA connection establishment via rdma_cm. We
@@ -793,7 +798,7 @@ static int iscsi_iser_get_ep_param(struct iscsi_endpoint *ep,
  */
 static struct iscsi_endpoint *
 iscsi_iser_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
-		      int non_blocking)
+		      int non_blocking, uint32_t queue_id)
 {
 	int err;
 	struct iser_conn *iser_conn;
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index 80d97f3..c9a57ff 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -421,7 +421,7 @@ static struct be_mcc_compl *be_mcc_compl_get(struct beiscsi_hba *phba)
  **/
 void be2iscsi_fail_session(struct iscsi_cls_session *cls_session)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&cls_session->dev);
 	struct beiscsi_hba *phba = iscsi_host_priv(shost);
 	uint32_t iscsi_err_flag;
 
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index b7391a3..ebfac73 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -33,14 +33,19 @@ extern struct iscsi_transport beiscsi_iscsi_transport;
 
 /**
  * beiscsi_session_create - creates a new iscsi session
+ * @grp: grp to add session to if mq is enabled
+ * @ep: endpoint handle
  * @cmds_max: max commands supported
  * @qdepth: max queue depth supported
  * @initial_cmdsn: initial iscsi CMDSN
+ * @queue_id: mq queue id
  */
-struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
+struct iscsi_cls_session *beiscsi_session_create(struct iscsi_session_grp *grp,
+						 struct iscsi_endpoint *ep,
 						 u16 cmds_max,
 						 u16 qdepth,
-						 u32 initial_cmdsn)
+						 u32 initial_cmdsn,
+						 u32 queue_id)
 {
 	struct Scsi_Host *shost;
 	struct beiscsi_endpoint *beiscsi_ep;
@@ -81,10 +86,11 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
 
 	shost = phba->shost;
 	cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
-					  shost, cmds_max,
+					  shost, grp, cmds_max,
 					  sizeof(*beiscsi_sess),
 					  sizeof(*io_task),
-					  initial_cmdsn, ISCSI_MAX_TARGET);
+					  initial_cmdsn, queue_id,
+					  ISCSI_MAX_TARGET);
 	if (!cls_session)
 		return NULL;
 	sess = cls_session->dd_data;
@@ -123,9 +129,12 @@ void beiscsi_session_destroy(struct iscsi_cls_session *cls_session)
  * beiscsi_conn_create - create an instance of iscsi connection
  * @cls_session: ptr to iscsi_cls_session
  * @cid: iscsi cid
+ * @queue_id: mq queue id
+ *
  */
 struct iscsi_cls_conn *
-beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid)
+beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid,
+		    u32 queue_id)
 {
 	struct beiscsi_hba *phba;
 	struct Scsi_Host *shost;
@@ -135,7 +144,7 @@ beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid)
 	struct iscsi_session *sess;
 	struct beiscsi_session *beiscsi_sess;
 
-	shost = iscsi_session_to_shost(cls_session);
+	shost = dev_to_shost(&cls_session->dev);
 	phba = iscsi_host_priv(shost);
 
 	beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
@@ -198,7 +207,7 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
 {
 	struct iscsi_conn *conn = cls_conn->dd_data;
 	struct beiscsi_conn *beiscsi_conn = conn->dd_data;
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&cls_session->dev);
 	struct beiscsi_hba *phba = iscsi_host_priv(shost);
 	struct hwi_controller *phwi_ctrlr = phba->phwi_ctrlr;
 	struct hwi_wrb_context *pwrb_context;
@@ -1190,12 +1199,13 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
  * @scsi_host: Pointer to scsi_host structure
  * @dst_addr: The IP address of Target
  * @non_blocking: blocking or non-blocking call
+ * @queue_id: mq queue id
  *
  * This routines first asks chip to create a connection and then allocates an EP
  */
 struct iscsi_endpoint *
 beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
-		   int non_blocking)
+		   int non_blocking, u32 queue_id)
 {
 	struct beiscsi_hba *phba;
 	struct beiscsi_endpoint *beiscsi_ep;
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index e0b3b2d..cd2187d 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -50,15 +50,18 @@ void beiscsi_offload_iscsi(struct beiscsi_hba *phba, struct iscsi_conn *conn,
 			   struct beiscsi_conn *beiscsi_conn,
 			   unsigned int fw_handle);
 
-struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
+struct iscsi_cls_session *beiscsi_session_create(struct iscsi_session_grp *grp,
+						 struct iscsi_endpoint *ep,
 						 uint16_t cmds_max,
 						 uint16_t qdepth,
-						 uint32_t initial_cmdsn);
+						 uint32_t initial_cmdsn,
+						 uint32_t queue_id);
 
 void beiscsi_session_destroy(struct iscsi_cls_session *cls_session);
 
 struct iscsi_cls_conn *beiscsi_conn_create(struct iscsi_cls_session
-					   *cls_session, uint32_t cid);
+					   *cls_session, uint32_t cid,
+					   uint32_t queue_id);
 
 int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
 		      struct iscsi_cls_conn *cls_conn,
@@ -79,7 +82,7 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn);
 
 struct iscsi_endpoint *beiscsi_ep_connect(struct Scsi_Host *shost,
 					  struct sockaddr *dst_addr,
-					  int non_blocking);
+					  int non_blocking, uint32_t queue_id);
 
 int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms);
 
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index f319340..8f8d4d1 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -218,7 +218,6 @@ static int beiscsi_slave_configure(struct scsi_device *sdev)
 
 static int beiscsi_eh_abort(struct scsi_cmnd *sc)
 {
-	struct iscsi_cls_session *cls_session;
 	struct iscsi_task *aborted_task = (struct iscsi_task *)sc->SCp.ptr;
 	struct beiscsi_io_task *aborted_io_task;
 	struct iscsi_conn *conn;
@@ -230,9 +229,7 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
 	unsigned int cid, tag, num_invalidate;
 	int rc;
 
-	cls_session = starget_to_session(scsi_target(sc->device));
-	session = cls_session->dd_data;
-
+	session = scsi_cmd_to_session(sc);
 	spin_lock_bh(&session->frwd_lock);
 	if (!aborted_task || !aborted_task->sc) {
 		/* we raced */
@@ -302,15 +299,13 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
 	struct beiscsi_conn *beiscsi_conn;
 	struct beiscsi_hba *phba;
 	struct iscsi_session *session;
-	struct iscsi_cls_session *cls_session;
 	struct invalidate_command_table *inv_tbl;
 	struct be_dma_mem nonemb_cmd;
 	unsigned int cid, tag, i, num_invalidate;
 	int rc;
 
 	/* invalidate iocbs */
-	cls_session = starget_to_session(scsi_target(sc->device));
-	session = cls_session->dd_data;
+	session = scsi_cmd_to_session(sc);
 	spin_lock_bh(&session->frwd_lock);
 	if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) {
 		spin_unlock_bh(&session->frwd_lock);
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index e53078d..55d2aa4 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -1222,7 +1222,7 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
 {
 	struct iscsi_conn *conn = task->conn;
 	struct iscsi_session *session = conn->session;
-	struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&session->cls_session->dev);
 	struct bnx2i_hba *hba = iscsi_host_priv(shost);
 	struct bnx2i_conn *bnx2i_conn = conn->dd_data;
 	struct scsi_cmnd *sc = task->sc;
@@ -1275,16 +1275,19 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
 
 /**
  * bnx2i_session_create - create a new iscsi session
+ * @grp:		grp to add session to if mq is enabled
+ * @ep:			endpoint handle
  * @cmds_max:		max commands supported
  * @qdepth:		scsi queue depth to support
  * @initial_cmdsn:	initial iscsi CMDSN to be used for this session
+ * @queue_id:		mq queue id
  *
  * Creates a new iSCSI session instance on given device.
  */
 static struct iscsi_cls_session *
-bnx2i_session_create(struct iscsi_endpoint *ep,
+bnx2i_session_create(struct iscsi_session_grp *grp, struct iscsi_endpoint *ep,
 		     uint16_t cmds_max, uint16_t qdepth,
-		     uint32_t initial_cmdsn)
+		     uint32_t initial_cmdsn, uint32_t queue_id)
 {
 	struct Scsi_Host *shost;
 	struct iscsi_cls_session *cls_session;
@@ -1311,9 +1314,10 @@ bnx2i_session_create(struct iscsi_endpoint *ep,
 	else if (cmds_max < BNX2I_SQ_WQES_MIN)
 		cmds_max = BNX2I_SQ_WQES_MIN;
 
-	cls_session = iscsi_session_setup(&bnx2i_iscsi_transport, shost,
+	cls_session = iscsi_session_setup(&bnx2i_iscsi_transport, shost, grp,
 					  cmds_max, 0, sizeof(struct bnx2i_cmd),
-					  initial_cmdsn, ISCSI_MAX_TARGET);
+					  initial_cmdsn, queue_id,
+					  ISCSI_MAX_TARGET);
 	if (!cls_session)
 		return NULL;
 
@@ -1337,7 +1341,7 @@ session_teardown:
 static void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
 {
 	struct iscsi_session *session = cls_session->dd_data;
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&cls_session->dev);
 	struct bnx2i_hba *hba = iscsi_host_priv(shost);
 
 	bnx2i_destroy_cmd_pool(hba, session);
@@ -1349,13 +1353,15 @@ static void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
  * bnx2i_conn_create - create iscsi connection instance
  * @cls_session:	pointer to iscsi cls session
  * @cid:		iscsi cid as per rfc (not NX2's CID terminology)
+ * @queue_id:		mq queue id
  *
  * Creates a new iSCSI connection instance for a given session
  */
 static struct iscsi_cls_conn *
-bnx2i_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid)
+bnx2i_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid,
+		  uint32_t queue_id)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&cls_session->dev);
 	struct bnx2i_hba *hba = iscsi_host_priv(shost);
 	struct bnx2i_conn *bnx2i_conn;
 	struct iscsi_cls_conn *cls_conn;
@@ -1408,7 +1414,7 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
 {
 	struct iscsi_conn *conn = cls_conn->dd_data;
 	struct bnx2i_conn *bnx2i_conn = conn->dd_data;
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&cls_session->dev);
 	struct bnx2i_hba *hba = iscsi_host_priv(shost);
 	struct bnx2i_endpoint *bnx2i_ep;
 	struct iscsi_endpoint *ep;
@@ -1480,7 +1486,7 @@ static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
 	unsigned cpu = 0;
 	struct bnx2i_percpu_s *p;
 
-	shost = iscsi_session_to_shost(iscsi_conn_to_session(cls_conn));
+	shost = dev_to_shost(&cls_conn->dev);
 	hba = iscsi_host_priv(shost);
 
 	bnx2i_conn_free_login_resources(hba, bnx2i_conn);
@@ -1761,6 +1767,7 @@ static int bnx2i_tear_down_conn(struct bnx2i_hba *hba,
  * @shost:		scsi host
  * @dst_addr:		target IP address
  * @non_blocking:	blocking or non-blocking call
+ * @queue_id:		mq queue id
  *
  * this routine initiates the TCP/IP connection by invoking Option-2 i/f
  *	with l5_core and the CNIC. This is a multi-step process of resolving
@@ -1770,7 +1777,8 @@ static int bnx2i_tear_down_conn(struct bnx2i_hba *hba,
  */
 static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
 					       struct sockaddr *dst_addr,
-					       int non_blocking)
+					       int non_blocking,
+					       uint32_t queue_id)
 {
 	u32 iscsi_cid = BNX2I_CID_RESERVED;
 	struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index eb58afc..96d2196 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -2492,7 +2492,7 @@ int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param,
 EXPORT_SYMBOL_GPL(cxgbi_get_ep_param);
 
 struct iscsi_cls_conn *
-cxgbi_create_conn(struct iscsi_cls_session *cls_session, u32 cid)
+cxgbi_create_conn(struct iscsi_cls_session *cls_session, u32 cid, u32 queue_id)
 {
 	struct iscsi_cls_conn *cls_conn;
 	struct iscsi_conn *conn;
@@ -2566,9 +2566,10 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
 }
 EXPORT_SYMBOL_GPL(cxgbi_bind_conn);
 
-struct iscsi_cls_session *cxgbi_create_session(struct iscsi_endpoint *ep,
+struct iscsi_cls_session *cxgbi_create_session(struct iscsi_session_grp *grp,
+						struct iscsi_endpoint *ep,
 						u16 cmds_max, u16 qdepth,
-						u32 initial_cmdsn)
+						u32 initial_cmdsn, u32 queue_id)
 {
 	struct cxgbi_endpoint *cep;
 	struct cxgbi_hba *chba;
@@ -2587,11 +2588,12 @@ struct iscsi_cls_session *cxgbi_create_session(struct iscsi_endpoint *ep,
 
 	BUG_ON(chba != iscsi_host_priv(shost));
 
-	cls_session = iscsi_session_setup(chba->cdev->itp, shost,
+	cls_session = iscsi_session_setup(chba->cdev->itp, shost, grp,
 					cmds_max, 0,
 					sizeof(struct iscsi_tcp_task) +
 					sizeof(struct cxgbi_task_data),
-					initial_cmdsn, ISCSI_MAX_TARGET);
+					initial_cmdsn, queue_id,
+					ISCSI_MAX_TARGET);
 	if (!cls_session)
 		return NULL;
 
@@ -2697,7 +2699,7 @@ EXPORT_SYMBOL_GPL(cxgbi_get_host_param);
 
 struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost,
 					struct sockaddr *dst_addr,
-					int non_blocking)
+					int non_blocking, u32 queue_id)
 {
 	struct iscsi_endpoint *ep;
 	struct cxgbi_endpoint *cep;
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h
index aba1af7..3e53e14 100644
--- a/drivers/scsi/cxgbi/libcxgbi.h
+++ b/drivers/scsi/cxgbi/libcxgbi.h
@@ -729,17 +729,17 @@ void cxgbi_get_conn_stats(struct iscsi_cls_conn *, struct iscsi_stats *);
 int cxgbi_set_conn_param(struct iscsi_cls_conn *,
 			enum iscsi_param, char *, int);
 int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param, char *);
-struct iscsi_cls_conn *cxgbi_create_conn(struct iscsi_cls_session *, u32);
+struct iscsi_cls_conn *cxgbi_create_conn(struct iscsi_cls_session *, u32, u32);
 int cxgbi_bind_conn(struct iscsi_cls_session *,
 			struct iscsi_cls_conn *, u64, int);
 void cxgbi_destroy_session(struct iscsi_cls_session *);
-struct iscsi_cls_session *cxgbi_create_session(struct iscsi_endpoint *,
-			u16, u16, u32);
+struct iscsi_cls_session *cxgbi_create_session(struct iscsi_session_grp *,
+			struct iscsi_endpoint *, u16, u16, u32, u32);
 int cxgbi_set_host_param(struct Scsi_Host *,
 			enum iscsi_host_param, char *, int);
 int cxgbi_get_host_param(struct Scsi_Host *, enum iscsi_host_param, char *);
 struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *,
-			struct sockaddr *, int);
+			struct sockaddr *, int, u32);
 int cxgbi_ep_poll(struct iscsi_endpoint *, int);
 void cxgbi_ep_disconnect(struct iscsi_endpoint *);
 
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 0b8af18..1754d84 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -537,7 +537,7 @@ static int iscsi_sw_tcp_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
 
 static struct iscsi_cls_conn *
 iscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session,
-			 uint32_t conn_idx)
+			 uint32_t conn_idx, uint32_t queue_idx)
 {
 	struct iscsi_conn *conn;
 	struct iscsi_cls_conn *cls_conn;
@@ -815,9 +815,69 @@ iscsi_sw_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
 	iscsi_tcp_conn_get_stats(cls_conn, stats);
 }
 
+static struct Scsi_Host *iscsi_sw_tcp_create_host(uint32_t nr_sessions)
+{
+	struct Scsi_Host *shost;
+
+	shost = iscsi_host_alloc(&iscsi_sw_tcp_sht,
+				 sizeof(struct iscsi_sw_tcp_host), 1);
+	if (!shost)
+		return shost;
+
+	shost->transportt = iscsi_sw_tcp_scsi_transport;
+	shost->max_lun = iscsi_max_lun;
+	shost->max_id = 0;
+	shost->max_channel = 0;
+	shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
+	shost->nr_hw_queues = nr_sessions;
+
+	if (iscsi_host_add(shost, NULL))
+		goto free_host;
+
+	return shost;
+free_host:
+	iscsi_host_free(shost);
+	return NULL;
+}
+
+void iscsi_sw_tcp_session_grp_destroy(struct iscsi_session_grp *grp)
+{
+	struct Scsi_Host *shost = dev_to_shost(&grp->dev);
+
+	iscsi_destroy_session_grp(grp);
+	iscsi_host_remove(shost);
+	iscsi_host_free(shost);
+}
+
+static struct iscsi_session_grp *
+iscsi_sw_tcp_session_grp_create(uint32_t host_no, uint32_t nr_sessions)
+{
+	struct Scsi_Host *shost;
+	struct iscsi_session_grp *grp;
+
+	shost = iscsi_sw_tcp_create_host(nr_sessions);
+	if (!shost)
+		goto fail;
+
+	grp = iscsi_create_session_grp(&iscsi_sw_tcp_transport, shost,
+				       nr_sessions);
+	if (!grp)
+		goto destroy_host;
+
+	return grp;
+
+destroy_host:
+	iscsi_host_remove(shost);
+	iscsi_host_free(shost);
+fail:
+	return NULL;
+}
+
 static struct iscsi_cls_session *
-iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
-			    uint16_t qdepth, uint32_t initial_cmdsn)
+iscsi_sw_tcp_session_create(struct iscsi_session_grp *grp,
+			    struct iscsi_endpoint *ep, uint16_t cmds_max,
+			    uint16_t qdepth, uint32_t initial_cmdsn,
+			    uint32_t queue_id)
 {
 	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
@@ -829,25 +889,25 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
 		return NULL;
 	}
 
-	shost = iscsi_host_alloc(&iscsi_sw_tcp_sht,
-				 sizeof(struct iscsi_sw_tcp_host), 1);
+	/*
+	 * MQ enabled tools do a host per session group. Non-mq tools expect
+	 * host per session.
+	 */
+	if (grp) {
+		shost = dev_to_shost(&grp->dev);
+	} else {
+		shost = iscsi_sw_tcp_create_host(0);
+	}
+
 	if (!shost)
 		return NULL;
-	shost->transportt = iscsi_sw_tcp_scsi_transport;
 	shost->cmd_per_lun = qdepth;
-	shost->max_lun = iscsi_max_lun;
-	shost->max_id = 0;
-	shost->max_channel = 0;
-	shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
-
-	if (iscsi_host_add(shost, NULL))
-		goto free_host;
 
 	cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost,
-					  cmds_max, 0,
+					  grp, cmds_max, 0,
 					  sizeof(struct iscsi_tcp_task) +
 					  sizeof(struct iscsi_sw_tcp_hdrbuf),
-					  initial_cmdsn, 0);
+					  initial_cmdsn, queue_id, 0);
 	if (!cls_session)
 		goto remove_host;
 	session = cls_session->dd_data;
@@ -862,21 +922,28 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
 remove_session:
 	iscsi_session_teardown(cls_session);
 remove_host:
-	iscsi_host_remove(shost);
-free_host:
-	iscsi_host_free(shost);
+	if (!grp) {
+		iscsi_host_remove(shost);
+		iscsi_host_free(shost);
+	}
 	return NULL;
 }
 
 static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct Scsi_Host *shost = dev_to_shost(&cls_session->dev);
+	bool in_group = false;
+
+	if (cls_session->grp)
+		in_group = true;
 
 	iscsi_tcp_r2tpool_free(cls_session->dd_data);
 	iscsi_session_teardown(cls_session);
 
-	iscsi_host_remove(shost);
-	iscsi_host_free(shost);
+	if (!in_group) {
+		iscsi_host_remove(shost);
+		iscsi_host_free(shost);
+	}
 }
 
 static umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param)
@@ -975,6 +1042,8 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
 	.caps			= CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
 				  | CAP_DATADGST,
 	/* session management */
+	.create_session_grp	= iscsi_sw_tcp_session_grp_create,
+	.destroy_session_grp	= iscsi_sw_tcp_session_grp_destroy,
 	.create_session		= iscsi_sw_tcp_session_create,
 	.destroy_session	= iscsi_sw_tcp_session_destroy,
 	/* connection management */
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 8053f24..3ea5b92 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -27,6 +27,10 @@
 #include <linux/log2.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/blk-mq.h>
+/* tmp hack for look up. Next patch will use tagging lookup or add proper callout and remove this */
+#include <../../block/blk-mq.h>
 #include <asm/unaligned.h>
 #include <net/tcp.h>
 #include <scsi/scsi_cmnd.h>
@@ -1628,6 +1632,30 @@ static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn,
 	return task;
 }
 
+struct iscsi_session *scsi_cmd_to_session(struct scsi_cmnd *sc)
+{
+	unsigned int queue_id;
+	struct iscsi_session_grp *grp;
+	struct iscsi_cls_session *cls_session;
+
+	if (sc->request->mq_ctx) {
+		/* temp hack - we should not be digging into these structs. Add callout/helpers so LLD can store session in hctx->driver_data, or add support for mq tagging and use that look up like srp. Next patch will fix */
+
+		queue_id = sc->request->q->mq_map[sc->request->mq_ctx->cpu];
+		grp = starget_to_session_grp(scsi_target(sc->device));
+		cls_session = grp->session_map[queue_id];
+		if (!cls_session)
+			return NULL;
+		printk(KERN_ERR "using %u %s %s\n", queue_id,
+			dev_name(&grp->dev), dev_name(&cls_session->dev));
+	} else {
+		cls_session = starget_to_session(scsi_target(sc->device));
+	}
+
+	return cls_session->dd_data;
+}
+EXPORT_SYMBOL_GPL(scsi_cmd_to_session);
+
 enum {
 	FAILURE_BAD_HOST = 1,
 	FAILURE_SESSION_FAILED,
@@ -1643,7 +1671,6 @@ enum {
 
 int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
 {
-	struct iscsi_cls_session *cls_session;
 	struct iscsi_host *ihost;
 	int reason = 0;
 	struct iscsi_session *session;
@@ -1654,12 +1681,16 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
 	sc->SCp.ptr = NULL;
 
 	ihost = shost_priv(host);
+	session = scsi_cmd_to_session(sc);
+	if (!session) {
+		sc->result = DID_NO_CONNECT << 16;
+		spin_lock_bh(&session->frwd_lock);
+		goto fault;
+	}
 
-	cls_session = starget_to_session(scsi_target(sc->device));
-	session = cls_session->dd_data;
 	spin_lock_bh(&session->frwd_lock);
 
-	reason = iscsi_session_chkready(cls_session);
+	reason = iscsi_session_chkready(session->cls_session);
 	if (reason) {
 		sc->result = reason;
 		goto fault;
@@ -2120,16 +2151,13 @@ static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
 
 int iscsi_eh_abort(struct scsi_cmnd *sc)
 {
-	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
 	struct iscsi_conn *conn;
 	struct iscsi_task *task;
 	struct iscsi_tm *hdr;
 	int rc, age;
 
-	cls_session = starget_to_session(scsi_target(sc->device));
-	session = cls_session->dd_data;
-
+	session = scsi_cmd_to_session(sc);
 	ISCSI_DBG_EH(session, "aborting sc %p\n", sc);
 
 	mutex_lock(&session->eh_mutex);
@@ -2260,15 +2288,12 @@ static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
 
 int iscsi_eh_device_reset(struct scsi_cmnd *sc)
 {
-	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
 	struct iscsi_conn *conn;
 	struct iscsi_tm *hdr;
 	int rc = FAILED;
 
-	cls_session = starget_to_session(scsi_target(sc->device));
-	session = cls_session->dd_data;
-
+	session = scsi_cmd_to_session(sc);
 	ISCSI_DBG_EH(session, "LU Reset [sc %p lun %llu]\n", sc,
 		     sc->device->lun);
 
@@ -2355,12 +2380,10 @@ EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
  */
 int iscsi_eh_session_reset(struct scsi_cmnd *sc)
 {
-	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
 	struct iscsi_conn *conn;
 
-	cls_session = starget_to_session(scsi_target(sc->device));
-	session = cls_session->dd_data;
+	session = scsi_cmd_to_session(sc);
 	conn = session->leadconn;
 
 	mutex_lock(&session->eh_mutex);
@@ -2423,15 +2446,12 @@ static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
  */
 int iscsi_eh_target_reset(struct scsi_cmnd *sc)
 {
-	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
 	struct iscsi_conn *conn;
 	struct iscsi_tm *hdr;
 	int rc = FAILED;
 
-	cls_session = starget_to_session(scsi_target(sc->device));
-	session = cls_session->dd_data;
-
+	session = scsi_cmd_to_session(sc);
 	ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc,
 		     session->targetname);
 
@@ -2701,9 +2721,12 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost)
  * iscsi_session_setup - create iscsi cls session and host and session
  * @iscsit: iscsi transport template
  * @shost: scsi host
+ * @grp: session group
  * @cmds_max: session can queue
  * @cmd_task_size: LLD task private data size
  * @initial_cmdsn: initial CmdSN
+ * @queue_id: mq queue id
+ * @id: optional scsi target id
  *
  * This can be used by software iscsi_transports that allocate
  * a session per scsi host.
@@ -2714,8 +2737,9 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost)
  */
 struct iscsi_cls_session *
 iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
-		    uint16_t cmds_max, int dd_size, int cmd_task_size,
-		    uint32_t initial_cmdsn, unsigned int id)
+		    struct iscsi_session_grp *grp, uint16_t cmds_max,
+		    int dd_size, int cmd_task_size, uint32_t initial_cmdsn,
+		    uint32_t queue_id, unsigned int id)
 {
 	struct iscsi_host *ihost = shost_priv(shost);
 	struct iscsi_session *session;
@@ -2763,11 +2787,13 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
 	}
 	scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX;
 
-	cls_session = iscsi_alloc_session(shost, iscsit,
+	cls_session = iscsi_alloc_session(iscsit, shost, grp,
 					  sizeof(struct iscsi_session) +
 					  dd_size);
 	if (!cls_session)
 		goto dec_session_count;
+	cls_session->queue_id = queue_id;
+
 	session = cls_session->dd_data;
 	session->cls_session = cls_session;
 	session->host = shost;
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 6d25879..2589dae 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -120,21 +120,24 @@ static int qla4xxx_get_iface_param(struct iscsi_iface *iface,
 static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc);
 static struct iscsi_endpoint *qla4xxx_ep_connect(struct Scsi_Host *shost,
 						 struct sockaddr *dst_addr,
-						 int non_blocking);
+						 int non_blocking,
+						 uint32_t queue_id);
 static int qla4xxx_ep_poll(struct iscsi_endpoint *ep, int timeout_ms);
 static void qla4xxx_ep_disconnect(struct iscsi_endpoint *ep);
 static int qla4xxx_get_ep_param(struct iscsi_endpoint *ep,
 				enum iscsi_param param, char *buf);
 static int qla4xxx_conn_start(struct iscsi_cls_conn *conn);
 static struct iscsi_cls_conn *
-qla4xxx_conn_create(struct iscsi_cls_session *cls_sess, uint32_t conn_idx);
+qla4xxx_conn_create(struct iscsi_cls_session *cls_sess, uint32_t conn_idx,
+		    uint32_t queue_idx);
 static int qla4xxx_conn_bind(struct iscsi_cls_session *cls_session,
 			     struct iscsi_cls_conn *cls_conn,
 			     uint64_t transport_fd, int is_leading);
 static void qla4xxx_conn_destroy(struct iscsi_cls_conn *conn);
 static struct iscsi_cls_session *
-qla4xxx_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
-			uint16_t qdepth, uint32_t initial_cmdsn);
+qla4xxx_session_create(struct iscsi_session_grp *grp, struct iscsi_endpoint *ep,
+			uint16_t cmds_max, uint16_t qdepth,
+			uint32_t initial_cmdsn, uint32_t queue_id);
 static void qla4xxx_session_destroy(struct iscsi_cls_session *sess);
 static void qla4xxx_task_work(struct work_struct *wdata);
 static int qla4xxx_alloc_pdu(struct iscsi_task *, uint8_t);
@@ -1658,7 +1661,7 @@ static int qla4xxx_get_iface_param(struct iscsi_iface *iface,
 
 static struct iscsi_endpoint *
 qla4xxx_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
-		   int non_blocking)
+		   int non_blocking, uint32_t queue_id)
 {
 	int ret;
 	struct iscsi_endpoint *ep;
@@ -1835,8 +1838,8 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc)
 	unsigned long flags;
 	enum blk_eh_timer_return ret = BLK_EH_NOT_HANDLED;
 
-	session = starget_to_session(scsi_target(sc->device));
-	sess = session->dd_data;
+	sess = scsi_cmd_to_session(sc);
+	session = sess->cls_session;
 
 	spin_lock_irqsave(&session->lock, flags);
 	if (session->state == ISCSI_SESSION_FAILED)
@@ -3036,9 +3039,9 @@ static int qla4xxx_match_fwdb_session(struct scsi_qla_host *ha,
 }
 
 static struct iscsi_cls_session *
-qla4xxx_session_create(struct iscsi_endpoint *ep,
+qla4xxx_session_create(struct iscsi_session_grp *grp, struct iscsi_endpoint *ep,
 			uint16_t cmds_max, uint16_t qdepth,
-			uint32_t initial_cmdsn)
+			uint32_t initial_cmdsn, uint32_t queue_id)
 {
 	struct iscsi_cls_session *cls_sess;
 	struct scsi_qla_host *ha;
@@ -3065,9 +3068,9 @@ qla4xxx_session_create(struct iscsi_endpoint *ep,
 		return NULL;
 
 	cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, qla_ep->host,
-				       cmds_max, sizeof(struct ddb_entry),
+				       grp, cmds_max, sizeof(struct ddb_entry),
 				       sizeof(struct ql4_task_data),
-				       initial_cmdsn, ddb_index);
+				       initial_cmdsn, queue_id, ddb_index);
 	if (!cls_sess)
 		return NULL;
 
@@ -3144,7 +3147,8 @@ destroy_session:
 }
 
 static struct iscsi_cls_conn *
-qla4xxx_conn_create(struct iscsi_cls_session *cls_sess, uint32_t conn_idx)
+qla4xxx_conn_create(struct iscsi_cls_session *cls_sess, uint32_t conn_idx,
+		    uint32_t queue_idx)
 {
 	struct iscsi_cls_conn *cls_conn;
 	struct iscsi_session *sess;
@@ -6574,7 +6578,7 @@ static struct iscsi_endpoint *qla4xxx_get_ep_fwdb(struct scsi_qla_host *ha,
 		addr->sin_port = htons(le16_to_cpu(fw_ddb_entry->port));
 	}
 
-	ep = qla4xxx_ep_connect(ha->host, (struct sockaddr *)dst_addr, 0);
+	ep = qla4xxx_ep_connect(ha->host, (struct sockaddr *)dst_addr, 0, 0);
 	vfree(dst_addr);
 	return ep;
 }
@@ -6885,9 +6889,9 @@ static int qla4xxx_sess_conn_setup(struct scsi_qla_host *ha,
 	 * the targer_id would get set when we issue the login
 	 */
 	cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, ha->host,
-				       cmds_max, sizeof(struct ddb_entry),
+				       NULL, cmds_max, sizeof(struct ddb_entry),
 				       sizeof(struct ql4_task_data),
-				       initial_cmdsn, INVALID_ENTRY);
+				       initial_cmdsn, 0, INVALID_ENTRY);
 	if (!cls_sess) {
 		ret = QLA_ERROR;
 		goto exit_setup;
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 67d43e3..3997d1f 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -79,6 +79,7 @@ struct iscsi_internal {
 	struct transport_container session_cont;
 };
 
+static atomic_t iscsi_session_grp_nr; /* sysfs id for next new session group */
 static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
 static struct workqueue_struct *iscsi_eh_timer_workq;
 
@@ -90,6 +91,7 @@ static DEFINE_IDA(iscsi_sess_ida);
  */
 static LIST_HEAD(iscsi_transports);
 static DEFINE_SPINLOCK(iscsi_transport_lock);
+static struct sock *nls;
 
 #define to_iscsi_internal(tmpl) \
 	container_of(tmpl, struct iscsi_internal, t)
@@ -97,6 +99,29 @@ static DEFINE_SPINLOCK(iscsi_transport_lock);
 #define dev_to_iscsi_internal(_dev) \
 	container_of(_dev, struct iscsi_internal, dev)
 
+static struct iscsi_internal *
+iscsi_if_transport_lookup(struct iscsi_transport *tt)
+{
+	struct iscsi_internal *priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&iscsi_transport_lock, flags);
+	list_for_each_entry(priv, &iscsi_transports, list) {
+		if (tt == priv->iscsi_transport) {
+			spin_unlock_irqrestore(&iscsi_transport_lock, flags);
+			return priv;
+		}
+	}
+	spin_unlock_irqrestore(&iscsi_transport_lock, flags);
+	return NULL;
+}
+
+static int
+iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
+{
+	return nlmsg_multicast(nls, skb, 0, group, gfp);
+}
+
 static void iscsi_transport_release(struct device *dev)
 {
 	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
@@ -1606,7 +1631,6 @@ static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
 			       NULL,
 			       NULL);
 
-static struct sock *nls;
 static DEFINE_MUTEX(rx_queue_mutex);
 
 static LIST_HEAD(sesslist);
@@ -1728,8 +1752,10 @@ static void iscsi_session_release(struct device *dev)
 	struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
 	struct Scsi_Host *shost;
 
-	shost = iscsi_session_to_shost(session);
-	scsi_host_put(shost);
+	if (!session->grp) {
+		shost = dev_to_shost(dev);
+		scsi_host_put(shost);
+	}
 	ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n");
 	kfree(session);
 }
@@ -1758,6 +1784,232 @@ void iscsi_host_for_each_session(struct Scsi_Host *shost,
 }
 EXPORT_SYMBOL_GPL(iscsi_host_for_each_session);
 
+/*
+ * Group of sessions that can be used for multiqueue
+ */
+struct bus_type iscsi_session_grp_bus;
+
+static int iscsi_session_grp_bus_match(struct device *dev,
+				       struct device_driver *drv)
+{
+	if (dev->bus == &iscsi_session_grp_bus)
+		return 1;
+	return 0;
+}
+
+static void iscsi_session_grp_release(struct device *dev)
+{
+	struct iscsi_session_grp *grp = iscsi_dev_to_session_grp(dev);
+	struct device *parent = grp->dev.parent;
+
+	kfree(grp->session_map);
+	kfree(grp);
+	put_device(parent);
+}
+
+static int iscsi_is_session_grp_dev(const struct device *dev)
+{
+	return dev->type && dev->type->release == iscsi_session_grp_release;
+}
+
+struct device_type iscsi_session_grp_dev_type = {
+	.name		= "iscsi_session_grp_dev_type",
+	.release	= iscsi_session_grp_release,
+};
+
+struct bus_type iscsi_session_grp_bus = {
+	.name		= "iscsi_session_grp",
+	.match		= iscsi_session_grp_bus_match,
+};
+
+/**
+ * iscsi_session_grp_event - send session_grp request completionevent
+ * @grp: iscsi class group
+ * @event: type of event
+ */
+static int iscsi_session_grp_event(struct iscsi_session_grp *grp,
+				   enum iscsi_uevent_e event)
+{
+	struct iscsi_internal *priv;
+	struct Scsi_Host *shost;
+	struct iscsi_uevent *ev;
+	struct sk_buff  *skb;
+	struct nlmsghdr *nlh;
+	int rc, len = nlmsg_total_size(sizeof(*ev));
+
+	priv = iscsi_if_transport_lookup(grp->transport);
+	if (!priv)
+		return -EINVAL;
+	shost = dev_to_shost(&grp->dev);
+
+	skb = alloc_skb(len, GFP_KERNEL);
+	if (!skb) {
+		iscsi_session_grp_printk(KERN_ERR, grp,
+					 "Cannot notify userspace of grp event %u\n",
+					 event);
+		return -ENOMEM;
+	}
+
+	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+	ev = nlmsg_data(nlh);
+	ev->transport_handle = iscsi_handle(grp->transport);
+
+	ev->type = event;
+	switch (event) {
+	case ISCSI_KEVENT_MQ_UNBIND_SESSION_GRP:
+		ev->r.u_mq_session_grp_ret.host_no = shost->host_no;
+		ev->r.u_mq_session_grp_ret.gid = grp->gid;
+		break;
+	default:
+		iscsi_session_grp_printk(KERN_ERR, grp, "Invalid event %u.\n",
+				     event);
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+
+	/*
+	 * 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_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+	if (rc == -ESRCH)
+		iscsi_session_grp_printk(KERN_ERR, grp,
+					 "Cannot notify userspace of group event %u. Check iscsi daemon\n",
+					 event);
+	return rc;
+}
+
+static void iscsi_unbind_session_grp(struct work_struct *work)
+{
+	struct iscsi_session_grp *grp =
+				container_of(work, struct iscsi_session_grp,
+					     unbind_work);
+	struct Scsi_Host *shost = dev_to_shost(&grp->dev);
+	struct iscsi_cls_host *ihost = shost->shost_data;
+
+	/* Prevent new scans and make sure scanning is not in progress */
+	mutex_lock(&ihost->mutex);
+	if (grp->target_id == ISCSI_MAX_TARGET) {
+		mutex_unlock(&ihost->mutex);
+
+		wait_for_completion(&grp->unbind_wait);
+		return;
+	}
+
+	grp->target_id = ISCSI_MAX_TARGET;
+	mutex_unlock(&ihost->mutex);
+
+	scsi_remove_target(&grp->dev);
+	complete_all(&grp->unbind_wait);
+	iscsi_session_grp_event(grp, ISCSI_KEVENT_MQ_UNBIND_SESSION_GRP);
+}
+
+struct iscsi_session_grp *
+iscsi_create_session_grp(struct iscsi_transport *transport,
+			 struct Scsi_Host *shost, uint32_t nr_sessions)
+{
+	struct iscsi_session_grp *grp;
+	int err;
+
+	grp = kzalloc(sizeof(*grp), GFP_KERNEL);
+	if (!grp)
+		return NULL;
+
+	grp->session_map = kzalloc(nr_cpu_ids *
+				   sizeof(struct iscsi_session_grp *),
+				   GFP_KERNEL);
+	if (!grp->session_map)
+		goto free_grp;
+
+	init_completion(&grp->unbind_wait);
+	INIT_WORK(&grp->unbind_work, iscsi_unbind_session_grp);
+	grp->transport = transport;
+	grp->max_sessions = nr_sessions;
+	grp->max_queue_id = nr_cpu_ids;
+	grp->target_id = ISCSI_MAX_TARGET;
+	grp->gid = atomic_add_return(1, &iscsi_session_grp_nr);
+	grp->dev.type = &iscsi_session_grp_dev_type;
+	grp->dev.bus = &iscsi_session_grp_bus;
+	/* released in grp release */
+	grp->dev.parent = get_device(&shost->shost_gendev);
+	dev_set_name(&grp->dev, "session_group-%u:%u",
+		     shost->host_no, grp->gid);
+	err = device_register(&grp->dev);
+	if (err) {
+		shost_printk(KERN_ERR, shost,
+			     "Could not create session group %s. Error %d\n",
+			     dev_name(&grp->dev), err);
+		goto free_map;
+	}
+	return grp;
+
+free_map:
+	kfree(grp->session_map);
+free_grp:
+	kfree(grp);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(iscsi_create_session_grp);
+
+void iscsi_destroy_session_grp(struct iscsi_session_grp *grp)
+{
+	if (grp->session_count)
+		return;
+
+	device_unregister(&grp->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_destroy_session_grp);
+
+static int iscsi_session_grp_match_id(struct device *dev, void *data)
+{
+        struct iscsi_session_grp *grp;
+
+        if (!iscsi_session_grp_bus_match(dev, NULL))
+                return 0;
+
+        grp = iscsi_dev_to_session_grp(dev);
+        return (grp->gid == *((int *)data)) ? 1 : 0;
+}
+
+static struct iscsi_session_grp *
+iscsi_find_session_grp_by_ids(uint32_t host_no, uint32_t gid)
+{
+        struct iscsi_session_grp *grp = NULL;
+	struct Scsi_Host *shost;
+        struct device *dev;
+
+	shost = scsi_host_lookup(host_no);
+	if (!shost)
+		return NULL;
+
+        dev = device_find_child(&shost->shost_gendev, &gid,
+				iscsi_session_grp_match_id);
+        if (dev)
+                grp = iscsi_dev_to_session_grp(dev);
+
+	scsi_host_put(shost);
+        return grp;
+}
+
+static int iscsi_find_first_session(struct device *dev, void *data)
+{
+	return iscsi_is_session_dev(dev);
+}
+
+struct iscsi_cls_session *iscsi_dev_to_lead_session(struct device *dev)
+{
+	if (iscsi_is_session_dev(dev))
+		return iscsi_dev_to_session(dev);
+
+	/* dev is grp, so search it for first one we find */
+        dev = device_find_child(dev, NULL, iscsi_find_first_session);
+        if (dev)
+                return iscsi_dev_to_session(dev);
+	else
+		return NULL;
+}
+EXPORT_SYMBOL_GPL(iscsi_dev_to_lead_session);
+
 /**
  * iscsi_scan_finished - helper to report when running scans are done
  * @shost: scsi host
@@ -1787,22 +2039,27 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
 {
 	struct iscsi_scan_data *scan_data = data;
 	struct iscsi_cls_session *session;
+	struct iscsi_session_grp *grp;
 	struct Scsi_Host *shost;
 	struct iscsi_cls_host *ihost;
 	unsigned long flags;
 	unsigned int id;
 
-	if (!iscsi_is_session_dev(dev))
+	if (!iscsi_is_session_dev(dev) && !iscsi_is_session_grp_dev(dev))
 		return 0;
 
-	session = iscsi_dev_to_session(dev);
-
-	ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");
-
-	shost = iscsi_session_to_shost(session);
+	shost = dev_to_shost(dev);
 	ihost = shost->shost_data;
+	session = iscsi_dev_to_lead_session(dev);
+	ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");
 
 	mutex_lock(&ihost->mutex);
+	if (iscsi_is_session_grp_dev(dev)) {
+		grp = iscsi_dev_to_session_grp(dev);
+		if (!grp->session_count || grp->target_id == ISCSI_MAX_TARGET)
+			goto user_scan_exit;
+	}
+
 	spin_lock_irqsave(&session->lock, flags);
 	if (session->state != ISCSI_SESSION_LOGGED_IN) {
 		spin_unlock_irqrestore(&session->lock, flags);
@@ -1816,8 +2073,7 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
 		     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);
+			scsi_scan_target(dev, 0, id, scan_data->lun, 1);
 	}
 
 user_scan_exit:
@@ -1843,7 +2099,7 @@ 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 Scsi_Host *shost = dev_to_shost(&session->dev);
 	struct iscsi_cls_host *ihost = shost->shost_data;
 	struct iscsi_scan_data scan_data;
 
@@ -1923,7 +2179,7 @@ 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 Scsi_Host *shost = dev_to_shost(&session->dev);
 	struct iscsi_cls_host *ihost = shost->shost_data;
 	unsigned long flags;
 
@@ -1997,7 +2253,7 @@ 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 Scsi_Host *shost = dev_to_shost(&session->dev);
 	struct iscsi_cls_host *ihost = shost->shost_data;
 	unsigned long flags;
 	unsigned int target_id;
@@ -2021,14 +2277,59 @@ static void __iscsi_unbind_session(struct work_struct *work)
 	if (session->ida_used)
 		ida_simple_remove(&iscsi_sess_ida, target_id);
 
-	scsi_remove_target(&session->dev);
+	if (!session->grp)
+		scsi_remove_target(&session->dev);
+	else
+		iscsi_unbind_session_grp(&session->grp->unbind_work);
+
 	iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
 	ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
 }
 
+static int iscsi_session_grp_add_session(struct iscsi_cls_session *session)
+{
+	struct iscsi_session_grp *grp = session->grp;
+	struct Scsi_Host *shost = dev_to_shost(&session->dev);
+	struct iscsi_cls_host *ihost = shost->shost_data;
+	int err;
+
+	if (!grp || session->queue_id >= grp->max_queue_id ||
+	    grp->max_sessions < grp->session_count + 1)
+		return -EINVAL;
+
+	mutex_lock(&ihost->mutex);
+	if (grp->session_map[session->queue_id]) {
+		struct iscsi_cls_session *existing;
+
+		existing = grp->session_map[session->queue_id];
+
+		iscsi_cls_session_printk(KERN_ERR, session,
+					 "%s already setup in map\n",
+					 dev_name(&existing->dev));
+		err = -EINVAL;
+		goto unlock;
+	}
+	grp->session_map[session->queue_id] = session;
+	grp->session_count++;
+
+	err = sysfs_create_link(&session->dev.kobj, &grp->dev.kobj,
+				"session_group");
+	if (err)
+		goto unmap;
+	mutex_unlock(&ihost->mutex);
+	return 0;
+
+unmap:
+	grp->session_map[session->queue_id] = NULL;
+	grp->session_count--;
+unlock:
+	mutex_unlock(&ihost->mutex);
+	return err;
+}
+
 struct iscsi_cls_session *
-iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
-		    int dd_size)
+iscsi_alloc_session(struct iscsi_transport *transport, struct Scsi_Host *shost,
+		    struct iscsi_session_grp *grp, int dd_size)
 {
 	struct iscsi_cls_session *session;
 
@@ -2037,6 +2338,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
 	if (!session)
 		return NULL;
 
+	session->grp = grp;
 	session->transport = transport;
 	session->creator = -1;
 	session->recovery_tmo = 120;
@@ -2049,9 +2351,13 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
 	INIT_WORK(&session->scan_work, iscsi_scan_session);
 	spin_lock_init(&session->lock);
 
-	/* this is released in the dev's release function */
-	scsi_host_get(shost);
-	session->dev.parent = &shost->shost_gendev;
+	if (grp) {
+		session->dev.parent = &grp->dev;
+	} else {
+		/* this is released in the dev's release function */
+		scsi_host_get(shost);
+		session->dev.parent = &shost->shost_gendev;
+	}
 	session->dev.release = iscsi_session_release;
 	device_initialize(&session->dev);
 	if (dd_size)
@@ -2064,7 +2370,8 @@ EXPORT_SYMBOL_GPL(iscsi_alloc_session);
 
 int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(session);
+	struct Scsi_Host *shost = dev_to_shost(&session->dev);
+	struct iscsi_session_grp *grp = session->grp;
 	struct iscsi_cls_host *ihost;
 	unsigned long flags;
 	int id = 0;
@@ -2073,20 +2380,30 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
 	ihost = shost->shost_data;
 	session->sid = atomic_add_return(1, &iscsi_session_nr);
 
-	if (target_id == ISCSI_MAX_TARGET) {
+	if (grp && grp->target_id != ISCSI_MAX_TARGET) {
+		session->target_id = grp->target_id;
+	} else if (target_id == ISCSI_MAX_TARGET) {
 		id = ida_simple_get(&iscsi_sess_ida, 0, 0, GFP_KERNEL);
-
 		if (id < 0) {
 			iscsi_cls_session_printk(KERN_ERR, session,
 					"Failure in Target ID Allocation\n");
 			return id;
 		}
+
 		session->target_id = (unsigned int)id;
 		session->ida_used = true;
-	} else
+	} else {
 		session->target_id = target_id;
+	}
 
-	dev_set_name(&session->dev, "session%u", session->sid);
+	if (grp)
+		grp->target_id = session->target_id;
+//		dev_set_name(&session->dev, "session%u:%u:%u:%u",
+//			     shost->host_no, grp->gid, session->sid,
+//			     session->target_id);
+//	} else {
+		dev_set_name(&session->dev, "session%u", session->sid);
+//	}
 	err = device_add(&session->dev);
 	if (err) {
 		iscsi_cls_session_printk(KERN_ERR, session,
@@ -2099,10 +2416,18 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
 	list_add(&session->sess_list, &sesslist);
 	spin_unlock_irqrestore(&sesslock, flags);
 
+	if (grp) {
+		err = iscsi_session_grp_add_session(session);
+		if (err)
+			goto destroy_session;
+	}
+
 	iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
 	ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n");
 	return 0;
 
+destroy_session:
+	iscsi_destroy_session(session);
 release_ida:
 	if (session->ida_used)
 		ida_simple_remove(&iscsi_sess_ida, session->target_id);
@@ -2126,7 +2451,7 @@ iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
 {
 	struct iscsi_cls_session *session;
 
-	session = iscsi_alloc_session(shost, transport, dd_size);
+	session = iscsi_alloc_session(transport, shost, NULL, dd_size);
 	if (!session)
 		return NULL;
 
@@ -2160,9 +2485,25 @@ static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
 	return iscsi_destroy_conn(iscsi_dev_to_conn(dev));
 }
 
+static void iscsi_session_grp_del_session(struct iscsi_cls_session *session)
+{
+	struct iscsi_session_grp *grp = session->grp;
+	struct Scsi_Host *shost = dev_to_shost(&session->dev);
+	struct iscsi_cls_host *ihost = shost->shost_data;
+
+	if (!grp)
+		return;
+
+	mutex_lock(&ihost->mutex);
+	grp->session_map[session->queue_id] = NULL;
+	grp->session_count--;
+	sysfs_remove_link(&session->dev.kobj, "session_group");
+	mutex_unlock(&ihost->mutex);
+}
+
 void iscsi_remove_session(struct iscsi_cls_session *session)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(session);
+	struct Scsi_Host *shost = dev_to_shost(&session->dev);
 	unsigned long flags;
 	int err;
 
@@ -2201,6 +2542,7 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
 					 "for session. Error %d.\n", err);
 
 	transport_unregister_device(&session->dev);
+	iscsi_session_grp_del_session(session);
 
 	ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n");
 	device_del(&session->dev);
@@ -2320,29 +2662,6 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_conn);
 /*
  * iscsi interface functions
  */
-static struct iscsi_internal *
-iscsi_if_transport_lookup(struct iscsi_transport *tt)
-{
-	struct iscsi_internal *priv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&iscsi_transport_lock, flags);
-	list_for_each_entry(priv, &iscsi_transports, list) {
-		if (tt == priv->iscsi_transport) {
-			spin_unlock_irqrestore(&iscsi_transport_lock, flags);
-			return priv;
-		}
-	}
-	spin_unlock_irqrestore(&iscsi_transport_lock, flags);
-	return NULL;
-}
-
-static int
-iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
-{
-	return nlmsg_multicast(nls, skb, 0, group, gfp);
-}
-
 int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
 		   char *data, uint32_t data_size)
 {
@@ -2649,7 +2968,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
 	priv = iscsi_if_transport_lookup(session->transport);
 	if (!priv)
 		return -EINVAL;
-	shost = iscsi_session_to_shost(session);
+	shost = dev_to_shost(&session->dev);
 
 	skb = alloc_skb(len, GFP_KERNEL);
 	if (!skb) {
@@ -2702,22 +3021,62 @@ 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_endpoint *ep,
-			struct iscsi_uevent *ev, pid_t pid,
-			uint32_t initial_cmdsn,	uint16_t cmds_max,
-			uint16_t queue_depth)
+iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev,
+			pid_t pid, int event_type)
 {
 	struct iscsi_transport *transport = priv->iscsi_transport;
 	struct iscsi_cls_session *session;
+	struct iscsi_endpoint *ep = NULL;
+	struct iscsi_session_grp *grp = NULL;
+	uint32_t initial_cmdsn;
+	uint16_t cmds_max;
+	uint16_t queue_depth;
 	struct Scsi_Host *shost;
+	uint32_t queue_id = 0;
+
+	switch (event_type) {
+	case ISCSI_UEVENT_CREATE_SESSION:
+		initial_cmdsn = ev->u.c_session.initial_cmdsn;
+		cmds_max = ev->u.c_session.cmds_max;
+		queue_depth = 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)
+			return -EINVAL;
 
-	session = transport->create_session(ep, cmds_max, queue_depth,
-					    initial_cmdsn);
+		initial_cmdsn = ev->u.c_bound_session.initial_cmdsn;
+		cmds_max = ev->u.c_bound_session.cmds_max;
+		queue_depth = ev->u.c_bound_session.queue_depth;
+		break;
+	case ISCSI_UEVENT_MQ_CREATE_SESSION:
+		if (ev->u.c_mq_session.flags & ISCSI_UEVENT_FLAG_EP_BOUND) {
+			ep = iscsi_lookup_endpoint(ev->u.c_mq_session.ep_handle);
+			if (!ep)
+				return -EINVAL;
+		}
+		grp = iscsi_find_session_grp_by_ids(ev->u.c_mq_session.host_no,
+						    ev->u.c_mq_session.gid);
+		if (!grp) {
+			return -EINVAL;
+		}
+
+		queue_id = ev->u.c_mq_session.queue_id;
+		initial_cmdsn = ev->u.c_mq_session.initial_cmdsn;
+		cmds_max = ev->u.c_mq_session.cmds_max;
+		queue_depth = ev->u.c_mq_session.queue_depth;
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	session = transport->create_session(grp, ep, cmds_max, queue_depth,
+					    initial_cmdsn, queue_id);
 	if (!session)
 		return -ENOMEM;
 
 	session->creator = pid;
-	shost = iscsi_session_to_shost(session);
+	shost = dev_to_shost(&session->dev);
 	ev->r.c_session_ret.host_no = shost->host_no;
 	ev->r.c_session_ret.sid = session->sid;
 	ISCSI_DBG_TRANS_SESSION(session,
@@ -2726,19 +3085,31 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
 }
 
 static int
-iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev,
+		     int event_type)
 {
 	struct iscsi_cls_conn *conn;
 	struct iscsi_cls_session *session;
+	uint32_t queue_id = 0;
+	uint32_t sid, cid;
 
-	session = iscsi_session_lookup(ev->u.c_conn.sid);
+	if (event_type == ISCSI_UEVENT_MQ_CREATE_CONN) {
+		queue_id = ev->u.c_mq_conn.queue_id;
+		sid = ev->u.c_mq_conn.sid;
+		cid = ev->u.c_mq_conn.cid;
+	} else {
+		sid = ev->u.c_conn.sid;
+		cid = ev->u.c_conn.cid;
+	}
+
+	session = iscsi_session_lookup(sid);
 	if (!session) {
 		printk(KERN_ERR "iscsi: invalid session %d.\n",
 		       ev->u.c_conn.sid);
 		return -EINVAL;
 	}
 
-	conn = transport->create_conn(session, ev->u.c_conn.cid);
+	conn = transport->create_conn(session, cid, queue_id);
 	if (!conn) {
 		iscsi_cls_session_printk(KERN_ERR, session,
 					 "couldn't create a new connection.");
@@ -2801,11 +3172,13 @@ static int iscsi_if_ep_connect(struct iscsi_transport *transport,
 	struct sockaddr *dst_addr;
 	struct Scsi_Host *shost = NULL;
 	int non_blocking, err = 0;
+	uint32_t queue_id = 0;
 
 	if (!transport->ep_connect)
 		return -EINVAL;
 
-	if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) {
+	switch (msg_type) {
+	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
 		shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no);
 		if (!shost) {
 			printk(KERN_ERR "ep connect failed. Could not find "
@@ -2814,11 +3187,29 @@ static int iscsi_if_ep_connect(struct iscsi_transport *transport,
 			return -ENODEV;
 		}
 		non_blocking = ev->u.ep_connect_through_host.non_blocking;
-	} else
+		break;
+	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
 		non_blocking = ev->u.ep_connect.non_blocking;
+		break;
+	case ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT:
+		if (ev->u.ep_mq_connect.flags & ISCSI_UEVENT_FLAG_HOST_BOUND) {
+			shost = scsi_host_lookup(ev->u.ep_mq_connect.host_no);
+			if (!shost) {
+				printk(KERN_ERR "ep connect failed. Could not find host no %u\n",
+				       ev->u.ep_mq_connect.host_no);
+				return -ENODEV;
+			}
+		}
+
+		non_blocking = ev->u.ep_mq_connect.non_blocking;
+		queue_id = ev->u.ep_mq_connect.queue_id;
+		break;
+	default:
+		return -ENOSYS;
+	}
 
 	dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
-	ep = transport->ep_connect(shost, dst_addr, non_blocking);
+	ep = transport->ep_connect(shost, dst_addr, non_blocking, queue_id);
 	if (IS_ERR(ep)) {
 		err = PTR_ERR(ep);
 		goto release_host;
@@ -2864,6 +3255,7 @@ iscsi_if_transport_ep(struct iscsi_transport *transport,
 	switch (msg_type) {
 	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
 	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
+	case ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT:
 		rc = iscsi_if_ep_connect(transport, ev, msg_type);
 		break;
 	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
@@ -3495,6 +3887,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 	struct iscsi_internal *priv;
 	struct iscsi_cls_session *session;
 	struct iscsi_cls_conn *conn;
+	struct Scsi_Host *shost;
+	struct iscsi_session_grp *grp;
 	struct iscsi_endpoint *ep = NULL;
 
 	if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
@@ -3512,24 +3906,32 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 
 	switch (nlh->nlmsg_type) {
 	case ISCSI_UEVENT_CREATE_SESSION:
-		err = iscsi_if_create_session(priv, ep, ev,
-					      NETLINK_CB(skb).portid,
-					      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;
+	case ISCSI_UEVENT_MQ_CREATE_SESSION:
+		err = iscsi_if_create_session(priv, ev, NETLINK_CB(skb).portid,
+					      nlh->nlmsg_type);
+		break;
+	case ISCSI_UEVENT_MQ_CREATE_SESSION_GRP:
+		grp = transport->create_session_grp(
+					ev->u.c_mq_session_grp.host_no,
+					ev->u.c_mq_session_grp.nr_sessions);
+		if (!grp) {
+			err = -ENOMEM;
+		} else {
+			shost = dev_to_shost(&grp->dev);
+			ev->r.retcode = 0;
+			ev->r.c_mq_session_grp_ret.host_no = shost->host_no;
+			ev->r.c_mq_session_grp_ret.gid = grp->gid;
 		}
-
-		err = iscsi_if_create_session(priv, ep, ev,
-					NETLINK_CB(skb).portid,
-					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_MQ_DESTROY_SESSION_GRP:
+		grp = iscsi_find_session_grp_by_ids(
+						ev->u.d_mq_session_grp.host_no,
+						ev->u.d_mq_session_grp.gid);
+		if (grp)
+			transport->destroy_session_grp(grp);
+		else
+			err = -EINVAL;
 		break;
 	case ISCSI_UEVENT_DESTROY_SESSION:
 		session = iscsi_session_lookup(ev->u.d_session.sid);
@@ -3538,16 +3940,27 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 		else
 			err = -EINVAL;
 		break;
+	case ISCSI_UEVENT_MQ_UNBIND_SESSION_GRP:
+		grp = iscsi_find_session_grp_by_ids(
+						ev->u.u_mq_session_grp.host_no,
+						ev->u.u_mq_session_grp.gid);
+		if (grp)
+			scsi_queue_work(dev_to_shost(&grp->dev),
+					&grp->unbind_work);
+		else
+			err = -EINVAL;
+		break;
 	case ISCSI_UEVENT_UNBIND_SESSION:
 		session = iscsi_session_lookup(ev->u.d_session.sid);
 		if (session)
-			scsi_queue_work(iscsi_session_to_shost(session),
+			scsi_queue_work(dev_to_shost(&session->dev),
 					&session->unbind_work);
 		else
 			err = -EINVAL;
 		break;
 	case ISCSI_UEVENT_CREATE_CONN:
-		err = iscsi_if_create_conn(transport, ev);
+	case ISCSI_UEVENT_MQ_CREATE_CONN:
+		err = iscsi_if_create_conn(transport, ev, nlh->nlmsg_type);
 		break;
 	case ISCSI_UEVENT_DESTROY_CONN:
 		err = iscsi_if_destroy_conn(transport, ev);
@@ -3616,6 +4029,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
 	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
 	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
+	case ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT:
 		err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
 		break;
 	case ISCSI_UEVENT_TGT_DSCVR:
@@ -4022,6 +4436,17 @@ show_priv_session_target_id(struct device *dev, struct device_attribute *attr,
 static ISCSI_CLASS_ATTR(priv_sess, target_id, S_IRUGO,
 			show_priv_session_target_id, NULL);
 
+static ssize_t
+show_priv_session_queue_id(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
+	return sprintf(buf, "%d\n", session->queue_id);
+}
+static ISCSI_CLASS_ATTR(priv_sess, queue_id, S_IRUGO,
+			show_priv_session_queue_id, NULL);
+
+
 #define iscsi_priv_session_attr_show(field, format)			\
 static ssize_t								\
 show_priv_session_##field(struct device *dev, 				\
@@ -4096,6 +4521,7 @@ static struct attribute *iscsi_session_attrs[] = {
 	&dev_attr_priv_sess_creator.attr,
 	&dev_attr_sess_chap_out_idx.attr,
 	&dev_attr_sess_chap_in_idx.attr,
+	&dev_attr_priv_sess_queue_id.attr,
 	&dev_attr_priv_sess_target_id.attr,
 	&dev_attr_sess_auto_snd_tgt_disable.attr,
 	&dev_attr_sess_discovery_session.attr,
@@ -4210,6 +4636,8 @@ static umode_t iscsi_session_attr_is_visible(struct kobject *kobj,
 		return S_IRUGO;
 	else if (attr == &dev_attr_priv_sess_target_id.attr)
 		return S_IRUGO;
+	else if (attr == &dev_attr_priv_sess_queue_id.attr)
+		return S_IRUGO;
 	else {
 		WARN_ONCE(1, "Invalid session attr");
 		return 0;
@@ -4349,15 +4777,13 @@ EXPORT_SYMBOL_GPL(iscsi_get_port_state_name);
 static int iscsi_session_match(struct attribute_container *cont,
 			   struct device *dev)
 {
-	struct iscsi_cls_session *session;
 	struct Scsi_Host *shost;
 	struct iscsi_internal *priv;
 
 	if (!iscsi_is_session_dev(dev))
 		return 0;
 
-	session = iscsi_dev_to_session(dev);
-	shost = iscsi_session_to_shost(session);
+	shost = dev_to_shost(dev);
 	if (!shost->transportt)
 		return 0;
 
@@ -4371,17 +4797,13 @@ static int iscsi_session_match(struct attribute_container *cont,
 static int iscsi_conn_match(struct attribute_container *cont,
 			   struct device *dev)
 {
-	struct iscsi_cls_session *session;
-	struct iscsi_cls_conn *conn;
 	struct Scsi_Host *shost;
 	struct iscsi_internal *priv;
 
 	if (!iscsi_is_conn_dev(dev))
 		return 0;
 
-	conn = iscsi_dev_to_conn(dev);
-	session = iscsi_dev_to_session(conn->dev.parent);
-	shost = iscsi_session_to_shost(session);
+	shost = dev_to_shost(dev);
 
 	if (!shost->transportt)
 		return 0;
@@ -4515,6 +4937,7 @@ static __init int iscsi_transport_init(void)
 	printk(KERN_INFO "Loading iSCSI transport class v%s.\n",
 		ISCSI_TRANSPORT_VERSION);
 
+	atomic_set(&iscsi_session_grp_nr, 0);
 	atomic_set(&iscsi_session_nr, 0);
 
 	err = class_register(&iscsi_transport_class);
@@ -4545,10 +4968,14 @@ static __init int iscsi_transport_init(void)
 	if (err)
 		goto unregister_session_class;
 
+	err = bus_register(&iscsi_session_grp_bus);
+	if (err)
+		goto unregister_flashnode_bus;
+
 	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, &cfg);
 	if (!nls) {
 		err = -ENOBUFS;
-		goto unregister_flashnode_bus;
+		goto unregister_session_grp_bus;
 	}
 
 	iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
@@ -4561,6 +4988,8 @@ static __init int iscsi_transport_init(void)
 
 release_nls:
 	netlink_kernel_release(nls);
+unregister_session_grp_bus:
+	bus_unregister(&iscsi_session_grp_bus);
 unregister_flashnode_bus:
 	bus_unregister(&iscsi_flashnode_bus);
 unregister_session_class:
@@ -4582,6 +5011,7 @@ static void __exit iscsi_transport_exit(void)
 {
 	destroy_workqueue(iscsi_eh_timer_workq);
 	netlink_kernel_release(nls);
+	bus_unregister(&iscsi_session_grp_bus);
 	bus_unregister(&iscsi_flashnode_bus);
 	transport_class_unregister(&iscsi_connection_class);
 	transport_class_unregister(&iscsi_session_class);
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index 95ed942..1d18426 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -72,6 +72,13 @@ enum iscsi_uevent_e {
 	ISCSI_UEVENT_SET_CHAP		= UEVENT_BASE + 31,
 	ISCSI_UEVENT_GET_HOST_STATS	= UEVENT_BASE + 32,
 
+	ISCSI_UEVENT_MQ_CREATE_SESSION		= UEVENT_BASE + 33,
+	ISCSI_UEVENT_MQ_CREATE_SESSION_GRP	= UEVENT_BASE + 34,
+	ISCSI_UEVENT_MQ_DESTROY_SESSION_GRP	= UEVENT_BASE + 35,
+	ISCSI_UEVENT_MQ_UNBIND_SESSION_GRP	= UEVENT_BASE + 36,
+	ISCSI_UEVENT_MQ_CREATE_CONN		= UEVENT_BASE + 37,
+	ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT	= UEVENT_BASE + 38,
+
 	/* up events */
 	ISCSI_KEVENT_RECV_PDU		= KEVENT_BASE + 1,
 	ISCSI_KEVENT_CONN_ERROR		= KEVENT_BASE + 2,
@@ -85,6 +92,7 @@ enum iscsi_uevent_e {
 	ISCSI_KEVENT_CONN_LOGIN_STATE   = KEVENT_BASE + 9,
 	ISCSI_KEVENT_HOST_EVENT		= KEVENT_BASE + 10,
 	ISCSI_KEVENT_PING_COMP		= KEVENT_BASE + 11,
+	ISCSI_KEVENT_MQ_UNBIND_SESSION_GRP	= KEVENT_BASE + 12,
 };
 
 enum iscsi_tgt_dscvr {
@@ -100,10 +108,13 @@ enum iscsi_host_event_code {
 	ISCSI_EVENT_MAX,
 };
 
+#define ISCSI_UEVENT_FLAG_EP_BOUND	0x1
+#define ISCSI_UEVENT_FLAG_HOST_BOUND	0x1
+
 struct iscsi_uevent {
 	uint32_t type; /* k/u events type */
 	uint32_t iferror; /* carries interface or resource errors */
-	uint64_t transport_handle;
+	__aligned_u64 transport_handle;
 
 	union {
 		/* messages u -> k */
@@ -113,11 +124,21 @@ struct iscsi_uevent {
 			uint16_t	queue_depth;
 		} c_session;
 		struct msg_create_bound_session {
-			uint64_t	ep_handle;
+			__aligned_u64	ep_handle;
 			uint32_t	initial_cmdsn;
 			uint16_t	cmds_max;
 			uint16_t	queue_depth;
 		} c_bound_session;
+		struct msg_mq_create_session {
+			__aligned_u64	ep_handle;
+			uint32_t	flags;
+			uint32_t	queue_id;
+			uint32_t	initial_cmdsn;
+			uint32_t	gid;
+			uint32_t	host_no;
+			uint16_t	cmds_max;
+			uint16_t	queue_depth;
+		} c_mq_session;
 		struct msg_destroy_session {
 			uint32_t	sid;
 		} d_session;
@@ -125,10 +146,15 @@ struct iscsi_uevent {
 			uint32_t	sid;
 			uint32_t	cid;
 		} c_conn;
+		struct msg_mq_create_conn {
+			uint32_t	sid;
+			uint32_t	cid;
+			uint32_t	queue_id;
+		} c_mq_conn;
 		struct msg_bind_conn {
 			uint32_t	sid;
 			uint32_t	cid;
-			uint64_t	transport_eph;
+			__aligned_u64	transport_eph;
 			uint32_t	is_leading;
 		} b_conn;
 		struct msg_destroy_conn {
@@ -154,7 +180,7 @@ struct iscsi_uevent {
 		struct msg_stop_conn {
 			uint32_t	sid;
 			uint32_t	cid;
-			uint64_t	conn_handle;
+			__aligned_u64	conn_handle;
 			uint32_t	flag;
 		} stop_conn;
 		struct msg_get_stats {
@@ -168,12 +194,21 @@ struct iscsi_uevent {
 			uint32_t	host_no;
 			uint32_t	non_blocking;
 		} ep_connect_through_host;
+		struct msg_mq_transport_connect {
+			uint32_t	flags;
+			uint32_t	host_no;
+			uint32_t	non_blocking;
+			uint32_t	queue_id;
+			/*
+			 * TODO: Sagi/Or, there were some new fields we wanted for iser multipath right (from a past issue and not related to mq)? Let's add them here now while we are at it.
+			 */
+		} ep_mq_connect;
 		struct msg_transport_poll {
-			uint64_t	ep_handle;
+			__aligned_u64	ep_handle;
 			uint32_t	timeout_ms;
 		} ep_poll;
 		struct msg_transport_disconnect {
-			uint64_t	ep_handle;
+			__aligned_u64	ep_handle;
 		} ep_disconnect;
 		struct msg_tgt_dscvr {
 			enum iscsi_tgt_dscvr	type;
@@ -244,12 +279,32 @@ struct iscsi_uevent {
 			uint32_t	sid;
 		} logout_flashnode_sid;
 		struct msg_get_host_stats {
-			uint32_t host_no;
+			uint32_t	host_no;
 		} get_host_stats;
+		struct msg_mq_create_session_grp {
+			uint32_t	host_no;
+			uint32_t	nr_sessions;
+		} c_mq_session_grp;
+		struct msg_mq_destroy_session_grp {
+			uint32_t	host_no;
+			uint32_t	gid;
+		} d_mq_session_grp;
+		struct msg_mq_unbind_session_grp {
+			uint32_t	host_no;
+			uint32_t	gid;
+		} u_mq_session_grp;
 	} u;
 	union {
 		/* messages k -> u */
 		int			retcode;
+		struct msg_mq_create_session_grp_ret {
+			uint32_t	gid;
+			uint32_t	host_no;
+		} c_mq_session_grp_ret;
+		struct msg_mq_unbind_session_grp_ret {
+			uint32_t	gid;
+			uint32_t	host_no;
+		} u_mq_session_grp_ret;
 		struct msg_create_session_ret {
 			uint32_t	sid;
 			uint32_t	host_no;
@@ -265,7 +320,7 @@ struct iscsi_uevent {
 		struct msg_recv_req {
 			uint32_t	sid;
 			uint32_t	cid;
-			uint64_t	recv_handle;
+			__aligned_u64	recv_handle;
 		} recv_req;
 		struct msg_conn_login {
 			uint32_t        sid;
@@ -282,7 +337,7 @@ struct iscsi_uevent {
 			uint32_t	sid;
 		} d_session;
 		struct msg_transport_connect_ret {
-			uint64_t	handle;
+			__aligned_u64	handle;
 		} ep_connect_ret;
 		struct msg_req_path {
 			uint32_t	host_no;
@@ -620,6 +675,7 @@ enum iscsi_param {
 	ISCSI_PARAM_DISCOVERY_PARENT_IDX,
 	ISCSI_PARAM_DISCOVERY_PARENT_TYPE,
 	ISCSI_PARAM_LOCAL_IPADDR,
+
 	/* must always be last */
 	ISCSI_PARAM_MAX,
 };
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 4d1c46a..74b289e2 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -408,7 +408,8 @@ extern int iscsi_target_alloc(struct scsi_target *starget);
  */
 extern struct iscsi_cls_session *
 iscsi_session_setup(struct iscsi_transport *, struct Scsi_Host *shost,
-		    uint16_t, int, int, uint32_t, unsigned int);
+		    struct iscsi_session_grp *, uint16_t, int, int, uint32_t,
+		    unsigned int, uint32_t);
 extern void iscsi_session_teardown(struct iscsi_cls_session *);
 extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *);
 extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
@@ -473,6 +474,7 @@ extern void iscsi_complete_scsi_task(struct iscsi_task *task,
 extern void iscsi_pool_free(struct iscsi_pool *);
 extern int iscsi_pool_init(struct iscsi_pool *, int, void ***, int);
 extern int iscsi_switch_str_param(char **, char *);
+extern struct iscsi_session *scsi_cmd_to_session(struct scsi_cmnd *sc);
 
 /*
  * inline functions to deal with padding.
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index 2555ee5..c24e95c 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -41,12 +41,15 @@ struct iscsi_iface;
 struct bsg_job;
 struct iscsi_bus_flash_session;
 struct iscsi_bus_flash_conn;
+struct iscsi_session_grp;
 
 /**
  * struct iscsi_transport - iSCSI Transport template
  *
  * @name:		transport name
  * @caps:		iSCSI Data-Path capabilities
+ * @create_session_grp:	create session group object and host if needed
+ * @destroy_session_grp: destroy group and host
  * @create_session:	create new iSCSI session object
  * @destroy_session:	destroy existing iSCSI session object
  * @create_conn:	create new iSCSI connection
@@ -89,12 +92,18 @@ struct iscsi_transport {
 	char *name;
 	unsigned int caps;
 
-	struct iscsi_cls_session *(*create_session) (struct iscsi_endpoint *ep,
+	struct iscsi_session_grp *(*create_session_grp) (uint32_t host_no,
+							 uint32_t nr_sessions);
+	void (*destroy_session_grp) (struct iscsi_session_grp *grp);
+	struct iscsi_cls_session *(*create_session) (
+					struct iscsi_session_grp *grp,
+					struct iscsi_endpoint *ep,
 					uint16_t cmds_max, uint16_t qdepth,
-					uint32_t sn);
+					uint32_t sn,
+					uint32_t queue_id);
 	void (*destroy_session) (struct iscsi_cls_session *session);
 	struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
-				uint32_t cid);
+				uint32_t cid, uint32_t queue_id);
 	int (*bind_conn) (struct iscsi_cls_session *session,
 			  struct iscsi_cls_conn *cls_conn,
 			  uint64_t transport_eph, int is_leading);
@@ -133,7 +142,8 @@ struct iscsi_transport {
 	void (*session_recovery_timedout) (struct iscsi_cls_session *session);
 	struct iscsi_endpoint *(*ep_connect) (struct Scsi_Host *shost,
 					      struct sockaddr *dst_addr,
-					      int non_blocking);
+					      int non_blocking,
+					      uint32_t queue_id);
 	int (*ep_poll) (struct iscsi_endpoint *ep, int timeout_ms);
 	void (*ep_disconnect) (struct iscsi_endpoint *ep);
 	int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
@@ -232,6 +242,7 @@ enum {
 
 struct iscsi_cls_session {
 	struct list_head sess_list;		/* item in session_list */
+	struct list_head *grp_list;
 	struct iscsi_transport *transport;
 	spinlock_t lock;
 	struct work_struct block_work;
@@ -246,6 +257,8 @@ struct iscsi_cls_session {
 	unsigned int target_id;
 	bool ida_used;
 
+	unsigned int queue_id;
+	struct iscsi_session_grp *grp;
 	/*
 	 * pid of userspace process that created session or -1 if
 	 * created by the kernel.
@@ -263,11 +276,28 @@ struct iscsi_cls_session {
 #define transport_class_to_session(_cdev) \
 	iscsi_dev_to_session(_cdev->parent)
 
-#define iscsi_session_to_shost(_session) \
-	dev_to_shost(_session->dev.parent)
-
 #define starget_to_session(_stgt) \
-	iscsi_dev_to_session(_stgt->dev.parent)
+	iscsi_dev_to_lead_session(_stgt->dev.parent)
+
+struct iscsi_session_grp {
+	struct device dev;
+	struct iscsi_transport *transport;
+	unsigned int target_id;
+	uint32_t gid;
+	/* hctx idx to cls_session mapping */
+	struct iscsi_cls_session **session_map;
+	uint32_t session_count;
+	uint32_t max_sessions;
+	uint32_t max_queue_id;
+	struct completion unbind_wait;
+	struct work_struct unbind_work;
+};
+
+#define iscsi_dev_to_session_grp(_dev) \
+	container_of(_dev, struct iscsi_session_grp, dev)
+
+#define starget_to_session_grp(_stgt) \
+	iscsi_dev_to_session_grp(_stgt->dev.parent)
 
 struct iscsi_cls_host {
 	atomic_t nr_scans;
@@ -416,13 +446,23 @@ struct iscsi_bus_flash_session {
 #define iscsi_cls_session_printk(prefix, _cls_session, fmt, a...) \
 	dev_printk(prefix, &(_cls_session)->dev, fmt, ##a)
 
+#define iscsi_session_grp_printk(prefix, _grp, fmt, a...) \
+	dev_printk(prefix, &(_grp)->dev, fmt, ##a)
+
 #define iscsi_cls_conn_printk(prefix, _cls_conn, fmt, a...) \
 	dev_printk(prefix, &(_cls_conn)->dev, fmt, ##a)
 
+
+extern struct iscsi_cls_session *iscsi_dev_to_lead_session(struct device *dev);
+extern struct iscsi_session_grp *
+iscsi_create_session_grp(struct iscsi_transport *transport,
+			  struct Scsi_Host *shost, uint32_t nr_sessions);
+extern void iscsi_destroy_session_grp(struct iscsi_session_grp *grp);
 extern int iscsi_session_chkready(struct iscsi_cls_session *session);
 extern int iscsi_is_session_online(struct iscsi_cls_session *session);
-extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost,
-				struct iscsi_transport *transport, int dd_size);
+extern struct iscsi_cls_session *
+iscsi_alloc_session(struct iscsi_transport *transport, struct Scsi_Host *shost,
+		    struct iscsi_session_grp *grp, int dd_size);
 extern int iscsi_add_session(struct iscsi_cls_session *session,
 			     unsigned int target_id);
 extern int iscsi_session_event(struct iscsi_cls_session *session,
>From 05abcf6947f32186e540a102b20cd7d768a29d08 Mon Sep 17 00:00:00 2001
From: Mike Christie <[email protected]>
Date: Thu, 5 Feb 2015 02:11:16 -0600
Subject: [PATCH 1/1] iscsid: make sure actor is delated before rescheduling

iscsi_conn_connect() be called from a login_timer that is not deleted.
This causes it to be scheduled multiple times. This patch adds a new
function actor_timer_mod to handle both deletion and rescheduling.

This patch also then removed the uio poll code which was using the
login_timer to schedule itself. This uio retry, is now just done in
the generic initial login retry.
---
 usr/actor.c            |   8 ++++
 usr/actor.h            |   3 +-
 usr/initiator.c        | 122 +++++--------------------------------------------
 usr/initiator.h        |   1 -
 usr/initiator_common.c |   8 ++--
 5 files changed, 26 insertions(+), 116 deletions(-)

diff --git a/usr/actor.c b/usr/actor.c
index 37b5024..21cd819 100644
--- a/usr/actor.c
+++ b/usr/actor.c
@@ -182,6 +182,14 @@ actor_timer(actor_t *thread, uint32_t timeout_secs, void (*callback)(void *),
 	actor_schedule_private(thread, timeout_secs, 0);
 }
 
+void
+actor_timer_mod(actor_t *thread, uint32_t new_timeout_secs, void *data)
+{
+	actor_delete(thread);
+	thread->data = data;
+	actor_schedule_private(thread, new_timeout_secs, 0);
+}
+
 /*
  * Execute all items that have expired.
  *
diff --git a/usr/actor.h b/usr/actor.h
index 7283dce..f572f2e 100644
--- a/usr/actor.h
+++ b/usr/actor.h
@@ -43,7 +43,8 @@ extern void actor_schedule_head(actor_t *thread);
 extern void actor_schedule(actor_t *thread);
 extern void actor_timer(actor_t *thread, uint32_t delay_secs,
 			void (*callback)(void *), void *data);
-extern int actor_timer_mod(actor_t *thread, uint32_t new_delay_secs, void *data);
+extern void actor_timer_mod(actor_t *thread, uint32_t new_delay_secs,
+			    void *data);
 extern void actor_poll(void);
 
 #endif /* ACTOR_H */
diff --git a/usr/initiator.c b/usr/initiator.c
index 1aadc9b..f70db4d 100644
--- a/usr/initiator.c
+++ b/usr/initiator.c
@@ -262,6 +262,7 @@ __session_conn_create(iscsi_session_t *session, int cid)
 
 	conn->state = ISCSI_CONN_STATE_FREE;
 	conn->session = session;
+	actor_init(&conn->login_timer, iscsi_login_timedout, NULL);
 	/*
 	 * TODO: we must export the socket_fd/transport_eph from sysfs
 	 * so if iscsid is resyncing up we can pick that up and cleanup up
@@ -528,9 +529,7 @@ queue_delayed_reopen(queue_task_t *qtask, int delay)
  	 * iscsi_login_eh can handle the login resched as
  	 * if it were login time out
  	 */
-	actor_delete(&conn->login_timer);
-	actor_timer(&conn->login_timer, delay,
-		    iscsi_login_timedout, qtask);
+	actor_timer_mod(&conn->login_timer, delay, qtask);
 }
 
 static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask)
@@ -565,53 +564,10 @@ static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask)
 	iscsi_sched_ev_context(ev_context, conn, 0, EV_CONN_POLL);
 	log_debug(3, "Setting login timer %p timeout %d", &conn->login_timer,
 		  conn->login_timeout);
-	actor_timer(&conn->login_timer, conn->login_timeout,
-		    iscsi_login_timedout, qtask);
+	actor_timer_mod(&conn->login_timer, conn->login_timeout, qtask);
 	return 0;
 }
 
-static void iscsi_uio_poll_login_timedout(void *data)
-{
-	struct queue_task *qtask = data;
-	struct iscsi_conn *conn = qtask->conn;
-	iscsi_session_t *session = conn->session;
-
-	log_debug(3, "timeout waiting for UIO ...\n");
-	mgmt_ipc_write_rsp(qtask, ISCSI_ERR_TRANS_TIMEOUT);
-	conn_delete_timers(conn);
-	__session_destroy(session);
-}
-
-static int iscsi_sched_uio_poll(queue_task_t *qtask)
-{
-	struct iscsi_conn *conn = qtask->conn;
-	struct iscsi_session *session = conn->session;
-	struct iscsi_transport *t = session->t;
-	struct iscsi_ev_context *ev_context;
-
-	if (!t->template->set_net_config)
-		return 0;
-
-	ev_context = iscsi_ev_context_get(conn, 0);
-	if (!ev_context) {
-		/* while reopening the recv pool should be full */
-		log_error("BUG: __session_conn_reopen could "
-			  "not get conn context for recv.");
-		return -ENOMEM;
-	}
-
-	ev_context->data = qtask;
-	conn->state = ISCSI_CONN_STATE_XPT_WAIT;
-
-	iscsi_sched_ev_context(ev_context, conn, 0, EV_UIO_POLL);
-
-	log_debug(3, "Setting login UIO poll timer %p timeout %d",
-		  &conn->login_timer, conn->login_timeout);
-	actor_timer(&conn->login_timer, conn->login_timeout,
-		    iscsi_uio_poll_login_timedout, qtask);
-	return -EAGAIN;
-}
-
 static void
 __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop,
 		      int redirected)
@@ -1740,53 +1696,6 @@ failed_login:
 
 }
 
-static void session_conn_uio_poll(void *data)
-{
-	struct iscsi_ev_context *ev_context = data;
-	iscsi_conn_t *conn = ev_context->conn;
-	struct iscsi_session *session = conn->session;
-	queue_task_t *qtask = ev_context->data;
-	int rc;
-
-	log_debug(4, "retrying uio poll");
-	rc = iscsi_set_net_config(session->t, session,
-				  &conn->session->nrec.iface);
-	if (rc != 0) {
-		if (rc == ISCSI_ERR_AGAIN) {
-			ev_context->data = qtask;
-			iscsi_sched_ev_context(ev_context, conn, 2,
-					       EV_UIO_POLL);
-			return;
-		} else {
-			log_error("session_conn_uio_poll() "
-				  "connection failure [0x%x]", rc);
-			actor_delete(&conn->login_timer);
-			iscsi_login_eh(conn, qtask, ISCSI_ERR_INTERNAL);
-			iscsi_ev_context_put(ev_context);
-			return;
-		}
-	}
-
-	iscsi_ev_context_put(ev_context);
-	actor_delete(&conn->login_timer);
-	log_debug(4, "UIO ready trying connect");
-
-	/*  uIP is ready try to connect */
-	if (gettimeofday(&conn->initial_connect_time, NULL))
-		log_error("Could not get initial connect time. If "
-			  "login errors iscsid may give up the initial "
-			  "login early. You should manually login.");
-
-	conn->state = ISCSI_CONN_STATE_XPT_WAIT;
-	if (iscsi_conn_connect(conn, qtask)) {
-		int delay = ISCSI_CONN_ERR_REOPEN_DELAY;
-
-		log_debug(4, "Waiting %u seconds before trying to reconnect.\n",
-			  delay);
-		queue_delayed_reopen(qtask, delay);
-	}
-}
-
 static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
 				  struct iscsi_conn *conn, unsigned long tmo,
 				  int event)
@@ -1825,12 +1734,7 @@ static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
 		break;
 	case EV_CONN_POLL:
 		actor_init(&ev_context->actor, session_conn_poll,
-			  ev_context);
-		actor_schedule(&ev_context->actor);
-		break;
-	case EV_UIO_POLL:
-		actor_init(&ev_context->actor, session_conn_uio_poll,
-			  ev_context);
+                          ev_context);
 		actor_schedule(&ev_context->actor);
 		break;
 	case EV_CONN_LOGOUT_TIMER:
@@ -1970,14 +1874,12 @@ static int __session_login_task(node_rec_t *rec, queue_task_t *qtask)
 
 	rc = iscsi_host_set_net_params(&rec->iface, session);
 	if (rc == ISCSI_ERR_AGAIN) {
-		iscsi_sched_uio_poll(qtask);
 		/*
-		 * Cannot block iscsid, so caller is going to internally
-		 * retry the operation.
+		 * host/iscsiuio not ready. Cannot block iscsid, so caller is
+		 * going to internally retry the operation.
 		 */
-		qtask->rsp.command = MGMT_IPC_SESSION_LOGIN;
-		qtask->rsp.err = ISCSI_SUCCESS;
-		return ISCSI_SUCCESS;
+		__session_destroy(session);
+		return ISCSI_ERR_HOST_NOT_FOUND;
 	} else if (rc) {
 		__session_destroy(session);
 		return ISCSI_ERR_LOGIN;
@@ -2024,17 +1926,17 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask)
 static void session_login_task_retry(void *data)
 {
 	struct login_task_retry_info *info = data;
+	struct node_rec *rec = info->rec;
 	int rc;
 
-	rc = __session_login_task(info->rec, info->qtask);
+	rc = __session_login_task(rec, info->qtask);
 	if (rc == ISCSI_ERR_HOST_NOT_FOUND) {
-		if (info->retry_count == 5) {
+		if (info->retry_count == rec->conn[0].timeo.login_timeout) {
 			/* give up */
 			goto write_rsp;
 		}
 
-		rc = queue_session_login_task_retry(info, info->rec,
-						    info->qtask);
+		rc = queue_session_login_task_retry(info, rec, info->qtask);
 		if (rc)
 			goto write_rsp;
 		/* we are going to internally retry */
diff --git a/usr/initiator.h b/usr/initiator.h
index c34625b..c11d77f 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -83,7 +83,6 @@ typedef enum iscsi_event_e {
 	EV_CONN_LOGOUT_TIMER,
 	EV_CONN_STOP,
 	EV_CONN_LOGIN,
-	EV_UIO_POLL,
 } iscsi_event_e;
 
 struct queue_task;
diff --git a/usr/initiator_common.c b/usr/initiator_common.c
index eb03b23..98ca636 100644
--- a/usr/initiator_common.c
+++ b/usr/initiator_common.c
@@ -249,7 +249,7 @@ int iscsi_setup_portal(struct iscsi_conn *conn, char *address, int port)
 	return 0;
 }
 
-int host_set_param(struct iscsi_transport *t,
+static int host_set_param(struct iscsi_transport *t,
 		   uint32_t host_no, int param, char *value,
 		   int type)
 {
@@ -261,7 +261,7 @@ int host_set_param(struct iscsi_transport *t,
 		log_error("can't set operational parameter %d for "
 			  "host %d, retcode %d (%d)", param, host_no,
 			  rc, errno);
-		return rc;
+		return ISCSI_ERR_INVAL;
 	}
 	return 0;
 }
@@ -677,13 +677,13 @@ int iscsi_host_set_net_params(struct iface_rec *iface,
 			log_warning("Please set the iface.ipaddress for iface "
 				    "%s, then retry the login command.\n",
 				    iface->name);
-			return EINVAL;
+			return ISCSI_ERR_INVAL;
 		} else if (t->template->set_host_ip == SET_HOST_IP_OPT) {
 			log_info("Optional iface.ipaddress for iface %s "
 				 "not set.\n", iface->name);
 			return 0;
 		} else {
-			return EINVAL;
+			return ISCSI_ERR_INVAL;
 		}
 	}
 
-- 
1.8.3.1

Reply via email to