From: Bart Van Assche <bvanassche@acm.org>
To: Alexey Charkov <alchark@flipper.net>,
"James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>,
"Martin K. Petersen" <mkp@kernel.org>
Cc: linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] scsi: core: Clear power-on UNIT ATTENTION on BIWIN UFS devices
Date: Thu, 29 Oct 2026 10:19:57 -0700 [thread overview]
Message-ID: <e9ada0a2-aa36-4cde-a7b7-5d4693d372ae@acm.org> (raw)
In-Reply-To: <20261029-biwin-tua-quirk-v2-1-4d71af956ac5@flipper.net>
[-- Attachment #1: Type: text/plain, Size: 583 bytes --]
On 10/29/26 3:41 AM, Alexey Charkov wrote:
> The SCSI core retries a command that receives a UNIT ATTENTION and relies
> on the device to clear the condition once reported. The BIWIN
> BWU2A0526B128G and BWU2A0516B064G UFS devices do not clear a power on or
> reset UNIT ATTENTION when reporting it to a WRITE. After a device reset,
> such as on resume with the UFS link off or in UFS error recovery, writes
> then fail on every retry for the full 180s retry window and end in I/O
> errors. A TEST UNIT READY or READ clears it.
Does the attached untested patch help?
Thanks,
Bart.
[-- Attachment #2: 0001-scsi-core-Clear-persistent-UNIT-ATTENTION-with-TEST-.patch --]
[-- Type: text/x-patch, Size: 9153 bytes --]
From a81dfc5b9d1ae8d77c0ef4ea70322c937767a8f2 Mon Sep 17 00:00:00 2001
From: Bart Van Assche <bvanassche@acm.org>
Date: Thu, 29 Oct 2026 10:11:42 -0700
Subject: [PATCH] scsi: core: Clear persistent UNIT ATTENTION with TEST UNIT
READY
Recover from persistent UNIT ATTENTION conditions in the SCSI midlayer
instead of maintaining a per-device quirk list for targets such as
BIWIN BWU2A05* UFS devices that fail to clear power-on UNIT ATTENTION
on WRITE commands after a reset and only clear it on TEST UNIT READY.
Retry UNIT ATTENTION up to twice on the normal completion path in
scsi_check_sense() for non-removable block requests by returning
NEEDS_RETRY while scmd->retries < 2 and FAILED thereafter, instead of
letting scsi_io_completion_action() retry uncounted until the command
runtime limit is exceeded.
Add scsi_eh_clear_ua() to scsi_eh_ready_devs() ahead of device reset
so that the error handler sends TEST UNIT READY to consume a stuck
UNIT ATTENTION and verifies with a follow-up TEST UNIT READY that the
condition cleared before requeueing affected commands. Save and
restore scmd->sense_buffer across scsi_send_eh_cmnd() so that the
original sense data remains intact while filtering work_q and if
retries are later exhausted.
Introduce scsi_cmd_needs_stu() and use it in scsi_eh_stu() instead of
testing scsi_check_sense(scmd) == FAILED so that scsi_eh_stu() only
sends START_UNIT for LU_NOT_READY_INITIALIZING_COMMAND_REQUIRED and
not for persistent UNIT ATTENTION when sdev->allow_restart is set.
Fail non-removable commands in scsi_io_completion_action() on UNIT
ATTENTION when scsi_noretry_cmd() is true or retries exceed
max(cmd->allowed, 3) to honor failfast flags and prevent infinite
retry loops if a target continues to report UNIT ATTENTION after
recovery.
Reported-by: Alexey Charkov <alchark@flipper.net>
Closes: https://lore.kernel.org/linux-scsi/20261028-biwin-tua-quirk-v1-1-767a12c9b9cc@flipper.net/
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/scsi/scsi_error.c | 167 ++++++++++++++++++++++++++++++++++++--
drivers/scsi/scsi_lib.c | 4 +
2 files changed, 162 insertions(+), 9 deletions(-)
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 75442d60c30e..e7899c86d171 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -716,6 +716,20 @@ enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd)
sshdr.sense_code ==
LU_NOT_READY_INITIALIZING_COMMAND_REQUIRED)
return FAILED;
+ /*
+ * Retry UNIT ATTENTION up to twice on the normal completion
+ * path for non-removable block requests, then invoke the error
+ * handler so TEST UNIT READY can clear a stuck UNIT ATTENTION.
+ */
+ if (sshdr.sense_key == UNIT_ATTENTION &&
+ !sdev->removable &&
+ !blk_rq_is_passthrough(req) &&
+ scmd->submitter != SUBMITTED_BY_SCSI_ERROR_HANDLER &&
+ !scsi_noretry_cmd(scmd)) {
+ if (scmd->retries < 2)
+ return NEEDS_RETRY;
+ return FAILED;
+ }
/*
* Pass the UA upwards for a determination in the completion
* functions.
@@ -1582,6 +1596,24 @@ static int scsi_eh_try_stu(struct scsi_cmnd *scmd)
return 1;
}
+static bool scsi_cmd_needs_stu(const struct scsi_cmnd *scmd)
+{
+ struct scsi_sense_hdr sshdr;
+
+ if (!scmd->device->allow_restart ||
+ (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) ||
+ host_byte(scmd->result) != DID_OK ||
+ !scsi_status_is_check_condition(scmd->result) ||
+ !SCSI_SENSE_VALID(scmd))
+ return false;
+
+ return scsi_command_normalize_sense(scmd, &sshdr) &&
+ !scsi_sense_is_deferred(&sshdr) &&
+ (sshdr.sense_key == NOT_READY ||
+ sshdr.sense_key == UNIT_ATTENTION) &&
+ sshdr.sense_code == LU_NOT_READY_INITIALIZING_COMMAND_REQUIRED;
+}
+
/**
* scsi_eh_stu - send START_UNIT if needed
* @shost: &scsi host being recovered.
@@ -1610,8 +1642,7 @@ static int scsi_eh_stu(struct Scsi_Host *shost,
}
stu_scmd = NULL;
list_for_each_entry(scmd, work_q, eh_entry)
- if (scmd->device == sdev && SCSI_SENSE_VALID(scmd) &&
- scsi_check_sense(scmd) == FAILED ) {
+ if (scmd->device == sdev && scsi_cmd_needs_stu(scmd)) {
stu_scmd = scmd;
break;
}
@@ -1645,6 +1676,124 @@ static int scsi_eh_stu(struct Scsi_Host *shost,
return list_empty(work_q);
}
+static bool scsi_cmd_has_ua(const struct scsi_cmnd *scmd)
+{
+ struct scsi_sense_hdr sshdr;
+
+ if ((scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) ||
+ host_byte(scmd->result) != DID_OK ||
+ !scsi_status_is_check_condition(scmd->result) ||
+ !SCSI_SENSE_VALID(scmd))
+ return false;
+
+ return scsi_command_normalize_sense(scmd, &sshdr) &&
+ !scsi_sense_is_deferred(&sshdr) &&
+ sshdr.sense_key == UNIT_ATTENTION;
+}
+
+static bool scsi_eh_send_tur_clear_ua(struct scsi_cmnd *scmd)
+{
+ static unsigned char tur_command[6] = { TEST_UNIT_READY };
+ u8 sense_buf[SCSI_SENSE_BUFFERSIZE];
+ struct scsi_sense_hdr sshdr;
+ enum scsi_disposition rtn;
+ bool cleared = false;
+ int i;
+
+ memcpy(sense_buf, scmd->sense_buffer, sizeof(sense_buf));
+
+ /*
+ * Send TEST UNIT READY to consume the pending UNIT ATTENTION, then
+ * send a follow-up TEST UNIT READY to verify that the condition
+ * cleared rather than remaining stuck.
+ */
+ for (i = 0; i < 4; i++) {
+ rtn = scsi_send_eh_cmnd(scmd, tur_command, sizeof(tur_command),
+ scmd->device->eh_timeout, 0);
+ if (rtn == NEEDS_RETRY)
+ continue;
+ if (rtn != SUCCESS)
+ break;
+ if (!scsi_command_normalize_sense(scmd, &sshdr) ||
+ sshdr.sense_key == NO_SENSE ||
+ sshdr.sense_key == RECOVERED_ERROR) {
+ cleared = true;
+ break;
+ }
+ if (sshdr.sense_key != UNIT_ATTENTION)
+ break;
+ }
+
+ memcpy(scmd->sense_buffer, sense_buf, sizeof(sense_buf));
+ return cleared;
+}
+
+/**
+ * scsi_eh_clear_ua - clear pending UNIT ATTENTION conditions if needed
+ * @shost: SCSI host being recovered.
+ * @work_q: &list_head for pending commands.
+ * @done_q: &list_head for processed commands.
+ *
+ * If commands fail repeatedly with UNIT ATTENTION because a target does not
+ * clear the condition for those commands (for example, WRITE), send
+ * TEST UNIT READY to clear the UNIT ATTENTION before escalating to a reset.
+ *
+ * Return: 1 if @work_q is empty, 0 otherwise.
+ */
+static int scsi_eh_clear_ua(struct Scsi_Host *shost,
+ struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct scsi_cmnd *scmd, *ua_scmd, *next;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, shost) {
+ if (scsi_host_eh_past_deadline(shost)) {
+ SCSI_LOG_ERROR_RECOVERY(3,
+ sdev_printk(KERN_INFO, sdev,
+ "%s: skip clear UA, past eh deadline\n",
+ current->comm));
+ scsi_device_put(sdev);
+ break;
+ }
+
+ ua_scmd = NULL;
+ list_for_each_entry(scmd, work_q, eh_entry) {
+ if (scmd->device == sdev && scsi_cmd_has_ua(scmd)) {
+ ua_scmd = scmd;
+ break;
+ }
+ }
+
+ if (!ua_scmd)
+ continue;
+
+ SCSI_LOG_ERROR_RECOVERY(3,
+ sdev_printk(KERN_INFO, sdev,
+ "%s: Sending TEST_UNIT_READY to clear UA\n",
+ current->comm));
+
+ if (!scsi_device_online(sdev) ||
+ scsi_eh_send_tur_clear_ua(ua_scmd)) {
+ if (sdev->expecting_cc_ua)
+ sdev->expecting_cc_ua = 0;
+ list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
+ if (scmd->device == sdev &&
+ scsi_cmd_has_ua(scmd) &&
+ scsi_eh_action(scmd, SUCCESS) == SUCCESS)
+ scsi_eh_finish_cmd(scmd, done_q);
+ }
+ } else {
+ SCSI_LOG_ERROR_RECOVERY(3,
+ sdev_printk(KERN_INFO, sdev,
+ "%s: Failed to clear UA\n",
+ current->comm));
+ }
+ }
+
+ return list_empty(work_q);
+}
+
/**
* scsi_eh_bus_device_reset - send bdr if needed
@@ -2275,13 +2424,13 @@ void scsi_eh_ready_devs(struct Scsi_Host *shost,
struct list_head *work_q,
struct list_head *done_q)
{
- if (!scsi_eh_stu(shost, work_q, done_q))
- if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
- if (!scsi_eh_target_reset(shost, work_q, done_q))
- if (!scsi_eh_bus_reset(shost, work_q, done_q))
- if (!scsi_eh_host_reset(shost, work_q, done_q))
- scsi_eh_offline_sdevs(work_q,
- done_q);
+ if (!scsi_eh_stu(shost, work_q, done_q) &&
+ !scsi_eh_clear_ua(shost, work_q, done_q) &&
+ !scsi_eh_bus_device_reset(shost, work_q, done_q) &&
+ !scsi_eh_target_reset(shost, work_q, done_q) &&
+ !scsi_eh_bus_reset(shost, work_q, done_q) &&
+ !scsi_eh_host_reset(shost, work_q, done_q))
+ scsi_eh_offline_sdevs(work_q, done_q);
}
EXPORT_SYMBOL_GPL(scsi_eh_ready_devs);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index ab39514cc0c6..28fcfc6de117 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -849,6 +849,10 @@ static void scsi_io_completion_action(struct scsi_cmnd *cmd, int result)
*/
cmd->device->changed = 1;
action = ACTION_FAIL;
+ } else if (scsi_noretry_cmd(cmd) ||
+ (cmd->allowed != SCSI_CMD_RETRIES_NO_LIMIT &&
+ cmd->retries > max(cmd->allowed, 3))) {
+ action = ACTION_FAIL;
} else {
/* Must have been a power glitch, or a
* bus reset. Could not have been a
prev parent reply other threads:[~2026-10-29 17:20 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 10:41 Alexey Charkov
[not found] ` <sashiko-outbox-189185@kernel.org>
2026-10-29 13:33 ` Alexey Charkov
2026-10-29 17:19 ` Bart Van Assche [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e9ada0a2-aa36-4cde-a7b7-5d4693d372ae@acm.org \
--to=bvanassche@acm.org \
--cc=James.Bottomley@HansenPartnership.com \
--cc=alchark@flipper.net \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=mkp@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®