bcachefs: Redo copygc throttling

The code that checked the current free space and waited if it was too
big was causing issues - btree node allocations do not increment the
write IO clock (perhaps they should); but more broadly the check
wouldn't run copygc at all until the device was mostly full, at which
point it might have to do a bunch of work.

This redoes that logic so that copygc starts to run earlier, smoothly
running more and more often as the device becomes closer to full.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2019-12-20 16:19:46 -05:00 committed by Kent Overstreet
parent 5873efbfd9
commit 309c54c3f4

View File

@ -212,14 +212,36 @@ static void bch2_copygc(struct bch_fs *c, struct bch_dev *ca)
buckets_to_move, buckets_not_moved);
}
/*
* Copygc runs when the amount of fragmented data is above some arbitrary
* threshold:
*
* The threshold at the limit - when the device is full - is the amount of space
* we reserved in bch2_recalc_capacity; we can't have more than that amount of
* disk space stranded due to fragmentation and store everything we have
* promised to store.
*
* But we don't want to be running copygc unnecessarily when the device still
* has plenty of free space - rather, we want copygc to smoothly run every so
* often and continually reduce the amount of fragmented space as the device
* fills up. So, we increase the threshold by half the current free space.
*/
unsigned long bch2_copygc_wait_amount(struct bch_dev *ca)
{
struct bch_fs *c = ca->fs;
struct bch_dev_usage usage = bch2_dev_usage_read(c, ca);
u64 fragmented_allowed = ca->copygc_threshold +
((__dev_buckets_available(ca, usage) * ca->mi.bucket_size) >> 1);
return max_t(s64, 0, fragmented_allowed - usage.sectors_fragmented);
}
static int bch2_copygc_thread(void *arg)
{
struct bch_dev *ca = arg;
struct bch_fs *c = ca->fs;
struct io_clock *clock = &c->io_clock[WRITE];
struct bch_dev_usage usage;
unsigned long last;
u64 available, fragmented, reserve, next;
unsigned long last, wait;
set_freezable();
@ -228,28 +250,10 @@ static int bch2_copygc_thread(void *arg)
break;
last = atomic_long_read(&clock->now);
wait = bch2_copygc_wait_amount(ca);
reserve = ca->copygc_threshold;
usage = bch2_dev_usage_read(c, ca);
available = __dev_buckets_available(ca, usage) *
ca->mi.bucket_size;
if (available > reserve) {
next = last + available - reserve;
bch2_kthread_io_clock_wait(clock, next,
MAX_SCHEDULE_TIMEOUT);
continue;
}
/*
* don't start copygc until there's more than half the copygc
* reserve of fragmented space:
*/
fragmented = usage.sectors_fragmented;
if (fragmented < reserve) {
next = last + reserve - fragmented;
bch2_kthread_io_clock_wait(clock, next,
if (wait > clock->max_slop) {
bch2_kthread_io_clock_wait(clock, last + wait,
MAX_SCHEDULE_TIMEOUT);
continue;
}