UBIFS: introduce helper functions for debugging checks and tests
[linux-3.10.git] / fs / ubifs / tnc_commit.c
index 8315387..8959c72 100644 (file)
@@ -23,6 +23,7 @@
 /* This file implements TNC functions for committing */
 
 #include "ubifs.h"
+#include <linux/random.h>
 
 /**
  * make_idx_node - make an index node for fill-the-gaps method of TNC commit.
@@ -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_gen(c)) {
                                /*
                                 * Do not print scary warnings if the debugging
                                 * option which forces in-the-gaps is enabled.
@@ -639,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);
@@ -685,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_gen(c) && !(random32() & 7))
                return -ENOSPC;
        return 0;
 }
@@ -888,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
@@ -903,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 */
@@ -983,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;