summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/md/bcache/extents.c111
1 files changed, 71 insertions, 40 deletions
diff --git a/drivers/md/bcache/extents.c b/drivers/md/bcache/extents.c
index e7a0aa525627..cd4e1117d667 100644
--- a/drivers/md/bcache/extents.c
+++ b/drivers/md/bcache/extents.c
@@ -148,36 +148,6 @@ static unsigned PTR_TIER(struct cache_set *c, const struct bkey *k,
return dev < c->sb.nr_in_set ? CACHE_TIER(&c->members[dev]) : UINT_MAX;
}
-bool bch_extent_normalize(struct cache_set *c, struct bkey *k)
-{
- unsigned i;
- bool swapped;
-
- if (!KEY_SIZE(k)) {
- bch_set_extent_ptrs(k, 0);
- SET_KEY_DELETED(k, true);
- return true;
- }
-
- bch_extent_drop_stale(c, k);
-
- /* Bubble sort pointers by tier, lowest (fastest) tier first */
- do {
- swapped = false;
- for (i = 0; i + 1 < bch_extent_ptrs(k); i++) {
- if (PTR_TIER(c, k, i) > PTR_TIER(c, k, i + 1)) {
- swap(k->val[i], k->val[i + 1]);
- swapped = true;
- }
- }
- } while (swapped);
-
- if (!bch_extent_ptrs(k))
- SET_KEY_DELETED(k, true);
-
- return KEY_DELETED(k);
-}
-
static bool bch_ptr_normalize(struct btree_keys *bk,
struct bkey *k)
{
@@ -815,29 +785,35 @@ static bool bch_extent_invalid(struct btree_keys *bk, struct bkey *k)
return __bch_extent_invalid(b->c, k);
}
-static bool bch_extent_debug_invalid(struct btree_keys *bk, struct bkey *k)
+static bool __bch_extent_debug_invalid(struct cache_set *c, struct bkey *k)
{
- struct btree *b = container_of(bk, struct btree, keys);
- struct cache_set *c = b->c;
struct cache *ca;
struct bucket *g;
unsigned seq, stale, replicas_needed;
char buf[80];
bool bad;
int i;
+ unsigned ptrs_per_tier[CACHE_TIERS];
+ unsigned dev, tier, replicas;
- if (bch_extent_invalid(bk, k)) {
+ if (__bch_extent_invalid(c, k)) {
bch_extent_to_text(buf, sizeof(buf), k);
- btree_bug(b, "invalid bkey %s", buf);
+ cache_bug(c, "invalid bkey %s", buf);
return true;
}
+ memset(ptrs_per_tier, 0, sizeof(ptrs_per_tier));
+
replicas_needed = KEY_CACHED(k) ? 0
: CACHE_SET_DATA_REPLICAS_WANT(&c->sb);
rcu_read_lock();
for (i = bch_extent_ptrs(k) - 1; i >= 0; --i) {
+ dev = PTR_DEV(k, i);
+ tier = CACHE_TIER(&c->members[dev]);
+ ptrs_per_tier[tier]++;
+
if ((ca = PTR_CACHE(c, k, i))) {
g = PTR_BUCKET(c, ca, k, i);
@@ -852,13 +828,13 @@ static bool bch_extent_debug_invalid(struct btree_keys *bk, struct bkey *k)
stale = ptr_stale(c, ca, k, i);
- btree_bug_on(stale > 96, b,
+ cache_bug_on(stale > 96, c,
"key too stale: %i",
stale);
- btree_bug_on(
+ cache_bug_on(
stale && replicas_needed &&
- KEY_SIZE(k), b,
+ KEY_SIZE(k), c,
"stale dirty pointer:\n"
"bucket %zu gen %i != %llu",
PTR_BUCKET_NR(c, k, i),
@@ -881,12 +857,23 @@ static bool bch_extent_debug_invalid(struct btree_keys *bk, struct bkey *k)
replicas_needed--;
}
- rcu_read_unlock();
+ replicas = CACHE_SET_DATA_REPLICAS_WANT(&c->sb);
+ for (i = 0; i < CACHE_TIERS; i++)
+ if (ptrs_per_tier[i] > replicas)
+ goto bad_ptrs;
+ rcu_read_unlock();
return false;
+
+bad_ptrs:
+ cache_bug(c, "too many pointers: %u but want %u in tier %u",
+ ptrs_per_tier[i], replicas, i);
+ rcu_read_unlock();
+ return true;
+
err:
bch_extent_to_text(buf, sizeof(buf), k);
- btree_bug(b, "extent pointer %i bad: %s:\nbucket %zu prio %i "
+ cache_bug(c, "extent pointer %i bad: %s:\nbucket %zu prio %i "
"gen %i last_gc %i mark 0x%08x", i,
buf, PTR_BUCKET_NR(c, k, i),
g->read_prio, PTR_BUCKET_GEN(c, ca, k, i),
@@ -895,6 +882,50 @@ err:
return true;
}
+static bool bch_extent_debug_invalid(struct btree_keys *bk, struct bkey *k)
+{
+ struct btree *b = container_of(bk, struct btree, keys);
+ struct cache_set *c = b->c;
+
+ return __bch_extent_debug_invalid(c, k);
+}
+
+bool bch_extent_normalize(struct cache_set *c, struct bkey *k)
+{
+ unsigned i;
+ bool swapped;
+
+ if (!KEY_SIZE(k)) {
+ bch_set_extent_ptrs(k, 0);
+ SET_KEY_DELETED(k, true);
+ return true;
+ }
+
+ bch_extent_drop_stale(c, k);
+
+ /* Bubble sort pointers by tier, lowest (fastest) tier first */
+ do {
+ swapped = false;
+ for (i = 0; i + 1 < bch_extent_ptrs(k); i++) {
+ if (PTR_TIER(c, k, i) > PTR_TIER(c, k, i + 1)) {
+ swap(k->val[i], k->val[i + 1]);
+ swapped = true;
+ }
+ }
+ } while (swapped);
+
+ if (!bch_extent_ptrs(k))
+ SET_KEY_DELETED(k, true);
+
+#ifdef CONFIG_BCACHE_DEBUG
+ if (expensive_debug_checks(c) &&
+ __bch_extent_debug_invalid(c, k))
+ return true;
+#endif
+
+ return KEY_DELETED(k);
+}
+
struct cache *bch_extent_pick_ptr(struct cache_set *c, const struct bkey *k,
unsigned *ptr)
{