1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/fs/bcachefs/ec.c
Kent Overstreet af4d05c46b bcachefs: Account for stripe parity sectors separately
Instead of trying to charge EC parity to the data within the stripe
(which is subject to rounding errors), let's charge it to the stripe
itself. It should also make -ENOSPC issues easier to deal with if we
charge for parity blocks up front, and means we can also make more fine
grained accounting available to the user.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-10-22 17:08:45 -04:00

1661 lines
37 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* erasure coding */
#include "bcachefs.h"
#include "alloc_foreground.h"
#include "bkey_on_stack.h"
#include "bset.h"
#include "btree_gc.h"
#include "btree_update.h"
#include "buckets.h"
#include "disk_groups.h"
#include "ec.h"
#include "error.h"
#include "io.h"
#include "keylist.h"
#include "recovery.h"
#include "super-io.h"
#include "util.h"
#include <linux/sort.h>
#ifdef __KERNEL__
#include <linux/raid/pq.h>
#include <linux/raid/xor.h>
static void raid5_recov(unsigned disks, unsigned failed_idx,
size_t size, void **data)
{
unsigned i = 2, nr;
BUG_ON(failed_idx >= disks);
swap(data[0], data[failed_idx]);
memcpy(data[0], data[1], size);
while (i < disks) {
nr = min_t(unsigned, disks - i, MAX_XOR_BLOCKS);
xor_blocks(nr, size, data[0], data + i);
i += nr;
}
swap(data[0], data[failed_idx]);
}
static void raid_gen(int nd, int np, size_t size, void **v)
{
if (np >= 1)
raid5_recov(nd + np, nd, size, v);
if (np >= 2)
raid6_call.gen_syndrome(nd + np, size, v);
BUG_ON(np > 2);
}
static void raid_rec(int nr, int *ir, int nd, int np, size_t size, void **v)
{
switch (nr) {
case 0:
break;
case 1:
if (ir[0] < nd + 1)
raid5_recov(nd + 1, ir[0], size, v);
else
raid6_call.gen_syndrome(nd + np, size, v);
break;
case 2:
if (ir[1] < nd) {
/* data+data failure. */
raid6_2data_recov(nd + np, size, ir[0], ir[1], v);
} else if (ir[0] < nd) {
/* data + p/q failure */
if (ir[1] == nd) /* data + p failure */
raid6_datap_recov(nd + np, size, ir[0], v);
else { /* data + q failure */
raid5_recov(nd + 1, ir[0], size, v);
raid6_call.gen_syndrome(nd + np, size, v);
}
} else {
raid_gen(nd, np, size, v);
}
break;
default:
BUG();
}
}
#else
#include <raid/raid.h>
#endif
struct ec_bio {
struct bch_dev *ca;
struct ec_stripe_buf *buf;
size_t idx;
struct bio bio;
};
/* Stripes btree keys: */
const char *bch2_stripe_invalid(const struct bch_fs *c, struct bkey_s_c k)
{
const struct bch_stripe *s = bkey_s_c_to_stripe(k).v;
if (k.k->p.inode)
return "invalid stripe key";
if (bkey_val_bytes(k.k) < sizeof(*s))
return "incorrect value size";
if (bkey_val_bytes(k.k) < sizeof(*s) ||
bkey_val_u64s(k.k) < stripe_val_u64s(s))
return "incorrect value size";
return bch2_bkey_ptrs_invalid(c, k);
}
void bch2_stripe_to_text(struct printbuf *out, struct bch_fs *c,
struct bkey_s_c k)
{
const struct bch_stripe *s = bkey_s_c_to_stripe(k).v;
unsigned i;
pr_buf(out, "algo %u sectors %u blocks %u:%u csum %u gran %u",
s->algorithm,
le16_to_cpu(s->sectors),
s->nr_blocks - s->nr_redundant,
s->nr_redundant,
s->csum_type,
1U << s->csum_granularity_bits);
for (i = 0; i < s->nr_blocks; i++)
pr_buf(out, " %u:%llu:%u", s->ptrs[i].dev,
(u64) s->ptrs[i].offset,
stripe_blockcount_get(s, i));
}
static int ptr_matches_stripe(struct bch_fs *c,
struct bch_stripe *v,
const struct bch_extent_ptr *ptr)
{
unsigned i;
for (i = 0; i < v->nr_blocks - v->nr_redundant; i++) {
const struct bch_extent_ptr *ptr2 = v->ptrs + i;
if (ptr->dev == ptr2->dev &&
ptr->gen == ptr2->gen &&
ptr->offset >= ptr2->offset &&
ptr->offset < ptr2->offset + le16_to_cpu(v->sectors))
return i;
}
return -1;
}
static int extent_matches_stripe(struct bch_fs *c,
struct bch_stripe *v,
struct bkey_s_c k)
{
switch (k.k->type) {
case KEY_TYPE_extent: {
struct bkey_s_c_extent e = bkey_s_c_to_extent(k);
const struct bch_extent_ptr *ptr;
int idx;
extent_for_each_ptr(e, ptr) {
idx = ptr_matches_stripe(c, v, ptr);
if (idx >= 0)
return idx;
}
break;
}
}
return -1;
}
static bool extent_has_stripe_ptr(struct bkey_s_c k, u64 idx)
{
switch (k.k->type) {
case KEY_TYPE_extent: {
struct bkey_s_c_extent e = bkey_s_c_to_extent(k);
const union bch_extent_entry *entry;
extent_for_each_entry(e, entry)
if (extent_entry_type(entry) ==
BCH_EXTENT_ENTRY_stripe_ptr &&
entry->stripe_ptr.idx == idx)
return true;
break;
}
}
return false;
}
/* Checksumming: */
static void ec_generate_checksums(struct ec_stripe_buf *buf)
{
struct bch_stripe *v = &buf->key.v;
unsigned csum_granularity = 1 << v->csum_granularity_bits;
unsigned csums_per_device = stripe_csums_per_device(v);
unsigned csum_bytes = bch_crc_bytes[v->csum_type];
unsigned i, j;
if (!csum_bytes)
return;
BUG_ON(buf->offset);
BUG_ON(buf->size != le16_to_cpu(v->sectors));
for (i = 0; i < v->nr_blocks; i++) {
for (j = 0; j < csums_per_device; j++) {
unsigned offset = j << v->csum_granularity_bits;
unsigned len = min(csum_granularity, buf->size - offset);
struct bch_csum csum =
bch2_checksum(NULL, v->csum_type,
null_nonce(),
buf->data[i] + (offset << 9),
len << 9);
memcpy(stripe_csum(v, i, j), &csum, csum_bytes);
}
}
}
static void ec_validate_checksums(struct bch_fs *c, struct ec_stripe_buf *buf)
{
struct bch_stripe *v = &buf->key.v;
unsigned csum_granularity = 1 << v->csum_granularity_bits;
unsigned csum_bytes = bch_crc_bytes[v->csum_type];
unsigned i;
if (!csum_bytes)
return;
for (i = 0; i < v->nr_blocks; i++) {
unsigned offset = buf->offset;
unsigned end = buf->offset + buf->size;
if (!test_bit(i, buf->valid))
continue;
while (offset < end) {
unsigned j = offset >> v->csum_granularity_bits;
unsigned len = min(csum_granularity, end - offset);
struct bch_csum csum;
BUG_ON(offset & (csum_granularity - 1));
BUG_ON(offset + len != le16_to_cpu(v->sectors) &&
((offset + len) & (csum_granularity - 1)));
csum = bch2_checksum(NULL, v->csum_type,
null_nonce(),
buf->data[i] + ((offset - buf->offset) << 9),
len << 9);
if (memcmp(stripe_csum(v, i, j), &csum, csum_bytes)) {
__bcache_io_error(c,
"checksum error while doing reconstruct read (%u:%u)",
i, j);
clear_bit(i, buf->valid);
break;
}
offset += len;
}
}
}
/* Erasure coding: */
static void ec_generate_ec(struct ec_stripe_buf *buf)
{
struct bch_stripe *v = &buf->key.v;
unsigned nr_data = v->nr_blocks - v->nr_redundant;
unsigned bytes = le16_to_cpu(v->sectors) << 9;
raid_gen(nr_data, v->nr_redundant, bytes, buf->data);
}
static unsigned __ec_nr_failed(struct ec_stripe_buf *buf, unsigned nr)
{
return nr - bitmap_weight(buf->valid, nr);
}
static unsigned ec_nr_failed(struct ec_stripe_buf *buf)
{
return __ec_nr_failed(buf, buf->key.v.nr_blocks);
}
static int ec_do_recov(struct bch_fs *c, struct ec_stripe_buf *buf)
{
struct bch_stripe *v = &buf->key.v;
unsigned i, failed[EC_STRIPE_MAX], nr_failed = 0;
unsigned nr_data = v->nr_blocks - v->nr_redundant;
unsigned bytes = buf->size << 9;
if (ec_nr_failed(buf) > v->nr_redundant) {
__bcache_io_error(c,
"error doing reconstruct read: unable to read enough blocks");
return -1;
}
for (i = 0; i < nr_data; i++)
if (!test_bit(i, buf->valid))
failed[nr_failed++] = i;
raid_rec(nr_failed, failed, nr_data, v->nr_redundant, bytes, buf->data);
return 0;
}
/* IO: */
static void ec_block_endio(struct bio *bio)
{
struct ec_bio *ec_bio = container_of(bio, struct ec_bio, bio);
struct bch_dev *ca = ec_bio->ca;
struct closure *cl = bio->bi_private;
if (bch2_dev_io_err_on(bio->bi_status, ca, "erasure coding %s: %s",
bio_data_dir(bio) ? "write" : "read",
bch2_blk_status_to_str(bio->bi_status)))
clear_bit(ec_bio->idx, ec_bio->buf->valid);
bio_put(&ec_bio->bio);
percpu_ref_put(&ca->io_ref);
closure_put(cl);
}
static void ec_block_io(struct bch_fs *c, struct ec_stripe_buf *buf,
unsigned rw, unsigned idx, struct closure *cl)
{
struct bch_stripe *v = &buf->key.v;
unsigned offset = 0, bytes = buf->size << 9;
struct bch_extent_ptr *ptr = &v->ptrs[idx];
struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
enum bch_data_type data_type = idx < buf->key.v.nr_blocks - buf->key.v.nr_redundant
? BCH_DATA_user
: BCH_DATA_parity;
if (!bch2_dev_get_ioref(ca, rw)) {
clear_bit(idx, buf->valid);
return;
}
this_cpu_add(ca->io_done->sectors[rw][data_type], buf->size);
while (offset < bytes) {
unsigned nr_iovecs = min_t(size_t, BIO_MAX_VECS,
DIV_ROUND_UP(bytes, PAGE_SIZE));
unsigned b = min_t(size_t, bytes - offset,
nr_iovecs << PAGE_SHIFT);
struct ec_bio *ec_bio;
ec_bio = container_of(bio_alloc_bioset(ca->disk_sb.bdev,
nr_iovecs,
rw,
GFP_KERNEL,
&c->ec_bioset),
struct ec_bio, bio);
ec_bio->ca = ca;
ec_bio->buf = buf;
ec_bio->idx = idx;
ec_bio->bio.bi_iter.bi_sector = ptr->offset + buf->offset + (offset >> 9);
ec_bio->bio.bi_end_io = ec_block_endio;
ec_bio->bio.bi_private = cl;
bch2_bio_map(&ec_bio->bio, buf->data[idx] + offset, b);
closure_get(cl);
percpu_ref_get(&ca->io_ref);
submit_bio(&ec_bio->bio);
offset += b;
}
percpu_ref_put(&ca->io_ref);
}
/* recovery read path: */
int bch2_ec_read_extent(struct bch_fs *c, struct bch_read_bio *rbio)
{
struct btree_trans trans;
struct btree_iter *iter;
struct ec_stripe_buf *buf;
struct closure cl;
struct bkey_s_c k;
struct bch_stripe *v;
unsigned stripe_idx;
unsigned offset, end;
unsigned i, nr_data, csum_granularity;
int ret = 0, idx;
closure_init_stack(&cl);
BUG_ON(!rbio->pick.has_ec);
stripe_idx = rbio->pick.ec.idx;
buf = kzalloc(sizeof(*buf), GFP_NOIO);
if (!buf)
return -ENOMEM;
bch2_trans_init(&trans, c, 0, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_EC,
POS(0, stripe_idx),
BTREE_ITER_SLOTS);
k = bch2_btree_iter_peek_slot(iter);
if (bkey_err(k) || k.k->type != KEY_TYPE_stripe) {
__bcache_io_error(c,
"error doing reconstruct read: stripe not found");
kfree(buf);
return bch2_trans_exit(&trans) ?: -EIO;
}
bkey_reassemble(&buf->key.k_i, k);
bch2_trans_exit(&trans);
v = &buf->key.v;
nr_data = v->nr_blocks - v->nr_redundant;
idx = ptr_matches_stripe(c, v, &rbio->pick.ptr);
BUG_ON(idx < 0);
csum_granularity = 1U << v->csum_granularity_bits;
offset = rbio->bio.bi_iter.bi_sector - v->ptrs[idx].offset;
end = offset + bio_sectors(&rbio->bio);
BUG_ON(end > le16_to_cpu(v->sectors));
buf->offset = round_down(offset, csum_granularity);
buf->size = min_t(unsigned, le16_to_cpu(v->sectors),
round_up(end, csum_granularity)) - buf->offset;
for (i = 0; i < v->nr_blocks; i++) {
buf->data[i] = kmalloc(buf->size << 9, GFP_NOIO);
if (!buf->data[i]) {
ret = -ENOMEM;
goto err;
}
}
memset(buf->valid, 0xFF, sizeof(buf->valid));
for (i = 0; i < v->nr_blocks; i++) {
struct bch_extent_ptr *ptr = v->ptrs + i;
struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
if (ptr_stale(ca, ptr)) {
__bcache_io_error(c,
"error doing reconstruct read: stale pointer");
clear_bit(i, buf->valid);
continue;
}
ec_block_io(c, buf, REQ_OP_READ, i, &cl);
}
closure_sync(&cl);
if (ec_nr_failed(buf) > v->nr_redundant) {
__bcache_io_error(c,
"error doing reconstruct read: unable to read enough blocks");
ret = -EIO;
goto err;
}
ec_validate_checksums(c, buf);
ret = ec_do_recov(c, buf);
if (ret)
goto err;
memcpy_to_bio(&rbio->bio, rbio->bio.bi_iter,
buf->data[idx] + ((offset - buf->offset) << 9));
err:
for (i = 0; i < v->nr_blocks; i++)
kfree(buf->data[i]);
kfree(buf);
return ret;
}
/* stripe bucket accounting: */
static int __ec_stripe_mem_alloc(struct bch_fs *c, size_t idx, gfp_t gfp)
{
ec_stripes_heap n, *h = &c->ec_stripes_heap;
if (idx >= h->size) {
if (!init_heap(&n, max(1024UL, roundup_pow_of_two(idx + 1)), gfp))
return -ENOMEM;
spin_lock(&c->ec_stripes_heap_lock);
if (n.size > h->size) {
memcpy(n.data, h->data, h->used * sizeof(h->data[0]));
n.used = h->used;
swap(*h, n);
}
spin_unlock(&c->ec_stripes_heap_lock);
free_heap(&n);
}
if (!genradix_ptr_alloc(&c->stripes[0], idx, gfp))
return -ENOMEM;
if (c->gc_pos.phase != GC_PHASE_NOT_RUNNING &&
!genradix_ptr_alloc(&c->stripes[1], idx, gfp))
return -ENOMEM;
return 0;
}
static int ec_stripe_mem_alloc(struct bch_fs *c,
struct btree_iter *iter)
{
size_t idx = iter->pos.offset;
int ret = 0;
if (!__ec_stripe_mem_alloc(c, idx, GFP_NOWAIT|__GFP_NOWARN))
return ret;
bch2_trans_unlock(iter->trans);
ret = -EINTR;
if (!__ec_stripe_mem_alloc(c, idx, GFP_KERNEL))
return ret;
return -ENOMEM;
}
static ssize_t stripe_idx_to_delete(struct bch_fs *c)
{
ec_stripes_heap *h = &c->ec_stripes_heap;
return h->used && h->data[0].blocks_nonempty == 0
? h->data[0].idx : -1;
}
static inline int ec_stripes_heap_cmp(ec_stripes_heap *h,
struct ec_stripe_heap_entry l,
struct ec_stripe_heap_entry r)
{
return ((l.blocks_nonempty > r.blocks_nonempty) -
(l.blocks_nonempty < r.blocks_nonempty));
}
static inline void ec_stripes_heap_set_backpointer(ec_stripes_heap *h,
size_t i)
{
struct bch_fs *c = container_of(h, struct bch_fs, ec_stripes_heap);
genradix_ptr(&c->stripes[0], h->data[i].idx)->heap_idx = i;
}
static void heap_verify_backpointer(struct bch_fs *c, size_t idx)
{
ec_stripes_heap *h = &c->ec_stripes_heap;
struct stripe *m = genradix_ptr(&c->stripes[0], idx);
BUG_ON(!m->alive);
BUG_ON(m->heap_idx >= h->used);
BUG_ON(h->data[m->heap_idx].idx != idx);
}
void bch2_stripes_heap_del(struct bch_fs *c,
struct stripe *m, size_t idx)
{
if (!m->on_heap)
return;
m->on_heap = false;
heap_verify_backpointer(c, idx);
heap_del(&c->ec_stripes_heap, m->heap_idx,
ec_stripes_heap_cmp,
ec_stripes_heap_set_backpointer);
}
void bch2_stripes_heap_insert(struct bch_fs *c,
struct stripe *m, size_t idx)
{
if (m->on_heap)
return;
BUG_ON(heap_full(&c->ec_stripes_heap));
m->on_heap = true;
heap_add(&c->ec_stripes_heap, ((struct ec_stripe_heap_entry) {
.idx = idx,
.blocks_nonempty = m->blocks_nonempty,
}),
ec_stripes_heap_cmp,
ec_stripes_heap_set_backpointer);
heap_verify_backpointer(c, idx);
}
void bch2_stripes_heap_update(struct bch_fs *c,
struct stripe *m, size_t idx)
{
ec_stripes_heap *h = &c->ec_stripes_heap;
size_t i;
if (!m->on_heap)
return;
heap_verify_backpointer(c, idx);
h->data[m->heap_idx].blocks_nonempty = m->blocks_nonempty;
i = m->heap_idx;
heap_sift_up(h, i, ec_stripes_heap_cmp,
ec_stripes_heap_set_backpointer);
heap_sift_down(h, i, ec_stripes_heap_cmp,
ec_stripes_heap_set_backpointer);
heap_verify_backpointer(c, idx);
if (stripe_idx_to_delete(c) >= 0 &&
!percpu_ref_is_dying(&c->writes))
schedule_work(&c->ec_stripe_delete_work);
}
/* stripe deletion */
static int ec_stripe_delete(struct bch_fs *c, size_t idx)
{
//pr_info("deleting stripe %zu", idx);
return bch2_btree_delete_range(c, BTREE_ID_EC,
POS(0, idx),
POS(0, idx + 1),
NULL);
}
static void ec_stripe_delete_work(struct work_struct *work)
{
struct bch_fs *c =
container_of(work, struct bch_fs, ec_stripe_delete_work);
ssize_t idx;
while (1) {
spin_lock(&c->ec_stripes_heap_lock);
idx = stripe_idx_to_delete(c);
if (idx < 0) {
spin_unlock(&c->ec_stripes_heap_lock);
break;
}
bch2_stripes_heap_del(c, genradix_ptr(&c->stripes[0], idx), idx);
spin_unlock(&c->ec_stripes_heap_lock);
if (ec_stripe_delete(c, idx))
break;
}
}
/* stripe creation: */
static int ec_stripe_bkey_insert(struct bch_fs *c,
struct ec_stripe_new *s,
struct bkey_i_stripe *stripe)
{
struct btree_trans trans;
struct btree_iter *iter;
struct bkey_s_c k;
struct bpos start_pos = POS(0, c->ec_stripe_hint);
int ret;
bch2_trans_init(&trans, c, 0, 0);
retry:
bch2_trans_begin(&trans);
for_each_btree_key(&trans, iter, BTREE_ID_EC, start_pos,
BTREE_ITER_SLOTS|BTREE_ITER_INTENT, k, ret) {
if (bkey_cmp(k.k->p, POS(0, U32_MAX)) > 0) {
if (start_pos.offset) {
start_pos = POS_MIN;
bch2_btree_iter_set_pos(iter, start_pos);
continue;
}
ret = -ENOSPC;
break;
}
if (bkey_deleted(k.k))
goto found_slot;
}
goto err;
found_slot:
start_pos = iter->pos;
ret = ec_stripe_mem_alloc(c, iter);
if (ret)
goto err;
stripe->k.p = iter->pos;
bch2_trans_update(&trans, iter, &stripe->k_i, 0);
ret = bch2_trans_commit(&trans, &s->res, NULL,
BTREE_INSERT_NOFAIL);
err:
bch2_trans_iter_put(&trans, iter);
if (ret == -EINTR)
goto retry;
c->ec_stripe_hint = ret ? start_pos.offset : start_pos.offset + 1;
bch2_trans_exit(&trans);
return ret;
}
static void extent_stripe_ptr_add(struct bkey_s_extent e,
struct ec_stripe_buf *s,
struct bch_extent_ptr *ptr,
unsigned block)
{
struct bch_extent_stripe_ptr *dst = (void *) ptr;
union bch_extent_entry *end = extent_entry_last(e);
memmove_u64s_up(dst + 1, dst, (u64 *) end - (u64 *) dst);
e.k->u64s += sizeof(*dst) / sizeof(u64);
*dst = (struct bch_extent_stripe_ptr) {
.type = 1 << BCH_EXTENT_ENTRY_stripe_ptr,
.block = block,
.idx = s->key.k.p.offset,
};
}
static int ec_stripe_update_ptrs(struct bch_fs *c,
struct ec_stripe_buf *s,
struct bkey *pos)
{
struct btree_trans trans;
struct btree_iter *iter;
struct bkey_s_c k;
struct bkey_s_extent e;
struct bkey_on_stack sk;
int ret = 0, dev, idx;
bkey_on_stack_init(&sk);
bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
/* XXX this doesn't support the reflink btree */
iter = bch2_trans_get_iter(&trans, BTREE_ID_EXTENTS,
bkey_start_pos(pos),
BTREE_ITER_INTENT);
while ((k = bch2_btree_iter_peek(iter)).k &&
!(ret = bkey_err(k)) &&
bkey_cmp(bkey_start_pos(k.k), pos->p) < 0) {
struct bch_extent_ptr *ptr, *ec_ptr = NULL;
if (extent_has_stripe_ptr(k, s->key.k.p.offset)) {
bch2_btree_iter_next(iter);
continue;
}
idx = extent_matches_stripe(c, &s->key.v, k);
if (idx < 0) {
bch2_btree_iter_next(iter);
continue;
}
dev = s->key.v.ptrs[idx].dev;
bkey_on_stack_reassemble(&sk, c, k);
e = bkey_i_to_s_extent(sk.k);
bch2_bkey_drop_ptrs(e.s, ptr, ptr->dev != dev);
ec_ptr = (void *) bch2_bkey_has_device(e.s_c, dev);
BUG_ON(!ec_ptr);
extent_stripe_ptr_add(e, s, ec_ptr, idx);
bch2_btree_iter_set_pos(iter, bkey_start_pos(&sk.k->k));
bch2_trans_update(&trans, iter, sk.k, 0);
ret = bch2_trans_commit(&trans, NULL, NULL,
BTREE_INSERT_NOFAIL|
BTREE_INSERT_USE_RESERVE);
if (ret == -EINTR)
ret = 0;
if (ret)
break;
}
bch2_trans_exit(&trans);
bkey_on_stack_exit(&sk, c);
return ret;
}
/*
* data buckets of new stripe all written: create the stripe
*/
static void ec_stripe_create(struct ec_stripe_new *s)
{
struct bch_fs *c = s->c;
struct open_bucket *ob;
struct bkey_i *k;
struct stripe *m;
struct bch_stripe *v = &s->stripe.key.v;
unsigned i, nr_data = v->nr_blocks - v->nr_redundant;
struct closure cl;
int ret;
BUG_ON(s->h->s == s);
closure_init_stack(&cl);
if (s->err) {
if (s->err != -EROFS)
bch_err(c, "error creating stripe: error writing data buckets");
goto err;
}
BUG_ON(!s->allocated);
if (!percpu_ref_tryget(&c->writes))
goto err;
BUG_ON(bitmap_weight(s->blocks_allocated,
s->blocks.nr) != s->blocks.nr);
ec_generate_ec(&s->stripe);
ec_generate_checksums(&s->stripe);
/* write p/q: */
for (i = nr_data; i < v->nr_blocks; i++)
ec_block_io(c, &s->stripe, REQ_OP_WRITE, i, &cl);
closure_sync(&cl);
for (i = nr_data; i < v->nr_blocks; i++)
if (!test_bit(i, s->stripe.valid)) {
bch_err(c, "error creating stripe: error writing redundancy buckets");
goto err_put_writes;
}
ret = s->existing_stripe
? bch2_btree_insert(c, BTREE_ID_EC, &s->stripe.key.k_i,
&s->res, NULL, BTREE_INSERT_NOFAIL)
: ec_stripe_bkey_insert(c, s, &s->stripe.key);
if (ret) {
bch_err(c, "error creating stripe: error creating stripe key");
goto err_put_writes;
}
for_each_keylist_key(&s->keys, k) {
ret = ec_stripe_update_ptrs(c, &s->stripe, &k->k);
if (ret) {
bch_err(c, "error creating stripe: error updating pointers");
break;
}
}
spin_lock(&c->ec_stripes_heap_lock);
m = genradix_ptr(&c->stripes[0], s->stripe.key.k.p.offset);
#if 0
pr_info("created a %s stripe %llu",
s->existing_stripe ? "existing" : "new",
s->stripe.key.k.p.offset);
#endif
BUG_ON(m->on_heap);
bch2_stripes_heap_insert(c, m, s->stripe.key.k.p.offset);
spin_unlock(&c->ec_stripes_heap_lock);
err_put_writes:
percpu_ref_put(&c->writes);
err:
bch2_disk_reservation_put(c, &s->res);
open_bucket_for_each(c, &s->blocks, ob, i) {
ob->ec = NULL;
__bch2_open_bucket_put(c, ob);
}
bch2_open_buckets_put(c, &s->parity);
bch2_keylist_free(&s->keys, s->inline_keys);
for (i = 0; i < s->stripe.key.v.nr_blocks; i++)
kvpfree(s->stripe.data[i], s->stripe.size << 9);
kfree(s);
}
static void ec_stripe_create_work(struct work_struct *work)
{
struct bch_fs *c = container_of(work,
struct bch_fs, ec_stripe_create_work);
struct ec_stripe_new *s, *n;
restart:
mutex_lock(&c->ec_stripe_new_lock);
list_for_each_entry_safe(s, n, &c->ec_stripe_new_list, list)
if (!atomic_read(&s->pin)) {
list_del(&s->list);
mutex_unlock(&c->ec_stripe_new_lock);
ec_stripe_create(s);
goto restart;
}
mutex_unlock(&c->ec_stripe_new_lock);
}
static void ec_stripe_new_put(struct bch_fs *c, struct ec_stripe_new *s)
{
BUG_ON(atomic_read(&s->pin) <= 0);
if (atomic_dec_and_test(&s->pin)) {
BUG_ON(!s->pending);
queue_work(system_long_wq, &c->ec_stripe_create_work);
}
}
static void ec_stripe_set_pending(struct bch_fs *c, struct ec_stripe_head *h)
{
struct ec_stripe_new *s = h->s;
BUG_ON(!s->allocated && !s->err);
h->s = NULL;
s->pending = true;
mutex_lock(&c->ec_stripe_new_lock);
list_add(&s->list, &c->ec_stripe_new_list);
mutex_unlock(&c->ec_stripe_new_lock);
ec_stripe_new_put(c, s);
}
/* have a full bucket - hand it off to be erasure coded: */
void bch2_ec_bucket_written(struct bch_fs *c, struct open_bucket *ob)
{
struct ec_stripe_new *s = ob->ec;
if (ob->sectors_free)
s->err = -1;
ec_stripe_new_put(c, s);
}
void bch2_ec_bucket_cancel(struct bch_fs *c, struct open_bucket *ob)
{
struct ec_stripe_new *s = ob->ec;
s->err = -EIO;
}
void *bch2_writepoint_ec_buf(struct bch_fs *c, struct write_point *wp)
{
struct open_bucket *ob = ec_open_bucket(c, &wp->ptrs);
struct bch_dev *ca;
unsigned offset;
if (!ob)
return NULL;
ca = bch_dev_bkey_exists(c, ob->ptr.dev);
offset = ca->mi.bucket_size - ob->sectors_free;
return ob->ec->stripe.data[ob->ec_idx] + (offset << 9);
}
void bch2_ec_add_backpointer(struct bch_fs *c, struct write_point *wp,
struct bpos pos, unsigned sectors)
{
struct open_bucket *ob = ec_open_bucket(c, &wp->ptrs);
struct ec_stripe_new *ec;
if (!ob)
return;
//pr_info("adding backpointer at %llu:%llu", pos.inode, pos.offset);
ec = ob->ec;
mutex_lock(&ec->lock);
if (bch2_keylist_realloc(&ec->keys, ec->inline_keys,
ARRAY_SIZE(ec->inline_keys),
BKEY_U64s)) {
BUG();
}
bkey_init(&ec->keys.top->k);
ec->keys.top->k.p = pos;
bch2_key_resize(&ec->keys.top->k, sectors);
bch2_keylist_push(&ec->keys);
mutex_unlock(&ec->lock);
}
static int unsigned_cmp(const void *_l, const void *_r)
{
unsigned l = *((const unsigned *) _l);
unsigned r = *((const unsigned *) _r);
return cmp_int(l, r);
}
/* pick most common bucket size: */
static unsigned pick_blocksize(struct bch_fs *c,
struct bch_devs_mask *devs)
{
struct bch_dev *ca;
unsigned i, nr = 0, sizes[BCH_SB_MEMBERS_MAX];
struct {
unsigned nr, size;
} cur = { 0, 0 }, best = { 0, 0 };
for_each_member_device_rcu(ca, c, i, devs)
sizes[nr++] = ca->mi.bucket_size;
sort(sizes, nr, sizeof(unsigned), unsigned_cmp, NULL);
for (i = 0; i < nr; i++) {
if (sizes[i] != cur.size) {
if (cur.nr > best.nr)
best = cur;
cur.nr = 0;
cur.size = sizes[i];
}
cur.nr++;
}
if (cur.nr > best.nr)
best = cur;
return best.size;
}
static bool may_create_new_stripe(struct bch_fs *c)
{
return false;
}
static void ec_stripe_key_init(struct bch_fs *c,
struct bkey_i_stripe *s,
unsigned nr_data,
unsigned nr_parity,
unsigned stripe_size)
{
unsigned u64s;
bkey_stripe_init(&s->k_i);
s->v.sectors = cpu_to_le16(stripe_size);
s->v.algorithm = 0;
s->v.nr_blocks = nr_data + nr_parity;
s->v.nr_redundant = nr_parity;
s->v.csum_granularity_bits = ilog2(c->sb.encoded_extent_max);
s->v.csum_type = BCH_CSUM_CRC32C;
s->v.pad = 0;
while ((u64s = stripe_val_u64s(&s->v)) > BKEY_VAL_U64s_MAX) {
BUG_ON(1 << s->v.csum_granularity_bits >=
le16_to_cpu(s->v.sectors) ||
s->v.csum_granularity_bits == U8_MAX);
s->v.csum_granularity_bits++;
}
set_bkey_val_u64s(&s->k, u64s);
}
static int ec_new_stripe_alloc(struct bch_fs *c, struct ec_stripe_head *h)
{
struct ec_stripe_new *s;
unsigned i;
lockdep_assert_held(&h->lock);
s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
return -ENOMEM;
mutex_init(&s->lock);
atomic_set(&s->pin, 1);
s->c = c;
s->h = h;
s->nr_data = min_t(unsigned, h->nr_active_devs,
EC_STRIPE_MAX) - h->redundancy;
s->nr_parity = h->redundancy;
bch2_keylist_init(&s->keys, s->inline_keys);
s->stripe.offset = 0;
s->stripe.size = h->blocksize;
memset(s->stripe.valid, 0xFF, sizeof(s->stripe.valid));
ec_stripe_key_init(c, &s->stripe.key, s->nr_data,
s->nr_parity, h->blocksize);
for (i = 0; i < s->stripe.key.v.nr_blocks; i++) {
s->stripe.data[i] = kvpmalloc(s->stripe.size << 9, GFP_KERNEL);
if (!s->stripe.data[i])
goto err;
}
h->s = s;
return 0;
err:
for (i = 0; i < s->stripe.key.v.nr_blocks; i++)
kvpfree(s->stripe.data[i], s->stripe.size << 9);
kfree(s);
return -ENOMEM;
}
static struct ec_stripe_head *
ec_new_stripe_head_alloc(struct bch_fs *c, unsigned target,
unsigned algo, unsigned redundancy)
{
struct ec_stripe_head *h;
struct bch_dev *ca;
unsigned i;
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return NULL;
mutex_init(&h->lock);
mutex_lock(&h->lock);
h->target = target;
h->algo = algo;
h->redundancy = redundancy;
rcu_read_lock();
h->devs = target_rw_devs(c, BCH_DATA_user, target);
for_each_member_device_rcu(ca, c, i, &h->devs)
if (!ca->mi.durability)
__clear_bit(i, h->devs.d);
h->blocksize = pick_blocksize(c, &h->devs);
for_each_member_device_rcu(ca, c, i, &h->devs)
if (ca->mi.bucket_size == h->blocksize)
h->nr_active_devs++;
rcu_read_unlock();
list_add(&h->list, &c->ec_stripe_head_list);
return h;
}
void bch2_ec_stripe_head_put(struct bch_fs *c, struct ec_stripe_head *h)
{
if (h->s &&
h->s->allocated &&
bitmap_weight(h->s->blocks_allocated,
h->s->blocks.nr) == h->s->blocks.nr)
ec_stripe_set_pending(c, h);
mutex_unlock(&h->lock);
}
struct ec_stripe_head *__bch2_ec_stripe_head_get(struct bch_fs *c,
unsigned target,
unsigned algo,
unsigned redundancy)
{
struct ec_stripe_head *h;
if (!redundancy)
return NULL;
mutex_lock(&c->ec_stripe_head_lock);
list_for_each_entry(h, &c->ec_stripe_head_list, list)
if (h->target == target &&
h->algo == algo &&
h->redundancy == redundancy) {
mutex_lock(&h->lock);
goto found;
}
h = ec_new_stripe_head_alloc(c, target, algo, redundancy);
found:
mutex_unlock(&c->ec_stripe_head_lock);
return h;
}
/*
* XXX: use a higher watermark for allocating open buckets here:
*/
static int new_stripe_alloc_buckets(struct bch_fs *c, struct ec_stripe_head *h)
{
struct bch_devs_mask devs;
struct open_bucket *ob;
unsigned i, nr_have, nr_data =
min_t(unsigned, h->nr_active_devs,
EC_STRIPE_MAX) - h->redundancy;
bool have_cache = true;
int ret = 0;
devs = h->devs;
for_each_set_bit(i, h->s->blocks_allocated, EC_STRIPE_MAX) {
__clear_bit(h->s->stripe.key.v.ptrs[i].dev, devs.d);
--nr_data;
}
BUG_ON(h->s->blocks.nr > nr_data);
BUG_ON(h->s->parity.nr > h->redundancy);
open_bucket_for_each(c, &h->s->parity, ob, i)
__clear_bit(ob->ptr.dev, devs.d);
open_bucket_for_each(c, &h->s->blocks, ob, i)
__clear_bit(ob->ptr.dev, devs.d);
percpu_down_read(&c->mark_lock);
rcu_read_lock();
if (h->s->parity.nr < h->redundancy) {
nr_have = h->s->parity.nr;
ret = bch2_bucket_alloc_set(c, &h->s->parity,
&h->parity_stripe,
&devs,
h->redundancy,
&nr_have,
&have_cache,
RESERVE_NONE,
0,
NULL);
if (ret)
goto err;
}
if (h->s->blocks.nr < nr_data) {
nr_have = h->s->blocks.nr;
ret = bch2_bucket_alloc_set(c, &h->s->blocks,
&h->block_stripe,
&devs,
nr_data,
&nr_have,
&have_cache,
RESERVE_NONE,
0,
NULL);
if (ret)
goto err;
}
err:
rcu_read_unlock();
percpu_up_read(&c->mark_lock);
return ret;
}
/* XXX: doesn't obey target: */
static s64 get_existing_stripe(struct bch_fs *c,
unsigned target,
unsigned algo,
unsigned redundancy)
{
ec_stripes_heap *h = &c->ec_stripes_heap;
struct stripe *m;
size_t heap_idx;
u64 stripe_idx;
if (may_create_new_stripe(c))
return -1;
spin_lock(&c->ec_stripes_heap_lock);
for (heap_idx = 0; heap_idx < h->used; heap_idx++) {
if (!h->data[heap_idx].blocks_nonempty)
continue;
stripe_idx = h->data[heap_idx].idx;
m = genradix_ptr(&c->stripes[0], stripe_idx);
if (m->algorithm == algo &&
m->nr_redundant == redundancy &&
m->blocks_nonempty < m->nr_blocks - m->nr_redundant) {
bch2_stripes_heap_del(c, m, stripe_idx);
spin_unlock(&c->ec_stripes_heap_lock);
return stripe_idx;
}
}
spin_unlock(&c->ec_stripes_heap_lock);
return -1;
}
static int get_stripe_key(struct bch_fs *c, u64 idx, struct ec_stripe_buf *stripe)
{
struct btree_trans trans;
struct btree_iter *iter;
struct bkey_s_c k;
int ret;
bch2_trans_init(&trans, c, 0, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_EC, POS(0, idx), BTREE_ITER_SLOTS);
k = bch2_btree_iter_peek_slot(iter);
ret = bkey_err(k);
if (!ret)
bkey_reassemble(&stripe->key.k_i, k);
bch2_trans_exit(&trans);
return ret;
}
struct ec_stripe_head *bch2_ec_stripe_head_get(struct bch_fs *c,
unsigned target,
unsigned algo,
unsigned redundancy)
{
struct closure cl;
struct ec_stripe_head *h;
struct open_bucket *ob;
unsigned i, data_idx = 0;
s64 idx;
int ret;
closure_init_stack(&cl);
h = __bch2_ec_stripe_head_get(c, target, algo, redundancy);
if (!h)
return NULL;
if (!h->s && ec_new_stripe_alloc(c, h)) {
bch2_ec_stripe_head_put(c, h);
return NULL;
}
if (!h->s->allocated) {
if (!h->s->existing_stripe &&
(idx = get_existing_stripe(c, target, algo, redundancy)) >= 0) {
//pr_info("got existing stripe %llu", idx);
h->s->existing_stripe = true;
h->s->existing_stripe_idx = idx;
if (get_stripe_key(c, idx, &h->s->stripe)) {
/* btree error */
BUG();
}
for (i = 0; i < h->s->stripe.key.v.nr_blocks; i++)
if (stripe_blockcount_get(&h->s->stripe.key.v, i)) {
__set_bit(i, h->s->blocks_allocated);
ec_block_io(c, &h->s->stripe, READ, i, &cl);
}
}
if (!h->s->existing_stripe &&
!h->s->res.sectors) {
ret = bch2_disk_reservation_get(c, &h->s->res,
h->blocksize,
h->s->nr_parity, 0);
if (ret) {
/* What should we do here? */
bch_err(c, "unable to create new stripe: %i", ret);
bch2_ec_stripe_head_put(c, h);
h = NULL;
goto out;
}
}
if (new_stripe_alloc_buckets(c, h)) {
bch2_ec_stripe_head_put(c, h);
h = NULL;
goto out;
}
open_bucket_for_each(c, &h->s->blocks, ob, i) {
data_idx = find_next_zero_bit(h->s->blocks_allocated,
h->s->nr_data, data_idx);
BUG_ON(data_idx >= h->s->nr_data);
h->s->stripe.key.v.ptrs[data_idx] = ob->ptr;
h->s->data_block_idx[i] = data_idx;
data_idx++;
}
open_bucket_for_each(c, &h->s->parity, ob, i)
h->s->stripe.key.v.ptrs[h->s->nr_data + i] = ob->ptr;
//pr_info("new stripe, blocks_allocated %lx", h->s->blocks_allocated[0]);
h->s->allocated = true;
}
out:
closure_sync(&cl);
return h;
}
void bch2_ec_stop_dev(struct bch_fs *c, struct bch_dev *ca)
{
struct ec_stripe_head *h;
struct open_bucket *ob;
unsigned i;
mutex_lock(&c->ec_stripe_head_lock);
list_for_each_entry(h, &c->ec_stripe_head_list, list) {
mutex_lock(&h->lock);
if (!h->s)
goto unlock;
open_bucket_for_each(c, &h->s->blocks, ob, i)
if (ob->ptr.dev == ca->dev_idx)
goto found;
open_bucket_for_each(c, &h->s->parity, ob, i)
if (ob->ptr.dev == ca->dev_idx)
goto found;
goto unlock;
found:
h->s->err = -EROFS;
ec_stripe_set_pending(c, h);
unlock:
mutex_unlock(&h->lock);
}
mutex_unlock(&c->ec_stripe_head_lock);
}
static int __bch2_stripe_write_key(struct btree_trans *trans,
struct btree_iter *iter,
struct stripe *m,
size_t idx,
struct bkey_i_stripe *new_key)
{
struct bch_fs *c = trans->c;
struct bkey_s_c k;
unsigned i;
int ret;
bch2_btree_iter_set_pos(iter, POS(0, idx));
k = bch2_btree_iter_peek_slot(iter);
ret = bkey_err(k);
if (ret)
return ret;
if (k.k->type != KEY_TYPE_stripe)
return -EIO;
bkey_reassemble(&new_key->k_i, k);
spin_lock(&c->ec_stripes_heap_lock);
for (i = 0; i < new_key->v.nr_blocks; i++)
stripe_blockcount_set(&new_key->v, i,
m->block_sectors[i]);
m->dirty = false;
spin_unlock(&c->ec_stripes_heap_lock);
bch2_trans_update(trans, iter, &new_key->k_i, 0);
return 0;
}
int bch2_stripes_write(struct bch_fs *c, unsigned flags)
{
struct btree_trans trans;
struct btree_iter *iter;
struct genradix_iter giter;
struct bkey_i_stripe *new_key;
struct stripe *m;
int ret = 0;
new_key = kmalloc(255 * sizeof(u64), GFP_KERNEL);
BUG_ON(!new_key);
bch2_trans_init(&trans, c, 0, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_EC, POS_MIN,
BTREE_ITER_SLOTS|BTREE_ITER_INTENT);
genradix_for_each(&c->stripes[0], giter, m) {
if (!m->dirty)
continue;
ret = __bch2_trans_do(&trans, NULL, NULL,
BTREE_INSERT_NOFAIL|flags,
__bch2_stripe_write_key(&trans, iter, m,
giter.pos, new_key));
if (ret)
break;
}
bch2_trans_exit(&trans);
kfree(new_key);
return ret;
}
static int bch2_stripes_read_fn(struct bch_fs *c, enum btree_id id,
unsigned level, struct bkey_s_c k)
{
int ret = 0;
if (k.k->type == KEY_TYPE_stripe) {
struct stripe *m;
ret = __ec_stripe_mem_alloc(c, k.k->p.offset, GFP_KERNEL) ?:
bch2_mark_key(c, k, 0, 0, NULL, 0,
BTREE_TRIGGER_NOATOMIC);
if (ret)
return ret;
spin_lock(&c->ec_stripes_heap_lock);
m = genradix_ptr(&c->stripes[0], k.k->p.offset);
bch2_stripes_heap_insert(c, m, k.k->p.offset);
spin_unlock(&c->ec_stripes_heap_lock);
}
return ret;
}
int bch2_stripes_read(struct bch_fs *c, struct journal_keys *journal_keys)
{
int ret = bch2_btree_and_journal_walk(c, journal_keys, BTREE_ID_EC,
NULL, bch2_stripes_read_fn);
if (ret)
bch_err(c, "error reading stripes: %i", ret);
return ret;
}
int bch2_ec_mem_alloc(struct bch_fs *c, bool gc)
{
struct btree_trans trans;
struct btree_iter *iter;
struct bkey_s_c k;
size_t i, idx = 0;
int ret = 0;
bch2_trans_init(&trans, c, 0, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_EC, POS(0, U64_MAX), 0);
k = bch2_btree_iter_prev(iter);
if (!IS_ERR_OR_NULL(k.k))
idx = k.k->p.offset + 1;
ret = bch2_trans_exit(&trans);
if (ret)
return ret;
if (!idx)
return 0;
if (!gc &&
!init_heap(&c->ec_stripes_heap, roundup_pow_of_two(idx),
GFP_KERNEL))
return -ENOMEM;
#if 0
ret = genradix_prealloc(&c->stripes[gc], idx, GFP_KERNEL);
#else
for (i = 0; i < idx; i++)
if (!genradix_ptr_alloc(&c->stripes[gc], i, GFP_KERNEL))
return -ENOMEM;
#endif
return 0;
}
void bch2_stripes_heap_to_text(struct printbuf *out, struct bch_fs *c)
{
ec_stripes_heap *h = &c->ec_stripes_heap;
struct stripe *m;
size_t i;
spin_lock(&c->ec_stripes_heap_lock);
for (i = 0; i < min(h->used, 20UL); i++) {
m = genradix_ptr(&c->stripes[0], h->data[i].idx);
pr_buf(out, "%zu %u/%u+%u\n", h->data[i].idx,
h->data[i].blocks_nonempty,
m->nr_blocks - m->nr_redundant,
m->nr_redundant);
}
spin_unlock(&c->ec_stripes_heap_lock);
}
void bch2_new_stripes_to_text(struct printbuf *out, struct bch_fs *c)
{
struct ec_stripe_head *h;
struct ec_stripe_new *s;
mutex_lock(&c->ec_stripe_head_lock);
list_for_each_entry(h, &c->ec_stripe_head_list, list) {
pr_buf(out, "target %u algo %u redundancy %u:\n",
h->target, h->algo, h->redundancy);
if (h->s)
pr_buf(out, "\tpending: blocks %u allocated %u\n",
h->s->blocks.nr,
bitmap_weight(h->s->blocks_allocated,
h->s->blocks.nr));
}
mutex_unlock(&c->ec_stripe_head_lock);
mutex_lock(&c->ec_stripe_new_lock);
list_for_each_entry(s, &c->ec_stripe_new_list, list) {
pr_buf(out, "\tin flight: blocks %u allocated %u pin %u\n",
s->blocks.nr,
bitmap_weight(s->blocks_allocated,
s->blocks.nr),
atomic_read(&s->pin));
}
mutex_unlock(&c->ec_stripe_new_lock);
}
void bch2_fs_ec_exit(struct bch_fs *c)
{
struct ec_stripe_head *h;
while (1) {
mutex_lock(&c->ec_stripe_head_lock);
h = list_first_entry_or_null(&c->ec_stripe_head_list,
struct ec_stripe_head, list);
if (h)
list_del(&h->list);
mutex_unlock(&c->ec_stripe_head_lock);
if (!h)
break;
BUG_ON(h->s);
kfree(h);
}
BUG_ON(!list_empty(&c->ec_stripe_new_list));
free_heap(&c->ec_stripes_heap);
genradix_free(&c->stripes[0]);
bioset_exit(&c->ec_bioset);
}
int bch2_fs_ec_init(struct bch_fs *c)
{
INIT_WORK(&c->ec_stripe_create_work, ec_stripe_create_work);
INIT_WORK(&c->ec_stripe_delete_work, ec_stripe_delete_work);
return bioset_init(&c->ec_bioset, 1, offsetof(struct ec_bio, bio),
BIOSET_NEED_BVECS);
}