From 12fe5797ad3e58a365442751aed58c776c09e69f Mon Sep 17 00:00:00 2001
From: Kent Overstreet <kent.overstreet@gmail.com>
Date: Thu, 30 Dec 2021 21:23:06 -0500
Subject: [PATCH] Update bcachefs sources to 916d92b6b4 bcachefs: Add error
 messages for memory allocation failures

---
 .bcachefs_revision                  |   2 +-
 libbcachefs/alloc_background.c      |   3 +-
 libbcachefs/bcachefs.h              |   1 +
 libbcachefs/btree_cache.c           |   2 +
 libbcachefs/btree_gc.c              |  24 +++---
 libbcachefs/btree_iter.c            |  17 ++--
 libbcachefs/btree_key_cache.c       |  28 ++++---
 libbcachefs/btree_update.h          |   2 +-
 libbcachefs/btree_update_interior.h |   4 +-
 libbcachefs/btree_update_leaf.c     |  14 +++-
 libbcachefs/buckets.c               |  16 ++--
 libbcachefs/ec.c                    |   2 +-
 libbcachefs/fsck.c                  |  37 +++++----
 libbcachefs/inode.c                 |  57 ++++++++-----
 libbcachefs/movinggc.c              |   5 ++
 libbcachefs/quota.c                 |   6 +-
 libbcachefs/recovery.c              | 121 +++++++++++++++++++---------
 libbcachefs/recovery.h              |   2 +
 libbcachefs/super.c                 |  46 ++++++-----
 libbcachefs/tests.c                 |  38 +++++----
 libbcachefs/util.c                  |   2 +-
 21 files changed, 268 insertions(+), 161 deletions(-)

diff --git a/.bcachefs_revision b/.bcachefs_revision
index e2012548..72264415 100644
--- a/.bcachefs_revision
+++ b/.bcachefs_revision
@@ -1 +1 @@
-078a1a596a74ade60db6eee0f0be927defb7abed
+916d92b6b46b13873118a608ff16212f966375ba
diff --git a/libbcachefs/alloc_background.c b/libbcachefs/alloc_background.c
index 2a36af5e..eb2e6642 100644
--- a/libbcachefs/alloc_background.c
+++ b/libbcachefs/alloc_background.c
@@ -859,7 +859,8 @@ static void discard_one_bucket(struct bch_fs *c, struct bch_dev *ca, u64 b)
 static bool allocator_thread_running(struct bch_dev *ca)
 {
 	unsigned state = ca->mi.state == BCH_MEMBER_STATE_rw &&
-		test_bit(BCH_FS_ALLOCATOR_RUNNING, &ca->fs->flags)
+		test_bit(BCH_FS_ALLOCATOR_RUNNING, &ca->fs->flags) &&
+		test_bit(BCH_FS_ALLOC_REPLAY_DONE, &ca->fs->flags)
 		? ALLOCATOR_running
 		: ALLOCATOR_stopped;
 	alloc_thread_set_state(ca, state);
diff --git a/libbcachefs/bcachefs.h b/libbcachefs/bcachefs.h
index 3ada85ac..b21151ea 100644
--- a/libbcachefs/bcachefs.h
+++ b/libbcachefs/bcachefs.h
@@ -510,6 +510,7 @@ enum {
 	BCH_FS_INITIAL_GC_DONE,
 	BCH_FS_INITIAL_GC_UNFIXED,
 	BCH_FS_TOPOLOGY_REPAIR_DONE,
+	BCH_FS_ALLOC_REPLAY_DONE,
 	BCH_FS_BTREE_INTERIOR_REPLAY_DONE,
 	BCH_FS_FSCK_DONE,
 	BCH_FS_STARTED,
diff --git a/libbcachefs/btree_cache.c b/libbcachefs/btree_cache.c
index b13563d7..2788ba17 100644
--- a/libbcachefs/btree_cache.c
+++ b/libbcachefs/btree_cache.c
@@ -83,6 +83,8 @@ static int btree_node_data_alloc(struct bch_fs *c, struct btree *b, gfp_t gfp)
 	b->aux_data = mmap(NULL, btree_aux_data_bytes(b),
 			   PROT_READ|PROT_WRITE|PROT_EXEC,
 			   MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
+	if (b->aux_data == MAP_FAILED)
+		b->aux_data = NULL;
 #endif
 	if (!b->aux_data) {
 		kvpfree(b->data, btree_bytes(c));
diff --git a/libbcachefs/btree_gc.c b/libbcachefs/btree_gc.c
index d1883701..9e3213b9 100644
--- a/libbcachefs/btree_gc.c
+++ b/libbcachefs/btree_gc.c
@@ -169,11 +169,11 @@ static int set_node_min(struct bch_fs *c, struct btree *b, struct bpos new_min)
 	new->v.min_key		= new_min;
 	SET_BTREE_PTR_RANGE_UPDATED(&new->v, true);
 
-	ret = bch2_journal_key_insert(c, b->c.btree_id, b->c.level + 1, &new->k_i);
-	kfree(new);
-
-	if (ret)
+	ret = bch2_journal_key_insert_take(c, b->c.btree_id, b->c.level + 1, &new->k_i);
+	if (ret) {
+		kfree(new);
 		return ret;
+	}
 
 	bch2_btree_node_drop_keys_outside_node(b);
 
@@ -198,11 +198,11 @@ static int set_node_max(struct bch_fs *c, struct btree *b, struct bpos new_max)
 	new->k.p		= new_max;
 	SET_BTREE_PTR_RANGE_UPDATED(&new->v, true);
 
-	ret = bch2_journal_key_insert(c, b->c.btree_id, b->c.level + 1, &new->k_i);
-	kfree(new);
-
-	if (ret)
+	ret = bch2_journal_key_insert_take(c, b->c.btree_id, b->c.level + 1, &new->k_i);
+	if (ret) {
+		kfree(new);
 		return ret;
+	}
 
 	bch2_btree_node_drop_keys_outside_node(b);
 
@@ -690,10 +690,10 @@ found:
 			}
 		}
 
-		ret = bch2_journal_key_insert(c, btree_id, level, new);
-		kfree(new);
-
-		if (!ret)
+		ret = bch2_journal_key_insert_take(c, btree_id, level, new);
+		if (ret)
+			kfree(new);
+		else
 			*k = bkey_i_to_s_c(new);
 	}
 fsck_err:
diff --git a/libbcachefs/btree_iter.c b/libbcachefs/btree_iter.c
index 65ab2cd6..9ebb81d7 100644
--- a/libbcachefs/btree_iter.c
+++ b/libbcachefs/btree_iter.c
@@ -2437,15 +2437,18 @@ struct bkey_s_c bch2_btree_iter_peek_slot(struct btree_iter *iter)
 		if (bkey_cmp(iter->pos, next) < 0) {
 			bkey_init(&iter->k);
 			iter->k.p = iter->pos;
-			bch2_key_resize(&iter->k,
-					min_t(u64, KEY_SIZE_MAX,
-					      (next.inode == iter->pos.inode
-					       ? next.offset
-					       : KEY_OFFSET_MAX) -
-					      iter->pos.offset));
+
+			if (iter->flags & BTREE_ITER_IS_EXTENTS) {
+				bch2_key_resize(&iter->k,
+						min_t(u64, KEY_SIZE_MAX,
+						      (next.inode == iter->pos.inode
+						       ? next.offset
+						       : KEY_OFFSET_MAX) -
+						      iter->pos.offset));
+				EBUG_ON(!iter->k.size);
+			}
 
 			k = (struct bkey_s_c) { &iter->k, NULL };
-			EBUG_ON(!k.k->size);
 		}
 	}
 
