summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2015-03-23 17:33:11 -0700
committerKent Overstreet <kent.overstreet@gmail.com>2016-10-07 12:33:35 -0800
commit3d70ad1e73cf671ef0d23bedce76f7d1ef68f13e (patch)
treeffe897f8f3e2a67408f88877768e022377581918
parentbf53814dd8a337df1bb6fab96946c14df81b372c (diff)
bcache: Don't crash when a journal entry is missing
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r--drivers/md/bcache/bcache.h2
-rw-r--r--drivers/md/bcache/fifo.h6
-rw-r--r--drivers/md/bcache/journal.c51
3 files changed, 42 insertions, 17 deletions
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 8bc5be3124a6..8400c59db60c 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -688,6 +688,7 @@ static inline unsigned bucket_bytes(const struct cache *ca)
/* Error handling macros */
+/* The underscore versions merely log an error, they don't fail the cache set */
#define __bch_cache_set_error(c, fmt, ...) \
printk(KERN_ERR "bcache: error on %pU: " fmt, \
(c)->sb.set_uuid.b, ##__VA_ARGS__)
@@ -700,6 +701,7 @@ do { \
##__VA_ARGS__); \
} while (0)
+/* These do fail the cache set */
#define bch_cache_set_error(c, ...) \
do { \
__bch_cache_set_error(c, __VA_ARGS__); \
diff --git a/drivers/md/bcache/fifo.h b/drivers/md/bcache/fifo.h
index 6ff02a09b197..8bd6168aea06 100644
--- a/drivers/md/bcache/fifo.h
+++ b/drivers/md/bcache/fifo.h
@@ -122,5 +122,11 @@ do { \
(_entry = (_fifo)->data[(_iter)], true)); \
_iter = ((_iter) + 1) & (_fifo)->mask)
+#define fifo_for_each_entry_ptr(_ptr, _fifo, _iter) \
+ for (_iter = (_fifo)->front; \
+ ((_iter != (_fifo)->back) && \
+ (_ptr = &(_fifo)->data[(_iter)], true)); \
+ _iter = ((_iter) + 1) & (_fifo)->mask)
+
#endif /* _BCACHE_FIFO_H */
diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
index e9a39c71cef1..d20f2d2a143f 100644
--- a/drivers/md/bcache/journal.c
+++ b/drivers/md/bcache/journal.c
@@ -260,6 +260,12 @@ reread: left = ca->mi.bucket_size - offset;
goto err;
}
+ if (j->last_seq > j->seq) {
+ __bch_cache_error(ca,
+ "invalid journal entry: last_seq > seq");
+ goto err;
+ }
+
ret = journal_add_entry(jlist, j);
if (ret < 0)
goto err;
@@ -427,8 +433,9 @@ const char *bch_journal_read(struct cache_set *c, struct list_head *list)
{
struct jset_entry *prio_ptrs;
struct journal_list jlist;
+ struct journal_replay *i;
struct jset *j;
- struct list_head *l;
+ atomic_t *p;
struct cache *ca;
unsigned iter;
@@ -457,20 +464,37 @@ const char *bch_journal_read(struct cache_set *c, struct list_head *list)
if (list_empty(list))
return "no journal entries found";
- /* XXX: this can't tolerate missing journal entries */
- list_for_each(l, list) {
- atomic_t p = { 1 };
+ j = &list_entry(list->prev, struct journal_replay, list)->j;
- BUG_ON(!fifo_push(&c->journal.pin, p));
- atomic_set(&fifo_back(&c->journal.pin), 1);
- }
+ if (j->seq - j->last_seq + 1 > c->journal.pin.size)
+ return "too many journal entries open for refcount fifo";
- j = &list_entry(list->prev, struct journal_replay, list)->j;
+ c->journal.pin.back = j->seq - j->last_seq + 1;
c->journal.seq = j->seq;
-
BUG_ON(last_seq(&c->journal) != j->last_seq);
+ i = list_first_entry(list, struct journal_replay, list);
+
+ fifo_for_each_entry_ptr(p, &c->journal.pin, iter) {
+ u64 seq = last_seq(&c->journal) +
+ fifo_entry_idx(&c->journal.pin, p);
+
+ if (!i || i->j.seq != seq) {
+ atomic_set(p, 0);
+
+ bch_cache_set_error(c,
+ "bcache: journal entry %llu missing! (replaying %llu-%llu)",
+ seq, last_seq(&c->journal), c->journal.seq);
+ } else {
+ atomic_set(p, 1);
+
+ i = list_next_entry(i, list);
+ if (&i->list == list)
+ i = NULL;
+ }
+ }
+
prio_ptrs = bch_journal_find_entry(j, JKEYS_PRIO_PTRS, 0);
if (!prio_ptrs)
return "prio bucket ptrs not found";
@@ -535,13 +559,7 @@ int bch_journal_replay(struct cache_set *c, struct list_head *list)
struct journal_replay *i =
list_entry(list->prev, struct journal_replay, list);
- uint64_t start = i->j.last_seq, end = i->j.seq, n = start;
-
list_for_each_entry(i, list, list) {
- cache_set_err_on(n != i->j.seq, c,
-"bcache: journal entries %llu-%llu missing! (replaying %llu-%llu)",
- n, i->j.seq - 1, start, end);
-
c->journal.cur_pin =
&c->journal.pin.data[((c->journal.pin.back - 1 -
(c->journal.seq - i->j.seq)) &
@@ -561,12 +579,11 @@ int bch_journal_replay(struct cache_set *c, struct list_head *list)
if (atomic_dec_and_test(c->journal.cur_pin))
wake_up(&c->journal.wait);
- n = i->j.seq + 1;
entries++;
}
pr_info("journal replay done, %i keys in %i entries, seq %llu",
- keys, entries, end);
+ keys, entries, c->journal.seq);
err:
if (ret)
pr_err("journal replay error: %d", ret);