Skip to content

Commit 6f8830f

Browse files
cleechmartinkpetersen
authored andcommitted
scsi: libiscsi: add lock around task lists to fix list corruption regression
There's a rather long standing regression from the commit "libiscsi: Reduce locking contention in fast path" Depending on iSCSI target behavior, it's possible to hit the case in iscsi_complete_task where the task is still on a pending list (!list_empty(&task->running)). When that happens the task is removed from the list while holding the session back_lock, but other task list modification occur under the frwd_lock. That leads to linked list corruption and eventually a panicked system. Rather than back out the session lock split entirely, in order to try and keep some of the performance gains this patch adds another lock to maintain the task lists integrity. Major enterprise supported kernels have been backing out the lock split for while now, thanks to the efforts at IBM where a lab setup has the most reliable reproducer I've seen on this issue. This patch has been tested there successfully. Signed-off-by: Chris Leech <cleech@redhat.com> Fixes: 659743b ("[SCSI] libiscsi: Reduce locking contention in fast path") Reported-by: Prashantha Subbarao <psubbara@us.ibm.com> Reviewed-by: Guilherme G. Piccoli <gpiccoli@linux.vnet.ibm.com> Cc: <stable@vger.kernel.org> # v3.15+ Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent d1a9ccc commit 6f8830f

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

drivers/scsi/libiscsi.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,8 +560,12 @@ static void iscsi_complete_task(struct iscsi_task *task, int state)
560560
WARN_ON_ONCE(task->state == ISCSI_TASK_FREE);
561561
task->state = state;
562562

563-
if (!list_empty(&task->running))
563+
spin_lock_bh(&conn->taskqueuelock);
564+
if (!list_empty(&task->running)) {
565+
pr_debug_once("%s while task on list", __func__);
564566
list_del_init(&task->running);
567+
}
568+
spin_unlock_bh(&conn->taskqueuelock);
565569

566570
if (conn->task == task)
567571
conn->task = NULL;
@@ -783,7 +787,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
783787
if (session->tt->xmit_task(task))
784788
goto free_task;
785789
} else {
790+
spin_lock_bh(&conn->taskqueuelock);
786791
list_add_tail(&task->running, &conn->mgmtqueue);
792+
spin_unlock_bh(&conn->taskqueuelock);
787793
iscsi_conn_queue_work(conn);
788794
}
789795

@@ -1474,8 +1480,10 @@ void iscsi_requeue_task(struct iscsi_task *task)
14741480
* this may be on the requeue list already if the xmit_task callout
14751481
* is handling the r2ts while we are adding new ones
14761482
*/
1483+
spin_lock_bh(&conn->taskqueuelock);
14771484
if (list_empty(&task->running))
14781485
list_add_tail(&task->running, &conn->requeue);
1486+
spin_unlock_bh(&conn->taskqueuelock);
14791487
iscsi_conn_queue_work(conn);
14801488
}
14811489
EXPORT_SYMBOL_GPL(iscsi_requeue_task);
@@ -1512,42 +1520,51 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
15121520
* only have one nop-out as a ping from us and targets should not
15131521
* overflow us with nop-ins
15141522
*/
1523+
spin_lock_bh(&conn->taskqueuelock);
15151524
check_mgmt:
15161525
while (!list_empty(&conn->mgmtqueue)) {
15171526
conn->task = list_entry(conn->mgmtqueue.next,
15181527
struct iscsi_task, running);
15191528
list_del_init(&conn->task->running);
1529+
spin_unlock_bh(&conn->taskqueuelock);
15201530
if (iscsi_prep_mgmt_task(conn, conn->task)) {
15211531
/* regular RX path uses back_lock */
15221532
spin_lock_bh(&conn->session->back_lock);
15231533
__iscsi_put_task(conn->task);
15241534
spin_unlock_bh(&conn->session->back_lock);
15251535
conn->task = NULL;
1536+
spin_lock_bh(&conn->taskqueuelock);
15261537
continue;
15271538
}
15281539
rc = iscsi_xmit_task(conn);
15291540
if (rc)
15301541
goto done;
1542+
spin_lock_bh(&conn->taskqueuelock);
15311543
}
15321544

15331545
/* process pending command queue */
15341546
while (!list_empty(&conn->cmdqueue)) {
15351547
conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task,
15361548
running);
15371549
list_del_init(&conn->task->running);
1550+
spin_unlock_bh(&conn->taskqueuelock);
15381551
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
15391552
fail_scsi_task(conn->task, DID_IMM_RETRY);
1553+
spin_lock_bh(&conn->taskqueuelock);
15401554
continue;
15411555
}
15421556
rc = iscsi_prep_scsi_cmd_pdu(conn->task);
15431557
if (rc) {
15441558
if (rc == -ENOMEM || rc == -EACCES) {
1559+
spin_lock_bh(&conn->taskqueuelock);
15451560
list_add_tail(&conn->task->running,
15461561
&conn->cmdqueue);
15471562
conn->task = NULL;
1563+
spin_unlock_bh(&conn->taskqueuelock);
15481564
goto done;
15491565
} else
15501566
fail_scsi_task(conn->task, DID_ABORT);
1567+
spin_lock_bh(&conn->taskqueuelock);
15511568
continue;
15521569
}
15531570
rc = iscsi_xmit_task(conn);
@@ -1558,6 +1575,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
15581575
* we need to check the mgmt queue for nops that need to
15591576
* be sent to aviod starvation
15601577
*/
1578+
spin_lock_bh(&conn->taskqueuelock);
15611579
if (!list_empty(&conn->mgmtqueue))
15621580
goto check_mgmt;
15631581
}
@@ -1577,12 +1595,15 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
15771595
conn->task = task;
15781596
list_del_init(&conn->task->running);
15791597
conn->task->state = ISCSI_TASK_RUNNING;
1598+
spin_unlock_bh(&conn->taskqueuelock);
15801599
rc = iscsi_xmit_task(conn);
15811600
if (rc)
15821601
goto done;
1602+
spin_lock_bh(&conn->taskqueuelock);
15831603
if (!list_empty(&conn->mgmtqueue))
15841604
goto check_mgmt;
15851605
}
1606+
spin_unlock_bh(&conn->taskqueuelock);
15861607
spin_unlock_bh(&conn->session->frwd_lock);
15871608
return -ENODATA;
15881609

@@ -1738,7 +1759,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
17381759
goto prepd_reject;
17391760
}
17401761
} else {
1762+
spin_lock_bh(&conn->taskqueuelock);
17411763
list_add_tail(&task->running, &conn->cmdqueue);
1764+
spin_unlock_bh(&conn->taskqueuelock);
17421765
iscsi_conn_queue_work(conn);
17431766
}
17441767

@@ -2896,6 +2919,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
28962919
INIT_LIST_HEAD(&conn->mgmtqueue);
28972920
INIT_LIST_HEAD(&conn->cmdqueue);
28982921
INIT_LIST_HEAD(&conn->requeue);
2922+
spin_lock_init(&conn->taskqueuelock);
28992923
INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
29002924

29012925
/* allocate login_task used for the login/text sequences */

include/scsi/libiscsi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ struct iscsi_conn {
196196
struct iscsi_task *task; /* xmit task in progress */
197197

198198
/* xmit */
199+
spinlock_t taskqueuelock; /* protects the next three lists */
199200
struct list_head mgmtqueue; /* mgmt (control) xmit queue */
200201
struct list_head cmdqueue; /* data-path cmd queue */
201202
struct list_head requeue; /* tasks needing another run */

0 commit comments

Comments
 (0)