ssize_t i = eytzinger0_find_le(h->data, h->used,
sizeof(h->data[0]),
bucket_offset_cmp, &search);
+#if 0
+ /* eytzinger search verify code: */
+ ssize_t j = -1, k;
+ for (k = 0; k < h->used; k++)
+ if (h->data[k].offset <= ptr->offset &&
+ (j < 0 || h->data[k].offset > h->data[j].offset))
+ j = k;
+
+ BUG_ON(i != j);
+#endif
return (i >= 0 &&
ptr->offset < h->data[i].offset + ca->mi.bucket_size &&
ptr->gen == h->data[i].gen);
if (sectors_not_moved && !ret)
bch_warn_ratelimited(c,
- "copygc finished but %llu/%llu sectors, %llu/%llu buckets not moved",
+ "copygc finished but %llu/%llu sectors, %llu/%llu buckets not moved (move stats: moved %llu sectors, raced %llu keys, %llu sectors)",
sectors_not_moved, sectors_to_move,
- buckets_not_moved, buckets_to_move);
+ buckets_not_moved, buckets_to_move,
+ atomic64_read(&move_stats.sectors_moved),
+ atomic64_read(&move_stats.keys_raced),
+ atomic64_read(&move_stats.sectors_raced));
trace_copygc(ca,
atomic64_read(&move_stats.sectors_moved), sectors_not_moved,
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();
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;
}