* Fix for a theoretical race condition which could lead to a situation when

UBIFS is unable to mount a file-system (Hujianyang)
 * Few fixes for the ubiblock sybsystem, error path fixes
 * The ubiblock subsystem has had the volume size change handling improved
 * Few fixes and nicifications in the fastmap subsystem
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJUO9l0AAoJECmIfjd9wqK01TwP/jAcA7GEnxUpQ8UFBZhJEIN0
 0Ad4oDrGShpuEYgYyRFjCstuXErJBhMwImrevJhRmwxaY2fzGqBeDO9YKKGkKDfa
 qjGsQrUaCJgV6qC2iT056ZmI7V/XyZfnZQ4Z8nQbzafoJ3MPbB6ExqBy8CZi8q/6
 A516cen/cnZfHOQ1aqN6gyw2l976IzdJx8v0WOeYaXcvfDMrmfY8mkfh7EahOIVm
 Kz9BVlVRxxfKPCqMpm+xV8KAOsMueOnKy+6zL7rFh+AvLQBACq44BV1HkZtg2avX
 NBAo1RTPumeCht2t4nLJfgc+BJZ7cNpNFAijsWVJxp6umUqlsnbqckAx69O+JE9/
 VZjM1KN1suI0bm01bj6xysGvg+JNTMiZ+HEqiseICSWtDbnCT4qDL3MPFgmD9OYh
 9ar92Ku2HeY3DakKNd89gqw0ey28cv4i957KleneYzewcfFQ5pC/dp4thcDWa5fH
 AHoblC4ShmcURDPYsIKRZsiTUf/uf3iLFIWAGJBDnSRg4dzzjoJkenz4W5ecWFDj
 JokceklSf0zm8qAAdIUXw5Sihza1cnSBAIYBxVR808U+bwkCTOFF5xcTQy6wKf3y
 NBb+ygh/ugps8B2evJEmp6ByLWQZr8j1q7IokZtglKWN2qOTfzyMxzlWl9vOQJYq
 EQytnka5OEEXamr7g1iB
 =2XCN
 -----END PGP SIGNATURE-----

Merge tag 'upstream-3.18-rc1-v2' of git://git.infradead.org/linux-ubifs

Pull UBI/UBIFS fixes from Artem Bityutskiy:
 - fix for a theoretical race condition which could lead to a situation
   when UBIFS is unable to mount a file-system (Hujianyang)
 - a few fixes for the ubiblock sybsystem, error path fixes
 - the ubiblock subsystem has had the volume size change handling
   improved
 - a few fixes and nicifications in the fastmap subsystem

* tag 'upstream-3.18-rc1-v2' of git://git.infradead.org/linux-ubifs:
  UBI: Fastmap: Calc fastmap size correctly
  UBIFS: Fix trivial typo in power_cut_emulated()
  UBI: Fix trivial typo in __schedule_ubi_work
  UBI: wl: Rename cancel flag to shutdown
  UBI: ubi_eba_read_leb: Remove in vain variable assignment
  UBIFS: Align the dump messages of SB_NODE
  UBI: Fix livelock in produce_free_peb()
  UBI: return on error in rename_volumes()
  UBI: Improve comment on work_sem
  UBIFS: Remove bogus assert
  UBI: Dispatch update notification if the volume is updated
  UBI: block: Add support for the UBI_VOLUME_UPDATED notification
  UBI: block: Fix block device size setting
  UBI: block: fix dereference on uninitialized dev
  UBI: add missing kmem_cache_free() in process_pool_aeb error path
  UBIFS: fix free log space calculation
  UBIFS: fix a race condition
This commit is contained in:
Linus Torvalds 2014-10-14 08:38:54 +02:00
commit b11445f830
10 changed files with 79 additions and 52 deletions

View File

