From: Gu Zheng
ohos inclusion
category: bugfix
issue: #I3ZXZF
CVE: NA
-----------------------------------------------
add the new functions mtd_table_mutex_lock/unlock to instead the
mutex_lock(&mtd_table_mutex)/mutex_unlock(&mtd_table_mutex),this
modification can avoid the deadlock when insmod ftl.ko
the deadlock is caused by the commit 857814ee65db ("mtd: fix: avoid
race condition when accessing mtd->usecount")
the process is as follows:
init_ftl
register_mtd_blktrans
mutex_lock(&mtd_table_mutex) //mtd_table_mutex locked
ftl_add_mtd
add_mtd_blktrans_dev
device_add_disk
register_disk
blkdev_get
__blkdev_get
blktrans_open
mutex_lock(&mtd_table_mutex) //dead lock
so we add the mtd_table_mutex_owner to record current process.
if the lock is locked before , it can jump the lock where will deadlock.
it solved the above issue,also can prevent some mtd_table_mutex
deadlock undiscovered.
Signed-off-by: Gu Zheng
Acked-by: Miao Xie
Signed-off-by: Ding Tianhong
Signed-off-by: zhangyi (F)
Reviewed-by: Hou Tao
conflict:
drivers/mtd/mtdcore.c
drivers/mtd/mtdcore.h
Signed-off-by: Ye Bin
Reviewed-by: Hou Tao
Signed-off-by: Chen Jun
Signed-off-by: Yu Changchun
---
drivers/mtd/mtd_blkdevs.c | 31 +++++++---------
drivers/mtd/mtdcore.c | 75 ++++++++++++++++++++++++++++-----------
drivers/mtd/mtdcore.h | 4 ++-
3 files changed, 70 insertions(+), 40 deletions(-)
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 0c05f77f9b21..37f4e169d305 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -209,7 +209,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
if (!dev)
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
mutex_lock(&dev->lock);
if (dev->open)
@@ -235,7 +235,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
unlock:
dev->open++;
mutex_unlock(&dev->lock);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
blktrans_dev_put(dev);
return ret;
@@ -246,7 +246,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
module_put(dev->tr->owner);
kref_put(&dev->ref, blktrans_dev_release);
mutex_unlock(&dev->lock);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
blktrans_dev_put(dev);
return ret;
}
@@ -258,7 +258,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
if (!dev)
return;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
mutex_lock(&dev->lock);
if (--dev->open)
@@ -274,7 +274,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
}
unlock:
mutex_unlock(&dev->lock);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
blktrans_dev_put(dev);
}
@@ -345,10 +345,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
struct gendisk *gd;
int ret;
- if (mutex_trylock(&mtd_table_mutex)) {
- mutex_unlock(&mtd_table_mutex);
- BUG();
- }
+ mtd_table_assert_mutex_locked();
mutex_lock(&blktrans_ref_mutex);
list_for_each_entry(d, &tr->devs, list) {
@@ -479,11 +476,7 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
{
unsigned long flags;
- if (mutex_trylock(&mtd_table_mutex)) {
- mutex_unlock(&mtd_table_mutex);
- BUG();
- }
-
+ mtd_table_assert_mutex_locked();
if (old->disk_attributes)
sysfs_remove_group(&disk_to_dev(old->disk)->kobj,
old->disk_attributes);
@@ -557,13 +550,13 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
register_mtd_user(&blktrans_notifier);
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
ret = register_blkdev(tr->major, tr->name);
if (ret < 0) {
printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
tr->name, tr->major, ret);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return ret;
}
@@ -579,7 +572,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
if (mtd->type != MTD_ABSENT)
tr->add_mtd(tr, mtd);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return 0;
}
@@ -587,7 +580,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
struct mtd_blktrans_dev *dev, *next;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
/* Remove it from the list of active majors */
list_del(&tr->list);
@@ -596,7 +589,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
tr->remove_dev(dev);
unregister_blkdev(tr->major, tr->name);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
BUG_ON(!list_empty(&tr->devs));
return 0;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 1c8c40728678..37bd3171157a 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -70,8 +70,9 @@ static DEFINE_IDR(mtd_idr);
/* These are exported solely for the purpose of mtd_blkdevs.c. You
should not use them for _anything_ else */
-DEFINE_MUTEX(mtd_table_mutex);
-EXPORT_SYMBOL_GPL(mtd_table_mutex);
+static DEFINE_MUTEX(mtd_table_mutex);
+static int mtd_table_mutex_depth;
+static struct task_struct *mtd_table_mutex_owner;
struct mtd_info *__mtd_next_device(int i)
{
@@ -84,6 +85,40 @@ static LIST_HEAD(mtd_notifiers);
#define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
+void mtd_table_mutex_lock(void)
+{
+ if (mtd_table_mutex_owner != current) {
+ mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_owner = current;
+ }
+ mtd_table_mutex_depth++;
+}
+EXPORT_SYMBOL_GPL(mtd_table_mutex_lock);
+
+
+void mtd_table_mutex_unlock(void)
+{
+ if (mtd_table_mutex_owner != current) {
+ pr_err("MTD:lock_owner is %s, but current is %s\n",
+ mtd_table_mutex_owner->comm, current->comm);
+ BUG();
+ }
+ if (--mtd_table_mutex_depth == 0) {
+ mtd_table_mutex_owner = NULL;
+ mutex_unlock(&mtd_table_mutex);
+ }
+}
+EXPORT_SYMBOL_GPL(mtd_table_mutex_unlock);
+
+void mtd_table_assert_mutex_locked(void)
+{
+ if (mtd_table_mutex_owner != current) {
+ pr_err("MTD:lock_owner is %s, but current is %s\n",
+ mtd_table_mutex_owner->comm, current->comm);
+ BUG();
+ }
+}
+EXPORT_SYMBOL_GPL(mtd_table_assert_mutex_locked);
/* REVISIT once MTD uses the driver model better, whoever allocates
* the mtd_info will probably want to use the release() hook...
*/
@@ -610,7 +645,7 @@ int add_mtd_device(struct mtd_info *mtd)
!master->pairing || master->_writev))
return -EINVAL;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
if (i < 0) {
@@ -686,7 +721,7 @@ int add_mtd_device(struct mtd_info *mtd)
list_for_each_entry(not, &mtd_notifiers, list)
not->add(mtd);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
/* We _know_ we aren't being removed, because
our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it
@@ -700,7 +735,7 @@ int add_mtd_device(struct mtd_info *mtd)
of_node_put(mtd_get_of_node(mtd));
idr_remove(&mtd_idr, i);
fail_locked:
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return error;
}
@@ -719,7 +754,7 @@ int del_mtd_device(struct mtd_info *mtd)
int ret;
struct mtd_notifier *not;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
debugfs_remove_recursive(mtd->dbg.dfs_dir);
@@ -752,7 +787,7 @@ int del_mtd_device(struct mtd_info *mtd)
}
out_error:
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return ret;
}
@@ -894,7 +929,7 @@ void register_mtd_user (struct mtd_notifier *new)
{
struct mtd_info *mtd;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
list_add(&new->list, &mtd_notifiers);
@@ -903,7 +938,7 @@ void register_mtd_user (struct mtd_notifier *new)
mtd_for_each_device(mtd)
new->add(mtd);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
}
EXPORT_SYMBOL_GPL(register_mtd_user);
@@ -920,7 +955,7 @@ int unregister_mtd_user (struct mtd_notifier *old)
{
struct mtd_info *mtd;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
module_put(THIS_MODULE);
@@ -928,7 +963,7 @@ int unregister_mtd_user (struct mtd_notifier *old)
old->remove(mtd);
list_del(&old->list);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return 0;
}
EXPORT_SYMBOL_GPL(unregister_mtd_user);
@@ -949,7 +984,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
struct mtd_info *ret = NULL, *other;
int err = -ENODEV;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
if (num == -1) {
mtd_for_each_device(other) {
@@ -973,7 +1008,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
if (err)
ret = ERR_PTR(err);
out:
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(get_mtd_device);
@@ -1020,7 +1055,7 @@ struct mtd_info *get_mtd_device_nm(const char *name)
int err = -ENODEV;
struct mtd_info *mtd = NULL, *other;
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
mtd_for_each_device(other) {
if (!strcmp(name, other->name)) {
@@ -1036,20 +1071,20 @@ struct mtd_info *get_mtd_device_nm(const char *name)
if (err)
goto out_unlock;
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return mtd;
out_unlock:
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(get_mtd_device_nm);
void put_mtd_device(struct mtd_info *mtd)
{
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
__put_mtd_device(mtd);
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
}
EXPORT_SYMBOL_GPL(put_mtd_device);
@@ -2161,13 +2196,13 @@ static int mtd_proc_show(struct seq_file *m, void *v)
struct mtd_info *mtd;
seq_puts(m, "dev: size erasesize name\n");
- mutex_lock(&mtd_table_mutex);
+ mtd_table_mutex_lock();
mtd_for_each_device(mtd) {
seq_printf(m, "mtd%d: %8.8llx %8.8x \"%s\"\n",
mtd->index, (unsigned long long)mtd->size,
mtd->erasesize, mtd->name);
}
- mutex_unlock(&mtd_table_mutex);
+ mtd_table_mutex_unlock();
return 0;
}
#endif /* CONFIG_PROC_FS */
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
index b5eefeabf310..40c5f104794c 100644
--- a/drivers/mtd/mtdcore.h
+++ b/drivers/mtd/mtdcore.h
@@ -4,7 +4,6 @@
* You should not use them for _anything_ else.
*/
-extern struct mutex mtd_table_mutex;
extern struct backing_dev_info *mtd_bdi;
struct mtd_info *__mtd_next_device(int i);
@@ -22,6 +21,9 @@ void mtd_part_parser_cleanup(struct mtd_partitions *parts);
int __init init_mtdchar(void);
void __exit cleanup_mtdchar(void);
+extern void mtd_table_mutex_lock(void);
+extern void mtd_table_mutex_unlock(void);
+extern void mtd_table_assert_mutex_locked(void);
#define mtd_for_each_device(mtd) \
for ((mtd) = __mtd_next_device(0); \
--
2.22.0