On 4/27/23 03:32, Ziyang Zhang wrote: > We are going to test ublk's user recovery feature so add support in > miniublk. > > Signed-off-by: Ziyang Zhang <ZiyangZhang@xxxxxxxxxxxxxxxxx> > --- > src/miniublk.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 198 insertions(+), 9 deletions(-) > > diff --git a/src/miniublk.c b/src/miniublk.c > index fe10291..558bb7b 100644 > --- a/src/miniublk.c > +++ b/src/miniublk.c > @@ -74,6 +74,7 @@ struct ublk_tgt_ops { > int (*queue_io)(struct ublk_queue *, int tag); > void (*tgt_io_done)(struct ublk_queue *, > int tag, const struct io_uring_cqe *); > + int (*recover_tgt)(struct ublk_dev *); > }; > > struct ublk_tgt { > @@ -372,6 +373,29 @@ static int ublk_ctrl_get_params(struct ublk_dev *dev, > return __ublk_ctrl_cmd(dev, &data); > } > > +static int ublk_ctrl_start_user_recover(struct ublk_dev *dev) > +{ > + struct ublk_ctrl_cmd_data data = { > + .cmd_op = UBLK_CMD_START_USER_RECOVERY, > + .flags = 0, > + }; > + > + return __ublk_ctrl_cmd(dev, &data); > +} > + > +static int ublk_ctrl_end_user_recover(struct ublk_dev *dev, > + int daemon_pid) > +{ > + struct ublk_ctrl_cmd_data data = { > + .cmd_op = UBLK_CMD_END_USER_RECOVERY, > + .flags = CTRL_CMD_HAS_DATA, > + }; > + > + dev->dev_info.ublksrv_pid = data.data[0] = daemon_pid; > + > + return __ublk_ctrl_cmd(dev, &data); > +} > + > static const char *ublk_dev_state_desc(struct ublk_dev *dev) > { > switch (dev->dev_info.state) { > @@ -379,6 +403,8 @@ static const char *ublk_dev_state_desc(struct ublk_dev *dev) > return "DEAD"; > case UBLK_S_DEV_LIVE: > return "LIVE"; > + case UBLK_S_DEV_QUIESCED: > + return "QUIESCED"; > default: > return "UNKNOWN"; > }; > @@ -550,9 +576,12 @@ static int ublk_dev_prep(struct ublk_dev *dev) > goto fail; > } > > - if (dev->tgt.ops->init_tgt) > + if (dev->dev_info.state != UBLK_S_DEV_QUIESCED && dev->tgt.ops->init_tgt) > ret = dev->tgt.ops->init_tgt(dev); > > + else if (dev->dev_info.state == UBLK_S_DEV_QUIESCED && dev->tgt.ops->recover_tgt) > + ret = dev->tgt.ops->recover_tgt(dev); > + > return ret; > fail: > close(dev->fds[0]); > @@ -831,7 +860,7 @@ static void ublk_set_parameters(struct ublk_dev *dev) > dev->dev_info.dev_id, ret); > } > > -static int ublk_start_daemon(struct ublk_dev *dev) > +static int ublk_start_daemon(struct ublk_dev *dev, bool recovery) > { > int ret, i; > void *thread_ret; > @@ -853,12 +882,22 @@ static int ublk_start_daemon(struct ublk_dev *dev) > &dev->q[i]); > } > > - ublk_set_parameters(dev); > > /* everything is fine now, start us */ > - ret = ublk_ctrl_start_dev(dev, getpid()); > - if (ret < 0) > - goto fail; > + if (recovery) { > + ret = ublk_ctrl_end_user_recover(dev, getpid()); > + if (ret < 0) { > + ublk_err("%s: ublk_ctrl_end_user_recover failed: %d\n", __func__, ret); > + goto fail; > + } > + } else { > + ublk_set_parameters(dev); > + ret = ublk_ctrl_start_dev(dev, getpid()); > + if (ret < 0) { > + ublk_err("%s: ublk_ctrl_start_dev failed: %d\n", __func__, ret); > + goto fail; > + } > + } > > ublk_ctrl_get_info(dev); > ublk_ctrl_dump(dev, true); > @@ -880,6 +919,7 @@ static int cmd_dev_add(int argc, char *argv[]) > { "number", 1, NULL, 'n' }, > { "queues", 1, NULL, 'q' }, > { "depth", 1, NULL, 'd' }, > + { "recovery", 1, NULL, 'r' }, > { "debug_mask", 1, NULL, 0}, > { "quiet", 0, NULL, 0}, > { NULL } > @@ -891,8 +931,9 @@ static int cmd_dev_add(int argc, char *argv[]) > const char *tgt_type = NULL; > int dev_id = -1; > unsigned nr_queues = 2, depth = UBLK_QUEUE_DEPTH; > + int user_recovery = 0; > > - while ((opt = getopt_long(argc, argv, "-:t:n:d:q:", > + while ((opt = getopt_long(argc, argv, "-:t:n:d:q:r:", > longopts, &option_idx)) != -1) { > switch (opt) { > case 'n': > @@ -907,6 +948,9 @@ static int cmd_dev_add(int argc, char *argv[]) > case 'd': > depth = strtol(optarg, NULL, 10); > break; > + case 'r': > + user_recovery = strtol(optarg, NULL, 10); user_recovery is int strtol returns long ? > + break; > case 0: > if (!strcmp(longopts[option_idx].name, "debug_mask")) > ublk_dbg_mask = strtol(optarg, NULL, 16); > @@ -942,6 +986,8 @@ static int cmd_dev_add(int argc, char *argv[]) > info->dev_id = dev_id; > info->nr_hw_queues = nr_queues; > info->queue_depth = depth; > + if (user_recovery) > + info->flags |= UBLK_F_USER_RECOVERY; > dev->tgt.ops = ops; > dev->tgt.argc = argc; > dev->tgt.argv = argv; > @@ -953,7 +999,95 @@ static int cmd_dev_add(int argc, char *argv[]) > goto fail; > } > > - ret = ublk_start_daemon(dev); > + ret = ublk_start_daemon(dev, false); > + if (ret < 0) { > + ublk_err("%s: can't start daemon id %d, type %s\n", > + __func__, dev_id, tgt_type); > + goto fail_del; > + } > + > +fail_del: > + ublk_ctrl_del_dev(dev); > +fail: > + ublk_ctrl_deinit(dev); > + return ret; > +} > + > +static int cmd_dev_recover(int argc, char *argv[]) > +{ > + static const struct option longopts[] = { > + { "type", 1, NULL, 't' }, > + { "number", 1, NULL, 'n' }, > + { "debug_mask", 1, NULL, 0}, > + { "quiet", 0, NULL, 0}, > + { NULL } > + }; > + const struct ublk_tgt_ops *ops; > + struct ublksrv_ctrl_dev_info *info; > + struct ublk_dev *dev; > + int ret, option_idx, opt; > + const char *tgt_type = NULL; > + int dev_id = -1; > + > + while ((opt = getopt_long(argc, argv, "-:t:n:d:q:", > + longopts, &option_idx)) != -1) { > + switch (opt) { > + case 'n': > + dev_id = strtol(optarg, NULL, 10); > + break; > + case 't': > + tgt_type = optarg; > + break; > + case 0: > + if (!strcmp(longopts[option_idx].name, "debug_mask")) > + ublk_dbg_mask = strtol(optarg, NULL, 16); > + if (!strcmp(longopts[option_idx].name, "quiet")) > + ublk_dbg_mask = 0; > + break; > + } > + } > + > + optind = 0; > + > + ops = ublk_find_tgt(tgt_type); > + if (!ops) { > + ublk_err("%s: no such tgt type, type %s\n", > + __func__, tgt_type); > + return -ENODEV; > + } > + > + dev = ublk_ctrl_init(); > + if (!dev) { > + ublk_err("%s: can't alloc dev id %d, type %s\n", > + __func__, dev_id, tgt_type); > + return -ENOMEM; > + } > + > + info = &dev->dev_info; > + info->dev_id = dev_id; > + ret = ublk_ctrl_get_info(dev); > + if (ret < 0) { > + ublk_err("%s: can't get dev info from %d\n", __func__, dev_id); > + goto fail; > + } > + > + ret = ublk_ctrl_get_params(dev, &dev->tgt.params); > + if (ret) { > + ublk_err("dev %d set basic parameter failed %d\n", > + dev->dev_info.dev_id, ret); > + goto fail; > + } > + > + dev->tgt.ops = ops; > + dev->tgt.argc = argc; > + dev->tgt.argv = argv; > + ret = ublk_ctrl_start_user_recover(dev); > + if (ret < 0) { > + ublk_err("%s: can't start recovery for %d\n", __func__, dev_id); > + goto fail; > + } > + > + ret = ublk_start_daemon(dev, true); > if (ret < 0) { > ublk_err("%s: can't start daemon id %d, type %s\n", > __func__, dev_id, tgt_type); > @@ -1125,7 +1259,10 @@ static int cmd_dev_help(int argc, char *argv[]) > printf("\t -a delete all devices -n delete specified device\n"); > printf("%s list [-n dev_id] -a \n", argv[0]); > printf("\t -a list all devices, -n list specified device, default -a \n"); > - > + printf("%s recover -t {null|loop} [-n dev_id] \n", > + argv[0]); > + printf("\t -t loop -f backing_file \n"); > + printf("\t -t null\n"); > return 0; > } > > @@ -1150,6 +1287,12 @@ static int ublk_null_tgt_init(struct ublk_dev *dev) > return 0; > } > > +static int ublk_null_tgt_recover(struct ublk_dev *dev) > +{ > + dev->tgt.dev_size = dev->tgt.params.basic.dev_sectors << 9; > + return 0; > +} > + > static int ublk_null_queue_io(struct ublk_queue *q, int tag) > { > const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); > @@ -1313,11 +1456,54 @@ static int ublk_loop_tgt_init(struct ublk_dev *dev) > return 0; > } > > +static int ublk_loop_tgt_recover(struct ublk_dev *dev) > +{ > + static const struct option lo_longopts[] = { > + { "file", 1, NULL, 'f' }, > + { NULL } > + }; > + char **argv = dev->tgt.argv; > + int argc = dev->tgt.argc; > + char *file = NULL; > + int fd, opt; > + > + while ((opt = getopt_long(argc, argv, "-:f:", > + lo_longopts, NULL)) != -1) { > + switch (opt) { > + case 'f': > + file = strdup(optarg); > + break; > + } > + } > + > + ublk_dbg(UBLK_DBG_DEV, "%s: file %s\n", __func__, file); why print file above before checking for NULL below? > + > + if (!file) > + return -EINVAL; > + > + fd = open(file, O_RDWR); > + if (fd < 0) { > + ublk_err( "%s: backing file %s can't be opened\n", > + __func__, file); for system call related errors printing error-no string is always useful. > + return -EBADF; > + } > + > + if (fcntl(fd, F_SETFL, O_DIRECT)) > + ublk_log("%s: ublk-loop fallback to buffered IO\n", __func__); same here ... > + > + dev->tgt.dev_size = dev->tgt.params.basic.dev_sectors << 9; > + dev->fds[1] = fd; > + dev->nr_fds += 1; > + > + return 0; > +} > + > const struct ublk_tgt_ops tgt_ops_list[] = { > { > .name = "null", > .init_tgt = ublk_null_tgt_init, > .queue_io = ublk_null_queue_io, > + .recover_tgt = ublk_null_tgt_recover, > }, > > { > @@ -1326,6 +1512,7 @@ const struct ublk_tgt_ops tgt_ops_list[] = { > .deinit_tgt = ublk_loop_tgt_deinit, > .queue_io = ublk_loop_queue_io, > .tgt_io_done = ublk_loop_io_done, > + .recover_tgt = ublk_loop_tgt_recover, > }, > }; > > @@ -1359,6 +1546,8 @@ int main(int argc, char *argv[]) > ret = cmd_dev_list(argc, argv); > else if (!strcmp(cmd, "help")) > ret = cmd_dev_help(argc, argv); > + else if (!strcmp(cmd, "recover")) > + ret = cmd_dev_recover(argc, argv); > out: > if (ret) > cmd_dev_help(argc, argv);