@ -188,8 +188,9 @@ static int ubiblock_read_to_buf(struct ubiblock *dev, char *buffer,
ret = ubi_read(dev->desc, leb, buffer, offset, len);
if (ret) {
ubi_err("%s ubi_read error %d",
dev->gd->disk_name, ret);
ubi_err("%s: error %d while reading from LEB %d (offset %d, "
"length %d)", dev->gd->disk_name, ret, leb, offset,
len);
return ret;
}
return 0;
@ -378,7 +379,7 @@ int ubiblock_create(struct ubi_volume_info *vi)
{
struct ubiblock *dev;
struct gendisk *gd;
u64 disk_capacity = ((u64)vi->size * vi->usable_leb_size) >> 9;
u64 disk_capacity = vi->used_bytes >> 9;
int ret;
if ((sector_t)disk_capacity != disk_capacity)
@ -502,13 +503,8 @@ int ubiblock_remove(struct ubi_volume_info *vi)
static int ubiblock_resize(struct ubi_volume_info *vi)
{
struct ubiblock *dev;
u64 disk_capacity = ((u64)vi->size * vi->usable_leb_size) >> 9;
u64 disk_capacity = vi->used_bytes >> 9;
if ((sector_t)disk_capacity != disk_capacity) {
ubi_warn("%s: the volume is too big, cannot resize (%d LEBs)",
dev->gd->disk_name, vi->size);
return -EFBIG;
}
/*
* Need to lock the device list until we stop using the device,
* otherwise the device struct might get released in
@ -520,10 +516,20 @@ static int ubiblock_resize(struct ubi_volume_info *vi)
mutex_unlock(&devices_mutex);
return -ENODEV;
}
if ((sector_t)disk_capacity != disk_capacity) {
mutex_unlock(&devices_mutex);
ubi_warn("%s: the volume is too big (%d LEBs), cannot resize",
dev->gd->disk_name, vi->size);
return -EFBIG;
}
mutex_lock(&dev->dev_mutex);
if (get_capacity(dev->gd) != disk_capacity) {
set_capacity(dev->gd, disk_capacity);
ubi_msg("%s resized to %d LEBs", dev->gd->disk_name, vi->size);
ubi_msg("%s resized to %lld bytes", dev->gd->disk_name,
vi->used_bytes);
}
mutex_unlock(&dev->dev_mutex);
mutex_unlock(&devices_mutex);
return 0;
@ -547,6 +553,14 @@ static int ubiblock_notify(struct notifier_block *nb,
case UBI_VOLUME_RESIZED:
ubiblock_resize(&nt->vi);
break;
case UBI_VOLUME_UPDATED:
/*
* If the volume is static, a content update might mean the
* size (i.e. used_bytes) was also changed.
*/
if (nt->vi.vol_type == UBI_STATIC_VOLUME)
ubiblock_resize(&nt->vi);
break;
default:
break;
}

View File

@ -425,8 +425,10 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
break;
err = ubi_start_update(ubi, vol, bytes);
if (bytes == 0)
if (bytes == 0) {
ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED);
revoke_exclusive(desc, UBI_READWRITE);
}
break;
}
@ -699,7 +701,7 @@ static int rename_volumes(struct ubi_device *ubi,
req->ents[i].name[req->ents[i].name_len] = '\0';
n = strlen(req->ents[i].name);
if (n != req->ents[i].name_len)
err = -EINVAL;
return -EINVAL;
}
/* Make sure volume IDs and names are unique */

View File

