public inbox for linux-bcachefs@vger.kernel.org
 help / color / mirror / Atom feed
From: Kent Overstreet <kent.overstreet@linux.dev>
To: linux-bcachefs@vger.kernel.org
Cc: Kent Overstreet <kent.overstreet@linux.dev>
Subject: [PATCH 3/3] bcachefs: Plumb error messages through data drop paths
Date: Fri, 29 Aug 2025 02:54:07 -0400	[thread overview]
Message-ID: <20250829065409.3248746-4-kent.overstreet@linux.dev> (raw)
In-Reply-To: <20250829065409.3248746-1-kent.overstreet@linux.dev>

The ioctl v2 rework added the ability to return error messages to
userspace directly - instead of via the dmesg log.

This lets us return more informative error messages: we can now return a
message with the key we failed on.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
---
 fs/bcachefs/ec.c      | 23 ++++++++++++------
 fs/bcachefs/ec.h      |  5 ++--
 fs/bcachefs/migrate.c | 55 ++++++++++++++++++++++++-------------------
 fs/bcachefs/migrate.h |  4 ++--
 fs/bcachefs/super.c   |  6 ++---
 5 files changed, 55 insertions(+), 38 deletions(-)

diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c
index c2840cb674b2..271e252152da 100644
--- a/fs/bcachefs/ec.c
+++ b/fs/bcachefs/ec.c
@@ -2040,7 +2040,7 @@ int bch2_invalidate_stripe_to_dev(struct btree_trans *trans,
 				  struct btree_iter *iter,
 				  struct bkey_s_c k,
 				  unsigned dev_idx,
-				  unsigned flags)
+				  unsigned flags, struct printbuf *err)
 {
 	if (k.k->type != KEY_TYPE_stripe)
 		return 0;
@@ -2081,13 +2081,21 @@ int bch2_invalidate_stripe_to_dev(struct btree_trans *trans,
 			nr_good += ca && ca->mi.state != BCH_MEMBER_STATE_failed;
 		}
 
-	if (nr_good < s->v.nr_blocks && !(flags & BCH_FORCE_IF_DATA_DEGRADED))
+	if (nr_good < s->v.nr_blocks && !(flags & BCH_FORCE_IF_DATA_DEGRADED)) {
+		prt_str(err, "cannot drop device without degrading\n  ");
+		bch2_bkey_val_to_text(err, c, k);
+		prt_newline(err);
 		return bch_err_throw(c, remove_would_lose_data);
+	}
 
 	unsigned nr_data = s->v.nr_blocks - s->v.nr_redundant;
 
-	if (nr_good < nr_data && !(flags & BCH_FORCE_IF_DATA_LOST))
+	if (nr_good < nr_data && !(flags & BCH_FORCE_IF_DATA_LOST)) {
+		prt_str(err, "cannot drop device without losing data\n  ");
+		bch2_bkey_val_to_text(err, c, k);
+		prt_newline(err);
 		return bch_err_throw(c, remove_would_lose_data);
+	}
 
 	sectors = -sectors;
 
@@ -2099,7 +2107,7 @@ int bch2_invalidate_stripe_to_dev(struct btree_trans *trans,
 }
 
 static int bch2_invalidate_stripe_to_dev_from_alloc(struct btree_trans *trans, struct bkey_s_c k_a,
-						    unsigned flags)
+						    unsigned flags, struct printbuf *err)
 {
 	struct bch_alloc_v4 a_convert;
 	const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k_a, &a_convert);
@@ -2119,17 +2127,18 @@ static int bch2_invalidate_stripe_to_dev_from_alloc(struct btree_trans *trans, s
 	if (ret)
 		return ret;
 
-	return bch2_invalidate_stripe_to_dev(trans, &iter, s.s_c, k_a.k->p.inode, flags);
+	return bch2_invalidate_stripe_to_dev(trans, &iter, s.s_c, k_a.k->p.inode, flags, err);
 }
 
