.org/src/commit/?id=c686e7d3b0d315c358be81b4a1151711213d998d commit c686e7d3b0d315c358be81b4a1151711213d998d Author: Jaeyoon Choi AuthorDate: 2026-08-10 01:49:31 +0000 Commit: Jaeyoon Choi CommitDate: 2026-08-10 02:28:53 +0000 ufshci: check completions under the queue lock The completion scan held only the recovery lock. The submit path sets a slot to SCHEDULED and then rings the doorbell, both under the queue lock. A scan running between those two steps saw a SCHEDULED slot with a clear doorbell and completed a command the device had not started. The command failed with OCS 0xf, and a reused slot could return wrong read data. Check the slot state and the doorbell under the queue lock. The submit path holds it across both steps, so a half-submitted slot can no longer be seen. Found with fio randrw verify on QEMU. Sponsored by: Samsung Electronics Reviewed by: imp (mentor) Differential Revision: https://reviews.freebsd.org/D58668 --- sys/dev/ufshci/ufshci_req_sdb.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sys/dev/ufshci/ufshci_req_sdb.c b/sys/dev/ufshci/ufshci_req_sdb.c index 9f3a2a866ae0..80c4e53230e3 100644 --- a/sys/dev/ufshci/ufshci_req_sdb.c +++ b/sys/dev/ufshci/ufshci_req_sdb.c @@ -584,16 +584,23 @@ ufshci_req_sdb_process_cpl(struct ufshci_req_queue *req_queue) BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (slot = 0; slot < req_queue->num_entries; slot++) { + bool completed; + tr = hwq->act_tr[slot]; KASSERT(tr, ("there is no tracker assigned to the slot")); /* * When the response is delivered from the device, the doorbell - * is cleared. + * is cleared. Check it under qlock so that a slot whose + * doorbell write is still in flight in the submit path is not + * mistaken for a completed one. */ - if (tr->slot_state == UFSHCI_SLOT_STATE_SCHEDULED && + mtx_lock(&hwq->qlock); + completed = tr->slot_state == UFSHCI_SLOT_STATE_SCHEDULED && req_queue->qops.is_doorbell_cleared(req_queue->ctrlr, - slot)) { + slot); + mtx_unlock(&hwq->qlock); + if (completed) { ufshci_req_queue_complete_tracker(tr); done = true; }