1*94c4a1e1SFrank Piva // SPDX-License-Identifier: MIT or LGPL-2.1-only
2*94c4a1e1SFrank Piva
3*94c4a1e1SFrank Piva #include <config.h>
4*94c4a1e1SFrank Piva
5*94c4a1e1SFrank Piva #include "ublksrv_priv.h"
6*94c4a1e1SFrank Piva
7*94c4a1e1SFrank Piva #define CTRL_DEV "/dev/ublk-control"
8*94c4a1e1SFrank Piva
9*94c4a1e1SFrank Piva #define CTRL_CMD_HAS_DATA 1
10*94c4a1e1SFrank Piva #define CTRL_CMD_HAS_BUF 2
11*94c4a1e1SFrank Piva #define CTRL_CMD_NO_TRANS 4
12*94c4a1e1SFrank Piva
13*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data {
14*94c4a1e1SFrank Piva unsigned int cmd_op;
15*94c4a1e1SFrank Piva unsigned short flags;
16*94c4a1e1SFrank Piva unsigned short _pad;
17*94c4a1e1SFrank Piva
18*94c4a1e1SFrank Piva __u64 data[1];
19*94c4a1e1SFrank Piva __u16 dev_path_len;
20*94c4a1e1SFrank Piva __u16 pad;
21*94c4a1e1SFrank Piva __u32 reserved;
22*94c4a1e1SFrank Piva
23*94c4a1e1SFrank Piva __u64 addr;
24*94c4a1e1SFrank Piva __u32 len;
25*94c4a1e1SFrank Piva };
26*94c4a1e1SFrank Piva
27*94c4a1e1SFrank Piva #define ublk_un_privileged_prep_data(dev, data) \
28*94c4a1e1SFrank Piva char buf[UBLKC_PATH_MAX]; \
29*94c4a1e1SFrank Piva if (ublk_is_unprivileged(dev)) { \
30*94c4a1e1SFrank Piva snprintf(buf, UBLKC_PATH_MAX, "%s%d", UBLKC_DEV, \
31*94c4a1e1SFrank Piva dev->dev_info.dev_id); \
32*94c4a1e1SFrank Piva data.flags |= CTRL_CMD_HAS_BUF | CTRL_CMD_HAS_DATA; \
33*94c4a1e1SFrank Piva data.len = sizeof(buf); \
34*94c4a1e1SFrank Piva data.dev_path_len = UBLKC_PATH_MAX; \
35*94c4a1e1SFrank Piva data.addr = (__u64)buf; \
36*94c4a1e1SFrank Piva }
37*94c4a1e1SFrank Piva
38*94c4a1e1SFrank Piva static const unsigned int ctrl_cmd_op[] = {
39*94c4a1e1SFrank Piva [UBLK_CMD_GET_QUEUE_AFFINITY] = UBLK_U_CMD_GET_QUEUE_AFFINITY,
40*94c4a1e1SFrank Piva [UBLK_CMD_GET_DEV_INFO] = UBLK_U_CMD_GET_DEV_INFO,
41*94c4a1e1SFrank Piva [UBLK_CMD_ADD_DEV] = UBLK_U_CMD_ADD_DEV,
42*94c4a1e1SFrank Piva [UBLK_CMD_DEL_DEV] = UBLK_U_CMD_DEL_DEV,
43*94c4a1e1SFrank Piva [UBLK_CMD_START_DEV] = UBLK_U_CMD_START_DEV,
44*94c4a1e1SFrank Piva [UBLK_CMD_STOP_DEV] = UBLK_U_CMD_STOP_DEV,
45*94c4a1e1SFrank Piva [UBLK_CMD_SET_PARAMS] = UBLK_U_CMD_SET_PARAMS,
46*94c4a1e1SFrank Piva [UBLK_CMD_GET_PARAMS] = UBLK_U_CMD_GET_PARAMS,
47*94c4a1e1SFrank Piva [UBLK_CMD_START_USER_RECOVERY] = UBLK_U_CMD_START_USER_RECOVERY,
48*94c4a1e1SFrank Piva [UBLK_CMD_END_USER_RECOVERY] = UBLK_U_CMD_END_USER_RECOVERY,
49*94c4a1e1SFrank Piva [UBLK_CMD_GET_DEV_INFO2] = UBLK_U_CMD_GET_DEV_INFO2,
50*94c4a1e1SFrank Piva };
51*94c4a1e1SFrank Piva
legacy_op_to_ioctl(unsigned int op)52*94c4a1e1SFrank Piva static unsigned int legacy_op_to_ioctl(unsigned int op)
53*94c4a1e1SFrank Piva {
54*94c4a1e1SFrank Piva assert(_IOC_TYPE(op) == 0);
55*94c4a1e1SFrank Piva assert(_IOC_DIR(op) == 0);
56*94c4a1e1SFrank Piva assert(_IOC_SIZE(op) == 0);
57*94c4a1e1SFrank Piva assert(op >= UBLK_CMD_GET_QUEUE_AFFINITY &&
58*94c4a1e1SFrank Piva op <= UBLK_CMD_GET_DEV_INFO2);
59*94c4a1e1SFrank Piva
60*94c4a1e1SFrank Piva return ctrl_cmd_op[op];
61*94c4a1e1SFrank Piva }
62*94c4a1e1SFrank Piva
63*94c4a1e1SFrank Piva
64*94c4a1e1SFrank Piva /*******************ctrl dev operation ********************************/
ublksrv_ctrl_init_cmd(struct ublksrv_ctrl_dev * dev,struct io_uring_sqe * sqe,struct ublksrv_ctrl_cmd_data * data)65*94c4a1e1SFrank Piva static inline void ublksrv_ctrl_init_cmd(struct ublksrv_ctrl_dev *dev,
66*94c4a1e1SFrank Piva struct io_uring_sqe *sqe,
67*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data *data)
68*94c4a1e1SFrank Piva {
69*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev_info *info = &dev->dev_info;
70*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd *cmd = (struct ublksrv_ctrl_cmd *)ublksrv_get_sqe_cmd(sqe);
71*94c4a1e1SFrank Piva unsigned int cmd_op = data->cmd_op;
72*94c4a1e1SFrank Piva
73*94c4a1e1SFrank Piva sqe->fd = dev->ctrl_fd;
74*94c4a1e1SFrank Piva sqe->opcode = IORING_OP_URING_CMD;
75*94c4a1e1SFrank Piva sqe->ioprio = 0;
76*94c4a1e1SFrank Piva
77*94c4a1e1SFrank Piva if (data->flags & CTRL_CMD_HAS_BUF) {
78*94c4a1e1SFrank Piva cmd->addr = data->addr;
79*94c4a1e1SFrank Piva cmd->len = data->len;
80*94c4a1e1SFrank Piva }
81*94c4a1e1SFrank Piva
82*94c4a1e1SFrank Piva if (data->flags & CTRL_CMD_HAS_DATA) {
83*94c4a1e1SFrank Piva cmd->data[0] = data->data[0];
84*94c4a1e1SFrank Piva cmd->dev_path_len = data->dev_path_len;
85*94c4a1e1SFrank Piva }
86*94c4a1e1SFrank Piva
87*94c4a1e1SFrank Piva cmd->dev_id = info->dev_id;
88*94c4a1e1SFrank Piva cmd->queue_id = -1;
89*94c4a1e1SFrank Piva
90*94c4a1e1SFrank Piva if (!(data->flags & CTRL_CMD_NO_TRANS) &&
91*94c4a1e1SFrank Piva (info->flags & UBLK_F_CMD_IOCTL_ENCODE))
92*94c4a1e1SFrank Piva cmd_op = legacy_op_to_ioctl(cmd_op);
93*94c4a1e1SFrank Piva ublksrv_set_sqe_cmd_op(sqe, cmd_op);
94*94c4a1e1SFrank Piva
95*94c4a1e1SFrank Piva io_uring_sqe_set_data(sqe, cmd);
96*94c4a1e1SFrank Piva
97*94c4a1e1SFrank Piva ublk_ctrl_dbg(UBLK_DBG_CTRL_CMD, "dev %d cmd_op %x/%x, user_data %p\n",
98*94c4a1e1SFrank Piva dev->dev_info.dev_id, data->cmd_op, cmd_op, cmd);
99*94c4a1e1SFrank Piva }
100*94c4a1e1SFrank Piva
__ublksrv_ctrl_cmd(struct ublksrv_ctrl_dev * dev,struct ublksrv_ctrl_cmd_data * data)101*94c4a1e1SFrank Piva static int __ublksrv_ctrl_cmd(struct ublksrv_ctrl_dev *dev,
102*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data *data)
103*94c4a1e1SFrank Piva {
104*94c4a1e1SFrank Piva struct io_uring_sqe *sqe;
105*94c4a1e1SFrank Piva struct io_uring_cqe *cqe;
106*94c4a1e1SFrank Piva int ret = -EINVAL;
107*94c4a1e1SFrank Piva
108*94c4a1e1SFrank Piva sqe = io_uring_get_sqe(&dev->ring);
109*94c4a1e1SFrank Piva if (!sqe) {
110*94c4a1e1SFrank Piva fprintf(stderr, "can't get sqe ret %d\n", ret);
111*94c4a1e1SFrank Piva return ret;
112*94c4a1e1SFrank Piva }
113*94c4a1e1SFrank Piva
114*94c4a1e1SFrank Piva ublksrv_ctrl_init_cmd(dev, sqe, data);
115*94c4a1e1SFrank Piva
116*94c4a1e1SFrank Piva ret = io_uring_submit(&dev->ring);
117*94c4a1e1SFrank Piva if (ret < 0) {
118*94c4a1e1SFrank Piva fprintf(stderr, "uring submit ret %d\n", ret);
119*94c4a1e1SFrank Piva return ret;
120*94c4a1e1SFrank Piva }
121*94c4a1e1SFrank Piva
122*94c4a1e1SFrank Piva ret = io_uring_wait_cqe(&dev->ring, &cqe);
123*94c4a1e1SFrank Piva if (ret < 0) {
124*94c4a1e1SFrank Piva fprintf(stderr, "wait cqe: %s\n", strerror(-ret));
125*94c4a1e1SFrank Piva return ret;
126*94c4a1e1SFrank Piva }
127*94c4a1e1SFrank Piva io_uring_cqe_seen(&dev->ring, cqe);
128*94c4a1e1SFrank Piva
129*94c4a1e1SFrank Piva ublk_ctrl_dbg(UBLK_DBG_CTRL_CMD, "dev %d, ctrl cqe res %d, user_data %llx\n",
130*94c4a1e1SFrank Piva dev->dev_info.dev_id, cqe->res, cqe->user_data);
131*94c4a1e1SFrank Piva return cqe->res;
132*94c4a1e1SFrank Piva }
133*94c4a1e1SFrank Piva
ublksrv_ctrl_deinit(struct ublksrv_ctrl_dev * dev)134*94c4a1e1SFrank Piva void ublksrv_ctrl_deinit(struct ublksrv_ctrl_dev *dev)
135*94c4a1e1SFrank Piva {
136*94c4a1e1SFrank Piva close(dev->ring.ring_fd);
137*94c4a1e1SFrank Piva close(dev->ctrl_fd);
138*94c4a1e1SFrank Piva free(dev->queues_cpuset);
139*94c4a1e1SFrank Piva free(dev);
140*94c4a1e1SFrank Piva }
141*94c4a1e1SFrank Piva
ublksrv_ctrl_init(struct ublksrv_dev_data * data)142*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *ublksrv_ctrl_init(struct ublksrv_dev_data *data)
143*94c4a1e1SFrank Piva {
144*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *dev = (struct ublksrv_ctrl_dev *)calloc(1,
145*94c4a1e1SFrank Piva sizeof(*dev));
146*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev_info *info = &dev->dev_info;
147*94c4a1e1SFrank Piva int ret;
148*94c4a1e1SFrank Piva
149*94c4a1e1SFrank Piva dev->ctrl_fd = open(CTRL_DEV, O_RDWR);
150*94c4a1e1SFrank Piva if (dev->ctrl_fd < 0) {
151*94c4a1e1SFrank Piva fprintf(stderr, "control dev %s can't be opened: %m\n", CTRL_DEV);
152*94c4a1e1SFrank Piva exit(dev->ctrl_fd);
153*94c4a1e1SFrank Piva }
154*94c4a1e1SFrank Piva
155*94c4a1e1SFrank Piva /* -1 means we ask ublk driver to allocate one free to us */
156*94c4a1e1SFrank Piva info->dev_id = data->dev_id;
157*94c4a1e1SFrank Piva info->nr_hw_queues = data->nr_hw_queues;
158*94c4a1e1SFrank Piva info->queue_depth = data->queue_depth;
159*94c4a1e1SFrank Piva info->max_io_buf_bytes = data->max_io_buf_bytes;
160*94c4a1e1SFrank Piva info->flags = data->flags;
161*94c4a1e1SFrank Piva info->ublksrv_flags = data->ublksrv_flags;
162*94c4a1e1SFrank Piva
163*94c4a1e1SFrank Piva dev->run_dir = data->run_dir;
164*94c4a1e1SFrank Piva dev->tgt_type = data->tgt_type;
165*94c4a1e1SFrank Piva dev->tgt_ops = data->tgt_ops;
166*94c4a1e1SFrank Piva dev->tgt_argc = data->tgt_argc;
167*94c4a1e1SFrank Piva dev->tgt_argv = data->tgt_argv;
168*94c4a1e1SFrank Piva
169*94c4a1e1SFrank Piva /* 32 is enough to send ctrl commands */
170*94c4a1e1SFrank Piva ret = ublksrv_setup_ring(&dev->ring, 32, 32, IORING_SETUP_SQE128);
171*94c4a1e1SFrank Piva if (ret < 0) {
172*94c4a1e1SFrank Piva fprintf(stderr, "queue_init: %s\n", strerror(-ret));
173*94c4a1e1SFrank Piva free(dev);
174*94c4a1e1SFrank Piva return NULL;
175*94c4a1e1SFrank Piva }
176*94c4a1e1SFrank Piva
177*94c4a1e1SFrank Piva return dev;
178*94c4a1e1SFrank Piva }
179*94c4a1e1SFrank Piva
180*94c4a1e1SFrank Piva /* queues_cpuset is only used for setting up queue pthread daemon */
ublksrv_ctrl_get_affinity(struct ublksrv_ctrl_dev * ctrl_dev)181*94c4a1e1SFrank Piva int ublksrv_ctrl_get_affinity(struct ublksrv_ctrl_dev *ctrl_dev)
182*94c4a1e1SFrank Piva {
183*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
184*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_GET_QUEUE_AFFINITY,
185*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_DATA | CTRL_CMD_HAS_BUF,
186*94c4a1e1SFrank Piva };
187*94c4a1e1SFrank Piva unsigned char *buf;
188*94c4a1e1SFrank Piva int i, ret;
189*94c4a1e1SFrank Piva int len;
190*94c4a1e1SFrank Piva int path_len;
191*94c4a1e1SFrank Piva
192*94c4a1e1SFrank Piva if (ublk_is_unprivileged(ctrl_dev))
193*94c4a1e1SFrank Piva path_len = UBLKC_PATH_MAX;
194*94c4a1e1SFrank Piva else
195*94c4a1e1SFrank Piva path_len = 0;
196*94c4a1e1SFrank Piva
197*94c4a1e1SFrank Piva len = (sizeof(cpu_set_t) + path_len) * ctrl_dev->dev_info.nr_hw_queues;
198*94c4a1e1SFrank Piva buf = malloc(len);
199*94c4a1e1SFrank Piva
200*94c4a1e1SFrank Piva if (!buf)
201*94c4a1e1SFrank Piva return -ENOMEM;
202*94c4a1e1SFrank Piva
203*94c4a1e1SFrank Piva for (i = 0; i < ctrl_dev->dev_info.nr_hw_queues; i++) {
204*94c4a1e1SFrank Piva data.data[0] = i;
205*94c4a1e1SFrank Piva data.dev_path_len = path_len;
206*94c4a1e1SFrank Piva data.len = sizeof(cpu_set_t) + path_len;
207*94c4a1e1SFrank Piva data.addr = (__u64)&buf[i * data.len];
208*94c4a1e1SFrank Piva
209*94c4a1e1SFrank Piva if (path_len)
210*94c4a1e1SFrank Piva snprintf((char *)data.addr, UBLKC_PATH_MAX, "%s%d",
211*94c4a1e1SFrank Piva UBLKC_DEV, ctrl_dev->dev_info.dev_id);
212*94c4a1e1SFrank Piva
213*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_cmd(ctrl_dev, &data);
214*94c4a1e1SFrank Piva if (ret < 0) {
215*94c4a1e1SFrank Piva free(buf);
216*94c4a1e1SFrank Piva return ret;
217*94c4a1e1SFrank Piva }
218*94c4a1e1SFrank Piva }
219*94c4a1e1SFrank Piva ctrl_dev->queues_cpuset = (cpu_set_t *)buf;
220*94c4a1e1SFrank Piva
221*94c4a1e1SFrank Piva return 0;
222*94c4a1e1SFrank Piva }
223*94c4a1e1SFrank Piva
224*94c4a1e1SFrank Piva /*
225*94c4a1e1SFrank Piva * Start the ublksrv device:
226*94c4a1e1SFrank Piva *
227*94c4a1e1SFrank Piva * 1) fork a daemon for handling IO command from driver
228*94c4a1e1SFrank Piva *
229*94c4a1e1SFrank Piva * 2) wait for the device becoming ready: the daemon should submit
230*94c4a1e1SFrank Piva * sqes to /dev/ublkcN, just like usb's urb usage, each request needs
231*94c4a1e1SFrank Piva * one sqe. If one IO request comes to kernel driver of /dev/ublkbN,
232*94c4a1e1SFrank Piva * the sqe for this request is completed, and the daemon gets notified.
233*94c4a1e1SFrank Piva * When every io request of driver gets its own sqe queued, we think
234*94c4a1e1SFrank Piva * /dev/ublkbN is ready to start
235*94c4a1e1SFrank Piva *
236*94c4a1e1SFrank Piva * 3) in current process context, sent START_DEV command to
237*94c4a1e1SFrank Piva * /dev/ublk-control with device id, which will cause ublk driver to
238*94c4a1e1SFrank Piva * expose /dev/ublkbN
239*94c4a1e1SFrank Piva */
ublksrv_ctrl_start_dev(struct ublksrv_ctrl_dev * ctrl_dev,int daemon_pid)240*94c4a1e1SFrank Piva int ublksrv_ctrl_start_dev(struct ublksrv_ctrl_dev *ctrl_dev,
241*94c4a1e1SFrank Piva int daemon_pid)
242*94c4a1e1SFrank Piva {
243*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
244*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_START_DEV,
245*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_DATA,
246*94c4a1e1SFrank Piva };
247*94c4a1e1SFrank Piva int ret;
248*94c4a1e1SFrank Piva
249*94c4a1e1SFrank Piva ublk_un_privileged_prep_data(ctrl_dev, data);
250*94c4a1e1SFrank Piva
251*94c4a1e1SFrank Piva ctrl_dev->dev_info.ublksrv_pid = data.data[0] = daemon_pid;
252*94c4a1e1SFrank Piva
253*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_cmd(ctrl_dev, &data);
254*94c4a1e1SFrank Piva
255*94c4a1e1SFrank Piva return ret;
256*94c4a1e1SFrank Piva }
257*94c4a1e1SFrank Piva
258*94c4a1e1SFrank Piva /*
259*94c4a1e1SFrank Piva * Stop the ublksrv device:
260*94c4a1e1SFrank Piva *
261*94c4a1e1SFrank Piva * 1) send STOP_DEV command to /dev/ublk-control with device id provided
262*94c4a1e1SFrank Piva *
263*94c4a1e1SFrank Piva * 2) ublk driver gets this command, freeze /dev/ublkbN, then complete all
264*94c4a1e1SFrank Piva * pending seq, meantime tell the daemon via cqe->res to not submit sqe
265*94c4a1e1SFrank Piva * any more, since we are being closed. Also delete /dev/ublkbN.
266*94c4a1e1SFrank Piva *
267*94c4a1e1SFrank Piva * 3) the ublk daemon figures out that all sqes are completed, and free,
268*94c4a1e1SFrank Piva * then close /dev/ublkcN and exit itself.
269*94c4a1e1SFrank Piva */
__ublksrv_ctrl_add_dev(struct ublksrv_ctrl_dev * dev,unsigned cmd_op)270*94c4a1e1SFrank Piva static int __ublksrv_ctrl_add_dev(struct ublksrv_ctrl_dev *dev, unsigned cmd_op)
271*94c4a1e1SFrank Piva {
272*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
273*94c4a1e1SFrank Piva .cmd_op = cmd_op,
274*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_BUF | CTRL_CMD_NO_TRANS,
275*94c4a1e1SFrank Piva .addr = (__u64)&dev->dev_info,
276*94c4a1e1SFrank Piva .len = sizeof(struct ublksrv_ctrl_dev_info),
277*94c4a1e1SFrank Piva };
278*94c4a1e1SFrank Piva
279*94c4a1e1SFrank Piva return __ublksrv_ctrl_cmd(dev, &data);
280*94c4a1e1SFrank Piva }
281*94c4a1e1SFrank Piva
ublksrv_ctrl_add_dev(struct ublksrv_ctrl_dev * dev)282*94c4a1e1SFrank Piva int ublksrv_ctrl_add_dev(struct ublksrv_ctrl_dev *dev)
283*94c4a1e1SFrank Piva {
284*94c4a1e1SFrank Piva int ret = __ublksrv_ctrl_add_dev(dev, UBLK_U_CMD_ADD_DEV);
285*94c4a1e1SFrank Piva
286*94c4a1e1SFrank Piva if (ret < 0)
287*94c4a1e1SFrank Piva return __ublksrv_ctrl_add_dev(dev, UBLK_CMD_ADD_DEV);
288*94c4a1e1SFrank Piva
289*94c4a1e1SFrank Piva return ret;
290*94c4a1e1SFrank Piva }
291*94c4a1e1SFrank Piva
ublksrv_ctrl_del_dev_async(struct ublksrv_ctrl_dev * dev)292*94c4a1e1SFrank Piva int ublksrv_ctrl_del_dev_async(struct ublksrv_ctrl_dev *dev)
293*94c4a1e1SFrank Piva {
294*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
295*94c4a1e1SFrank Piva .cmd_op = UBLK_U_CMD_DEL_DEV_ASYNC,
296*94c4a1e1SFrank Piva .flags = CTRL_CMD_NO_TRANS,
297*94c4a1e1SFrank Piva };
298*94c4a1e1SFrank Piva
299*94c4a1e1SFrank Piva ublk_un_privileged_prep_data(dev, data);
300*94c4a1e1SFrank Piva
301*94c4a1e1SFrank Piva return __ublksrv_ctrl_cmd(dev, &data);
302*94c4a1e1SFrank Piva }
303*94c4a1e1SFrank Piva
ublksrv_ctrl_del_dev(struct ublksrv_ctrl_dev * dev)304*94c4a1e1SFrank Piva int ublksrv_ctrl_del_dev(struct ublksrv_ctrl_dev *dev)
305*94c4a1e1SFrank Piva {
306*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
307*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_DEL_DEV,
308*94c4a1e1SFrank Piva .flags = 0,
309*94c4a1e1SFrank Piva };
310*94c4a1e1SFrank Piva
311*94c4a1e1SFrank Piva ublk_un_privileged_prep_data(dev, data);
312*94c4a1e1SFrank Piva
313*94c4a1e1SFrank Piva return __ublksrv_ctrl_cmd(dev, &data);
314*94c4a1e1SFrank Piva }
315*94c4a1e1SFrank Piva
__ublksrv_ctrl_get_info_no_trans(struct ublksrv_ctrl_dev * dev,unsigned cmd_op)316*94c4a1e1SFrank Piva static int __ublksrv_ctrl_get_info_no_trans(struct ublksrv_ctrl_dev *dev,
317*94c4a1e1SFrank Piva unsigned cmd_op)
318*94c4a1e1SFrank Piva {
319*94c4a1e1SFrank Piva char buf[UBLKC_PATH_MAX + sizeof(dev->dev_info)];
320*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
321*94c4a1e1SFrank Piva .cmd_op = cmd_op,
322*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_BUF | CTRL_CMD_NO_TRANS,
323*94c4a1e1SFrank Piva .addr = (__u64)&dev->dev_info,
324*94c4a1e1SFrank Piva .len = sizeof(struct ublksrv_ctrl_dev_info),
325*94c4a1e1SFrank Piva };
326*94c4a1e1SFrank Piva bool has_dev_path = false;
327*94c4a1e1SFrank Piva int ret;
328*94c4a1e1SFrank Piva
329*94c4a1e1SFrank Piva if (ublk_is_unprivileged(dev) && _IOC_NR(data.cmd_op) == UBLK_CMD_GET_DEV_INFO)
330*94c4a1e1SFrank Piva return -EINVAL;
331*94c4a1e1SFrank Piva
332*94c4a1e1SFrank Piva if (_IOC_NR(data.cmd_op) == UBLK_CMD_GET_DEV_INFO2) {
333*94c4a1e1SFrank Piva snprintf(buf, UBLKC_PATH_MAX, "%s%d", UBLKC_DEV,
334*94c4a1e1SFrank Piva dev->dev_info.dev_id);
335*94c4a1e1SFrank Piva data.flags |= CTRL_CMD_HAS_BUF | CTRL_CMD_HAS_DATA;
336*94c4a1e1SFrank Piva data.len = sizeof(buf);
337*94c4a1e1SFrank Piva data.dev_path_len = UBLKC_PATH_MAX;
338*94c4a1e1SFrank Piva data.addr = (__u64)buf;
339*94c4a1e1SFrank Piva has_dev_path = true;
340*94c4a1e1SFrank Piva }
341*94c4a1e1SFrank Piva
342*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_cmd(dev, &data);
343*94c4a1e1SFrank Piva if (ret >= 0 && has_dev_path)
344*94c4a1e1SFrank Piva memcpy(&dev->dev_info, &buf[UBLKC_PATH_MAX],
345*94c4a1e1SFrank Piva sizeof(dev->dev_info));
346*94c4a1e1SFrank Piva return ret;
347*94c4a1e1SFrank Piva }
348*94c4a1e1SFrank Piva
__ublksrv_ctrl_get_info(struct ublksrv_ctrl_dev * dev,unsigned cmd_op)349*94c4a1e1SFrank Piva static int __ublksrv_ctrl_get_info(struct ublksrv_ctrl_dev *dev,
350*94c4a1e1SFrank Piva unsigned cmd_op)
351*94c4a1e1SFrank Piva {
352*94c4a1e1SFrank Piva unsigned new_code = legacy_op_to_ioctl(cmd_op);
353*94c4a1e1SFrank Piva int ret = __ublksrv_ctrl_get_info_no_trans(dev, new_code);
354*94c4a1e1SFrank Piva
355*94c4a1e1SFrank Piva /*
356*94c4a1e1SFrank Piva * Try ioctl cmd encoding first, then fallback to legacy command
357*94c4a1e1SFrank Piva * opcode if ioctl encoding fails
358*94c4a1e1SFrank Piva */
359*94c4a1e1SFrank Piva if (ret < 0)
360*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_get_info_no_trans(dev, cmd_op);
361*94c4a1e1SFrank Piva
362*94c4a1e1SFrank Piva return ret;
363*94c4a1e1SFrank Piva }
364*94c4a1e1SFrank Piva
365*94c4a1e1SFrank Piva /*
366*94c4a1e1SFrank Piva * Deal with userspace/kernel compatibility
367*94c4a1e1SFrank Piva *
368*94c4a1e1SFrank Piva * 1) if kernel is capable of handling UBLK_F_UNPRIVILEGED_DEV,
369*94c4a1e1SFrank Piva * - ublksrv supports UBLK_F_UNPRIVILEGED_DEV
370*94c4a1e1SFrank Piva * ublksrv should send UBLK_CMD_GET_DEV_INFO2, given anytime unprivileged
371*94c4a1e1SFrank Piva * application needs to query devices it owns, when the application has
372*94c4a1e1SFrank Piva * no idea if UBLK_F_UNPRIVILEGED_DEV is set given the capability info
373*94c4a1e1SFrank Piva * is stateless, and application always get it via control command
374*94c4a1e1SFrank Piva *
375*94c4a1e1SFrank Piva * - ublksrv doesn't support UBLK_F_UNPRIVILEGED_DEV
376*94c4a1e1SFrank Piva * UBLK_CMD_GET_DEV_INFO is always sent to kernel, and the feature of
377*94c4a1e1SFrank Piva * UBLK_F_UNPRIVILEGED_DEV isn't available for user
378*94c4a1e1SFrank Piva *
379*94c4a1e1SFrank Piva * 2) if kernel isn't capable of handling UBLK_F_UNPRIVILEGED_DEV
380*94c4a1e1SFrank Piva * - ublksrv supports UBLK_F_UNPRIVILEGED_DEV
381*94c4a1e1SFrank Piva * UBLK_CMD_GET_DEV_INFO2 is tried first, and will be failed, then
382*94c4a1e1SFrank Piva * UBLK_CMD_GET_DEV_INFO is retried given UBLK_F_UNPRIVILEGED_DEV
383*94c4a1e1SFrank Piva * can't be set
384*94c4a1e1SFrank Piva *
385*94c4a1e1SFrank Piva * - ublksrv doesn't support UBLK_F_UNPRIVILEGED_DEV
386*94c4a1e1SFrank Piva * UBLK_CMD_GET_DEV_INFO is always sent to kernel, and the feature of
387*94c4a1e1SFrank Piva * UBLK_F_UNPRIVILEGED_DEV isn't available for user
388*94c4a1e1SFrank Piva *
389*94c4a1e1SFrank Piva */
ublksrv_ctrl_get_info(struct ublksrv_ctrl_dev * dev)390*94c4a1e1SFrank Piva int ublksrv_ctrl_get_info(struct ublksrv_ctrl_dev *dev)
391*94c4a1e1SFrank Piva {
392*94c4a1e1SFrank Piva int ret;
393*94c4a1e1SFrank Piva
394*94c4a1e1SFrank Piva unsigned cmd_op =
395*94c4a1e1SFrank Piva #ifdef UBLK_CMD_GET_DEV_INFO2
396*94c4a1e1SFrank Piva UBLK_CMD_GET_DEV_INFO2;
397*94c4a1e1SFrank Piva #else
398*94c4a1e1SFrank Piva UBLK_CMD_GET_DEV_INFO;
399*94c4a1e1SFrank Piva #endif
400*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_get_info(dev, cmd_op);
401*94c4a1e1SFrank Piva
402*94c4a1e1SFrank Piva if (cmd_op == UBLK_CMD_GET_DEV_INFO)
403*94c4a1e1SFrank Piva return ret;
404*94c4a1e1SFrank Piva
405*94c4a1e1SFrank Piva if (ret < 0) {
406*94c4a1e1SFrank Piva /* unprivileged does support GET_DEV_INFO2 */
407*94c4a1e1SFrank Piva if (ublk_is_unprivileged(dev))
408*94c4a1e1SFrank Piva return ret;
409*94c4a1e1SFrank Piva /*
410*94c4a1e1SFrank Piva * fallback to GET_DEV_INFO since driver may not support
411*94c4a1e1SFrank Piva * GET_DEV_INFO2
412*94c4a1e1SFrank Piva */
413*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_get_info(dev, UBLK_CMD_GET_DEV_INFO);
414*94c4a1e1SFrank Piva }
415*94c4a1e1SFrank Piva
416*94c4a1e1SFrank Piva return ret;
417*94c4a1e1SFrank Piva }
418*94c4a1e1SFrank Piva
ublksrv_ctrl_stop_dev(struct ublksrv_ctrl_dev * dev)419*94c4a1e1SFrank Piva int ublksrv_ctrl_stop_dev(struct ublksrv_ctrl_dev *dev)
420*94c4a1e1SFrank Piva {
421*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
422*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_STOP_DEV,
423*94c4a1e1SFrank Piva };
424*94c4a1e1SFrank Piva int ret;
425*94c4a1e1SFrank Piva
426*94c4a1e1SFrank Piva ublk_un_privileged_prep_data(dev, data);
427*94c4a1e1SFrank Piva
428*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_cmd(dev, &data);
429*94c4a1e1SFrank Piva return ret;
430*94c4a1e1SFrank Piva }
431*94c4a1e1SFrank Piva
ublksrv_dev_state_desc(struct ublksrv_ctrl_dev * dev)432*94c4a1e1SFrank Piva static const char *ublksrv_dev_state_desc(struct ublksrv_ctrl_dev *dev)
433*94c4a1e1SFrank Piva {
434*94c4a1e1SFrank Piva switch (dev->dev_info.state) {
435*94c4a1e1SFrank Piva case UBLK_S_DEV_DEAD:
436*94c4a1e1SFrank Piva return "DEAD";
437*94c4a1e1SFrank Piva case UBLK_S_DEV_LIVE:
438*94c4a1e1SFrank Piva return "LIVE";
439*94c4a1e1SFrank Piva case UBLK_S_DEV_QUIESCED:
440*94c4a1e1SFrank Piva return "QUIESCED";
441*94c4a1e1SFrank Piva default:
442*94c4a1e1SFrank Piva return "UNKNOWN";
443*94c4a1e1SFrank Piva };
444*94c4a1e1SFrank Piva }
445*94c4a1e1SFrank Piva
ublksrv_ctrl_dump(struct ublksrv_ctrl_dev * dev,const char * jbuf)446*94c4a1e1SFrank Piva void ublksrv_ctrl_dump(struct ublksrv_ctrl_dev *dev, const char *jbuf)
447*94c4a1e1SFrank Piva {
448*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev_info *info = &dev->dev_info;
449*94c4a1e1SFrank Piva int i, ret;
450*94c4a1e1SFrank Piva struct ublk_params p;
451*94c4a1e1SFrank Piva
452*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_params(dev, &p);
453*94c4a1e1SFrank Piva if (ret < 0) {
454*94c4a1e1SFrank Piva fprintf(stderr, "failed to get params %m\n");
455*94c4a1e1SFrank Piva return;
456*94c4a1e1SFrank Piva }
457*94c4a1e1SFrank Piva
458*94c4a1e1SFrank Piva printf("dev id %d: nr_hw_queues %d queue_depth %d block size %d dev_capacity %lld\n",
459*94c4a1e1SFrank Piva info->dev_id,
460*94c4a1e1SFrank Piva info->nr_hw_queues, info->queue_depth,
461*94c4a1e1SFrank Piva 1 << p.basic.logical_bs_shift, p.basic.dev_sectors);
462*94c4a1e1SFrank Piva printf("\tmax rq size %d daemon pid %d flags 0x%llx state %s\n",
463*94c4a1e1SFrank Piva info->max_io_buf_bytes,
464*94c4a1e1SFrank Piva info->ublksrv_pid, info->flags,
465*94c4a1e1SFrank Piva ublksrv_dev_state_desc(dev));
466*94c4a1e1SFrank Piva printf("\tublkc: %u:%d ublkb: %u:%u owner: %u:%u\n",
467*94c4a1e1SFrank Piva p.devt.char_major, p.devt.char_minor,
468*94c4a1e1SFrank Piva p.devt.disk_major, p.devt.disk_minor,
469*94c4a1e1SFrank Piva info->owner_uid, info->owner_gid);
470*94c4a1e1SFrank Piva
471*94c4a1e1SFrank Piva if (jbuf) {
472*94c4a1e1SFrank Piva char buf[512];
473*94c4a1e1SFrank Piva
474*94c4a1e1SFrank Piva for(i = 0; i < info->nr_hw_queues; i++) {
475*94c4a1e1SFrank Piva unsigned tid;
476*94c4a1e1SFrank Piva
477*94c4a1e1SFrank Piva ublksrv_json_read_queue_info(jbuf, i, &tid, buf, 512);
478*94c4a1e1SFrank Piva printf("\tqueue %u: tid %d affinity(%s)\n",
479*94c4a1e1SFrank Piva i, tid, buf);
480*94c4a1e1SFrank Piva }
481*94c4a1e1SFrank Piva
482*94c4a1e1SFrank Piva ublksrv_json_read_target_info(jbuf, buf, 512);
483*94c4a1e1SFrank Piva printf("\ttarget %s\n", buf);
484*94c4a1e1SFrank Piva }
485*94c4a1e1SFrank Piva }
486*94c4a1e1SFrank Piva
ublksrv_ctrl_set_params(struct ublksrv_ctrl_dev * dev,struct ublk_params * params)487*94c4a1e1SFrank Piva int ublksrv_ctrl_set_params(struct ublksrv_ctrl_dev *dev,
488*94c4a1e1SFrank Piva struct ublk_params *params)
489*94c4a1e1SFrank Piva {
490*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
491*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_SET_PARAMS,
492*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_BUF,
493*94c4a1e1SFrank Piva .addr = (__u64)params,
494*94c4a1e1SFrank Piva .len = sizeof(*params),
495*94c4a1e1SFrank Piva };
496*94c4a1e1SFrank Piva char buf[UBLKC_PATH_MAX + sizeof(*params)];
497*94c4a1e1SFrank Piva
498*94c4a1e1SFrank Piva params->len = sizeof(*params);
499*94c4a1e1SFrank Piva
500*94c4a1e1SFrank Piva if (ublk_is_unprivileged(dev)) {
501*94c4a1e1SFrank Piva snprintf(buf, UBLKC_PATH_MAX, "%s%d", UBLKC_DEV,
502*94c4a1e1SFrank Piva dev->dev_info.dev_id);
503*94c4a1e1SFrank Piva memcpy(&buf[UBLKC_PATH_MAX], params, sizeof(*params));
504*94c4a1e1SFrank Piva data.flags |= CTRL_CMD_HAS_BUF | CTRL_CMD_HAS_DATA;
505*94c4a1e1SFrank Piva data.len = sizeof(buf);
506*94c4a1e1SFrank Piva data.dev_path_len = UBLKC_PATH_MAX;
507*94c4a1e1SFrank Piva data.addr = (__u64)buf;
508*94c4a1e1SFrank Piva }
509*94c4a1e1SFrank Piva
510*94c4a1e1SFrank Piva return __ublksrv_ctrl_cmd(dev, &data);
511*94c4a1e1SFrank Piva }
512*94c4a1e1SFrank Piva
ublksrv_ctrl_get_params(struct ublksrv_ctrl_dev * dev,struct ublk_params * params)513*94c4a1e1SFrank Piva int ublksrv_ctrl_get_params(struct ublksrv_ctrl_dev *dev,
514*94c4a1e1SFrank Piva struct ublk_params *params)
515*94c4a1e1SFrank Piva {
516*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
517*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_GET_PARAMS,
518*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_BUF,
519*94c4a1e1SFrank Piva .addr = (__u64)params,
520*94c4a1e1SFrank Piva .len = sizeof(*params),
521*94c4a1e1SFrank Piva };
522*94c4a1e1SFrank Piva char buf[UBLKC_PATH_MAX + sizeof(*params)];
523*94c4a1e1SFrank Piva int ret;
524*94c4a1e1SFrank Piva
525*94c4a1e1SFrank Piva params->len = sizeof(*params);
526*94c4a1e1SFrank Piva
527*94c4a1e1SFrank Piva if (ublk_is_unprivileged(dev)) {
528*94c4a1e1SFrank Piva snprintf(buf, UBLKC_PATH_MAX, "%s%d", UBLKC_DEV,
529*94c4a1e1SFrank Piva dev->dev_info.dev_id);
530*94c4a1e1SFrank Piva memcpy(&buf[UBLKC_PATH_MAX], params, sizeof(*params));
531*94c4a1e1SFrank Piva data.flags |= CTRL_CMD_HAS_BUF | CTRL_CMD_HAS_DATA;
532*94c4a1e1SFrank Piva data.len = sizeof(buf);
533*94c4a1e1SFrank Piva data.dev_path_len = UBLKC_PATH_MAX;
534*94c4a1e1SFrank Piva data.addr = (__u64)buf;
535*94c4a1e1SFrank Piva }
536*94c4a1e1SFrank Piva
537*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_cmd(dev, &data);
538*94c4a1e1SFrank Piva if (ret >= 0 && ublk_is_unprivileged(dev))
539*94c4a1e1SFrank Piva memcpy(params, &buf[UBLKC_PATH_MAX], sizeof(*params));
540*94c4a1e1SFrank Piva
541*94c4a1e1SFrank Piva return 0;
542*94c4a1e1SFrank Piva }
543*94c4a1e1SFrank Piva
ublksrv_ctrl_start_recovery(struct ublksrv_ctrl_dev * dev)544*94c4a1e1SFrank Piva int ublksrv_ctrl_start_recovery(struct ublksrv_ctrl_dev *dev)
545*94c4a1e1SFrank Piva {
546*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
547*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_START_USER_RECOVERY,
548*94c4a1e1SFrank Piva .flags = 0,
549*94c4a1e1SFrank Piva };
550*94c4a1e1SFrank Piva int ret;
551*94c4a1e1SFrank Piva
552*94c4a1e1SFrank Piva ublk_un_privileged_prep_data(dev, data);
553*94c4a1e1SFrank Piva
554*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_cmd(dev, &data);
555*94c4a1e1SFrank Piva return ret;
556*94c4a1e1SFrank Piva }
557*94c4a1e1SFrank Piva
ublksrv_ctrl_end_recovery(struct ublksrv_ctrl_dev * dev,int daemon_pid)558*94c4a1e1SFrank Piva int ublksrv_ctrl_end_recovery(struct ublksrv_ctrl_dev *dev, int daemon_pid)
559*94c4a1e1SFrank Piva {
560*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
561*94c4a1e1SFrank Piva .cmd_op = UBLK_CMD_END_USER_RECOVERY,
562*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_DATA,
563*94c4a1e1SFrank Piva };
564*94c4a1e1SFrank Piva int ret;
565*94c4a1e1SFrank Piva
566*94c4a1e1SFrank Piva ublk_un_privileged_prep_data(dev, data);
567*94c4a1e1SFrank Piva
568*94c4a1e1SFrank Piva dev->dev_info.ublksrv_pid = data.data[0] = daemon_pid;
569*94c4a1e1SFrank Piva
570*94c4a1e1SFrank Piva ret = __ublksrv_ctrl_cmd(dev, &data);
571*94c4a1e1SFrank Piva return ret;
572*94c4a1e1SFrank Piva }
573*94c4a1e1SFrank Piva
ublksrv_ctrl_get_features(struct ublksrv_ctrl_dev * dev,__u64 * features)574*94c4a1e1SFrank Piva int ublksrv_ctrl_get_features(struct ublksrv_ctrl_dev *dev,
575*94c4a1e1SFrank Piva __u64 *features)
576*94c4a1e1SFrank Piva {
577*94c4a1e1SFrank Piva struct ublksrv_ctrl_cmd_data data = {
578*94c4a1e1SFrank Piva .cmd_op = UBLK_U_CMD_GET_FEATURES,
579*94c4a1e1SFrank Piva .flags = CTRL_CMD_HAS_BUF,
580*94c4a1e1SFrank Piva .addr = (__u64)features,
581*94c4a1e1SFrank Piva .len = sizeof(*features),
582*94c4a1e1SFrank Piva };
583*94c4a1e1SFrank Piva
584*94c4a1e1SFrank Piva return __ublksrv_ctrl_cmd(dev, &data);
585*94c4a1e1SFrank Piva }
586*94c4a1e1SFrank Piva
ublksrv_ctrl_get_dev_info(const struct ublksrv_ctrl_dev * dev)587*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *ublksrv_ctrl_get_dev_info(
588*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev *dev)
589*94c4a1e1SFrank Piva {
590*94c4a1e1SFrank Piva return &dev->dev_info;
591*94c4a1e1SFrank Piva }
592*94c4a1e1SFrank Piva
ublksrv_ctrl_get_run_dir(const struct ublksrv_ctrl_dev * dev)593*94c4a1e1SFrank Piva const char *ublksrv_ctrl_get_run_dir(const struct ublksrv_ctrl_dev *dev)
594*94c4a1e1SFrank Piva {
595*94c4a1e1SFrank Piva return dev->run_dir;
596*94c4a1e1SFrank Piva }
597*94c4a1e1SFrank Piva
ublksrv_ctrl_prep_recovery(struct ublksrv_ctrl_dev * dev,const char * tgt_type,const struct ublksrv_tgt_type * tgt_ops,const char * recovery_jbuf)598*94c4a1e1SFrank Piva void ublksrv_ctrl_prep_recovery(struct ublksrv_ctrl_dev *dev,
599*94c4a1e1SFrank Piva const char *tgt_type, const struct ublksrv_tgt_type *tgt_ops,
600*94c4a1e1SFrank Piva const char *recovery_jbuf)
601*94c4a1e1SFrank Piva {
602*94c4a1e1SFrank Piva dev->tgt_type = tgt_type;
603*94c4a1e1SFrank Piva dev->tgt_ops = tgt_ops;
604*94c4a1e1SFrank Piva dev->tgt_argc = -1;
605*94c4a1e1SFrank Piva dev->recovery_jbuf = recovery_jbuf;
606*94c4a1e1SFrank Piva }
607*94c4a1e1SFrank Piva
ublksrv_ctrl_get_recovery_jbuf(const struct ublksrv_ctrl_dev * dev)608*94c4a1e1SFrank Piva const char *ublksrv_ctrl_get_recovery_jbuf(const struct ublksrv_ctrl_dev *dev)
609*94c4a1e1SFrank Piva {
610*94c4a1e1SFrank Piva return dev->recovery_jbuf;
611*94c4a1e1SFrank Piva }
612