-int bch2_dev_remove_stripes(struct bch_fs *c, unsigned dev_idx, unsigned flags)
+int bch2_dev_remove_stripes(struct bch_fs *c, unsigned dev_idx,
+			    unsigned flags, struct printbuf *err)
 {
 	CLASS(btree_trans, trans)(c);
 	int ret = for_each_btree_key_max_commit(trans, iter,
 				  BTREE_ID_alloc, POS(dev_idx, 0), POS(dev_idx, U64_MAX),
 				  BTREE_ITER_intent, k,
 				  NULL, NULL, 0, ({
-			bch2_invalidate_stripe_to_dev_from_alloc(trans, k, flags);
+		bch2_invalidate_stripe_to_dev_from_alloc(trans, k, flags, err);
 	}));
 	bch_err_fn(c, ret);
 	return ret;
diff --git a/fs/bcachefs/ec.h b/fs/bcachefs/ec.h
index e807e7027d7a..cc778da99030 100644
--- a/fs/bcachefs/ec.h
+++ b/fs/bcachefs/ec.h
@@ -289,8 +289,9 @@ static inline void ec_stripe_new_put(struct bch_fs *c, struct ec_stripe_new *s,
 }
 
 int bch2_invalidate_stripe_to_dev(struct btree_trans *, struct btree_iter *,
-				  struct bkey_s_c, unsigned, unsigned);
-int bch2_dev_remove_stripes(struct bch_fs *, unsigned, unsigned);
+				  struct bkey_s_c, unsigned,
+				  unsigned, struct printbuf *);
+int bch2_dev_remove_stripes(struct bch_fs *, unsigned, unsigned, struct printbuf *);
 
 void bch2_ec_stop_dev(struct bch_fs *, struct bch_dev *);
 void bch2_fs_ec_stop(struct bch_fs *);
diff --git a/fs/bcachefs/migrate.c b/fs/bcachefs/migrate.c
index 892990b4a6a6..5b4c3f4b1c25 100644
--- a/fs/bcachefs/migrate.c
+++ b/fs/bcachefs/migrate.c
@@ -22,8 +22,8 @@
 #include "replicas.h"
 #include "super-io.h"
 
