summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHuang Shijie <b32955@freescale.com>2010-12-23 17:05:03 +0800
committerFrank Li <Frank.Li@freescale.com>2010-12-24 15:59:18 +0800
commit00d336a50e6a06f41e95675b64c26c76a7f01bc9 (patch)
treef1591376cd3c9a2d4bb575c78c04d4558fe4c8d8
parent3b667325bf959716cd04b3161c8f31bde24ad4f8 (diff)
ENGR00136994 UBI : system halt when detaching UBI in NFS environment
The problem is caused by: ubi double free ubiblk_dev structrue, cause the list and memory mess up. This patch changes field `m` in the ubiblk_dev{} from the mtd_blktrans_dev{} to pointer. Also add a mutex for protecting the global ubiblk_devices. Signed-off-by: Huang Shijie <b32955@freescale.com>
-rw-r--r--drivers/mtd/ubiblock.c70
1 files changed, 53 insertions, 17 deletions
diff --git a/drivers/mtd/ubiblock.c b/drivers/mtd/ubiblock.c
index aee5a73b805e..d71c1f800d09 100644
--- a/drivers/mtd/ubiblock.c
+++ b/drivers/mtd/ubiblock.c
@@ -35,6 +35,7 @@
#include "mtdcore.h"
static LIST_HEAD(ubiblk_devices);
+static struct mutex ubiblk_devices_lock;
/**
* ubiblk_dev - the structure representing translation layer
@@ -50,7 +51,7 @@ static LIST_HEAD(ubiblk_devices);
* @cache_size: cache size in bytes, usually equal to LEB size
*/
struct ubiblk_dev {
- struct mtd_blktrans_dev m;
+ struct mtd_blktrans_dev *m;
int ubi_num;
int ubi_vol;
@@ -293,6 +294,21 @@ out:
return err;
}
+static struct ubiblk_dev *ubiblk_find_by_ptr(struct mtd_blktrans_dev *m)
+{
+ struct ubiblk_dev *pos;
+
+ mutex_lock(&ubiblk_devices_lock);
+ list_for_each_entry(pos, &ubiblk_devices, list)
+ if (pos->m == m) {
+ mutex_unlock(&ubiblk_devices_lock);
+ return pos;
+ }
+ mutex_unlock(&ubiblk_devices_lock);
+ BUG();
+ return NULL;
+}
+
/**
* ubiblock_writesect - write the sector
*
@@ -302,7 +318,7 @@ out:
static int ubiblock_writesect(struct mtd_blktrans_dev *mbd,
unsigned long block, char *buf)
{
- struct ubiblk_dev *u = container_of(mbd, struct ubiblk_dev, m);
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
if (unlikely(!u->cache_data)) {
u->cache_data = vmalloc(u->cache_size);
@@ -321,7 +337,7 @@ static int ubiblock_writesect(struct mtd_blktrans_dev *mbd,
static int ubiblock_readsect(struct mtd_blktrans_dev *mbd,
unsigned long block, char *buf)
{
- struct ubiblk_dev *u = container_of(mbd, struct ubiblk_dev, m);
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
if (unlikely(!u->cache_data)) {
u->cache_data = vmalloc(u->cache_size);
@@ -340,7 +356,7 @@ static int ubiblk_flush_locked(struct ubiblk_dev *u)
static int ubiblock_flush(struct mtd_blktrans_dev *mbd)
{
- struct ubiblk_dev *u = container_of(mbd, struct ubiblk_dev, m);
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
mutex_lock(&u->cache_mutex);
ubiblk_flush_locked(u);
@@ -351,7 +367,7 @@ static int ubiblock_flush(struct mtd_blktrans_dev *mbd)
static int ubiblock_open(struct mtd_blktrans_dev *mbd)
{
- struct ubiblk_dev *u = container_of(mbd, struct ubiblk_dev, m);
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
if (u->usecount == 0) {
u->ubi = ubi_open_volume(u->ubi_num, u->ubi_vol,
@@ -365,7 +381,7 @@ static int ubiblock_open(struct mtd_blktrans_dev *mbd)
static int ubiblock_release(struct mtd_blktrans_dev *mbd)
{
- struct ubiblk_dev *u = container_of(mbd, struct ubiblk_dev, m);
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
if (--u->usecount == 0) {
mutex_lock(&u->cache_mutex);
@@ -379,6 +395,7 @@ static int ubiblock_release(struct mtd_blktrans_dev *mbd)
return 0;
}
+
/*
* sysfs routines. The ubiblk creates two entries under /sys/block/ubiblkX:
* - volume, R/O, which is read like "ubi0:volume_name"
@@ -393,7 +410,7 @@ ssize_t volume_show(struct device *dev,
{
struct gendisk *gd = dev_to_disk(dev);
struct mtd_blktrans_dev *m = gd->private_data;
- struct ubiblk_dev *u = container_of(m, struct ubiblk_dev, m);
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(m);
return sprintf(buf, "%d:%d\n", u->ubi_num, u->ubi_vol);
}
@@ -410,7 +427,7 @@ ssize_t unbind_store(struct device *dev, struct device_attribute *attr,
{
struct gendisk *gd = dev_to_disk(dev);
struct mtd_blktrans_dev *m = gd->private_data;
- struct ubiblk_dev *u = container_of(m, struct ubiblk_dev, m);
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(m);
INIT_WORK(&u->unbind, ubiblk_unbind);
schedule_work(&u->unbind);
@@ -465,10 +482,18 @@ static void *ubiblk_add_locked(int ubi_num, int ubi_vol_id)
u = ERR_PTR(-ENOMEM);
goto out;
}
+ u->m = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL);
+ if (!u->m) {
+ kfree(u);
+ u = ERR_PTR(-ENOMEM);
+ goto out;
+ }
ubi = ubi_open_volume(ubi_num, ubi_vol_id, UBI_READONLY);
if (IS_ERR(ubi)) {
pr_err("cannot open the volume\n");
+ kfree(u->m);
+ kfree(u);
u = (void *)ubi;
goto out;
}
@@ -478,10 +503,10 @@ static void *ubiblk_add_locked(int ubi_num, int ubi_vol_id)
pr_debug("adding volume of size %d (used_size %lld), LEB size %d\n",
uvi.size, uvi.used_bytes, uvi.usable_leb_size);
- u->m.mtd = ubi->vol->ubi->mtd;
- u->m.devnum = -1;
- u->m.size = uvi.used_bytes >> 9;
- u->m.tr = &ubiblock_tr;
+ u->m->mtd = ubi->vol->ubi->mtd;
+ u->m->devnum = -1;
+ u->m->size = uvi.used_bytes >> 9;
+ u->m->tr = &ubiblock_tr;
ubi_close_volume(ubi);
@@ -495,9 +520,11 @@ static void *ubiblk_add_locked(int ubi_num, int ubi_vol_id)
u->usecount = 0;
INIT_LIST_HEAD(&u->list);
+ mutex_lock(&ubiblk_devices_lock);
list_add_tail(&u->list, &ubiblk_devices);
- add_mtd_blktrans_dev(&u->m);
- ubiblk_sysfs(u->m.disk, true);
+ mutex_unlock(&ubiblk_devices_lock);
+ add_mtd_blktrans_dev(u->m);
+ ubiblk_sysfs(u->m->disk, true);
out:
return u;
}
@@ -515,9 +542,13 @@ static int ubiblk_del_locked(struct ubiblk_dev *u)
{
if (u->usecount != 0)
return -EBUSY;
- ubiblk_sysfs(u->m.disk, false);
- del_mtd_blktrans_dev(&u->m);
+ ubiblk_sysfs(u->m->disk, false);
+ del_mtd_blktrans_dev(u->m);
+
+ mutex_lock(&ubiblk_devices_lock);
list_del(&u->list);
+ mutex_unlock(&ubiblk_devices_lock);
+
BUG_ON(u->cache_data != NULL); /* who did not free the cache ?! */
kfree(u);
return 0;
@@ -527,9 +558,13 @@ static struct ubiblk_dev *ubiblk_find(int num, int vol)
{
struct ubiblk_dev *pos;
+ mutex_lock(&ubiblk_devices_lock);
list_for_each_entry(pos, &ubiblk_devices, list)
- if (pos->ubi_num == num && pos->ubi_vol == vol)
+ if (pos->ubi_num == num && pos->ubi_vol == vol) {
+ mutex_unlock(&ubiblk_devices_lock);
return pos;
+ }
+ mutex_unlock(&ubiblk_devices_lock);
return NULL;
}
@@ -563,6 +598,7 @@ static int __init ubiblock_init(void)
{
int r;
+ mutex_init(&ubiblk_devices_lock);
r = register_mtd_blktrans(&ubiblock_tr);
if (r)
goto out;