* [PATCH-v4.1.y 1/8] iscsi-target: Fix rx_login_comp hang after login failure
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 2/8] target: Fix race for SCF_COMPARE_AND_WRITE_POST checking Nicholas A. Bellinger
` (7 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable; +Cc: Sasha Levin, Nicholas Bellinger, Sagi Grimberg
From: Nicholas Bellinger <nab@linux-iscsi.org>
[ Upstream commit ca82c2bded29b38d36140bfa1e76a7bbfcade390 ]
This patch addresses a case where iscsi_target_do_tx_login_io()
fails sending the last login response PDU, after the RX/TX
threads have already been started.
The case centers around iscsi_target_rx_thread() not invoking
allow_signal(SIGINT) before the send_sig(SIGINT, ...) occurs
from the failure path, resulting in RX thread hanging
indefinately on iscsi_conn->rx_login_comp.
Note this bug is a regression introduced by:
commit e54198657b65625085834847ab6271087323ffea
Author: Nicholas Bellinger <nab@linux-iscsi.org>
Date: Wed Jul 22 23:14:19 2015 -0700
iscsi-target: Fix iscsit_start_kthreads failure OOPs
To address this bug, complete ->rx_login_complete for good
measure in the failure path, and immediately return from
RX thread context if connection state did not actually reach
full feature phase (TARG_CONN_STATE_LOGGED_IN).
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: <stable@vger.kernel.org> # v3.10+
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/iscsi/iscsi_target.c | 13 ++++++++++++-
drivers/target/iscsi/iscsi_target_nego.c | 1 +
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 2e58279..6f50e9d 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -4095,6 +4095,17 @@ reject:
return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
}
+static bool iscsi_target_check_conn_state(struct iscsi_conn *conn)
+{
+ bool ret;
+
+ spin_lock_bh(&conn->state_lock);
+ ret = (conn->conn_state != TARG_CONN_STATE_LOGGED_IN);
+ spin_unlock_bh(&conn->state_lock);
+
+ return ret;
+}
+
int iscsi_target_rx_thread(void *arg)
{
int ret, rc;
@@ -4112,7 +4123,7 @@ int iscsi_target_rx_thread(void *arg)
* incoming iscsi/tcp socket I/O, and/or failing the connection.
*/
rc = wait_for_completion_interruptible(&conn->rx_login_comp);
- if (rc < 0)
+ if (rc < 0 || iscsi_target_check_conn_state(conn))
return 0;
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index f9cde91..9a96f17 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -393,6 +393,7 @@ err:
if (login->login_complete) {
if (conn->rx_thread && conn->rx_thread_active) {
send_sig(SIGINT, conn->rx_thread, 1);
+ complete(&conn->rx_login_comp);
kthread_stop(conn->rx_thread);
}
if (conn->tx_thread && conn->tx_thread_active) {
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH-v4.1.y 2/8] target: Fix race for SCF_COMPARE_AND_WRITE_POST checking
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 1/8] iscsi-target: Fix rx_login_comp hang after login failure Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 3/8] target: fix COMPARE_AND_WRITE non zero SGL offset data corruption Nicholas A. Bellinger
` (6 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable; +Cc: Sasha Levin, Nicholas Bellinger, Sagi Grimberg
From: Nicholas Bellinger <nab@linux-iscsi.org>
[ Upstream commit 057085e522f8bf94c2e691a5b76880f68060f8ba ]
This patch addresses a race + use after free where the first
stage of COMPARE_AND_WRITE in compare_and_write_callback()
is rescheduled after the backend sends the secondary WRITE,
resulting in second stage compare_and_write_post() callback
completing in target_complete_ok_work() before the first
can return.
Because current code depends on checking se_cmd->se_cmd_flags
after return from se_cmd->transport_complete_callback(),
this results in first stage having SCF_COMPARE_AND_WRITE_POST
set, which incorrectly falls through into second stage CAW
processing code, eventually triggering a NULL pointer
dereference due to use after free.
To address this bug, pass in a new *post_ret parameter into
se_cmd->transport_complete_callback(), and depend upon this
value instead of ->se_cmd_flags to determine when to return
or fall through into ->queue_status() code for CAW.
Cc: Sagi Grimberg <sagig@mellanox.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/target_core_sbc.c | 13 +++++++++----
drivers/target/target_core_transport.c | 14 ++++++++------
include/target/target_core_base.h | 2 +-
3 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 733824e..a3bfafc 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -321,7 +321,8 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
return 0;
}
-static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success)
+static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success,
+ int *post_ret)
{
unsigned char *buf, *addr;
struct scatterlist *sg;
@@ -385,7 +386,8 @@ sbc_execute_rw(struct se_cmd *cmd)
cmd->data_direction);
}
-static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
+static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
+ int *post_ret)
{
struct se_device *dev = cmd->se_dev;
@@ -395,8 +397,10 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
* sent to the backend driver.
*/
spin_lock_irq(&cmd->t_state_lock);
- if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status)
+ if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) {
cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
+ *post_ret = 1;
+ }
spin_unlock_irq(&cmd->t_state_lock);
/*
@@ -408,7 +412,8 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
return TCM_NO_SENSE;
}
-static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success)
+static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
+ int *post_ret)
{
struct se_device *dev = cmd->se_dev;
struct scatterlist *write_sg = NULL, *sg;
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 3881504..316de3f 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1638,7 +1638,7 @@ bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags)
void transport_generic_request_failure(struct se_cmd *cmd,
sense_reason_t sense_reason)
{
- int ret = 0;
+ int ret = 0, post_ret = 0;
pr_debug("-----[ Storage Engine Exception for cmd: %p ITT: 0x%08x"
" CDB: 0x%02x\n", cmd, cmd->se_tfo->get_task_tag(cmd),
@@ -1661,7 +1661,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
*/
if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
cmd->transport_complete_callback)
- cmd->transport_complete_callback(cmd, false);
+ cmd->transport_complete_callback(cmd, false, &post_ret);
switch (sense_reason) {
case TCM_NON_EXISTENT_LUN:
@@ -2056,11 +2056,13 @@ static void target_complete_ok_work(struct work_struct *work)
*/
if (cmd->transport_complete_callback) {
sense_reason_t rc;
+ bool caw = (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE);
+ bool zero_dl = !(cmd->data_length);
+ int post_ret = 0;
- rc = cmd->transport_complete_callback(cmd, true);
- if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) {
- if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
- !cmd->data_length)
+ rc = cmd->transport_complete_callback(cmd, true, &post_ret);
+ if (!rc && !post_ret) {
+ if (caw && zero_dl)
goto queue_rsp;
return;
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 480e9f8..b7a0594 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -522,7 +522,7 @@ struct se_cmd {
sense_reason_t (*execute_cmd)(struct se_cmd *);
sense_reason_t (*execute_rw)(struct se_cmd *, struct scatterlist *,
u32, enum dma_data_direction);
- sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool);
+ sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool, int *);
unsigned char *t_task_cdb;
unsigned char __t_task_cdb[TCM_MAX_COMMAND_SIZE];
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH-v4.1.y 3/8] target: fix COMPARE_AND_WRITE non zero SGL offset data corruption
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 1/8] iscsi-target: Fix rx_login_comp hang after login failure Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 2/8] target: Fix race for SCF_COMPARE_AND_WRITE_POST checking Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 4/8] target: Fix LUN_RESET active I/O handling for ACK_KREF Nicholas A. Bellinger
` (5 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable; +Cc: Sasha Levin, Jan Engelhardt
From: Jan Engelhardt <jengelh@inai.de>
[ Upstream commit d94e5a61357a04938ce14d6033b4d33a3c5fd780 ]
target_core_sbc's compare_and_write functionality suffers from taking
data at the wrong memory location when writing a CAW request to disk
when a SGL offset is non-zero.
This can happen with loopback and vhost-scsi fabric drivers when
SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is used to map existing user-space
SGL memory into COMPARE_AND_WRITE READ/WRITE payload buffers.
Given the following sample LIO subtopology,
% targetcli ls /loopback/
o- loopback ................................. [1 Target]
o- naa.6001405ebb8df14a ....... [naa.60014059143ed2b3]
o- luns ................................... [2 LUNs]
o- lun0 ................ [iblock/ram0 (/dev/ram0)]
o- lun1 ................ [iblock/ram1 (/dev/ram1)]
% lsscsi -g
[3:0:1:0] disk LIO-ORG IBLOCK 4.0 /dev/sdc /dev/sg3
[3:0:1:1] disk LIO-ORG IBLOCK 4.0 /dev/sdd /dev/sg4
the following bug can be observed in Linux 4.3 and 4.4~rc1:
% perl -e 'print chr$_ for 0..255,reverse 0..255' >rand
% perl -e 'print "\0" x 512' >zero
% cat rand >/dev/sdd
% sg_compare_and_write -i rand -D zero --lba 0 /dev/sdd
% sg_compare_and_write -i zero -D rand --lba 0 /dev/sdd
Miscompare reported
% hexdump -Cn 512 /dev/sdd
00000000 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
00000200
Rather than writing all-zeroes as instructed with the -D file, it
corrupts the data in the sector by splicing some of the original
bytes in. The page of the first entry of cmd->t_data_sg includes the
CDB, and sg->offset is set to a position past the CDB. I presume that
sg->offset is also the right choice to use for subsequent sglist
members.
Signed-off-by: Jan Engelhardt <jengelh@netitwork.de>
Tested-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/target_core_sbc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index a3bfafc..46b966d 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -509,11 +509,11 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
if (block_size < PAGE_SIZE) {
sg_set_page(&write_sg[i], m.page, block_size,
- block_size);
+ m.piter.sg->offset + block_size);
} else {
sg_miter_next(&m);
sg_set_page(&write_sg[i], m.page, block_size,
- 0);
+ m.piter.sg->offset);
}
len -= block_size;
i++;
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH-v4.1.y 4/8] target: Fix LUN_RESET active I/O handling for ACK_KREF
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
` (2 preceding siblings ...)
2016-03-06 1:24 ` [PATCH-v4.1.y 3/8] target: fix COMPARE_AND_WRITE non zero SGL offset data corruption Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 5/8] target: Fix TAS handling for multi-session se_node_acls Nicholas A. Bellinger
` (4 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable
Cc: Sasha Levin, Nicholas Bellinger, Himanshu Madhani, Sagi Grimberg,
Christoph Hellwig, Hannes Reinecke, Andy Grover, Mike Christie
From: Nicholas Bellinger <nab@linux-iscsi.org>
commit febe562c20dfa8f33bee7d419c6b517986a5aa33 upstream.
This patch fixes a NULL pointer se_cmd->cmd_kref < 0
refcount bug during TMR LUN_RESET with active se_cmd
I/O, that can be triggered during se_cmd descriptor
shutdown + release via core_tmr_drain_state_list() code.
To address this bug, add common __target_check_io_state()
helper for ABORT_TASK + LUN_RESET w/ CMD_T_COMPLETE
checking, and set CMD_T_ABORTED + obtain ->cmd_kref for
both cases ahead of last target_put_sess_cmd() after
TFO->aborted_task() -> transport_cmd_finish_abort()
callback has completed.
It also introduces SCF_ACK_KREF to determine when
transport_cmd_finish_abort() needs to drop the second
extra reference, ahead of calling target_put_sess_cmd()
for the final kref_put(&se_cmd->cmd_kref).
It also updates transport_cmd_check_stop() to avoid
holding se_cmd->t_state_lock while dropping se_cmd
device state via target_remove_from_state_list(), now
that core_tmr_drain_state_list() is holding the
se_device lock while checking se_cmd state from
within TMR logic.
Finally, move transport_put_cmd() release of SGL +
TMR + extended CDB memory into target_free_cmd_mem()
in order to avoid potential resource leaks in TMR
ABORT_TASK + LUN_RESET code-paths. Also update
target_release_cmd_kref() accordingly.
Reviewed-by: Quinn Tran <quinn.tran@qlogic.com>
Cc: Himanshu Madhani <himanshu.madhani@qlogic.com>
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Andy Grover <agrover@redhat.com>
Cc: Mike Christie <mchristi@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/target_core_tmr.c | 64 +++++++++++++++++++++++---------
drivers/target/target_core_transport.c | 67 +++++++++++++++-------------------
include/target/target_core_base.h | 1 +
3 files changed, 77 insertions(+), 55 deletions(-)
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index adb8016..103c029 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -110,6 +110,34 @@ static int target_check_cdb_and_preempt(struct list_head *list,
return 1;
}
+static bool __target_check_io_state(struct se_cmd *se_cmd)
+{
+ struct se_session *sess = se_cmd->se_sess;
+
+ assert_spin_locked(&sess->sess_cmd_lock);
+ WARN_ON_ONCE(!irqs_disabled());
+ /*
+ * If command already reached CMD_T_COMPLETE state within
+ * target_complete_cmd(), this se_cmd has been passed to
+ * fabric driver and will not be aborted.
+ *
+ * Otherwise, obtain a local se_cmd->cmd_kref now for TMR
+ * ABORT_TASK + LUN_RESET for CMD_T_ABORTED processing as
+ * long as se_cmd->cmd_kref is still active unless zero.
+ */
+ spin_lock(&se_cmd->t_state_lock);
+ if (se_cmd->transport_state & CMD_T_COMPLETE) {
+ pr_debug("Attempted to abort io tag: %u already complete,"
+ " skipping\n", se_cmd->se_tfo->get_task_tag(se_cmd));
+ spin_unlock(&se_cmd->t_state_lock);
+ return false;
+ }
+ se_cmd->transport_state |= CMD_T_ABORTED;
+ spin_unlock(&se_cmd->t_state_lock);
+
+ return kref_get_unless_zero(&se_cmd->cmd_kref);
+}
+
void core_tmr_abort_task(
struct se_device *dev,
struct se_tmr_req *tmr,
@@ -136,25 +164,20 @@ void core_tmr_abort_task(
printk("ABORT_TASK: Found referenced %s task_tag: %u\n",
se_cmd->se_tfo->get_fabric_name(), ref_tag);
- spin_lock(&se_cmd->t_state_lock);
- if (se_cmd->transport_state & CMD_T_COMPLETE) {
- printk("ABORT_TASK: ref_tag: %u already complete, skipping\n", ref_tag);
- spin_unlock(&se_cmd->t_state_lock);
+ if (!__target_check_io_state(se_cmd)) {
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+ target_put_sess_cmd(se_cmd);
goto out;
}
- se_cmd->transport_state |= CMD_T_ABORTED;
- spin_unlock(&se_cmd->t_state_lock);
list_del_init(&se_cmd->se_cmd_list);
- kref_get(&se_cmd->cmd_kref);
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
cancel_work_sync(&se_cmd->work);
transport_wait_for_tasks(se_cmd);
- target_put_sess_cmd(se_cmd);
transport_cmd_finish_abort(se_cmd, true);
+ target_put_sess_cmd(se_cmd);
printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for"
" ref_tag: %d\n", ref_tag);
@@ -259,8 +282,10 @@ static void core_tmr_drain_state_list(
struct list_head *preempt_and_abort_list)
{
LIST_HEAD(drain_task_list);
+ struct se_session *sess;
struct se_cmd *cmd, *next;
unsigned long flags;
+ int rc;
/*
* Complete outstanding commands with TASK_ABORTED SAM status.
@@ -299,6 +324,16 @@ static void core_tmr_drain_state_list(
if (prout_cmd == cmd)
continue;
+ sess = cmd->se_sess;
+ if (WARN_ON_ONCE(!sess))
+ continue;
+
+ spin_lock(&sess->sess_cmd_lock);
+ rc = __target_check_io_state(cmd);
+ spin_unlock(&sess->sess_cmd_lock);
+ if (!rc)
+ continue;
+
list_move_tail(&cmd->state_list, &drain_task_list);
cmd->state_active = false;
}
@@ -306,7 +341,7 @@ static void core_tmr_drain_state_list(
while (!list_empty(&drain_task_list)) {
cmd = list_entry(drain_task_list.next, struct se_cmd, state_list);
- list_del(&cmd->state_list);
+ list_del_init(&cmd->state_list);
pr_debug("LUN_RESET: %s cmd: %p"
" ITT/CmdSN: 0x%08x/0x%08x, i_state: %d, t_state: %d"
@@ -330,16 +365,11 @@ static void core_tmr_drain_state_list(
* loop above, but we do it down here given that
* cancel_work_sync may block.
*/
- if (cmd->t_state == TRANSPORT_COMPLETE)
- cancel_work_sync(&cmd->work);
-
- spin_lock_irqsave(&cmd->t_state_lock, flags);
- target_stop_cmd(cmd, &flags);
-
- cmd->transport_state |= CMD_T_ABORTED;
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ cancel_work_sync(&cmd->work);
+ transport_wait_for_tasks(cmd);
core_tmr_handle_tas_abort(tmr_nacl, cmd, tas);
+ target_put_sess_cmd(cmd);
}
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 316de3f..458ecbd 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -555,9 +555,6 @@ void transport_deregister_session(struct se_session *se_sess)
}
EXPORT_SYMBOL(transport_deregister_session);
-/*
- * Called with cmd->t_state_lock held.
- */
static void target_remove_from_state_list(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
@@ -582,10 +579,6 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
{
unsigned long flags;
- spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (write_pending)
- cmd->t_state = TRANSPORT_WRITE_PENDING;
-
if (remove_from_lists) {
target_remove_from_state_list(cmd);
@@ -595,6 +588,10 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
cmd->se_lun = NULL;
}
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ if (write_pending)
+ cmd->t_state = TRANSPORT_WRITE_PENDING;
+
/*
* Determine if frontend context caller is requesting the stopping of
* this command for frontend exceptions.
@@ -649,6 +646,8 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
{
+ bool ack_kref = (cmd->se_cmd_flags & SCF_ACK_KREF);
+
if (cmd->se_cmd_flags & SCF_SE_LUN_CMD)
transport_lun_remove_cmd(cmd);
/*
@@ -660,7 +659,7 @@ void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
if (transport_cmd_check_stop_to_fabric(cmd))
return;
- if (remove)
+ if (remove && ack_kref)
transport_put_cmd(cmd);
}
@@ -728,7 +727,7 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
* Check for case where an explicit ABORT_TASK has been received
* and transport_wait_for_tasks() will be waiting for completion..
*/
- if (cmd->transport_state & CMD_T_ABORTED &&
+ if (cmd->transport_state & CMD_T_ABORTED ||
cmd->transport_state & CMD_T_STOP) {
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
complete_all(&cmd->t_transport_stop_comp);
@@ -2211,20 +2210,14 @@ static inline void transport_free_pages(struct se_cmd *cmd)
}
/**
- * transport_release_cmd - free a command
- * @cmd: command to free
+ * transport_put_cmd - release a reference to a command
+ * @cmd: command to release
*
- * This routine unconditionally frees a command, and reference counting
- * or list removal must be done in the caller.
+ * This routine releases our reference to the command and frees it if possible.
*/
-static int transport_release_cmd(struct se_cmd *cmd)
+static int transport_put_cmd(struct se_cmd *cmd)
{
BUG_ON(!cmd->se_tfo);
-
- if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
- core_tmr_release_req(cmd->se_tmr_req);
- if (cmd->t_task_cdb != cmd->__t_task_cdb)
- kfree(cmd->t_task_cdb);
/*
* If this cmd has been setup with target_get_sess_cmd(), drop
* the kref and call ->release_cmd() in kref callback.
@@ -2232,18 +2225,6 @@ static int transport_release_cmd(struct se_cmd *cmd)
return target_put_sess_cmd(cmd);
}
-/**
- * transport_put_cmd - release a reference to a command
- * @cmd: command to release
- *
- * This routine releases our reference to the command and frees it if possible.
- */
-static int transport_put_cmd(struct se_cmd *cmd)
-{
- transport_free_pages(cmd);
- return transport_release_cmd(cmd);
-}
-
void *transport_kmap_data_sg(struct se_cmd *cmd)
{
struct scatterlist *sg = cmd->t_data_sg;
@@ -2441,14 +2422,13 @@ static void transport_write_pending_qf(struct se_cmd *cmd)
int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
{
- unsigned long flags;
int ret = 0;
if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) {
if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
- transport_wait_for_tasks(cmd);
+ transport_wait_for_tasks(cmd);
- ret = transport_release_cmd(cmd);
+ ret = transport_put_cmd(cmd);
} else {
if (wait_for_tasks)
transport_wait_for_tasks(cmd);
@@ -2457,11 +2437,8 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
* has already added se_cmd to state_list, but fabric has
* failed command before I/O submission.
*/
- if (cmd->state_active) {
- spin_lock_irqsave(&cmd->t_state_lock, flags);
+ if (cmd->state_active)
target_remove_from_state_list(cmd);
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- }
if (cmd->se_lun)
transport_lun_remove_cmd(cmd);
@@ -2506,6 +2483,16 @@ out:
}
EXPORT_SYMBOL(target_get_sess_cmd);
+static void target_free_cmd_mem(struct se_cmd *cmd)
+{
+ transport_free_pages(cmd);
+
+ if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
+ core_tmr_release_req(cmd->se_tmr_req);
+ if (cmd->t_task_cdb != cmd->__t_task_cdb)
+ kfree(cmd->t_task_cdb);
+}
+
static void target_release_cmd_kref(struct kref *kref)
__releases(&se_cmd->se_sess->sess_cmd_lock)
{
@@ -2514,17 +2501,20 @@ static void target_release_cmd_kref(struct kref *kref)
if (list_empty(&se_cmd->se_cmd_list)) {
spin_unlock(&se_sess->sess_cmd_lock);
+ target_free_cmd_mem(se_cmd);
se_cmd->se_tfo->release_cmd(se_cmd);
return;
}
if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
spin_unlock(&se_sess->sess_cmd_lock);
+ target_free_cmd_mem(se_cmd);
complete(&se_cmd->cmd_wait_comp);
return;
}
list_del(&se_cmd->se_cmd_list);
spin_unlock(&se_sess->sess_cmd_lock);
+ target_free_cmd_mem(se_cmd);
se_cmd->se_tfo->release_cmd(se_cmd);
}
@@ -2536,6 +2526,7 @@ int target_put_sess_cmd(struct se_cmd *se_cmd)
struct se_session *se_sess = se_cmd->se_sess;
if (!se_sess) {
+ target_free_cmd_mem(se_cmd);
se_cmd->se_tfo->release_cmd(se_cmd);
return 1;
}
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index b7a0594..f8a1b85 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -167,6 +167,7 @@ enum se_cmd_flags_table {
SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC = 0x00020000,
SCF_COMPARE_AND_WRITE = 0x00080000,
SCF_COMPARE_AND_WRITE_POST = 0x00100000,
+ SCF_ACK_KREF = 0x00400000,
};
/* struct se_dev_entry->lun_flags and struct se_lun->lun_access */
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH-v4.1.y 5/8] target: Fix TAS handling for multi-session se_node_acls
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
` (3 preceding siblings ...)
2016-03-06 1:24 ` [PATCH-v4.1.y 4/8] target: Fix LUN_RESET active I/O handling for ACK_KREF Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 6/8] target: Fix remote-port TMR ABORT + se_cmd fabric stop Nicholas A. Bellinger
` (3 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable
Cc: Sasha Levin, Nicholas Bellinger, Quinn Tran, Himanshu Madhani,
Sagi Grimberg, Hannes Reinecke, Andy Grover, Mike Christie
From: Nicholas Bellinger <nab@linux-iscsi.org>
commit ebde1ca5a908b10312db4ecd7553e3ba039319ab upstream.
This patch fixes a bug in TMR task aborted status (TAS)
handling when multiple sessions are connected to the
same target WWPN endpoint and se_node_acl descriptor,
resulting in TASK_ABORTED status to not be generated
for aborted se_cmds on the remote port.
This is due to core_tmr_handle_tas_abort() incorrectly
comparing se_node_acl instead of se_session, for which
the multi-session case is expected to be sharing the
same se_node_acl.
Instead, go ahead and update core_tmr_handle_tas_abort()
to compare tmr_sess + cmd->se_sess in order to determine
if the LUN_RESET was received on a different I_T nexus,
and TASK_ABORTED status response needs to be generated.
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Quinn Tran <quinn.tran@qlogic.com>
Cc: Himanshu Madhani <himanshu.madhani@qlogic.com>
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Andy Grover <agrover@redhat.com>
Cc: Mike Christie <mchristi@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/target_core_tmr.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 103c029..a7e15b3 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -79,7 +79,7 @@ void core_tmr_release_req(struct se_tmr_req *tmr)
}
static void core_tmr_handle_tas_abort(
- struct se_node_acl *tmr_nacl,
+ struct se_session *tmr_sess,
struct se_cmd *cmd,
int tas)
{
@@ -87,7 +87,7 @@ static void core_tmr_handle_tas_abort(
/*
* TASK ABORTED status (TAS) bit support
*/
- if ((tmr_nacl && (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) {
+ if (tmr_sess && tmr_sess != cmd->se_sess && tas) {
remove = false;
transport_send_task_abort(cmd);
}
@@ -277,7 +277,7 @@ static void core_tmr_drain_tmr_list(
static void core_tmr_drain_state_list(
struct se_device *dev,
struct se_cmd *prout_cmd,
- struct se_node_acl *tmr_nacl,
+ struct se_session *tmr_sess,
int tas,
struct list_head *preempt_and_abort_list)
{
@@ -368,7 +368,7 @@ static void core_tmr_drain_state_list(
cancel_work_sync(&cmd->work);
transport_wait_for_tasks(cmd);
- core_tmr_handle_tas_abort(tmr_nacl, cmd, tas);
+ core_tmr_handle_tas_abort(tmr_sess, cmd, tas);
target_put_sess_cmd(cmd);
}
}
@@ -381,6 +381,7 @@ int core_tmr_lun_reset(
{
struct se_node_acl *tmr_nacl = NULL;
struct se_portal_group *tmr_tpg = NULL;
+ struct se_session *tmr_sess = NULL;
int tas;
/*
* TASK_ABORTED status bit, this is configurable via ConfigFS
@@ -399,8 +400,9 @@ int core_tmr_lun_reset(
* or struct se_device passthrough..
*/
if (tmr && tmr->task_cmd && tmr->task_cmd->se_sess) {
- tmr_nacl = tmr->task_cmd->se_sess->se_node_acl;
- tmr_tpg = tmr->task_cmd->se_sess->se_tpg;
+ tmr_sess = tmr->task_cmd->se_sess;
+ tmr_nacl = tmr_sess->se_node_acl;
+ tmr_tpg = tmr_sess->se_tpg;
if (tmr_nacl && tmr_tpg) {
pr_debug("LUN_RESET: TMR caller fabric: %s"
" initiator port %s\n",
@@ -413,7 +415,7 @@ int core_tmr_lun_reset(
dev->transport->name, tas);
core_tmr_drain_tmr_list(dev, tmr, preempt_and_abort_list);
- core_tmr_drain_state_list(dev, prout_cmd, tmr_nacl, tas,
+ core_tmr_drain_state_list(dev, prout_cmd, tmr_sess, tas,
preempt_and_abort_list);
/*
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH-v4.1.y 6/8] target: Fix remote-port TMR ABORT + se_cmd fabric stop
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
` (4 preceding siblings ...)
2016-03-06 1:24 ` [PATCH-v4.1.y 5/8] target: Fix TAS handling for multi-session se_node_acls Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 7/8] target: Fix race with SCF_SEND_DELAYED_TAS handling Nicholas A. Bellinger
` (2 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable
Cc: Sasha Levin, Nicholas Bellinger, Quinn Tran, Himanshu Madhani,
Sagi Grimberg, Christoph Hellwig, Hannes Reinecke, Andy Grover,
Mike Christie
From: Nicholas Bellinger <nab@linux-iscsi.org>
commit 0f4a943168f31d29a1701908931acaba518b131a upstream.
To address the bug where fabric driver level shutdown
of se_cmd occurs at the same time when TMR CMD_T_ABORTED
is happening resulting in a -1 ->cmd_kref, this patch
adds a CMD_T_FABRIC_STOP bit that is used to determine
when TMR + driver I_T nexus shutdown is happening
concurrently.
It changes target_sess_cmd_list_set_waiting() to obtain
se_cmd->cmd_kref + set CMD_T_FABRIC_STOP, and drop local
reference in target_wait_for_sess_cmds() and invoke extra
target_put_sess_cmd() during Task Aborted Status (TAS)
when necessary.
Also, it adds a new target_wait_free_cmd() wrapper around
transport_wait_for_tasks() for the special case within
transport_generic_free_cmd() to set CMD_T_FABRIC_STOP,
and is now aware of CMD_T_ABORTED + CMD_T_TAS status
bits to know when an extra transport_put_cmd() during
TAS is required.
Note transport_generic_free_cmd() is expected to block on
cmd->cmd_wait_comp in order to follow what iscsi-target
expects during iscsi_conn context se_cmd shutdown.
Cc: Quinn Tran <quinn.tran@qlogic.com>
Cc: Himanshu Madhani <himanshu.madhani@qlogic.com>
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Andy Grover <agrover@redhat.com>
Cc: Mike Christie <mchristi@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@daterainc.com>
---
drivers/target/target_core_tmr.c | 55 +++++++++----
drivers/target/target_core_transport.c | 137 +++++++++++++++++++++++++--------
include/target/target_core_base.h | 2 +
3 files changed, 147 insertions(+), 47 deletions(-)
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index a7e15b3..ad48837 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -78,16 +78,18 @@ void core_tmr_release_req(struct se_tmr_req *tmr)
kfree(tmr);
}
-static void core_tmr_handle_tas_abort(
- struct se_session *tmr_sess,
- struct se_cmd *cmd,
- int tas)
+static void core_tmr_handle_tas_abort(struct se_cmd *cmd, int tas)
{
- bool remove = true;
+ unsigned long flags;
+ bool remove = true, send_tas;
/*
* TASK ABORTED status (TAS) bit support
*/
- if (tmr_sess && tmr_sess != cmd->se_sess && tas) {
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ send_tas = (cmd->transport_state & CMD_T_TAS);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+
+ if (send_tas) {
remove = false;
transport_send_task_abort(cmd);
}
@@ -110,7 +112,8 @@ static int target_check_cdb_and_preempt(struct list_head *list,
return 1;
}
-static bool __target_check_io_state(struct se_cmd *se_cmd)
+static bool __target_check_io_state(struct se_cmd *se_cmd,
+ struct se_session *tmr_sess, int tas)
{
struct se_session *sess = se_cmd->se_sess;
@@ -118,21 +121,33 @@ static bool __target_check_io_state(struct se_cmd *se_cmd)
WARN_ON_ONCE(!irqs_disabled());
/*
* If command already reached CMD_T_COMPLETE state within
- * target_complete_cmd(), this se_cmd has been passed to
- * fabric driver and will not be aborted.
+ * target_complete_cmd() or CMD_T_FABRIC_STOP due to shutdown,
+ * this se_cmd has been passed to fabric driver and will
+ * not be aborted.
*
* Otherwise, obtain a local se_cmd->cmd_kref now for TMR
* ABORT_TASK + LUN_RESET for CMD_T_ABORTED processing as
* long as se_cmd->cmd_kref is still active unless zero.
*/
spin_lock(&se_cmd->t_state_lock);
- if (se_cmd->transport_state & CMD_T_COMPLETE) {
- pr_debug("Attempted to abort io tag: %u already complete,"
+ if (se_cmd->transport_state & (CMD_T_COMPLETE | CMD_T_FABRIC_STOP)) {
+ pr_debug("Attempted to abort io tag: %u already complete or"
+ " fabric stop, skipping\n",
+ se_cmd->se_tfo->get_task_tag(se_cmd));
+ spin_unlock(&se_cmd->t_state_lock);
+ return false;
+ }
+ if (sess->sess_tearing_down || se_cmd->cmd_wait_set) {
+ pr_debug("Attempted to abort io tag: %u already shutdown,"
" skipping\n", se_cmd->se_tfo->get_task_tag(se_cmd));
spin_unlock(&se_cmd->t_state_lock);
return false;
}
se_cmd->transport_state |= CMD_T_ABORTED;
+
+ if ((tmr_sess != se_cmd->se_sess) && tas)
+ se_cmd->transport_state |= CMD_T_TAS;
+
spin_unlock(&se_cmd->t_state_lock);
return kref_get_unless_zero(&se_cmd->cmd_kref);
@@ -164,7 +179,7 @@ void core_tmr_abort_task(
printk("ABORT_TASK: Found referenced %s task_tag: %u\n",
se_cmd->se_tfo->get_fabric_name(), ref_tag);
- if (!__target_check_io_state(se_cmd)) {
+ if (!__target_check_io_state(se_cmd, se_sess, 0)) {
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
target_put_sess_cmd(se_cmd);
goto out;
@@ -234,7 +249,8 @@ static void core_tmr_drain_tmr_list(
spin_lock(&sess->sess_cmd_lock);
spin_lock(&cmd->t_state_lock);
- if (!(cmd->transport_state & CMD_T_ACTIVE)) {
+ if (!(cmd->transport_state & CMD_T_ACTIVE) ||
+ (cmd->transport_state & CMD_T_FABRIC_STOP)) {
spin_unlock(&cmd->t_state_lock);
spin_unlock(&sess->sess_cmd_lock);
continue;
@@ -244,15 +260,22 @@ static void core_tmr_drain_tmr_list(
spin_unlock(&sess->sess_cmd_lock);
continue;
}
+ if (sess->sess_tearing_down || cmd->cmd_wait_set) {
+ spin_unlock(&cmd->t_state_lock);
+ spin_unlock(&sess->sess_cmd_lock);
+ continue;
+ }
cmd->transport_state |= CMD_T_ABORTED;
spin_unlock(&cmd->t_state_lock);
rc = kref_get_unless_zero(&cmd->cmd_kref);
- spin_unlock(&sess->sess_cmd_lock);
if (!rc) {
printk("LUN_RESET TMR: non-zero kref_get_unless_zero\n");
+ spin_unlock(&sess->sess_cmd_lock);
continue;
}
+ spin_unlock(&sess->sess_cmd_lock);
+
list_move_tail(&tmr_p->tmr_list, &drain_tmr_list);
}
spin_unlock_irqrestore(&dev->se_tmr_lock, flags);
@@ -329,7 +352,7 @@ static void core_tmr_drain_state_list(
continue;
spin_lock(&sess->sess_cmd_lock);
- rc = __target_check_io_state(cmd);
+ rc = __target_check_io_state(cmd, tmr_sess, tas);
spin_unlock(&sess->sess_cmd_lock);
if (!rc)
continue;
@@ -368,7 +391,7 @@ static void core_tmr_drain_state_list(
cancel_work_sync(&cmd->work);
transport_wait_for_tasks(cmd);
- core_tmr_handle_tas_abort(tmr_sess, cmd, tas);
+ core_tmr_handle_tas_abort(cmd, tas);
target_put_sess_cmd(cmd);
}
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 458ecbd..43b6ee9 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2420,18 +2420,33 @@ static void transport_write_pending_qf(struct se_cmd *cmd)
}
}
+static bool
+__transport_wait_for_tasks(struct se_cmd *, bool, bool *, bool *,
+ unsigned long *flags);
+
+static void target_wait_free_cmd(struct se_cmd *cmd, bool *aborted, bool *tas)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ __transport_wait_for_tasks(cmd, true, aborted, tas, &flags);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+}
+
int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
{
int ret = 0;
+ bool aborted = false, tas = false;
if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) {
if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
- transport_wait_for_tasks(cmd);
+ target_wait_free_cmd(cmd, &aborted, &tas);
- ret = transport_put_cmd(cmd);
+ if (!aborted || tas)
+ ret = transport_put_cmd(cmd);
} else {
if (wait_for_tasks)
- transport_wait_for_tasks(cmd);
+ target_wait_free_cmd(cmd, &aborted, &tas);
/*
* Handle WRITE failure case where transport_generic_new_cmd()
* has already added se_cmd to state_list, but fabric has
@@ -2443,7 +2458,21 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
if (cmd->se_lun)
transport_lun_remove_cmd(cmd);
- ret = transport_put_cmd(cmd);
+ if (!aborted || tas)
+ ret = transport_put_cmd(cmd);
+ }
+ /*
+ * If the task has been internally aborted due to TMR ABORT_TASK
+ * or LUN_RESET, target_core_tmr.c is responsible for performing
+ * the remaining calls to target_put_sess_cmd(), and not the
+ * callers of this function.
+ */
+ if (aborted) {
+ pr_debug("Detected CMD_T_ABORTED for ITT: %u\n",
+ cmd->se_tfo->get_task_tag(cmd));
+ wait_for_completion(&cmd->cmd_wait_comp);
+ cmd->se_tfo->release_cmd(cmd);
+ ret = 1;
}
return ret;
}
@@ -2498,6 +2527,7 @@ static void target_release_cmd_kref(struct kref *kref)
{
struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
struct se_session *se_sess = se_cmd->se_sess;
+ bool fabric_stop;
if (list_empty(&se_cmd->se_cmd_list)) {
spin_unlock(&se_sess->sess_cmd_lock);
@@ -2505,13 +2535,19 @@ static void target_release_cmd_kref(struct kref *kref)
se_cmd->se_tfo->release_cmd(se_cmd);
return;
}
- if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
+
+ spin_lock(&se_cmd->t_state_lock);
+ fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP);
+ spin_unlock(&se_cmd->t_state_lock);
+
+ if (se_cmd->cmd_wait_set || fabric_stop) {
+ list_del_init(&se_cmd->se_cmd_list);
spin_unlock(&se_sess->sess_cmd_lock);
target_free_cmd_mem(se_cmd);
complete(&se_cmd->cmd_wait_comp);
return;
}
- list_del(&se_cmd->se_cmd_list);
+ list_del_init(&se_cmd->se_cmd_list);
spin_unlock(&se_sess->sess_cmd_lock);
target_free_cmd_mem(se_cmd);
@@ -2544,6 +2580,7 @@ void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
{
struct se_cmd *se_cmd;
unsigned long flags;
+ int rc;
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
if (se_sess->sess_tearing_down) {
@@ -2553,8 +2590,15 @@ void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
se_sess->sess_tearing_down = 1;
list_splice_init(&se_sess->sess_cmd_list, &se_sess->sess_wait_list);
- list_for_each_entry(se_cmd, &se_sess->sess_wait_list, se_cmd_list)
- se_cmd->cmd_wait_set = 1;
+ list_for_each_entry(se_cmd, &se_sess->sess_wait_list, se_cmd_list) {
+ rc = kref_get_unless_zero(&se_cmd->cmd_kref);
+ if (rc) {
+ se_cmd->cmd_wait_set = 1;
+ spin_lock(&se_cmd->t_state_lock);
+ se_cmd->transport_state |= CMD_T_FABRIC_STOP;
+ spin_unlock(&se_cmd->t_state_lock);
+ }
+ }
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
}
@@ -2567,15 +2611,25 @@ void target_wait_for_sess_cmds(struct se_session *se_sess)
{
struct se_cmd *se_cmd, *tmp_cmd;
unsigned long flags;
+ bool tas;
list_for_each_entry_safe(se_cmd, tmp_cmd,
&se_sess->sess_wait_list, se_cmd_list) {
- list_del(&se_cmd->se_cmd_list);
+ list_del_init(&se_cmd->se_cmd_list);
pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:"
" %d\n", se_cmd, se_cmd->t_state,
se_cmd->se_tfo->get_cmd_state(se_cmd));
+ spin_lock_irqsave(&se_cmd->t_state_lock, flags);
+ tas = (se_cmd->transport_state & CMD_T_TAS);
+ spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+
+ if (!target_put_sess_cmd(se_cmd)) {
+ if (tas)
+ target_put_sess_cmd(se_cmd);
+ }
+
wait_for_completion(&se_cmd->cmd_wait_comp);
pr_debug("After cmd_wait_comp: se_cmd: %p t_state: %d"
" fabric state: %d\n", se_cmd, se_cmd->t_state,
@@ -2618,34 +2672,38 @@ int transport_clear_lun_ref(struct se_lun *lun)
return 0;
}
-/**
- * transport_wait_for_tasks - wait for completion to occur
- * @cmd: command to wait
- *
- * Called from frontend fabric context to wait for storage engine
- * to pause and/or release frontend generated struct se_cmd.
- */
-bool transport_wait_for_tasks(struct se_cmd *cmd)
+static bool
+__transport_wait_for_tasks(struct se_cmd *cmd, bool fabric_stop,
+ bool *aborted, bool *tas, unsigned long *flags)
+ __releases(&cmd->t_state_lock)
+ __acquires(&cmd->t_state_lock)
{
- unsigned long flags;
- spin_lock_irqsave(&cmd->t_state_lock, flags);
+ assert_spin_locked(&cmd->t_state_lock);
+ WARN_ON_ONCE(!irqs_disabled());
+
+ if (fabric_stop)
+ cmd->transport_state |= CMD_T_FABRIC_STOP;
+
+ if (cmd->transport_state & CMD_T_ABORTED)
+ *aborted = true;
+
+ if (cmd->transport_state & CMD_T_TAS)
+ *tas = true;
+
if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) &&
- !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) {
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
return false;
- }
if (!(cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) &&
- !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) {
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
return false;
- }
- if (!(cmd->transport_state & CMD_T_ACTIVE)) {
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ if (!(cmd->transport_state & CMD_T_ACTIVE))
+ return false;
+
+ if (fabric_stop && *aborted)
return false;
- }
cmd->transport_state |= CMD_T_STOP;
@@ -2654,20 +2712,37 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
cmd, cmd->se_tfo->get_task_tag(cmd),
cmd->se_tfo->get_cmd_state(cmd), cmd->t_state);
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ spin_unlock_irqrestore(&cmd->t_state_lock, *flags);
wait_for_completion(&cmd->t_transport_stop_comp);
- spin_lock_irqsave(&cmd->t_state_lock, flags);
+ spin_lock_irqsave(&cmd->t_state_lock, *flags);
cmd->transport_state &= ~(CMD_T_ACTIVE | CMD_T_STOP);
pr_debug("wait_for_tasks: Stopped wait_for_completion("
"&cmd->t_transport_stop_comp) for ITT: 0x%08x\n",
cmd->se_tfo->get_task_tag(cmd));
+ return true;
+}
+
+/**
+ * transport_wait_for_tasks - wait for completion to occur
+ * @cmd: command to wait
+ *
+ * Called from frontend fabric context to wait for storage engine
+ * to pause and/or release frontend generated struct se_cmd.
+ */
+bool transport_wait_for_tasks(struct se_cmd *cmd)
+{
+ unsigned long flags;
+ bool ret, aborted = false, tas = false;
+
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ ret = __transport_wait_for_tasks(cmd, false, &aborted, &tas, &flags);
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- return true;
+ return ret;
}
EXPORT_SYMBOL(transport_wait_for_tasks);
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index f8a1b85..2b40a1f 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -538,6 +538,8 @@ struct se_cmd {
#define CMD_T_DEV_ACTIVE (1 << 7)
#define CMD_T_REQUEST_STOP (1 << 8)
#define CMD_T_BUSY (1 << 9)
+#define CMD_T_TAS (1 << 10)
+#define CMD_T_FABRIC_STOP (1 << 11)
spinlock_t t_state_lock;
struct completion t_transport_stop_comp;
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH-v4.1.y 7/8] target: Fix race with SCF_SEND_DELAYED_TAS handling
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
` (5 preceding siblings ...)
2016-03-06 1:24 ` [PATCH-v4.1.y 6/8] target: Fix remote-port TMR ABORT + se_cmd fabric stop Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 1:24 ` [PATCH-v4.1.y 8/8] target: Fix linux-4.1.y specific compile warning Nicholas A. Bellinger
2016-03-06 19:17 ` [PATCH-v4.1.y 0/8] target: stable backports Sasha Levin
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable
Cc: Sasha Levin, Nicholas Bellinger, Quinn Tran, Himanshu Madhani,
Sagi Grimberg, Christoph Hellwig, Hannes Reinecke, Andy Grover,
Mike Christie
From: Nicholas Bellinger <nab@linux-iscsi.org>
commit 310d3d314be7f0a84011ebdc4bdccbcae9755a87 upstream.
This patch fixes a race between setting of SCF_SEND_DELAYED_TAS
in transport_send_task_abort(), and check of the same bit in
transport_check_aborted_status().
It adds a __transport_check_aborted_status() version that is
used by target_execute_cmd() when se_cmd->t_state_lock is
held, and a transport_check_aborted_status() wrapper for
all other existing callers.
Also, it handles the case where the check happens before
transport_send_task_abort() gets called. For this, go
ahead and set SCF_SEND_DELAYED_TAS early when necessary,
and have transport_send_task_abort() send the abort.
Cc: Quinn Tran <quinn.tran@qlogic.com>
Cc: Himanshu Madhani <himanshu.madhani@qlogic.com>
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Andy Grover <agrover@redhat.com>
Cc: Mike Christie <mchristi@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/target_core_transport.c | 53 +++++++++++++++++++++++++++-------
1 file changed, 42 insertions(+), 11 deletions(-)
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 43b6ee9..be12b9d 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1835,19 +1835,21 @@ static bool target_handle_task_attr(struct se_cmd *cmd)
return true;
}
+static int __transport_check_aborted_status(struct se_cmd *, int);
+
void target_execute_cmd(struct se_cmd *cmd)
{
/*
- * If the received CDB has aleady been aborted stop processing it here.
- */
- if (transport_check_aborted_status(cmd, 1))
- return;
-
- /*
* Determine if frontend context caller is requesting the stopping of
* this command for frontend exceptions.
+ *
+ * If the received CDB has aleady been aborted stop processing it here.
*/
spin_lock_irq(&cmd->t_state_lock);
+ if (__transport_check_aborted_status(cmd, 1)) {
+ spin_unlock_irq(&cmd->t_state_lock);
+ return;
+ }
if (cmd->transport_state & CMD_T_STOP) {
pr_debug("%s:%d CMD_T_STOP for ITT: 0x%08x\n",
__func__, __LINE__,
@@ -3028,8 +3030,13 @@ after_reason:
}
EXPORT_SYMBOL(transport_send_check_condition_and_sense);
-int transport_check_aborted_status(struct se_cmd *cmd, int send_status)
+static int __transport_check_aborted_status(struct se_cmd *cmd, int send_status)
+ __releases(&cmd->t_state_lock)
+ __acquires(&cmd->t_state_lock)
{
+ assert_spin_locked(&cmd->t_state_lock);
+ WARN_ON_ONCE(!irqs_disabled());
+
if (!(cmd->transport_state & CMD_T_ABORTED))
return 0;
@@ -3037,19 +3044,37 @@ int transport_check_aborted_status(struct se_cmd *cmd, int send_status)
* If cmd has been aborted but either no status is to be sent or it has
* already been sent, just return
*/
- if (!send_status || !(cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS))
+ if (!send_status || !(cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS)) {
+ if (send_status)
+ cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS;
return 1;
+ }
- pr_debug("Sending delayed SAM_STAT_TASK_ABORTED status for CDB: 0x%02x ITT: 0x%08x\n",
- cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd));
+ pr_debug("Sending delayed SAM_STAT_TASK_ABORTED status for CDB:"
+ " 0x%02x ITT: 0x%08x\n", cmd->t_task_cdb[0],
+ cmd->se_tfo->get_task_tag(cmd));
cmd->se_cmd_flags &= ~SCF_SEND_DELAYED_TAS;
cmd->scsi_status = SAM_STAT_TASK_ABORTED;
trace_target_cmd_complete(cmd);
+
+ spin_unlock_irq(&cmd->t_state_lock);
cmd->se_tfo->queue_status(cmd);
+ spin_lock_irq(&cmd->t_state_lock);
return 1;
}
+
+int transport_check_aborted_status(struct se_cmd *cmd, int send_status)
+{
+ int ret;
+
+ spin_lock_irq(&cmd->t_state_lock);
+ ret = __transport_check_aborted_status(cmd, send_status);
+ spin_unlock_irq(&cmd->t_state_lock);
+
+ return ret;
+}
EXPORT_SYMBOL(transport_check_aborted_status);
void transport_send_task_abort(struct se_cmd *cmd)
@@ -3071,11 +3096,17 @@ void transport_send_task_abort(struct se_cmd *cmd)
*/
if (cmd->data_direction == DMA_TO_DEVICE) {
if (cmd->se_tfo->write_pending_status(cmd) != 0) {
- cmd->transport_state |= CMD_T_ABORTED;
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ if (cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ goto send_abort;
+ }
cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS;
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return;
}
}
+send_abort:
cmd->scsi_status = SAM_STAT_TASK_ABORTED;
transport_lun_remove_cmd(cmd);
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH-v4.1.y 8/8] target: Fix linux-4.1.y specific compile warning
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
` (6 preceding siblings ...)
2016-03-06 1:24 ` [PATCH-v4.1.y 7/8] target: Fix race with SCF_SEND_DELAYED_TAS handling Nicholas A. Bellinger
@ 2016-03-06 1:24 ` Nicholas A. Bellinger
2016-03-06 19:17 ` [PATCH-v4.1.y 0/8] target: stable backports Sasha Levin
8 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-06 1:24 UTC (permalink / raw)
To: target-devel, stable; +Cc: Sasha Levin, Nicholas Bellinger
From: Nicholas Bellinger <nab@linux-iscsi.org>
The linux-4.1.y specific patch to fix a previous v4.1 UNIT_ATTENTION
read-copy-update conversion regression:
commit 35afa65642a9a88c81913377b93a3a66220f8b9d
Author: Nicholas Bellinger <nab@linux-iscsi.org>
Date: Wed Sep 23 07:49:26 2015 +0000
target: Fix v4.1 UNIT_ATTENTION se_node_acl->device_list[] NULL pointer
introduced the following compile warning:
drivers/target/target_core_pr.c: In function ‘core_scsi3_pr_seq_non_holder’:
drivers/target/target_core_pr.c:332:3: warning: ‘return’ with no value, in function returning non-void [-Wreturn-type]
Go ahead and fix this up to always returning zero when no ACL
device list exists within core_scsi3_pr_seq_non_holder().
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/target_core_pr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 08aa7cc..57fd4e1 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -329,7 +329,7 @@ static int core_scsi3_pr_seq_non_holder(
* RESERVATION CONFLICT on some CDBs */
if (!se_sess->se_node_acl->device_list)
- return;
+ return 0;
se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
--
1.8.5.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH-v4.1.y 0/8] target: stable backports
2016-03-06 1:24 [PATCH-v4.1.y 0/8] target: stable backports Nicholas A. Bellinger
` (7 preceding siblings ...)
2016-03-06 1:24 ` [PATCH-v4.1.y 8/8] target: Fix linux-4.1.y specific compile warning Nicholas A. Bellinger
@ 2016-03-06 19:17 ` Sasha Levin
2016-03-07 2:32 ` Nicholas A. Bellinger
2016-03-07 20:43 ` Greg Kroah-Hartman
8 siblings, 2 replies; 12+ messages in thread
From: Sasha Levin @ 2016-03-06 19:17 UTC (permalink / raw)
To: Nicholas A. Bellinger, target-devel, stable, Greg Kroah-Hartman
On 03/05/2016 08:24 PM, Nicholas A. Bellinger wrote:
> From: Nicholas Bellinger <nab@linux-iscsi.org>
>
> Hi Sasha,
>
> The following series contains v4.1.y stable backports for a number
> of target patches from upstream code, that don't appear in your
> stable tree.
>
> Also included is a small complile fix specific to v4.1.y code.
>
> Please apply at your earliest convience.
Thanks Nicholas, I've added both series to their corresponding trees.
> Btw, I didn't seen any 'failed to apply' messages from you for
> any of these patches, which is how I tell from Greg-KH when
> something needs to be manually backported.
>
> Is there a reason for not sending out 'failed to apply' emails..?
Good point. Since I was only maintaining 3.18 so far, I'd wait for
maintainers to respond to Greg's mails about failure to apply on either
4.1 or 3.14, and just take their backport from there - which did the trick,
and also reduced the amount of mails maintainers get regarding -stable trees.
I don't have a problem with starting to send "F-T-A" mails as well, but I
wonder if we can somehow coordinate these mails between myself and Greg
so we won't send one for each and every kernel version, but rather one
specifying which stable versions failed to apply? Greg, What are your
thoughts about this?
Thanks,
Sasha
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH-v4.1.y 0/8] target: stable backports
2016-03-06 19:17 ` [PATCH-v4.1.y 0/8] target: stable backports Sasha Levin
@ 2016-03-07 2:32 ` Nicholas A. Bellinger
2016-03-07 20:43 ` Greg Kroah-Hartman
1 sibling, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-03-07 2:32 UTC (permalink / raw)
To: Sasha Levin; +Cc: target-devel, stable, Greg Kroah-Hartman
On Sun, 2016-03-06 at 14:17 -0500, Sasha Levin wrote:
> On 03/05/2016 08:24 PM, Nicholas A. Bellinger wrote:
> > From: Nicholas Bellinger <nab@linux-iscsi.org>
> >
> > Hi Sasha,
> >
> > The following series contains v4.1.y stable backports for a number
> > of target patches from upstream code, that don't appear in your
> > stable tree.
> >
> > Also included is a small complile fix specific to v4.1.y code.
> >
> > Please apply at your earliest convience.
>
> Thanks Nicholas, I've added both series to their corresponding trees.
>
> > Btw, I didn't seen any 'failed to apply' messages from you for
> > any of these patches, which is how I tell from Greg-KH when
> > something needs to be manually backported.
> >
> > Is there a reason for not sending out 'failed to apply' emails..?
>
> Good point. Since I was only maintaining 3.18 so far, I'd wait for
> maintainers to respond to Greg's mails about failure to apply on either
> 4.1 or 3.14, and just take their backport from there - which did the trick,
> and also reduced the amount of mails maintainers get regarding -stable trees.
>
Sure, thanks for the extra background.
>
> I don't have a problem with starting to send "F-T-A" mails as well, but I
> wonder if we can somehow coordinate these mails between myself and Greg
> so we won't send one for each and every kernel version, but rather one
> specifying which stable versions failed to apply?
So the process bug is when patches apply cleanly to Greg's stable trees,
but do not apply to 4.1.y.
Perhaps a F-T-A email only when the patch applied cleanly to Greg's
(oldest) tree, but not 4.1.y or older..?
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH-v4.1.y 0/8] target: stable backports
2016-03-06 19:17 ` [PATCH-v4.1.y 0/8] target: stable backports Sasha Levin
2016-03-07 2:32 ` Nicholas A. Bellinger
@ 2016-03-07 20:43 ` Greg Kroah-Hartman
1 sibling, 0 replies; 12+ messages in thread
From: Greg Kroah-Hartman @ 2016-03-07 20:43 UTC (permalink / raw)
To: Sasha Levin; +Cc: Nicholas A. Bellinger, target-devel, stable
On Sun, Mar 06, 2016 at 02:17:55PM -0500, Sasha Levin wrote:
> On 03/05/2016 08:24 PM, Nicholas A. Bellinger wrote:
> > From: Nicholas Bellinger <nab@linux-iscsi.org>
> >
> > Hi Sasha,
> >
> > The following series contains v4.1.y stable backports for a number
> > of target patches from upstream code, that don't appear in your
> > stable tree.
> >
> > Also included is a small complile fix specific to v4.1.y code.
> >
> > Please apply at your earliest convience.
>
> Thanks Nicholas, I've added both series to their corresponding trees.
>
> > Btw, I didn't seen any 'failed to apply' messages from you for
> > any of these patches, which is how I tell from Greg-KH when
> > something needs to be manually backported.
> >
> > Is there a reason for not sending out 'failed to apply' emails..?
>
> Good point. Since I was only maintaining 3.18 so far, I'd wait for
> maintainers to respond to Greg's mails about failure to apply on either
> 4.1 or 3.14, and just take their backport from there - which did the trick,
> and also reduced the amount of mails maintainers get regarding -stable trees.
>
> I don't have a problem with starting to send "F-T-A" mails as well, but I
> wonder if we can somehow coordinate these mails between myself and Greg
> so we won't send one for each and every kernel version, but rather one
> specifying which stable versions failed to apply? Greg, What are your
> thoughts about this?
I don't know, I don't check any trees other than the ones I care about,
and send those messages out "manually" when I eventually give up on
trying to apply a patch. Sasha, please send them out if you can't get a
patch to apply that is marked to be added to the kernels you are
maintaining, there's no reason for you not to.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 12+ messages in thread