Hi,

On 5/28/26 03:20, [email protected] wrote:
From: Denis Mukhin <[email protected]>

Add flush command implementation along with a high-level
disk_blk_flush() API to be called at certain checkpoints
during the boot (e.g. updating custom bootflow flags stored
on NVMe).

First general comment, please split the patch by adding the
flush infrastructure first and then the nvme support.

The for NVMe, can it support write-through like SCSI FUA (Force Unit
Access) which bypasses the cache on writes, with slower writes
but safer and simpler than calling a flush before OS boot.


Signed-off-by: Denis Mukhin <[email protected]>
---
  disk/disk-uclass.c         |  6 ++++++
  drivers/block/blk-uclass.c | 18 ++++++++++++++++++
  drivers/nvme/nvme.c        | 20 ++++++++++++++++++++
  include/blk.h              | 28 ++++++++++++++++++++++++++++
  include/part.h             |  8 ++++++++
  5 files changed, 80 insertions(+)

diff --git a/disk/disk-uclass.c b/disk/disk-uclass.c
index ee3cc4407d76..b04b6306e5f2 100644
--- a/disk/disk-uclass.c
+++ b/disk/disk-uclass.c
@@ -122,6 +122,11 @@ unsigned long disk_blk_erase(struct udevice *dev, lbaint_t 
start,
                         blkcnt);
  }
+unsigned long disk_blk_flush(struct udevice *dev)
+{
+       return blk_flush(dev_get_parent(dev));
+}
+
  UCLASS_DRIVER(partition) = {
        .id             = UCLASS_PARTITION,
        .per_device_plat_auto   = sizeof(struct disk_part),
@@ -132,6 +137,7 @@ static const struct blk_ops blk_part_ops = {
        .read   = disk_blk_read,
        .write  = disk_blk_write,
        .erase  = disk_blk_erase,
+       .flush  = disk_blk_flush,
  };
U_BOOT_DRIVER(blk_partition) = {
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index 73c24fd91763..0be1fdab1ba5 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -514,6 +514,19 @@ long blk_erase(struct udevice *dev, lbaint_t start, 
lbaint_t blkcnt)
        return ops->erase(dev, start, blkcnt);
  }
+long blk_flush(struct udevice *dev)
+{
+       struct blk_desc *desc = dev_get_uclass_plat(dev);
+       const struct blk_ops *ops = blk_get_ops(dev);
+
+       if (!ops->flush)
+               return -ENOSYS;
+
+       blkcache_invalidate(desc->uclass_id, desc->devnum);
+
+       return ops->flush(dev);
+}
+
  ulong blk_dread(struct blk_desc *desc, lbaint_t start, lbaint_t blkcnt,
                void *buffer)
  {
@@ -531,6 +544,11 @@ ulong blk_derase(struct blk_desc *desc, lbaint_t start, 
lbaint_t blkcnt)
        return blk_erase(desc->bdev, start, blkcnt);
  }
+ulong blk_dflush(struct blk_desc *desc)
+{
+       return blk_flush(desc->bdev);
+}
+
  int blk_find_from_parent(struct udevice *parent, struct udevice **devp)
  {
        struct udevice *dev;
diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c
index c3c44e50f19a..d9f099a11593 100644
--- a/drivers/nvme/nvme.c
+++ b/drivers/nvme/nvme.c
@@ -819,9 +819,29 @@ static ulong nvme_blk_write(struct udevice *udev, lbaint_t 
blknr,
        return nvme_blk_rw(udev, blknr, blkcnt, (void *)buffer, false);
  }
+/*
+ * NVM Flush command (opcode 0x00).
+ *
+ * Applies to a single namespace; the controller must commit all dirty
+ * data for that namespace to storage before completing the command.
+ */
+static ulong nvme_blk_flush(struct udevice *udev)
+{
+       struct nvme_ns *ns = dev_get_priv(udev);
+       struct nvme_dev *dev = ns->dev;
+       struct nvme_command c;
+
+       memset(&c, 0, sizeof(c));
+       c.common.opcode = nvme_cmd_flush;
+       c.common.nsid = cpu_to_le32(ns->ns_id);
+
+       return nvme_submit_sync_cmd(dev->queues[NVME_IO_Q], &c, NULL, 
IO_TIMEOUT);
+}
+
  static const struct blk_ops nvme_blk_ops = {
        .read   = nvme_blk_read,
        .write  = nvme_blk_write,
+       .flush  = nvme_blk_flush,
  };
U_BOOT_DRIVER(nvme_blk) = {
diff --git a/include/blk.h b/include/blk.h
index 8d1b70cabd31..3e2160a1ae4d 100644
--- a/include/blk.h
+++ b/include/blk.h
@@ -99,6 +99,7 @@ struct blk_desc {
        unsigned long   (*block_erase)(struct blk_desc *block_dev,
                                       lbaint_t start,
                                       lbaint_t blkcnt);
+       unsigned long   (*block_flush)(struct blk_desc *block_dev);
        void            *priv;          /* driver private struct pointer */
  #endif
  };
@@ -275,6 +276,14 @@ struct blk_ops {
         */
        int (*buffer_aligned)(struct udevice *dev, struct bounce_buffer *state);
  #endif        /* CONFIG_BOUNCE_BUFFER */
+
+       /**
+        * flush() -  commit all dirty data to storage
+        *
+        * @dev:        Device to flush
+        * @return 0 if OK, -ve on error
+        */
+       unsigned long (*flush)(struct udevice *dev);
  };
#if CONFIG_IS_ENABLED(BLK)
@@ -291,6 +300,7 @@ unsigned long blk_dwrite(struct blk_desc *block_dev, 
lbaint_t start,
                         lbaint_t blkcnt, const void *buffer);
  unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
                         lbaint_t blkcnt);
+unsigned long blk_dflush(struct blk_desc *block_dev);
#endif /* BLK */ @@ -331,6 +341,14 @@ long blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
   */
  long blk_erase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt);
+/**
+ * blk_flush() - Commit data to a block device
+ *
+ * @dev: Device to flush
+ * @return 0 if operation succeeded, or -ve on error.
+ */
+long blk_flush(struct udevice *dev);
+
  /**
   * blk_find_device() - Find a block device
   *
@@ -559,6 +577,16 @@ static inline ulong blk_derase(struct blk_desc *block_dev, 
lbaint_t start,
        return block_dev->block_erase(block_dev, start, blkcnt);
  }
+static inline ulong blk_dflush(struct blk_desc *block_dev)
+{
+       blkcache_invalidate(block_dev->uclass_id, block_dev->devnum);

Call blkcache_invalidate only if flush cmd is supported

+
+       if (block_dev->block_flush)
+               return block_dev->block_flush(block_dev);
+
+       return 0;

Why do you return success here and not from blk_flush() when flush is not 
supported?

+}
+
  /**
   * struct blk_driver - Driver for block interface types
   *
diff --git a/include/part.h b/include/part.h
index 15daacd7faaa..63982d7b9370 100644
--- a/include/part.h
+++ b/include/part.h
@@ -454,6 +454,14 @@ ulong disk_blk_write(struct udevice *dev, lbaint_t start, 
lbaint_t blkcnt,
   */
  ulong disk_blk_erase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt);
+/**
+ * disk_blk_flush() - commit data to a disk
+ *
+ * @dev:       Device to flush
+ * Return:     0 success, or -ve error number (see the IS_ERR_VALUE() macro
+ */
+ulong disk_blk_flush(struct udevice *dev);
+
  /*
   * We don't support printing partition information in SPL and only support
   * getting partition information in a few cases.

Thanks,
Neil

Reply via email to