From: Chengyu Zhu <[email protected]>

Enable ublk user recovery so erofsmount can reattach a daemon to a
recoverable ublk device after the previous userspace server exits.

Store the recovery source information in a ublk-specific runtime record,
create ublk devices with user recovery enabled, and add the mount-side
reattach path for existing recoverable ublk devices.

Also encode ublk IO uring command opcodes explicitly for non-legacy kernels,
and initialize the control ring before querying whether a ublk device is
recoverable.

Signed-off-by: Chengyu Zhu <[email protected]>
---
 lib/backends/ublk.c |  23 ++++++---
 mount/main.c        | 115 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 122 insertions(+), 16 deletions(-)

diff --git a/lib/backends/ublk.c b/lib/backends/ublk.c
index a8ecad9..49191de 100644
--- a/lib/backends/ublk.c
+++ b/lib/backends/ublk.c
@@ -258,7 +258,16 @@ 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)
+       if (erofs_ublk_use_legacy_cmds)
+               return op;
+
+       /* 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;
 }
@@ -528,11 +537,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 +1263,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 5955e2d..754e585 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_write_recovery_path(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,55 @@ static int erofsmount_ublk_handler(void *ctx, struct 
erofs_ublk_request *rq)
        return 0;
 }
 
+static int ublk_dev_id_from_path(const char *path);
+
+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,6 +1528,10 @@ static int erofsmount_reattach(const char *target)
        if (err < 0)
                return -errno;
 
+       nbdnum = ublk_dev_id_from_path(target);
+       if (S_ISBLK(st.st_mode) && nbdnum >= 0)
+               return erofsmount_ublk_reattach(nbdnum);
+
        if (!S_ISBLK(st.st_mode) || major(st.st_rdev) != EROFS_NBD_MAJOR)
                return -ENOTBLK;
 
@@ -2108,13 +2190,15 @@ static int erofsmount_ublk(struct erofsmount_source 
*source,
                if (err)
                        exit(EXIT_FAILURE);
 
+               char *recp = NULL;
+
                info = (struct erofs_ublk_dev_info) {
                        .nr_hw_queues = EROFS_UBLK_DEF_NR_HW_QUEUES,
                        .queue_depth = EROFS_UBLK_DEF_QUEUE_DEPTH,
                        .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,
@@ -2128,6 +2212,13 @@ static int erofsmount_ublk(struct erofsmount_source 
*source,
                        exit(EXIT_FAILURE);
                }
 
+               if (asprintf(&recp, EROFSMOUNT_UBLK_REC_FMT, dev_id) > 0) {
+                       err = erofsmount_write_recovery_path(source, recp);
+                       if (err)
+                               erofs_warn("ublk dev %d recovery info 
unwritable: %s",
+                                          dev_id, strerror(-err));
+               }
+
                if (write(pipefd[1], &dev_id,
                          sizeof(dev_id)) != sizeof(dev_id))
                        exit(EXIT_FAILURE);
@@ -2137,6 +2228,10 @@ static int erofsmount_ublk(struct erofsmount_source 
*source,
                        erofs_err("erofs_ublk_start: %s", strerror(-err));
                erofs_ublk_destroy(dev_id);
                erofs_io_close(ctx.vd);
+               if (recp) {
+                       (void)unlink(recp);
+                       free(recp);
+               }
                exit(EXIT_SUCCESS);
        }
 
-- 
2.47.1


Reply via email to