aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libiscsi_tcp.c
diff options
context:
space:
mode:
authorShlomo Pongratz <shlomop@mellanox.com>2014-02-07 01:41:38 -0500
committerJames Bottomley <JBottomley@Parallels.com>2014-03-15 13:19:18 -0400
commit659743b02c411075b26601725947b21df0bb29c8 (patch)
tree5c0350e508cdc32c97bf8d86da0ecfa5c15915f5 /drivers/scsi/libiscsi_tcp.c
parent5d0fddd0a72d3023bb0fa97e546f687aa6222be3 (diff)
[SCSI] libiscsi: Reduce locking contention in fast path
Replace the session lock with two locks, a forward lock and a backwards lock named frwd_lock and back_lock respectively. The forward lock protects resources that change while sending a request to the target, such as cmdsn, queued_cmdsn, and allocating task from the commands' pool with kfifo_out. The backward lock protects resources that change while processing a response or in error path, such as cmdsn_exp, cmdsn_max, and returning tasks to the commands' pool with kfifo_in. Under a steady state fast-path situation, that is when one or more processes/threads submit IO to an iscsi device and a single kernel upcall (e.g softirq) is dealing with processing of responses without errors, this patch eliminates the contention between the queuecommand()/request response/scsi_done() flows associated with iscsi sessions. Between the forward and the backward locks exists a strict locking hierarchy. The mutual exclusion zone protected by the forward lock can enclose the mutual exclusion zone protected by the backward lock but not vice versa. For example, in iscsi_conn_teardown or in iscsi_xmit_data when there is a failure and __iscsi_put_task is called, the backward lock is taken while the forward lock is still taken. On the other hand, if in the RX path a nop is to be sent, for example in iscsi_handle_reject or __iscsi_complete_pdu than the forward lock is released and the backward lock is taken for the duration of iscsi_send_nopout, later the backward lock is released and the forward lock is retaken. libiscsi_tcp uses two kernel fifos the r2t pool and the r2t queue. The insertion and deletion from these queues didn't corespond to the assumption taken by the new forward/backwards session locking paradigm. That is, in iscsi_tcp_clenup_task which belongs to the RX (backwards) path, r2t is taken out from r2t queue and inserted to the r2t pool. In iscsi_tcp_get_curr_r2t which belong to the TX (forward) path, r2t is also inserted to the r2t pool and another r2t is pulled from r2t queue. Only in iscsi_tcp_r2t_rsp which is called in the RX path but can requeue to the TX path, r2t is taken from the r2t pool and inserted to the r2t queue. In order to cope with this situation, two spin locks were added, pool2queue and queue2pool. The former protects extracting from the r2t pool and inserting to the r2t queue, and the later protects the extracing from the r2t queue and inserting to the r2t pool. Signed-off-by: Shlomo Pongratz <shlomop@mellanox.com> Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com> [minor fix up to apply cleanly and compile fix] Signed-off-by: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/libiscsi_tcp.c')
-rw-r--r--drivers/scsi/libiscsi_tcp.c28
1 files changed, 18 insertions, 10 deletions
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index 2f738dddd078..60cb6dc3c6f0 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -446,7 +446,7 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
446 * iscsi_tcp_cleanup_task - free tcp_task resources 446 * iscsi_tcp_cleanup_task - free tcp_task resources
447 * @task: iscsi task 447 * @task: iscsi task
448 * 448 *
449 * must be called with session lock 449 * must be called with session back_lock
450 */ 450 */
451void iscsi_tcp_cleanup_task(struct iscsi_task *task) 451void iscsi_tcp_cleanup_task(struct iscsi_task *task)
452{ 452{
@@ -457,6 +457,7 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task)
457 if (!task->sc) 457 if (!task->sc)
458 return; 458 return;
459 459
460 spin_lock_bh(&tcp_task->queue2pool);
460 /* flush task's r2t queues */ 461 /* flush task's r2t queues */
461 while (kfifo_out(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) { 462 while (kfifo_out(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
462 kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t, 463 kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t,
@@ -470,6 +471,7 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task)
470 sizeof(void*)); 471 sizeof(void*));
471 tcp_task->r2t = NULL; 472 tcp_task->r2t = NULL;
472 } 473 }
474 spin_unlock_bh(&tcp_task->queue2pool);
473} 475}
474EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task); 476EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task);
475 477
@@ -577,11 +579,13 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
577 return ISCSI_ERR_DATALEN; 579 return ISCSI_ERR_DATALEN;
578 } 580 }
579 581
582 spin_lock(&tcp_task->pool2queue);
580 rc = kfifo_out(&tcp_task->r2tpool.queue, (void *)&r2t, sizeof(void *)); 583 rc = kfifo_out(&tcp_task->r2tpool.queue, (void *)&r2t, sizeof(void *));
581 if (!rc) { 584 if (!rc) {
582 iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. " 585 iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. "
583 "Target has sent more R2Ts than it " 586 "Target has sent more R2Ts than it "
584 "negotiated for or driver has leaked.\n"); 587 "negotiated for or driver has leaked.\n");
588 spin_unlock(&tcp_task->pool2queue);
585 return ISCSI_ERR_PROTO; 589 return ISCSI_ERR_PROTO;
586 } 590 }
587 591
@@ -596,6 +600,7 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
596 tcp_task->exp_datasn = r2tsn + 1; 600 tcp_task->exp_datasn = r2tsn + 1;
597 kfifo_in(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*)); 601 kfifo_in(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
598 conn->r2t_pdus_cnt++; 602 conn->r2t_pdus_cnt++;
603 spin_unlock(&tcp_task->pool2queue);
599 604
600 iscsi_requeue_task(task); 605 iscsi_requeue_task(task);
601 return 0; 606 return 0;
@@ -668,14 +673,14 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
668 673
669 switch(opcode) { 674 switch(opcode) {
670 case ISCSI_OP_SCSI_DATA_IN: 675 case ISCSI_OP_SCSI_DATA_IN:
671 spin_lock(&conn->session->lock); 676 spin_lock(&conn->session->back_lock);
672 task = iscsi_itt_to_ctask(conn, hdr->itt); 677 task = iscsi_itt_to_ctask(conn, hdr->itt);
673 if (!task) 678 if (!task)
674 rc = ISCSI_ERR_BAD_ITT; 679 rc = ISCSI_ERR_BAD_ITT;
675 else 680 else
676 rc = iscsi_tcp_data_in(conn, task); 681 rc = iscsi_tcp_data_in(conn, task);
677 if (rc) { 682 if (rc) {
678 spin_unlock(&conn->session->lock); 683 spin_unlock(&conn->session->back_lock);
679 break; 684 break;
680 } 685 }
681 686
@@ -708,11 +713,11 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
708 tcp_conn->in.datalen, 713 tcp_conn->in.datalen,
709 iscsi_tcp_process_data_in, 714 iscsi_tcp_process_data_in,
710 rx_hash); 715 rx_hash);
711 spin_unlock(&conn->session->lock); 716 spin_unlock(&conn->session->back_lock);
712 return rc; 717 return rc;
713 } 718 }
714 rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); 719 rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
715 spin_unlock(&conn->session->lock); 720 spin_unlock(&conn->session->back_lock);
716 break; 721 break;
717 case ISCSI_OP_SCSI_CMD_RSP: 722 case ISCSI_OP_SCSI_CMD_RSP:
718 if (tcp_conn->in.datalen) { 723 if (tcp_conn->in.datalen) {
@@ -722,18 +727,20 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
722 rc = iscsi_complete_pdu(conn, hdr, NULL, 0); 727 rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
723 break; 728 break;
724 case ISCSI_OP_R2T: 729 case ISCSI_OP_R2T:
725 spin_lock(&conn->session->lock); 730 spin_lock(&conn->session->back_lock);
726 task = iscsi_itt_to_ctask(conn, hdr->itt); 731 task = iscsi_itt_to_ctask(conn, hdr->itt);
732 spin_unlock(&conn->session->back_lock);
727 if (!task) 733 if (!task)
728 rc = ISCSI_ERR_BAD_ITT; 734 rc = ISCSI_ERR_BAD_ITT;
729 else if (ahslen) 735 else if (ahslen)
730 rc = ISCSI_ERR_AHSLEN; 736 rc = ISCSI_ERR_AHSLEN;
731 else if (task->sc->sc_data_direction == DMA_TO_DEVICE) { 737 else if (task->sc->sc_data_direction == DMA_TO_DEVICE) {
732 task->last_xfer = jiffies; 738 task->last_xfer = jiffies;
739 spin_lock(&conn->session->frwd_lock);
733 rc = iscsi_tcp_r2t_rsp(conn, task); 740 rc = iscsi_tcp_r2t_rsp(conn, task);
741 spin_unlock(&conn->session->frwd_lock);
734 } else 742 } else
735 rc = ISCSI_ERR_PROTO; 743 rc = ISCSI_ERR_PROTO;
736 spin_unlock(&conn->session->lock);
737 break; 744 break;
738 case ISCSI_OP_LOGIN_RSP: 745 case ISCSI_OP_LOGIN_RSP:
739 case ISCSI_OP_TEXT_RSP: 746 case ISCSI_OP_TEXT_RSP:
@@ -981,14 +988,13 @@ EXPORT_SYMBOL_GPL(iscsi_tcp_task_init);
981 988
982static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) 989static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
983{ 990{
984 struct iscsi_session *session = task->conn->session;
985 struct iscsi_tcp_task *tcp_task = task->dd_data; 991 struct iscsi_tcp_task *tcp_task = task->dd_data;
986 struct iscsi_r2t_info *r2t = NULL; 992 struct iscsi_r2t_info *r2t = NULL;
987 993
988 if (iscsi_task_has_unsol_data(task)) 994 if (iscsi_task_has_unsol_data(task))
989 r2t = &task->unsol_r2t; 995 r2t = &task->unsol_r2t;
990 else { 996 else {
991 spin_lock_bh(&session->lock); 997 spin_lock_bh(&tcp_task->queue2pool);
992 if (tcp_task->r2t) { 998 if (tcp_task->r2t) {
993 r2t = tcp_task->r2t; 999 r2t = tcp_task->r2t;
994 /* Continue with this R2T? */ 1000 /* Continue with this R2T? */
@@ -1010,7 +1016,7 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
1010 else 1016 else
1011 r2t = tcp_task->r2t; 1017 r2t = tcp_task->r2t;
1012 } 1018 }
1013 spin_unlock_bh(&session->lock); 1019 spin_unlock_bh(&tcp_task->queue2pool);
1014 } 1020 }
1015 1021
1016 return r2t; 1022 return r2t;
@@ -1140,6 +1146,8 @@ int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session)
1140 iscsi_pool_free(&tcp_task->r2tpool); 1146 iscsi_pool_free(&tcp_task->r2tpool);
1141 goto r2t_alloc_fail; 1147 goto r2t_alloc_fail;
1142 } 1148 }
1149 spin_lock_init(&tcp_task->pool2queue);
1150 spin_lock_init(&tcp_task->queue2pool);
1143 } 1151 }
1144 1152
1145 return 0; 1153 return 0;