Skip to content

Commit

Permalink
Merge branch 'linux-next' of git://git.infradead.org/ubi-2.6
Browse files Browse the repository at this point in the history
* 'linux-next' of git://git.infradead.org/ubi-2.6: (21 commits)
  UBI: add reboot notifier
  UBI: handle more error codes
  UBI: fix multiple spelling typos
  UBI: fix kmem_cache_free on error patch
  UBI: print amount of reserved PEBs
  UBI: improve messages in the WL worker
  UBI: make gluebi a separate module
  UBI: remove built-in gluebi
  UBI: add notification API
  UBI: do not switch to R/O mode on read errors
  UBI: fix and clean-up error paths in WL worker
  UBI: introduce new constants
  UBI: fix race condition
  UBI: minor serialization fix
  UBI: do not panic if volume check fails
  UBI: add dump_stack in checking code
  UBI: fix races in I/O debugging checks
  UBI: small debugging code optimization
  UBI: improve debugging messages
  UBI: re-name volumes_mutex to device_mutex
  ...
  • Loading branch information
Linus Torvalds committed Jun 17, 2009
2 parents b7c142d + d9dd088 commit b069e8e
Show file tree
Hide file tree
Showing 13 changed files with 922 additions and 335 deletions.
13 changes: 7 additions & 6 deletions drivers/mtd/ubi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,16 @@ config MTD_UBI_BEB_RESERVE
reserved. Leave the default value if unsure.

config MTD_UBI_GLUEBI
bool "Emulate MTD devices"
tristate "MTD devices emulation driver (gluebi)"
default n
depends on MTD_UBI
help
This option enables MTD devices emulation on top of UBI volumes: for
each UBI volumes an MTD device is created, and all I/O to this MTD
device is redirected to the UBI volume. This is handy to make
MTD-oriented software (like JFFS2) work on top of UBI. Do not enable
this if no legacy software will be used.
This option enables gluebi - an additional driver which emulates MTD
devices on top of UBI volumes: for each UBI volumes an MTD device is
created, and all I/O to this MTD device is redirected to the UBI
volume. This is handy to make MTD-oriented software (like JFFS2)
work on top of UBI. Do not enable this unless you use legacy
software.

source "drivers/mtd/ubi/Kconfig.debug"
endmenu
2 changes: 1 addition & 1 deletion drivers/mtd/ubi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o scan.o
ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
161 changes: 150 additions & 11 deletions drivers/mtd/ubi/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <linux/miscdevice.h>
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/reboot.h>
#include "ubi.h"

/* Maximum length of the 'mtd=' parameter */
Expand Down Expand Up @@ -121,6 +122,94 @@ static struct device_attribute dev_bgt_enabled =
static struct device_attribute dev_mtd_num =
__ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL);

/**
* ubi_volume_notify - send a volume change notification.
* @ubi: UBI device description object
* @vol: volume description object of the changed volume
* @ntype: notification type to send (%UBI_VOLUME_ADDED, etc)
*
* This is a helper function which notifies all subscribers about a volume
* change event (creation, removal, re-sizing, re-naming, updating). Returns
* zero in case of success and a negative error code in case of failure.
*/
int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol, int ntype)
{
struct ubi_notification nt;

ubi_do_get_device_info(ubi, &nt.di);
ubi_do_get_volume_info(ubi, vol, &nt.vi);
return blocking_notifier_call_chain(&ubi_notifiers, ntype, &nt);
}

/**
* ubi_notify_all - send a notification to all volumes.
* @ubi: UBI device description object
* @ntype: notification type to send (%UBI_VOLUME_ADDED, etc)
* @nb: the notifier to call
*
* This function walks all volumes of UBI device @ubi and sends the @ntype
* notification for each volume. If @nb is %NULL, then all registered notifiers
* are called, otherwise only the @nb notifier is called. Returns the number of
* sent notifications.
*/
int ubi_notify_all(struct ubi_device *ubi, int ntype, struct notifier_block *nb)
{
struct ubi_notification nt;
int i, count = 0;

ubi_do_get_device_info(ubi, &nt.di);

mutex_lock(&ubi->device_mutex);
for (i = 0; i < ubi->vtbl_slots; i++) {
/*
* Since the @ubi->device is locked, and we are not going to
* change @ubi->volumes, we do not have to lock
* @ubi->volumes_lock.
*/
if (!ubi->volumes[i])
continue;

ubi_do_get_volume_info(ubi, ubi->volumes[i], &nt.vi);
if (nb)
nb->notifier_call(nb, ntype, &nt);
else
blocking_notifier_call_chain(&ubi_notifiers, ntype,
&nt);
count += 1;
}
mutex_unlock(&ubi->device_mutex);

return count;
}

