]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/ubifs/recovery.c
UBIFS: remove an unneeded check
[karo-tx-linux.git] / fs / ubifs / recovery.c
index 3dbad6fbd1eba528661e458ae56e97c03c43b609..d6c8ce3c722ecf1369f9e5f47506fbb1c678ba41 100644 (file)
@@ -1069,6 +1069,53 @@ int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf)
        return 0;
 }
 
+/**
+ * grab_empty_leb - grab an empty LEB to use as GC LEB and run commit.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function for 'ubifs_rcvry_gc_commit()' which grabs an empty
+ * LEB to be used as GC LEB (@c->gc_lnum), and then runs the commit. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static int grab_empty_leb(struct ubifs_info *c)
+{
+       int lnum, err;
+
+       /*
+        * Note, it is very important to first search for an empty LEB and then
+        * run the commit, not vice-versa. The reason is that there might be
+        * only one empty LEB at the moment, the one which has been the
+        * @c->gc_lnum just before the power cut happened. During the regular
+        * UBIFS operation (not now) @c->gc_lnum is marked as "taken", so no
+        * one but GC can grab it. But at this moment this single empty LEB is
+        * not marked as taken, so if we run commit - what happens? Right, the
+        * commit will grab it and write the index there. Remember that the
+        * index always expands as long as there is free space, and it only
+        * starts consolidating when we run out of space.
+        *
+        * IOW, if we run commit now, we might not be able to find a free LEB
+        * after this.
+        */
+       lnum = ubifs_find_free_leb_for_idx(c);
+       if (lnum < 0) {
+               dbg_err("could not find an empty LEB");
+               dbg_dump_lprops(c);
+               dbg_dump_budg(c, &c->bi);
+               return lnum;
+       }
+
+       /* Reset the index flag */
+       err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+                                 LPROPS_INDEX, 0);
+       if (err)
+               return err;
+
+       c->gc_lnum = lnum;
+       dbg_rcvry("found empty LEB %d, run commit", lnum);
+
+       return ubifs_run_commit(c);
+}
+
 /**
  * ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit.
  * @c: UBIFS file-system description object
@@ -1091,71 +1138,27 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
 {
        struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
        struct ubifs_lprops lp;
-       int lnum, err;
+       int err;
 
        c->gc_lnum = -1;
-       if (wbuf->lnum == -1) {
-               dbg_rcvry("no GC head LEB");
-               goto find_free;
-       }
-       /*
-        * See whether the used space in the dirtiest LEB fits in the GC head
-        * LEB.
-        */
-       if (wbuf->offs == c->leb_size) {
-               dbg_rcvry("no room in GC head LEB");
-               goto find_free;
+       if (wbuf->lnum == -1 || wbuf->offs == c->leb_size) {
+               dbg_rcvry("no GC head: wbuf->lnum %d, wbuf->offs %d",
+                         wbuf->lnum, wbuf->offs);
+               return grab_empty_leb(c);
        }
+
        err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2);
        if (err) {
-               /*
-                * 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));
-       lnum = lp.lnum;
-       if (lp.free + lp.dirty == c->leb_size) {
-               /* An empty LEB was returned */
-               if (lp.free != c->leb_size) {
-                       err = ubifs_change_one_lp(c, lnum, c->leb_size,
-                                                 0, 0, 0, 0);
-                       if (err)
-                               return err;
-               }
-               err = ubifs_leb_unmap(c, lnum);
-               if (err)
-                       return err;
-               c->gc_lnum = lnum;
-               dbg_rcvry("allocated LEB %d for GC", lnum);
-               /* Run the commit */
-               dbg_rcvry("committing");
-               return ubifs_run_commit(c);
-       }
-       /*
-        * There was no empty LEB so the used space in the dirtiest LEB must fit
-        * in the GC head LEB.
-        */
-       if (lp.free + lp.dirty < wbuf->offs) {
-               dbg_rcvry("LEB %d doesn't fit in GC head LEB %d:%d",
-                         lnum, wbuf->lnum, wbuf->offs);
-               err = ubifs_return_leb(c, lnum);
-               if (err)
+               if (err != -ENOSPC)
                        return err;
-               goto find_free;
+
+               dbg_rcvry("could not find a dirty LEB");
+               return grab_empty_leb(c);
        }
