xref: /aosp_15_r20/external/ublksrv/lib/ublksrv_cmd.c (revision 94c4a1e103eb1715230460aab379dff275992c20)
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