scsi:libiscsi: Hold back_lock when calling iscsi_complete_task
PATCH v2 1/1 scsi:libiscsi: Hold back_lock when calling iscsi_complete_task
From: Lee Duncan <[email protected]>
If there is an error queueing an iscsi command in iscsi_queuecommand(), for example if the transport fails to take the command in sessuin->tt->xmit_task(), then the error path can call iscsi_complete_task() without first aquiring the back_lock as required. This can lead to things like ITT pool can get corrupt, resulting in duplicate ITTs being sent out.
The solution is to hold the back_lock around iscsi_complete_task() calls, and to add a little commenting to help others understand when back_lock must be held.
--- drivers/scsi/libiscsi.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)
drivers/scsi/libiscsi.c +20 -2
@@ -838,7 +838,7 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);* @datalen: len of buffer * * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and -* then completes the command and task. +* then completes the command and task. called under back_lock **/ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, struct iscsi_task *task, char *data,
@@ -941,6 +941,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,* @conn: iscsi connection * @hdr: iscsi pdu * @task: scsi command task +* +* iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received +* then completes the command and task. called under back_lock **/ static void iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
@@ -1025,6 +1028,16 @@ static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)return 0 ; } +/** +* iscsi_nop_out_rsp - SCSI NOP Response processing +* @task: scsi command task +* @nop: the nop structure +* @data: where to put the data +* @datalen: length of data +* +* iscsi_nop_out_rsp handles nop response from use or +* from user space. called under back_lock +**/ static int iscsi_nop_out_rsp (struct iscsi_task *task,struct iscsi_nopin *nop,char *data,int datalen) {
@@ -1791,7 +1804,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)return 0 ; prepd_reject: +spin_lock_bh (&session->back_lock);iscsi_complete_task (task,ISCSI_TASK_REQUEUE_SCSIQ ); +spin_unlock_bh (&session->back_lock); reject:spin_unlock_bh (&session->frwd_lock); ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",
@@ -1799,7 +1814,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)return SCSI_MLQUEUE_TARGET_BUSY ; prepd_fault: +spin_lock_bh (&session->back_lock);iscsi_complete_task (task,ISCSI_TASK_REQUEUE_SCSIQ ); +spin_unlock_bh (&session->back_lock); fault:spin_unlock_bh (&session->frwd_lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",
@@ -3121,8 +3138,9 @@ fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn) state =ISCSI_TASK_ABRT_SESS_RECOV ;if (task->state ==ISCSI_TASK_PENDING ) state =ISCSI_TASK_COMPLETED ; +spin_lock_bh (&session->back_lock);iscsi_complete_task (task, state); - +spin_unlock_bh (&session->back_lock); } }
Signature · 1 line
2.16.4
Chris Leech
maintainerQuoted 10 lines · Lee Duncan
From: Lee Duncan <[email protected]>
If there is an error queueing an iscsi command in
iscsi_queuecommand(), for example if the transport fails to take the command insessuin->tt->xmit_task(), then the error path can calliscsi_complete_task()without first aquiring theback_lockas required. This can lead to things like ITT pool can get corrupt, resulting in duplicate ITTs being sent out.
Quoted 6 lines
The solution is to hold the
back_lockaroundiscsi_complete_task()calls, and to add a little commenting to help others understand whenback_lockmust be held.Signed-off-by Lee Duncan <[email protected]>---
Lee,
Quick question, can you confirm that you tested this with lockdep?
It seems right to me, it's just that we've been hit with lockdep problems dealing with these locks before.
- Chris
Quoted 77 lines
drivers/scsi/libiscsi.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)drivers/scsi/libiscsi.c +20
-2@@ -838,7 +838,7 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);* @datalen: len of buffer * * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and -* then completes the command and task. +* then completes the command and task. called under back_lock **/ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, struct iscsi_task *task, char *data,@@ -941,6 +941,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,* @conn: iscsi connection * @hdr: iscsi pdu * @task: scsi command task +* +* iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received +* then completes the command and task. called under back_lock **/ static void iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,@@ -1025,6 +1028,16 @@ static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)return 0 ; } +/** +* iscsi_nop_out_rsp - SCSI NOP Response processing +* @task: scsi command task +* @nop: the nop structure +* @data: where to put the data +* @datalen: length of data +* +* iscsi_nop_out_rsp handles nop response from use or +* from user space. called under back_lock +**/ static int iscsi_nop_out_rsp (struct iscsi_task *task,struct iscsi_nopin *nop,char *data,int datalen) {@@ -1791,7 +1804,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)return 0 ; prepd_reject: +spin_lock_bh (&session->back_lock);iscsi_complete_task (task,ISCSI_TASK_REQUEUE_SCSIQ ); +spin_unlock_bh (&session->back_lock); reject:spin_unlock_bh (&session->frwd_lock); ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",@@ -1799,7 +1814,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)return SCSI_MLQUEUE_TARGET_BUSY ; prepd_fault: +spin_lock_bh (&session->back_lock);iscsi_complete_task (task,ISCSI_TASK_REQUEUE_SCSIQ ); +spin_unlock_bh (&session->back_lock); fault:spin_unlock_bh (&session->frwd_lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",@@ -3121,8 +3138,9 @@ fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn) state =ISCSI_TASK_ABRT_SESS_RECOV ;if (task->state ==ISCSI_TASK_PENDING ) state =ISCSI_TASK_COMPLETED ; +spin_lock_bh (&session->back_lock);iscsi_complete_task (task, state); - +spin_unlock_bh (&session->back_lock); } }Signature · 1 line
2.16.4
Lee Duncan
maintainerQuoted 18 lines · Chris Leech
Quoted 10 lines · Lee Duncan
From: Lee Duncan <[email protected]>
If there is an error queueing an iscsi command in
iscsi_queuecommand(), for example if the transport fails to take the command insessuin->tt->xmit_task(), then the error path can calliscsi_complete_task()without first aquiring theback_lockas required. This can lead to things like ITT pool can get corrupt, resulting in duplicate ITTs being sent out.Quoted 6 lines
The solution is to hold the
back_lockaroundiscsi_complete_task()calls, and to add a little commenting to help others understand whenback_lockmust be held.Signed-off-by Lee Duncan <[email protected]>---
Quoted 8 lines
Lee,
Quick question, can you confirm that you tested this with lockdep?
It seems right to me, it's just that we've been hit with lockdep problems dealing with these locks before.
- Chris
I'm glad you asked, to keep me honest -- I had not done it yet, because it seemed obvious to me.
But I did check today, and did not find any deadlock nor leaks. I was testing on a 5.0.0-1 kernel.
Quoted 79 lines
Quoted 78 lines
drivers/scsi/libiscsi.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)drivers/scsi/libiscsi.c +20
-2@@ -838,7 +838,7 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);* @datalen: len of buffer * * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and -* then completes the command and task. +* then completes the command and task. called under back_lock **/ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, struct iscsi_task *task, char *data,@@ -941,6 +941,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,* @conn: iscsi connection * @hdr: iscsi pdu * @task: scsi command task +* +* iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received +* then completes the command and task. called under back_lock **/ static void iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,@@ -1025,6 +1028,16 @@ static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)return 0 ; } +/** +* iscsi_nop_out_rsp - SCSI NOP Response processing +* @task: scsi command task +* @nop: the nop structure +* @data: where to put the data +* @datalen: length of data +* +* iscsi_nop_out_rsp handles nop response from use or +* from user space. called under back_lock +**/ static int iscsi_nop_out_rsp (struct iscsi_task *task,struct iscsi_nopin *nop,char *data,int datalen) {@@ -1791,7 +1804,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)return 0 ; prepd_reject: +spin_lock_bh (&session->back_lock);iscsi_complete_task (task,ISCSI_TASK_REQUEUE_SCSIQ ); +spin_unlock_bh (&session->back_lock); reject:spin_unlock_bh (&session->frwd_lock); ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",@@ -1799,7 +1814,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)return SCSI_MLQUEUE_TARGET_BUSY ; prepd_fault: +spin_lock_bh (&session->back_lock);iscsi_complete_task (task,ISCSI_TASK_REQUEUE_SCSIQ ); +spin_unlock_bh (&session->back_lock); fault:spin_unlock_bh (&session->frwd_lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",@@ -3121,8 +3138,9 @@ fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn) state =ISCSI_TASK_ABRT_SESS_RECOV ;if (task->state ==ISCSI_TASK_PENDING ) state =ISCSI_TASK_COMPLETED ; +spin_lock_bh (&session->back_lock);iscsi_complete_task (task, state); - +spin_unlock_bh (&session->back_lock); } }Signature · 1 line
2.16.4
Quoted 26 lines · Lee Duncan
Quoted 25 lines · Chris Leech
Quoted 10 lines · Lee Duncan
From: Lee Duncan <[email protected]>
If there is an error queueing an iscsi command in
iscsi_queuecommand(), for example if the transport fails to take the command insessuin->tt->xmit_task(), then the error path can calliscsi_complete_task()without first aquiring theback_lockas required. This can lead to things like ITT pool can get corrupt, resulting in duplicate ITTs being sent out.Quoted 6 lines
The solution is to hold the
back_lockaroundiscsi_complete_task()calls, and to add a little commenting to help others understand whenback_lockmust be held.Signed-off-by Lee Duncan <[email protected]>---
Lee,
Quick question, can you confirm that you tested this with lockdep?
It seems right to me, it's just that we've been hit with lockdep problems dealing with these locks before.
Quoted 7 lines
Quoted 1 line
- Chris
I'm glad you asked, to keep me honest -- I had not done it yet, because it seemed obvious to me.
But I did check today, and did not find any deadlock nor leaks. I was testing on a 5.0.0-1 kernel.
Thanks!
Martin K. Petersen
AppliedLee,
Quoted 10 lines
If there is an error queueing an iscsi command in
iscsi_queuecommand(), for example if the transport fails to take the command insessuin->tt->xmit_task(), then the error path can calliscsi_complete_task()without first aquiring theback_lockas required. This can lead to things like ITT pool can get corrupt, resulting in duplicate ITTs being sent out.The solution is to hold the
back_lockaroundiscsi_complete_task()calls, and to add a little commenting to help others understand whenback_lockmust be held.
Applied to 5.1/scsi-queue, thanks!
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering