+struct copygc_bucket_in_flight {
+ struct bpos bucket;
+ u8 gen;
+ struct moving_context ctxt;
+};
+
+typedef FIFO(struct copygc_bucket_in_flight) copygc_buckets_in_flight;
+
+struct copygc_bucket {
+ struct bpos bucket;
+ u8 gen;
+};
+
+typedef DARRAY(struct copygc_bucket) copygc_buckets;
+
+static int copygc_bucket_cmp(const void *_l, const void *_r)
+{
+ const struct copygc_bucket *l = _l;
+ const struct copygc_bucket *r = _r;
+
+ return bpos_cmp(l->bucket, r->bucket) ?: cmp_int(l->gen, r->gen);
+}
+
+static bool bucket_in_flight(copygc_buckets *buckets_sorted, struct copygc_bucket b)
+{
+ return bsearch(&b,
+ buckets_sorted->data,
+ buckets_sorted->nr,
+ sizeof(buckets_sorted->data[0]),
+ copygc_bucket_cmp) != NULL;
+}
+
+static void copygc_buckets_wait(struct btree_trans *trans,
+ copygc_buckets_in_flight *buckets_in_flight,
+ size_t nr, bool verify_evacuated)
+{
+ while (!fifo_empty(buckets_in_flight)) {
+ struct copygc_bucket_in_flight *i = &fifo_peek_front(buckets_in_flight);
+
+ if (fifo_used(buckets_in_flight) <= nr &&
+ closure_nr_remaining(&i->ctxt.cl) != 1)
+ break;
+
+ /*
+ * moving_ctxt_exit calls bch2_write as it flushes pending
+ * reads, which inits another btree_trans; this one must be
+ * unlocked:
+ */
+ bch2_trans_unlock(trans);
+ bch2_moving_ctxt_exit(&i->ctxt);
+ if (verify_evacuated)
+ bch2_verify_bucket_evacuated(trans, i->bucket, i->gen);
+ buckets_in_flight->front++;
+ }
+}
+
+static int bch2_copygc_get_buckets(struct btree_trans *trans,
+ copygc_buckets_in_flight *buckets_in_flight,
+ copygc_buckets *buckets)