/**
* ubi_enumerate_volumes - send "add" notification for all existing volumes.
* @nb: the notifier to call
*
* This function walks all UBI devices and volumes and sends the
* %UBI_VOLUME_ADDED notification for each volume. If @nb is %NULL, then all
* registered notifiers are called, otherwise only the @nb notifier is called.
* Returns the number of sent notifications.
*/
int ubi_enumerate_volumes(struct notifier_block *nb)
{
int i, count = 0;

/*
* Since the @ubi_devices_mutex is locked, and we are not going to
* change @ubi_devices, we do not have to lock @ubi_devices_lock.
*/
for (i = 0; i < UBI_MAX_DEVICES; i++) {
struct ubi_device *ubi = ubi_devices[i];

if (!ubi)
continue;
count += ubi_notify_all(ubi, UBI_VOLUME_ADDED, nb);
}

return count;
}

/**
* ubi_get_device - get UBI device.
* @ubi_num: UBI device number
Expand Down Expand Up @@ -380,7 +469,7 @@ static void free_user_volumes(struct ubi_device *ubi)
* @ubi: UBI device description object
*
* 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 failes.
* case of failure. Note, this function destroys all volumes if it fails.
*/
static int uif_init(struct ubi_device *ubi)
{
Expand Down Expand Up @@ -632,6 +721,15 @@ static int io_init(struct ubi_device *ubi)
return -EINVAL;
}

/*
* Set maximum amount of physical erroneous eraseblocks to be 10%.
* Erroneous PEB are those which have read errors.
*/
ubi->max_erroneous = ubi->peb_count / 10;
if (ubi->max_erroneous < 16)
ubi->max_erroneous = 16;
dbg_msg("max_erroneous %d", ubi->max_erroneous);

/*
* It may happen that EC and VID headers are situated in one minimal
* I/O unit. In this case we can only accept this UBI image in
Expand Down Expand Up @@ -725,6 +823,34 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
return 0;
}

/**
* ubi_reboot_notifier - halt UBI transactions immediately prior to a reboot.
* @n: reboot notifier object
* @state: SYS_RESTART, SYS_HALT, or SYS_POWER_OFF
* @cmd: pointer to command string for RESTART2
*
* This function stops the UBI background thread so that the flash device
* remains quiescent when Linux restarts the system. Any queued work will be
* discarded, but this function will block until do_work() finishes if an
* operation is already in progress.
*
* This function solves a real-life problem observed on NOR flashes when an
* PEB erase operation starts, then the system is rebooted before the erase is
* finishes, and the boot loader gets confused and dies. So we prefer to finish
* the ongoing operation before rebooting.
*/
static int ubi_reboot_notifier(struct notifier_block *n, unsigned long state,
void *cmd)
{
struct ubi_device *ubi;

ubi = container_of(n, struct ubi_device, reboot_notifier);
if (ubi->bgt_thread)
kthread_stop(ubi->bgt_thread);
ubi_sync(ubi->ubi_num);
return NOTIFY_DONE;
}

/**
* ubi_attach_mtd_dev - attach an MTD device.
* @mtd: MTD device description object
Expand Down Expand Up @@ -806,8 +932,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)

mutex_init(&ubi->buf_mutex);
mutex_init(&ubi->ckvol_mutex);
mutex_init(&ubi->mult_mutex);
mutex_init(&ubi->volumes_mutex);
mutex_init(&ubi->device_mutex);
spin_lock_init(&ubi->volumes_lock);

ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num);
Expand All @@ -825,7 +950,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if (!ubi->peb_buf2)
goto out_free;

#ifdef CONFIG_MTD_UBI_DEBUG
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
mutex_init(&ubi->dbg_buf_mutex);
ubi->dbg_peb_buf = vmalloc(ubi->peb_size);
if (!ubi->dbg_peb_buf)
Expand Down Expand Up @@ -872,11 +997,23 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
ubi->beb_rsvd_pebs);
ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);

/*
* The below lock makes sure we do not race with 'ubi_thread()' which
* checks @ubi->thread_enabled. Otherwise we may fail to wake it up.
*/
spin_lock(&ubi->wl_lock);
if (!DBG_DISABLE_BGT)
ubi->thread_enabled = 1;
wake_up_process(ubi->bgt_thread);
spin_unlock(&ubi->wl_lock);

/* Flash device priority is 0 - UBI needs to shut down first */
ubi->reboot_notifier.priority = 1;
ubi->reboot_notifier.notifier_call = ubi_reboot_notifier;
register_reboot_notifier(&ubi->reboot_notifier);

ubi_devices[ubi_num] = ubi;
ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL);
return ubi_num;

out_uif:
Expand All @@ -892,7 +1029,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
out_free:
vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2);
#ifdef CONFIG_MTD_UBI_DEBUG
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
vfree(ubi->dbg_peb_buf);
#endif
kfree(ubi);
Expand All @@ -919,13 +1056,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
return -EINVAL;

spin_lock(&ubi_devices_lock);
ubi = ubi_devices[ubi_num];
if (!ubi) {
spin_unlock(&ubi_devices_lock);
ubi = ubi_get_device(ubi_num);
if (!ubi)
return -EINVAL;
}

