| // SPDX-License-Identifier: GPL-2.0 | |
| /* ublk control commands, shared by kublk and ublk_cancel_ready */ | |
| #include "kublk.h" | |
| static void ublk_ctrl_init_cmd(struct ublk_dev *dev, | |
| struct io_uring_sqe *sqe, | |
| struct ublk_ctrl_cmd_data *data) | |
| { | |
| struct ublksrv_ctrl_dev_info *info = &dev->dev_info; | |
| struct ublksrv_ctrl_cmd *cmd = (struct ublksrv_ctrl_cmd *)ublk_get_sqe_cmd(sqe); | |
| sqe->fd = dev->ctrl_fd; | |
| sqe->opcode = IORING_OP_URING_CMD; | |
| sqe->ioprio = 0; | |
| if (data->flags & CTRL_CMD_HAS_BUF) { | |
| cmd->addr = data->addr; | |
| cmd->len = data->len; | |
| } | |
| if (data->flags & CTRL_CMD_HAS_DATA) | |
| cmd->data[0] = data->data[0]; | |
| cmd->dev_id = info->dev_id; | |
| cmd->queue_id = -1; | |
| ublk_set_sqe_cmd_op(sqe, data->cmd_op); | |
| io_uring_sqe_set_data(sqe, cmd); | |
| } | |
| int __ublk_ctrl_cmd(struct ublk_dev *dev, | |
| struct ublk_ctrl_cmd_data *data) | |
| { | |
| struct io_uring_sqe *sqe; | |
| struct io_uring_cqe *cqe; | |
| int ret = -EINVAL; | |
| sqe = io_uring_get_sqe(&dev->ring); | |
| if (!sqe) { | |
| ublk_err("%s: can't get sqe ret %d\n", __func__, ret); | |
| return ret; | |
| } | |
| ublk_ctrl_init_cmd(dev, sqe, data); | |
| ret = io_uring_submit(&dev->ring); | |
| if (ret < 0) { | |
| ublk_err("uring submit ret %d\n", ret); | |
| return ret; | |
| } | |
| ret = io_uring_wait_cqe(&dev->ring, &cqe); | |
| if (ret < 0) { | |
| ublk_err("wait cqe: %s\n", strerror(-ret)); | |
| return ret; | |
| } | |
| io_uring_cqe_seen(&dev->ring, cqe); | |
| return cqe->res; | |
| } | |
| void ublk_ctrl_deinit(struct ublk_dev *dev) | |
| { | |
| io_uring_queue_exit(&dev->ring); | |
| close(dev->ctrl_fd); | |
| free(dev); | |
| } | |
| struct ublk_dev *ublk_ctrl_init(void) | |
| { | |
| struct ublk_dev *dev = (struct ublk_dev *)calloc(1, sizeof(*dev)); | |
| struct ublksrv_ctrl_dev_info *info; | |
| int ret; | |
| if (!dev) | |
| return NULL; | |
| info = &dev->dev_info; | |
| dev->ctrl_fd = open(CTRL_DEV, O_RDWR); | |
| if (dev->ctrl_fd < 0) { | |
| free(dev); | |
| return NULL; | |
| } | |
| info->max_io_buf_bytes = UBLK_IO_MAX_BYTES; | |
| ret = ublk_setup_ring(&dev->ring, UBLK_CTRL_RING_DEPTH, | |
| UBLK_CTRL_RING_DEPTH, IORING_SETUP_SQE128); | |
| if (ret < 0) { | |
| ublk_err("queue_init: %s\n", strerror(-ret)); | |
| close(dev->ctrl_fd); | |
| free(dev); | |
| return NULL; | |
| } | |
| dev->nr_fds = 1; | |
| return dev; | |
| } | |
| int ublk_ctrl_stop_dev(struct ublk_dev *dev) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_STOP_DEV, | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_try_stop_dev(struct ublk_dev *dev) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_TRY_STOP_DEV, | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_start_dev(struct ublk_dev *dev, | |
| int daemon_pid) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_START_DEV, | |
| .flags = CTRL_CMD_HAS_DATA, | |
| }; | |
| dev->dev_info.ublksrv_pid = data.data[0] = daemon_pid; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_start_user_recovery(struct ublk_dev *dev) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_START_USER_RECOVERY, | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_end_user_recovery(struct ublk_dev *dev, int daemon_pid) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_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); | |
| } | |
| int ublk_ctrl_add_dev(struct ublk_dev *dev) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_ADD_DEV, | |
| .flags = CTRL_CMD_HAS_BUF, | |
| .addr = (__u64) (uintptr_t) &dev->dev_info, | |
| .len = sizeof(struct ublksrv_ctrl_dev_info), | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_del_dev(struct ublk_dev *dev) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_DEL_DEV, | |
| .flags = 0, | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_get_info(struct ublk_dev *dev) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_GET_DEV_INFO, | |
| .flags = CTRL_CMD_HAS_BUF, | |
| .addr = (__u64) (uintptr_t) &dev->dev_info, | |
| .len = sizeof(struct ublksrv_ctrl_dev_info), | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_set_params(struct ublk_dev *dev, | |
| struct ublk_params *params) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_SET_PARAMS, | |
| .flags = CTRL_CMD_HAS_BUF, | |
| .addr = (__u64) (uintptr_t) params, | |
| .len = sizeof(*params), | |
| }; | |
| params->len = sizeof(*params); | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_get_params(struct ublk_dev *dev, | |
| struct ublk_params *params) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_GET_PARAMS, | |
| .flags = CTRL_CMD_HAS_BUF, | |
| .addr = (__u64)params, | |
| .len = sizeof(*params), | |
| }; | |
| params->len = sizeof(*params); | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_get_features(struct ublk_dev *dev, | |
| __u64 *features) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_GET_FEATURES, | |
| .flags = CTRL_CMD_HAS_BUF, | |
| .addr = (__u64) (uintptr_t) features, | |
| .len = sizeof(*features), | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_update_size(struct ublk_dev *dev, | |
| __u64 nr_sects) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_UPDATE_SIZE, | |
| .flags = CTRL_CMD_HAS_DATA, | |
| }; | |
| data.data[0] = nr_sects; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_quiesce_dev(struct ublk_dev *dev, unsigned int timeout_ms) | |
| { | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_QUIESCE_DEV, | |
| .flags = CTRL_CMD_HAS_DATA, | |
| }; | |
| data.data[0] = timeout_ms; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } | |
| int ublk_ctrl_reg_buf(struct ublk_dev *dev, void *addr, size_t size, | |
| __u32 flags) | |
| { | |
| struct ublk_shmem_buf_reg buf_reg = { | |
| .addr = (unsigned long)addr, | |
| .len = size, | |
| .flags = flags, | |
| }; | |
| struct ublk_ctrl_cmd_data data = { | |
| .cmd_op = UBLK_U_CMD_REG_BUF, | |
| .flags = CTRL_CMD_HAS_BUF, | |
| .addr = (unsigned long)&buf_reg, | |
| .len = sizeof(buf_reg), | |
| }; | |
| return __ublk_ctrl_cmd(dev, &data); | |
| } |