+
+       ubifs_assert(!(lp.flags & LPROPS_INDEX));
+       ubifs_assert(lp.free + lp.dirty >= wbuf->offs);
+
        /*
         * We run the commit before garbage collection otherwise subsequent
         * mounts will see the GC and orphan deletion in a different order.
@@ -1164,11 +1167,8 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
        err = ubifs_run_commit(c);
        if (err)
                return err;
-       /*
-        * The data in the dirtiest LEB fits in the GC head LEB, so do the GC
-        * - use locking to keep 'ubifs_assert()' happy.
-        */
-       dbg_rcvry("GC'ing LEB %d", lnum);
+
+       dbg_rcvry("GC'ing LEB %d", lp.lnum);
        mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
        err = ubifs_garbage_collect_leb(c, &lp);
        if (err >= 0) {
@@ -1184,37 +1184,17 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c)
                        err = -EINVAL;
                return err;
        }
-       if (err != LEB_RETAINED) {
-               dbg_err("GC returned %d", err);
+
+       ubifs_assert(err == LEB_RETAINED);
+       if (err != LEB_RETAINED)
                return -EINVAL;
-       }
+
        err = ubifs_leb_unmap(c, c->gc_lnum);
        if (err)
                return err;
-       dbg_rcvry("allocated LEB %d for GC", lnum);
-       return 0;
 
-find_free:
-       /*
-        * There is no GC head LEB or the free space in the GC head LEB is too
-        * 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) {
-               dbg_err("could not find an empty LEB");
-               return lnum;
-       }
-       /* And reset the index flag */
-       err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
-                                 LPROPS_INDEX, 0);
-       if (err)
-               return err;
-       c->gc_lnum = lnum;
-       dbg_rcvry("allocated LEB %d for GC", lnum);
-       /* Run the commit */
-       dbg_rcvry("committing");
-       return ubifs_run_commit(c);
+       dbg_rcvry("allocated LEB %d for GC", lp.lnum);
+       return 0;
 }
 
 /**
@@ -1456,7 +1436,7 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
        err = ubi_leb_change(c->ubi, lnum, c->sbuf, len, UBI_UNKNOWN);
        if (err)
                goto out;
-       dbg_rcvry("inode %lu at %d:%d size %lld -> %lld ",
+       dbg_rcvry("inode %lu at %d:%d size %lld -> %lld",
                  (unsigned long)e->inum, lnum, offs, i_size, e->d_size);
        return 0;
 
@@ -1505,18 +1485,21 @@ int ubifs_recover_size(struct ubifs_info *c)
                                e->i_size = le64_to_cpu(ino->size);
                        }
                }
+
                if (e->exists && e->i_size < e->d_size) {
-                       if (!e->inode && c->ro_mount) {
+                       if (c->ro_mount) {
                                /* Fix the inode size and pin it in memory */
                                struct inode *inode;
 
+                               ubifs_assert(!e->inode);
+
                                inode = ubifs_iget(c->vfs_sb, e->inum);
                                if (IS_ERR(inode))
                                        return PTR_ERR(inode);
                                if (inode->i_size < e->d_size) {
                                        dbg_rcvry("ino %lu size %lld -> %lld",
                                                  (unsigned long)e->inum,
-                                                 e->d_size, inode->i_size);
+                                                 inode->i_size, e->d_size);
                                        inode->i_size = e->d_size;
                                        ubifs_inode(inode)->ui_size = e->d_size;
                                        e->inode = inode;
@@ -1533,9 +1516,11 @@ int ubifs_recover_size(struct ubifs_info *c)
                                        iput(e->inode);
                        }
                }
+
                this = rb_next(this);
                rb_erase(&e->rb, &c->size_tree);
                kfree(e);
        }
+
        return 0;
 }