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: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
This commit is contained in:
@@ -830,21 +830,29 @@ out:
|
|||||||
* 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,
|
|||||||
|
|
||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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 @@ out:
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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);
|
||||||
}
|
|
||||||
|
|
||||||
ubifs_destroy_idx_gc(c);
|
err = ubifs_destroy_idx_gc(c);
|
||||||
|
if (err)
|
||||||
|
ubifs_ro_mode(c, err);
|
||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
* 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);
|
|||||||
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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user