-static int drop_dev_ptrs(struct bch_fs *c, struct bkey_s k,
-			 unsigned dev_idx, unsigned flags, bool metadata)
+static int drop_dev_ptrs(struct bch_fs *c, struct bkey_s k, unsigned dev_idx,
+			 unsigned flags, struct printbuf *err, bool metadata)
 {
 	unsigned replicas = metadata ? c->opts.metadata_replicas : c->opts.data_replicas;
 	unsigned lost = metadata ? BCH_FORCE_IF_METADATA_LOST : BCH_FORCE_IF_DATA_LOST;
@@ -34,14 +34,19 @@ static int drop_dev_ptrs(struct bch_fs *c, struct bkey_s k,
 
 	nr_good = bch2_bkey_durability(c, k.s_c);
 	if ((!nr_good && !(flags & lost)) ||
-	    (nr_good < replicas && !(flags & degraded)))
+	    (nr_good < replicas && !(flags & degraded))) {
+		prt_str(err, "cannot drop device without degrading/losing data\n  ");
+		bch2_bkey_val_to_text(err, c, k.s_c);
+		prt_newline(err);
 		return bch_err_throw(c, remove_would_lose_data);
+	}
 
 	return 0;
 }
 
 static int drop_btree_ptrs(struct btree_trans *trans, struct btree_iter *iter,
-			   struct btree *b, unsigned dev_idx, unsigned flags)
+			   struct btree *b, unsigned dev_idx,
+			   unsigned flags, struct printbuf *err)
 {
 	struct bch_fs *c = trans->c;
 	struct bkey_buf k;
@@ -49,10 +54,9 @@ static int drop_btree_ptrs(struct btree_trans *trans, struct btree_iter *iter,
 	bch2_bkey_buf_init(&k);
 	bch2_bkey_buf_copy(&k, c, &b->key);
 
-	int ret = drop_dev_ptrs(c, bkey_i_to_s(k.k), dev_idx, flags, true) ?:
+	int ret = drop_dev_ptrs(c, bkey_i_to_s(k.k), dev_idx, flags, err, true) ?:
 		bch2_btree_node_update_key(trans, iter, b, k.k, 0, false);
 
-	bch_err_fn(c, ret);
 	bch2_bkey_buf_exit(&k, c);
 	return ret;
 }
@@ -61,7 +65,7 @@ static int bch2_dev_usrdata_drop_key(struct btree_trans *trans,
 				     struct btree_iter *iter,
 				     struct bkey_s_c k,
 				     unsigned dev_idx,
-				     unsigned flags)
+				     unsigned flags, struct printbuf *err)
 {
 	struct bch_fs *c = trans->c;
 	struct bkey_i *n;
@@ -75,7 +79,7 @@ static int bch2_dev_usrdata_drop_key(struct btree_trans *trans,
 	if (ret)
 		return ret;
 
-	ret = drop_dev_ptrs(c, bkey_i_to_s(n), dev_idx, flags, false);
+	ret = drop_dev_ptrs(c, bkey_i_to_s(n), dev_idx, flags, err, false);
 	if (ret)
 		return ret;
 
@@ -101,7 +105,7 @@ static int bch2_dev_btree_drop_key(struct btree_trans *trans,
 				   struct bkey_s_c_backpointer bp,
 				   unsigned dev_idx,
 				   struct bkey_buf *last_flushed,
-				   unsigned flags)
+				   unsigned flags, struct printbuf *err)
 {
 	struct btree_iter iter;
 	struct btree *b = bch2_backpointer_get_node(trans, bp, &iter, last_flushed);
@@ -109,7 +113,7 @@ static int bch2_dev_btree_drop_key(struct btree_trans *trans,
 	if (ret)
 		return ret == -BCH_ERR_backpointer_to_overwritten_btree_node ? 0 : ret;
 
-	ret = drop_btree_ptrs(trans, &iter, b, dev_idx, flags);
+	ret = drop_btree_ptrs(trans, &iter, b, dev_idx, flags, err);
 
 	bch2_trans_iter_exit(&iter);
 	return ret;
@@ -117,7 +121,8 @@ static int bch2_dev_btree_drop_key(struct btree_trans *trans,
 
 static int bch2_dev_usrdata_drop(struct bch_fs *c,
 				 struct progress_indicator_state *progress,
-				 unsigned dev_idx, unsigned flags)
+				 unsigned dev_idx,
+				 unsigned flags, struct printbuf *err)
 {
 	CLASS(btree_trans, trans)(c);
 
@@ -133,7 +138,7 @@ static int bch2_dev_usrdata_drop(struct bch_fs *c,
 				BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
 				NULL, NULL, BCH_TRANS_COMMIT_no_enospc, ({
 			bch2_progress_update_iter(trans, progress, &iter, "dropping user data");
-			bch2_dev_usrdata_drop_key(trans, &iter, k, dev_idx, flags);
+			bch2_dev_usrdata_drop_key(trans, &iter, k, dev_idx, flags, err);
 		}));
 		if (ret)
 			return ret;
@@ -144,7 +149,8 @@ static int bch2_dev_usrdata_drop(struct bch_fs *c,
 
 static int bch2_dev_metadata_drop(struct bch_fs *c,
 				  struct progress_indicator_state *progress,
-				  unsigned dev_idx, unsigned flags)
+				  unsigned dev_idx,
+				  unsigned flags, struct printbuf *err)
 {
 	struct btree_iter iter;
 	struct closure cl;
@@ -174,7 +180,7 @@ static int bch2_dev_metadata_drop(struct bch_fs *c,
 			if (!bch2_bkey_has_device_c(bkey_i_to_s_c(&b->key), dev_idx))
 				goto next;
 
-			ret = drop_btree_ptrs(trans, &iter, b, dev_idx, flags);
+			ret = drop_btree_ptrs(trans, &iter, b, dev_idx, flags, err);
 			if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) {
 				ret = 0;
 				continue;
@@ -206,7 +212,7 @@ static int bch2_dev_metadata_drop(struct bch_fs *c,
 
 static int data_drop_bp(struct btree_trans *trans, unsigned dev_idx,
 			struct bkey_s_c_backpointer bp, struct bkey_buf *last_flushed,
-			unsigned flags)
+			unsigned flags, struct printbuf *err)
 {
 	struct btree_iter iter;
 	struct bkey_s_c k = bch2_backpointer_get_key(trans, bp, &iter, BTREE_ITER_intent,
@@ -226,17 +232,18 @@ static int data_drop_bp(struct btree_trans *trans, unsigned dev_idx,
 	 */
 
 	if (bkey_is_btree_ptr(k.k))
-		ret = bch2_dev_btree_drop_key(trans, bp, dev_idx, last_flushed, flags);
+		ret = bch2_dev_btree_drop_key(trans, bp, dev_idx, last_flushed, flags, err);
 	else if (k.k->type == KEY_TYPE_stripe)
-		ret = bch2_invalidate_stripe_to_dev(trans, &iter, k, dev_idx, flags);
+		ret = bch2_invalidate_stripe_to_dev(trans, &iter, k, dev_idx, flags, err);
 	else
-		ret = bch2_dev_usrdata_drop_key(trans, &iter, k, dev_idx, flags);
+		ret = bch2_dev_usrdata_drop_key(trans, &iter, k, dev_idx, flags, err);
 out:
 	bch2_trans_iter_exit(&iter);
 	return ret;
 }
 
-int bch2_dev_data_drop_by_backpointers(struct bch_fs *c, unsigned dev_idx, unsigned flags)
+int bch2_dev_data_drop_by_backpointers(struct bch_fs *c, unsigned dev_idx, unsigned flags,
+				       struct printbuf *err)
 {
 	CLASS(btree_trans, trans)(c);
 
@@ -253,22 +260,22 @@ int bch2_dev_data_drop_by_backpointers(struct bch_fs *c, unsigned dev_idx, unsig
 				continue;
 
 			data_drop_bp(trans, dev_idx, bkey_s_c_to_backpointer(k),
-				     &last_flushed, flags);
+				     &last_flushed, flags, err);
 
 	}));
 
 	bch2_bkey_buf_exit(&last_flushed, trans->c);
-	bch_err_fn(c, ret);
 	return ret;
 }
 
-int bch2_dev_data_drop(struct bch_fs *c, unsigned dev_idx, unsigned flags)
+int bch2_dev_data_drop(struct bch_fs *c, unsigned dev_idx,
+		       unsigned flags, struct printbuf *err)
 {
 	struct progress_indicator_state progress;
 	bch2_progress_init(&progress, c,
 			   BIT_ULL(BTREE_ID_extents)|
 			   BIT_ULL(BTREE_ID_reflink));
 
-	return bch2_dev_usrdata_drop(c, &progress, dev_idx, flags) ?:
-		bch2_dev_metadata_drop(c, &progress, dev_idx, flags);
+	return bch2_dev_usrdata_drop(c, &progress, dev_idx, flags, err) ?:
+		bch2_dev_metadata_drop(c, &progress, dev_idx, flags, err);
 }
diff --git a/fs/bcachefs/migrate.h b/fs/bcachefs/migrate.h
index 30018140711b..ff4567fb5a83 100644
--- a/fs/bcachefs/migrate.h
+++ b/fs/bcachefs/migrate.h
@@ -2,7 +2,7 @@
 #ifndef _BCACHEFS_MIGRATE_H
 #define _BCACHEFS_MIGRATE_H
 
-int bch2_dev_data_drop_by_backpointers(struct bch_fs *, unsigned, unsigned);
-int bch2_dev_data_drop(struct bch_fs *, unsigned, unsigned);
+int bch2_dev_data_drop_by_backpointers(struct bch_fs *, unsigned, unsigned, struct printbuf *);
+int bch2_dev_data_drop(struct bch_fs *, unsigned, unsigned, struct printbuf *);
 
 #endif /* _BCACHEFS_MIGRATE_H */
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index 793c16fa8b09..d640ae188722 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -1992,9 +1992,9 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags,
 	__bch2_dev_read_only(c, ca);
 
 	ret = fast_device_removal
-		? bch2_dev_data_drop_by_backpointers(c, ca->dev_idx, flags)
-		: (bch2_dev_data_drop(c, ca->dev_idx, flags) ?:
-		   bch2_dev_remove_stripes(c, ca->dev_idx, flags));
+		? bch2_dev_data_drop_by_backpointers(c, ca->dev_idx, flags, err)
+		: (bch2_dev_data_drop(c, ca->dev_idx, flags, err) ?:
+		   bch2_dev_remove_stripes(c, ca->dev_idx, flags, err));
 	if (ret)
 		goto err;
 
-- 
2.50.1


      parent reply	other threads:[~2025-08-29  6:54 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-29  6:54 [PATCH 0/3] better error messages for ioctls Kent Overstreet
2025-08-29  6:54 ` [PATCH 1/3] bcachefs: Add v2 ioctls that return error strings Kent Overstreet
2025-08-29  6:54 ` [PATCH 2/3] bcachefs: v2 versions of subvolume ioctls Kent Overstreet
2025-08-29  6:54 ` Kent Overstreet [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250829065409.3248746-4-kent.overstreet@linux.dev \
    --to=kent.overstreet@linux.dev \
    --cc=linux-bcachefs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox