From 4c47fd0b6bce62162e11b8a22e2eaf0d8f6673b1 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 2 Dec 2018 10:54:22 +0100 Subject: mtd: Add a function to report when the MTD dev list has been updated We need to parse mtdparts/mtids again everytime a device has been added/removed from the MTD list, but there's currently no way to know when such an update has been done. Add an ->updated field to the idr struct that we set to true every time a device is added/removed and expose a function returning the value of this field and resetting it to false. Signed-off-by: Boris Brezillon Tested-by: Heiko Schocher --- include/linux/mtd/mtd.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/mtd') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 68e59153249..d20ebd82028 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -581,6 +581,7 @@ int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, const uint64_t length, uint64_t *len_incl_bad, int *truncated); +bool mtd_dev_list_updated(void); /* drivers/mtd/mtd_uboot.c */ int mtd_search_alternate_name(const char *mtdname, char *altname, -- cgit v1.3.1 From a02820fca90ce9ccf243b3fce59c04dabd5671a8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 2 Dec 2018 10:54:24 +0100 Subject: mtd: Delete partitions attached to the device when a device is deleted If we don't do that, partitions might still be exposed while the underlying device is gone. Fixes: 2a74930da57f ("mtd: mtdpart: implement proper partition handling") Signed-off-by: Boris Brezillon Tested-by: Heiko Schocher --- drivers/mtd/mtdcore.c | 7 +++++++ include/linux/mtd/mtd.h | 15 +++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 7a15ded8c88..cb7ca38d074 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -528,6 +528,13 @@ int del_mtd_device(struct mtd_info *mtd) struct mtd_notifier *not; #endif + ret = del_mtd_partitions(mtd); + if (ret) { + debug("Failed to delete MTD partitions attached to %s (err %d)\n", + mtd->name, ret); + return ret; + } + mutex_lock(&mtd_table_mutex); if (idr_find(&mtd_idr, mtd->index) != mtd) { diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index d20ebd82028..4d0096d9f1d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -562,8 +562,23 @@ unsigned mtd_mmap_capabilities(struct mtd_info *mtd); /* drivers/mtd/mtdcore.h */ int add_mtd_device(struct mtd_info *mtd); int del_mtd_device(struct mtd_info *mtd); + +#ifdef CONFIG_MTD_PARTITIONS int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +#else +static inline int add_mtd_partitions(struct mtd_info *mtd, + const struct mtd_partition *parts, + int nparts) +{ + return 0; +} + +static inline int del_mtd_partitions(struct mtd_info *mtd) +{ + return 0; +} +#endif struct mtd_info *__mtd_next_device(int i); #define mtd_for_each_device(mtd) \ -- cgit v1.3.1 From 4a5594fa20d0fa6479f477d2bd67967aca201c2f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 2 Dec 2018 10:54:30 +0100 Subject: mtd: Don't stop MTD partition creation when it fails on one device MTD partition creation code is a bit tricky. It tries to figure out when things have changed (either MTD dev list or mtdparts/mtdids vars) and when that happens it first deletes all the partitions that had been previously created and then creates the new ones based on the new mtdparts/mtdids values. But before deleting the old partitions, it ensures that none of the currently registered parts are being used and bails out when that's not the case. So, we end up in a situation where, if at least one MTD dev has one of its partitions used by someone (UBI for instance), the partitions update logic no longer works for other devs. Rework the code to relax the logic and allow updates of MTD parts on devices that are not being used (we still refuse to updates parts on devices who have at least one of their partitions used by someone). Fixes: 5db66b3aee6f ("cmd: mtd: add 'mtd' command") Signed-off-by: Boris Brezillon Tested-by: Heiko Schocher --- drivers/mtd/mtd_uboot.c | 96 ++++++++++++++++++++++++++++++++++--------------- drivers/mtd/mtdpart.c | 12 +++++++ include/linux/mtd/mtd.h | 2 ++ 3 files changed, 82 insertions(+), 28 deletions(-) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c index 6a36948b917..d638f700d04 100644 --- a/drivers/mtd/mtd_uboot.c +++ b/drivers/mtd/mtd_uboot.c @@ -149,6 +149,54 @@ static const char *get_mtdparts(void) return mtdparts; } +static int mtd_del_parts(struct mtd_info *mtd, bool quiet) +{ + int ret; + + if (!mtd_has_partitions(mtd)) + return 0; + + /* do not delete partitions if they are in use. */ + if (mtd_partitions_used(mtd)) { + if (!quiet) + printf("\"%s\" partitions still in use, can't delete them\n", + mtd->name); + return -EACCES; + } + + ret = del_mtd_partitions(mtd); + if (ret) + return ret; + + return 1; +} + +static bool mtd_del_all_parts_failed; + +static void mtd_del_all_parts(void) +{ + struct mtd_info *mtd; + int ret = 0; + + mtd_del_all_parts_failed = false; + + /* + * It is not safe to remove entries from the mtd_for_each_device loop + * as it uses idr indexes and the partitions removal is done in bulk + * (all partitions of one device at the same time), so break and + * iterate from start each time a new partition is found and deleted. + */ + do { + mtd_for_each_device(mtd) { + ret = mtd_del_parts(mtd, false); + if (ret > 0) + break; + else if (ret < 0) + mtd_del_all_parts_failed = true; + } + } while (ret > 0); +} + int mtd_probe_devices(void) { static char *old_mtdparts; @@ -156,18 +204,19 @@ int mtd_probe_devices(void) const char *mtdparts = get_mtdparts(); const char *mtdids = get_mtdids(); const char *mtdparts_next = mtdparts; - bool remaining_partitions = true; struct mtd_info *mtd; mtd_probe_uclass_mtd_devs(); /* - * Check if mtdparts/mtdids changed or if the MTD dev list was updated - * since last call, otherwise: exit + * Check if mtdparts/mtdids changed, if the MTD dev list was updated + * or if our previous attempt to delete existing partititions failed. + * In any of these cases we want to update the partitions, otherwise, + * everything is up-to-date and we can return 0 directly. */ if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) || (mtdparts && old_mtdparts && mtdids && old_mtdids && - !mtd_dev_list_updated() && + !mtd_dev_list_updated() && !mtd_del_all_parts_failed && !strcmp(mtdparts, old_mtdparts) && !strcmp(mtdids, old_mtdids))) return 0; @@ -178,32 +227,12 @@ int mtd_probe_devices(void) old_mtdparts = strdup(mtdparts); old_mtdids = strdup(mtdids); - /* If at least one partition is still in use, do not delete anything */ - mtd_for_each_device(mtd) { - if (mtd->usecount) { - printf("Partition \"%s\" already in use, aborting\n", - mtd->name); - return -EACCES; - } - } - /* - * Everything looks clear, remove all partitions. It is not safe to - * remove entries from the mtd_for_each_device loop as it uses idr - * indexes and the partitions removal is done in bulk (all partitions of - * one device at the same time), so break and iterate from start each - * time a new partition is found and deleted. + * Remove all old parts. Note that partition removal can fail in case + * one of the partition is still being used by an MTD user, so this + * does not guarantee that all old partitions are gone. */ - while (remaining_partitions) { - remaining_partitions = false; - mtd_for_each_device(mtd) { - if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { - del_mtd_partitions(mtd); - remaining_partitions = true; - break; - } - } - } + mtd_del_all_parts(); /* * Call mtd_dev_list_updated() to clear updates generated by our own @@ -278,6 +307,17 @@ int mtd_probe_devices(void) } } + /* + * Call mtd_del_parts() again, even if it's already been called + * in mtd_del_all_parts(). We need to know if old partitions are + * still around (because they are still being used by someone), + * and if they are, we shouldn't create new partitions, so just + * skip this MTD device and try the next one. + */ + ret = mtd_del_parts(mtd, true); + if (ret < 0) + continue; + /* * Parse the MTD device partitions. It will update the mtdparts * pointer, create an array of parts (that must be freed), and diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 4d2ac8107f0..fd8d8e5ea72 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -63,6 +63,18 @@ char *kstrdup(const char *s, gfp_t gfp) #define MTD_SIZE_REMAINING (~0LLU) #define MTD_OFFSET_NOT_SPECIFIED (~0LLU) +bool mtd_partitions_used(struct mtd_info *master) +{ + struct mtd_info *slave; + + list_for_each_entry(slave, &master->partitions, node) { + if (slave->usecount) + return true; + } + + return false; +} + /** * mtd_parse_partition - Parse @mtdparts partition definition, fill @partition * with it and update the @mtdparts string pointer. diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 4d0096d9f1d..cd1f557a2f3 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -366,6 +366,8 @@ static inline bool mtd_has_partitions(const struct mtd_info *mtd) return !list_empty(&mtd->partitions); } +bool mtd_partitions_used(struct mtd_info *master); + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, -- cgit v1.3.1