// 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); }