git: 30fd39861f8d - main - ufshci: fail the controller when a polled command never completes
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 03 Nov 2026 04:33:50 UTC
The branch main has been updated by jaeyoon:
URL: https://cgit.FreeBSD.org/src/commit/?id=30fd39861f8d4f24bbe5c3931e8c5adce21d2f1d
commit 30fd39861f8d4f24bbe5c3931e8c5adce21d2f1d
Author: Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-11-03 04:24:22 +0000
Commit: Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-11-03 04:29:45 +0000
ufshci: fail the controller when a polled command never completes
The reset task waits on its own polled bring-up commands. If one was
lost, nothing could complete it, and the poll panicked after ten
seconds.
Fail the controller instead. The failure path completes every
outstanding request, including the polled one, so the poll returns an
error to its caller. A polled command is only sent while the controller
is being brought up or by the passthrough ioctl, and a lost one means
the controller is not usable anyway.
Reviewed by: imp
Sponsored by: Samsung Electronics
Differential Revision: https://reviews.freebsd.org/D61119
---
sys/dev/ufshci/ufshci_ctrlr.c | 4 ++--
sys/dev/ufshci/ufshci_dev.c | 2 +-
sys/dev/ufshci/ufshci_ioctl.c | 2 +-
sys/dev/ufshci/ufshci_private.h | 33 +++++++++++++++++++++++++--------
4 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index 8b2471fd0558..0f44c6e04991 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -12,7 +12,7 @@
#include "ufshci_private.h"
#include "ufshci_reg.h"
-static void
+void
ufshci_ctrlr_fail(struct ufshci_controller *ctrlr)
{
/*
@@ -591,7 +591,7 @@ ufshci_ctrlr_send_nop(struct ufshci_controller *ctrlr)
if (error)
return (error);
- ufshci_completion_poll(&status);
+ ufshci_completion_poll(ctrlr, &status);
if (status.error) {
ufshci_printf(ctrlr, "ufshci_ctrlr_send_nop failed!\n");
return (ENXIO);
diff --git a/sys/dev/ufshci/ufshci_dev.c b/sys/dev/ufshci/ufshci_dev.c
index 28c91828a925..410e8cbe8d74 100644
--- a/sys/dev/ufshci/ufshci_dev.c
+++ b/sys/dev/ufshci/ufshci_dev.c
@@ -25,7 +25,7 @@ ufshci_dev_send_query(struct ufshci_controller *ctrlr,
if (error)
return (error);
- ufshci_completion_poll(status);
+ ufshci_completion_poll(ctrlr, status);
if (status->error) {
ufshci_printf(ctrlr, "%s failed!\n", fail_msg);
return (ENXIO);
diff --git a/sys/dev/ufshci/ufshci_ioctl.c b/sys/dev/ufshci/ufshci_ioctl.c
index 4ea2dbc38ed2..29fcf428ae74 100644
--- a/sys/dev/ufshci/ufshci_ioctl.c
+++ b/sys/dev/ufshci/ufshci_ioctl.c
@@ -171,7 +171,7 @@ ufshci_passthrough_cmd(struct ufshci_controller *ctrlr,
goto out;
}
- ufshci_completion_poll(&status);
+ ufshci_completion_poll(ctrlr, &status);
memcpy(&pt->resp_upiu, &status.cpl.response_upiu,
min(sizeof(pt->resp_upiu), sizeof(status.cpl.response_upiu)));
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index 5b3360c48198..110d8a7757ca 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -458,6 +458,7 @@ int ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start,
int ufshci_ctrlr_construct(struct ufshci_controller *ctrlr, device_t dev);
void ufshci_ctrlr_destruct(struct ufshci_controller *ctrlr, device_t dev);
void ufshci_ctrlr_reset(struct ufshci_controller *ctrlr);
+void ufshci_ctrlr_fail(struct ufshci_controller *ctrlr);
int ufshci_ctrlr_suspend(struct ufshci_controller *ctrlr,
enum power_stype stype);
int ufshci_ctrlr_resume(struct ufshci_controller *ctrlr,
@@ -588,10 +589,13 @@ int ufshci_detach(device_t dev);
* Wait for a command to complete using the ufshci_completion_poll_cb. Used in
* limited contexts where the caller knows it's OK to block briefly while the
* command runs. The ISR will run the callback which will set status->done to
- * true, usually within microseconds. If not, then after one second timeout
- * handler should reset the controller and abort all outstanding requests
- * including this polled one. If still not after ten seconds, then something is
- * wrong with the driver, and panic is the only way to recover.
+ * true, usually within microseconds.
+ *
+ * A command that does not complete within ten seconds is lost, and the reset
+ * task cannot rescue its own bring-up command. Returning would leave status,
+ * which lives on the caller's stack, to a late completion. Instead, fail the
+ * controller from here: the failure path completes every outstanding request,
+ * including this one, so the callback runs and the loop ends with an error.
*
* Most commands using this interface aren't actual I/O to the drive's media so
* complete within a few microseconds. Adaptively spin for one tick to catch the
@@ -599,15 +603,28 @@ int ufshci_detach(device_t dev);
* Since these are on startup, this drastically reduces startup time.
*/
static __inline void
-ufshci_completion_poll(struct ufshci_completion_poll_status *status)
+ufshci_completion_poll(struct ufshci_controller *ctrlr,
+ struct ufshci_completion_poll_status *status)
{
int timeout = ticks + 10 * hz;
sbintime_t delta_t = SBT_1US;
+ bool failed = false;
while (!atomic_load_acq_int(&status->done)) {
- if (timeout - ticks < 0)
- panic(
- "UFSHCI polled command failed to complete within 10s.");
+ if (timeout - ticks < 0) {
+ if (failed)
+ panic(
+ "UFSHCI polled command stuck after controller failure.");
+ /*
+ * The failure path completes every outstanding
+ * request, including this one. It skips a request
+ * that another path is completing right now, so
+ * give that path a second to finish.
+ */
+ ufshci_ctrlr_fail(ctrlr);
+ failed = true;
+ timeout = ticks + hz;
+ }
pause_sbt("ufshci_cpl", delta_t, 0, C_PREL(1));
delta_t = min(SBT_1MS, delta_t * 3 / 2);
}