FAILED: patch "[PATCH] scsi: target: tcm_loop: Drain commands in target_reset" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 1333eee56cdf3f0cf67c6ab4114c2c9e0a952026
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026033011-baffle-superior-ddf5@gregkh' --subject-prefix 'PATCH 6.12.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 1333eee56cdf3f0cf67c6ab4114c2c9e0a952026 Mon Sep 17 00:00:00 2001
From: Josef Bacik <[email protected]>
Date: Mon, 16 Mar 2026 20:23:29 -0400
Subject: [PATCH] scsi: target: tcm_loop: Drain commands in target_reset
handler
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
tcm_loop_target_reset() violates the SCSI EH contract: it returns SUCCESS without draining any in-flight commands. The SCSI EH documentation (scsi_eh.rst) requires that when a reset handler returns SUCCESS the driver has made lower layers "forget about timed out scmds" and is ready for new commands. Every other SCSI LLD (virtio_scsi, mpt3sas, ipr, scsi_debug, mpi3mr) enforces this by draining or completing outstanding commands before returning SUCCESS.
Because tcm_loop_target_reset() doesn't drain, the SCSI EH reuses in-flight scsi_cmnd structures for recovery commands (e.g. TUR) while the target core still has async completion work queued for the old se_cmd. The memset in queuecommand zeroes se_lun and lun_ref_active, causing transport_lun_remove_cmd() to skip its percpu_ref_put(). The leaked LUN reference prevents transport_clear_lun_ref() from completing, hanging configfs LUN unlink forever in D-state:
INFO: task rm:264 blocked for more than 122 seconds. rm D 0 264 258 0x00004000 Call Trace: __schedule+0x3d0/0x8e0 schedule+0x36/0xf0 transport_clear_lun_ref+0x78/0x90 [target_core_mod] core_tpg_remove_lun+0x28/0xb0 [target_core_mod] target_fabric_port_unlink+0x50/0x60 [target_core_mod] configfs_unlink+0x156/0x1f0 [configfs] vfs_unlink+0x109/0x290 do_unlinkat+0x1d5/0x2d0
Fix this by making tcm_loop_target_reset() actually drain commands:
1. Issue TMR_LUN_RESET via tcm_loop_issue_tmr() to drain all commands that the target core knows about (those not yet CMD_T_COMPLETE).
2. Use blk_mq_tagset_busy_iter() to iterate all started requests and flush_work() on each se_cmd — this drains any deferred completion work for commands that already had CMD_T_COMPLETE set before the TMR (which the TMR skips via __target_check_io_state()). This is the same pattern used by mpi3mr, scsi_debug, and libsas to drain outstanding commands during reset.
drivers/target/loopback/tcm_loop.c +46 -6
@@ -26,6 +26,7 @@#include <linux/slab.h> #include <linux/types.h> #include <linux/configfs.h> +#include <linux/blk-mq.h> #include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_host.h>
@@ -269,15 +270,27 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)return (ret ==TMR_FUNCTION_COMPLETE ) ?SUCCESS :FAILED ; } +static bool tcm_loop_flush_work_iter (struct request *rq,void *data) +{ +struct scsi_cmnd *sc =blk_mq_rq_to_pdu (rq); +struct tcm_loop_cmd *tl_cmd =scsi_cmd_priv (sc); +struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; + +flush_work (&se_cmd->work); +return true; +} +static int tcm_loop_target_reset (struct scsi_cmnd *sc) {struct tcm_loop_hba *tl_hba;struct tcm_loop_tpg *tl_tpg; +struct Scsi_Host *sh = sc->device->host; +int ret;/* * Locate the tcm_loop_hba_t pointer */ - tl_hba = *(struct tcm_loop_hba **)shost_priv (sc->device->host); + tl_hba = *(struct tcm_loop_hba **)shost_priv (sh);if (!tl_hba) {pr_err ("Unable to perform device reset without active I_T Nexus\n" );return FAILED ;
@@ -286,11 +299,38 @@ static int tcm_loop_target_reset(struct scsi_cmnd *sc)* Locate the tl_tpg pointer from TargetID in sc->device->id */ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; -if (tl_tpg) { - tl_tpg->tl_transport_status =TCM_TRANSPORT_ONLINE ; -return SUCCESS ; - } -return FAILED ; +if (!tl_tpg) +return FAILED ; + +/* +* Issue a LUN_RESET to drain all commands that the target core +* knows about. This handles commands not yet marked CMD_T_COMPLETE. +*/ + ret =tcm_loop_issue_tmr (tl_tpg, sc->device->lun,0 ,TMR_LUN_RESET ); +if (ret !=TMR_FUNCTION_COMPLETE ) +return FAILED ; + +/* +* Flush any deferred target core completion work that may still be +* queued. Commands that already had CMD_T_COMPLETE set before the TMR +* are skipped by the TMR drain, but their async completion work +* (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done) +* may still be pending in target_completion_wq. +* +* The SCSI EH will reuse in-flight scsi_cmnd structures for recovery +* commands (e.g. TUR) immediately after this handler returns SUCCESS — +* if deferred work is still pending, the memset in queuecommand would +* zero the se_cmd while the work accesses it, leaking the LUN +* percpu_ref and hanging configfs unlink forever. +* +* Use blk_mq_tagset_busy_iter() to find all started requests and +* flush_work() on each — the same pattern used by mpi3mr, scsi_debug, +* and other SCSI drivers to drain outstanding commands during reset. +*/ +blk_mq_tagset_busy_iter (&sh->tag_set, tcm_loop_flush_work_iter,NULL ); + + tl_tpg->tl_transport_status =TCM_TRANSPORT_ONLINE ; +return SUCCESS ; }static const struct scsi_host_template tcm_loop_driver_template = {
Sasha Levin
maintainerPATCH 1/1 scsi: target: tcm_loop: Drain commands in target_reset handler
From: Josef Bacik <[email protected]>
[ Upstream commit 1333eee56cdf3f0cf67c6ab4114c2c9e0a952026 ]
tcm_loop_target_reset() violates the SCSI EH contract: it returns SUCCESS without draining any in-flight commands. The SCSI EH documentation (scsi_eh.rst) requires that when a reset handler returns SUCCESS the driver has made lower layers "forget about timed out scmds" and is ready for new commands. Every other SCSI LLD (virtio_scsi, mpt3sas, ipr, scsi_debug, mpi3mr) enforces this by draining or completing outstanding commands before returning SUCCESS.
Because tcm_loop_target_reset() doesn't drain, the SCSI EH reuses in-flight scsi_cmnd structures for recovery commands (e.g. TUR) while the target core still has async completion work queued for the old se_cmd. The memset in queuecommand zeroes se_lun and lun_ref_active, causing transport_lun_remove_cmd() to skip its percpu_ref_put(). The leaked LUN reference prevents transport_clear_lun_ref() from completing, hanging configfs LUN unlink forever in D-state:
INFO: task rm:264 blocked for more than 122 seconds. rm D 0 264 258 0x00004000 Call Trace: __schedule+0x3d0/0x8e0 schedule+0x36/0xf0 transport_clear_lun_ref+0x78/0x90 [target_core_mod] core_tpg_remove_lun+0x28/0xb0 [target_core_mod] target_fabric_port_unlink+0x50/0x60 [target_core_mod] configfs_unlink+0x156/0x1f0 [configfs] vfs_unlink+0x109/0x290 do_unlinkat+0x1d5/0x2d0
Fix this by making tcm_loop_target_reset() actually drain commands:
1. Issue TMR_LUN_RESET via tcm_loop_issue_tmr() to drain all commands that the target core knows about (those not yet CMD_T_COMPLETE).
2. Use blk_mq_tagset_busy_iter() to iterate all started requests and flush_work() on each se_cmd — this drains any deferred completion work for commands that already had CMD_T_COMPLETE set before the TMR (which the TMR skips via __target_check_io_state()). This is the same pattern used by mpi3mr, scsi_debug, and libsas to drain outstanding commands during reset.
--- drivers/target/loopback/tcm_loop.c | 52 ++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-)
drivers/target/loopback/tcm_loop.c +46 -6
@@ -26,6 +26,7 @@#include <linux/slab.h> #include <linux/types.h> #include <linux/configfs.h> +#include <linux/blk-mq.h> #include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_host.h>
@@ -267,15 +268,27 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)return (ret ==TMR_FUNCTION_COMPLETE ) ?SUCCESS :FAILED ; } +static bool tcm_loop_flush_work_iter (struct request *rq,void *data) +{ +struct scsi_cmnd *sc =blk_mq_rq_to_pdu (rq); +struct tcm_loop_cmd *tl_cmd =scsi_cmd_priv (sc); +struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; + +flush_work (&se_cmd->work); +return true; +} +static int tcm_loop_target_reset (struct scsi_cmnd *sc) {struct tcm_loop_hba *tl_hba;struct tcm_loop_tpg *tl_tpg; +struct Scsi_Host *sh = sc->device->host; +int ret;/* * Locate the tcm_loop_hba_t pointer */ - tl_hba = *(struct tcm_loop_hba **)shost_priv (sc->device->host); + tl_hba = *(struct tcm_loop_hba **)shost_priv (sh);if (!tl_hba) {pr_err ("Unable to perform device reset without active I_T Nexus\n" );return FAILED ;
@@ -284,11 +297,38 @@ static int tcm_loop_target_reset(struct scsi_cmnd *sc)* Locate the tl_tpg pointer from TargetID in sc->device->id */ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; -if (tl_tpg) { - tl_tpg->tl_transport_status =TCM_TRANSPORT_ONLINE ; -return SUCCESS ; - } -return FAILED ; +if (!tl_tpg) +return FAILED ; + +/* +* Issue a LUN_RESET to drain all commands that the target core +* knows about. This handles commands not yet marked CMD_T_COMPLETE. +*/ + ret =tcm_loop_issue_tmr (tl_tpg, sc->device->lun,0 ,TMR_LUN_RESET ); +if (ret !=TMR_FUNCTION_COMPLETE ) +return FAILED ; + +/* +* Flush any deferred target core completion work that may still be +* queued. Commands that already had CMD_T_COMPLETE set before the TMR +* are skipped by the TMR drain, but their async completion work +* (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done) +* may still be pending in target_completion_wq. +* +* The SCSI EH will reuse in-flight scsi_cmnd structures for recovery +* commands (e.g. TUR) immediately after this handler returns SUCCESS — +* if deferred work is still pending, the memset in queuecommand would +* zero the se_cmd while the work accesses it, leaking the LUN +* percpu_ref and hanging configfs unlink forever. +* +* Use blk_mq_tagset_busy_iter() to find all started requests and +* flush_work() on each — the same pattern used by mpi3mr, scsi_debug, +* and other SCSI drivers to drain outstanding commands during reset. +*/ +blk_mq_tagset_busy_iter (&sh->tag_set, tcm_loop_flush_work_iter,NULL ); + + tl_tpg->tl_transport_status =TCM_TRANSPORT_ONLINE ; +return SUCCESS ; }static const struct scsi_host_template tcm_loop_driver_template = {
Signature · 1 line
2.53.0