]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/ubifs/tnc_commit.c
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[karo-tx-linux.git] / fs / ubifs / tnc_commit.c
index 41920f357bbfc13e49668ef19fd6a0560ca195fc..4c15f07a8bb2b25d2f56f413f4e9883ee33e4b31 100644 (file)
@@ -22,6 +22,7 @@
 
 /* This file implements TNC functions for committing */
 
+#include <linux/random.h>
 #include "ubifs.h"
 
 /**
@@ -87,8 +88,12 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
        atomic_long_dec(&c->dirty_zn_cnt);
 
        ubifs_assert(ubifs_zn_dirty(znode));
-       ubifs_assert(test_bit(COW_ZNODE, &znode->flags));
+       ubifs_assert(ubifs_zn_cow(znode));
 
+       /*
+        * Note, unlike 'write_index()' we do not add memory barriers here
+        * because this function is called with @c->tnc_mutex locked.
+        */
        __clear_bit(DIRTY_ZNODE, &znode->flags);
        __clear_bit(COW_ZNODE, &znode->flags);
 
@@ -377,7 +382,7 @@ static int layout_in_gaps(struct ubifs_info *c, int cnt)
                                c->gap_lebs = NULL;
                                return err;
                        }
-                       if (dbg_force_in_the_gaps_enabled()) {
+                       if (!dbg_is_chk_index(c)) {
                                /*
                                 * Do not print scary warnings if the debugging
                                 * option which forces in-the-gaps is enabled.
@@ -491,25 +496,6 @@ static int layout_in_empty_space(struct ubifs_info *c)
                else
                        next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
 
-               if (c->min_io_size == 1) {
-                       buf_offs += ALIGN(len, 8);
-                       if (next_len) {
-                               if (buf_offs + next_len <= c->leb_size)
-                                       continue;
-                               err = ubifs_update_one_lp(c, lnum, 0,
-                                               c->leb_size - buf_offs, 0, 0);
-                               if (err)
-                                       return err;
-                               lnum = -1;
-                               continue;
-                       }
-                       err = ubifs_update_one_lp(c, lnum,
-                                       c->leb_size - buf_offs, 0, 0, 0);
-                       if (err)
-                               return err;
-                       break;
-               }
-
                /* Update buffer positions */
                wlen = used + len;
                used += ALIGN(len, 8);
@@ -658,7 +644,7 @@ static int get_znodes_to_commit(struct ubifs_info *c)
        }
        cnt += 1;
        while (1) {
-               ubifs_assert(!test_bit(COW_ZNODE, &znode->flags));
+               ubifs_assert(!ubifs_zn_cow(znode));
                __set_bit(COW_ZNODE, &znode->flags);
                znode->alt = 0;
                cnext = find_next_dirty(znode);
@@ -704,7 +690,7 @@ static int alloc_idx_lebs(struct ubifs_info *c, int cnt)
                c->ilebs[c->ileb_cnt++] = lnum;
                dbg_cmt("LEB %d", lnum);
        }
-       if (dbg_force_in_the_gaps())
+       if (dbg_is_chk_index(c) && !(random32() & 7))
                return -ENOSPC;
        return 0;
 }
@@ -830,7 +816,7 @@ static int write_index(struct ubifs_info *c)
        struct ubifs_idx_node *idx;
        struct ubifs_znode *znode, *cnext;
        int i, lnum, offs, len, next_len, buf_len, buf_offs, used;
-       int avail, wlen, err, lnum_pos = 0;
+       int avail, wlen, err, lnum_pos = 0, blen, nxt_offs;
 
        cnext = c->enext;
        if (!cnext)
@@ -907,7 +893,7 @@ static int write_index(struct ubifs_info *c)
                cnext = znode->cnext;
 
                ubifs_assert(ubifs_zn_dirty(znode));
