bcache: avoid journal no-space deadlock by reserving 1 journal bucket
[linux-2.6-microblaze.git] / drivers / md / bcache / journal.c
index 7c2ca52..42407b4 100644 (file)
@@ -405,6 +405,11 @@ err:
        return ret;
 }
 
+void bch_journal_space_reserve(struct journal *j)
+{
+       j->do_reserve = true;
+}
+
 /* Journalling */
 
 static void btree_flush_write(struct cache_set *c)
@@ -621,12 +626,30 @@ static void do_journal_discard(struct cache *ca)
        }
 }
 
+static unsigned int free_journal_buckets(struct cache_set *c)
+{
+       struct journal *j = &c->journal;
+       struct cache *ca = c->cache;
+       struct journal_device *ja = &c->cache->journal;
+       unsigned int n;
+
+       /* In case njournal_buckets is not power of 2 */
+       if (ja->cur_idx >= ja->discard_idx)
+               n = ca->sb.njournal_buckets +  ja->discard_idx - ja->cur_idx;
+       else
+               n = ja->discard_idx - ja->cur_idx;
+
+       if (n > (1 + j->do_reserve))
+               return n - (1 + j->do_reserve);
+
+       return 0;
+}
+
 static void journal_reclaim(struct cache_set *c)
 {
        struct bkey *k = &c->journal.key;
        struct cache *ca = c->cache;
        uint64_t last_seq;
-       unsigned int next;
        struct journal_device *ja = &ca->journal;
        atomic_t p __maybe_unused;
 
@@ -649,12 +672,10 @@ static void journal_reclaim(struct cache_set *c)
        if (c->journal.blocks_free)
                goto out;
 
-       next = (ja->cur_idx + 1) % ca->sb.njournal_buckets;
-       /* No space available on this device */
-       if (next == ja->discard_idx)
+       if (!free_journal_buckets(c))
                goto out;
 
-       ja->cur_idx = next;
+       ja->cur_idx = (ja->cur_idx + 1) % ca->sb.njournal_buckets;
        k->ptr[0] = MAKE_PTR(0,
                             bucket_to_sector(c, ca->sb.d[ja->cur_idx]),
                             ca->sb.nr_this_dev);