summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/md/bcache/bcache.h1
-rw-r--r--drivers/md/bcache/bkey.c35
-rw-r--r--drivers/md/bcache/bkey.h1
-rw-r--r--drivers/md/bcache/bset.c68
-rw-r--r--drivers/md/bcache/bset.h2
-rw-r--r--drivers/md/bcache/debug.c155
6 files changed, 204 insertions, 58 deletions
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 3f9294c17cda..8ee369bcbf13 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -488,6 +488,7 @@ struct btree_debug {
unsigned id;
struct dentry *btree;
struct dentry *btree_format;
+ struct dentry *failed;
};
struct cache_set {
diff --git a/drivers/md/bcache/bkey.c b/drivers/md/bcache/bkey.c
index 37e3cb134ae9..1c48ea3e8a6c 100644
--- a/drivers/md/bcache/bkey.c
+++ b/drivers/md/bcache/bkey.c
@@ -9,29 +9,30 @@
const struct bkey_format bch_bkey_format_current = BKEY_FORMAT_CURRENT;
-#ifdef CONFIG_BCACHE_DEBUG
-
-static void to_binary(char *out, const void *p, unsigned nr_bytes)
+void bch_to_binary(char *out, const u64 *p, unsigned nr_bits)
{
- unsigned i;
+ unsigned bit = high_bit_offset, done = 0;
while (1) {
- u8 b = *((u8 *) p);
-
- for (i = 0; i < 8; i++) {
- *out++ = b & (1U << (7 - i)) ? '1' : '0';
+ while (bit < 64) {
+ if (done && !(done % 8))
+ *out++ = ' ';
+ *out++ = *p & (1ULL << (63 - bit)) ? '1' : '0';
+ bit++;
+ done++;
+ if (done == nr_bits) {
+ *out++ = '\0';
+ return;
+ }
}
- if (!--nr_bytes)
- break;
-
- *out++ = ' ';
- p++;
+ p = next_word(p);
+ bit = 0;
}
-
- *out++ = '\0';
}
+#ifdef CONFIG_BCACHE_DEBUG
+
static void bch_bkey_pack_verify(const struct bkey_packed *packed,
const struct bkey *unpacked,
const struct bkey_format *format)
@@ -51,8 +52,8 @@ static void bch_bkey_pack_verify(const struct bkey_packed *packed,
bch_bkey_to_text(buf1, sizeof(buf1), unpacked);
bch_bkey_to_text(buf2, sizeof(buf2), &tmp);
- to_binary(buf3, unpacked, 10);
- to_binary(buf4, high_word(format, packed), 10);
+ bch_to_binary(buf3, (void *) unpacked, 80);
+ bch_to_binary(buf4, high_word(format, packed), 80);
panic("keys differ: format u64s %u fields %u %u %u %u %u\n%s\n%s\n%s\n%s\n",
format->key_u64s,
diff --git a/drivers/md/bcache/bkey.h b/drivers/md/bcache/bkey.h
index 1e68822e13bd..f0bd3faf0d76 100644
--- a/drivers/md/bcache/bkey.h
+++ b/drivers/md/bcache/bkey.h
@@ -6,6 +6,7 @@
#include "util.h"
+void bch_to_binary(char *, const u64 *, unsigned);
int bch_bkey_to_text(char *, size_t, const struct bkey *);
/* bkey with split value, const */
diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c
index fb559e3f2115..4940d961755f 100644
--- a/drivers/md/bcache/bset.c
+++ b/drivers/md/bcache/bset.c
@@ -1773,3 +1773,71 @@ void bch_btree_keys_stats(struct btree_keys *b, struct bset_stats *stats)
}
}
}
+
+int bch_bkey_print_bfloat(struct btree_keys *b, struct bkey_packed *k,
+ char *buf, size_t size)
+{
+ struct bset_tree *t = bch_bkey_to_bset(b, k);
+ struct bkey_packed *l, *r, *p;
+ struct bkey uk, up;
+ char buf1[200], buf2[200];
+ unsigned j;
+
+ if (!size)
+ return 0;
+
+ if (!bset_has_aux_tree(t))
+ goto out;
+
+ j = inorder_to_tree(bkey_to_cacheline(t, k), t);
+ if (j &&
+ j < t->size &&
+ k == tree_to_bkey(t, j))
+ switch (t->tree[j].exponent) {
+ case BFLOAT_FAILED_UNPACKED:
+ uk = bkey_unpack_key(&b->format, k);
+ return scnprintf(buf, size,
+ " failed unpacked at depth %u\n"
+ "\t%llu:%llu\n",
+ ilog2(j),
+ uk.p.inode, uk.p.offset);
+ case BFLOAT_FAILED_PREV:
+ p = tree_to_prev_bkey(t, j);
+ l = is_power_of_2(j)
+ ? t->data->start
+ : tree_to_prev_bkey(t, j >> ffs(j));
+ r = is_power_of_2(j + 1)
+ ? bset_bkey_idx(t->data,
+ le16_to_cpu(t->data->u64s) - t->end.u64s)
+ : tree_to_bkey(t, j >> (ffz(j) + 1));
+
+ up = bkey_unpack_key(&b->format, p);
+ uk = bkey_unpack_key(&b->format, k);
+ bch_to_binary(buf1, high_word(&b->format, p), bkey_format_key_bits(&b->format));
+ bch_to_binary(buf2, high_word(&b->format, k), bkey_format_key_bits(&b->format));
+
+ return scnprintf(buf, size,
+ " failed prev at depth %u\n"
+ "\tkey starts at bit %u but first differing bit at %u\n"
+ "\t%llu:%llu\n"
+ "\t%llu:%llu\n"
+ "\t%s\n"
+ "\t%s\n",
+ ilog2(j),
+ bkey_greatest_differing_bit(&b->format, l, r),
+ bkey_greatest_differing_bit(&b->format, p, k),
+ uk.p.inode, uk.p.offset,
+ up.p.inode, up.p.offset,
+ buf1, buf2);
+ case BFLOAT_FAILED_OVERFLOW:
+ uk = bkey_unpack_key(&b->format, k);
+ return scnprintf(buf, size,
+ " failed overflow at depth %u\n"
+ "\t%llu:%llu\n",
+ ilog2(j),
+ uk.p.inode, uk.p.offset);
+ }
+out:
+ *buf = '\0';
+ return 0;
+}
diff --git a/drivers/md/bcache/bset.h b/drivers/md/bcache/bset.h
index 512bea8b033e..bf136be0873f 100644
--- a/drivers/md/bcache/bset.h
+++ b/drivers/md/bcache/bset.h
@@ -596,6 +596,8 @@ struct bset_stats {
};
void bch_btree_keys_stats(struct btree_keys *, struct bset_stats *);
+int bch_bkey_print_bfloat(struct btree_keys *, struct bkey_packed *,
+ char *, size_t);
/* Debug stuff */
diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c
index 88c082a4ec39..3ada6d9c4e56 100644
--- a/drivers/md/bcache/debug.c
+++ b/drivers/md/bcache/debug.c
@@ -295,6 +295,50 @@ static const struct file_operations btree_debug_ops = {
.read = bch_read_btree,
};
+static int print_btree_node(struct dump_iter *i, struct btree *b)
+{
+ const struct bkey_format *f = &b->keys.format;
+ struct bset_stats stats;
+
+ memset(&stats, 0, sizeof(stats));
+
+ bch_btree_keys_stats(&b->keys, &stats);
+
+ i->bytes = scnprintf(i->buf, sizeof(i->buf),
+ "l %u %llu:%llu - %llu:%llu:\n"
+ " format: u64s %u fields %u %u %u %u %u\n"
+ " bytes used %zu/%zu (%zu%% full)\n"
+ " nr packed keys %u\n"
+ " nr unpacked keys %u\n"
+ " floats %zu\n"
+ " failed unpacked %zu\n"
+ " failed prev %zu\n"
+ " failed overflow %zu\n",
+ b->level,
+ b->data->min_key.inode,
+ b->data->min_key.offset,
+ b->data->max_key.inode,
+ b->data->max_key.offset,
+ f->key_u64s,
+ f->bits_per_field[0],
+ f->bits_per_field[1],
+ f->bits_per_field[2],
+ f->bits_per_field[3],
+ f->bits_per_field[4],
+ b->keys.nr.live_u64s * sizeof(u64),
+ btree_bytes(i->c) - sizeof(struct btree_node),
+ (b->keys.nr.live_u64s * sizeof(u64) * 100) /
+ (btree_bytes(i->c) - sizeof(struct btree_node)),
+ b->keys.nr.packed_keys,
+ b->keys.nr.unpacked_keys,
+ stats.floats,
+ stats.failed_unpacked,
+ stats.failed_prev,
+ stats.failed_overflow);
+
+ return flush_buf(i);
+}
+
static ssize_t bch_read_btree_formats(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
@@ -315,46 +359,7 @@ static ssize_t bch_read_btree_formats(struct file *file, char __user *buf,
return i->ret;
for_each_btree_node(&iter, i->c, i->id, i->from, 0, b) {
- const struct bkey_format *f = &b->keys.format;
- struct bset_stats stats;
-
- memset(&stats, 0, sizeof(stats));
-
- bch_btree_keys_stats(&b->keys, &stats);
-
- i->bytes = scnprintf(i->buf, sizeof(i->buf),
- "l %u %llu:%llu - %llu:%llu:\n"
- "\tformat: u64s %u fields %u %u %u %u %u\n"
- "\tbytes used %zu/%zu (%zu%% full)\n"
- "\tnr packed keys %u\n"
- "\tnr unpacked keys %u\n"
- "\tfloats %zu\n"
- "\tfailed unpacked %zu\n"
- "\tfailed prev %zu\n"
- "\tfailed overflow %zu\n",
- b->level,
- b->data->min_key.inode,
- b->data->min_key.offset,
- b->data->max_key.inode,
- b->data->max_key.offset,
- f->key_u64s,
- f->bits_per_field[0],
- f->bits_per_field[1],
- f->bits_per_field[2],
- f->bits_per_field[3],
- f->bits_per_field[4],
- b->keys.nr.live_u64s * sizeof(u64),
- btree_bytes(i->c) - sizeof(struct btree_node),
- (b->keys.nr.live_u64s * sizeof(u64) * 100) /
- (btree_bytes(i->c) - sizeof(struct btree_node)),
- b->keys.nr.packed_keys,
- b->keys.nr.unpacked_keys,
- stats.floats,
- stats.failed_unpacked,
- stats.failed_prev,
- stats.failed_overflow);
-
- err = flush_buf(i);
+ err = print_btree_node(i, b);
if (err)
break;
@@ -381,6 +386,68 @@ static const struct file_operations btree_format_debug_ops = {
.read = bch_read_btree_formats,
};
+static ssize_t bch_read_bfloat_failed(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct dump_iter *i = file->private_data;
+ struct btree_iter iter;
+ struct bkey_s_c k;
+ struct btree *prev_node = NULL;
+ int err;
+
+ i->ubuf = buf;
+ i->size = size;
+ i->ret = 0;
+
+ err = flush_buf(i);
+ if (err)
+ return err;
+
+ if (!i->size)
+ return i->ret;
+
+ bch_btree_iter_init(&iter, i->c, i->id, i->from);
+
+ while ((k = bch_btree_iter_peek(&iter)).k) {
+ struct btree_keys *b = &iter.nodes[0]->keys;
+ struct btree_node_iter *node_iter = &iter.node_iters[0];
+ struct bkey_packed *_k = bch_btree_node_iter_peek(node_iter, b);
+
+ if (iter.nodes[0] != prev_node) {
+ err = print_btree_node(i, iter.nodes[0]);
+ if (err)
+ break;
+ }
+ prev_node = iter.nodes[0];
+
+ i->bytes = bch_bkey_print_bfloat(b, _k, i->buf, sizeof(i->buf));
+
+ err = flush_buf(i);
+ if (err)
+ break;
+
+ bch_btree_iter_advance_pos(&iter);
+ i->from = iter.pos;
+
+ err = flush_buf(i);
+ if (err)
+ break;
+
+ if (!i->size)
+ break;
+ }
+ bch_btree_iter_unlock(&iter);
+
+ return err < 0 ? err : i->ret;
+}
+
+static const struct file_operations bfloat_failed_debug_ops = {
+ .owner = THIS_MODULE,
+ .open = bch_dump_open,
+ .release = bch_dump_release,
+ .read = bch_read_bfloat_failed,
+};
+
void bch_debug_exit_cache_set(struct cache_set *c)
{
if (!IS_ERR_OR_NULL(c->debug))
@@ -390,7 +457,7 @@ void bch_debug_exit_cache_set(struct cache_set *c)
void bch_debug_init_cache_set(struct cache_set *c)
{
struct btree_debug *bd;
- char name[50];
+ char name[100];
if (IS_ERR_OR_NULL(bch_debug))
return;
@@ -413,6 +480,12 @@ void bch_debug_init_cache_set(struct cache_set *c)
bd->btree_format = debugfs_create_file(name, 0400, c->debug, bd,
&btree_format_debug_ops);
+
+ snprintf(name, sizeof(name), "%s-bfloat-failed",
+ bch_btree_id_names[bd->id]);
+
+ bd->failed = debugfs_create_file(name, 0400, c->debug, bd,
+ &bfloat_failed_debug_ops);
}
}