Commit b069e8ed authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'linux-next' of git://git.infradead.org/ubi-2.6

* '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
  ...
parents b7c142db d9dd0887
...@@ -49,15 +49,16 @@ config MTD_UBI_BEB_RESERVE ...@@ -49,15 +49,16 @@ config MTD_UBI_BEB_RESERVE
reserved. Leave the default value if unsure. reserved. Leave the default value if unsure.
config MTD_UBI_GLUEBI config MTD_UBI_GLUEBI
bool "Emulate MTD devices" tristate "MTD devices emulation driver (gluebi)"
default n default n
depends on MTD_UBI depends on MTD_UBI
help help
This option enables MTD devices emulation on top of UBI volumes: for This option enables gluebi - an additional driver which emulates MTD
each UBI volumes an MTD device is created, and all I/O to this MTD devices on top of UBI volumes: for each UBI volumes an MTD device is
device is redirected to the UBI volume. This is handy to make created, and all I/O to this MTD device is redirected to the UBI
MTD-oriented software (like JFFS2) work on top of UBI. Do not enable volume. This is handy to make MTD-oriented software (like JFFS2)
this if no legacy software will be used. work on top of UBI. Do not enable this unless you use legacy
software.
source "drivers/mtd/ubi/Kconfig.debug" source "drivers/mtd/ubi/Kconfig.debug"
endmenu endmenu
...@@ -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 ...@@ -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-y += misc.o
ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/reboot.h>
#include "ubi.h" #include "ubi.h"
/* Maximum length of the 'mtd=' parameter */ /* Maximum length of the 'mtd=' parameter */
...@@ -121,6 +122,94 @@ static struct device_attribute dev_bgt_enabled = ...@@ -121,6 +122,94 @@ static struct device_attribute dev_bgt_enabled =
static struct device_attribute dev_mtd_num = static struct device_attribute dev_mtd_num =
__ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); __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_get_device - get UBI device.
* @ubi_num: UBI device number * @ubi_num: UBI device number
...@@ -380,7 +469,7 @@ static void free_user_volumes(struct ubi_device *ubi) ...@@ -380,7 +469,7 @@ static void free_user_volumes(struct ubi_device *ubi)
* @ubi: UBI device description object * @ubi: UBI device description object
* *
* 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 failes. * case of failure. Note, this function destroys all volumes if it fails.
*/ */
static int uif_init(struct ubi_device *ubi) static int uif_init(struct ubi_device *ubi)
{ {
...@@ -632,6 +721,15 @@ static int io_init(struct ubi_device *ubi) ...@@ -632,6 +721,15 @@ static int io_init(struct ubi_device *ubi)
return -EINVAL; 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 * 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 * I/O unit. In this case we can only accept this UBI image in
...@@ -725,6 +823,34 @@ static int autoresize(struct ubi_device *ubi, int vol_id) ...@@ -725,6 +823,34 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
return 0; 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. * ubi_attach_mtd_dev - attach an MTD device.
* @mtd: MTD device description object * @mtd: MTD device description object
...@@ -806,8 +932,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ...@@ -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->buf_mutex);
mutex_init(&ubi->ckvol_mutex); mutex_init(&ubi->ckvol_mutex);
mutex_init(&ubi->mult_mutex); mutex_init(&ubi->device_mutex);
mutex_init(&ubi->volumes_mutex);
spin_lock_init(&ubi->volumes_lock); spin_lock_init(&ubi->volumes_lock);
ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num); ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num);
...@@ -825,7 +950,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ...@@ -825,7 +950,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if (!ubi->peb_buf2) if (!ubi->peb_buf2)
goto out_free; goto out_free;
#ifdef CONFIG_MTD_UBI_DEBUG #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
mutex_init(&ubi->dbg_buf_mutex); mutex_init(&ubi->dbg_buf_mutex);
ubi->dbg_peb_buf = vmalloc(ubi->peb_size); ubi->dbg_peb_buf = vmalloc(ubi->peb_size);
if (!ubi->dbg_peb_buf) if (!ubi->dbg_peb_buf)
...@@ -872,11 +997,23 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ...@@ -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->beb_rsvd_pebs);
ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); 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) if (!DBG_DISABLE_BGT)
ubi->thread_enabled = 1; ubi->thread_enabled = 1;
wake_up_process(ubi->bgt_thread); 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_devices[ubi_num] = ubi;
ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL);
return ubi_num; return ubi_num;
out_uif: out_uif:
...@@ -892,7 +1029,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ...@@ -892,7 +1029,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
out_free: out_free:
vfree(ubi->peb_buf1); vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2); vfree(ubi->peb_buf2);
#ifdef CONFIG_MTD_UBI_DEBUG #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
vfree(ubi->dbg_peb_buf); vfree(ubi->dbg_peb_buf);
#endif #endif
kfree(ubi); kfree(ubi);
...@@ -919,13 +1056,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ...@@ -919,13 +1056,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
return -EINVAL; return -EINVAL;
spin_lock(&ubi_devices_lock); ubi = ubi_get_device(ubi_num);
ubi = ubi_devices[ubi_num]; if (!ubi)
if (!ubi) {
spin_unlock(&ubi_devices_lock);
return -EINVAL; return -EINVAL;
}
spin_lock(&ubi_devices_lock);
put_device(&ubi->dev);
ubi->ref_count -= 1;
if (ubi->ref_count) { if (ubi->ref_count) {
if (!anyway) { if (!anyway) {
spin_unlock(&ubi_devices_lock); spin_unlock(&ubi_devices_lock);
...@@ -939,12 +1076,14 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ...@@ -939,12 +1076,14 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
spin_unlock(&ubi_devices_lock); spin_unlock(&ubi_devices_lock);
ubi_assert(ubi_num == ubi->ubi_num); 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); dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
/* /*
* Before freeing anything, we have to stop the background thread to * Before freeing anything, we have to stop the background thread to
* prevent it from doing anything on this device while we are freeing. * prevent it from doing anything on this device while we are freeing.
*/ */
unregister_reboot_notifier(&ubi->reboot_notifier);
if (ubi->bgt_thread) if (ubi->bgt_thread)
kthread_stop(ubi->bgt_thread); kthread_stop(ubi->bgt_thread);
...@@ -961,7 +1100,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ...@@ -961,7 +1100,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
put_mtd_device(ubi->mtd); put_mtd_device(ubi->mtd);
vfree(ubi->peb_buf1); vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2); vfree(ubi->peb_buf2);
#ifdef CONFIG_MTD_UBI_DEBUG #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
vfree(ubi->dbg_peb_buf); vfree(ubi->dbg_peb_buf);
#endif #endif
ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
......
...@@ -113,7 +113,8 @@ static int vol_cdev_open(struct inode *inode, struct file *file) ...@@ -113,7 +113,8 @@ static int vol_cdev_open(struct inode *inode, struct file *file)
else else
mode = UBI_READONLY; 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); desc = ubi_open_volume(ubi_num, vol_id, mode);
if (IS_ERR(desc)) if (IS_ERR(desc))
...@@ -128,7 +129,8 @@ static int vol_cdev_release(struct inode *inode, struct file *file) ...@@ -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_desc *desc = file->private_data;
struct ubi_volume *vol = desc->vol; 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) { if (vol->updating) {
ubi_warn("update of volume %d not finished, volume is damaged", ubi_warn("update of volume %d not finished, volume is damaged",
...@@ -393,7 +395,7 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, ...@@ -393,7 +395,7 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
vol->corrupted = 1; vol->corrupted = 1;
} }
vol->checked = 1; vol->checked = 1;
ubi_gluebi_updated(vol); ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED);
revoke_exclusive(desc, UBI_READWRITE); revoke_exclusive(desc, UBI_READWRITE);
} }
...@@ -558,7 +560,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -558,7 +560,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
break; break;
} }
/* Set volume property command*/ /* Set volume property command */
case UBI_IOCSETPROP: case UBI_IOCSETPROP:
{ {
struct ubi_set_prop_req req; struct ubi_set_prop_req req;
...@@ -571,9 +573,9 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -571,9 +573,9 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
} }
switch (req.property) { switch (req.property) {
case UBI_PROP_DIRECT_WRITE: case UBI_PROP_DIRECT_WRITE:
mutex_lock(&ubi->volumes_mutex); mutex_lock(&ubi->device_mutex);
desc->vol->direct_writes = !!req.value; desc->vol->direct_writes = !!req.value;
mutex_unlock(&ubi->volumes_mutex); mutex_unlock(&ubi->device_mutex);
break; break;
default: default:
err = -EINVAL; err = -EINVAL;
...@@ -810,9 +812,9 @@ static int rename_volumes(struct ubi_device *ubi, ...@@ -810,9 +812,9 @@ static int rename_volumes(struct ubi_device *ubi,
re->desc->vol->vol_id, re->desc->vol->name); 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); err = ubi_rename_volumes(ubi, &rename_list);
mutex_unlock(&ubi->volumes_mutex); mutex_unlock(&ubi->device_mutex);
out_free: out_free:
list_for_each_entry_safe(re, re1, &rename_list, list) { list_for_each_entry_safe(re, re1, &rename_list, list) {
...@@ -856,9 +858,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -856,9 +858,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
if (err) if (err)
break; break;
mutex_lock(&ubi->volumes_mutex); mutex_lock(&ubi->device_mutex);
err = ubi_create_volume(ubi, &req); err = ubi_create_volume(ubi, &req);
mutex_unlock(&ubi->volumes_mutex); mutex_unlock(&ubi->device_mutex);
if (err) if (err)
break; break;
...@@ -887,9 +889,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -887,9 +889,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
break; break;
} }
mutex_lock(&ubi->volumes_mutex); mutex_lock(&ubi->device_mutex);
err = ubi_remove_volume(desc, 0); 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 * The volume is deleted (unless an error occurred), and the
...@@ -926,9 +928,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -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, pebs = div_u64(req.bytes + desc->vol->usable_leb_size - 1,
desc->vol->usable_leb_size); desc->vol->usable_leb_size);
mutex_lock(&ubi->volumes_mutex); mutex_lock(&ubi->device_mutex);
err = ubi_resize_volume(desc, pebs); err = ubi_resize_volume(desc, pebs);
mutex_unlock(&ubi->volumes_mutex); mutex_unlock(&ubi->device_mutex);
ubi_close_volume(desc); ubi_close_volume(desc);
break; break;
} }
...@@ -952,9 +954,7 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -952,9 +954,7 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
break; break;
} }
mutex_lock(&ubi->mult_mutex);
err = rename_volumes(ubi, req); err = rename_volumes(ubi, req);
mutex_unlock(&ubi->mult_mutex);
kfree(req); kfree(req);
break; break;
} }
......
...@@ -419,8 +419,9 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ...@@ -419,8 +419,9 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
* not implemented. * not implemented.
*/ */
if (err == UBI_IO_BAD_VID_HDR) { if (err == UBI_IO_BAD_VID_HDR) {
ubi_warn("bad VID header at PEB %d, LEB" ubi_warn("corrupted VID header at PEB "
"%d:%d", pnum, vol_id, lnum); "%d, LEB %d:%d", pnum, vol_id,
lnum);
err = -EBADMSG; err = -EBADMSG;
} else } else
ubi_ro_mode(ubi); ubi_ro_mode(ubi);
...@@ -939,6 +940,33 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -939,6 +940,33 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
goto retry; goto retry;
} }
/**
* is_error_sane - check whether a read error is sane.
* @err: code of the error happened during reading
*
* This is a helper function for 'ubi_eba_copy_leb()' which is called when we
* cannot read data from the target PEB (an error @err happened). If the error
* code is sane, then we treat this error as non-fatal. Otherwise the error is
* fatal and UBI will be switched to R/O mode later.
*
* The idea is that we try not to switch to R/O mode if the read error is
* something which suggests there was a real read problem. E.g., %-EIO. Or a
* memory allocation failed (-%ENOMEM). Otherwise, it is safer to switch to R/O
* mode, simply because we do not know what happened at the MTD level, and we
* cannot handle this. E.g., the underlying driver may have become crazy, and
* it is safer to switch to R/O mode to preserve the data.
*
* And bear in mind, this is about reading from the target PEB, i.e. the PEB
* which we have just written.
*/
static int is_error_sane(int err)
{
if (err == -EIO || err == -ENOMEM || err == UBI_IO_BAD_VID_HDR ||
err == -ETIMEDOUT)
return 0;
return 1;
}
/** /**
* ubi_eba_copy_leb - copy logical eraseblock. * ubi_eba_copy_leb - copy logical eraseblock.
* @ubi: UBI device description object * @ubi: UBI device description object
...@@ -950,12 +978,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -950,12 +978,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
* physical eraseblock @to. The @vid_hdr buffer may be changed by this * physical eraseblock @to. The @vid_hdr buffer may be changed by this
* function. Returns: * function. Returns:
* o %0 in case of success; * o %0 in case of success;
* o %1 if the operation was canceled because the volume is being deleted * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_CANCEL_BITFLIPS, etc;
* or because the PEB was put meanwhile;
* o %2 if the operation was canceled because there was a write error to the
* target PEB;
* o %-EAGAIN if the operation was canceled because a bit-flip was detected
* in the target PEB;
* o a negative error code in case of failure. * o a negative error code in case of failure.
*/ */
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
...@@ -968,7 +991,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -968,7 +991,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
vol_id = be32_to_cpu(vid_hdr->vol_id); vol_id = be32_to_cpu(vid_hdr->vol_id);
lnum = be32_to_cpu(vid_hdr->lnum); lnum = be32_to_cpu(vid_hdr->lnum);
dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); dbg_wl("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to);
if (vid_hdr->vol_type == UBI_VID_STATIC) { if (vid_hdr->vol_type == UBI_VID_STATIC) {
data_size = be32_to_cpu(vid_hdr->data_size); data_size = be32_to_cpu(vid_hdr->data_size);
...@@ -986,13 +1009,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -986,13 +1009,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
* be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish.
*/ */
vol = ubi->volumes[idx]; vol = ubi->volumes[idx];
spin_unlock(&ubi->volumes_lock);
if (!vol) { if (!vol) {
/* No need to do further work, cancel */ /* No need to do further work, cancel */
dbg_eba("volume %d is being removed, cancel", vol_id); dbg_wl("volume %d is being removed, cancel", vol_id);
spin_unlock(&ubi->volumes_lock); return MOVE_CANCEL_RACE;
return 1;
} }
spin_unlock(&ubi->volumes_lock);
/* /*
* We do not want anybody to write to this logical eraseblock while we * We do not want anybody to write to this logical eraseblock while we
...@@ -1004,12 +1026,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1004,12 +1026,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
* (@from). This task locks the LEB and goes sleep in the * (@from). This task locks the LEB and goes sleep in the
* 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are
* holding @ubi->move_mutex and go sleep on the LEB lock. So, if the * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the
* LEB is already locked, we just do not move it and return %1. * LEB is already locked, we just do not move it and return
* %MOVE_CANCEL_RACE, which means that UBI will re-try, but later.
*/ */
err = leb_write_trylock(ubi, vol_id, lnum); err = leb_write_trylock(ubi, vol_id, lnum);
if (err) { if (err) {
dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); dbg_wl("contention on LEB %d:%d, cancel", vol_id, lnum);
return err; return MOVE_CANCEL_RACE;
} }
/* /*
...@@ -1018,25 +1041,26 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1018,25 +1041,26 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
* cancel it. * cancel it.
*/ */
if (vol->eba_tbl[lnum] != from) { if (vol->eba_tbl[lnum] != from) {
dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " dbg_wl("LEB %d:%d is no longer mapped to PEB %d, mapped to "
"PEB %d, cancel", vol_id, lnum, from, "PEB %d, cancel", vol_id, lnum, from,
vol->eba_tbl[lnum]); vol->eba_tbl[lnum]);
err = 1; err = MOVE_CANCEL_RACE;
goto out_unlock_leb; goto out_unlock_leb;
} }
/* /*
* OK, now the LEB is locked and we can safely start moving it. Since * OK, now the LEB is locked and we can safely start moving it. Since
* this function utilizes the @ubi->peb1_buf buffer which is shared * this function utilizes the @ubi->peb_buf1 buffer which is shared
* with some other functions, so lock the buffer by taking the * with some other functions - we lock the buffer by taking the
* @ubi->buf_mutex. * @ubi->buf_mutex.
*/ */
mutex_lock(&ubi->buf_mutex); mutex_lock(&ubi->buf_mutex);
dbg_eba("read %d bytes of data", aldata_size); dbg_wl("read %d bytes of data", aldata_size);
err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size);
if (err && err != UBI_IO_BITFLIPS) { if (err && err != UBI_IO_BITFLIPS) {
ubi_warn("error %d while reading data from PEB %d", ubi_warn("error %d while reading data from PEB %d",
err, from); err, from);
err = MOVE_SOURCE_RD_ERR;
goto out_unlock_buf; goto out_unlock_buf;
} }
...@@ -1059,7 +1083,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1059,7 +1083,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
cond_resched(); cond_resched();
/* /*
* It may turn out to me that the whole @from physical eraseblock * It may turn out to be that the whole @from physical eraseblock
* contains only 0xFF bytes. Then we have to only write the VID header * contains only 0xFF bytes. Then we have to only write the VID header
* and do not write any data. This also means we should not set * and do not write any data. This also means we should not set
* @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc. * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc.
...@@ -1074,7 +1098,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1074,7 +1098,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
if (err) { if (err) {
if (err == -EIO) if (err == -EIO)
err = 2; err = MOVE_TARGET_WR_ERR;
goto out_unlock_buf; goto out_unlock_buf;
} }
...@@ -1083,10 +1107,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1083,10 +1107,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
/* Read the VID header back and check if it was written correctly */ /* Read the VID header back and check if it was written correctly */
err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1);
if (err) { if (err) {
if (err != UBI_IO_BITFLIPS) if (err != UBI_IO_BITFLIPS) {
ubi_warn("cannot read VID header back from PEB %d", to); ubi_warn("error %d while reading VID header back from "
else "PEB %d", err, to);
err = -EAGAIN; if (is_error_sane(err))
err = MOVE_TARGET_RD_ERR;
} else
err = MOVE_CANCEL_BITFLIPS;
goto out_unlock_buf; goto out_unlock_buf;
} }
...@@ -1094,7 +1121,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1094,7 +1121,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
if (err) { if (err) {
if (err == -EIO) if (err == -EIO)
err = 2; err = MOVE_TARGET_WR_ERR;
goto out_unlock_buf; goto out_unlock_buf;
} }
...@@ -1107,11 +1134,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ...@@ -1107,11 +1134,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size); err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size);
if (err) { if (err) {
if (err != UBI_IO_BITFLIPS) if (err != UBI_IO_BITFLIPS) {
ubi_warn("cannot read data back from PEB %d", ubi_warn("error %d while reading data back "
to); "from PEB %d", err, to);
else if (is_error_sane(err))
err = -EAGAIN; err = MOVE_TARGET_RD_ERR;
} else
err = MOVE_CANCEL_BITFLIPS;
goto out_unlock_buf; goto out_unlock_buf;
} }
......
This diff is collapsed.
...@@ -100,6 +100,7 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, ...@@ -100,6 +100,7 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
const struct ubi_vid_hdr *vid_hdr); const struct ubi_vid_hdr *vid_hdr);
static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
int len); int len);
static int paranoid_check_empty(struct ubi_device *ubi, int pnum);
#else #else
#define paranoid_check_not_bad(ubi, pnum) 0 #define paranoid_check_not_bad(ubi, pnum) 0
#define paranoid_check_peb_ec_hdr(ubi, pnum) 0 #define paranoid_check_peb_ec_hdr(ubi, pnum) 0
...@@ -107,6 +108,7 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, ...@@ -107,6 +108,7 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
#define paranoid_check_peb_vid_hdr(ubi, pnum) 0 #define paranoid_check_peb_vid_hdr(ubi, pnum) 0
#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 #define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0
#define paranoid_check_all_ff(ubi, pnum, offset, len) 0 #define paranoid_check_all_ff(ubi, pnum, offset, len) 0
#define paranoid_check_empty(ubi, pnum) 0
#endif #endif
/** /**
...@@ -670,11 +672,6 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, ...@@ -670,11 +672,6 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
if (read_err != -EBADMSG && if (read_err != -EBADMSG &&
check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) {
/* The physical eraseblock is supposedly empty */ /* The physical eraseblock is supposedly empty */
/*
* The below is just a paranoid check, it has to be
* compiled out if paranoid checks are disabled.
*/
err = paranoid_check_all_ff(ubi, pnum, 0, err = paranoid_check_all_ff(ubi, pnum, 0,
ubi->peb_size); ubi->peb_size);
if (err) if (err)
...@@ -902,7 +899,7 @@ static int validate_vid_hdr(const struct ubi_device *ubi, ...@@ -902,7 +899,7 @@ static int validate_vid_hdr(const struct ubi_device *ubi,
* o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected
* and corrected by the flash driver; this is harmless but may indicate that * and corrected by the flash driver; this is harmless but may indicate that
* this eraseblock may become bad soon; * this eraseblock may become bad soon;
* o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC * o %UBI_IO_BAD_VID_HDR if the volume identifier header is corrupted (a CRC
* error detected); * error detected);
* o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID
* header there); * header there);
...@@ -955,8 +952,7 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, ...@@ -955,8 +952,7 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
* The below is just a paranoid check, it has to be * The below is just a paranoid check, it has to be
* compiled out if paranoid checks are disabled. * compiled out if paranoid checks are disabled.
*/ */
err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start, err = paranoid_check_empty(ubi, pnum);
ubi->leb_size);
if (err) if (err)
return err > 0 ? UBI_IO_BAD_VID_HDR : err; return err > 0 ? UBI_IO_BAD_VID_HDR : err;
...@@ -1280,4 +1276,74 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, ...@@ -1280,4 +1276,74 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
return err; return err;
} }
/**
* paranoid_check_empty - whether a PEB is empty.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to check
*
* This function makes sure PEB @pnum is empty, which means it contains only
* %0xFF data bytes. Returns zero if the PEB is empty, %1 if not, and a
* negative error code in case of failure.
*
* Empty PEBs have the EC header, and do not have the VID header. The caller of
* this function should have already made sure the PEB does not have the VID
* header. However, this function re-checks that, because it is possible that
* the header and data has already been written to the PEB.
*
* Let's consider a possible scenario. Suppose there are 2 tasks - A and B.
* Task A is in 'wear_leveling_worker()'. It is reading VID header of PEB X to
* find which LEB it corresponds to. PEB X is currently unmapped, and has no
* VID header. Task B is trying to write to PEB X.
*
* Task A: in 'ubi_io_read_vid_hdr()': reads the VID header from PEB X. The
* read data contain all 0xFF bytes;
* Task B: writes VID header and some data to PEB X;
* Task A: assumes PEB X is empty, calls 'paranoid_check_empty()'. And if we
* do not re-read the VID header, and do not cancel the checking if it
* is there, we fail.
*/
static int paranoid_check_empty(struct ubi_device *ubi, int pnum)
{
int err, offs = ubi->vid_hdr_aloffset, len = ubi->vid_hdr_alsize;
size_t read;
uint32_t magic;
const struct ubi_vid_hdr *vid_hdr;
mutex_lock(&ubi->dbg_buf_mutex);
err = ubi->mtd->read(ubi->mtd, offs, len, &read, ubi->dbg_peb_buf);
if (err && err != -EUCLEAN) {
ubi_err("error %d while reading %d bytes from PEB %d:%d, "
"read %zd bytes", err, len, pnum, offs, read);
goto error;
}
vid_hdr = ubi->dbg_peb_buf;
magic = be32_to_cpu(vid_hdr->magic);
if (magic == UBI_VID_HDR_MAGIC)
/* The PEB contains VID header, so it is not empty */
goto out;
err = check_pattern(ubi->dbg_peb_buf, 0xFF, len);
if (err == 0) {
ubi_err("flash region at PEB %d:%d, length %d does not "
"contain all 0xFF bytes", pnum, offs, len);
goto fail;
}
out:
mutex_unlock(&ubi->dbg_buf_mutex);
return 0;
fail:
ubi_err("paranoid check failed for PEB %d", pnum);
ubi_msg("hex dump of the %d-%d region", offs, offs + len);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
ubi->dbg_peb_buf, len, 1);
err = 1;
error:
ubi_dbg_dump_stack();
mutex_unlock(&ubi->dbg_buf_mutex);
return err;
}
#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ #endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
...@@ -25,6 +25,24 @@ ...@@ -25,6 +25,24 @@
#include <asm/div64.h> #include <asm/div64.h>
#include "ubi.h" #include "ubi.h"
/**
* ubi_do_get_device_info - get information about UBI device.
* @ubi: UBI device description object
* @di: the information is stored here
*
* This function is the same as 'ubi_get_device_info()', but it assumes the UBI
* device is locked and cannot disappear.
*/
void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di)
{
di->ubi_num = ubi->ubi_num;
di->leb_size = ubi->leb_size;
di->min_io_size = ubi->min_io_size;
di->ro_mode = ubi->ro_mode;
di->cdev = ubi->cdev.dev;
}
EXPORT_SYMBOL_GPL(ubi_do_get_device_info);
/** /**
* ubi_get_device_info - get information about UBI device. * ubi_get_device_info - get information about UBI device.
* @ubi_num: UBI device number * @ubi_num: UBI device number
...@@ -39,33 +57,24 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) ...@@ -39,33 +57,24 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di)
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
return -EINVAL; return -EINVAL;
ubi = ubi_get_device(ubi_num); ubi = ubi_get_device(ubi_num);
if (!ubi) if (!ubi)
return -ENODEV; return -ENODEV;
ubi_do_get_device_info(ubi, di);
di->ubi_num = ubi->ubi_num;
di->leb_size = ubi->leb_size;
di->min_io_size = ubi->min_io_size;
di->ro_mode = ubi->ro_mode;
di->cdev = ubi->cdev.dev;
ubi_put_device(ubi); ubi_put_device(ubi);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ubi_get_device_info); EXPORT_SYMBOL_GPL(ubi_get_device_info);
/** /**
* ubi_get_volume_info - get information about UBI volume. * ubi_do_get_volume_info - get information about UBI volume.
* @desc: volume descriptor * @ubi: UBI device description object
* @vol: volume description object
* @vi: the information is stored here * @vi: the information is stored here
*/ */
void ubi_get_volume_info(struct ubi_volume_desc *desc, void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
struct ubi_volume_info *vi) struct ubi_volume_info *vi)
{ {
const struct ubi_volume *vol = desc->vol;
const struct ubi_device *ubi = vol->ubi;
vi->vol_id = vol->vol_id; vi->vol_id = vol->vol_id;
vi->ubi_num = ubi->ubi_num; vi->ubi_num = ubi->ubi_num;
vi->size = vol->reserved_pebs; vi->size = vol->reserved_pebs;
...@@ -79,6 +88,17 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc, ...@@ -79,6 +88,17 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc,
vi->name = vol->name; vi->name = vol->name;
vi->cdev = vol->cdev.dev; vi->cdev = vol->cdev.dev;
} }
/**
* ubi_get_volume_info - get information about UBI volume.
* @desc: volume descriptor
* @vi: the information is stored here
*/
void ubi_get_volume_info(struct ubi_volume_desc *desc,
struct ubi_volume_info *vi)
{
ubi_do_get_volume_info(desc->vol->ubi, desc->vol, vi);
}
EXPORT_SYMBOL_GPL(ubi_get_volume_info); EXPORT_SYMBOL_GPL(ubi_get_volume_info);
/** /**
...@@ -106,7 +126,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) ...@@ -106,7 +126,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
struct ubi_device *ubi; struct ubi_device *ubi;
struct ubi_volume *vol; struct ubi_volume *vol;
dbg_gen("open device %d volume %d, mode %d", ubi_num, vol_id, mode); dbg_gen("open device %d, volume %d, mode %d", ubi_num, vol_id, mode);
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -196,6 +216,8 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) ...@@ -196,6 +216,8 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
kfree(desc); kfree(desc);
out_put_ubi: out_put_ubi:
ubi_put_device(ubi); ubi_put_device(ubi);
dbg_err("cannot open device %d, volume %d, error %d",
ubi_num, vol_id, err);
return ERR_PTR(err); return ERR_PTR(err);
} }
EXPORT_SYMBOL_GPL(ubi_open_volume); EXPORT_SYMBOL_GPL(ubi_open_volume);
...@@ -215,7 +237,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, ...@@ -215,7 +237,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
struct ubi_device *ubi; struct ubi_device *ubi;
struct ubi_volume_desc *ret; struct ubi_volume_desc *ret;
dbg_gen("open volume %s, mode %d", name, mode); dbg_gen("open device %d, volume %s, mode %d", ubi_num, name, mode);
if (!name) if (!name)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -266,7 +288,8 @@ void ubi_close_volume(struct ubi_volume_desc *desc) ...@@ -266,7 +288,8 @@ void ubi_close_volume(struct ubi_volume_desc *desc)
struct ubi_volume *vol = desc->vol; struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi; struct ubi_device *ubi = vol->ubi;
dbg_gen("close volume %d, mode %d", vol->vol_id, desc->mode); dbg_gen("close device %d, volume %d, mode %d",
ubi->ubi_num, vol->vol_id, desc->mode);
spin_lock(&ubi->volumes_lock); spin_lock(&ubi->volumes_lock);
switch (desc->mode) { switch (desc->mode) {
...@@ -558,7 +581,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) ...@@ -558,7 +581,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
EXPORT_SYMBOL_GPL(ubi_leb_unmap); EXPORT_SYMBOL_GPL(ubi_leb_unmap);
/** /**
* ubi_leb_map - map logical erasblock to a physical eraseblock. * ubi_leb_map - map logical eraseblock to a physical eraseblock.
* @desc: volume descriptor * @desc: volume descriptor
* @lnum: logical eraseblock number * @lnum: logical eraseblock number
* @dtype: expected data type * @dtype: expected data type
...@@ -656,3 +679,59 @@ int ubi_sync(int ubi_num) ...@@ -656,3 +679,59 @@ int ubi_sync(int ubi_num)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ubi_sync); EXPORT_SYMBOL_GPL(ubi_sync);
BLOCKING_NOTIFIER_HEAD(ubi_notifiers);
/**
* ubi_register_volume_notifier - register a volume notifier.
* @nb: the notifier description object
* @ignore_existing: if non-zero, do not send "added" notification for all
* already existing volumes
*
* This function registers a volume notifier, which means that
* 'nb->notifier_call()' will be invoked when an UBI volume is created,
* removed, re-sized, re-named, or updated. The first argument of the function
* is the notification type. The second argument is pointer to a
* &struct ubi_notification object which describes the notification event.
* Using UBI API from the volume notifier is prohibited.
*
* This function returns zero in case of success and a negative error code
* in case of failure.
*/
int ubi_register_volume_notifier(struct notifier_block *nb,
int ignore_existing)
{
int err;
err = blocking_notifier_chain_register(&ubi_notifiers, nb);
if (err != 0)
return err;
if (ignore_existing)
return 0;
/*
* We are going to walk all UBI devices and all volumes, and
* notify the user about existing volumes by the %UBI_VOLUME_ADDED
* event. We have to lock the @ubi_devices_mutex to make sure UBI
* devices do not disappear.
*/
mutex_lock(&ubi_devices_mutex);
ubi_enumerate_volumes(nb);
mutex_unlock(&ubi_devices_mutex);
return err;
}
EXPORT_SYMBOL_GPL(ubi_register_volume_notifier);
/**
* ubi_unregister_volume_notifier - unregister the volume notifier.
* @nb: the notifier description object
*
* This function unregisters volume notifier @nm and returns zero in case of
* success and a negative error code in case of failure.
*/
int ubi_unregister_volume_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&ubi_notifiers, nb);
}
EXPORT_SYMBOL_GPL(ubi_unregister_volume_notifier);
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/notifier.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/ubi.h> #include <linux/mtd/ubi.h>
...@@ -100,6 +101,28 @@ enum { ...@@ -100,6 +101,28 @@ enum {
UBI_IO_BITFLIPS UBI_IO_BITFLIPS
}; };
/*
* Return codes of the 'ubi_eba_copy_leb()' function.
*
* MOVE_CANCEL_RACE: canceled because the volume is being deleted, the source
* PEB was put meanwhile, or there is I/O on the source PEB
* MOVE_SOURCE_RD_ERR: canceled because there was a read error from the source
* PEB
* MOVE_TARGET_RD_ERR: canceled because there was a read error from the target
* PEB
* MOVE_TARGET_WR_ERR: canceled because there was a write error to the target
* PEB
* MOVE_CANCEL_BITFLIPS: canceled because a bit-flip was detected in the
* target PEB
*/
enum {
MOVE_CANCEL_RACE = 1,
MOVE_SOURCE_RD_ERR,
MOVE_TARGET_RD_ERR,
MOVE_TARGET_WR_ERR,
MOVE_CANCEL_BITFLIPS,
};
/** /**
* struct ubi_wl_entry - wear-leveling entry. * struct ubi_wl_entry - wear-leveling entry.
* @u.rb: link in the corresponding (free/used) RB-tree * @u.rb: link in the corresponding (free/used) RB-tree
...@@ -208,10 +231,6 @@ struct ubi_volume_desc; ...@@ -208,10 +231,6 @@ struct ubi_volume_desc;
* @changing_leb: %1 if the atomic LEB change ioctl command is in progress * @changing_leb: %1 if the atomic LEB change ioctl command is in progress
* @direct_writes: %1 if direct writes are enabled for this volume * @direct_writes: %1 if direct writes are enabled for this volume
* *
* @gluebi_desc: gluebi UBI volume descriptor
* @gluebi_refcount: reference count of the gluebi MTD device
* @gluebi_mtd: MTD device description object of the gluebi MTD device
*
* The @corrupted field indicates that the volume's contents is corrupted. * The @corrupted field indicates that the volume's contents is corrupted.
* Since UBI protects only static volumes, this field is not relevant to * Since UBI protects only static volumes, this field is not relevant to
* dynamic volumes - it is user's responsibility to assure their data * dynamic volumes - it is user's responsibility to assure their data
...@@ -255,17 +274,6 @@ struct ubi_volume { ...@@ -255,17 +274,6 @@ struct ubi_volume {
unsigned int updating:1; unsigned int updating:1;
unsigned int changing_leb:1; unsigned int changing_leb:1;
unsigned int direct_writes:1; unsigned int direct_writes:1;
#ifdef CONFIG_MTD_UBI_GLUEBI
/*
* Gluebi-related stuff may be compiled out.
* Note: this should not be built into UBI but should be a separate
* ubimtd driver which works on top of UBI and emulates MTD devices.
*/
struct ubi_volume_desc *gluebi_desc;
int gluebi_refcount;
struct mtd_info gluebi_mtd;
#endif
}; };
/** /**
...@@ -305,9 +313,9 @@ struct ubi_wl_entry; ...@@ -305,9 +313,9 @@ struct ubi_wl_entry;
* @vtbl_slots: how many slots are available in the volume table * @vtbl_slots: how many slots are available in the volume table
* @vtbl_size: size of the volume table in bytes * @vtbl_size: size of the volume table in bytes
* @vtbl: in-RAM volume table copy * @vtbl: in-RAM volume table copy
* @volumes_mutex: protects on-flash volume table and serializes volume * @device_mutex: protects on-flash volume table and serializes volume
* changes, like creation, deletion, update, re-size, * creation, deletion, update, re-size, re-name and set
* re-name and set property * property
* *
* @max_ec: current highest erase counter value * @max_ec: current highest erase counter value
* @mean_ec: current mean erase counter value * @mean_ec: current mean erase counter value
...@@ -318,14 +326,15 @@ struct ubi_wl_entry; ...@@ -318,14 +326,15 @@ struct ubi_wl_entry;
* @alc_mutex: serializes "atomic LEB change" operations * @alc_mutex: serializes "atomic LEB change" operations
* *
* @used: RB-tree of used physical eraseblocks * @used: RB-tree of used physical eraseblocks
* @erroneous: RB-tree of erroneous used physical eraseblocks
* @free: RB-tree of free physical eraseblocks * @free: RB-tree of free physical eraseblocks
* @scrub: RB-tree of physical eraseblocks which need scrubbing * @scrub: RB-tree of physical eraseblocks which need scrubbing
* @pq: protection queue (contain physical eraseblocks which are temporarily * @pq: protection queue (contain physical eraseblocks which are temporarily
* protected from the wear-leveling worker) * protected from the wear-leveling worker)
* @pq_head: protection queue head * @pq_head: protection queue head
* @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from, * @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from,
* @move_to, @move_to_put @erase_pending, @wl_scheduled and @works * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works,
* fields * @erroneous, and @erroneous_peb_count fields
* @move_mutex: serializes eraseblock moves * @move_mutex: serializes eraseblock moves
* @work_sem: synchronizes the WL worker with use tasks * @work_sem: synchronizes the WL worker with use tasks
* @wl_scheduled: non-zero if the wear-leveling was scheduled * @wl_scheduled: non-zero if the wear-leveling was scheduled
...@@ -339,12 +348,15 @@ struct ubi_wl_entry; ...@@ -339,12 +348,15 @@ struct ubi_wl_entry;
* @bgt_thread: background thread description object * @bgt_thread: background thread description object
* @thread_enabled: if the background thread is enabled * @thread_enabled: if the background thread is enabled
* @bgt_name: background thread name * @bgt_name: background thread name
* @reboot_notifier: notifier to terminate background thread before rebooting
* *
* @flash_size: underlying MTD device size (in bytes) * @flash_size: underlying MTD device size (in bytes)
* @peb_count: count of physical eraseblocks on the MTD device * @peb_count: count of physical eraseblocks on the MTD device
* @peb_size: physical eraseblock size * @peb_size: physical eraseblock size
* @bad_peb_count: count of bad physical eraseblocks * @bad_peb_count: count of bad physical eraseblocks
* @good_peb_count: count of good physical eraseblocks * @good_peb_count: count of good physical eraseblocks
* @erroneous_peb_count: count of erroneous physical eraseblocks in @erroneous
* @max_erroneous: maximum allowed amount of erroneous physical eraseblocks
* @min_io_size: minimal input/output unit size of the underlying MTD device * @min_io_size: minimal input/output unit size of the underlying MTD device
* @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers
* @ro_mode: if the UBI device is in read-only mode * @ro_mode: if the UBI device is in read-only mode
...@@ -366,7 +378,6 @@ struct ubi_wl_entry; ...@@ -366,7 +378,6 @@ struct ubi_wl_entry;
* @peb_buf2: another buffer of PEB size used for different purposes * @peb_buf2: another buffer of PEB size used for different purposes
* @buf_mutex: protects @peb_buf1 and @peb_buf2 * @buf_mutex: protects @peb_buf1 and @peb_buf2
* @ckvol_mutex: serializes static volume checking when opening * @ckvol_mutex: serializes static volume checking when opening
* @mult_mutex: serializes operations on multiple volumes, like re-naming
* @dbg_peb_buf: buffer of PEB size used for debugging * @dbg_peb_buf: buffer of PEB size used for debugging
* @dbg_buf_mutex: protects @dbg_peb_buf * @dbg_buf_mutex: protects @dbg_peb_buf
*/ */
...@@ -389,7 +400,7 @@ struct ubi_device { ...@@ -389,7 +400,7 @@ struct ubi_device {
int vtbl_slots; int vtbl_slots;
int vtbl_size; int vtbl_size;
struct ubi_vtbl_record *vtbl; struct ubi_vtbl_record *vtbl;
struct mutex volumes_mutex; struct mutex device_mutex;
int max_ec; int max_ec;
/* Note, mean_ec is not updated run-time - should be fixed */ /* Note, mean_ec is not updated run-time - should be fixed */
...@@ -403,6 +414,7 @@ struct ubi_device { ...@@ -403,6 +414,7 @@ struct ubi_device {
/* Wear-leveling sub-system's stuff */ /* Wear-leveling sub-system's stuff */
struct rb_root used; struct rb_root used;
struct rb_root erroneous;
struct rb_root free; struct rb_root free;
struct rb_root scrub; struct rb_root scrub;
struct list_head pq[UBI_PROT_QUEUE_LEN]; struct list_head pq[UBI_PROT_QUEUE_LEN];
...@@ -420,6 +432,7 @@ struct ubi_device { ...@@ -420,6 +432,7 @@ struct ubi_device {
struct task_struct *bgt_thread; struct task_struct *bgt_thread;
int thread_enabled; int thread_enabled;
char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
struct notifier_block reboot_notifier;
/* I/O sub-system's stuff */ /* I/O sub-system's stuff */
long long flash_size; long long flash_size;
...@@ -427,6 +440,8 @@ struct ubi_device { ...@@ -427,6 +440,8 @@ struct ubi_device {
int peb_size; int peb_size;
int bad_peb_count; int bad_peb_count;
int good_peb_count; int good_peb_count;
int erroneous_peb_count;
int max_erroneous;
int min_io_size; int min_io_size;
int hdrs_min_io_size; int hdrs_min_io_size;
int ro_mode; int ro_mode;
...@@ -444,8 +459,7 @@ struct ubi_device { ...@@ -444,8 +459,7 @@ struct ubi_device {
void *peb_buf2; void *peb_buf2;
struct mutex buf_mutex; struct mutex buf_mutex;
struct mutex ckvol_mutex; struct mutex ckvol_mutex;
struct mutex mult_mutex; #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
#ifdef CONFIG_MTD_UBI_DEBUG
void *dbg_peb_buf; void *dbg_peb_buf;
struct mutex dbg_buf_mutex; struct mutex dbg_buf_mutex;
#endif #endif
...@@ -457,6 +471,7 @@ extern const struct file_operations ubi_cdev_operations; ...@@ -457,6 +471,7 @@ extern const struct file_operations ubi_cdev_operations;
extern const struct file_operations ubi_vol_cdev_operations; extern const struct file_operations ubi_vol_cdev_operations;
extern struct class *ubi_class; extern struct class *ubi_class;
extern struct mutex ubi_devices_mutex; extern struct mutex ubi_devices_mutex;
extern struct blocking_notifier_head ubi_notifiers;
/* vtbl.c */ /* vtbl.c */
int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
...@@ -489,17 +504,6 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, ...@@ -489,17 +504,6 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
int ubi_check_volume(struct ubi_device *ubi, int vol_id); int ubi_check_volume(struct ubi_device *ubi, int vol_id);
void ubi_calculate_reserved(struct ubi_device *ubi); void ubi_calculate_reserved(struct ubi_device *ubi);
/* gluebi.c */
#ifdef CONFIG_MTD_UBI_GLUEBI
int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol);
int ubi_destroy_gluebi(struct ubi_volume *vol);
void ubi_gluebi_updated(struct ubi_volume *vol);
#else
#define ubi_create_gluebi(ubi, vol) 0
#define ubi_destroy_gluebi(vol) 0
#define ubi_gluebi_updated(vol)
#endif
/* eba.c */ /* eba.c */
int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
int lnum); int lnum);
...@@ -549,6 +553,16 @@ struct ubi_device *ubi_get_device(int ubi_num); ...@@ -549,6 +553,16 @@ struct ubi_device *ubi_get_device(int ubi_num);
void ubi_put_device(struct ubi_device *ubi); void ubi_put_device(struct ubi_device *ubi);
struct ubi_device *ubi_get_by_major(int major); struct ubi_device *ubi_get_by_major(int major);
int ubi_major2num(int major); int ubi_major2num(int major);
int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol,
int ntype);
int ubi_notify_all(struct ubi_device *ubi, int ntype,
struct notifier_block *nb);
int ubi_enumerate_volumes(struct notifier_block *nb);
/* kapi.c */
void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di);
void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
struct ubi_volume_info *vi);
/* /*
* ubi_rb_for_each_entry - walk an RB-tree. * ubi_rb_for_each_entry - walk an RB-tree.
......
...@@ -68,10 +68,10 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) ...@@ -68,10 +68,10 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol)
sizeof(struct ubi_vtbl_record)); sizeof(struct ubi_vtbl_record));
vtbl_rec.upd_marker = 1; vtbl_rec.upd_marker = 1;
mutex_lock(&ubi->volumes_mutex); mutex_lock(&ubi->device_mutex);
err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
mutex_unlock(&ubi->volumes_mutex);
vol->upd_marker = 1; vol->upd_marker = 1;
mutex_unlock(&ubi->device_mutex);
return err; return err;
} }
...@@ -109,10 +109,10 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -109,10 +109,10 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol,
vol->last_eb_bytes = vol->usable_leb_size; vol->last_eb_bytes = vol->usable_leb_size;
} }
mutex_lock(&ubi->volumes_mutex); mutex_lock(&ubi->device_mutex);
err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
mutex_unlock(&ubi->volumes_mutex);
vol->upd_marker = 0; vol->upd_marker = 0;
mutex_unlock(&ubi->device_mutex);
return err; return err;
} }
......
...@@ -198,7 +198,7 @@ static void volume_sysfs_close(struct ubi_volume *vol) ...@@ -198,7 +198,7 @@ static void volume_sysfs_close(struct ubi_volume *vol)
* %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume
* and saves it in @req->vol_id. Returns zero in case of success and a negative * and saves it in @req->vol_id. Returns zero in case of success and a negative
* error code in case of failure. Note, the caller has to have the * error code in case of failure. Note, the caller has to have the
* @ubi->volumes_mutex locked. * @ubi->device_mutex locked.
*/ */
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
{ {
...@@ -232,8 +232,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -232,8 +232,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
req->vol_id = vol_id; req->vol_id = vol_id;
} }
dbg_gen("volume ID %d, %llu bytes, type %d, name %s", dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s",
vol_id, (unsigned long long)req->bytes, ubi->ubi_num, vol_id, (unsigned long long)req->bytes,
(int)req->vol_type, req->name); (int)req->vol_type, req->name);
/* Ensure that this volume does not exist */ /* Ensure that this volume does not exist */
...@@ -317,10 +317,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -317,10 +317,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
goto out_mapping; goto out_mapping;
} }
err = ubi_create_gluebi(ubi, vol);
if (err)
goto out_cdev;
vol->dev.release = vol_release; vol->dev.release = vol_release;
vol->dev.parent = &ubi->dev; vol->dev.parent = &ubi->dev;
vol->dev.devt = dev; vol->dev.devt = dev;
...@@ -330,7 +326,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -330,7 +326,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
err = device_register(&vol->dev); err = device_register(&vol->dev);
if (err) { if (err) {
ubi_err("cannot register device"); ubi_err("cannot register device");
goto out_gluebi; goto out_cdev;
} }
err = volume_sysfs_init(ubi, vol); err = volume_sysfs_init(ubi, vol);
...@@ -358,7 +354,9 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -358,7 +354,9 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
ubi->vol_count += 1; ubi->vol_count += 1;
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
err = paranoid_check_volumes(ubi); ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED);
if (paranoid_check_volumes(ubi))
dbg_err("check failed while creating volume %d", vol_id);
return err; return err;
out_sysfs: out_sysfs:
...@@ -373,10 +371,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -373,10 +371,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
do_free = 0; do_free = 0;
get_device(&vol->dev); get_device(&vol->dev);
volume_sysfs_close(vol); volume_sysfs_close(vol);
out_gluebi:
if (ubi_destroy_gluebi(vol))
dbg_err("cannot destroy gluebi for volume %d:%d",
ubi->ubi_num, vol_id);
out_cdev: out_cdev:
cdev_del(&vol->cdev); cdev_del(&vol->cdev);
out_mapping: out_mapping:
...@@ -403,7 +397,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -403,7 +397,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
* *
* This function removes volume described by @desc. The volume has to be opened * This function removes volume described by @desc. The volume has to be opened
* in "exclusive" mode. Returns zero in case of success and a negative error * in "exclusive" mode. Returns zero in case of success and a negative error
* code in case of failure. The caller has to have the @ubi->volumes_mutex * code in case of failure. The caller has to have the @ubi->device_mutex
* locked. * locked.
*/ */
int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
...@@ -412,7 +406,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ...@@ -412,7 +406,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
struct ubi_device *ubi = vol->ubi; struct ubi_device *ubi = vol->ubi;
int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs; int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
dbg_gen("remove UBI volume %d", vol_id); dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id);
ubi_assert(desc->mode == UBI_EXCLUSIVE); ubi_assert(desc->mode == UBI_EXCLUSIVE);
ubi_assert(vol == ubi->volumes[vol_id]); ubi_assert(vol == ubi->volumes[vol_id]);
...@@ -431,10 +425,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ...@@ -431,10 +425,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
ubi->volumes[vol_id] = NULL; ubi->volumes[vol_id] = NULL;
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
err = ubi_destroy_gluebi(vol);
if (err)
goto out_err;
if (!no_vtbl) { if (!no_vtbl) {
err = ubi_change_vtbl_record(ubi, vol_id, NULL); err = ubi_change_vtbl_record(ubi, vol_id, NULL);
if (err) if (err)
...@@ -465,8 +455,10 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ...@@ -465,8 +455,10 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
ubi->vol_count -= 1; ubi->vol_count -= 1;
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
if (!no_vtbl) ubi_volume_notify(ubi, vol, UBI_VOLUME_REMOVED);
err = paranoid_check_volumes(ubi); if (!no_vtbl && paranoid_check_volumes(ubi))
dbg_err("check failed while removing volume %d", vol_id);
return err; return err;
out_err: out_err:
...@@ -485,7 +477,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ...@@ -485,7 +477,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
* *
* This function re-sizes the volume and returns zero in case of success, and a * This function re-sizes the volume and returns zero in case of success, and a
* negative error code in case of failure. The caller has to have the * negative error code in case of failure. The caller has to have the
* @ubi->volumes_mutex locked. * @ubi->device_mutex locked.
*/ */
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
{ {
...@@ -498,8 +490,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ...@@ -498,8 +490,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
if (ubi->ro_mode) if (ubi->ro_mode)
return -EROFS; return -EROFS;
dbg_gen("re-size volume %d to from %d to %d PEBs", dbg_gen("re-size device %d, volume %d to from %d to %d PEBs",
vol_id, vol->reserved_pebs, reserved_pebs); ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs);
if (vol->vol_type == UBI_STATIC_VOLUME && if (vol->vol_type == UBI_STATIC_VOLUME &&
reserved_pebs < vol->used_ebs) { reserved_pebs < vol->used_ebs) {
...@@ -587,7 +579,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ...@@ -587,7 +579,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
(long long)vol->used_ebs * vol->usable_leb_size; (long long)vol->used_ebs * vol->usable_leb_size;
} }
err = paranoid_check_volumes(ubi); ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED);
if (paranoid_check_volumes(ubi))
dbg_err("check failed while re-sizing volume %d", vol_id);
return err; return err;
out_acc: out_acc:
...@@ -632,11 +626,12 @@ int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list) ...@@ -632,11 +626,12 @@ int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
vol->name_len = re->new_name_len; vol->name_len = re->new_name_len;
memcpy(vol->name, re->new_name, re->new_name_len + 1); memcpy(vol->name, re->new_name, re->new_name_len + 1);
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED);
} }
} }
if (!err) if (!err && paranoid_check_volumes(ubi))
err = paranoid_check_volumes(ubi); ;
return err; return err;
} }
...@@ -667,10 +662,6 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) ...@@ -667,10 +662,6 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
return err; return err;
} }
err = ubi_create_gluebi(ubi, vol);
if (err)
goto out_cdev;
vol->dev.release = vol_release; vol->dev.release = vol_release;
vol->dev.parent = &ubi->dev; vol->dev.parent = &ubi->dev;
vol->dev.devt = dev; vol->dev.devt = dev;
...@@ -678,21 +669,19 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) ...@@ -678,21 +669,19 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
err = device_register(&vol->dev); err = device_register(&vol->dev);
if (err) if (err)
goto out_gluebi; goto out_cdev;
err = volume_sysfs_init(ubi, vol); err = volume_sysfs_init(ubi, vol);
if (err) { if (err) {
cdev_del(&vol->cdev); cdev_del(&vol->cdev);
err = ubi_destroy_gluebi(vol);
volume_sysfs_close(vol); volume_sysfs_close(vol);
return err; return err;
} }
err = paranoid_check_volumes(ubi); if (paranoid_check_volumes(ubi))
dbg_err("check failed while adding volume %d", vol_id);
return err; return err;
out_gluebi:
err = ubi_destroy_gluebi(vol);
out_cdev: out_cdev:
cdev_del(&vol->cdev); cdev_del(&vol->cdev);
return err; return err;
...@@ -708,12 +697,9 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) ...@@ -708,12 +697,9 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
*/ */
void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
{ {
int err;
dbg_gen("free volume %d", vol->vol_id); dbg_gen("free volume %d", vol->vol_id);
ubi->volumes[vol->vol_id] = NULL; ubi->volumes[vol->vol_id] = NULL;
err = ubi_destroy_gluebi(vol);
cdev_del(&vol->cdev); cdev_del(&vol->cdev);
volume_sysfs_close(vol); volume_sysfs_close(vol);
} }
...@@ -868,6 +854,7 @@ static int paranoid_check_volume(struct ubi_device *ubi, int vol_id) ...@@ -868,6 +854,7 @@ static int paranoid_check_volume(struct ubi_device *ubi, int vol_id)
if (vol) if (vol)
ubi_dbg_dump_vol_info(vol); ubi_dbg_dump_vol_info(vol);
ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
dump_stack();
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
return -EINVAL; return -EINVAL;
} }
......
...@@ -55,8 +55,8 @@ ...@@ -55,8 +55,8 @@
* *
* As it was said, for the UBI sub-system all physical eraseblocks are either * As it was said, for the UBI sub-system all physical eraseblocks are either
* "free" or "used". Free eraseblock are kept in the @wl->free RB-tree, while * "free" or "used". Free eraseblock are kept in the @wl->free RB-tree, while
* used eraseblocks are kept in @wl->used or @wl->scrub RB-trees, or * used eraseblocks are kept in @wl->used, @wl->erroneous, or @wl->scrub
* (temporarily) in the @wl->pq queue. * RB-trees, as well as (temporarily) in the @wl->pq queue.
* *
* When the WL sub-system returns a physical eraseblock, the physical * When the WL sub-system returns a physical eraseblock, the physical
* eraseblock is protected from being moved for some "time". For this reason, * eraseblock is protected from being moved for some "time". For this reason,
...@@ -83,6 +83,8 @@ ...@@ -83,6 +83,8 @@
* used. The former state corresponds to the @wl->free tree. The latter state * used. The former state corresponds to the @wl->free tree. The latter state
* is split up on several sub-states: * is split up on several sub-states:
* o the WL movement is allowed (@wl->used tree); * o the WL movement is allowed (@wl->used tree);
* o the WL movement is disallowed (@wl->erroneous) because the PEB is
* erroneous - e.g., there was a read error;
* o the WL movement is temporarily prohibited (@wl->pq queue); * o the WL movement is temporarily prohibited (@wl->pq queue);
* o scrubbing is needed (@wl->scrub tree). * o scrubbing is needed (@wl->scrub tree).
* *
...@@ -653,7 +655,8 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, ...@@ -653,7 +655,8 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
int cancel) int cancel)
{ {
int err, scrubbing = 0, torture = 0; int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
int vol_id = -1, uninitialized_var(lnum);
struct ubi_wl_entry *e1, *e2; struct ubi_wl_entry *e1, *e2;
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
...@@ -738,68 +741,78 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -738,68 +741,78 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
/* /*
* We are trying to move PEB without a VID header. UBI * We are trying to move PEB without a VID header. UBI
* always write VID headers shortly after the PEB was * always write VID headers shortly after the PEB was
* given, so we have a situation when it did not have * given, so we have a situation when it has not yet
* chance to write it down because it was preempted. * had a chance to write it, because it was preempted.
* Just re-schedule the work, so that next time it will * So add this PEB to the protection queue so far,
* likely have the VID header in place. * because presumably more data will be written there
* (including the missing VID header), and then we'll
* move it.
*/ */
dbg_wl("PEB %d has no VID header", e1->pnum); dbg_wl("PEB %d has no VID header", e1->pnum);
protect = 1;
goto out_not_moved; goto out_not_moved;
} }
ubi_err("error %d while reading VID header from PEB %d", ubi_err("error %d while reading VID header from PEB %d",
err, e1->pnum); err, e1->pnum);
if (err > 0)
err = -EIO;
goto out_error; goto out_error;
} }
vol_id = be32_to_cpu(vid_hdr->vol_id);
lnum = be32_to_cpu(vid_hdr->lnum);
err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr);
if (err) { if (err) {
if (err == -EAGAIN) if (err == MOVE_CANCEL_RACE) {
/*
* The LEB has not been moved because the volume is
* being deleted or the PEB has been put meanwhile. We
* should prevent this PEB from being selected for
* wear-leveling movement again, so put it to the
* protection queue.
*/
protect = 1;
goto out_not_moved; goto out_not_moved;
if (err < 0) }
goto out_error;
if (err == 2) { if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR ||
/* Target PEB write error, torture it */ err == MOVE_TARGET_RD_ERR) {
/*
* Target PEB had bit-flips or write error - torture it.
*/
torture = 1; torture = 1;
goto out_not_moved; goto out_not_moved;
} }
if (err == MOVE_SOURCE_RD_ERR) {
/* /*
* The LEB has not been moved because the volume is being * An error happened while reading the source PEB. Do
* deleted or the PEB has been put meanwhile. We should prevent * not switch to R/O mode in this case, and give the
* this PEB from being selected for wear-leveling movement * upper layers a possibility to recover from this,
* again, so put it to the protection queue. * e.g. by unmapping corresponding LEB. Instead, just
* put this PEB to the @ubi->erroneous list to prevent
* UBI from trying to move it over and over again.
*/ */
if (ubi->erroneous_peb_count > ubi->max_erroneous) {
ubi_err("too many erroneous eraseblocks (%d)",
ubi->erroneous_peb_count);
goto out_error;
}
erroneous = 1;
goto out_not_moved;
}
dbg_wl("canceled moving PEB %d", e1->pnum); if (err < 0)
ubi_assert(err == 1);
ubi_free_vid_hdr(ubi, vid_hdr);
vid_hdr = NULL;
spin_lock(&ubi->wl_lock);
prot_queue_add(ubi, e1);
ubi_assert(!ubi->move_to_put);
ubi->move_from = ubi->move_to = NULL;
ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock);
e1 = NULL;
err = schedule_erase(ubi, e2, 0);
if (err)
goto out_error; goto out_error;
mutex_unlock(&ubi->move_mutex);
return 0; ubi_assert(0);
} }
/* The PEB has been successfully moved */ /* The PEB has been successfully moved */
ubi_free_vid_hdr(ubi, vid_hdr);
vid_hdr = NULL;
if (scrubbing) if (scrubbing)
ubi_msg("scrubbed PEB %d, data moved to PEB %d", ubi_msg("scrubbed PEB %d (LEB %d:%d), data moved to PEB %d",
e1->pnum, e2->pnum); e1->pnum, vol_id, lnum, e2->pnum);
ubi_free_vid_hdr(ubi, vid_hdr);
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
if (!ubi->move_to_put) { if (!ubi->move_to_put) {
...@@ -812,8 +825,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -812,8 +825,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
err = schedule_erase(ubi, e1, 0); err = schedule_erase(ubi, e1, 0);
if (err) { if (err) {
e1 = NULL; kmem_cache_free(ubi_wl_entry_slab, e1);
goto out_error; if (e2)
kmem_cache_free(ubi_wl_entry_slab, e2);
goto out_ro;
} }
if (e2) { if (e2) {
...@@ -821,10 +836,13 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -821,10 +836,13 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* Well, the target PEB was put meanwhile, schedule it for * Well, the target PEB was put meanwhile, schedule it for
* erasure. * erasure.
*/ */
dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); dbg_wl("PEB %d (LEB %d:%d) was put meanwhile, erase",
e2->pnum, vol_id, lnum);
err = schedule_erase(ubi, e2, 0); err = schedule_erase(ubi, e2, 0);
if (err) if (err) {
goto out_error; kmem_cache_free(ubi_wl_entry_slab, e2);
goto out_ro;
}
} }
dbg_wl("done"); dbg_wl("done");
...@@ -837,11 +855,19 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -837,11 +855,19 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* have been changed, schedule it for erasure. * have been changed, schedule it for erasure.
*/ */
out_not_moved: out_not_moved:
dbg_wl("canceled moving PEB %d", e1->pnum); if (vol_id != -1)
ubi_free_vid_hdr(ubi, vid_hdr); dbg_wl("cancel moving PEB %d (LEB %d:%d) to PEB %d (%d)",
vid_hdr = NULL; e1->pnum, vol_id, lnum, e2->pnum, err);
else
dbg_wl("cancel moving PEB %d to PEB %d (%d)",
e1->pnum, e2->pnum, err);
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
if (scrubbing) if (protect)
prot_queue_add(ubi, e1);
else if (erroneous) {
wl_tree_add(e1, &ubi->erroneous);
ubi->erroneous_peb_count += 1;
} else if (scrubbing)
wl_tree_add(e1, &ubi->scrub); wl_tree_add(e1, &ubi->scrub);
else else
wl_tree_add(e1, &ubi->used); wl_tree_add(e1, &ubi->used);
...@@ -850,32 +876,36 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -850,32 +876,36 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
ubi->wl_scheduled = 0; ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
e1 = NULL; ubi_free_vid_hdr(ubi, vid_hdr);
err = schedule_erase(ubi, e2, torture); err = schedule_erase(ubi, e2, torture);
if (err) if (err) {
goto out_error; kmem_cache_free(ubi_wl_entry_slab, e2);
goto out_ro;
}
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
return 0; return 0;
out_error: out_error:
if (vol_id != -1)
ubi_err("error %d while moving PEB %d to PEB %d", ubi_err("error %d while moving PEB %d to PEB %d",
err, e1->pnum, e2->pnum); err, e1->pnum, e2->pnum);
else
ubi_free_vid_hdr(ubi, vid_hdr); ubi_err("error %d while moving PEB %d (LEB %d:%d) to PEB %d",
err, e1->pnum, vol_id, lnum, e2->pnum);
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
ubi->move_from = ubi->move_to = NULL; ubi->move_from = ubi->move_to = NULL;
ubi->move_to_put = ubi->wl_scheduled = 0; ubi->move_to_put = ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
if (e1) ubi_free_vid_hdr(ubi, vid_hdr);
kmem_cache_free(ubi_wl_entry_slab, e1); kmem_cache_free(ubi_wl_entry_slab, e1);
if (e2)
kmem_cache_free(ubi_wl_entry_slab, e2); kmem_cache_free(ubi_wl_entry_slab, e2);
ubi_ro_mode(ubi);
out_ro:
ubi_ro_mode(ubi);
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
return err; ubi_assert(err != 0);
return err < 0 ? err : -EIO;
out_cancel: out_cancel:
ubi->wl_scheduled = 0; ubi->wl_scheduled = 0;
...@@ -1015,7 +1045,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ...@@ -1015,7 +1045,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
/* /*
* If this is not %-EIO, we have no idea what to do. Scheduling * If this is not %-EIO, we have no idea what to do. Scheduling
* this physical eraseblock for erasure again would cause * this physical eraseblock for erasure again would cause
* errors again and again. Well, lets switch to RO mode. * errors again and again. Well, lets switch to R/O mode.
*/ */
goto out_ro; goto out_ro;
} }
...@@ -1043,10 +1073,9 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ...@@ -1043,10 +1073,9 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
ubi_err("no reserved physical eraseblocks"); ubi_err("no reserved physical eraseblocks");
goto out_ro; goto out_ro;
} }
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
ubi_msg("mark PEB %d as bad", pnum);
ubi_msg("mark PEB %d as bad", pnum);
err = ubi_io_mark_bad(ubi, pnum); err = ubi_io_mark_bad(ubi, pnum);
if (err) if (err)
goto out_ro; goto out_ro;
...@@ -1056,7 +1085,9 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ...@@ -1056,7 +1085,9 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
ubi->bad_peb_count += 1; ubi->bad_peb_count += 1;
ubi->good_peb_count -= 1; ubi->good_peb_count -= 1;
ubi_calculate_reserved(ubi); ubi_calculate_reserved(ubi);
if (ubi->beb_rsvd_pebs == 0) if (ubi->beb_rsvd_pebs)
ubi_msg("%d PEBs left in the reserve", ubi->beb_rsvd_pebs);
else
ubi_warn("last PEB from the reserved pool was used"); ubi_warn("last PEB from the reserved pool was used");
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
...@@ -1125,6 +1156,13 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) ...@@ -1125,6 +1156,13 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture)
} else if (in_wl_tree(e, &ubi->scrub)) { } else if (in_wl_tree(e, &ubi->scrub)) {
paranoid_check_in_wl_tree(e, &ubi->scrub); paranoid_check_in_wl_tree(e, &ubi->scrub);
rb_erase(&e->u.rb, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub);
} else if (in_wl_tree(e, &ubi->erroneous)) {
paranoid_check_in_wl_tree(e, &ubi->erroneous);
rb_erase(&e->u.rb, &ubi->erroneous);
ubi->erroneous_peb_count -= 1;
ubi_assert(ubi->erroneous_peb_count >= 0);
/* Erroneous PEBs should be tortured */
torture = 1;
} else { } else {
err = prot_queue_del(ubi, e->pnum); err = prot_queue_del(ubi, e->pnum);
if (err) { if (err) {
...@@ -1373,7 +1411,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ...@@ -1373,7 +1411,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
struct ubi_scan_leb *seb, *tmp; struct ubi_scan_leb *seb, *tmp;
struct ubi_wl_entry *e; struct ubi_wl_entry *e;
ubi->used = ubi->free = ubi->scrub = RB_ROOT; ubi->used = ubi->erroneous = ubi->free = ubi->scrub = RB_ROOT;
spin_lock_init(&ubi->wl_lock); spin_lock_init(&ubi->wl_lock);
mutex_init(&ubi->move_mutex); mutex_init(&ubi->move_mutex);
init_rwsem(&ubi->work_sem); init_rwsem(&ubi->work_sem);
...@@ -1511,6 +1549,7 @@ void ubi_wl_close(struct ubi_device *ubi) ...@@ -1511,6 +1549,7 @@ void ubi_wl_close(struct ubi_device *ubi)
cancel_pending(ubi); cancel_pending(ubi);
protection_queue_destroy(ubi); protection_queue_destroy(ubi);
tree_destroy(&ubi->used); tree_destroy(&ubi->used);
tree_destroy(&ubi->erroneous);
tree_destroy(&ubi->free); tree_destroy(&ubi->free);
tree_destroy(&ubi->scrub); tree_destroy(&ubi->scrub);
kfree(ubi->lookuptbl); kfree(ubi->lookuptbl);
......
...@@ -132,6 +132,39 @@ struct ubi_device_info { ...@@ -132,6 +132,39 @@ struct ubi_device_info {
dev_t cdev; dev_t cdev;
}; };
/*
* enum - volume notification types.
* @UBI_VOLUME_ADDED: volume has been added
* @UBI_VOLUME_REMOVED: start volume volume
* @UBI_VOLUME_RESIZED: volume size has been re-sized
* @UBI_VOLUME_RENAMED: volume name has been re-named
* @UBI_VOLUME_UPDATED: volume name has been updated
*
* These constants define which type of event has happened when a volume
* notification function is invoked.
*/
enum {
UBI_VOLUME_ADDED,
UBI_VOLUME_REMOVED,
UBI_VOLUME_RESIZED,
UBI_VOLUME_RENAMED,
UBI_VOLUME_UPDATED,
};
/*
* struct ubi_notification - UBI notification description structure.
* @di: UBI device description object
* @vi: UBI volume description object
*
* UBI notifiers are called with a pointer to an object of this type. The
* object describes the notification. Namely, it provides a description of the
* UBI device and UBI volume the notification informs about.
*/
struct ubi_notification {
struct ubi_device_info di;
struct ubi_volume_info vi;
};
/* UBI descriptor given to users when they open UBI volumes */ /* UBI descriptor given to users when they open UBI volumes */
struct ubi_volume_desc; struct ubi_volume_desc;
...@@ -141,6 +174,10 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc, ...@@ -141,6 +174,10 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc,
struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode); struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode);
struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
int mode); int mode);
int ubi_register_volume_notifier(struct notifier_block *nb,
int ignore_existing);
int ubi_unregister_volume_notifier(struct notifier_block *nb);
void ubi_close_volume(struct ubi_volume_desc *desc); void ubi_close_volume(struct ubi_volume_desc *desc);
int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
int len, int check); int len, int check);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment