Dropping lsf for iscsi list.

On 2/22/15, 12:12 PM, Sagi Grimberg wrote:
On 2/22/2015 7:51 PM, James Bottomley wrote:
On Sun, 2015-02-22 at 18:57 +0200, Sagi Grimberg wrote:
On 2/22/2015 6:32 PM, James Bottomley wrote:

Hey All,

I'm also interested in talking about block/scsi-mq. Moreover, I was
hoping we can talk about iSCSI in the context of scsi-mq. We
started the
discussion on the list
(http://marc.info/?l=linux-scsi&w=2&r=2&s=iSCSI+MQ+adoption+via+MCS+discussion&q=b)

and I'd like us to come
to an agreement on this.

What I would like to talk about is:
- How will we implement multiple submission queues?
     MCS or multiple sessions - pros/cons.

- Fitting iSCSI sockets to HW contexts is trivial, but getting
     the completions to steer to the correct CPU is something we
may want
     to discuss. Do we want to control that? or do we want to let the
     networking stack to do handle it like any other socket (RFS,
XPS ...)

I think it might be worth do dedicate a session for this if we have an
open slot.

We have a few open slots, yes.  I thought this was largely a solved
problem.  If not, I can add it to the storage track.

IMO this thread didn't come to any final conclusions. Lets see if this
session is interesting to anyone else...

OK, let's see who pipes up.  I have to say I have institutional bias
against MC/S since it seems to me to try to take us back to the bad old
days where every "enterprise" SCSI driver saw value add in implementing
its own version of multi-path.  Trying to get them all removed in favour
of dm-multipath was a long hard battle I don't really want to revisit.

On the other hand, if there's a real problem with dm-multipath and
scsi-mq which MC/S solves, I'd also like to know about it ... although
probably from a "how can we fix dm-multipath" point of view.

I don't think anyone is actually considering implementing MCS to do any
type of HA or load-balancing but rather just make it fit the multi-queue
schema.

We can do multiple sessions but then we either make iSCSI expose a
scsi_host per port (not per session like today) or create some kind of
abstraction layer to manage multiple sessions (like srp_target_port
which holds several srp_rdma_ch).

I think different folks might have different views on this...


Sorry about that. I thought I sent this to you. It must have got lost. Attached is the kernel changes for session based mq support (patch made over linus's tree). It just has the core changes only. I did not yet modify iscsi_tcp/libiscsi to bind our threads and sockets to cpus or deal with the networking.

I was waiting to hear back from you about how it could work for ib_iser and was waiting to hear back from some hw vendors about how they are going to support mq in their hw/fw. So, the queue_id that gets passed in is really generic at this point and can map to whatever the transport driver wants to map it to (actual hw/fw queue for offload, msix/cpu combo like srp for iser, cpu and something else for iscsi_tcp, etc).

I just hit a bug in the userspace code. Will send that later.

But yeah, just added a new struct between the host and sessions.

--
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 open-iscsi+unsubscr...@googlegroups.com.
To post to this group, send email to open-iscsi@googlegroups.com.
Visit this group at http://groups.google.com/group/open-iscsi.
For more options, visit https://groups.google.com/d/optout.
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..d0626a4 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,67 @@ 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(void)
+{
+       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;
+
+       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_host_remove(shost);
+       iscsi_host_free(shost);
+       iscsi_destroy_session_grp(grp);
+}
+
+static struct iscsi_session_grp *
+iscsi_sw_tcp_session_grp_create(uint32_t host_no)
+{
+       struct Scsi_Host *shost;
+       struct iscsi_session_grp *grp;
+
+       shost = iscsi_sw_tcp_create_host();
+       if (!shost)
+               goto fail;
+
+       grp = iscsi_create_session_grp(shost);
+       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 +887,28 @@ 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);
+if (!shost)
+       printk(KERN_ERR "could not find host\n");
+       } else {
+printk(KERN_ERR "doh\n");
+               shost = iscsi_sw_tcp_create_host();
+       }
+
        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 +923,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 +1043,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..c0958db 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,28 @@ 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];
+               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 +1669,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 +1679,11 @@ 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);
 
-       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 +2144,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 +2281,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 +2373,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 +2439,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 +2714,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 +2730,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 +2780,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;
@@ -2810,8 +2829,11 @@ iscsi_session_setup(struct iscsi_transport *iscsit, 
struct Scsi_Host *shost,
                goto module_get_fail;
 
        if (iscsi_add_session(cls_session, id))
+{
+printk(KERN_ERR "add session fail\n");
                goto cls_session_fail;
 
+}
        return cls_session;
 
 cls_session_fail:
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..18464720 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;
 
@@ -1728,8 +1729,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 +1761,145 @@ 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,
+};
+
+struct iscsi_session_grp *
+iscsi_create_session_grp(struct Scsi_Host *shost)
+{
+       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;
+
+       grp->max_sessions = 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
@@ -1792,15 +1934,13 @@ static int iscsi_user_scan_session(struct device *dev, 
void *data)
        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);
        spin_lock_irqsave(&session->lock, flags);
@@ -1816,8 +1956,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 +1982,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 +2062,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 +2136,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;
@@ -2026,9 +2165,43 @@ static void __iscsi_unbind_session(struct work_struct 
*work)
        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;
+       int err;
+
+       if (!grp || session->queue_id >= grp->max_sessions)
+               return -EINVAL;
+
+       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));
+               return -EINVAL;
+       }
+       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;
+
+       return 0;
+
+unmap:
+       grp->session_map[session->queue_id] = NULL;
+       grp->session_count--;
+       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 +2210,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 +2223,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 +2242,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 +2252,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 +2288,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 +2323,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 +2357,21 @@ 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;
+
+       if (!grp)
+               return;
+
+       grp->session_map[session->queue_id] = NULL;
+       grp->session_count--;
+       sysfs_remove_link(&session->dev.kobj, "session_group");
+}
+
 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 +2410,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);
@@ -2649,7 +2859,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 +2912,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;
+
+               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(ep, cmds_max, queue_depth,
-                                           initial_cmdsn);
+       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 +2976,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 +3063,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 +3078,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 +3146,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 +3778,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 +3797,30 @@ 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);
+               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)
+                       return -EINVAL;
+               transport->destroy_session_grp(grp);
                break;
        case ISCSI_UEVENT_DESTROY_SESSION:
                session = iscsi_session_lookup(ev->u.d_session.sid);
@@ -3541,13 +3832,14 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr 
*nlh, uint32_t *group)
        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 +3908,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 +4315,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 +4400,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 +4515,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 +4656,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 +4676,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 +4816,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 +4847,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 +4867,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 +4890,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..3faf644 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -72,6 +72,12 @@ 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_CREATE_CONN             = UEVENT_BASE + 36,
+       ISCSI_UEVENT_MQ_TRANSPORT_EP_CONNECT    = UEVENT_BASE + 37,
+
        /* up events */
        ISCSI_KEVENT_RECV_PDU           = KEVENT_BASE + 1,
        ISCSI_KEVENT_CONN_ERROR         = KEVENT_BASE + 2,
@@ -100,10 +106,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 +122,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 +144,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 +178,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 +192,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 +277,23 @@ 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;
+               } 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;
@@ -265,7 +309,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 +326,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 +664,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..6d94384 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,17 @@ 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);
+       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 +141,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 +241,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 +256,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 +275,25 @@ 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;
+       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;
+       struct list_head *session_list;
+};
+
+#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;
@@ -419,10 +445,15 @@ struct iscsi_bus_flash_session {
 #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 Scsi_Host *shost);
+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,

Reply via email to