Re: [PATCH v9 06/10] block: implement NVMEM provider
Loic Poulain <[email protected]> Thu, 6 Aug 2026 10:35:27 +0200
| Newsgroups | org.kernel.vger.linux-mmc,org.infradead.lists.ath10k,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-block,org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.linux-wireless,org.kernel.vger.netdev |
|---|---|
| Message-ID | <CAFEp6-2kOsFcvBYk9Ow-ua7KAzdRgPD4Ky009O0Hgm-MXhJAMA@mail.gmail.com> |
Hi Christoph, On Tue, Aug 4, 2026 at 6:35=E2=80=AFPM Christoph Hellwig <[email protected]= > wrote: > > On Thu, Jul 30, 2026 at 06:00:36PM +0200, Loic Poulain wrote: > > On embedded devices using an eMMC it is common that one or more partiti= ons > > on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPRO= M > > data. Allow referencing the partition in device tree for the kernel and > > Wi-Fi drivers accessing it via the NVMEM layer. > > > > NVMEM is registered for a block device whose OF node describes an NVMEM > > layout, either via an "nvmem-layout" child or by being a "fixed-layout" > > node itself (e.g. an eMMC boot partition associated through its mmc-car= d > > node). > > > > The NVMEM device is a read-only side channel: the block device stays > > fully usable, reads are performed with a non-exclusive BLK_OPEN_READ > > open, and no synchronization is done against concurrent writes to the > > same region. This is acceptable for the intended use case of write-once= , > > factory-provisioned data. > > > > Signed-off-by: Loic Poulain <[email protected]> > > Reviewed-by: Bartosz Golaszewski <[email protected]> > > --- > > block/Kconfig | 11 +++++ > > block/Makefile | 1 + > > block/blk-nvmem.c | 104 ++++++++++++++++++++++++++++++++++++++= ++++++++ > > block/blk.h | 8 ++++ > > block/genhd.c | 4 ++ > > block/partitions/core.c | 5 +++ > > include/linux/blk_types.h | 4 ++ > > 7 files changed, 137 insertions(+) > > > > diff --git a/block/Kconfig b/block/Kconfig > > index 70e4a66d941ff66ecb0ec34f4f7eff7fd1e4be40..4899ad572c71ffdbb62460d= 57623216254893ddc 100644 > > --- a/block/Kconfig > > +++ b/block/Kconfig > > @@ -209,6 +209,17 @@ config BLK_INLINE_ENCRYPTION_FALLBACK > > by falling back to the kernel crypto API when inline > > encryption hardware is not present. > > > > +config BLK_NVMEM > > + bool "Block device NVMEM provider" > > + depends on OF > > + depends on NVMEM > > + help > > + Allow block devices (or partitions) to act as NVMEM providers, > > + exposing factory-provisioned data such as MAC addresses or Wi-F= i > > + calibration blobs to the drivers that consume them. This is > > + typically used on embedded devices where such data is stored in= a > > + dedicated area of an eMMC, instead of a separate EEPROM or OTP. > > + > > source "block/partitions/Kconfig" > > > > config BLK_PM > > diff --git a/block/Makefile b/block/Makefile > > index e7bd320e3d6971a8abf584f5de42776bb54e8216..8200297fbcbab304beb4239= d0c33da07011c3ed9 100644 > > --- a/block/Makefile > > +++ b/block/Makefile > > @@ -39,3 +39,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) +=3D blk-crypto.o= blk-crypto-profile.o \ > > blk-crypto-sysfs.o > > obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) +=3D blk-crypto-fallback.= o > > obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) +=3D holder.o > > +obj-$(CONFIG_BLK_NVMEM) +=3D blk-nvmem.o > > diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c > > new file mode 100644 > > index 0000000000000000000000000000000000000000..05bc35b9b8f373ccb606bdc= a35d211c41d2764fe > > --- /dev/null > > +++ b/block/blk-nvmem.c > > @@ -0,0 +1,104 @@ > > +// SPDX-License-Identifier: GPL-2.0-or-later > > +/* > > + * block device NVMEM provider > > + * > > + * Copyright (c) 2024 Daniel Golle <[email protected]> > > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. > > + * > > + * Useful on devices using a whole disk or a partition (e.g. an eMMC b= oot > > + * partition) to store MAC addresses, Bluetooth addresses or Wi-Fi > > + * calibration EEPROM data. > > + * > > + * The NVMEM device is a side channel onto a block device that stays f= ully > > + * usable. This is somewhat mitigated by opening the device exclusivel= y. > > + */ > > + > > +#include <linux/cleanup.h> > > +#include <linux/device.h> > > +#include <linux/file.h> > > +#include <linux/fs.h> > > +#include <linux/nvmem-provider.h> > > +#include <linux/nvmem-consumer.h> > > +#include <linux/of.h> > > +#include <linux/property.h> > > + > > +#include "blk.h" > > + > > +static int blk_nvmem_reg_read(void *priv, unsigned int from, > > + void *val, size_t bytes) > > +{ > > + struct block_device *bdev =3D priv; > > + struct file *bdev_file; > > + loff_t pos =3D from; > > + ssize_t ret; > > + > > + /* open and prevent other exclusive openers */ > > + bdev_file =3D bdev_file_open_by_dev(bdev->bd_dev, > > + BLK_OPEN_READ | BLK_OPEN_EXCL, > > + blk_nvmem_reg_read, NULL); > > + if (IS_ERR(bdev_file)) > > + return PTR_ERR(bdev_file); > > + > > + ret =3D kernel_read(bdev_file, val, bytes, &pos); > > + if (ret >=3D 0 && ret !=3D bytes) > > + ret =3D -EIO; > > + > > + fput(bdev_file); > > + > > + return ret < 0 ? ret : 0; > > The error handling here is a bit weird as you seem to handle the > positive/negative values twice. What about something like this instead? > > bdev_file =3D bdev_file_open_by_dev(bdev->bd_dev, > BLK_OPEN_READ | BLK_OPEN_EXCL, > blk_nvmem_reg_read, NULL); > if (IS_ERR(bdev_file)) > return PTR_ERR(bdev_file); > ret =3D kernel_read(bdev_file, val, bytes, &pos); > fput(bdev_file); > > if (ret < 0) > return ret; > if (ret !=3D bytes) > return -EIO; > return 0; > > ? Yes, that actually makes things clearer > > > + struct device_node *child __free(device_node) =3D > > + of_get_child_by_name(np, "nvmem-layout"); > > Please avoid the __cleanups stuff as it doesn't interact well with > the goto basd cleanups usually used in block drivers. Ack, will remove. > > > + config.id =3D NVMEM_DEVID_NONE; > > + config.dev =3D dev; > > + config.name =3D dev_name(dev); > > + config.owner =3D THIS_MODULE; > > + config.priv =3D bdev; > > + config.reg_read =3D blk_nvmem_reg_read; > > + config.size =3D bdev_nr_bytes(bdev); > > + config.word_size =3D 1; > > + config.stride =3D 1; > > + config.read_only =3D true; > > + config.root_only =3D true; > > + config.ignore_wp =3D true; > > + config.of_node =3D np; > > Maybe initialize all the static information at declaration time > here? Ok. > > > + bdev->bd_nvmem =3D nvmem; > > What guards access to this pointer? bd_nvmem is only ever assigned in blk_nvmem_add() and cleared in blk_nvmem_del(), and those are only called from the block device add/remove lifecycle, So registration and unregistration of the NVMEM device are serialized against each other by the same lock that serializes partition/disk creation and teardown, there is no window where blk_nvmem_add() and blk_nvmem_del() can race on the same bdev. Additionally, recent fixes in the nvmem framework have made it robust against concurrent access (for example, consumer reads) during or after nvmem unregistration. Thanks, Loic