mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 21:57:43 +00:00
UBI: fix attaching error path
In the error path of 'ubi_attach_mtd_dev()' we have a tricky situation: we have to release things differently depending on at which point the failure happening. Namely, if @ubi->dev is not initialized, we have to free everything ourselves. But if it was, we should not free the @ubi object, because it will be freed in the 'dev_release()' function. And we did not get this situation right. This patch introduces additional argument to the 'uif_init()' function. On exit, this argument indicates whether the final 'free(ubi)' will happen in 'dev_release()' or not. So the caller always knows how to properly release the resources. Impact: all memory is now correctly released when UBI fails to attach an MTD device. Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
This commit is contained in:
parent
f9b0080e10
commit
0bf1c4399a
1 changed files with 30 additions and 33 deletions
|
@ -365,11 +365,13 @@ static void dev_release(struct device *dev)
|
||||||
/**
|
/**
|
||||||
* ubi_sysfs_init - initialize sysfs for an UBI device.
|
* ubi_sysfs_init - initialize sysfs for an UBI device.
|
||||||
* @ubi: UBI device description object
|
* @ubi: UBI device description object
|
||||||
|
* @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was
|
||||||
|
* taken
|
||||||
*
|
*
|
||||||
* This function returns zero in case of success and a negative error code in
|
* This function returns zero in case of success and a negative error code in
|
||||||
* case of failure.
|
* case of failure.
|
||||||
*/
|
*/
|
||||||
static int ubi_sysfs_init(struct ubi_device *ubi)
|
static int ubi_sysfs_init(struct ubi_device *ubi, int *ref)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -381,6 +383,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
*ref = 1;
|
||||||
err = device_create_file(&ubi->dev, &dev_eraseblock_size);
|
err = device_create_file(&ubi->dev, &dev_eraseblock_size);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
@ -436,7 +439,7 @@ static void ubi_sysfs_close(struct ubi_device *ubi)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kill_volumes - destroy all volumes.
|
* kill_volumes - destroy all user volumes.
|
||||||
* @ubi: UBI device description object
|
* @ubi: UBI device description object
|
||||||
*/
|
*/
|
||||||
static void kill_volumes(struct ubi_device *ubi)
|
static void kill_volumes(struct ubi_device *ubi)
|
||||||
|
@ -448,37 +451,30 @@ static void kill_volumes(struct ubi_device *ubi)
|
||||||
ubi_free_volume(ubi, ubi->volumes[i]);
|
ubi_free_volume(ubi, ubi->volumes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* free_user_volumes - free all user volumes.
|
|
||||||
* @ubi: UBI device description object
|
|
||||||
*
|
|
||||||
* Normally the volumes are freed at the release function of the volume device
|
|
||||||
* objects. However, on error paths the volumes have to be freed before the
|
|
||||||
* device objects have been initialized.
|
|
||||||
*/
|
|
||||||
static void free_user_volumes(struct ubi_device *ubi)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ubi->vtbl_slots; i++)
|
|
||||||
if (ubi->volumes[i]) {
|
|
||||||
kfree(ubi->volumes[i]->eba_tbl);
|
|
||||||
kfree(ubi->volumes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* uif_init - initialize user interfaces for an UBI device.
|
* uif_init - initialize user interfaces for an UBI device.
|
||||||
* @ubi: UBI device description object
|
* @ubi: UBI device description object
|
||||||
|
* @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was
|
||||||
|
* taken, otherwise set to %0
|
||||||
|
*
|
||||||
|
* This function initializes various user interfaces for an UBI device. If the
|
||||||
|
* initialization fails at an early stage, this function frees all the
|
||||||
|
* resources it allocated, returns an error, and @ref is set to %0. However,
|
||||||
|
* if the initialization fails after the UBI device was registered in the
|
||||||
|
* driver core subsystem, this function takes a reference to @ubi->dev, because
|
||||||
|
* otherwise the release function ('dev_release()') would free whole @ubi
|
||||||
|
* object. The @ref argument is set to %1 in this case. The caller has to put
|
||||||
|
* this reference.
|
||||||
*
|
*
|
||||||
* This function returns zero in case of success and a negative error code in
|
* This function returns zero in case of success and a negative error code in
|
||||||
* case of failure. Note, this function destroys all volumes if it fails.
|
* case of failure.
|
||||||
*/
|
*/
|
||||||
static int uif_init(struct ubi_device *ubi)
|
static int uif_init(struct ubi_device *ubi, int *ref)
|
||||||
{
|
{
|
||||||
int i, err;
|
int i, err;
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
|
|
||||||
|
*ref = 0;
|
||||||
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
|
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -506,7 +502,7 @@ static int uif_init(struct ubi_device *ubi)
|
||||||
goto out_unreg;
|
goto out_unreg;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ubi_sysfs_init(ubi);
|
err = ubi_sysfs_init(ubi, ref);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_sysfs;
|
goto out_sysfs;
|
||||||
|
|
||||||
|
@ -524,6 +520,8 @@ static int uif_init(struct ubi_device *ubi)
|
||||||
out_volumes:
|
out_volumes:
|
||||||
kill_volumes(ubi);
|
kill_volumes(ubi);
|
||||||
out_sysfs:
|
out_sysfs:
|
||||||
|
if (*ref)
|
||||||
|
get_device(&ubi->dev);
|
||||||
ubi_sysfs_close(ubi);
|
ubi_sysfs_close(ubi);
|
||||||
cdev_del(&ubi->cdev);
|
cdev_del(&ubi->cdev);
|
||||||
out_unreg:
|
out_unreg:
|
||||||
|
@ -877,7 +875,7 @@ static int ubi_reboot_notifier(struct notifier_block *n, unsigned long state,
|
||||||
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
|
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
|
||||||
{
|
{
|
||||||
struct ubi_device *ubi;
|
struct ubi_device *ubi;
|
||||||
int i, err, do_free = 1;
|
int i, err, ref = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if we already have the same MTD device attached.
|
* Check if we already have the same MTD device attached.
|
||||||
|
@ -977,9 +975,9 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
|
||||||
goto out_detach;
|
goto out_detach;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = uif_init(ubi);
|
err = uif_init(ubi, &ref);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_nofree;
|
goto out_detach;
|
||||||
|
|
||||||
ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
|
ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
|
||||||
if (IS_ERR(ubi->bgt_thread)) {
|
if (IS_ERR(ubi->bgt_thread)) {
|
||||||
|
@ -1027,12 +1025,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
|
||||||
|
|
||||||
out_uif:
|
out_uif:
|
||||||
uif_close(ubi);
|
uif_close(ubi);
|
||||||
out_nofree:
|
|
||||||
do_free = 0;
|
|
||||||
out_detach:
|
out_detach:
|
||||||
ubi_wl_close(ubi);
|
ubi_wl_close(ubi);
|
||||||
if (do_free)
|
|
||||||
free_user_volumes(ubi);
|
|
||||||
free_internal_volumes(ubi);
|
free_internal_volumes(ubi);
|
||||||
vfree(ubi->vtbl);
|
vfree(ubi->vtbl);
|
||||||
out_free:
|
out_free:
|
||||||
|
@ -1041,7 +1035,10 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
|
||||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||||
vfree(ubi->dbg_peb_buf);
|
vfree(ubi->dbg_peb_buf);
|
||||||
#endif
|
#endif
|
||||||
kfree(ubi);
|
if (ref)
|
||||||
|
put_device(&ubi->dev);
|
||||||
|
else
|
||||||
|
kfree(ubi);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,7 +1095,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get a reference to the device in order to prevent 'dev_release()'
|
* Get a reference to the device in order to prevent 'dev_release()'
|
||||||
* from freeing @ubi object.
|
* from freeing the @ubi object.
|
||||||
*/
|
*/
|
||||||
get_device(&ubi->dev);
|
get_device(&ubi->dev);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue