On 02/06/2014 04:11 PM, Nicholas A. Bellinger wrote: >> +struct request_queue *scsi_mq_alloc_queue(struct scsi_device *sdev) >> > +{ >> > + struct Scsi_Host *shost = sdev->host; >> > + struct blk_mq_hw_ctx *hctx; >> > + struct request_queue *q; >> > + struct request *rq; >> > + struct scsi_cmnd *cmd; >> > + struct blk_mq_reg reg; >> > + int i, j, sgl_size; >> > + >> > + memset(®, 0, sizeof(reg)); >> > + reg.ops = &scsi_mq_ops; >> > + reg.queue_depth = shost->cmd_per_lun; >> > + if (!reg.queue_depth) >> > + reg.queue_depth = 1; >> > + >> > + /* XXX: what to do about chained S/G lists? */ >> > + if (shost->hostt->sg_tablesize > SCSI_MAX_SG_SEGMENTS) >> > + shost->sg_tablesize = SCSI_MAX_SG_SEGMENTS; >> > + sgl_size = shost->sg_tablesize * sizeof(struct scatterlist); >> > + >> > + reg.cmd_size = sizeof(struct scsi_cmnd) + >> > + sgl_size + >> > + shost->hostt->cmd_size; >> > + if (scsi_host_get_prot(shost)) >> > + reg.cmd_size += sizeof(struct scsi_data_buffer) + sgl_size; > OK, so your in-lining the allocation of data + protection SGLs from > blk-mq.. > > The original prototype code was doing these allocations separately below > for each pre-allocated cmd, and offering LLD's to optionally > pre-allocate their own descripts using sh->hostt->cmd_size if > necessary.. > > This was necessary to eliminate all fast-path allocations for > virtio-scsi, and I'd like to see something similar here as an optional > feature as well. Yeah, it would be nice if like in Nick's patches, the driver could just set the scsi_host_template->cmd_size then when the scsi_cmnd got to the driver's queuecommand, the driver could just get its internal cmd struct from the scsi_cmnd struct (for example in Nick's patch it was off the SCp.ptr). I started converting my iscsi mq patch from Nick's code to Christoph's and am currently trying to figure out how to setup the scsi_host_template->cmd_pool. Current iscsi patch is attached if anyone cares. However, one question I had with both approaches is how to deal with per cmd pci/dma memory and preallocations.
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index bc77a6f..1d0857c 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -320,10 +320,10 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc) memset(inv_tbl, 0x0, sizeof(*inv_tbl) * BE2_CMDS_PER_CXN); num_invalidate = 0; for (i = 0; i < conn->session->cmds_max; i++) { - abrt_task = conn->session->cmds[i]; - abrt_io_task = abrt_task->dd_data; - if (!abrt_task->sc || abrt_task->state == ISCSI_TASK_FREE) + abrt_task = conn->session->task_map[i]; + if (!abrt_task || abrt_task->state != ISCSI_TASK_RUNNING) continue; + abrt_io_task = abrt_task->dd_data; if (sc->device->lun != abrt_task->sc->device->lun) continue; diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index c00642f..34289b4 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -447,7 +447,7 @@ static int bnx2i_alloc_bdt(struct bnx2i_hba *hba, struct iscsi_session *session, io->bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, ISCSI_MAX_BDS_PER_CMD * sizeof(*bd), - &io->bd_tbl_dma, GFP_KERNEL); + &io->bd_tbl_dma, GFP_ATOMIC); if (!io->bd_tbl) { iscsi_session_printk(KERN_ERR, session, "Could not " "allocate bdt.\n"); @@ -458,61 +458,6 @@ static int bnx2i_alloc_bdt(struct bnx2i_hba *hba, struct iscsi_session *session, } /** - * bnx2i_destroy_cmd_pool - destroys iscsi command pool and release BD table - * @hba: adapter instance pointer - * @session: iscsi session pointer - * @cmd: iscsi command structure - */ -static void bnx2i_destroy_cmd_pool(struct bnx2i_hba *hba, - struct iscsi_session *session) -{ - int i; - - for (i = 0; i < session->cmds_max; i++) { - struct iscsi_task *task = session->cmds[i]; - struct bnx2i_cmd *cmd = task->dd_data; - - if (cmd->io_tbl.bd_tbl) - dma_free_coherent(&hba->pcidev->dev, - ISCSI_MAX_BDS_PER_CMD * - sizeof(struct iscsi_bd), - cmd->io_tbl.bd_tbl, - cmd->io_tbl.bd_tbl_dma); - } - -} - - -/** - * bnx2i_setup_cmd_pool - sets up iscsi command pool for the session - * @hba: adapter instance pointer - * @session: iscsi session pointer - */ -static int bnx2i_setup_cmd_pool(struct bnx2i_hba *hba, - struct iscsi_session *session) -{ - int i; - - for (i = 0; i < session->cmds_max; i++) { - struct iscsi_task *task = session->cmds[i]; - struct bnx2i_cmd *cmd = task->dd_data; - - task->hdr = &cmd->hdr; - task->hdr_max = sizeof(struct iscsi_hdr); - - if (bnx2i_alloc_bdt(hba, session, cmd)) - goto free_bdts; - } - - return 0; - -free_bdts: - bnx2i_destroy_cmd_pool(hba, session); - return -ENOMEM; -} - - -/** * bnx2i_setup_mp_bdt - allocate BD table resources * @hba: pointer to adapter structure * @@ -1157,7 +1102,14 @@ static void bnx2i_cleanup_task(struct iscsi_task *task) struct iscsi_conn *conn = task->conn; struct bnx2i_conn *bnx2i_conn = conn->dd_data; struct bnx2i_hba *hba = bnx2i_conn->hba; + struct bnx2i_cmd *cmd = task->dd_data; + if (cmd->io_tbl.bd_tbl) + dma_free_coherent(&hba->pcidev->dev, + ISCSI_MAX_BDS_PER_CMD * + sizeof(struct iscsi_bd), + cmd->io_tbl.bd_tbl, + cmd->io_tbl.bd_tbl_dma); /* * mgmt task or cmd was never sent to us to transmit. */ @@ -1178,6 +1130,29 @@ static void bnx2i_cleanup_task(struct iscsi_task *task) } /** + * bnx2i_alloc_pdu - setup task/pdu and its bdt + * @task: transport layer command structure pointer + * @opcode: iscsi opcode for task/pdu to setup for + * + * The bdt will be freed in bnx2i_cleanup_task. + */ +static int bnx2i_alloc_pdu(struct iscsi_task *task, uint8_t opcode) +{ + struct bnx2i_cmd *cmd = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; + + task->hdr = &cmd->hdr; + task->hdr_max = sizeof(struct iscsi_hdr); + + if (bnx2i_alloc_bdt(hba, conn->session, cmd)) + return -ENOMEM; + + return 0; +} + +/** * bnx2i_mtask_xmit - transmit mtask to chip for further processing * @conn: transport layer conn structure pointer * @task: transport layer command structure pointer @@ -1284,7 +1259,6 @@ bnx2i_session_create(struct iscsi_endpoint *ep, uint32_t initial_cmdsn) { struct Scsi_Host *shost; - struct iscsi_cls_session *cls_session; struct bnx2i_hba *hba; struct bnx2i_endpoint *bnx2i_ep; @@ -1308,40 +1282,11 @@ 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, - cmds_max, 0, sizeof(struct bnx2i_cmd), - initial_cmdsn, ISCSI_MAX_TARGET); - if (!cls_session) - return NULL; - - if (bnx2i_setup_cmd_pool(hba, cls_session->dd_data)) - goto session_teardown; - return cls_session; - -session_teardown: - iscsi_session_teardown(cls_session); - return NULL; -} - - -/** - * bnx2i_session_destroy - destroys iscsi session - * @cls_session: pointer to iscsi cls session - * - * Destroys previously created iSCSI session instance and releases - * all resources held by it - */ -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 bnx2i_hba *hba = iscsi_host_priv(shost); - - bnx2i_destroy_cmd_pool(hba, session); - iscsi_session_teardown(cls_session); + return iscsi_session_setup(&bnx2i_iscsi_transport, shost, + cmds_max, 0, sizeof(struct bnx2i_cmd), + initial_cmdsn, ISCSI_MAX_TARGET); } - /** * bnx2i_conn_create - create iscsi connection instance * @cls_session: pointer to iscsi cls session @@ -2273,7 +2218,7 @@ struct iscsi_transport bnx2i_iscsi_transport = { CAP_DATA_PATH_OFFLOAD | CAP_TEXT_NEGO, .create_session = bnx2i_session_create, - .destroy_session = bnx2i_session_destroy, + .destroy_session = iscsi_session_teardown, .create_conn = bnx2i_conn_create, .bind_conn = bnx2i_conn_bind, .destroy_conn = bnx2i_conn_destroy, @@ -2284,6 +2229,7 @@ struct iscsi_transport bnx2i_iscsi_transport = { .get_host_param = bnx2i_host_get_param, .start_conn = bnx2i_conn_start, .stop_conn = iscsi_conn_stop, + .alloc_pdu = bnx2i_alloc_pdu, .send_pdu = iscsi_conn_send_pdu, .xmit_task = bnx2i_task_xmit, .get_stats = bnx2i_conn_get_stats, diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index b44c1cf..9b64ddc 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -2306,7 +2306,6 @@ struct iscsi_cls_session *cxgbi_create_session(struct iscsi_endpoint *ep, struct cxgbi_hba *chba; struct Scsi_Host *shost; struct iscsi_cls_session *cls_session; - struct iscsi_session *session; if (!ep) { pr_err("missing endpoint.\n"); @@ -2327,17 +2326,9 @@ struct iscsi_cls_session *cxgbi_create_session(struct iscsi_endpoint *ep, if (!cls_session) return NULL; - session = cls_session->dd_data; - if (iscsi_tcp_r2tpool_alloc(session)) - goto remove_session; - log_debug(1 << CXGBI_DBG_ISCSI, "ep 0x%p, cls sess 0x%p.\n", ep, cls_session); return cls_session; - -remove_session: - iscsi_session_teardown(cls_session); - return NULL; } EXPORT_SYMBOL_GPL(cxgbi_create_session); @@ -2346,7 +2337,6 @@ void cxgbi_destroy_session(struct iscsi_cls_session *cls_session) log_debug(1 << CXGBI_DBG_ISCSI, "cls sess 0x%p.\n", cls_session); - iscsi_tcp_r2tpool_free(cls_session->dd_data); iscsi_session_teardown(cls_session); } EXPORT_SYMBOL_GPL(cxgbi_destroy_session); diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index ad5244d..efb8d75 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -850,12 +850,8 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, tcp_sw_host->session = session; shost->can_queue = session->scsi_cmds_max; - if (iscsi_tcp_r2tpool_alloc(session)) - goto remove_session; return cls_session; -remove_session: - iscsi_session_teardown(cls_session); remove_host: iscsi_host_remove(shost); free_host: @@ -867,7 +863,6 @@ static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) { struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); - iscsi_tcp_r2tpool_free(cls_session->dd_data); iscsi_session_teardown(cls_session); iscsi_host_remove(shost); @@ -961,6 +956,9 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { .proc_name = "iscsi_tcp", .this_id = -1, .use_blk_mq = true, + .cmd_size = sizeof(struct iscsi_task) + + sizeof(struct iscsi_tcp_task) + + sizeof(struct iscsi_sw_tcp_hdrbuf), }; static struct iscsi_transport iscsi_sw_tcp_transport = { diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 63032d3..24b2c3f 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -426,6 +426,12 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) r2t->data_offset = task->imm_count; r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); r2t->exp_statsn = cpu_to_be32(conn->exp_statsn); + /* + * make sure if xmit thread is handling multiple tasks + * it sees all these updated + */ + smp_wmb(); + r2t->sent = 0; } if (!task->unsol_r2t.data_length) @@ -496,9 +502,12 @@ static void iscsi_free_task(struct iscsi_task *task) if (conn->login_task == task) return; - kfifo_in(&session->cmdpool.queue, (void*)&task, sizeof(void*)); + session->task_map[task->itt] = NULL; + percpu_ida_free(&session->itts, task->itt); - if (sc) { + if (!sc) { + kfifo_in(&session->mgmt_pool.queue, (void*)&task, sizeof(void*)); + } else { /* SCSI eh reuses commands to verify us */ sc->SCp.ptr = NULL; /* @@ -595,7 +604,7 @@ EXPORT_SYMBOL_GPL(iscsi_complete_scsi_task); /* - * session back_lock must be held and if not called for a task that is + * session frwd_lock must be held and if not called for a task that is * still pending or from the xmit thread, then xmit thread must * be suspended. */ @@ -696,6 +705,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; struct iscsi_task *task; itt_t itt; + int tag; if (session->state == ISCSI_STATE_TERMINATE) return NULL; @@ -721,10 +731,20 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); - if (!kfifo_out(&session->cmdpool.queue, + if (!kfifo_out(&session->mgmt_pool.queue, (void*)&task, sizeof(void*))) return NULL; + + tag = percpu_ida_alloc(&session->itts, GFP_ATOMIC); + if (tag < 0) { + kfifo_in(&session->mgmt_pool.queue, (void*)&task, + sizeof(void*)); + return NULL; + } + task->itt = tag; + session->task_map[tag] = task; } + /* * released in complete pdu for task we expect a response for, and * released by the lld when it has transmitted the task for @@ -735,6 +755,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, task->sc = NULL; INIT_LIST_HEAD(&task->running); task->state = ISCSI_TASK_PENDING; + task->dd_data = &task[1]; if (data_size) { memcpy(task->data, data, data_size); @@ -1095,7 +1116,7 @@ struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt) if (i >= session->cmds_max) return NULL; - return session->cmds[i]; + return session->task_map[i]; } EXPORT_SYMBOL_GPL(iscsi_itt_to_task); @@ -1563,19 +1584,23 @@ static void iscsi_xmitworker(struct work_struct *work) } while (rc >= 0 || rc == -EAGAIN); } -static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn, - struct scsi_cmnd *sc) +static inline struct iscsi_task *iscsi_init_task(struct iscsi_conn *conn, + struct scsi_cmnd *sc) { - struct iscsi_task *task; - - if (!kfifo_out(&conn->session->cmdpool.queue, - (void *) &task, sizeof(void *))) - return NULL; + struct iscsi_session *session = conn->session; + struct iscsi_task *task = scsi_get_drv_cmd(sc); /*TODO - how to go from cmd to driver struct in hch's patches */ + int tag; - sc->SCp.phase = conn->session->age; + sc->SCp.phase = session->age; sc->SCp.ptr = (char *) task; + tag = percpu_ida_alloc(&session->itts, GFP_ATOMIC); + if (tag < 0) + return NULL; + + task->dd_data = &task[1]; atomic_set(&task->refcount, 1); + task->itt = tag; task->state = ISCSI_TASK_PENDING; task->conn = conn; task->sc = sc; @@ -1583,6 +1608,7 @@ static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn, task->last_timeout = jiffies; task->last_xfer = jiffies; INIT_LIST_HEAD(&task->running); + session->task_map[tag] = task; return task; } @@ -1673,7 +1699,7 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) goto reject; } - task = iscsi_alloc_task(conn, sc); + task = iscsi_init_task(conn, sc); if (!task) { reason = FAILURE_OOM; goto reject; @@ -1824,8 +1850,8 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, } /* - * Fail commands. session lock held and recv side suspended and xmit - * thread flushed + * Fail commands. session frwd_lock must be held and recv side suspended and + * xmit thread flushed */ static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun, int error) @@ -1834,8 +1860,8 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun, int i; for (i = 0; i < conn->session->cmds_max; i++) { - task = conn->session->cmds[i]; - if (!task->sc || task->state == ISCSI_TASK_FREE) + task = conn->session->task_map[i]; + if (!task || !task->sc || task->state == ISCSI_TASK_FREE) continue; if (lun != -1 && lun != task->sc->device->lun) @@ -1978,9 +2004,10 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) } for (i = 0; i < conn->session->cmds_max; i++) { - running_task = conn->session->cmds[i]; - if (!running_task->sc || running_task == task || - running_task->state != ISCSI_TASK_RUNNING) + running_task = conn->session->task_map[i]; + if (!running_task || !running_task->sc || + running_task == task || + running_task->state != ISCSI_TASK_RUNNING) continue; /* @@ -2697,7 +2724,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, struct iscsi_host *ihost = shost_priv(shost); struct iscsi_session *session; struct iscsi_cls_session *cls_session; - int cmd_i, scsi_cmds, total_cmds = cmds_max; + int scsi_cmds, total_cmds = cmds_max; unsigned long flags; spin_lock_irqsave(&ihost->lock, flags); @@ -2766,22 +2793,23 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, spin_lock_init(&session->frwd_lock); spin_lock_init(&session->back_lock); - /* initialize SCSI PDU commands pool */ - if (iscsi_pool_init(&session->cmdpool, session->cmds_max, - (void***)&session->cmds, - cmd_task_size + sizeof(struct iscsi_task))) - goto cmdpool_alloc_fail; + /* + * TODO: make block layer handle this like was done for + * the non-mq host wide tagging. + */ + if (percpu_ida_init(&session->itts, total_cmds)) + goto itts_init; - /* pre-format cmds pool with ITT */ - for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { - struct iscsi_task *task = session->cmds[cmd_i]; + /* initialize LOGIN/NOP/TMF PDU pool */ + if (iscsi_pool_init(&session->mgmt_pool, ISCSI_MGMT_CMDS_MAX, + (void***)&session->mgmt_cmds, + cmd_task_size + sizeof(struct iscsi_task))) + goto mgmtpool_alloc_fail; - if (cmd_task_size) - task->dd_data = &task[1]; - task->itt = cmd_i; - task->state = ISCSI_TASK_FREE; - INIT_LIST_HEAD(&task->running); - } + session->task_map = kzalloc(sizeof(struct iscsi_task *) * + session->cmds_max, GFP_KERNEL); + if (!session->task_map) + goto task_map_alloc_fail; if (!try_module_get(iscsit->owner)) goto module_get_fail; @@ -2794,8 +2822,12 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, cls_session_fail: module_put(iscsit->owner); module_get_fail: - iscsi_pool_free(&session->cmdpool); -cmdpool_alloc_fail: + kfree(session->task_map); +task_map_alloc_fail: + iscsi_pool_free(&session->mgmt_pool); +mgmtpool_alloc_fail: + percpu_ida_destroy(&session->itts); +itts_init: iscsi_free_session(cls_session); dec_session_count: iscsi_host_dec_session_cnt(shost); @@ -2816,7 +2848,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) struct module *owner = cls_session->transport->owner; struct Scsi_Host *shost = session->host; - iscsi_pool_free(&session->cmdpool); + iscsi_pool_free(&session->mgmt_pool); kfree(session->password); kfree(session->password_in); @@ -2831,6 +2863,9 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) kfree(session->ifacename); kfree(session->portal_type); kfree(session->discovery_parent_type); + kfree(session->task_map); + + percpu_ida_destroy(&session->itts); iscsi_destroy_session(cls_session); iscsi_host_dec_session_cnt(shost); @@ -2852,6 +2887,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, struct iscsi_conn *conn; struct iscsi_cls_conn *cls_conn; char *data; + int tag; cls_conn = iscsi_create_conn(cls_session, sizeof(*conn) + dd_size, conn_idx); @@ -2879,7 +2915,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, /* allocate login_task used for the login/text sequences */ spin_lock_bh(&session->frwd_lock); - if (!kfifo_out(&session->cmdpool.queue, + if (!kfifo_out(&session->mgmt_pool.queue, (void*)&conn->login_task, sizeof(void*))) { spin_unlock_bh(&session->frwd_lock); @@ -2893,13 +2929,22 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, goto login_task_data_alloc_fail; conn->login_task->data = conn->data = data; + tag = percpu_ida_alloc(&session->itts, GFP_KERNEL); + if (tag < 0) + goto login_itt_fail; + + conn->login_task->itt = tag; +printk(KERN_ERR "iscsi login task !!!!!!!!!!!!!!!!!! %d\n", conn->login_task->itt); + session->task_map[tag] = conn->login_task; init_timer(&conn->tmf_timer); init_waitqueue_head(&conn->ehwait); return cls_conn; +login_itt_fail: + kfree(data); login_task_data_alloc_fail: - kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, + kfifo_in(&session->mgmt_pool.queue, (void*)&conn->login_task, sizeof(void*)); login_task_alloc_fail: iscsi_destroy_conn(cls_conn); @@ -2965,7 +3010,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) kfree(conn->local_ipaddr); /* regular RX path uses back_lock */ spin_lock_bh(&session->back_lock); - kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, + percpu_ida_free(&session->itts, conn->login_task->itt); + kfifo_in(&session->mgmt_pool.queue, (void*)&conn->login_task, sizeof(void*)); spin_unlock_bh(&session->back_lock); if (session->leadconn == conn) @@ -3051,8 +3097,8 @@ fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn) int i, state; for (i = 0; i < conn->session->cmds_max; i++) { - task = conn->session->cmds[i]; - if (task->sc) + task = conn->session->task_map[i]; + if (!task || task->sc) continue; if (task->state == ISCSI_TASK_FREE) diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e4bec58..3c95032 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -451,27 +451,13 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) void iscsi_tcp_cleanup_task(struct iscsi_task *task) { struct iscsi_tcp_task *tcp_task = task->dd_data; - struct iscsi_r2t_info *r2t; /* nothing to do for mgmt */ if (!task->sc) return; - spin_lock_bh(&tcp_task->queue2pool); - /* flush task's r2t queues */ - while (kfifo_out(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) { - kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t, - sizeof(void*)); - ISCSI_DBG_TCP(task->conn, "pending r2t dropped\n"); - } - - r2t = tcp_task->r2t; - if (r2t != NULL) { - kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t, - sizeof(void*)); - tcp_task->r2t = NULL; - } - spin_unlock_bh(&tcp_task->queue2pool); + tcp_task->r2t.data_length = 0; + tcp_task->r2t.sent = 0; } EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task); @@ -529,11 +515,10 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) struct iscsi_tcp_task *tcp_task = task->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr; - struct iscsi_r2t_info *r2t; + struct iscsi_r2t_info *r2t = &tcp_task->r2t; int r2tsn = be32_to_cpu(rhdr->r2tsn); u32 data_length; u32 data_offset; - int rc; if (tcp_conn->in.datalen) { iscsi_conn_printk(KERN_ERR, conn, @@ -579,28 +564,21 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) return ISCSI_ERR_DATALEN; } - spin_lock(&tcp_task->pool2queue); - rc = kfifo_out(&tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*)); - if (!rc) { - iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. " - "Target has sent more R2Ts than it " - "negotiated for or driver has leaked.\n"); - spin_unlock(&tcp_task->pool2queue); - return ISCSI_ERR_PROTO; - } - r2t->exp_statsn = rhdr->statsn; r2t->data_length = data_length; r2t->data_offset = data_offset; - r2t->ttt = rhdr->ttt; /* no flip */ r2t->datasn = 0; + /* + * TODO: I think this is needed to make sure if the xmit thread + * is handling multiple tasks at a time then we want to make sure + * it sees these fields updated + */ + smp_wmb(); r2t->sent = 0; tcp_task->exp_datasn = r2tsn + 1; - kfifo_in(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*)); conn->r2t_pdus_cnt++; - spin_unlock(&tcp_task->pool2queue); iscsi_requeue_task(task); return 0; @@ -971,7 +949,6 @@ int iscsi_tcp_task_init(struct iscsi_task *task) return conn->session->tt->init_pdu(task, 0, task->data_count); } - BUG_ON(kfifo_len(&tcp_task->r2tqueue)); tcp_task->exp_datasn = 0; /* Prepare PDU, optionally w/ immediate data */ @@ -989,37 +966,17 @@ EXPORT_SYMBOL_GPL(iscsi_tcp_task_init); static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) { struct iscsi_tcp_task *tcp_task = task->dd_data; - struct iscsi_r2t_info *r2t = NULL; - - if (iscsi_task_has_unsol_data(task)) - r2t = &task->unsol_r2t; - else { - spin_lock_bh(&tcp_task->queue2pool); - if (tcp_task->r2t) { - r2t = tcp_task->r2t; - /* Continue with this R2T? */ - if (r2t->data_length <= r2t->sent) { - ISCSI_DBG_TCP(task->conn, - " done with r2t %p\n", r2t); - kfifo_in(&tcp_task->r2tpool.queue, - (void *)&tcp_task->r2t, - sizeof(void *)); - tcp_task->r2t = r2t = NULL; - } - } - - if (r2t == NULL) { - if (kfifo_out(&tcp_task->r2tqueue, - (void *)&tcp_task->r2t, sizeof(void *)) != - sizeof(void *)) - r2t = NULL; - else - r2t = tcp_task->r2t; - } - spin_unlock_bh(&tcp_task->queue2pool); - } - return r2t; + if (task->unsol_r2t.sent == 0 && iscsi_task_has_unsol_data(task)) { + /* Make sure we see the queue sides update of these */ + smp_rmb(); + return &task->unsol_r2t; + } else if (tcp_task->r2t.data_length > tcp_task->r2t.sent) { + /* Make sure we see the recv sides update of these */ + smp_rmb(); + return &tcp_task->r2t; + } else + return NULL; } /** @@ -1115,84 +1072,17 @@ void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn) } EXPORT_SYMBOL_GPL(iscsi_tcp_conn_teardown); -int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session) -{ - int i; - int cmd_i; - - /* - * initialize per-task: R2T pool and xmit queue - */ - for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { - struct iscsi_task *task = session->cmds[cmd_i]; - struct iscsi_tcp_task *tcp_task = task->dd_data; - - /* - * pre-allocated x2 as much r2ts to handle race when - * target acks DataOut faster than we data_xmit() queues - * could replenish r2tqueue. - */ - - /* R2T pool */ - if (iscsi_pool_init(&tcp_task->r2tpool, - session->max_r2t * 2, NULL, - sizeof(struct iscsi_r2t_info))) { - goto r2t_alloc_fail; - } - - /* R2T xmit queue */ - if (kfifo_alloc(&tcp_task->r2tqueue, - session->max_r2t * 4 * sizeof(void*), GFP_KERNEL)) { - iscsi_pool_free(&tcp_task->r2tpool); - goto r2t_alloc_fail; - } - spin_lock_init(&tcp_task->pool2queue); - spin_lock_init(&tcp_task->queue2pool); - } - - return 0; - -r2t_alloc_fail: - for (i = 0; i < cmd_i; i++) { - struct iscsi_task *task = session->cmds[i]; - struct iscsi_tcp_task *tcp_task = task->dd_data; - - kfifo_free(&tcp_task->r2tqueue); - iscsi_pool_free(&tcp_task->r2tpool); - } - return -ENOMEM; -} -EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_alloc); - -void iscsi_tcp_r2tpool_free(struct iscsi_session *session) -{ - int i; - - for (i = 0; i < session->cmds_max; i++) { - struct iscsi_task *task = session->cmds[i]; - struct iscsi_tcp_task *tcp_task = task->dd_data; - - kfifo_free(&tcp_task->r2tqueue); - iscsi_pool_free(&tcp_task->r2tpool); - } -} -EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free); - int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf) { struct iscsi_session *session = conn->session; unsigned short r2ts = 0; sscanf(buf, "%hu", &r2ts); - if (session->max_r2t == r2ts) - return 0; - - if (!r2ts || !is_power_of_2(r2ts)) + if (r2ts != 1) return -EINVAL; session->max_r2t = r2ts; - iscsi_tcp_r2tpool_free(session); - return iscsi_tcp_r2tpool_alloc(session); + return 0; } EXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t); diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 7221a24..1ef3aa0 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -29,6 +29,7 @@ #include <linux/timer.h> #include <linux/workqueue.h> #include <linux/kfifo.h> +#include <linux/percpu_ida.h> #include <scsi/iscsi_proto.h> #include <scsi/iscsi_if.h> #include <scsi/scsi_transport_iscsi.h> @@ -335,18 +336,18 @@ struct iscsi_session { spinlock_t frwd_lock; /* protects session state, * * cmdsn, queued_cmdsn * * session resources: * - * - cmdpool kfifo_out , * - * - mgmtpool, */ + * - mgmt_pool kfifo, */ spinlock_t back_lock; /* protects cmdsn_exp * - * cmdsn_max, * - * cmdpool kfifo_in */ + * cmdsn_max, */ int state; /* session state */ int age; /* counts session re-opens */ - int scsi_cmds_max; /* max scsi commands */ + int scsi_cmds_max; /* max scsi commands */ + struct percpu_ida itts; /* itt ida */ + struct iscsi_task **task_map; /* itt to task map */ int cmds_max; /* size of cmds array */ - struct iscsi_task **cmds; /* Original Cmds arr */ - struct iscsi_pool cmdpool; /* PDU's pool */ + struct iscsi_task **mgmt_cmds; /* Mgmt cmds ar */ + struct iscsi_pool mgmt_pool; /* Mgmt PDU pool */ void *dd_data; /* LLD private data */ }; diff --git a/include/scsi/libiscsi_tcp.h b/include/scsi/libiscsi_tcp.h index 2a7aa75..c494660 100644 --- a/include/scsi/libiscsi_tcp.h +++ b/include/scsi/libiscsi_tcp.h @@ -79,12 +79,8 @@ struct iscsi_tcp_conn { struct iscsi_tcp_task { uint32_t exp_datasn; /* expected target's R2TSN/DataSN */ int data_offset; - struct iscsi_r2t_info *r2t; /* in progress solict R2T */ - struct iscsi_pool r2tpool; - struct kfifo r2tqueue; + struct iscsi_r2t_info r2t; void *dd_data; - spinlock_t pool2queue; - spinlock_t queue2pool; }; enum { @@ -128,8 +124,6 @@ iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size, extern void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn); /* misc helpers */ -extern int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session); -extern void iscsi_tcp_r2tpool_free(struct iscsi_session *session); extern int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf); extern void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats);