mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
bcachefs: bch2_ec_stripe_head_get() now checks for change in rw devices
This factors out ec_strie_head_devs_update(), which initializes the bitmap of devices we're allocating from, and runs it every time c->rw_devs_change_count changes. We also cancel pending, not allocated stripes, since they may refer to devices that are no longer available. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
83ccd9b31d
commit
035d72f72c
118
fs/bcachefs/ec.c
118
fs/bcachefs/ec.c
@ -1572,10 +1572,12 @@ void bch2_ec_do_stripe_creates(struct bch_fs *c)
|
|||||||
bch2_write_ref_put(c, BCH_WRITE_REF_stripe_create);
|
bch2_write_ref_put(c, BCH_WRITE_REF_stripe_create);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ec_stripe_set_pending(struct bch_fs *c, struct ec_stripe_head *h)
|
static void ec_stripe_new_set_pending(struct bch_fs *c, struct ec_stripe_head *h)
|
||||||
{
|
{
|
||||||
struct ec_stripe_new *s = h->s;
|
struct ec_stripe_new *s = h->s;
|
||||||
|
|
||||||
|
lockdep_assert_held(&h->lock);
|
||||||
|
|
||||||
BUG_ON(!s->allocated && !s->err);
|
BUG_ON(!s->allocated && !s->err);
|
||||||
|
|
||||||
h->s = NULL;
|
h->s = NULL;
|
||||||
@ -1588,6 +1590,12 @@ static void ec_stripe_set_pending(struct bch_fs *c, struct ec_stripe_head *h)
|
|||||||
ec_stripe_new_put(c, s, STRIPE_REF_io);
|
ec_stripe_new_put(c, s, STRIPE_REF_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ec_stripe_new_cancel(struct bch_fs *c, struct ec_stripe_head *h, int err)
|
||||||
|
{
|
||||||
|
h->s->err = err;
|
||||||
|
ec_stripe_new_set_pending(c, h);
|
||||||
|
}
|
||||||
|
|
||||||
void bch2_ec_bucket_cancel(struct bch_fs *c, struct open_bucket *ob)
|
void bch2_ec_bucket_cancel(struct bch_fs *c, struct open_bucket *ob)
|
||||||
{
|
{
|
||||||
struct ec_stripe_new *s = ob->ec;
|
struct ec_stripe_new *s = ob->ec;
|
||||||
@ -1711,6 +1719,60 @@ static int ec_new_stripe_alloc(struct bch_fs *c, struct ec_stripe_head *h)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ec_stripe_head_devs_update(struct bch_fs *c, struct ec_stripe_head *h)
|
||||||
|
{
|
||||||
|
struct bch_devs_mask devs = h->devs;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
h->devs = target_rw_devs(c, BCH_DATA_user, h->disk_label
|
||||||
|
? group_to_target(h->disk_label - 1)
|
||||||
|
: 0);
|
||||||
|
unsigned nr_devs = dev_mask_nr(&h->devs);
|
||||||
|
|
||||||
|
for_each_member_device_rcu(c, ca, &h->devs)
|
||||||
|
if (!ca->mi.durability)
|
||||||
|
__clear_bit(ca->dev_idx, h->devs.d);
|
||||||
|
unsigned nr_devs_with_durability = dev_mask_nr(&h->devs);
|
||||||
|
|
||||||
|
h->blocksize = pick_blocksize(c, &h->devs);
|
||||||
|
|
||||||
|
h->nr_active_devs = 0;
|
||||||
|
for_each_member_device_rcu(c, ca, &h->devs)
|
||||||
|
if (ca->mi.bucket_size == h->blocksize)
|
||||||
|
h->nr_active_devs++;
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we only have redundancy + 1 devices, we're better off with just
|
||||||
|
* replication:
|
||||||
|
*/
|
||||||
|
h->insufficient_devs = h->nr_active_devs < h->redundancy + 2;
|
||||||
|
|
||||||
|
if (h->insufficient_devs) {
|
||||||
|
const char *err;
|
||||||
|
|
||||||
|
if (nr_devs < h->redundancy + 2)
|
||||||
|
err = NULL;
|
||||||
|
else if (nr_devs_with_durability < h->redundancy + 2)
|
||||||
|
err = "cannot use durability=0 devices";
|
||||||
|
else
|
||||||
|
err = "mismatched bucket sizes";
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
bch_err(c, "insufficient devices available to create stripe (have %u, need %u): %s",
|
||||||
|
h->nr_active_devs, h->redundancy + 2, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bch_devs_mask devs_leaving;
|
||||||
|
bitmap_andnot(devs_leaving.d, devs.d, h->devs.d, BCH_SB_MEMBERS_MAX);
|
||||||
|
|
||||||
|
if (h->s && !h->s->allocated && dev_mask_nr(&devs_leaving))
|
||||||
|
ec_stripe_new_cancel(c, h, -EINTR);
|
||||||
|
|
||||||
|
h->rw_devs_change_count = c->rw_devs_change_count;
|
||||||
|
}
|
||||||
|
|
||||||
static struct ec_stripe_head *
|
static struct ec_stripe_head *
|
||||||
ec_new_stripe_head_alloc(struct bch_fs *c, unsigned disk_label,
|
ec_new_stripe_head_alloc(struct bch_fs *c, unsigned disk_label,
|
||||||
unsigned algo, unsigned redundancy,
|
unsigned algo, unsigned redundancy,
|
||||||
@ -1730,42 +1792,6 @@ ec_new_stripe_head_alloc(struct bch_fs *c, unsigned disk_label,
|
|||||||
h->redundancy = redundancy;
|
h->redundancy = redundancy;
|
||||||
h->watermark = watermark;
|
h->watermark = watermark;
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
h->devs = target_rw_devs(c, BCH_DATA_user, disk_label ? group_to_target(disk_label - 1) : 0);
|
|
||||||
unsigned nr_devs = dev_mask_nr(&h->devs);
|
|
||||||
|
|
||||||
for_each_member_device_rcu(c, ca, &h->devs)
|
|
||||||
if (!ca->mi.durability)
|
|
||||||
__clear_bit(ca->dev_idx, h->devs.d);
|
|
||||||
unsigned nr_devs_with_durability = dev_mask_nr(&h->devs);
|
|
||||||
|
|
||||||
h->blocksize = pick_blocksize(c, &h->devs);
|
|
||||||
|
|
||||||
for_each_member_device_rcu(c, ca, &h->devs)
|
|
||||||
if (ca->mi.bucket_size == h->blocksize)
|
|
||||||
h->nr_active_devs++;
|
|
||||||
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we only have redundancy + 1 devices, we're better off with just
|
|
||||||
* replication:
|
|
||||||
*/
|
|
||||||
if (h->nr_active_devs < h->redundancy + 2) {
|
|
||||||
const char *err;
|
|
||||||
|
|
||||||
if (nr_devs < h->redundancy + 2)
|
|
||||||
err = NULL;
|
|
||||||
else if (nr_devs_with_durability < h->redundancy + 2)
|
|
||||||
err = "cannot use durability=0 devices";
|
|
||||||
else
|
|
||||||
err = "mismatched bucket sizes";
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
bch_err(c, "insufficient devices available to create stripe (have %u, need %u): %s",
|
|
||||||
h->nr_active_devs, h->redundancy + 2, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_add(&h->list, &c->ec_stripe_head_list);
|
list_add(&h->list, &c->ec_stripe_head_list);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
@ -1776,7 +1802,7 @@ void bch2_ec_stripe_head_put(struct bch_fs *c, struct ec_stripe_head *h)
|
|||||||
h->s->allocated &&
|
h->s->allocated &&
|
||||||
bitmap_weight(h->s->blocks_allocated,
|
bitmap_weight(h->s->blocks_allocated,
|
||||||
h->s->nr_data) == h->s->nr_data)
|
h->s->nr_data) == h->s->nr_data)
|
||||||
ec_stripe_set_pending(c, h);
|
ec_stripe_new_set_pending(c, h);
|
||||||
|
|
||||||
mutex_unlock(&h->lock);
|
mutex_unlock(&h->lock);
|
||||||
}
|
}
|
||||||
@ -1801,7 +1827,7 @@ __bch2_ec_stripe_head_get(struct btree_trans *trans,
|
|||||||
|
|
||||||
if (test_bit(BCH_FS_going_ro, &c->flags)) {
|
if (test_bit(BCH_FS_going_ro, &c->flags)) {
|
||||||
h = ERR_PTR(-BCH_ERR_erofs_no_writes);
|
h = ERR_PTR(-BCH_ERR_erofs_no_writes);
|
||||||
goto found;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(h, &c->ec_stripe_head_list, list)
|
list_for_each_entry(h, &c->ec_stripe_head_list, list)
|
||||||
@ -1810,18 +1836,23 @@ __bch2_ec_stripe_head_get(struct btree_trans *trans,
|
|||||||
h->redundancy == redundancy &&
|
h->redundancy == redundancy &&
|
||||||
h->watermark == watermark) {
|
h->watermark == watermark) {
|
||||||
ret = bch2_trans_mutex_lock(trans, &h->lock);
|
ret = bch2_trans_mutex_lock(trans, &h->lock);
|
||||||
if (ret)
|
if (ret) {
|
||||||
h = ERR_PTR(ret);
|
h = ERR_PTR(ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
|
|
||||||
h = ec_new_stripe_head_alloc(c, disk_label, algo, redundancy, watermark);
|
h = ec_new_stripe_head_alloc(c, disk_label, algo, redundancy, watermark);
|
||||||
found:
|
found:
|
||||||
if (!IS_ERR_OR_NULL(h) &&
|
if (h->rw_devs_change_count != c->rw_devs_change_count)
|
||||||
h->nr_active_devs < h->redundancy + 2) {
|
ec_stripe_head_devs_update(c, h);
|
||||||
|
|
||||||
|
if (h->insufficient_devs) {
|
||||||
mutex_unlock(&h->lock);
|
mutex_unlock(&h->lock);
|
||||||
h = NULL;
|
h = NULL;
|
||||||
}
|
}
|
||||||
|
err:
|
||||||
mutex_unlock(&c->ec_stripe_head_lock);
|
mutex_unlock(&c->ec_stripe_head_lock);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
@ -2261,8 +2292,7 @@ static void __bch2_ec_stop(struct bch_fs *c, struct bch_dev *ca)
|
|||||||
}
|
}
|
||||||
goto unlock;
|
goto unlock;
|
||||||
found:
|
found:
|
||||||
h->s->err = -BCH_ERR_erofs_no_writes;
|
ec_stripe_new_cancel(c, h, -BCH_ERR_erofs_no_writes);
|
||||||
ec_stripe_set_pending(c, h);
|
|
||||||
unlock:
|
unlock:
|
||||||
mutex_unlock(&h->lock);
|
mutex_unlock(&h->lock);
|
||||||
}
|
}
|
||||||
|
@ -192,6 +192,9 @@ struct ec_stripe_head {
|
|||||||
unsigned algo;
|
unsigned algo;
|
||||||
unsigned redundancy;
|
unsigned redundancy;
|
||||||
enum bch_watermark watermark;
|
enum bch_watermark watermark;
|
||||||
|
bool insufficient_devs;
|
||||||
|
|
||||||
|
unsigned long rw_devs_change_count;
|
||||||
|
|
||||||
u64 nr_created;
|
u64 nr_created;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user