summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2025-04-10 14:21:46 -0600
committerTom Rini <trini@konsulko.com>2025-04-10 14:21:46 -0600
commitd82f7bc94c1b2fc82ebb8be5f743fd7c5292531f (patch)
tree3a8c3a0bcd90a0af2cf162399386889dc2a8026b
parent0d6e005a8cd7312cddaa4125125c4fc3470f5e18 (diff)
parent77c13f30b67c1a30b33b4dd4820c5a2528c3a3bb (diff)
Merge patch series "scsi: ensure writes are flushed to disk"
Caleb Connolly <caleb.connolly@linaro.org> says: SCSI devices like UFS may maintain their own cache to speed up writes, however this is lost on board reset (and may be lost on device removal or reset by OS drivers). Currently this can be worked around by "waiting for a while" after writing data to disk, but of course this is not an acceptable solution. Ideally U-Boot would have a mechanism to flush caches during board reset, but until that logic is hooked up let's be sure that all writes are actually propagated to the storage device so that we don't lose data on board reset. The same logic was already implemented just for the AHCI backend, this duplicated logic has been removed and support for the SYNC_CACHE command is added to AHCI. This is particularly noticeable during capsule updates, since the update file is deleted and the board is reset immediately afterwards which resulted in the same capsule update being applied over and over again. This specifically fixes Qualcomm SDM845 devices with UFS 2.1, but likely all UFS devices that use a cache. Link: https://lore.kernel.org/r/20250326-scsi-sync-on-write-v2-0-12ab05bd464b@linaro.org
-rw-r--r--drivers/ata/ahci.c14
-rw-r--r--drivers/scsi/scsi.c25
2 files changed, 27 insertions, 12 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index e593e228685..b532b3b7339 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -735,17 +735,6 @@ static int ata_scsiop_read_write(struct ahci_uc_priv *uc_priv,
is_write ? "WRITE" : "READ");
return -EIO;
}
-
- /* If this transaction is a write, do a following flush.
- * Writes in u-boot are so rare, and the logic to know when is
- * the last write and do a flush only there is sufficiently
- * difficult. Just do a flush after every write. This incurs,
- * usually, one extra flush when the rare writes do happen.
- */
- if (is_write) {
- if (-EIO == ata_io_flush(uc_priv, pccb->target))
- return -EIO;
- }
user_buffer += transfer_size;
user_buffer_size -= transfer_size;
blocks -= now_blocks;
@@ -845,6 +834,9 @@ static int ahci_scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
case SCSI_INQUIRY:
ret = ata_scsiop_inquiry(uc_priv, pccb);
break;
+ case SCSI_SYNC_CACHE:
+ ret = ata_io_flush(uc_priv, pccb->target);
+ break;
default:
printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);
return -ENOTSUPP;
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index cd0b84c0622..3e556540cae 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -78,6 +78,23 @@ static void scsi_setup_inquiry(struct scsi_cmd *pccb)
pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
}
+static void scsi_setup_sync_cache(struct scsi_cmd *pccb, lbaint_t start,
+ unsigned short blocks)
+{
+ pccb->cmd[0] = SCSI_SYNC_CACHE;
+ pccb->cmd[1] = 0;
+ pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff;
+ pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff;
+ pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff;
+ pccb->cmd[5] = (unsigned char)start & 0xff;
+ pccb->cmd[6] = 0;
+ pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff;
+ pccb->cmd[8] = (unsigned char)blocks & 0xff;
+ pccb->cmd[9] = 0;
+ pccb->cmdlen = 10;
+ pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
+}
+
static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start,
unsigned short blocks)
{
@@ -90,7 +107,7 @@ static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start,
pccb->cmd[6] = 0;
pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff;
pccb->cmd[8] = (unsigned char)blocks & 0xff;
- pccb->cmd[6] = 0;
+ pccb->cmd[9] = 0;
pccb->cmdlen = 10;
pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
debug("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n",
@@ -240,6 +257,12 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
}
buf_addr += pccb->datalen;
} while (blks != 0);
+
+ /* Flush the SCSI cache so we don't lose data on board reset. */
+ scsi_setup_sync_cache(pccb, 0, 0);
+ if (scsi_exec(bdev, pccb))
+ scsi_print_error(pccb);
+
debug("%s: end startblk " LBAF ", blccnt %x buffer %lX\n",
__func__, start, smallblks, buf_addr);
return blkcnt;