Commit e4d9b6cb authored by Artem Bityutskiy's avatar Artem Bityutskiy

UBIFS: fix LEB list freeing

When freeing the c->idx_lebs list, we have to release the LEBs as well,
because we might be called from mount to read-only mode code. Otherwise
the LEBs stay taken forever, which may cause problems when we re-mount
back ro RW mode.
Signed-off-by: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
parent 82c1593c
...@@ -830,21 +830,29 @@ int ubifs_gc_end_commit(struct ubifs_info *c) ...@@ -830,21 +830,29 @@ int ubifs_gc_end_commit(struct ubifs_info *c)
* ubifs_destroy_idx_gc - destroy idx_gc list. * ubifs_destroy_idx_gc - destroy idx_gc list.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
* *
* This function destroys the idx_gc list. It is called when unmounting or * This function destroys the @c->idx_gc list. It is called when unmounting or
* remounting read-only so locks are not needed. * remounting read-only so locks are not needed. Returns zero in case of
* success and a negative error code in case of failure.
*/ */
void ubifs_destroy_idx_gc(struct ubifs_info *c) int ubifs_destroy_idx_gc(struct ubifs_info *c)
{ {
int ret = 0;
while (!list_empty(&c->idx_gc)) { while (!list_empty(&c->idx_gc)) {
int err;
struct ubifs_gced_idx_leb *idx_gc; struct ubifs_gced_idx_leb *idx_gc;
idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb, idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb,
list); list);
c->idx_gc_cnt -= 1; err = ubifs_change_one_lp(c, idx_gc->lnum, LPROPS_NC,
LPROPS_NC, 0, LPROPS_TAKEN, -1);
if (err && !ret)
ret = err;
list_del(&idx_gc->list); list_del(&idx_gc->list);
kfree(idx_gc); kfree(idx_gc);
} }
return ret;
} }
/** /**
......
...@@ -678,6 +678,9 @@ int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, ...@@ -678,6 +678,9 @@ int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
out: out:
ubifs_release_lprops(c); ubifs_release_lprops(c);
if (err)
ubifs_err("cannot change properties of LEB %d, error %d",
lnum, err);
return err; return err;
} }
...@@ -714,6 +717,9 @@ int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, ...@@ -714,6 +717,9 @@ int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
out: out:
ubifs_release_lprops(c); ubifs_release_lprops(c);
if (err)
ubifs_err("cannot update properties of LEB %d, error %d",
lnum, err);
return err; return err;
} }
...@@ -737,6 +743,8 @@ int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp) ...@@ -737,6 +743,8 @@ int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp)
lpp = ubifs_lpt_lookup(c, lnum); lpp = ubifs_lpt_lookup(c, lnum);
if (IS_ERR(lpp)) { if (IS_ERR(lpp)) {
err = PTR_ERR(lpp); err = PTR_ERR(lpp);
ubifs_err("cannot read properties of LEB %d, error %d",
lnum, err);
goto out; goto out;
} }
......
...@@ -1469,9 +1469,6 @@ static int ubifs_remount_rw(struct ubifs_info *c) ...@@ -1469,9 +1469,6 @@ static int ubifs_remount_rw(struct ubifs_info *c)
{ {
int err, lnum; int err, lnum;
if (c->ro_media)
return -EINVAL;
mutex_lock(&c->umount_mutex); mutex_lock(&c->umount_mutex);
c->remounting_rw = 1; c->remounting_rw = 1;
c->always_chk_crc = 1; c->always_chk_crc = 1;
...@@ -1605,9 +1602,13 @@ static int ubifs_remount_rw(struct ubifs_info *c) ...@@ -1605,9 +1602,13 @@ static int ubifs_remount_rw(struct ubifs_info *c)
*/ */
static void commit_on_unmount(struct ubifs_info *c) static void commit_on_unmount(struct ubifs_info *c)
{ {
struct super_block *sb = c->vfs_sb;
long long bud_bytes; long long bud_bytes;
if (!c->fast_unmount) {
dbg_gen("skip committing - fast unmount enabled");
return;
}
/* /*
* This function is called before the background thread is stopped, so * This function is called before the background thread is stopped, so
* we may race with ongoing commit, which means we have to take * we may race with ongoing commit, which means we have to take
...@@ -1617,8 +1618,11 @@ static void commit_on_unmount(struct ubifs_info *c) ...@@ -1617,8 +1618,11 @@ static void commit_on_unmount(struct ubifs_info *c)
bud_bytes = c->bud_bytes; bud_bytes = c->bud_bytes;
spin_unlock(&c->buds_lock); spin_unlock(&c->buds_lock);
if (!c->fast_unmount && !(sb->s_flags & MS_RDONLY) && bud_bytes) if (bud_bytes) {
dbg_gen("run commit");
ubifs_run_commit(c); ubifs_run_commit(c);
} else
dbg_gen("journal is empty, do not run commit");
} }
/** /**
...@@ -1633,6 +1637,8 @@ static void ubifs_remount_ro(struct ubifs_info *c) ...@@ -1633,6 +1637,8 @@ static void ubifs_remount_ro(struct ubifs_info *c)
int i, err; int i, err;
ubifs_assert(!c->need_recovery); ubifs_assert(!c->need_recovery);
ubifs_assert(!c->ro_media);
commit_on_unmount(c); commit_on_unmount(c);
mutex_lock(&c->umount_mutex); mutex_lock(&c->umount_mutex);
...@@ -1646,16 +1652,17 @@ static void ubifs_remount_ro(struct ubifs_info *c) ...@@ -1646,16 +1652,17 @@ static void ubifs_remount_ro(struct ubifs_info *c)
del_timer_sync(&c->jheads[i].wbuf.timer); del_timer_sync(&c->jheads[i].wbuf.timer);
} }
if (!c->ro_media) { c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum);
c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum); err = ubifs_write_master(c);
err = ubifs_write_master(c); if (err)
if (err) ubifs_ro_mode(c, err);
ubifs_ro_mode(c, err);
} err = ubifs_destroy_idx_gc(c);
if (err)
ubifs_ro_mode(c, err);
ubifs_destroy_idx_gc(c);
free_wbufs(c); free_wbufs(c);
vfree(c->orph_buf); vfree(c->orph_buf);
c->orph_buf = NULL; c->orph_buf = NULL;
...@@ -1754,6 +1761,11 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) ...@@ -1754,6 +1761,11 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
} }
if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) {
if (c->ro_media) {
ubifs_msg("cannot re-mount R/W, UBIFS is working in "
"R/O mode");
return -EINVAL;
}
err = ubifs_remount_rw(c); err = ubifs_remount_rw(c);
if (err) if (err)
return err; return err;
...@@ -2044,7 +2056,7 @@ static void ubifs_kill_sb(struct super_block *sb) ...@@ -2044,7 +2056,7 @@ static void ubifs_kill_sb(struct super_block *sb)
* We do 'commit_on_unmount()' here instead of 'ubifs_put_super()' * We do 'commit_on_unmount()' here instead of 'ubifs_put_super()'
* in order to be outside BKL. * in order to be outside BKL.
*/ */
if (sb->s_root) if (sb->s_root && !(sb->s_flags & MS_RDONLY))
commit_on_unmount(c); commit_on_unmount(c);
/* The un-mount routine is actually done in put_super() */ /* The un-mount routine is actually done in put_super() */
generic_shutdown_super(sb); generic_shutdown_super(sb);
......
...@@ -1593,7 +1593,7 @@ int ubifs_replay_journal(struct ubifs_info *c); ...@@ -1593,7 +1593,7 @@ int ubifs_replay_journal(struct ubifs_info *c);
int ubifs_garbage_collect(struct ubifs_info *c, int anyway); int ubifs_garbage_collect(struct ubifs_info *c, int anyway);
int ubifs_gc_start_commit(struct ubifs_info *c); int ubifs_gc_start_commit(struct ubifs_info *c);
int ubifs_gc_end_commit(struct ubifs_info *c); int ubifs_gc_end_commit(struct ubifs_info *c);
void ubifs_destroy_idx_gc(struct ubifs_info *c); int ubifs_destroy_idx_gc(struct ubifs_info *c);
int ubifs_get_idx_gc_leb(struct ubifs_info *c); int ubifs_get_idx_gc_leb(struct ubifs_info *c);
int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp); int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp);
......
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