When underlying device is removed mtd core will crash in case user space is still holding an open handle to a mtd device node. A proper refcounting is needed so device is release only when a partition has no active users. The current simple counter is not sufficient.
Signed-off-by: Tomas Winkler <tomas.wink...@intel.com> --- drivers/mtd/mtdcore.c | 55 ++++++++++++++++++++++------------------- drivers/mtd/mtdcore.h | 1 + drivers/mtd/mtdpart.c | 12 ++++----- include/linux/mtd/mtd.h | 2 +- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 2d6423d89a17..db5167eacaa4 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -93,9 +93,29 @@ static void mtd_release(struct device *dev) dev_t index = MTD_DEVT(mtd->index); /* remove /dev/mtdXro node */ + if (mtd_is_partition(mtd)) + release_mtd_partition(mtd); + device_destroy(&mtd_class, index + 1); } +static void mtd_device_release(struct kref *kref) +{ + struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt); + + if (mtd->nvmem) { + nvmem_unregister(mtd->nvmem); + mtd->nvmem = NULL; + } + + idr_remove(&mtd_idr, mtd->index); + of_node_put(mtd_get_of_node(mtd)); + + device_unregister(&mtd->dev); + + module_put(THIS_MODULE); +} + static ssize_t mtd_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -619,7 +639,7 @@ int add_mtd_device(struct mtd_info *mtd) } mtd->index = i; - mtd->usecount = 0; + kref_init(&mtd->refcnt); /* default value if not set by driver */ if (mtd->bitflip_threshold == 0) @@ -733,23 +753,8 @@ int del_mtd_device(struct mtd_info *mtd) list_for_each_entry(not, &mtd_notifiers, list) not->remove(mtd); - if (mtd->usecount) { - printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", - mtd->index, mtd->name, mtd->usecount); - ret = -EBUSY; - } else { - /* Try to remove the NVMEM provider */ - if (mtd->nvmem) - nvmem_unregister(mtd->nvmem); - - device_unregister(&mtd->dev); - - idr_remove(&mtd_idr, mtd->index); - of_node_put(mtd_get_of_node(mtd)); - - module_put(THIS_MODULE); - ret = 0; - } + kref_put(&mtd->refcnt, mtd_device_release); + ret = 0; out_error: mutex_unlock(&mtd_table_mutex); @@ -984,20 +989,21 @@ int __get_mtd_device(struct mtd_info *mtd) if (!try_module_get(master->owner)) return -ENODEV; + kref_get(&mtd->refcnt); + if (master->_get_device) { err = master->_get_device(mtd); if (err) { + kref_put(&mtd->refcnt, mtd_device_release); module_put(master->owner); return err; } } - master->usecount++; - while (mtd->parent) { - mtd->usecount++; mtd = mtd->parent; + kref_get(&mtd->refcnt); } return 0; @@ -1055,14 +1061,13 @@ void __put_mtd_device(struct mtd_info *mtd) { struct mtd_info *master = mtd_get_master(mtd); + kref_put(&mtd->refcnt, mtd_device_release); + while (mtd->parent) { - --mtd->usecount; - BUG_ON(mtd->usecount < 0); mtd = mtd->parent; + kref_put(&mtd->refcnt, mtd_device_release); } - master->usecount--; - if (master->_put_device) master->_put_device(master); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index b5eefeabf310..b014861a06a6 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -12,6 +12,7 @@ int __must_check add_mtd_device(struct mtd_info *mtd); int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +void release_mtd_partition(struct mtd_info *mtd); struct mtd_partitions; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 12ca4f19cb14..8175f6d9c790 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -31,6 +31,12 @@ static inline void free_partition(struct mtd_info *mtd) kfree(mtd); } +void release_mtd_partition(struct mtd_info *mtd) +{ + list_del_init(&mtd->part.node); + free_partition(mtd); +} + static struct mtd_info *allocate_partition(struct mtd_info *parent, const struct mtd_partition *part, int partno, uint64_t cur_offset) @@ -313,9 +319,6 @@ static int __mtd_del_partition(struct mtd_info *mtd) if (err) return err; - list_del(&child->part.node); - free_partition(mtd); - return 0; } @@ -341,9 +344,6 @@ static int __del_mtd_partitions(struct mtd_info *mtd) err = ret; continue; } - - list_del(&child->part.node); - free_partition(child); } return err; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 157357ec1441..1217c9d8d69d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -373,7 +373,7 @@ struct mtd_info { struct module *owner; struct device dev; - int usecount; + struct kref refcnt; struct mtd_debug_info dbg; struct nvmem_device *nvmem; -- 2.26.2