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