[PATCH v1 2/5] drivers: nvme: Implement NVMe flush command (0x0)

Neil Armstrong neil.armstrong at linaro.org
Thu May 28 11:28:33 CEST 2026


Hi,

On 5/28/26 03:20, dmukhin at ford.com wrote:
> From: Denis Mukhin <dmukhin at ford.com>
> 
> 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 <dmukhin at ford.com>
> ---
>   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


More information about the U-Boot mailing list