diff options
-rw-r--r-- | drivers/md/bcache/bkey.c | 17 | ||||
-rw-r--r-- | drivers/md/bcache/btree_update.c | 22 | ||||
-rw-r--r-- | drivers/md/bcache/extents.c | 231 |
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; } |