Linux USB
 help / color / mirror / Atom feed
* [RFC]alpha implementation of eh_abort_handler for UAS
@ 2026-11-04 15:54 Oliver Neukum
  2026-11-04 16:13 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Oliver Neukum @ 2026-11-04 15:54 UTC (permalink / raw)
  To: Jiayi Li; +Cc: USB list

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

Hi,

as you apparently have a sophisticated test setup, here
is a patch I hope is useful. I've looked at your patches.
You are trying very hard and doing good work.

Yet the more I think about it the more it seems that
you are fundamentally hampered by a lack of a working
implementation of eh_abort_handler() in UAS.

Could you test this implementation?

	Regards
		Oliver

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

From 18794034f1b5e3f887a220550427d7e222c8a8d0 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

First draft of an actually working eh_abort_handler.

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

diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 8655edbd66b1..45182df35a7b 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -71,6 +71,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 */
@@ -286,6 +289,12 @@ 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:
+		struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd);
+
+		usb_unlink_urb(cmdinfo->killer);
+		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 +687,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 +740,167 @@ 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 int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
+
+#define SIMPLE_TUR_LENGTH 6
+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;
+	test_unit_ready->len = SIMPLE_TUR_LENGTH;
+	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;
-	struct urb *data_in_urb = NULL;
-	struct urb *data_out_urb = NULL;
+	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);
+
+	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;
+
+	if (status < 0) {
+		/*
+		 * 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);
+	} else {
+		kfree(u->transfer_buffer);
+		dummy = kzalloc(sizeof(*dummy), GFP_ATOMIC);
+		if (!dummy)
+			goto bailout;
+		usb_fill_bulk_urb(cmdinfo->killer, udev, devinfo->cmd_pipe,
+				dummy, sizeof(*dummy), uas_dummy_sense, cmnd);
+		cmdinfo->killer->transfer_flags |= URB_FREE_BUFFER;
+		spin_lock_irqsave(&devinfo->lock, flags);
+		if (devinfo->use_streams)
+			cmdinfo->killer->stream_id = cmdinfo->uas_tag;
+		rv = usb_submit_urb(cmdinfo->killer, GFP_ATOMIC);
+		spin_unlock_irqrestore(&devinfo->lock, flags);
+		if (rv < 0) {
+			kfree (dummy);
+			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;
+	struct urb *killer;
+	struct command_iu *bullet;
+	int target_tag, rv;
+	int result = FAILED;
+
+	killer = usb_alloc_urb(0, GFP_NOIO);
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	spin_lock_irq(&devinfo->lock);
+	target_tag = cmdinfo->uas_tag - 1;
+	if (!killer)
+		goto admit_failure;
+	cmdinfo->killer = killer;
+	bullet = allocate_double_command(target_tag, cmnd->device);
+	if (!bullet)
+		goto admit_failure;
+
+	usb_fill_bulk_urb(killer, udev, devinfo->cmd_pipe,
+			bullet, sizeof(*bullet), uas_abort_complete, cmnd);
+	if (devinfo->use_streams)
+		killer->stream_id = cmdinfo->uas_tag;
 	uas_log_cmd_state(cmnd, __func__, 0);
 
 	/* Ensure that try_complete does not call scsi_done */
 	cmdinfo->state |= COMMAND_ABORTED;
+	target_tag = cmdinfo->uas_tag - 1;
+	cmdinfo->killer = killer;
+	add_wait_queue(&cmdinfo->cancel, &wait);
+
+	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);
 
-	/* 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_irqrestore(&devinfo->lock, flags);
+		uas_free_unsubmitted_urbs(cmnd);
+		spin_unlock_irq(&devinfo->lock);
+		kfree(bullet);
+	} else {
+		spin_unlock_irq(&devinfo->lock);
+		rv = schedule_timeout(HZ);
+		if (rv)
+			result = cmdinfo->cleanly_cancelled ? SUCCESS : FAILED;
+	}
+	set_current_state(TASK_RUNNING);
+	/*
+	 * we know a callback will free the tag
+	 */
+	usb_kill_urb(killer);
+	usb_free_urb(killer);
 
 	if (data_in_urb) {
 		usb_kill_urb(data_in_urb);
@@ -769,7 +911,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)
-- 
2.56.0


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

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

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04 15:54 [RFC]alpha implementation of eh_abort_handler for UAS Oliver Neukum
2026-11-04 16:13 ` sashiko-bot

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