-               ubifs_assert(test_bit(COW_ZNODE, &znode->flags));
+               ubifs_assert(ubifs_zn_cow(znode));
 
                /*
                 * It is important that other threads should see %DIRTY_ZNODE
@@ -922,6 +908,28 @@ static int write_index(struct ubifs_info *c)
                clear_bit(COW_ZNODE, &znode->flags);
                smp_mb__after_clear_bit();
 
+               /*
+                * We have marked the znode as clean but have not updated the
+                * @c->clean_zn_cnt counter. If this znode becomes dirty again
+                * before 'free_obsolete_znodes()' is called, then
+                * @c->clean_zn_cnt will be decremented before it gets
+                * incremented (resulting in 2 decrements for the same znode).
+                * This means that @c->clean_zn_cnt may become negative for a
+                * while.
+                *
+                * Q: why we cannot increment @c->clean_zn_cnt?
+                * A: because we do not have the @c->tnc_mutex locked, and the
+                *    following code would be racy and buggy:
+                *
+                *    if (!ubifs_zn_obsolete(znode)) {
+                *            atomic_long_inc(&c->clean_zn_cnt);
+                *            atomic_long_inc(&ubifs_clean_zn_cnt);
+                *    }
+                *
+                *    Thus, we just delay the @c->clean_zn_cnt update until we
+                *    have the mutex locked.
+                */
+
                /* Do not access znode from this point on */
 
                /* Update buffer positions */
@@ -938,65 +946,38 @@ static int write_index(struct ubifs_info *c)
                else
                        next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
 
-               if (c->min_io_size == 1) {
-                       /*
-                        * Write the prepared index node immediately if there is
-                        * no minimum IO size
-                        */
-                       err = ubifs_leb_write(c, lnum, c->cbuf, buf_offs,
-                                             wlen, UBI_SHORTTERM);
-                       if (err)
-                               return err;
-                       buf_offs += ALIGN(wlen, 8);
-                       if (next_len) {
-                               used = 0;
-                               avail = buf_len;
-                               if (buf_offs + next_len > c->leb_size) {
-                                       err = ubifs_update_one_lp(c, lnum,
-                                               LPROPS_NC, 0, 0, LPROPS_TAKEN);
-                                       if (err)
-                                               return err;
-                                       lnum = -1;
-                               }
+               nxt_offs = buf_offs + used + next_len;
+               if (next_len && nxt_offs <= c->leb_size) {
+                       if (avail > 0)
                                continue;
-                       }
+                       else
+                               blen = buf_len;
                } else {
-                       int blen, nxt_offs = buf_offs + used + next_len;
-
-                       if (next_len && nxt_offs <= c->leb_size) {
-                               if (avail > 0)
-                                       continue;
-                               else
-                                       blen = buf_len;
-                       } else {
-                               wlen = ALIGN(wlen, 8);
-                               blen = ALIGN(wlen, c->min_io_size);
-                               ubifs_pad(c, c->cbuf + wlen, blen - wlen);
-                       }
-                       /*
-                        * The buffer is full or there are no more znodes
-                        * to do
-                        */
-                       err = ubifs_leb_write(c, lnum, c->cbuf, buf_offs,
-                                             blen, UBI_SHORTTERM);
-                       if (err)
-                               return err;
-                       buf_offs += blen;
-                       if (next_len) {
-                               if (nxt_offs > c->leb_size) {
-                                       err = ubifs_update_one_lp(c, lnum,
-                                               LPROPS_NC, 0, 0, LPROPS_TAKEN);
-                                       if (err)
-                                               return err;
-                                       lnum = -1;
-                               }
-                               used -= blen;
-                               if (used < 0)
-                                       used = 0;
-                               avail = buf_len - used;
-                               memmove(c->cbuf, c->cbuf + blen, used);
-                               continue;
+                       wlen = ALIGN(wlen, 8);
+                       blen = ALIGN(wlen, c->min_io_size);
+                       ubifs_pad(c, c->cbuf + wlen, blen - wlen);
+               }
+
+               /* The buffer is full or there are no more znodes to do */
+               err = ubifs_leb_write(c, lnum, c->cbuf, buf_offs, blen,
+                                     UBI_SHORTTERM);
+               if (err)
+                       return err;
+               buf_offs += blen;
+               if (next_len) {
+                       if (nxt_offs > c->leb_size) {
+                               err = ubifs_update_one_lp(c, lnum, LPROPS_NC, 0,
+                                                         0, LPROPS_TAKEN);
+                               if (err)
+                                       return err;
+                               lnum = -1;
                        }
+                       used -= blen;
+                       if (used < 0)
+                               used = 0;
+                       avail = buf_len - used;
+                       memmove(c->cbuf, c->cbuf + blen, used);
+                       continue;
                }
                break;
        }
@@ -1029,7 +1010,7 @@ static void free_obsolete_znodes(struct ubifs_info *c)
        do {
                znode = cnext;
                cnext = znode->cnext;
-               if (test_bit(OBSOLETE_ZNODE, &znode->flags))
+               if (ubifs_zn_obsolete(znode))
                        kfree(znode);
                else {
                        znode->cnext = NULL;