Linux-EROFS Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Gao Xiang <hsiangkao@linux.alibaba.com>
To: linux-erofs@lists.ozlabs.org
Cc: Chengyu Zhu <hudsonzhu@tencent.com>,
	Gao Xiang <hsiangkao@linux.alibaba.com>
Subject: [PATCH v3 1/2] erofs-utils: mount: support ublk recovery
Date: Sun, 28 Jun 2026 22:39:09 +0800	[thread overview]
Message-ID: <20260628143910.1062931-1-hsiangkao@linux.alibaba.com> (raw)
In-Reply-To: <20260624133732.18218-1-hudson@cyzhu.com>

From: Chengyu Zhu <hudsonzhu@tencent.com>

Allow users to reattach to an existing ublk device after deamon crashes.

Signed-off-by: Chengyu Zhu <hudsonzhu@tencent.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
---
 lib/backends/ublk.c |  33 +++++++---
 mount/main.c        | 142 +++++++++++++++++++++++++++++++++++++-------
 2 files changed, 143 insertions(+), 32 deletions(-)

diff --git a/lib/backends/ublk.c b/lib/backends/ublk.c
index a8ecad9fc031..090c6d5bd1e8 100644
--- a/lib/backends/ublk.c
+++ b/lib/backends/ublk.c
@@ -258,8 +258,15 @@ static unsigned int erofsublk_formalize_cmd_op(unsigned int op)
 	DBG_BUGON(_IOC_DIR(op) != 0);
 	DBG_BUGON(_IOC_SIZE(op) != 0);
 
-	if (op < ARRAY_SIZE(ctrl_cmd_op) && !erofs_ublk_use_legacy_cmds)
-		return ctrl_cmd_op[op];
+	if (!erofs_ublk_use_legacy_cmds) {
+		/* IO opcodes live above the ctrl table and need explicit encoding */
+		if (op == UBLK_IO_FETCH_REQ)
+			return UBLK_U_IO_FETCH_REQ;
+		if (op == UBLK_IO_COMMIT_AND_FETCH_REQ)
+			return UBLK_U_IO_COMMIT_AND_FETCH_REQ;
+		if (op < ARRAY_SIZE(ctrl_cmd_op))
+			return ctrl_cmd_op[op];
+	}
 	return op;
 }
 
@@ -492,8 +499,14 @@ static int ublk_get_dev_info(struct erofs_ublk_dev *dev, int dev_id)
 	int ret;
 
 	ret = ublk_dev_ctrl_cmd(dev, UBLK_CMD_GET_DEV_INFO, &cmd);
-	if (ret < 0)
-		erofs_err("GET_DEV_INFO failed: %s", strerror(-ret));
+	if ((ret == -ENODEV || ret == -EOPNOTSUPP) &&
+	    !erofs_ublk_use_legacy_cmds) {
+		erofs_ublk_use_legacy_cmds = true;
+		ret = ublk_dev_ctrl_cmd(dev, UBLK_CMD_GET_DEV_INFO, &cmd);
+		if (ret < 0)
+			erofs_err("GET_DEV_INFO failed for device %d: %s",
+				  dev_id, erofs_strerror(ret));
+	}
 	return ret;
 }
 
@@ -528,11 +541,6 @@ static inline unsigned int user_data_to_tag(u64 user_data)
 	return user_data & 0xffff;
 }
 
