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
next prev 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