mtd: ubi: allow UBI volumes to provide NVMEM
v6 cover mtd: ubi: allow UBI volumes to provide NVMEM
Similar to how MAC addresses and Wi-Fi calibration data would be stored inside an MTD partition on devices coming with NOR flash, a UBI volume is used by some vendors in the same way on devices with NAND flash.
The goal of this series is to support such embedded Linux devices which got NVMEM bits stored inside a UBI volume.
Representing the UBI volume in the Device Tree and adding a phandle to be referenced by NVMEM consumers allows such devices to come up with their correct MAC addresses and device-specific Wi-Fi calibration data loaded.
In order for NVMEM bits to be available for other drivers, attaching UBI devices has to be moved from late_initcall (which is too late for other drivers) to happen earlier. As an alternative to the existing kernel cmdline parameter the Device Tree property 'compatible = "linux,ubi";' inside an MTD partition can be used to have that MTD device attached as UBI device. MTD partitions which serve as UBI devices may have a "volumes" subnode with volumes, and volumes may have an "nvmem-layout" object which will trigger the creation of an emulated NVMEM device on top of the UBI volume.
In this way, other drivers (think: Ethernet, Wi-Fi) can resolve and acquire NVMEM bits using the usual device tree phandle, just this time the NVMEM content is read from a UBI volume.
This series is a follow-up and contains most patches of the previous series "mtd: ubi: behave like a good MTD citizen"[1] which was meant in preparation for implementing the NVMEM provider.
[1]: https://patchwork.ozlabs.org/project/linux-mtd/list/?series=353177&state=%2A&archive=both
Changes since v5:
* fix whitespace problems in dt-schema additions
Changes since v4:
* split ubi_open_volume_path() breaking out reusable parts for new match_volume_desc() function as suggested by Richard Weinberger. Doing the same for ubi_open_volume_nm() doesn't work as we are working on struct ubi_volume_info in match_volume_desc() while ubi_open_volume_nm() is working on struct ubi_volume. That reduces the common part to a string comparision and length check which doesn't seem worth breaking out of the existing function.
* drop patches and changes not strictly needed for NVMEM use-case:
- don't handle ubi detach on MTD removal notification. It was not done until now and the locking hell I was facing when trying to implement that is non trivial.
- don't relocate the call to ubiblock device creation to the notification handler
- change ubiblock only as far as needed to handle creation from cmdline parameter when a volume is added.
* improve commit messages and comments
Changes since v3:
* dt-bindings fixes as requested
Changes since v2:
* include dt-bindings additions
Changes since v1:
* include patch to fix exiting Kconfig formatting issues
* fix typo and indentation in Kconfig
Daniel Golle (7):
dt-bindings: mtd: add basic bindings for UBI
dt-bindings: mtd: ubi-volume: allow UBI volumes to provide NVMEM
mtd: ubi: block: use notifier to create ubiblock from parameter
mtd: ubi: attach from device tree
mtd: ubi: introduce pre-removal notification for UBI volumes
mtd: ubi: populate ubi volume fwnode
mtd: ubi: provide NVMEM layer over UBI volumes
.../bindings/mtd/partitions/linux,ubi.yaml | 75 +++++++ .../bindings/mtd/partitions/ubi-volume.yaml | 40 ++++ drivers/mtd/ubi/Kconfig | 12 ++ drivers/mtd/ubi/Makefile | 1 + drivers/mtd/ubi/block.c | 136 ++++++------- drivers/mtd/ubi/build.c | 154 ++++++++++---- drivers/mtd/ubi/kapi.c | 56 ++++-- drivers/mtd/ubi/nvmem.c | 188 ++++++++++++++++++ drivers/mtd/ubi/ubi.h | 3 + drivers/mtd/ubi/vmt.c | 44 +++- include/linux/mtd/ubi.h | 2 + 11 files changed, 579 insertions(+), 132 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml create mode 100644 drivers/mtd/ubi/nvmem.c
Signature · 1 line
2.43.0
PATCH v6 1/7 dt-bindings: mtd: add basic bindings for UBI
Add basic bindings for UBI devices and volumes.
--- .../bindings/mtd/partitions/linux,ubi.yaml | 65 +++++++++++++++++++ .../bindings/mtd/partitions/ubi-volume.yaml | 35 ++++++++++ 2 files changed, 100 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml new file +65 -0
@@ -0,0 +1,65 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Unsorted Block Images + +description: | + UBI ("Unsorted Block Images") is a volume management system for raw + flash devices which manages multiple logical volumes on a single + physical flash device and spreads the I/O load (i.e wear-leveling) + across the whole flash chip. + +maintainers: + - Daniel Golle <[email protected]> + +allOf: + - $ref: partition.yaml# + +properties: + compatible: + const: linux,ubi + + volumes: + type: object + description: UBI Volumes + + patternProperties: + "^ubi-volume-.*$": + $ref: /schemas/mtd/partitions/ubi-volume.yaml# + + unevaluatedProperties: false + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + reg = <0x0 0x100000>; + label = "bootloader"; + read-only; + }; + + partition@100000 { + reg = <0x100000 0x1ff00000>; + label = "ubi"; + compatible = "linux,ubi"; + + volumes { + ubi-volume-caldata { + volid = <2>; + volname = "rf"; + }; + }; + }; + };
Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml new file +35 -0
@@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UBI volume + +description: | + This binding describes a single UBI volume. Volumes can be matches either + by their ID or their name, or both. + +maintainers: + - Daniel Golle <[email protected]> + +properties: + volid: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: + Match UBI volume ID + + volname: + $ref: "/schemas/types.yaml#/definitions/string" + description: + Match UBI volume ID + +anyOf: + - required: + - volid + + - required: + - volname + +# This is a generic file other binding inherit from and extend +additionalProperties: true
Signature · 1 line
2.43.0
PATCH v6 2/7 dt-bindings: mtd: ubi-volume: allow UBI volumes to provide NVMEM
In reply to Daniel GolleUBI volumes may be used to contain NVMEM bits, typically device MAC addresses or wireless radio calibration data.
--- .../bindings/mtd/partitions/linux,ubi.yaml | 10 ++++++++++ .../bindings/mtd/partitions/ubi-volume.yaml | 13 +++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-)
Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml +10 -0
@@ -59,6 +59,16 @@ examples: ubi-volume-caldata { volid = <2>; volname = "rf"; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + eeprom@0 { + reg = <0x0 0x1000>; + }; + }; }; }; };
Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml +9 -4
@@ -15,21 +15,26 @@ maintainers: properties: volid: - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 description: Match UBI volume ID volname: - $ref: "/schemas/types.yaml#/definitions/string" + $ref: /schemas/types.yaml#/definitions/string description: Match UBI volume ID + nvmem-layout: + $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# + description: + This container may reference an NVMEM layout parser. + anyOf: - required: - - volid + - volid - required: - - volname + - volname # This is a generic file other binding inherit from and extend additionalProperties: true
Signature · 1 line
2.43.0
PATCH v6 3/7 mtd: ubi: block: use notifier to create ubiblock from parameter
In reply to Daniel GolleUse UBI_VOLUME_ADDED notification to create ubiblock device specified on kernel cmdline or module parameter.
This makes thing more simple and has the advantage that ubiblock devices on volumes which are not present at the time the ubi module is probed will still be created.
--- drivers/mtd/ubi/block.c | 136 ++++++++++++++++++++-------------------- drivers/mtd/ubi/kapi.c | 54 +++++++++++----- drivers/mtd/ubi/ubi.h | 1 + 3 files changed, 106 insertions(+), 85 deletions(-)
drivers/mtd/ubi/block.c +68 -68
@@ -65,10 +65,10 @@ struct ubiblock_pdu { };/* Numbers of elements set in the @ubiblock_param array */ -static int ubiblock_devs__initdata ; +static int ubiblock_devs;/* MTD devices specification parameters */ -static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES ]__initdata ; +static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES ];struct ubiblock {struct ubi_volume_desc *desc;
@@ -534,6 +534,70 @@ static int ubiblock_resize(struct ubi_volume_info *vi)return 0 ; } +static bool +match_volume_desc (struct ubi_volume_info *vi,const char *name,int ubi_num,int vol_id) +{ +int err, len, cur_ubi_num, cur_vol_id; + +if (ubi_num ==-1 ) { +/* No ubi num, name must be a vol device path */ + err =ubi_get_num_by_path (name, &cur_ubi_num, &cur_vol_id); +if (err || vi->ubi_num != cur_ubi_num || vi->vol_id != cur_vol_id) +return false; + +return true; + } + +if (vol_id ==-1 ) { +/* Got ubi_num, but no vol_id, name must be volume name */ +if (vi->ubi_num != ubi_num) +return false; + + len =strnlen (name,UBI_VOL_NAME_MAX +1 ); +if (len <1 || vi->name_len != len) +return false; + +if (strcmp (name, vi->name)) +return false; + +return true; + } + +if (vi->ubi_num != ubi_num) +return false; + +if (vi->vol_id != vol_id) +return false; + +return true; +} + +static void +ubiblock_create_from_param (struct ubi_volume_info *vi) +{ +int i, ret =0 ; +struct ubiblock_param *p; + +/* +* Iterate over ubiblock cmdline parameters. If a parameter matches the +* newly added volume create the ubiblock device for it. +*/ +for (i =0 ; i < ubiblock_devs; i++) { + p = &ubiblock_param[i]; + +if (!match_volume_desc (vi, p->name, p->ubi_num, p->vol_id)) +continue ; + + ret =ubiblock_create (vi); +if (ret) { +pr_err ( +"UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n" , + vi->name, p->ubi_num, p->vol_id, ret); + } +break ; + } +} +static int ubiblock_notify (struct notifier_block *nb,unsigned long notification_type,void *ns_ptr) {
@@ -541,10 +605,7 @@ static int ubiblock_notify(struct notifier_block *nb,switch (notification_type) {case UBI_VOLUME_ADDED : -/* -* We want to enforce explicit block device creation for -* volumes, so when a volume is added we do nothing. -*/ +ubiblock_create_from_param (&nt->vi);break ;case UBI_VOLUME_REMOVED :ubiblock_remove (&nt->vi);
@@ -570,56 +631,6 @@ static struct notifier_block ubiblock_notifier = { .notifier_call = ubiblock_notify, }; -static struct ubi_volume_desc *__init -open_volume_desc (const char *name,int ubi_num,int vol_id) -{ -if (ubi_num ==-1 ) -/* No ubi num, name must be a vol device path */ -return ubi_open_volume_path (name,UBI_READONLY ); -else if (vol_id ==-1 ) -/* No vol_id, must be vol_name */ -return ubi_open_volume_nm (ubi_num, name,UBI_READONLY ); -else -return ubi_open_volume (ubi_num, vol_id,UBI_READONLY ); -} - -static void __init ubiblock_create_from_param (void ) -{ -int i, ret =0 ; -struct ubiblock_param *p; -struct ubi_volume_desc *desc; -struct ubi_volume_info vi; - -/* -* If there is an error creating one of the ubiblocks, continue on to -* create the following ubiblocks. This helps in a circumstance where -* the kernel command-line specifies multiple block devices and some -* may be broken, but we still want the working ones to come up. -*/ -for (i =0 ; i < ubiblock_devs; i++) { - p = &ubiblock_param[i]; - - desc =open_volume_desc (p->name, p->ubi_num, p->vol_id); -if (IS_ERR (desc)) { -pr_err ( -"UBI: block: can't open volume on ubi%d_%d, err=%ld\n" , - p->ubi_num, p->vol_id,PTR_ERR (desc)); -continue ; - } - -ubi_get_volume_info (desc, &vi); -ubi_close_volume (desc); - - ret =ubiblock_create (&vi); -if (ret) { -pr_err ( -"UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n" , - vi.name, p->ubi_num, p->vol_id, ret); -continue ; - } - } -} -static void ubiblock_remove_all (void ) {struct ubiblock *next;
@@ -645,18 +656,7 @@ int __init ubiblock_init(void)if (ubiblock_major <0 )return ubiblock_major; -/* -* Attach block devices from 'block=' module param. -* Even if one block device in the param list fails to come up, -* still allow the module to load and leave any others up. -*/ -ubiblock_create_from_param (); - -/* -* Block devices are only created upon user requests, so we ignore -* existing volumes. -*/ - ret =ubi_register_volume_notifier (&ubiblock_notifier,1 ); + ret =ubi_register_volume_notifier (&ubiblock_notifier,0 );if (ret) goto err_unreg;return 0 ;
drivers/mtd/ubi/kapi.c +37 -17
75 lines not on this page · raw message
drivers/mtd/ubi/ubi.h +1 -0
@@ -962,6 +962,7 @@ void ubi_free_internal_volumes(struct ubi_device *ubi);void ubi_do_get_device_info (struct ubi_device *ubi,struct ubi_device_info *di);void ubi_do_get_volume_info (struct ubi_device *ubi,struct ubi_volume *vol,struct ubi_volume_info *vi); +int ubi_get_num_by_path (const char *pathname,int *ubi_num,int *vol_id);/* scan.c */ int ubi_compare_lebs (struct ubi_device *ubi,const struct ubi_ainf_peb *aeb,int pnum,const struct ubi_vid_hdr *vid_hdr);
Signature · 1 line
2.43.0
PATCH v6 4/7 mtd: ubi: attach from device tree
In reply to Daniel GolleIntroduce device tree compatible 'linux,ubi' and attach compatible MTD devices using the MTD add notifier. This is needed for a UBI device to be available early at boot (and not only after late_initcall), so volumes on them can be used eg. as NVMEM providers for other drivers.
--- drivers/mtd/ubi/build.c | 135 ++++++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 39 deletions(-)
drivers/mtd/ubi/build.c +96 -39
@@ -27,6 +27,7 @@#include <linux/log2.h> #include <linux/kthread.h> #include <linux/kernel.h> +#include <linux/of.h> #include <linux/slab.h> #include <linux/major.h> #include "ubi.h"
@@ -1219,43 +1220,43 @@ static struct mtd_info * __init open_mtd_device(const char *mtd_dev)return mtd; } -static int __init ubi_init (void ) +static void ubi_notify_add (struct mtd_info *mtd) { -int err, i, k; +struct device_node *np =mtd_get_of_node (mtd); +int err; -/* Ensure that EC and VID headers have correct size */ -BUILD_BUG_ON (sizeof (struct ubi_ec_hdr ) !=64 ); -BUILD_BUG_ON (sizeof (struct ubi_vid_hdr ) !=64 ); +if (!of_device_is_compatible (np,"linux,ubi" )) +return ; -if (mtd_devs >UBI_MAX_DEVICES ) { -pr_err ("UBI error: too many MTD devices, maximum is %d\n" , -UBI_MAX_DEVICES ); -return -EINVAL ; - } +/* +* we are already holding &mtd_table_mutex, but still need +* to bump refcount +*/ + err =__get_mtd_device (mtd); +if (err) +return ; -/* Create base sysfs directory and sysfs files */ - err =class_register (&ubi_class); +/* called while holding mtd_table_mutex */ +mutex_lock_nested (&ubi_devices_mutex,SINGLE_DEPTH_NESTING ); + err =ubi_attach_mtd_dev (mtd,UBI_DEV_NUM_AUTO ,0 ,0 , false, false); +mutex_unlock (&ubi_devices_mutex);if (err <0 ) -return err; - - err =misc_register (&ubi_ctrl_cdev); -if (err) { -pr_err ("UBI error: cannot register device\n" ); - goto out; - } +__put_mtd_device (mtd); +} - ubi_wl_entry_slab =kmem_cache_create ("ubi_wl_entry_slab" , -sizeof (struct ubi_wl_entry ), -0 ,0 ,NULL ); -if (!ubi_wl_entry_slab) { - err = -ENOMEM ; - goto out_dev_unreg; - } +static void ubi_notify_remove (struct mtd_info *mtd) +{ +/* do nothing for now */ +} - err =ubi_debugfs_init (); -if (err) - goto out_slab; +static struct mtd_notifier ubi_mtd_notifier = { + .add = ubi_notify_add, + .remove = ubi_notify_remove, +}; +static int __init ubi_init_attach (void ) +{ +int err, i, k;/* Attach MTD devices */ for (i =0 ; i < mtd_devs; i++) {
@@ -1304,25 +1305,79 @@ static int __init ubi_init(void) } } +return 0 ; + +out_detach: +for (k =0 ; k < i; k++) +if (ubi_devices[k]) { +mutex_lock (&ubi_devices_mutex); +ubi_detach_mtd_dev (ubi_devices[k]->ubi_num,1 ); +mutex_unlock (&ubi_devices_mutex); + } +return err; +} +#ifndef CONFIG_MTD_UBI_MODULE +late_initcall (ubi_init_attach); +#endif + +static int __init ubi_init (void ) +{ +int err; + +/* Ensure that EC and VID headers have correct size */ +BUILD_BUG_ON (sizeof (struct ubi_ec_hdr ) !=64 ); +BUILD_BUG_ON (sizeof (struct ubi_vid_hdr ) !=64 ); + +if (mtd_devs >UBI_MAX_DEVICES ) { +pr_err ("UBI error: too many MTD devices, maximum is %d\n" , +UBI_MAX_DEVICES ); +return -EINVAL ; + } + +/* Create base sysfs directory and sysfs files */ + err =class_register (&ubi_class); +if (err <0 ) +return err; + + err =misc_register (&ubi_ctrl_cdev); +if (err) { +pr_err ("UBI error: cannot register device\n" ); + goto out; + } + + ubi_wl_entry_slab =kmem_cache_create ("ubi_wl_entry_slab" , +sizeof (struct ubi_wl_entry ), +0 ,0 ,NULL ); +if (!ubi_wl_entry_slab) { + err = -ENOMEM ; + goto out_dev_unreg; + } + + err =ubi_debugfs_init (); +if (err) + goto out_slab; + err =ubiblock_init ();if (err) {pr_err ("UBI error: block: cannot initialize, error %d\n" , err);/* See comment above re-ubi_is_module(). */ if (ubi_is_module ()) - goto out_detach; + goto out_slab; + } + +register_mtd_user (&ubi_mtd_notifier); + +if (ubi_is_module ()) { + err =ubi_init_attach (); +if (err) + goto out_mtd_notifier; }return 0 ; -out_detach: -for (k =0 ; k < i; k++) -if (ubi_devices[k]) { -mutex_lock (&ubi_devices_mutex); -ubi_detach_mtd_dev (ubi_devices[k]->ubi_num,1 ); -mutex_unlock (&ubi_devices_mutex); - } -ubi_debugfs_exit (); +out_mtd_notifier: +unregister_mtd_user (&ubi_mtd_notifier); out_slab:kmem_cache_destroy (ubi_wl_entry_slab); out_dev_unreg:
@@ -1332,13 +1387,15 @@ static int __init ubi_init(void)pr_err ("UBI error: cannot initialize UBI, error %d\n" , err);return err; } -late_initcall (ubi_init); +device_initcall (ubi_init); + static void __exit ubi_exit(void) {int i;ubiblock_exit (); +unregister_mtd_user (&ubi_mtd_notifier);for (i =0 ; i <UBI_MAX_DEVICES ; i++)if (ubi_devices[i]) {
Signature · 1 line
2.43.0
PATCH v6 5/7 mtd: ubi: introduce pre-removal notification for UBI volumes
In reply to Daniel GolleIntroduce a new notification type UBI_VOLUME_SHUTDOWN to inform users that a volume is just about to be removed.
This is needed because users (such as the NVMEM subsystem) expect that at the time their removal function is called, the parenting device is still available (for removal of sysfs nodes, for example, in case of NVMEM which otherwise WARNs on volume removal).
--- drivers/mtd/ubi/build.c | 19 ++++++++++++++----- drivers/mtd/ubi/kapi.c | 2 +- drivers/mtd/ubi/ubi.h | 2 ++ drivers/mtd/ubi/vmt.c | 17 +++++++++++++++-- include/linux/mtd/ubi.h | 2 ++ 5 files changed, 34 insertions(+), 8 deletions(-)
drivers/mtd/ubi/build.c +14 -5
@@ -93,7 +93,7 @@ static struct ubi_device *ubi_devices[UBI_MAX_DEVICES];/* Serializes UBI devices creations and removals */ DEFINE_MUTEX (ubi_devices_mutex); -/* Protects @ubi_devices and @ubi->ref_count */ +/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */ static DEFINE_SPINLOCK (ubi_devices_lock);/* "Show" method for files in '/<sysfs>/class/ubi/' */
@@ -261,6 +261,9 @@ struct ubi_device *ubi_get_device(int ubi_num)spin_lock (&ubi_devices_lock); ubi = ubi_devices[ubi_num]; +if (ubi && ubi->is_dead) + ubi =NULL ; +if (ubi) {ubi_assert (ubi->ref_count >=0 ); ubi->ref_count +=1 ;
@@ -298,7 +301,7 @@ struct ubi_device *ubi_get_by_major(int major)spin_lock (&ubi_devices_lock);for (i =0 ; i <UBI_MAX_DEVICES ; i++) { ubi = ubi_devices[i]; -if (ubi &&MAJOR (ubi->cdev.dev) == major) { +if (ubi && !ubi->is_dead &&MAJOR (ubi->cdev.dev) == major) {ubi_assert (ubi->ref_count >=0 ); ubi->ref_count +=1 ;get_device (&ubi->dev);
@@ -327,7 +330,7 @@ int ubi_major2num(int major)for (i =0 ; i <UBI_MAX_DEVICES ; i++) {struct ubi_device *ubi = ubi_devices[i]; -if (ubi &&MAJOR (ubi->cdev.dev) == major) { +if (ubi && !ubi->is_dead &&MAJOR (ubi->cdev.dev) == major) { ubi_num = ubi->ubi_num;break ; }
@@ -514,7 +517,7 @@ static void ubi_free_volumes_from(struct ubi_device *ubi, int from)int i;for (i = from; i < ubi->vtbl_slots +UBI_INT_VOL_COUNT ; i++) { -if (!ubi->volumes[i]) +if (!ubi->volumes[i] || ubi->volumes[i]->is_dead)continue ;ubi_eba_replace_table (ubi->volumes[i],NULL );ubi_fastmap_destroy_checkmap (ubi->volumes[i]);
@@ -1099,7 +1102,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)return -EINVAL ;spin_lock (&ubi_devices_lock); -put_device (&ubi->dev); ubi->ref_count -=1 ;if (ubi->ref_count) {if (!anyway) {
@@ -1110,6 +1112,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)ubi_err (ubi,"%s reference count %d, destroy anyway" , ubi->ubi_name, ubi->ref_count); } + ubi->is_dead = true; +spin_unlock (&ubi_devices_lock); + +ubi_notify_all (ubi,UBI_VOLUME_SHUTDOWN ,NULL ); + +spin_lock (&ubi_devices_lock); +put_device (&ubi->dev); ubi_devices[ubi_num] =NULL ;spin_unlock (&ubi_devices_lock);
drivers/mtd/ubi/kapi.c +1 -1
@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)spin_lock (&ubi->volumes_lock); vol = ubi->volumes[vol_id]; -if (!vol) +if (!vol || vol->is_dead) goto out_unlock; err = -EBUSY ;
drivers/mtd/ubi/ubi.h +2 -0
@@ -348,6 +348,7 @@ struct ubi_volume {int writers;int exclusive;int metaonly; +bool is_dead;int reserved_pebs;int vol_type;
@@ -568,6 +569,7 @@ struct ubi_device {spinlock_t volumes_lock;int ref_count;int image_seq; +bool is_dead;int rsvd_pebs;int avail_pebs;
drivers/mtd/ubi/vmt.c +15 -2
@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct device *dev,struct ubi_device *ubi = vol->ubi;spin_lock (&ubi->volumes_lock); -if (!ubi->volumes[vol->vol_id]) { +if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) {spin_unlock (&ubi->volumes_lock);return -ENODEV ; }
@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)/* Ensure that the name is unique */ for (i =0 ; i < ubi->vtbl_slots; i++) -if (ubi->volumes[i] && +if (ubi->volumes[i] && !ubi->volumes[i]->is_dead && ubi->volumes[i]->name_len == req->name_len && !strcmp (ubi->volumes[i]->name, req->name)) { ubi_err(ubi, "volume \"%s\" exists (ID %d)",
@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) err = -EBUSY ; goto out_unlock; } + +/* +* Mark volume as dead at this point to prevent that anyone +* can take a reference to the volume from now on. +* This is necessary as we have to release the spinlock before +* calling ubi_volume_notify. +*/ + vol->is_dead = true; +spin_unlock (&ubi->volumes_lock); + +ubi_volume_notify (ubi, vol,UBI_VOLUME_SHUTDOWN ); + +spin_lock (&ubi->volumes_lock); ubi->volumes[vol_id] =NULL ;spin_unlock (&ubi->volumes_lock);
include/linux/mtd/ubi.h +2 -0
@@ -192,6 +192,7 @@ struct ubi_device_info {* or a volume was removed) * @UBI_VOLUME_RESIZED: a volume has been re-sized * @UBI_VOLUME_RENAMED: a volume has been re-named +* @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users * @UBI_VOLUME_UPDATED: data has been written to a volume * * These constants define which type of event has happened when a volume
@@ -202,6 +203,7 @@ enum {UBI_VOLUME_REMOVED ,UBI_VOLUME_RESIZED ,UBI_VOLUME_RENAMED , +UBI_VOLUME_SHUTDOWN , UBI_VOLUME_UPDATED, };
Signature · 1 line
2.43.0
PATCH v6 6/7 mtd: ubi: populate ubi volume fwnode
In reply to Daniel GolleLook for the 'volumes' subnode of an MTD partition attached to a UBI device and attach matching child nodes to UBI volumes. This allows UBI volumes to be referenced in device tree, e.g. for use as NVMEM providers.
--- drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
drivers/mtd/ubi/vmt.c +27 -0
@@ -124,6 +124,31 @@ static void vol_release(struct device *dev)kfree (vol); } +static struct fwnode_handle *find_volume_fwnode (struct ubi_volume *vol) +{ +struct fwnode_handle *fw_vols, *fw_vol; +const char *volname; +u32 volid; + + fw_vols =device_get_named_child_node (vol->dev.parent->parent,"volumes" ); +if (!fw_vols) +return NULL ; + +fwnode_for_each_child_node (fw_vols, fw_vol) { +if (!fwnode_property_read_string (fw_vol,"volname" , &volname) && +strncmp (volname, vol->name, vol->name_len)) +continue ; + +if (!fwnode_property_read_u32 (fw_vol,"volid" , &volid) && + vol->vol_id != volid) +continue ; + +return fw_vol; + } + +return NULL ; +} +/** * ubi_create_volume - create volume. * @ubi: UBI device description object
@@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->name_len = req->name_len;memcpy (vol->name, req->name, vol->name_len); vol->ubi = ubi; +device_set_node (&vol->dev,find_volume_fwnode (vol));/* * Finish all pending erases because there may be some LEBs belonging
@@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) vol->dev.class = &ubi_class; vol->dev.groups = volume_dev_groups;dev_set_name (&vol->dev,"%s_%d" , ubi->ubi_name, vol->vol_id); +device_set_node (&vol->dev,find_volume_fwnode (vol)); err =device_register (&vol->dev);if (err) {cdev_del (&vol->cdev);
Signature · 1 line
2.43.0
PATCH v6 7/7 mtd: ubi: provide NVMEM layer over UBI volumes
In reply to Daniel GolleIn an ideal world we would like UBI to be used where ever possible on a NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it is possible to achieve an (almost-)all-UBI flash layout. Hence the need for a way to also use UBI volumes to store board-level constants, such as MAC addresses and calibration data of wireless interfaces.
Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM providers. Allow UBI devices to have a "volumes" firmware subnode with volumes which may be compatible with "nvmem-cells".
Access to UBI volumes via the NVMEM interface at this point is read-only, and it is slow, opening and closing the UBI volume for each access due to limitations of the NVMEM provider API.
--- drivers/mtd/ubi/Kconfig | 12 +++ drivers/mtd/ubi/Makefile | 1 + drivers/mtd/ubi/nvmem.c | 188 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 drivers/mtd/ubi/nvmem.c
drivers/mtd/ubi/Kconfig +12 -0
@@ -104,4 +104,16 @@ config MTD_UBI_BLOCK If in doubt, say "N". +config MTD_UBI_NVMEM + tristate "UBI virtual NVMEM" + default n + depends on NVMEM + help + This option enabled an additional driver exposing UBI volumes as NVMEM + providers, intended for platforms where UBI is part of the firmware + specification and used to store also e.g. MAC addresses or board- + specific Wi-Fi calibration data. + + If in doubt, say "N". + endif # MTD_UBI
drivers/mtd/ubi/Makefile +1 -0
@@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o +obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o
drivers/mtd/ubi/nvmem.c new file +188 -0
188 lines not on this page · raw message
Signature · 1 line
2.43.0
Miquel Raynal
maintainerRe: [PATCH v6 2/7] dt-bindings: mtd: ubi-volume: allow UBI volumes to provide NVMEM
In reply to Daniel GolleHi Daniel,
Quoted 20 lines
Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml +5
-2@@ -15,21 +15,26 @@ maintainers: properties: volid: - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 description: Match UBI volume ID volname: - $ref: "/schemas/types.yaml#/definitions/string" + $ref: /schemas/types.yaml#/definitions/string description: Match UBI volume ID + nvmem-layout: + $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# + description:
Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml maintainers: · line 37
Quoted 10 lines
Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml +4
-2+ This container may reference an NVMEM layout parser. + anyOf: - required: - - volid + - volid - required: - - volname + - volname
This should be squashed in patch 1.
Thanks,
Miquèl