Linux USB
 help / color / mirror / Atom feed
* [RFC]preliminary support for eh_abort_handler
@ 2026-11-05 12:51 Oliver Neukum
  2026-11-05 13:08 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Oliver Neukum @ 2026-11-05 12:51 UTC (permalink / raw)
  To: Jiayi Li; +Cc: USB list

[-- Attachment #1: Type: text/plain, Size: 173 bytes --]

Hi,

this should actually implement the abort method
by means of triggering an error by reusing the tag
of the command to be aborted.

What do you think?

	Regards
		Oliver

[-- Attachment #2: 0001-usb-uas-cancel-outstanding-commands.patch --]
[-- Type: text/x-patch, Size: 9529 bytes --]

From f0636ab6c0e0729153f2fdaf4f67ee5c72e98e0d Mon Sep 17 00:00:00 2001
From: Oliver Neukum <oneukum@suse.com>
Date: Wed, 4 Nov 2026 11:17:34 +0100
Subject: [PATCH] usb: uas: cancel outstanding commands

Second draft of an actually working eh_abort_handler.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
 drivers/usb/storage/uas.c | 190 ++++++++++++++++++++++++++++++++++----
 1 file changed, 174 insertions(+), 16 deletions(-)

diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 8655edbd66b1..d7421bf82209 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -38,6 +38,7 @@ struct uas_dev_info {
 	struct usb_anchor cmd_urbs;
 	struct usb_anchor sense_urbs;
 	struct usb_anchor data_urbs;
+	struct usb_anchor killer_urbs;
 	u64 flags;
 	int qdepth, resetting;
 	unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
@@ -71,6 +72,9 @@ struct uas_cmd_info {
 	struct urb *cmd_urb;
 	struct urb *data_in_urb;
 	struct urb *data_out_urb;
+	struct urb *killer;
+	wait_queue_head_t cancel;
+	int cleanly_cancelled;
 };
 
 /* I hate forward declarations, but I actually have a loop */
@@ -166,7 +170,7 @@ static void uas_zap_pending(struct uas_dev_info *devinfo, int result)
 		cmdinfo = scsi_cmd_priv(cmnd);
 		uas_log_cmd_state(cmnd, __func__, 0);
 		/* Sense urbs were killed, clear COMMAND_INFLIGHT manually */
-		cmdinfo->state &= ~COMMAND_INFLIGHT;
+		cmdinfo->state &= ~(COMMAND_INFLIGHT | COMMAND_ABORTED);
 		cmnd->result = result << 16;
 		err = uas_try_complete(cmnd, __func__);
 		WARN_ON(err != 0);
@@ -286,6 +290,9 @@ static bool uas_evaluate_response_iu(struct response_iu *riu, struct scsi_cmnd *
 	case RC_TMF_NOT_SUPPORTED:
 		set_host_byte(cmnd, DID_BAD_TARGET);
 		break;
+	case RC_OVERLAPPED_TAG:
+		set_host_byte(cmnd, DID_ABORT);
+		break;
 	default:
 		uas_log_cmd_state(cmnd, "response iu", response_code);
 		set_host_byte(cmnd, DID_ERROR);
@@ -678,6 +685,7 @@ static enum scsi_qc_status uas_queuecommand_lck(struct scsi_cmnd *cmnd)
 	memset(cmdinfo, 0, sizeof(*cmdinfo));
 	cmdinfo->uas_tag = idx + 1; /* uas-tag == usb-stream-id, so 1 based */
 	cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB;
+	init_waitqueue_head(&cmdinfo->cancel);
 
 	switch (cmnd->sc_data_direction) {
 	case DMA_FROM_DEVICE:
@@ -730,35 +738,182 @@ static enum scsi_qc_status uas_queuecommand_lck(struct scsi_cmnd *cmnd)
 static DEF_SCSI_QCMD(uas_queuecommand)
 
 /*
- * For now we do not support actually sending an abort to the device, so
- * this eh always fails. Still we must define it to make sure that we've
- * dropped all references to the cmnd in question once this function exits.
+ * We abort a command by reusing the tag, thus forcing an error
  */
+
+static struct command_iu *allocate_double_command(int tag, struct scsi_device *sdev)
+{
+	struct command_iu *test_unit_ready;
+
+	test_unit_ready = kzalloc(sizeof(*test_unit_ready), GFP_ATOMIC);
+	if (!test_unit_ready)
+		return NULL;
+
+	test_unit_ready->iu_id = IU_ID_COMMAND;
+	test_unit_ready->tag = cpu_to_be16(tag);
+	test_unit_ready->prio_attr = UAS_SIMPLE_TAG;
+	int_to_scsilun(sdev->lun, &test_unit_ready->lun);
+	/*
+	 * We need something that does not do harm
+	 * if the command to be aborted does finish
+	 * regularly
+	 */
+	test_unit_ready->cdb[0] = TEST_UNIT_READY;
+
+	return test_unit_ready;
+}
+
+static void uas_dummy_sense(struct urb *u)
+{
+	struct scsi_cmnd *cmnd = u->context;
+	struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd);
+	struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
+	int tag = cmdinfo->uas_tag;
+	unsigned long flags;
+	/*
+	 * An error implies success
+	 */
+	int status = u->status;
+
+	/*
+	 * The tag must be freed in every case
+	 */
+	spin_lock_irqsave(&devinfo->lock, flags);
+	devinfo->cmnd[tag - 1] = NULL;
+	switch (status) {
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case 0:
+		cmdinfo->cleanly_cancelled = 1;
+		break;
+	}
+	spin_unlock_irqrestore(&devinfo->lock, flags);
+
+	kfree(u->transfer_buffer);
+	wake_up_all(&cmdinfo->cancel);
+}
+
+static void uas_abort_complete(struct urb *u)
+{
+	int status = u->status;
+	struct scsi_cmnd *cmnd = u->context;
+	struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
+	struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd);
+	struct usb_device *udev = devinfo->udev;
+	struct response_iu *dummy;
+	int tag = cmdinfo->uas_tag;
+	unsigned long flags;
+	int rv;
+
+	switch (status) {
+	default:
+		/*
+		 * immediate cleanup
+		 */
+		kfree(u->transfer_buffer);
+bailout:
+		spin_lock_irqsave(&devinfo->lock, flags);
+		devinfo->cmnd[tag - 1] = NULL;
+		spin_unlock_irqrestore(&devinfo->lock, flags);
+		wake_up_all(&cmdinfo->cancel);
+		break;
+	case 0:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case -ENOENT:
+		kfree(u->transfer_buffer);
+		dummy = kzalloc(sizeof(*dummy), GFP_ATOMIC);
+		if (!dummy)
+			goto bailout;
+		usb_fill_bulk_urb(cmdinfo->killer, udev, devinfo->status_pipe,
+				dummy, sizeof(*dummy), uas_dummy_sense, cmnd);
+		spin_lock_irqsave(&devinfo->lock, flags);
+		if (devinfo->use_streams)
+			cmdinfo->killer->stream_id = cmdinfo->uas_tag;
+		usb_anchor_urb(u, &devinfo->killer_urbs);
+		rv = usb_submit_urb(cmdinfo->killer, GFP_ATOMIC);
+		spin_unlock_irqrestore(&devinfo->lock, flags);
+		if (rv < 0) {
+			usb_unanchor_urb(u);
+			goto bailout;
+		}
+	}
+}
+
 static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
 {
+	DECLARE_WAITQUEUE(wait, current);
 	struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd);
 	struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
+	struct usb_device *udev = devinfo->udev;
 	struct urb *data_in_urb = NULL;
 	struct urb *data_out_urb = NULL;
-	unsigned long flags;
+	struct urb *killer = NULL;
+	struct command_iu *bullet = NULL;
+	int target_tag, rv;
+	int result = FAILED;
 
-	spin_lock_irqsave(&devinfo->lock, flags);
+	killer = usb_alloc_urb(0, GFP_NOIO);
 
-	uas_log_cmd_state(cmnd, __func__, 0);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	spin_lock_irq(&devinfo->lock);
+	target_tag = cmdinfo->uas_tag;
 
 	/* Ensure that try_complete does not call scsi_done */
 	cmdinfo->state |= COMMAND_ABORTED;
 
-	/* Drop all refs to this cmnd, kill data urbs to break their ref */
-	devinfo->cmnd[cmdinfo->uas_tag - 1] = NULL;
-	if (cmdinfo->state & DATA_IN_URB_INFLIGHT)
-		data_in_urb = usb_get_urb(cmdinfo->data_in_urb);
-	if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
-		data_out_urb = usb_get_urb(cmdinfo->data_out_urb);
+	if (!killer)
+		goto admit_failure;
+	cmdinfo->killer = killer;
+	bullet = allocate_double_command(target_tag, cmnd->device);
+	if (!bullet)
+		goto admit_failure;
 
-	uas_free_unsubmitted_urbs(cmnd);
+	usb_fill_bulk_urb(killer, udev, devinfo->cmd_pipe,
+			bullet, sizeof(*bullet), uas_abort_complete, cmnd);
+	uas_log_cmd_state(cmnd, __func__, 0);
 
-	spin_unlock_irqrestore(&devinfo->lock, flags);
+	cmdinfo->killer = killer;
+	add_wait_queue(&cmdinfo->cancel, &wait);
+
+	usb_anchor_urb(killer, &devinfo->killer_urbs);
+	rv = usb_submit_urb(killer, GFP_ATOMIC);
+	if (rv < 0) {
+admit_failure:
+		/* Drop all refs to this cmnd, kill data urbs to break their ref */
+		devinfo->cmnd[cmdinfo->uas_tag - 1] = NULL;
+		if (cmdinfo->state & DATA_IN_URB_INFLIGHT)
+			data_in_urb = usb_get_urb(cmdinfo->data_in_urb);
+		if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
+			data_out_urb = usb_get_urb(cmdinfo->data_out_urb);
+
+		uas_free_unsubmitted_urbs(cmnd);
+		spin_unlock_irq(&devinfo->lock);
+		usb_unanchor_urb(killer);
+		kfree(bullet);
+	} else {
+		spin_unlock_irq(&devinfo->lock);
+		rv = schedule_timeout(HZ);
+		if (rv)
+			result = cmdinfo->cleanly_cancelled ? SUCCESS : FAILED;
+		spin_lock_irq(&devinfo->lock);
+		if (cmdinfo->state & DATA_IN_URB_INFLIGHT)
+			data_in_urb = usb_get_urb(cmdinfo->data_in_urb);
+		if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
+			data_out_urb = usb_get_urb(cmdinfo->data_out_urb);
+		spin_unlock_irq(&devinfo->lock);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&cmdinfo->cancel, &wait);
+	/*
+	 * we know a callback will free the tag
+	 */
+	spin_lock_irq(&devinfo->lock);
+	cmdinfo->killer = NULL;
+	spin_unlock_irq(&devinfo->lock);
+	usb_kill_urb(killer);
+	usb_free_urb(killer);
 
 	if (data_in_urb) {
 		usb_kill_urb(data_in_urb);
@@ -769,7 +924,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
 		usb_put_urb(data_out_urb);
 	}
 
-	return FAILED;
+	return result;
 }
 
 static int uas_eh_host_reset_handler(struct scsi_cmnd *cmnd)
@@ -796,6 +951,7 @@ static int uas_eh_host_reset_handler(struct scsi_cmnd *cmnd)
 	usb_kill_anchored_urbs(&devinfo->cmd_urbs);
 	usb_kill_anchored_urbs(&devinfo->sense_urbs);
 	usb_kill_anchored_urbs(&devinfo->data_urbs);
+	usb_kill_anchored_urbs(&devinfo->killer_urbs);
 	uas_zap_pending(devinfo, DID_RESET);
 
 	err = usb_reset_device(udev);
@@ -1038,6 +1194,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 	init_usb_anchor(&devinfo->cmd_urbs);
 	init_usb_anchor(&devinfo->sense_urbs);
 	init_usb_anchor(&devinfo->data_urbs);
+	init_usb_anchor(&devinfo->killer_urbs);
 	spin_lock_init(&devinfo->lock);
 	INIT_WORK(&devinfo->work, uas_do_work);
 	INIT_WORK(&devinfo->scan_work, uas_scan_work);
@@ -1225,6 +1382,7 @@ static void uas_disconnect(struct usb_interface *intf)
 	usb_kill_anchored_urbs(&devinfo->cmd_urbs);
 	usb_kill_anchored_urbs(&devinfo->sense_urbs);
 	usb_kill_anchored_urbs(&devinfo->data_urbs);
+	usb_kill_anchored_urbs(&devinfo->killer_urbs);
 	uas_zap_pending(devinfo, DID_NO_CONNECT);
 
 	/*
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-05 13:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 12:51 [RFC]preliminary support for eh_abort_handler Oliver Neukum
2026-11-05 13:08 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox