diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2010-05-23 15:20:21 +0300 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2010-07-13 06:51:57 +0300 |
commit | 6fb4374f6b1b3932f3acfe9d353568d3d8599cad (patch) | |
tree | 839a78b9ee35727cceb8a8ed325dba4475b9d531 /fs/ubifs | |
parent | 6da5156fad495730cca9238ead566222175b30b9 (diff) |
UBIFS: fix GC LEB recovery
UBIFS tries to alway have an LEB reserved for GC, and stores it
in c->gc_lnum. Besides, there is GC head which points to the current
GC head LEB.
In case of an unclean power cut, what may happen is that the GC head
was switched to the reserved GC LEB (c->gc_lnum), but a new reserved
GC LEB was not created yet. So, after an unclean reboot we may have
no reserved GC LEB, and we need to find a new LEB for this.
To do this, we find a dirty LEB which can fit the current GC head,
move the data, unmap this dirty LEB, and it becomes our reserved GC
LEB.
However, if we cannot find a dirty enough LEB, we return failure,
which is wrong, because we still can have free LEBs to use for
the reserved GC LEB. This patch fixes the issue.
This patch also fixes few typos in comments, which were spotted by
aspell.
Note, this patch fixes a real issue
[ 14.328117] UBIFS: recovery needed
[ 53.941378] UBIFS error (pid 462): ubifs_rcvry_gc_commit: could not find a dirty LEB
[ 89.606399] UBIFS: recovery completed
[ 89.609329] UBIFS assert failed in mount_ubifs at 1358 (pid 462)
[ 89.616165] [<c0026144>] (unwind_backtrace+0x0/0xe4) from [<c0125ce4>] (ubifs_fill_super+0x11d0/0x1c4c)
[ 89.625930] [<c0125ce4>] (ubifs_fill_super+0x11d0/0x1c4c) from [<c0126910>] (ubifs_get_sb+0x1b0/0x354)
[ 89.635696] [<c0126910>] (ubifs_get_sb+0x1b0/0x354) from [<c008a50c>] (vfs_kern_mount+0x50/0xe0)
[ 89.644485] [<c008a50c>] (vfs_kern_mount+0x50/0xe0) from [<c008a5e0>] (do_kern_mount+0x34/0xdc)
[ 89.653274] [<c008a5e0>] (do_kern_mount+0x34/0xdc) from [<c00a29d8>] (do_mount+0x148/0x7cc)
[ 89.662063] [<c00a29d8>] (do_mount+0x148/0x7cc) from [<c00a30f4>] (sys_mount+0x98/0xc8)
[ 89.670852] [<c00a30f4>] (sys_mount+0x98/0xc8) from [<c0021f40>] (ret_fast_syscall+0x0/0x28)
which was reported here:
http://article.gmane.org/gmane.linux.drivers.mtd/29923
by Alexander Pazdnikov <pazdnikov@list.ru>
Reported-by: Alexander Pazdnikov <pazdnikov@list.ru>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Reviewed-by: Adrian Hunter <adrian.hunter@nokia.com>
Diffstat (limited to 'fs/ubifs')
-rw-r--r-- | fs/ubifs/recovery.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 109c6ea03bb5..daae9e1f5382 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -24,7 +24,7 @@ * This file implements functions needed to recover from unclean un-mounts. * When UBIFS is mounted, it checks a flag on the master node to determine if * an un-mount was completed successfully. If not, the process of mounting - * incorparates additional checking and fixing of on-flash data structures. + * incorporates additional checking and fixing of on-flash data structures. * UBIFS always cleans away all remnants of an unclean un-mount, so that * errors do not accumulate. However UBIFS defers recovery if it is mounted * read-only, and the flash is not modified in that case. @@ -1063,8 +1063,21 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) } err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2); if (err) { - if (err == -ENOSPC) - dbg_err("could not find a dirty LEB"); + /* + * There are no dirty or empty LEBs subject to here being + * enough for the index. Try to use + * 'ubifs_find_free_leb_for_idx()', which will return any empty + * LEBs (ignoring index requirements). If the index then + * doesn't have enough LEBs the recovery commit will fail - + * which is the same result anyway i.e. recovery fails. So + * there is no problem ignoring index requirements and just + * grabbing a free LEB since we have already established there + * is not a dirty LEB we could have used instead. + */ + if (err == -ENOSPC) { + dbg_rcvry("could not find a dirty LEB"); + goto find_free; + } return err; } ubifs_assert(!(lp.flags & LPROPS_INDEX)); @@ -1139,8 +1152,8 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) find_free: /* * There is no GC head LEB or the free space in the GC head LEB is too - * small. Allocate gc_lnum by calling 'ubifs_find_free_leb_for_idx()' so - * GC is not run. + * small, or there are not dirty LEBs. Allocate gc_lnum by calling + * 'ubifs_find_free_leb_for_idx()' so GC is not run. */ lnum = ubifs_find_free_leb_for_idx(c); if (lnum < 0) { |