summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/md/bcache/bkey.c17
-rw-r--r--drivers/md/bcache/btree_update.c22
-rw-r--r--drivers/md/bcache/extents.c231
3 files changed, 157 insertions, 113 deletions
diff --git a/drivers/md/bcache/bkey.c b/drivers/md/bcache/bkey.c
index 9bf020a30f00..44f54505f415 100644
--- a/drivers/md/bcache/bkey.c
+++ b/drivers/md/bcache/bkey.c
@@ -555,6 +555,9 @@ void bch_bkey_format_init(struct bkey_format_state *s)
for (i = 0; i < ARRAY_SIZE(s->field_max); i++)
s->field_max[i] = 0;
+
+ /* Make sure we can store a size of 0: */
+ s->field_min[BKEY_FIELD_SIZE] = 0;
}
static void __bkey_format_add(struct bkey_format_state *s,
@@ -569,14 +572,12 @@ static void __bkey_format_add(struct bkey_format_state *s,
*/
void bch_bkey_format_add_key(struct bkey_format_state *s, const struct bkey *k)
{
- unsigned field = 0;
-
- __bkey_format_add(s, field++, k->p.inode);
- __bkey_format_add(s, field++, k->p.offset);
- __bkey_format_add(s, field++, k->p.snapshot);
- __bkey_format_add(s, field++, k->size);
- __bkey_format_add(s, field++, k->version);
- EBUG_ON(field != BKEY_NR_FIELDS);
+ __bkey_format_add(s, BKEY_FIELD_INODE, k->p.inode);
+ __bkey_format_add(s, BKEY_FIELD_OFFSET, k->p.offset);
+ __bkey_format_add(s, BKEY_FIELD_OFFSET, bkey_start_offset(k));
+ __bkey_format_add(s, BKEY_FIELD_SNAPSHOT, k->p.snapshot);
+ __bkey_format_add(s, BKEY_FIELD_SIZE, k->size);
+ __bkey_format_add(s, BKEY_FIELD_VERSION, k->version);
}
void bch_bkey_format_add_pos(struct bkey_format_state *s, struct bpos p)
diff --git a/drivers/md/bcache/btree_update.c b/drivers/md/bcache/btree_update.c
index 7d67b5625dbf..dab70f795904 100644
--- a/drivers/md/bcache/btree_update.c
+++ b/drivers/md/bcache/btree_update.c
@@ -30,28 +30,6 @@ void __bch_btree_calc_format(struct bkey_format_state *s, struct btree *b)
for_each_btree_node_key_unpack(&b->keys, k, &iter, &unpacked)
bch_bkey_format_add_key(s, k.k);
-
- if (b->keys.ops->is_extents) {
- /*
- * Extents need special consideration because of
- * bch_insert_fixup_extent() - they have to be modified in
- * place, and successfully repack, when insert an overlapping
- * extent:
- */
- bch_bkey_format_add_pos(s, b->data->min_key);
- bch_bkey_format_add_pos(s, b->data->max_key);
-
- /*
- * If we span multiple inodes, need to be able to store an
- * offset of 0:
- */
- if (s->field_min[BKEY_FIELD_INODE] !=
- s->field_max[BKEY_FIELD_INODE])
- s->field_min[BKEY_FIELD_OFFSET] = 0;
-
- /* Make sure we can store a size of 0: */
- s->field_min[BKEY_FIELD_SIZE] = 0;
- }
}
static struct bkey_format bch_btree_calc_format(struct btree *b)
diff --git a/drivers/md/bcache/extents.c b/drivers/md/bcache/extents.c
index 6eec881ebb70..620780ced03a 100644
--- a/drivers/md/bcache/extents.c
+++ b/drivers/md/bcache/extents.c
@@ -630,15 +630,23 @@ void bch_key_resize(struct bkey *k,
* that we have to unpack the key, modify the unpacked key - then this
* copies/repacks the unpacked to the original as necessary.
*/
-static void extent_save(struct bkey_packed *dst, struct bkey *src,
- const struct bkey_format *f)
+static bool __extent_save(struct bkey_packed *dst, struct bkey *src,
+ const struct bkey_format *f)
{
struct bkey_i *dst_unpacked;
- if ((dst_unpacked = packed_to_bkey(dst)))
+ if ((dst_unpacked = packed_to_bkey(dst))) {
dst_unpacked->k = *src;
- else
- BUG_ON(!bkey_pack_key(dst, src, f));
+ return true;
+ } else {
+ return bkey_pack_key(dst, src, f);
+ }
+}
+
+static void extent_save(struct bkey_packed *dst, struct bkey *src,
+ const struct bkey_format *f)
+{
+ BUG_ON(!__extent_save(dst, src, f));
}
/*
@@ -1287,19 +1295,47 @@ void bch_insert_fixup_extent(struct btree_iter *iter,
bch_btree_node_iter_advance(node_iter, &b->keys);
break;
- case BCH_EXTENT_OVERLAP_ALL:
+ case BCH_EXTENT_OVERLAP_ALL: {
+ struct bpos orig_pos = k.k->p;
+
/* The insert key completely covers k, invalidate k */
if (!bkey_deleted(_k))
btree_keys_account_key_drop(&b->keys.nr, _k);
bch_drop_subtract(iter, k, &stats);
k.k->p = bkey_start_pos(&insert->k);
- extent_save(_k, k.k, f);
+ if (!__extent_save(_k, k.k, f)) {
+ /*
+ * Couldn't repack: we aren't necessarily able
+ * to repack if the new key is outside the range
+ * of the old extent, so we have to split
+ * @insert:
+ */
+ struct bkey_i *split =
+ bch_key_split(orig_pos, insert);
+
+ k.k->p = orig_pos;
+ extent_save(_k, k.k, f);
+
+ extent_insert_do_pos_hook(hook, iter, split,
+ bkey_s_null, res, &stats);
+ if (split->k.size)
+ bch_btree_insert_and_journal(iter,
+ split, res);
+
+ /*
+ * We split and inserted upto at k.k->p - that
+ * has to coincide with iter->pos, so that we
+ * don't have anything more we have to insert
+ * until we recheck our journal reservation:
+ */
+ BUG_ON(bkey_cmp(iter->pos, k.k->p));
+ }
bch_bset_fix_invalidated_key(&b->keys, _k);
bch_btree_node_iter_advance(node_iter, &b->keys);
break;
-
+ }
case BCH_EXTENT_OVERLAP_MIDDLE:
/*
* The insert key falls 'in the middle' of k
@@ -1867,19 +1903,103 @@ static enum merge_result bch_extent_merge(struct btree_keys *bk,
static bool extent_i_save(struct bkey_packed *dst, struct bkey_i *src,
const struct bkey_format *f)
{
- struct bkey_i *dst_unpacked;
- bool ret;
-
BUG_ON(bkeyp_val_u64s(f, dst) != bkey_val_u64s(&src->k));
- if ((dst_unpacked = packed_to_bkey(dst))) {
- bkey_copy(dst_unpacked, src);
- ret = true;
+ if (!__extent_save(dst, &src->k, f))
+ return false;
+
+ memcpy(bkeyp_val(f, dst), &src->v, bkey_val_bytes(&src->k));
+ return true;
+}
+
+static bool extent_merge_one_overlapping(struct btree_keys *b,
+ struct bkey *m, struct bpos new_pos,
+ struct bkey_packed *k, struct bkey uk,
+ bool check, bool could_pack)
+{
+ BUG_ON(!bkey_deleted(k));
+
+ if (check) {
+ if (bkey_packed(k) && !could_pack)
+ return false;
} else {
- ret = bkey_pack(dst, src, f);
+ uk.p = new_pos;
+ extent_save(k, &uk, &b->format);
+ bch_bset_fix_invalidated_key(b, k);
}
- return ret;
+ return true;
+}
+
+static bool extent_merge_do_overlapping(struct btree_keys *b,
+ struct btree_node_iter *iter,
+ struct bkey *m,
+ bool back_merge, bool check)
+{
+ const struct bkey_format *f = &b->format;
+ struct bset_tree *t;
+ struct bkey_packed *k;
+ struct bkey uk;
+ struct bpos new_pos = back_merge ? bkey_start_pos(m) : m->p;
+ bool could_pack = bkey_pack_pos((void *) &uk, new_pos, f);
+
+ /*
+ * @m is the new merged extent:
+ *
+ * The merge took place in the last bset; we know there can't be any 0
+ * size extents overlapping with m there because if so they would have
+ * been between the two extents we merged.
+ *
+ * But in the other bsets, we have to check for and fix such extents:
+ */
+ for (t = b->set; t < b->set + b->nsets; t++) {
+ if (!t->data->u64s)
+ continue;
+
+ /*
+ * if we don't find this bset in the iterator we already got to
+ * the end of that bset, so start searching from the end.
+ */
+ k = bch_btree_node_iter_bset_pos(iter, b, t->data) ?:
+ bkey_prev(t, bset_bkey_last(t->data));
+
+ if (back_merge) {
+ /*
+ * Back merge: 0 size extents will be before the key
+ * that was just inserted (and thus the iterator
+ * position) - walk backwards to find them
+ */
+ for (;
+ k &&
+ (uk = bkey_unpack_key(f, k),
+ bkey_cmp(uk.p, bkey_start_pos(m)) > 0);
+ k = bkey_prev(t, k)) {
+ if (bkey_cmp(uk.p, m->p) >= 0)
+ continue;
+
+ if (!extent_merge_one_overlapping(b, m, new_pos,
+ k, uk, check, could_pack))
+ return false;
+ }
+ } else {
+ /* Front merge - walk forwards */
+ for (;
+ k != bset_bkey_last(t->data) &&
+ (uk = bkey_unpack_key(f, k),
+ bkey_cmp(uk.p, m->p) < 0);
+ k = bkey_next(k)) {
+ if (bkey_cmp(uk.p,
+ bkey_start_pos(m)) <= 0)
+ continue;
+
+ if (!extent_merge_one_overlapping(b, m, new_pos,
+ k, uk, check, could_pack))
+ return false;
+ }
+ }
+ }
+
+ return true;
}
/*
@@ -1897,9 +2017,7 @@ static bool bch_extent_merge_inline(struct btree_keys *b,
bool back_merge)
{
const struct bkey_format *f = &b->format;
- struct bset_tree *t;
- struct bkey_packed *k, *m;
- struct bkey uk;
+ struct bkey_packed *m;
BKEY_PADDED(k) li;
BKEY_PADDED(k) ri;
struct bkey_i *mi;
@@ -1915,13 +2033,18 @@ static bool bch_extent_merge_inline(struct btree_keys *b,
m = back_merge ? l : r;
mi = back_merge ? &li.k : &ri.k;
- BUG_ON(m < b->set->data->start ||
+ /* l & r should be in last bset: */
+ BUG_ON(m < bset_tree_last(b)->data->start ||
m >= bset_bkey_last(bset_tree_last(b)->data));
switch (bch_extent_merge(b, &li.k, &ri.k)) {
case BCH_MERGE_NOMERGE:
return false;
case BCH_MERGE_PARTIAL:
+ if (!extent_merge_do_overlapping(b, iter, &li.k.k,
+ back_merge, true))
+ return false;
+
if (!extent_i_save(m, mi, f))
return false;
@@ -1933,6 +2056,10 @@ static bool bch_extent_merge_inline(struct btree_keys *b,
ret = false;
break;
case BCH_MERGE_MERGE:
+ if (!extent_merge_do_overlapping(b, iter, &li.k.k,
+ back_merge, true))
+ return false;
+
if (!extent_i_save(m, &li.k, f))
return false;
@@ -1942,69 +2069,7 @@ static bool bch_extent_merge_inline(struct btree_keys *b,
BUG();
}
- /*
- * l is the output of bch_extent_merge(), m is the extent that was in
- * the btree.
- *
- * Iterate over every bset that doesn't contain m, find the iterator's
- * position and search from there for 0 size extents that overlap with
- * m.
- */
- for (t = b->set; t <= b->set + b->nsets; t++) {
- if (!t->data->u64s ||
- (m >= t->data->start &&
- m < bset_bkey_last(t->data)))
- continue;
-
- /*
- * if we don't find this bset in the iterator we already got to
- * the end of that bset, so start searching from the end.
- */
- k = bch_btree_node_iter_bset_pos(iter, b, t->data) ?:
- bkey_prev(t, bset_bkey_last(t->data));
-
- if (back_merge) {
- /*
- * Back merge: 0 size extents will be before the key
- * that was just inserted (and thus the iterator
- * position) - walk backwards to find them
- */
- for (;
- k &&
- (uk = bkey_unpack_key(f, k),
- bkey_cmp(uk.p, bkey_start_pos(&li.k.k)) > 0);
- k = bkey_prev(t, k)) {
- if (bkey_cmp(uk.p, li.k.k.p) >= 0)
- continue;
-
- BUG_ON(!bkey_deleted(k));
-
- uk.p = bkey_start_pos(&li.k.k);
- extent_save(k, &uk, f);
-
- bch_bset_fix_invalidated_key(b, k);
- }
- } else {
- /* Front merge - walk forwards */
- for (;
- k != bset_bkey_last(t->data) &&
- (uk = bkey_unpack_key(f, k),
- bkey_cmp(uk.p, li.k.k.p) < 0);
- k = bkey_next(k)) {
- if (bkey_cmp(uk.p,
- bkey_start_pos(&li.k.k)) <= 0)
- continue;
-
- BUG_ON(!bkey_deleted(k));
-
- uk.p = li.k.k.p;
- extent_save(k, &uk, f);
-
- bch_bset_fix_invalidated_key(b, k);
- }
- }
- }
-
+ extent_merge_do_overlapping(b, iter, &li.k.k, back_merge, false);
return ret;
}