-static inline unsigned int user_data_to_op(u64 user_data)
-{
-	return (user_data >> 16) & 0xff;
-}
-
 static inline struct io_uring_sqe *erofsublk_alloc_sqe(struct io_uring *r)
 {
 	unsigned int left = io_uring_sq_space_left(r);
@@ -1259,7 +1267,14 @@ int erofs_ublk_is_recoverable(int dev_id)
 	memset(&dev, 0, sizeof(dev));
 	dev.ctrl_fd = ctrl_fd;
 
+	ret = ublk_ctrl_ring_init(&dev.ctrl_ring);
+	if (ret < 0) {
+		close(ctrl_fd);
+		return 0;
+	}
+
 	ret = ublk_get_dev_info(&dev, dev_id);
+	io_uring_queue_exit(&dev.ctrl_ring);
 	close(ctrl_fd);
 
 	if (ret < 0)
diff --git a/mount/main.c b/mount/main.c
index 5955e2d209dc..dbf5cdddd265 100644
--- a/mount/main.c
+++ b/mount/main.c
@@ -51,6 +51,8 @@ struct loop_info {
 #define EROFSMOUNT_RUNDIR		"/var/run/erofsmount"
 #define EROFSMOUNT_NBD_RUNDIR		EROFSMOUNT_RUNDIR "/nbd"
 #define EROFSMOUNT_NBD_REC_FMT		EROFSMOUNT_NBD_RUNDIR "/mountnbd_nbd%d"
+#define EROFSMOUNT_UBLK_RUNDIR		EROFSMOUNT_RUNDIR "/ublk"
+#define EROFSMOUNT_UBLK_REC_FMT		EROFSMOUNT_UBLK_RUNDIR "/mountublk_ublk%d"
 
 #define EROFSMOUNT_FANOTIFY_STATE_DIR	EROFSMOUNT_RUNDIR "/fanotify"
 
@@ -1004,6 +1006,17 @@ static int erofsmount_write_recovery_s3(FILE *f, struct erofsmount_source *sourc
 }
 #endif
 
+static int erofsmount_write_recovery_fp(FILE *f, struct erofsmount_source *source)
+{
+	if (source->type == EROFSMOUNT_SOURCE_OCI)
+		return erofsmount_write_recovery_oci(f, source);
+	if (source->type == EROFSMOUNT_SOURCE_S3_OBJECT)
+		return erofsmount_write_recovery_s3(f, source);
+	if (source->type == EROFSMOUNT_SOURCE_LOCAL)
+		return erofsmount_write_recovery_local(f, source);
+	return -EOPNOTSUPP;
+}
+
 static char *erofsmount_write_recovery_info(struct erofsmount_source *source)
 {
 	char recp[] = EROFSMOUNT_NBD_RUNDIR "/mountnbd_XXXXXX";
@@ -1028,21 +1041,37 @@ static char *erofsmount_write_recovery_info(struct erofsmount_source *source)
 		return ERR_PTR(-errno);
 	}
 
-	if (source->type == EROFSMOUNT_SOURCE_OCI)
-		err = erofsmount_write_recovery_oci(f, source);
-	else if (source->type == EROFSMOUNT_SOURCE_S3_OBJECT)
-		err = erofsmount_write_recovery_s3(f, source);
-	else if (source->type == EROFSMOUNT_SOURCE_LOCAL)
-		err = erofsmount_write_recovery_local(f, source);
-	else
-		err = -EOPNOTSUPP;
-
+	err = erofsmount_write_recovery_fp(f, source);
 	fclose(f);
 	if (err)
 		return ERR_PTR(err);
 	return strdup(recp) ?: ERR_PTR(-ENOMEM);
 }
 
+static int erofsmount_ublk_write_recovery(struct erofsmount_source *source,
+					  const char *path)
+{
+	FILE *f;
+	int err;
+
+	f = fopen(path, "w");
+	if (!f && errno == ENOENT) {
+		if (mkdir(EROFSMOUNT_RUNDIR, 0700) < 0 && errno != EEXIST)
+			return -errno;
+		if (mkdir(EROFSMOUNT_UBLK_RUNDIR, 0700) < 0 && errno != EEXIST)
+			return -errno;
+		f = fopen(path, "w");
+	}
+	if (!f)
+		return -errno;
+
+	err = erofsmount_write_recovery_fp(f, source);
+	fclose(f);
+	if (err)
+		(void)unlink(path);
+	return err;
+}
+
 #ifdef OCIEROFS_ENABLED
 /* Parse input string in format: "image_ref platform layer [b64cred]" */
 static int erofsmount_parse_recovery_ocilayer(struct ocierofs_config *oci_cfg,
@@ -1438,6 +1467,62 @@ static int erofsmount_ublk_handler(void *ctx, struct erofs_ublk_request *rq)
 	return 0;
 }
 
+static int ublk_dev_id_from_path(const char *path)
+{
+	int dev_id;
+
+	if (sscanf(path, "/dev/ublkb%d", &dev_id) == 1)
+		return dev_id;
+	return -1;
+}
+
+static int erofsmount_ublk_reattach(int dev_id)
+{
+	struct erofsmount_nbd_ctx ctx = { .vd = &ctx._vd };
+	char *recp;
+	FILE *f;
+	int err;
+
+	if (!erofs_ublk_is_recoverable(dev_id))
+		return -EINVAL;
+
+	if (asprintf(&recp, EROFSMOUNT_UBLK_REC_FMT, dev_id) <= 0)
+		return -ENOMEM;
+
+	f = fopen(recp, "r");
+	if (!f) {
+		err = -errno;
+		free(recp);
+		return err;
+	}
+
+	err = erofsmount_open_recovery_source(&ctx, f);
+	if (err) {
+		free(recp);
+		return err;
+	}
+
+	if (fork() == 0) {
+		err = erofs_ublk_recover_dev(dev_id, erofsmount_ublk_handler,
+					     ctx.vd);
+		if (err) {
+			erofs_err("ublk recover dev %d failed: %s",
+				  dev_id, strerror(-err));
+			erofs_io_close(ctx.vd);
+			exit(EXIT_FAILURE);
+		}
+		err = erofs_ublk_start(dev_id, -1);
+		erofs_ublk_destroy(dev_id);
+		erofs_io_close(ctx.vd);
+		(void)unlink(recp);
+		exit(err ? EXIT_FAILURE : EXIT_SUCCESS);
+	}
+
+	erofs_io_close(ctx.vd);
+	free(recp);
+	return 0;
+}
+
 static int erofsmount_reattach(const char *target)
 {
 	struct erofsmount_nbd_ctx ctx = { .vd = &ctx._vd };
@@ -1450,7 +1535,14 @@ static int erofsmount_reattach(const char *target)
 	if (err < 0)
 		return -errno;
 
-	if (!S_ISBLK(st.st_mode) || major(st.st_rdev) != EROFS_NBD_MAJOR)
+	if (!S_ISBLK(st.st_mode))
+		return -ENOTBLK;
+
+	nbdnum = ublk_dev_id_from_path(target);
+	if (nbdnum >= 0)
+		return erofsmount_ublk_reattach(nbdnum);
+
+	if (major(st.st_rdev) != EROFS_NBD_MAJOR)
 		return -ENOTBLK;
 
 	nbdnum = erofs_nbd_get_index_from_minor(minor(st.st_rdev));
@@ -2101,6 +2193,7 @@ static int erofsmount_ublk(struct erofsmount_source *source,
 	if (pid == 0) {
 		struct erofsmount_nbd_ctx ctx = { .vd = &ctx._vd };
 		struct erofs_ublk_dev_info info;
+		char *recp = NULL;
 		struct stat st;
 
 		close(pipefd[0]);
@@ -2114,7 +2207,7 @@ static int erofsmount_ublk(struct erofsmount_source *source,
 			.max_io_buf_bytes = EROFS_UBLK_DEF_MAX_IO_BUF_BYTES,
 			.dev_id = -1,
 			.blkbits = EROFS_UBLK_DEF_BLK_BITS,
-			.flags = 0,
+			.flags = EROFS_UBLK_F_USER_RECOVERY,
 			.dev_size = source->type == EROFSMOUNT_SOURCE_LOCAL &&
 				erofs_io_fstat(ctx.vd, &st) == 0 ?
 					st.st_size : INT64_MAX,
@@ -2123,20 +2216,32 @@ static int erofsmount_ublk(struct erofsmount_source *source,
 		dev_id = erofs_ublk_create_dev(&info, erofsmount_ublk_handler,
 					       ctx.vd);
 		if (dev_id < 0) {
-			erofs_err("erofs_ublk_create_dev failed: %s",
-				  strerror(-dev_id));
+			erofs_err("failed to erofs_ublk_create_dev: %s",
+				  erofs_strerror(dev_id));
 			exit(EXIT_FAILURE);
 		}
 
+		if (asprintf(&recp, EROFSMOUNT_UBLK_REC_FMT, dev_id) > 0) {
+			err = erofsmount_ublk_write_recovery(source, recp);
+			if (err)
+				erofs_warn("failed to write recovery info for ublk %d: %s",
+					   dev_id, erofs_strerror(err));
+		}
+
 		if (write(pipefd[1], &dev_id,
 			  sizeof(dev_id)) != sizeof(dev_id))
 			exit(EXIT_FAILURE);
 
 		err = erofs_ublk_start(dev_id, pipefd[1]);
 		if (err)
-			erofs_err("erofs_ublk_start: %s", strerror(-err));
+			erofs_err("failed to erofs_ublk_start: %s",
+				  erofs_strerror(err));
 		erofs_ublk_destroy(dev_id);
 		erofs_io_close(ctx.vd);
+		if (recp) {
+			(void)unlink(recp);
+			free(recp);
+		}
 		exit(EXIT_SUCCESS);
 	}
 
@@ -2167,15 +2272,6 @@ static int erofsmount_ublk(struct erofsmount_source *source,
 	return 0;
 }
 
-static int ublk_dev_id_from_path(const char *path)
-{
-	int dev_id;
-
-	if (sscanf(path, "/dev/ublkb%d", &dev_id) == 1)
-		return dev_id;
-	return -1;
-}
-
 int erofsmount_umount(char *target)
 {
 	char *device = NULL, *mountpoint = NULL;
-- 
2.43.5



  parent reply	other threads:[~2026-06-28 14:39 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-19  4:19 [PATCH 0/2] erofs-utils: support ublk recovery Chengyu
2026-06-19  4:19 ` [PATCH 1/2] ublk: " Chengyu
2026-06-22  7:28   ` Gao Xiang
2026-06-19  4:19 ` [PATCH 2/2] mount: rename erofsmount_nbd_ctx to erofsmount_ctx Chengyu
2026-06-24 13:37 ` [PATCH 0/2] erofs-utils: support ublk recovery Chengyu
2026-06-24 13:37   ` [PATCH 1/2] ublk: " Chengyu
2026-06-24 13:37   ` [PATCH 2/2] mount: rename erofsmount_nbd_ctx to erofsmount_ctx Chengyu
2026-06-28 14:39   ` Gao Xiang [this message]
2026-06-28 14:39     ` [PATCH v3 2/2] erofs-utils: " Gao Xiang

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=20260628143910.1062931-1-hsiangkao@linux.alibaba.com \
    --to=hsiangkao@linux.alibaba.com \
    --cc=hudsonzhu@tencent.com \
    --cc=linux-erofs@lists.ozlabs.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