spin_lock(&ubi_devices_lock);
put_device(&ubi->dev);
ubi->ref_count -= 1;
if (ubi->ref_count) {
if (!anyway) {
spin_unlock(&ubi_devices_lock);
Expand All @@ -939,12 +1076,14 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
spin_unlock(&ubi_devices_lock);

ubi_assert(ubi_num == ubi->ubi_num);
ubi_notify_all(ubi, UBI_VOLUME_REMOVED, NULL);
dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);

/*
* Before freeing anything, we have to stop the background thread to
* prevent it from doing anything on this device while we are freeing.
*/
unregister_reboot_notifier(&ubi->reboot_notifier);
if (ubi->bgt_thread)
kthread_stop(ubi->bgt_thread);

Expand All @@ -961,7 +1100,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
put_mtd_device(ubi->mtd);
vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2);
#ifdef CONFIG_MTD_UBI_DEBUG
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
vfree(ubi->dbg_peb_buf);
#endif
ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
Expand Down
32 changes: 16 additions & 16 deletions drivers/mtd/ubi/cdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ static int vol_cdev_open(struct inode *inode, struct file *file)
else
mode = UBI_READONLY;

dbg_gen("open volume %d, mode %d", vol_id, mode);
dbg_gen("open device %d, volume %d, mode %d",
ubi_num, vol_id, mode);

desc = ubi_open_volume(ubi_num, vol_id, mode);
if (IS_ERR(desc))
Expand All @@ -128,7 +129,8 @@ static int vol_cdev_release(struct inode *inode, struct file *file)
struct ubi_volume_desc *desc = file->private_data;
struct ubi_volume *vol = desc->vol;

dbg_gen("release volume %d, mode %d", vol->vol_id, desc->mode);
dbg_gen("release device %d, volume %d, mode %d",
vol->ubi->ubi_num, vol->vol_id, desc->mode);

if (vol->updating) {
ubi_warn("update of volume %d not finished, volume is damaged",
Expand Down Expand Up @@ -393,7 +395,7 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
vol->corrupted = 1;
}
vol->checked = 1;
ubi_gluebi_updated(vol);
ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED);
revoke_exclusive(desc, UBI_READWRITE);
}

Expand Down Expand Up @@ -558,7 +560,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
break;
}

/* Set volume property command*/
/* Set volume property command */
case UBI_IOCSETPROP:
{
struct ubi_set_prop_req req;
Expand All @@ -571,9 +573,9 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
}
switch (req.property) {
case UBI_PROP_DIRECT_WRITE:
mutex_lock(&ubi->volumes_mutex);
mutex_lock(&ubi->device_mutex);
desc->vol->direct_writes = !!req.value;
mutex_unlock(&ubi->volumes_mutex);
mutex_unlock(&ubi->device_mutex);
break;
default:
err = -EINVAL;
Expand Down Expand Up @@ -810,9 +812,9 @@ static int rename_volumes(struct ubi_device *ubi,
re->desc->vol->vol_id, re->desc->vol->name);
}

mutex_lock(&ubi->volumes_mutex);
mutex_lock(&ubi->device_mutex);
err = ubi_rename_volumes(ubi, &rename_list);
mutex_unlock(&ubi->volumes_mutex);
mutex_unlock(&ubi->device_mutex);

out_free:
list_for_each_entry_safe(re, re1, &rename_list, list) {
Expand Down Expand Up @@ -856,9 +858,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
if (err)
break;

mutex_lock(&ubi->volumes_mutex);
mutex_lock(&ubi->device_mutex);
err = ubi_create_volume(ubi, &req);
mutex_unlock(&ubi->volumes_mutex);
mutex_unlock(&ubi->device_mutex);
if (err)
break;

Expand Down Expand Up @@ -887,9 +889,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
break;
}

mutex_lock(&ubi->volumes_mutex);
mutex_lock(&ubi->device_mutex);
err = ubi_remove_volume(desc, 0);
mutex_unlock(&ubi->volumes_mutex);
mutex_unlock(&ubi->device_mutex);

/*
* The volume is deleted (unless an error occurred), and the
Expand Down Expand Up @@ -926,9 +928,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
pebs = div_u64(req.bytes + desc->vol->usable_leb_size - 1,
desc->vol->usable_leb_size);

mutex_lock(&ubi->volumes_mutex);
mutex_lock(&ubi->device_mutex);
err = ubi_resize_volume(desc, pebs);
mutex_unlock(&ubi->volumes_mutex);
mutex_unlock(&ubi->device_mutex);
ubi_close_volume(desc);
break;
}
Expand All @@ -952,9 +954,7 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
break;
}

mutex_lock(&ubi->mult_mutex);
err = rename_volumes(ubi, req);
mutex_unlock(&ubi->mult_mutex);
kfree(req);
break;
}
Expand Down
Loading

0 comments on commit b069e8e

Please sign in to comment.