* [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0
@ 2026-09-02 8:16 Stanley Jhu
2026-09-03 6:06 ` Jeuk Kim
0 siblings, 1 reply; 6+ messages in thread
From: Stanley Jhu @ 2026-09-02 8:16 UTC (permalink / raw)
To: Jeuk Kim; +Cc: qemu-devel, Jeuk Kim, Jinyoung Choi, Stanley Jhu
According to the JEDEC Universal Flash Storage Host Controller Interface
(UFSHCI) specification (Section 5.2.1 "Host Controller Enable"):
when Host Controller Enable (HCE) transitions from 1 to 0, a host
controller reset is initiated. The host controller shall abort all
active transfers, return internal state machines to idle, and de-assert
all interrupts.
Currently, QEMU's UFS emulator only clears HCS and HCE registers upon
HCE=0, leaving internal state active. Specifically:
- Outstanding SCSI requests in the block layer are not purged.
- Active bottom halves (doorbell_bh, complete_bh, and MCQ sq/cq BHs)
remain scheduled.
- Allocated MCQ Submission and Completion Queues (sq and cq) are
not freed.
- Dynamic MCQ queue registers, legacy UTRL request states, and status
registers (UTRLCNR, UTRLRSR) remain stale.
Implement ufs_hce_reset() to:
1. Implement .cancel callback in ufs_scsi_info to properly unref
scsi_req and prevent reference leaks when SCSI requests are purged.
2. Cancel active bottom halves (doorbell_bh, complete_bh, and MCQ sq/cq
BHs) and guard them with a resetting flag before purging requests,
ensuring blk_drain() cannot run or reschedule request-producing BHs.
3. Purge outstanding SCSI requests for all logical units via
scsi_device_purge_requests().
4. Clear standard request list slots and release SGLs via
ufs_clear_req(), and clear doorbells and status registers (UTRLCNR,
UTRLRSR).
5. Free allocated MCQ queues and clear dynamic queue registers while
preserving static capability offsets.
6. De-assert interrupts via ufs_irq_check().
7. Add a trace_ufs_hce_reset() trace event.
Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
---
v2 -> v3:
- Rebased on top of current upstream master (resolving context conflict
with recently merged Write Booster in hw/ufs/ufs.h).
- Fixed patch formatting and line-wrapping issues.
v1 -> v2:
- Added ufs_scsi_command_cancelled() as .cancel callback in ufs_scsi_info
to drop scsi_req reference and avoid leaks upon purge (Jeuk Kim).
- Reordered sequence to cancel all active BHs before
scsi_device_purge_requests() and introduced resetting flag to prevent
CQ BH from rescheduling SQ BH during blk_drain() (Jeuk Kim).
- Cleared UTRLCNR and UTRLRSR registers upon reset (Jeuk Kim).
- Guarded ufs_process_req() and ufs_complete_req() against execution while
resetting is in progress.
- Cleaned up array address-of idiom in mcq_op_reg memset.
---
hw/ufs/lu.c | 7 ++++
hw/ufs/trace-events | 1 +
hw/ufs/ufs.c | 96 ++++++++++++++++++++++++++++++++++++++++++++-
hw/ufs/ufs.h | 2 +
4 files changed, 105 insertions(+), 1 deletion(-)
diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c
index eeca865..bdb1650 100644
--- a/hw/ufs/lu.c
+++ b/hw/ufs/lu.c
@@ -188,6 +188,12 @@ static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid)
scsi_req_unref(scsi_req);
}
+static void ufs_scsi_command_cancelled(SCSIRequest *scsi_req)
+{
+ scsi_req->hba_private = NULL;
+ scsi_req_unref(scsi_req);
+}
+
static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req)
{
UfsRequest *req = scsi_req->hba_private;
@@ -202,6 +208,7 @@ static const struct SCSIBusInfo ufs_scsi_info = {
.get_sg_list = ufs_get_sg_list,
.complete = ufs_scsi_command_complete,
+ .cancel = ufs_scsi_command_cancelled,
};
static int ufs_emulate_report_luns(UfsRequest *req, uint8_t *outbuf,
diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
index 0cd3ba9..9222933 100644
--- a/hw/ufs/trace-events
+++ b/hw/ufs/trace-events
@@ -14,6 +14,7 @@ ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32
ufs_mcq_complete_req(uint8_t qid) "sqid %"PRIu8""
ufs_mcq_create_sq(uint8_t sqid, uint8_t cqid, uint64_t addr, uint16_t size) "mcq create sq sqid %"PRIu8", cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16""
ufs_mcq_create_cq(uint8_t cqid, uint64_t addr, uint16_t size) "mcq create cq cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16""
+ufs_hce_reset(void) "HCE 1 -> 0 reset: cancelling BHs, resetting MCQ and request lists"
# error condition
ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index 160a4ac..3c4d742 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -452,6 +452,10 @@ static void ufs_mcq_process_sq(void *opaque)
uint16_t head = ufs_mcq_sq_head(u, sq->sqid);
int err;
+ if (u->resetting) {
+ return;
+ }
+
while (!(ufs_mcq_sq_empty(u, sq->sqid) || QTAILQ_EMPTY(&sq->req_list))) {
addr = sq->addr + head;
err = ufs_addr_read(sq->u, addr, (void *)&sqe, sizeof(sqe));
@@ -525,7 +529,7 @@ static void ufs_mcq_process_cq(void *opaque)
tail = (tail + sizeof(req->cqe)) % (cq->size * sizeof(req->cqe));
ufs_mcq_update_cq_tail(u, cq->cqid, tail);
- if (QTAILQ_EMPTY(&req->sq->req_list) &&
+ if (!u->resetting && QTAILQ_EMPTY(&req->sq->req_list) &&
!ufs_mcq_sq_empty(u, req->sq->sqid)) {
/* Dequeueing from SQ was blocked due to lack of free requests */
qemu_bh_schedule(req->sq->bh);
@@ -722,6 +726,87 @@ static bool ufs_mcq_delete_cq(UfsHc *u, uint8_t qid)
return true;
}
+static void ufs_hce_reset(UfsHc *u)
+{
+ int i;
+
+ trace_ufs_hce_reset();
+
+ u->resetting = true;
+
+ /* 1. Cancel active Bottom Halves before purging requests */
+ if (u->doorbell_bh) {
+ qemu_bh_cancel(u->doorbell_bh);
+ }
+ if (u->complete_bh) {
+ qemu_bh_cancel(u->complete_bh);
+ }
+ if (u->params.mcq) {
+ for (i = 0; i < ARRAY_SIZE(u->sq); i++) {
+ if (u->sq[i] && u->sq[i]->bh) {
+ qemu_bh_cancel(u->sq[i]->bh);
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(u->cq); i++) {
+ if (u->cq[i] && u->cq[i]->bh) {
+ qemu_bh_cancel(u->cq[i]->bh);
+ }
+ }
+ }
+
+ /* 2. Purge outstanding SCSI requests for all logical units */
+ for (i = 0; i < UFS_MAX_LUS; i++) {
+ if (u->lus[i] && u->lus[i]->scsi_dev) {
+ scsi_device_purge_requests(u->lus[i]->scsi_dev, SENSE_CODE(RESET));
+ }
+ }
+
+ /* 3. Reset standard request list slots, doorbells, and status registers */
+ for (i = 0; i < u->params.nutrs; i++) {
+ ufs_clear_req(&u->req_list[i]);
+ u->req_list[i].state = UFS_REQUEST_IDLE;
+ }
+ u->reg.utrldbr = 0;
+ u->reg.utmrldbr = 0;
+ u->reg.utrlcnr = 0;
+ u->reg.utrlrsr = 0;
+ u->reg.is = 0;
+
+ /* 4. Free MCQ Queues and reset MCQ dynamic registers */
+ if (u->params.mcq) {
+ for (i = 0; i < ARRAY_SIZE(u->sq); i++) {
+ if (u->sq[i]) {
+ ufs_mcq_free_sq(u->sq[i]);
+ u->sq[i] = NULL;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(u->cq); i++) {
+ if (u->cq[i]) {
+ ufs_mcq_free_cq(u->cq[i]);
+ u->cq[i] = NULL;
+ }
+ }
+
+ /* Clear dynamic queue configuration registers without overwriting static capability offsets */
+ for (i = 0; i < ARRAY_SIZE(u->mcq_reg); i++) {
+ u->mcq_reg[i].sqattr = 0;
+ u->mcq_reg[i].sqlba = 0;
+ u->mcq_reg[i].squba = 0;
+ u->mcq_reg[i].sqcfg = 0;
+ u->mcq_reg[i].cqattr = 0;
+ u->mcq_reg[i].cqlba = 0;
+ u->mcq_reg[i].cquba = 0;
+ u->mcq_reg[i].cqcfg = 0;
+ }
+ memset(u->mcq_op_reg, 0, sizeof(u->mcq_op_reg));
+ }
+
+ u->resetting = false;
+
+ /* 5. De-assert IRQ */
+ ufs_irq_check(u);
+}
+
static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
{
switch (offset) {
@@ -739,6 +824,7 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 1);
} else if (FIELD_EX32(u->reg.hce, HCE, HCE) &&
!FIELD_EX32(data, HCE, HCE)) {
+ ufs_hce_reset(u);
u->reg.hcs = 0;
u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 0);
}
@@ -2089,6 +2175,10 @@ static void ufs_process_req(void *opaque)
UfsRequest *req;
int slot;
+ if (u->resetting) {
+ return;
+ }
+
for (slot = 0; slot < u->params.nutrs; slot++) {
req = &u->req_list[slot];
@@ -2115,6 +2205,10 @@ void ufs_complete_req(UfsRequest *req, UfsReqResult req_result)
req->state = UFS_REQUEST_COMPLETE;
+ if (u->resetting) {
+ return;
+ }
+
if (ufs_mcq_req(req)) {
trace_ufs_mcq_complete_req(req->sq->sqid);
QTAILQ_INSERT_TAIL(&req->sq->cq->req_list, req, entry);
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
index aa8361d..6f2693b 100644
--- a/hw/ufs/ufs.h
+++ b/hw/ufs/ufs.h
@@ -173,6 +173,8 @@ typedef struct UfsHc {
UfsSq *sq[UFS_MAX_MCQ_QNUM];
UfsCq *cq[UFS_MAX_MCQ_QNUM];
+ bool resetting;
+
/* Extended features */
UfsWb wb;
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0
2026-09-02 8:16 [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0 Stanley Jhu
@ 2026-09-03 6:06 ` Jeuk Kim
2026-09-17 12:22 ` Stanley Jhu
0 siblings, 1 reply; 6+ messages in thread
From: Jeuk Kim @ 2026-09-03 6:06 UTC (permalink / raw)
To: Stanley Jhu, Jeuk Kim; +Cc: qemu-devel, Jinyoung Choi
On 9/2/2026 5:16 PM, Stanley Jhu wrote:
> According to the JEDEC Universal Flash Storage Host Controller Interface
> (UFSHCI) specification (Section 5.2.1 "Host Controller Enable"):
> when Host Controller Enable (HCE) transitions from 1 to 0, a host
> controller reset is initiated. The host controller shall abort all
> active transfers, return internal state machines to idle, and de-assert
> all interrupts.
>
> Currently, QEMU's UFS emulator only clears HCS and HCE registers upon
> HCE=0, leaving internal state active. Specifically:
> - Outstanding SCSI requests in the block layer are not purged.
> - Active bottom halves (doorbell_bh, complete_bh, and MCQ sq/cq BHs)
> remain scheduled.
> - Allocated MCQ Submission and Completion Queues (sq and cq) are
> not freed.
> - Dynamic MCQ queue registers, legacy UTRL request states, and status
> registers (UTRLCNR, UTRLRSR) remain stale.
>
> Implement ufs_hce_reset() to:
> 1. Implement .cancel callback in ufs_scsi_info to properly unref
> scsi_req and prevent reference leaks when SCSI requests are purged.
> 2. Cancel active bottom halves (doorbell_bh, complete_bh, and MCQ sq/cq
> BHs) and guard them with a resetting flag before purging requests,
> ensuring blk_drain() cannot run or reschedule request-producing BHs.
> 3. Purge outstanding SCSI requests for all logical units via
> scsi_device_purge_requests().
> 4. Clear standard request list slots and release SGLs via
> ufs_clear_req(), and clear doorbells and status registers (UTRLCNR,
> UTRLRSR).
> 5. Free allocated MCQ queues and clear dynamic queue registers while
> preserving static capability offsets.
> 6. De-assert interrupts via ufs_irq_check().
> 7. Add a trace_ufs_hce_reset() trace event.
>
> Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
> ---
Thank you for your contribution.
Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0
2026-09-03 6:06 ` Jeuk Kim
@ 2026-09-17 12:22 ` Stanley Jhu
2026-09-17 14:41 ` Stefan Hajnoczi
2026-09-18 0:12 ` Jeuk Kim
0 siblings, 2 replies; 6+ messages in thread
From: Stanley Jhu @ 2026-09-17 12:22 UTC (permalink / raw)
To: Jeuk Kim, Jeuk Kim
Cc: qemu-block, qemu-devel, Stefan Hajnoczi, Richard Henderson,
Kevin Wolf, Hanna Reitz, Jinyoung Choi
On Thu, Sep 03, 2026 at 03:06:42PM +0900, Jeuk Kim wrote:
> Thank you for your contribution.
> Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com>
Hi Jeuk, Stefan, and all,
Gentle ping on this patch. It has held Reviewed-by from Jeuk for two
weeks:
https://lore.kernel.org/qemu-devel/20260902081610.2290123-1-stanleyjhu@google.com/
Could this please be queued for the next UFS or block pull request?
Thanks,
Stanley
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0
2026-09-17 12:22 ` Stanley Jhu
@ 2026-09-17 14:41 ` Stefan Hajnoczi
2026-09-21 13:58 ` Jeuk Kim
2026-09-18 0:12 ` Jeuk Kim
1 sibling, 1 reply; 6+ messages in thread
From: Stefan Hajnoczi @ 2026-09-17 14:41 UTC (permalink / raw)
To: Jeuk Kim, Jeuk Kim
Cc: qemu-block, qemu-devel, Richard Henderson, Kevin Wolf,
Hanna Reitz, Jinyoung Choi, Stanley Jhu
[-- Attachment #1: Type: text/plain, Size: 561 bytes --]
On Thu, Sep 17, 2026 at 12:22:09PM +0000, Stanley Jhu wrote:
> On Thu, Sep 03, 2026 at 03:06:42PM +0900, Jeuk Kim wrote:
> > Thank you for your contribution.
>
> > Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com>
>
> Hi Jeuk, Stefan, and all,
>
> Gentle ping on this patch. It has held Reviewed-by from Jeuk for two
> weeks:
> https://lore.kernel.org/qemu-devel/20260902081610.2290123-1-stanleyjhu@google.com/
>
> Could this please be queued for the next UFS or block pull request?
Jeuk: Are you planning to send a UFS pull request?
Stefan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0
2026-09-17 12:22 ` Stanley Jhu
2026-09-17 14:41 ` Stefan Hajnoczi
@ 2026-09-18 0:12 ` Jeuk Kim
1 sibling, 0 replies; 6+ messages in thread
From: Jeuk Kim @ 2026-09-18 0:12 UTC (permalink / raw)
To: Stanley Jhu, Jeuk Kim
Cc: qemu-block, qemu-devel, Stefan Hajnoczi, Richard Henderson,
Kevin Wolf, Hanna Reitz, Jinyoung Choi
On 9/17/2026 9:22 PM, Stanley Jhu wrote:
> On Thu, Sep 03, 2026 at 03:06:42PM +0900, Jeuk Kim wrote:
>> Thank you for your contribution.
>
>> Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com>
>
> Hi Jeuk, Stefan, and all,
>
> Gentle ping on this patch. It has held Reviewed-by from Jeuk for two
> weeks:
> https://lore.kernel.org/qemu-devel/20260902081610.2290123-1-stanleyjhu@google.com/
>
>
> Could this please be queued for the next UFS or block pull request?
>
> Thanks,
> Stanley
Hi Stanley,
Sorry for the delay. I was away for the past few weeks.
I've just sent a pull request including this patch:
https://lore.kernel.org/qemu-devel/cover.1789689763.git.jeuk20.kim@samsung.com/
I'll also catch up on the remaining UFS patches and review them next week.
Thanks,
Jeuk
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0
2026-09-17 14:41 ` Stefan Hajnoczi
@ 2026-09-21 13:58 ` Jeuk Kim
0 siblings, 0 replies; 6+ messages in thread
From: Jeuk Kim @ 2026-09-21 13:58 UTC (permalink / raw)
To: Stefan Hajnoczi, Jeuk Kim
Cc: qemu-block, qemu-devel, Richard Henderson, Kevin Wolf,
Hanna Reitz, Jinyoung Choi, Stanley Jhu
On 9/17/2026 11:41 PM, Stefan Hajnoczi wrote:
> On Thu, Sep 17, 2026 at 12:22:09PM +0000, Stanley Jhu wrote:
>> On Thu, Sep 03, 2026 at 03:06:42PM +0900, Jeuk Kim wrote:
>>> Thank you for your contribution.
>>> Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com>
>> Hi Jeuk, Stefan, and all,
>>
>> Gentle ping on this patch. It has held Reviewed-by from Jeuk for two
>> weeks:
>> https://lore.kernel.org/qemu-devel/20260902081610.2290123-1-stanleyjhu@google.com/
>>
>> Could this please be queued for the next UFS or block pull request?
> Jeuk: Are you planning to send a UFS pull request?
>
> Stefan
Hi Stefan
Yes, I've already sent a UFS pull request:
https://lore.kernel.org/qemu-devel/cover.1789689763.git.jeuk20.kim@samsung.com/
Thanks,
Jeuk
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-21 13:59 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 8:16 [PATCH v3] hw/ufs: Reset controller and MCQ state on HCE transition to 0 Stanley Jhu
2026-09-03 6:06 ` Jeuk Kim
2026-09-17 12:22 ` Stanley Jhu
2026-09-17 14:41 ` Stefan Hajnoczi
2026-09-21 13:58 ` Jeuk Kim
2026-09-18 0:12 ` Jeuk Kim
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.