@ -441,10 +441,9 @@ retry:
err = ubi_io_read_data(ubi, buf, pnum, offset, len);
if (err) {
if (err == UBI_IO_BITFLIPS) {
if (err == UBI_IO_BITFLIPS)
scrub = 1;
err = 0;
} else if (mtd_is_eccerr(err)) {
else if (mtd_is_eccerr(err)) {
if (vol->vol_type == UBI_DYNAMIC_VOLUME)
goto out_unlock;
scrub = 1;

View File

@ -24,7 +24,8 @@ size_t ubi_calc_fm_size(struct ubi_device *ubi)
{
size_t size;
size = sizeof(struct ubi_fm_hdr) + \
size = sizeof(struct ubi_fm_sb) + \
sizeof(struct ubi_fm_hdr) + \
sizeof(struct ubi_fm_scan_pool) + \
sizeof(struct ubi_fm_scan_pool) + \
(ubi->peb_count * sizeof(struct ubi_fm_ec)) + \
@ -330,6 +331,7 @@ static int process_pool_aeb(struct ubi_device *ubi, struct ubi_attach_info *ai,
av = tmp_av;
else {
ubi_err("orphaned volume in fastmap pool!");
kmem_cache_free(ai->aeb_slab_cache, new_aeb);
return UBI_BAD_FASTMAP;
}

View File

@ -439,7 +439,8 @@ struct ubi_debug_info {
* @move_to, @move_to_put @erase_pending, @wl_scheduled, @works,
* @erroneous, and @erroneous_peb_count fields
* @move_mutex: serializes eraseblock moves
* @work_sem: synchronizes the WL worker with use tasks
* @work_sem: used to wait for all the scheduled works to finish and prevent
* new works from being submitted
* @wl_scheduled: non-zero if the wear-leveling was scheduled
* @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any
* physical eraseblock
@ -713,14 +714,15 @@ struct ubi_attach_info {
* @torture: if the physical eraseblock has to be tortured
* @anchor: produce a anchor PEB to by used by fastmap
*
* The @func pointer points to the worker function. If the @cancel argument is
* not zero, the worker has to free the resources and exit immediately. The
* worker has to return zero in case of success and a negative error code in
* The @func pointer points to the worker function. If the @shutdown argument is
* not zero, the worker has to free the resources and exit immediately as the
* WL sub-system is shutting down.
* The worker has to return zero in case of success and a negative error code in
* case of failure.
*/
struct ubi_work {
struct list_head list;
int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel);
int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int shutdown);
/* The below fields are only relevant to erasure works */
struct ubi_wl_entry *e;
int vol_id;

View File

@ -272,7 +272,7 @@ static int produce_free_peb(struct ubi_device *ubi)
{
int err;
while (!ubi->free.rb_node) {
while (!ubi->free.rb_node && ubi->works_count) {
spin_unlock(&ubi->wl_lock);
dbg_wl("do one work synchronously");
@ -835,7 +835,7 @@ repeat:
* @wrk: the work to schedule
*
* This function adds a work defined by @wrk to the tail of the pending works
* list. Can only be used of ubi->work_sem is already held in read mode!
* list. Can only be used if ubi->work_sem is already held in read mode!
*/
static void __schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
{
@ -864,7 +864,7 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
}
static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
int cancel);
int shutdown);
#ifdef CONFIG_MTD_UBI_FASTMAP
/**
@ -990,14 +990,15 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e,
* wear_leveling_worker - wear-leveling worker function.
* @ubi: UBI device description object
* @wrk: the work object
* @cancel: non-zero if the worker has to free memory and exit
* @shutdown: non-zero if the worker has to free memory and exit
* because the WL-subsystem is shutting down
*
* This function copies a more worn out physical eraseblock to a less worn out
* one. Returns zero in case of success and a negative error code in case of
* failure.
*/
static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
int cancel)
int shutdown)
{
int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
int vol_id = -1, uninitialized_var(lnum);
@ -1008,7 +1009,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
struct ubi_vid_hdr *vid_hdr;
kfree(wrk);
if (cancel)
if (shutdown)
return 0;
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
@ -1407,7 +1408,8 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
* erase_worker - physical eraseblock erase worker function.
* @ubi: UBI device description object
* @wl_wrk: the work object
* @cancel: non-zero if the worker has to free memory and exit
* @shutdown: non-zero if the worker has to free memory and exit
* because the WL sub-system is shutting down
*
* This function erases a physical eraseblock and perform torture testing if
* needed. It also takes care about marking the physical eraseblock bad if
@ -1415,7 +1417,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
* failure.
*/
static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
int cancel)
int shutdown)
{
struct ubi_wl_entry *e = wl_wrk->e;
int pnum = e->pnum;
@ -1423,7 +1425,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
int lnum = wl_wrk->lnum;
int err, available_consumed = 0;
if (cancel) {
if (shutdown) {
dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
kfree(wl_wrk);
kmem_cache_free(ubi_wl_entry_slab, e);
@ -1845,10 +1847,10 @@ int ubi_thread(void *u)
}
/**
* cancel_pending - cancel all pending works.
* shutdown_work - shutdown all pending works.
* @ubi: UBI device description object
*/
static void cancel_pending(struct ubi_device *ubi)
static void shutdown_work(struct ubi_device *ubi)
{
while (!list_empty(&ubi->works)) {
struct ubi_work *wrk;
@ -1997,7 +1999,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
return 0;
out_free:
cancel_pending(ubi);
shutdown_work(ubi);
tree_destroy(&ubi->used);
tree_destroy(&ubi->free);
tree_destroy(&ubi->scrub);
@ -2029,7 +2031,7 @@ static void protection_queue_destroy(struct ubi_device *ubi)
void ubi_wl_close(struct ubi_device *ubi)
{
dbg_wl("close the WL sub-system");
cancel_pending(ubi);
shutdown_work(ubi);
protection_queue_destroy(ubi);
tree_destroy(&ubi->used);
tree_destroy(&ubi->erroneous);

View File

@ -164,10 +164,6 @@ static int do_commit(struct ubifs_info *c)
if (err)
goto out;
err = ubifs_orphan_end_commit(c);
if (err)
goto out;
old_ltail_lnum = c->ltail_lnum;
err = ubifs_log_end_commit(c, new_ltail_lnum);
if (err)
goto out;
err = dbg_check_old_index(c, &zroot);
@ -202,7 +198,9 @@ static int do_commit(struct ubifs_info *c)
c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
else
c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_NO_ORPHS);
err = ubifs_write_master(c);
old_ltail_lnum = c->ltail_lnum;
err = ubifs_log_end_commit(c, new_ltail_lnum);
if (err)
goto out;

View File

@ -2462,7 +2462,7 @@ static int power_cut_emulated(struct ubifs_info *c, int lnum, int write)
if (chance(1, 2)) {
d->pc_delay = 1;
/* Fail withing 1 minute */
/* Fail within 1 minute */
delay = prandom_u32() % 60000;
d->pc_timeout = jiffies;
d->pc_timeout += msecs_to_jiffies(delay);

View File

@ -546,15 +546,14 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
int aligned_dlen, aligned_ilen, sync = IS_DIRSYNC(dir);
int last_reference = !!(deletion && inode->i_nlink == 0);
struct ubifs_inode *ui = ubifs_inode(inode);
struct ubifs_inode *dir_ui = ubifs_inode(dir);
struct ubifs_inode *host_ui = ubifs_inode(dir);
struct ubifs_dent_node *dent;
struct ubifs_ino_node *ino;
union ubifs_key dent_key, ino_key;
dbg_jnl("ino %lu, dent '%.*s', data len %d in dir ino %lu",
inode->i_ino, nm->len, nm->name, ui->data_len, dir->i_ino);
ubifs_assert(dir_ui->data_len == 0);
ubifs_assert(mutex_is_locked(&dir_ui->ui_mutex));
ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
dlen = UBIFS_DENT_NODE_SZ + nm->len + 1;
ilen = UBIFS_INO_NODE_SZ;
@ -658,7 +657,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
ui->synced_i_size = ui->ui_size;
spin_unlock(&ui->ui_lock);
mark_inode_clean(c, ui);
mark_inode_clean(c, dir_ui);
mark_inode_clean(c, host_ui);
return 0;
out_finish:

View File

@ -106,10 +106,14 @@ static inline long long empty_log_bytes(const struct ubifs_info *c)
h = (long long)c->lhead_lnum * c->leb_size + c->lhead_offs;
t = (long long)c->ltail_lnum * c->leb_size;
if (h >= t)
if (h > t)
return c->log_bytes - h + t;
else
else if (h != t)
return t - h;
else if (c->lhead_lnum != c->ltail_lnum)
return 0;
else
return c->log_bytes;
}
/**
@ -447,9 +451,9 @@ out:
* @ltail_lnum: new log tail LEB number
*
* This function is called on when the commit operation was finished. It
* moves log tail to new position and unmaps LEBs which contain obsolete data.
* Returns zero in case of success and a negative error code in case of
* failure.
* moves log tail to new position and updates the master node so that it stores
* the new log tail LEB number. Returns zero in case of success and a negative
* error code in case of failure.
*/
int ubifs_log_end_commit(struct ubifs_info *c, int ltail_lnum)
{
@ -477,7 +481,12 @@ int ubifs_log_end_commit(struct ubifs_info *c, int ltail_lnum)
spin_unlock(&c->buds_lock);
err = dbg_check_bud_bytes(c);
if (err)
goto out;
err = ubifs_write_master(c);
out:
mutex_unlock(&c->log_mutex);
return err;
}