diff --git a/libbcachefs/btree_key_cache.c b/libbcachefs/btree_key_cache.c
index 230a920a..80ed79b0 100644
--- a/libbcachefs/btree_key_cache.c
+++ b/libbcachefs/btree_key_cache.c
@@ -146,19 +146,23 @@ bkey_cached_reuse(struct btree_key_cache *c)
 }
 
 static struct bkey_cached *
-btree_key_cache_create(struct btree_key_cache *c,
+btree_key_cache_create(struct bch_fs *c,
 		       enum btree_id btree_id,
 		       struct bpos pos)
 {
+	struct btree_key_cache *bc = &c->btree_key_cache;
 	struct bkey_cached *ck;
 	bool was_new = true;
 
-	ck = bkey_cached_alloc(c);
+	ck = bkey_cached_alloc(bc);
 
 	if (unlikely(!ck)) {
-		ck = bkey_cached_reuse(c);
-		if (unlikely(!ck))
+		ck = bkey_cached_reuse(bc);
+		if (unlikely(!ck)) {
+			bch_err(c, "error allocating memory for key cache item, btree %s",
+				bch2_btree_ids[btree_id]);
 			return ERR_PTR(-ENOMEM);
+		}
 
 		was_new = false;
 	}
@@ -175,7 +179,7 @@ btree_key_cache_create(struct btree_key_cache *c,
 	ck->valid		= false;
 	ck->flags		= 1U << BKEY_CACHED_ACCESSED;
 
-	if (unlikely(rhashtable_lookup_insert_fast(&c->table,
+	if (unlikely(rhashtable_lookup_insert_fast(&bc->table,
 					  &ck->hash,
 					  bch2_btree_key_cache_params))) {
 		/* We raced with another fill: */
@@ -185,15 +189,15 @@ btree_key_cache_create(struct btree_key_cache *c,
 			six_unlock_intent(&ck->c.lock);
 			kfree(ck);
 		} else {
-			mutex_lock(&c->lock);
-			bkey_cached_free(c, ck);
-			mutex_unlock(&c->lock);
+			mutex_lock(&bc->lock);
+			bkey_cached_free(bc, ck);
+			mutex_unlock(&bc->lock);
 		}
 
 		return NULL;
 	}
 
-	atomic_long_inc(&c->nr_keys);
+	atomic_long_inc(&bc->nr_keys);
 
 	six_unlock_write(&ck->c.lock);
 
@@ -204,6 +208,7 @@ static int btree_key_cache_fill(struct btree_trans *trans,
 				struct btree_path *ck_path,
 				struct bkey_cached *ck)
 {
+	struct bch_fs *c = trans->c;
 	struct btree_iter iter;
 	struct bkey_s_c k;
 	unsigned new_u64s = 0;
@@ -233,6 +238,8 @@ static int btree_key_cache_fill(struct btree_trans *trans,
 		new_u64s = roundup_pow_of_two(new_u64s);
 		new_k = kmalloc(new_u64s * sizeof(u64), GFP_NOFS);
 		if (!new_k) {
+			bch_err(c, "error allocating memory for key cache key, btree %s u64s %u",
+				bch2_btree_ids[ck->key.btree_id], new_u64s);
 			ret = -ENOMEM;
 			goto err;
 		}
@@ -293,8 +300,7 @@ retry:
 			return 0;
 		}
 
-		ck = btree_key_cache_create(&c->btree_key_cache,
-					    path->btree_id, path->pos);
+		ck = btree_key_cache_create(c, path->btree_id, path->pos);
 		ret = PTR_ERR_OR_ZERO(ck);
 		if (ret)
 			goto err;
diff --git a/libbcachefs/btree_update.h b/libbcachefs/btree_update.h
index 89f07e58..16ebf1a2 100644
--- a/libbcachefs/btree_update.h
+++ b/libbcachefs/btree_update.h
@@ -63,7 +63,7 @@ int bch2_btree_insert(struct bch_fs *, enum btree_id, struct bkey_i *,
 int bch2_btree_delete_range_trans(struct btree_trans *, enum btree_id,
 				  struct bpos, struct bpos, unsigned, u64 *);
 int bch2_btree_delete_range(struct bch_fs *, enum btree_id,
-			    struct bpos, struct bpos, u64 *);
+			    struct bpos, struct bpos, unsigned, u64 *);
 
 int bch2_btree_node_rewrite(struct btree_trans *, struct btree_iter *,
 			    struct btree *, unsigned);
diff --git a/libbcachefs/btree_update_interior.h b/libbcachefs/btree_update_interior.h
index 8cf59cee..8dc86fa6 100644
--- a/libbcachefs/btree_update_interior.h
+++ b/libbcachefs/btree_update_interior.h
@@ -82,12 +82,12 @@ struct btree_update {
 	/* Nodes being freed: */
 	struct keylist			old_keys;
 	u64				_old_keys[BTREE_UPDATE_NODES_MAX *
-						  BKEY_BTREE_PTR_VAL_U64s_MAX];
+						  BKEY_BTREE_PTR_U64s_MAX];
 
 	/* Nodes being added: */
 	struct keylist			new_keys;
 	u64				_new_keys[BTREE_UPDATE_NODES_MAX *
-						  BKEY_BTREE_PTR_VAL_U64s_MAX];
+						  BKEY_BTREE_PTR_U64s_MAX];
 
 	/* New nodes, that will be made reachable by this update: */
 	struct btree			*new_nodes[BTREE_UPDATE_NODES_MAX];
diff --git a/libbcachefs/btree_update_leaf.c b/libbcachefs/btree_update_leaf.c
index 1966441b..f561e09c 100644
--- a/libbcachefs/btree_update_leaf.c
+++ b/libbcachefs/btree_update_leaf.c
@@ -308,6 +308,7 @@ btree_key_can_insert_cached(struct btree_trans *trans,
 			    struct btree_path *path,
 			    unsigned u64s)
 {
+	struct bch_fs *c = trans->c;
 	struct bkey_cached *ck = (void *) path->l[0].b;
 	unsigned new_u64s;
 	struct bkey_i *new_k;
@@ -315,7 +316,7 @@ btree_key_can_insert_cached(struct btree_trans *trans,
 	EBUG_ON(path->level);
 
 	if (!test_bit(BKEY_CACHED_DIRTY, &ck->flags) &&
-	    bch2_btree_key_cache_must_wait(trans->c) &&
+	    bch2_btree_key_cache_must_wait(c) &&
 	    !(trans->flags & BTREE_INSERT_JOURNAL_RECLAIM))
 		return BTREE_INSERT_NEED_JOURNAL_RECLAIM;
 
@@ -330,8 +331,11 @@ btree_key_can_insert_cached(struct btree_trans *trans,
 
 	new_u64s	= roundup_pow_of_two(u64s);
 	new_k		= krealloc(ck->k, new_u64s * sizeof(u64), GFP_NOFS);
-	if (!new_k)
+	if (!new_k) {
+		bch_err(c, "error allocating memory for key cache key, btree %s u64s %u",
+			bch2_btree_ids[path->btree_id], new_u64s);
 		return -ENOMEM;
+	}
 
 	ck->u64s	= new_u64s;
 	ck->k		= new_k;
@@ -1463,7 +1467,7 @@ retry:
 		 */
 		delete.k.p = iter.pos;
 
-		if (btree_node_type_is_extents(id)) {
+		if (iter.flags & BTREE_ITER_IS_EXTENTS) {
 			unsigned max_sectors =
 				KEY_SIZE_MAX & (~0 << trans->c->block_bits);
 
@@ -1500,8 +1504,10 @@ retry:
  */
 int bch2_btree_delete_range(struct bch_fs *c, enum btree_id id,
 			    struct bpos start, struct bpos end,
+			    unsigned iter_flags,
 			    u64 *journal_seq)
 {
 	return bch2_trans_do(c, NULL, journal_seq, 0,
-			     bch2_btree_delete_range_trans(&trans, id, start, end, 0, journal_seq));
+			     bch2_btree_delete_range_trans(&trans, id, start, end,
+							   iter_flags, journal_seq));
 }
diff --git a/libbcachefs/buckets.c b/libbcachefs/buckets.c
index 738ce67d..709df569 100644
--- a/libbcachefs/buckets.c
+++ b/libbcachefs/buckets.c
@@ -940,9 +940,11 @@ static int bch2_mark_stripe_ptr(struct btree_trans *trans,
 	BUG_ON(!(flags & BTREE_TRIGGER_GC));
 
 	m = genradix_ptr_alloc(&c->gc_stripes, p.idx, GFP_KERNEL);
-
-	if (!m)
+	if (!m) {
+		bch_err(c, "error allocating memory for gc_stripes, idx %llu",
+			(u64) p.idx);
 		return -ENOMEM;
+	}
 
 	spin_lock(&c->ec_stripes_heap_lock);
 
@@ -1053,7 +1055,7 @@ static int bch2_mark_stripe(struct btree_trans *trans,
 	bool gc = flags & BTREE_TRIGGER_GC;
 	u64 journal_seq = trans->journal_res.seq;
 	struct bch_fs *c = trans->c;
-	size_t idx = new.k->p.offset;
+	u64 idx = new.k->p.offset;
 	const struct bch_stripe *old_s = old.k->type == KEY_TYPE_stripe
 		? bkey_s_c_to_stripe(old).v : NULL;
 	const struct bch_stripe *new_s = new.k->type == KEY_TYPE_stripe
@@ -1071,7 +1073,7 @@ static int bch2_mark_stripe(struct btree_trans *trans,
 
 			bch2_bkey_val_to_text(&PBUF(buf1), c, old);
 			bch2_bkey_val_to_text(&PBUF(buf2), c, new);
-			bch_err_ratelimited(c, "error marking nonexistent stripe %zu while marking\n"
+			bch_err_ratelimited(c, "error marking nonexistent stripe %llu while marking\n"
 					    "old %s\n"
 					    "new %s", idx, buf1, buf2);
 			bch2_inconsistent_error(c);
@@ -1103,9 +1105,11 @@ static int bch2_mark_stripe(struct btree_trans *trans,
 		struct gc_stripe *m =
 			genradix_ptr_alloc(&c->gc_stripes, idx, GFP_KERNEL);
 
-		if (!m)
+		if (!m) {
+			bch_err(c, "error allocating memory for gc_stripes, idx %llu",
+				idx);
 			return -ENOMEM;
-
+		}
 		/*
 		 * This will be wrong when we bring back runtime gc: we should
 		 * be unmarking the old key and then marking the new key
diff --git a/libbcachefs/ec.c b/libbcachefs/ec.c
index 3cccd1fa..9a1751d4 100644
--- a/libbcachefs/ec.c
+++ b/libbcachefs/ec.c
@@ -677,7 +677,7 @@ static int ec_stripe_delete(struct bch_fs *c, size_t idx)
 	return bch2_btree_delete_range(c, BTREE_ID_stripes,
 				       POS(0, idx),
 				       POS(0, idx + 1),
-				       NULL);
+				       0, NULL);
 }
 
 static void ec_stripe_delete_work(struct work_struct *work)
diff --git a/libbcachefs/fsck.c b/libbcachefs/fsck.c
index 835ef114..43b6159b 100644
--- a/libbcachefs/fsck.c
+++ b/libbcachefs/fsck.c
@@ -564,14 +564,17 @@ static struct inode_walker inode_walker_init(void)
 	return (struct inode_walker) { 0, };
 }
 
-static int inode_walker_realloc(struct inode_walker *w)
+static int inode_walker_realloc(struct bch_fs *c, struct inode_walker *w)
 {
 	if (w->nr == w->size) {
 		size_t new_size = max_t(size_t, 8UL, w->size * 2);
 		void *d = krealloc(w->d, new_size * sizeof(w->d[0]),
 				   GFP_KERNEL);
-		if (!d)
+		if (!d) {
+			bch_err(c, "fsck: error allocating memory for inode_walker, size %zu",
+				new_size);
 			return -ENOMEM;
+		}
 
 		w->d = d;
 		w->size = new_size;
@@ -586,7 +589,7 @@ static int add_inode(struct bch_fs *c, struct inode_walker *w,
 	struct bch_inode_unpacked u;
 	int ret;
 
-	ret = inode_walker_realloc(w);
+	ret = inode_walker_realloc(c, w);
 	if (ret)
 		return ret;
 
@@ -647,7 +650,7 @@ found:
 		while (i && w->d[i - 1].snapshot > pos.snapshot)
 			--i;
 
-		ret = inode_walker_realloc(w);
+		ret = inode_walker_realloc(c, w);
 		if (ret)
 			return ret;
 
@@ -1132,9 +1135,9 @@ static int check_i_sectors(struct btree_trans *trans, struct inode_walker *w)
 		count2 = lockrestart_do(trans,
 			bch2_count_inode_sectors(trans, w->cur_inum, i->snapshot));
 
-		if (fsck_err_on(i->count != count2, c,
-				"fsck counted i_sectors wrong: got %llu should be %llu",
-				i->count, count2)) {
+		if (i->count != count2) {
+			bch_err(c, "fsck counted i_sectors wrong: got %llu should be %llu",
+				i->count, count2);
 			i->count = count2;
 			if (i->inode.bi_sectors == i->count)
 				continue;
@@ -1316,9 +1319,9 @@ static int check_subdir_count(struct btree_trans *trans, struct inode_walker *w)
 		count2 = lockrestart_do(trans,
 				bch2_count_subdirs(trans, w->cur_inum, i->snapshot));
 
-		if (fsck_err_on(i->count != count2, c,
-				"directory %llu:%u: fsck counted subdirectories wrong, got %llu should be %llu",
-				w->cur_inum, i->snapshot, i->count, count2)) {
+		if (i->count != count2) {
+			bch_err(c, "fsck counted subdirectories wrong: got %llu should be %llu",
+				i->count, count2);
 			i->count = count2;
 			if (i->inode.bi_nlink == i->count)
 				continue;
@@ -1812,7 +1815,8 @@ static bool path_is_dup(struct pathbuf *p, u64 inum, u32 snapshot)
 	return false;
 }
 
-static int path_down(struct pathbuf *p, u64 inum, u32 snapshot)
+static int path_down(struct bch_fs *c, struct pathbuf *p,
+		     u64 inum, u32 snapshot)
 {
 	if (p->nr == p->size) {
 		size_t new_size = max_t(size_t, 256UL, p->size * 2);
@@ -1820,6 +1824,8 @@ static int path_down(struct pathbuf *p, u64 inum, u32 snapshot)
 				   new_size * sizeof(p->entries[0]),
 				   GFP_KERNEL);
 		if (!n) {
+			bch_err(c, "fsck: error allocating memory for pathbuf, size %zu",
+				new_size);
 			return -ENOMEM;
 		}
 
@@ -1893,7 +1899,7 @@ static int check_path(struct btree_trans *trans,
 		if (!S_ISDIR(inode->bi_mode))
 			break;
 
-		ret = path_down(p, inode->bi_inum, snapshot);
+		ret = path_down(c, p, inode->bi_inum, snapshot);
 		if (ret) {
 			bch_err(c, "memory allocation failure");
 			return ret;
@@ -1998,12 +2004,15 @@ struct nlink_table {
 	}		*d;
 };
 
-static int add_nlink(struct nlink_table *t, u64 inum, u32 snapshot)
+static int add_nlink(struct bch_fs *c, struct nlink_table *t,
+		     u64 inum, u32 snapshot)
 {
 	if (t->nr == t->size) {
 		size_t new_size = max_t(size_t, 128UL, t->size * 2);
 		void *d = kvmalloc(new_size * sizeof(t->d[0]), GFP_KERNEL);
 		if (!d) {
+			bch_err(c, "fsck: error allocating memory for nlink_table, size %zu",
+				new_size);
 			return -ENOMEM;
 		}
 
@@ -2093,7 +2102,7 @@ static int check_nlinks_find_hardlinks(struct bch_fs *c,
 		if (!u.bi_nlink)
 			continue;
 
-		ret = add_nlink(t, k.k->p.offset, k.k->p.snapshot);
+		ret = add_nlink(c, t, k.k->p.offset, k.k->p.snapshot);
 		if (ret) {
 			*end = k.k->p.offset;
 			ret = 0;
diff --git a/libbcachefs/inode.c b/libbcachefs/inode.c
index 3a7c1468..ef6da535 100644
--- a/libbcachefs/inode.c
+++ b/libbcachefs/inode.c
@@ -585,49 +585,62 @@ found_slot:
 static int bch2_inode_delete_keys(struct btree_trans *trans,
 				  subvol_inum inum, enum btree_id id)
 {
-	struct btree_iter iter;
-	struct bkey_s_c k;
-	struct bkey_i delete;
-	u32 snapshot;
+	u64 offset = 0;
 	int ret = 0;
 
-	/*
-	 * We're never going to be deleting extents, no need to use an extent
-	 * iterator:
-	 */
-	bch2_trans_iter_init(trans, &iter, id, POS(inum.inum, 0),
-			     BTREE_ITER_NOT_EXTENTS|
-			     BTREE_ITER_INTENT);
+	while (!ret || ret == -EINTR) {
+		struct disk_reservation disk_res =
+			bch2_disk_reservation_init(trans->c, 0);
+		struct btree_iter iter;
+		struct bkey_s_c k;
+		struct bkey_i delete;
+		u32 snapshot;
 
-	while (1) {
 		bch2_trans_begin(trans);
 
 		ret = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot);
 		if (ret)
-			goto err;
-
-		bch2_btree_iter_set_snapshot(&iter, snapshot);
+			continue;
 
+		bch2_trans_iter_init(trans, &iter, id,
+				     SPOS(inum.inum, offset, snapshot),
+				     BTREE_ITER_INTENT);
 		k = bch2_btree_iter_peek(&iter);
+
+		if (!k.k || iter.pos.inode != inum.inum) {
+			bch2_trans_iter_exit(trans, &iter);
+			break;
+		}
+
 		ret = bkey_err(k);
 		if (ret)
 			goto err;
 
-		if (!k.k || iter.pos.inode != inum.inum)
-			break;
-
 		bkey_init(&delete.k);
 		delete.k.p = iter.pos;
 
+		if (btree_node_type_is_extents(iter.btree_id)) {
+			unsigned max_sectors =
+				min_t(u64, U64_MAX - iter.pos.offset,
+				      KEY_SIZE_MAX & (~0 << trans->c->block_bits));
+
+			/* create the biggest key we can */
+			bch2_key_resize(&delete.k, max_sectors);
+
+			ret = bch2_extent_trim_atomic(trans, &iter, &delete);
+			if (ret)
+				goto err;
+		}
+
 		ret = bch2_trans_update(trans, &iter, &delete, 0) ?:
-		      bch2_trans_commit(trans, NULL, NULL,
+		      bch2_trans_commit(trans, &disk_res, NULL,
 					BTREE_INSERT_NOFAIL);
+		bch2_disk_reservation_put(trans->c, &disk_res);
 err:
-		if (ret && ret != -EINTR)
-			break;
+		offset = iter.pos.offset;
+		bch2_trans_iter_exit(trans, &iter);
 	}
 
-	bch2_trans_iter_exit(trans, &iter);
 	return ret;
 }
 
diff --git a/libbcachefs/movinggc.c b/libbcachefs/movinggc.c
index 7b7eee9b..7cd1b0cf 100644
--- a/libbcachefs/movinggc.c
+++ b/libbcachefs/movinggc.c
@@ -205,6 +205,11 @@ static int bch2_copygc(struct bch_fs *c)
 		up_read(&ca->bucket_lock);
 	}
 
+	if (!h->used) {
+		bch_err_ratelimited(c, "copygc requested to run but found no buckets to move!");
+		return 0;
+	}
+
 	/*
 	 * Our btree node allocations also come out of RESERVE_MOVINGGC:
 	 */
diff --git a/libbcachefs/quota.c b/libbcachefs/quota.c
index 8f8f4b0a..54bb2a45 100644
--- a/libbcachefs/quota.c
+++ b/libbcachefs/quota.c
@@ -570,7 +570,7 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags)
 		ret = bch2_btree_delete_range(c, BTREE_ID_quotas,
 					      POS(QTYP_USR, 0),
 					      POS(QTYP_USR + 1, 0),
-					      NULL);
+					      0, NULL);
 		if (ret)
 			return ret;
 	}
@@ -582,7 +582,7 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags)
 		ret = bch2_btree_delete_range(c, BTREE_ID_quotas,
 					      POS(QTYP_GRP, 0),
 					      POS(QTYP_GRP + 1, 0),
-					      NULL);
+					      0, NULL);
 		if (ret)
 			return ret;
 	}
@@ -594,7 +594,7 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags)
 		ret = bch2_btree_delete_range(c, BTREE_ID_quotas,
 					      POS(QTYP_PRJ, 0),
 					      POS(QTYP_PRJ + 1, 0),
-					      NULL);
+					      0, NULL);
 		if (ret)
 			return ret;
 	}
diff --git a/libbcachefs/recovery.c b/libbcachefs/recovery.c
index 8b0e468f..0b923037 100644
--- a/libbcachefs/recovery.c
+++ b/libbcachefs/recovery.c
@@ -109,18 +109,27 @@ static void journal_iter_fix(struct bch_fs *c, struct journal_iter *iter, unsign
 		iter->idx++;
 }
 
-int bch2_journal_key_insert(struct bch_fs *c, enum btree_id id,
-			    unsigned level, struct bkey_i *k)
+int bch2_journal_key_insert_take(struct bch_fs *c, enum btree_id id,
+				 unsigned level, struct bkey_i *k)
 {
 	struct journal_key n = {
 		.btree_id	= id,
 		.level		= level,
+		.k		= k,
 		.allocated	= true
 	};
 	struct journal_keys *keys = &c->journal_keys;
 	struct journal_iter *iter;
 	unsigned idx = journal_key_search(keys, id, level, k->k.p);
 
+	if (idx < keys->nr &&
+	    journal_key_cmp(&n, &keys->d[idx]) == 0) {
+		if (keys->d[idx].allocated)
+			kfree(keys->d[idx].k);
+		keys->d[idx] = n;
+		return 0;
+	}
+
 	if (keys->nr == keys->size) {
 		struct journal_keys new_keys = {
 			.nr			= keys->nr,
@@ -140,27 +149,31 @@ int bch2_journal_key_insert(struct bch_fs *c, enum btree_id id,
 		*keys = new_keys;
 	}
 
-	n.k = kmalloc(bkey_bytes(&k->k), GFP_KERNEL);
-	if (!n.k)
-		return -ENOMEM;
+	array_insert_item(keys->d, keys->nr, idx, n);
 
-	bkey_copy(n.k, k);
-
-	if (idx < keys->nr &&
-	    journal_key_cmp(&n, &keys->d[idx]) == 0) {
-		if (keys->d[idx].allocated)
-			kfree(keys->d[idx].k);
-		keys->d[idx] = n;
-	} else {
-		array_insert_item(keys->d, keys->nr, idx, n);
-
-		list_for_each_entry(iter, &c->journal_iters, list)
-			journal_iter_fix(c, iter, idx);
-	}
+	list_for_each_entry(iter, &c->journal_iters, list)
+		journal_iter_fix(c, iter, idx);
 
 	return 0;
 }
 
+int bch2_journal_key_insert(struct bch_fs *c, enum btree_id id,
+			    unsigned level, struct bkey_i *k)
+{
+	struct bkey_i *n;
+	int ret;
+
+	n = kmalloc(bkey_bytes(&k->k), GFP_KERNEL);
+	if (!n)
+		return -ENOMEM;
+
+	bkey_copy(n, k);
+	ret = bch2_journal_key_insert_take(c, id, level, n);
+	if (ret)
+		kfree(n);
+	return ret;
+}
+
 int bch2_journal_key_delete(struct bch_fs *c, enum btree_id id,
 			    unsigned level, struct bpos pos)
 {
@@ -548,8 +561,8 @@ static int bch2_journal_replay_key(struct bch_fs *c, struct journal_key *k)
 
 static int journal_sort_seq_cmp(const void *_l, const void *_r)
 {
-	const struct journal_key *l = _l;
-	const struct journal_key *r = _r;
+	const struct journal_key *l = *((const struct journal_key **)_l);
+	const struct journal_key *r = *((const struct journal_key **)_r);
 
 	return  cmp_int(r->level,	l->level) ?:
 		cmp_int(l->journal_seq, r->journal_seq) ?:
@@ -557,18 +570,30 @@ static int journal_sort_seq_cmp(const void *_l, const void *_r)
 		bpos_cmp(l->k->k.p,	r->k->k.p);
 }
 
-static int bch2_journal_replay(struct bch_fs *c,
-			       struct journal_keys keys)
+static int bch2_journal_replay(struct bch_fs *c)
 {
+	struct journal_keys *keys = &c->journal_keys;
+	struct journal_key **keys_sorted, *k;
 	struct journal *j = &c->journal;
-	struct journal_key *i;
+	struct bch_dev *ca;
+	unsigned idx;
+	size_t i;
 	u64 seq;
 	int ret;
 
-	sort(keys.d, keys.nr, sizeof(keys.d[0]), journal_sort_seq_cmp, NULL);
+	keys_sorted = kmalloc_array(sizeof(*keys_sorted), keys->nr, GFP_KERNEL);
+	if (!keys_sorted)
+		return -ENOMEM;
 
-	if (keys.nr)
-		replay_now_at(j, keys.journal_seq_base);
+	for (i = 0; i < keys->nr; i++)
+		keys_sorted[i] = &keys->d[i];
+
+	sort(keys_sorted, keys->nr,
+	     sizeof(keys_sorted[0]),
+	     journal_sort_seq_cmp, NULL);
+
+	if (keys->nr)
+		replay_now_at(j, keys->journal_seq_base);
 
 	seq = j->replay_journal_seq;
 
@@ -576,26 +601,35 @@ static int bch2_journal_replay(struct bch_fs *c,
 	 * First replay updates to the alloc btree - these will only update the
 	 * btree key cache:
 	 */
-	for_each_journal_key(keys, i) {
+	for (i = 0; i < keys->nr; i++) {
+		k = keys_sorted[i];
+
 		cond_resched();
 
-		if (!i->level && i->btree_id == BTREE_ID_alloc) {
-			j->replay_journal_seq = keys.journal_seq_base + i->journal_seq;
-			ret = bch2_journal_replay_key(c, i);
+		if (!k->level && k->btree_id == BTREE_ID_alloc) {
+			j->replay_journal_seq = keys->journal_seq_base + k->journal_seq;
+			ret = bch2_journal_replay_key(c, k);
 			if (ret)
 				goto err;
 		}
 	}
 
+	/* Now we can start the allocator threads: */
+	set_bit(BCH_FS_ALLOC_REPLAY_DONE, &c->flags);
+	for_each_member_device(ca, c, idx)
+		bch2_wake_allocator(ca);
+
 	/*
 	 * Next replay updates to interior btree nodes:
 	 */
-	for_each_journal_key(keys, i) {
+	for (i = 0; i < keys->nr; i++) {
+		k = keys_sorted[i];
+
 		cond_resched();
 
-		if (i->level) {
-			j->replay_journal_seq = keys.journal_seq_base + i->journal_seq;
-			ret = bch2_journal_replay_key(c, i);
+		if (k->level) {
+			j->replay_journal_seq = keys->journal_seq_base + k->journal_seq;
+			ret = bch2_journal_replay_key(c, k);
 			if (ret)
 				goto err;
 		}
@@ -615,15 +649,17 @@ static int bch2_journal_replay(struct bch_fs *c,
 	/*
 	 * Now replay leaf node updates:
 	 */
-	for_each_journal_key(keys, i) {
+	for (i = 0; i < keys->nr; i++) {
+		k = keys_sorted[i];
+
 		cond_resched();
 
-		if (i->level || i->btree_id == BTREE_ID_alloc)
+		if (k->level || k->btree_id == BTREE_ID_alloc)
 			continue;
 
-		replay_now_at(j, keys.journal_seq_base + i->journal_seq);
+		replay_now_at(j, keys->journal_seq_base + k->journal_seq);
 
-		ret = bch2_journal_replay_key(c, i);
+		ret = bch2_journal_replay_key(c, k);
 		if (ret)
 			goto err;
 	}
@@ -633,10 +669,14 @@ static int bch2_journal_replay(struct bch_fs *c,
 
 	bch2_journal_set_replay_done(j);
 	bch2_journal_flush_all_pins(j);
+	kfree(keys_sorted);
+
 	return bch2_journal_error(j);
 err:
 	bch_err(c, "journal replay: error %d while replaying key at btree %s level %u",
-		ret, bch2_btree_ids[i->btree_id], i->level);
+		ret, bch2_btree_ids[k->btree_id], k->level);
+	kfree(keys_sorted);
+
 	return ret;
 }
 
@@ -1208,7 +1248,7 @@ use_clean:
 
 	bch_verbose(c, "starting journal replay");
 	err = "journal replay failed";
-	ret = bch2_journal_replay(c, c->journal_keys);
+	ret = bch2_journal_replay(c);
 	if (ret)
 		goto err;
 	bch_verbose(c, "journal replay done");
@@ -1380,6 +1420,7 @@ int bch2_fs_initialize(struct bch_fs *c)
 	for (i = 0; i < BTREE_ID_NR; i++)
 		bch2_btree_root_alloc(c, i);
 
+	set_bit(BCH_FS_ALLOC_REPLAY_DONE, &c->flags);
 	set_bit(BCH_FS_BTREE_INTERIOR_REPLAY_DONE, &c->flags);
 	set_bit(JOURNAL_RECLAIM_STARTED, &c->journal.flags);
 
diff --git a/libbcachefs/recovery.h b/libbcachefs/recovery.h
index e45c70b3..1504e0bd 100644
--- a/libbcachefs/recovery.h
+++ b/libbcachefs/recovery.h
@@ -31,6 +31,8 @@ struct btree_and_journal_iter {
 	}			last;
 };
 
+int bch2_journal_key_insert_take(struct bch_fs *, enum btree_id,
+				 unsigned, struct bkey_i *);
 int bch2_journal_key_insert(struct bch_fs *, enum btree_id,
 			    unsigned, struct bkey_i *);
 int bch2_journal_key_delete(struct bch_fs *, enum btree_id,
diff --git a/libbcachefs/super.c b/libbcachefs/super.c
index df6bffef..3afa7ebd 100644
--- a/libbcachefs/super.c
+++ b/libbcachefs/super.c
@@ -1478,7 +1478,7 @@ static int bch2_dev_remove_alloc(struct bch_fs *c, struct bch_dev *ca)
 	return bch2_btree_delete_range(c, BTREE_ID_alloc,
 				       POS(ca->dev_idx, 0),
 				       POS(ca->dev_idx + 1, 0),
-				       NULL);
+				       0, NULL);
 }
 
 int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
@@ -1599,18 +1599,24 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
 	int ret;
 
 	ret = bch2_read_super(path, &opts, &sb);
-	if (ret)
+	if (ret) {
+		bch_err(c, "device add error: error reading super: %i", ret);
 		return ret;
+	}
 
 	err = bch2_sb_validate(&sb);
-	if (err)
+	if (err) {
+		bch_err(c, "device add error: error validating super: %s", err);
 		return -EINVAL;
+	}
 
 	dev_mi = bch2_sb_get_members(sb.sb)->members[sb.sb->dev_idx];
 
 	err = bch2_dev_may_add(sb.sb, c);
-	if (err)
+	if (err) {
+		bch_err(c, "device add error: %s", err);
 		return -EINVAL;
+	}
 
 	ca = __bch2_dev_alloc(c, &dev_mi);
 	if (!ca) {
@@ -1624,24 +1630,27 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
 		return ret;
 	}
 
-	err = "journal alloc failed";
 	ret = bch2_dev_journal_alloc(ca);
-	if (ret)
+	if (ret) {
+		bch_err(c, "device add error: journal alloc failed");
 		goto err;
+	}
 
 	down_write(&c->state_lock);
 	mutex_lock(&c->sb_lock);
 
-	err = "insufficient space in new superblock";
 	ret = bch2_sb_from_fs(c, ca);
-	if (ret)
+	if (ret) {
+		bch_err(c, "device add error: new device superblock too small");
 		goto err_unlock;
+	}
 
 	mi = bch2_sb_get_members(ca->disk_sb.sb);
 
 	if (!bch2_sb_resize_members(&ca->disk_sb,
 				le32_to_cpu(mi->field.u64s) +
 				sizeof(dev_mi) / sizeof(u64))) {
+		bch_err(c, "device add error: new device superblock too small");
 		ret = -ENOSPC;
 		goto err_unlock;
 	}
@@ -1654,7 +1663,7 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
 		if (!bch2_dev_exists(c->disk_sb.sb, mi, dev_idx))
 			goto have_slot;
 no_slot:
-	err = "no slots available in superblock";
+	bch_err(c, "device add error: already have maximum number of devices");
 	ret = -ENOSPC;
 	goto err_unlock;
 
@@ -1663,12 +1672,12 @@ have_slot:
 	u64s = (sizeof(struct bch_sb_field_members) +
 		sizeof(struct bch_member) * nr_devices) / sizeof(u64);
 
-	err = "no space in superblock for member info";
-	ret = -ENOSPC;
-
 	mi = bch2_sb_resize_members(&c->disk_sb, u64s);
-	if (!mi)
+	if (!mi) {
+		bch_err(c, "device add error: no room in superblock for member info");
+		ret = -ENOSPC;
 		goto err_unlock;
+	}
 
 	/* success: */
 
@@ -1684,17 +1693,20 @@ have_slot:
 
 	bch2_dev_usage_journal_reserve(c);
 
-	err = "error marking superblock";
 	ret = bch2_trans_mark_dev_sb(c, ca);
-	if (ret)
+	if (ret) {
+		bch_err(c, "device add error: error marking new superblock: %i", ret);
 		goto err_late;
+	}
 
 	ca->new_fs_bucket_idx = 0;
 
 	if (ca->mi.state == BCH_MEMBER_STATE_rw) {
 		ret = __bch2_dev_read_write(c, ca);
-		if (ret)
+		if (ret) {
+			bch_err(c, "device add error: error going RW on new device: %i", ret);
 			goto err_late;
+		}
 	}
 
 	up_write(&c->state_lock);
@@ -1707,11 +1719,9 @@ err:
 	if (ca)
 		bch2_dev_free(ca);
 	bch2_free_super(&sb);
-	bch_err(c, "Unable to add device: %s", err);
 	return ret;
 err_late:
 	up_write(&c->state_lock);
-	bch_err(c, "Error going rw after adding device: %s", err);
 	return -EINVAL;
 }
 
diff --git a/libbcachefs/tests.c b/libbcachefs/tests.c
index 60ccb94e..16d67eb6 100644
--- a/libbcachefs/tests.c
+++ b/libbcachefs/tests.c
@@ -14,14 +14,14 @@ static void delete_test_keys(struct bch_fs *c)
 	int ret;
 
 	ret = bch2_btree_delete_range(c, BTREE_ID_extents,
-				      SPOS(0, 0, U32_MAX),
-				      SPOS(0, U64_MAX, U32_MAX),
+				      POS_MIN, SPOS_MAX,
+				      BTREE_ITER_ALL_SNAPSHOTS,
 				      NULL);
 	BUG_ON(ret);
 
 	ret = bch2_btree_delete_range(c, BTREE_ID_xattrs,
-				      SPOS(0, 0, U32_MAX),
-				      SPOS(0, U64_MAX, U32_MAX),
+				      POS_MIN, SPOS_MAX,
+				      BTREE_ITER_ALL_SNAPSHOTS,
 				      NULL);
 	BUG_ON(ret);
 }
@@ -146,7 +146,7 @@ static int test_iterate(struct bch_fs *c, u64 nr)
 	i = 0;
 
 	for_each_btree_key(&trans, iter, BTREE_ID_xattrs,
-			   POS_MIN, 0, k, ret) {
+			   SPOS(0, 0, U32_MAX), 0, k, ret) {
 		if (k.k->p.inode)
 			break;
 
@@ -202,7 +202,7 @@ static int test_iterate_extents(struct bch_fs *c, u64 nr)
 	i = 0;
 
 	for_each_btree_key(&trans, iter, BTREE_ID_extents,
-			   POS_MIN, 0, k, ret) {
+			   SPOS(0, 0, U32_MAX), 0, k, ret) {
 		BUG_ON(bkey_start_offset(k.k) != i);
 		i = k.k->p.offset;
 	}
@@ -256,8 +256,8 @@ static int test_iterate_slots(struct bch_fs *c, u64 nr)
 
 	i = 0;
 
-	for_each_btree_key(&trans, iter, BTREE_ID_xattrs, POS_MIN,
-			   0, k, ret) {
+	for_each_btree_key(&trans, iter, BTREE_ID_xattrs,
+			   SPOS(0, 0, U32_MAX), 0, k, ret) {
 		if (k.k->p.inode)
 			break;
 
@@ -272,7 +272,8 @@ static int test_iterate_slots(struct bch_fs *c, u64 nr)
 
 	i = 0;
 
-	for_each_btree_key(&trans, iter, BTREE_ID_xattrs, POS_MIN,
+	for_each_btree_key(&trans, iter, BTREE_ID_xattrs,
+			   SPOS(0, 0, U32_MAX),
 			   BTREE_ITER_SLOTS, k, ret) {
 		BUG_ON(k.k->p.offset != i);
 		BUG_ON(bkey_deleted(k.k) != (i & 1));
@@ -321,8 +322,8 @@ static int test_iterate_slots_extents(struct bch_fs *c, u64 nr)
 
 	i = 0;
 
-	for_each_btree_key(&trans, iter, BTREE_ID_extents, POS_MIN,
-			   0, k, ret) {
+	for_each_btree_key(&trans, iter, BTREE_ID_extents,
+			   SPOS(0, 0, U32_MAX), 0, k, ret) {
 		BUG_ON(bkey_start_offset(k.k) != i + 8);
 		BUG_ON(k.k->size != 8);
 		i += 16;
@@ -335,7 +336,8 @@ static int test_iterate_slots_extents(struct bch_fs *c, u64 nr)
 
 	i = 0;
 
-	for_each_btree_key(&trans, iter, BTREE_ID_extents, POS_MIN,
+	for_each_btree_key(&trans, iter, BTREE_ID_extents,
+			   SPOS(0, 0, U32_MAX),
 			   BTREE_ITER_SLOTS, k, ret) {
 		BUG_ON(bkey_deleted(k.k) != !(i % 16));
 
@@ -363,7 +365,8 @@ static int test_peek_end(struct bch_fs *c, u64 nr)
 	struct bkey_s_c k;
 
 	bch2_trans_init(&trans, c, 0, 0);
-	bch2_trans_iter_init(&trans, &iter, BTREE_ID_xattrs, POS_MIN, 0);
+	bch2_trans_iter_init(&trans, &iter, BTREE_ID_xattrs,
+			     SPOS(0, 0, U32_MAX), 0);
 
 	k = bch2_btree_iter_peek(&iter);
 	BUG_ON(k.k);
@@ -383,7 +386,8 @@ static int test_peek_end_extents(struct bch_fs *c, u64 nr)
 	struct bkey_s_c k;
 
 	bch2_trans_init(&trans, c, 0, 0);
-	bch2_trans_iter_init(&trans, &iter, BTREE_ID_extents, POS_MIN, 0);
+	bch2_trans_iter_init(&trans, &iter, BTREE_ID_extents,
+			     SPOS(0, 0, U32_MAX), 0);
 
 	k = bch2_btree_iter_peek(&iter);
 	BUG_ON(k.k);
@@ -406,8 +410,6 @@ static int insert_test_extent(struct bch_fs *c,
 	struct bkey_i_cookie k;
 	int ret;
 
-	//pr_info("inserting %llu-%llu v %llu", start, end, test_version);
-
 	bkey_cookie_init(&k.k_i);
 	k.k_i.k.p.offset = end;
 	k.k_i.k.p.snapshot = U32_MAX;
@@ -747,7 +749,9 @@ static int seq_delete(struct bch_fs *c, u64 nr)
 	int ret;
 
 	ret = bch2_btree_delete_range(c, BTREE_ID_xattrs,
-				      SPOS(0, 0, U32_MAX), POS_MAX, NULL);
+				      POS_MIN, SPOS_MAX,
+				      BTREE_ITER_ALL_SNAPSHOTS,
+				      NULL);
 	if (ret)
 		bch_err(c, "error in seq_delete: %i", ret);
 	return ret;
diff --git a/libbcachefs/util.c b/libbcachefs/util.c
index 52de7c49..0bbea332 100644
--- a/libbcachefs/util.c
+++ b/libbcachefs/util.c
@@ -114,7 +114,7 @@ void bch2_hprint(struct printbuf *buf, s64 v)
 	 * 103 is magic: t is in the range [-1023, 1023] and we want
 	 * to turn it into [-9, 9]
 	 */
-	if (u && v < 100 && v > -100)
+	if (u && t && v < 100 && v > -100)
 		pr_buf(buf, ".%i", t / 103);
 	if (u)
 		pr_buf(buf, "%c", si_units[u]);