1*94c4a1e1SFrank Piva // SPDX-License-Identifier: GPL-2.0
2*94c4a1e1SFrank Piva
3*94c4a1e1SFrank Piva #include <config.h>
4*94c4a1e1SFrank Piva #include <vector>
5*94c4a1e1SFrank Piva #include "ublksrv_tgt.h"
6*94c4a1e1SFrank Piva #include "ublksrv_tgt_endian.h"
7*94c4a1e1SFrank Piva #include "cliserv.h"
8*94c4a1e1SFrank Piva #include "nbd.h"
9*94c4a1e1SFrank Piva
10*94c4a1e1SFrank Piva //#define NBD_DEBUG_HANDSHAKE 1
11*94c4a1e1SFrank Piva //#define NBD_DEBUG_IO 1
12*94c4a1e1SFrank Piva //#define NBD_DEBUG_CQE 1
13*94c4a1e1SFrank Piva
14*94c4a1e1SFrank Piva #ifdef NBD_DEBUG_IO
15*94c4a1e1SFrank Piva #define NBD_IO_DBG ublk_err
16*94c4a1e1SFrank Piva #else
17*94c4a1e1SFrank Piva #define NBD_IO_DBG(...)
18*94c4a1e1SFrank Piva #endif
19*94c4a1e1SFrank Piva
20*94c4a1e1SFrank Piva #ifdef NBD_DEBUG_HANDSHAKE
21*94c4a1e1SFrank Piva #define NBD_HS_DBG ublk_err
22*94c4a1e1SFrank Piva #else
23*94c4a1e1SFrank Piva #define NBD_HS_DBG(...)
24*94c4a1e1SFrank Piva #endif
25*94c4a1e1SFrank Piva
26*94c4a1e1SFrank Piva #define nbd_err ublk_err
27*94c4a1e1SFrank Piva
28*94c4a1e1SFrank Piva #define NBD_MAX_NAME 512
29*94c4a1e1SFrank Piva
30*94c4a1e1SFrank Piva #define NBD_OP_READ_REQ 0x80
31*94c4a1e1SFrank Piva #define NBD_OP_READ_REPLY 0x81
32*94c4a1e1SFrank Piva
33*94c4a1e1SFrank Piva struct nbd_tgt_data {
34*94c4a1e1SFrank Piva bool unix_sock;
35*94c4a1e1SFrank Piva bool use_send_zc;
36*94c4a1e1SFrank Piva };
37*94c4a1e1SFrank Piva
38*94c4a1e1SFrank Piva #ifndef HAVE_LIBURING_SEND_ZC
39*94c4a1e1SFrank Piva #define io_uring_prep_sendmsg_zc io_uring_prep_sendmsg
40*94c4a1e1SFrank Piva #define IORING_CQE_F_NOTIF (1U << 3)
41*94c4a1e1SFrank Piva #endif
42*94c4a1e1SFrank Piva
43*94c4a1e1SFrank Piva struct nbd_queue_data {
44*94c4a1e1SFrank Piva unsigned short in_flight_ios;
45*94c4a1e1SFrank Piva
46*94c4a1e1SFrank Piva unsigned short recv_started:1;
47*94c4a1e1SFrank Piva unsigned short use_send_zc:1;
48*94c4a1e1SFrank Piva unsigned short use_unix_sock:1;
49*94c4a1e1SFrank Piva unsigned short need_handle_recv:1;
50*94c4a1e1SFrank Piva unsigned short send_sqe_chain_busy:1;
51*94c4a1e1SFrank Piva
52*94c4a1e1SFrank Piva unsigned int chained_send_ios;
53*94c4a1e1SFrank Piva
54*94c4a1e1SFrank Piva /*
55*94c4a1e1SFrank Piva * When the current chain is busy, staggering send ios
56*94c4a1e1SFrank Piva * into this queue(next_chain). After the current chain
57*94c4a1e1SFrank Piva * is consumed, submit all send ios in 'next_chain' as
58*94c4a1e1SFrank Piva * one whole batch.
59*94c4a1e1SFrank Piva */
60*94c4a1e1SFrank Piva std::vector <const struct ublk_io_data *> next_chain;
61*94c4a1e1SFrank Piva
62*94c4a1e1SFrank Piva struct io_uring_sqe *last_send_sqe;
63*94c4a1e1SFrank Piva struct nbd_reply reply;
64*94c4a1e1SFrank Piva struct io_uring_cqe recv_cqe;
65*94c4a1e1SFrank Piva };
66*94c4a1e1SFrank Piva
67*94c4a1e1SFrank Piva struct nbd_io_data {
68*94c4a1e1SFrank Piva unsigned int cmd_cookie;
69*94c4a1e1SFrank Piva unsigned int done; //for handling partial recv
70*94c4a1e1SFrank Piva };
71*94c4a1e1SFrank Piva
72*94c4a1e1SFrank Piva static inline struct nbd_queue_data *
nbd_get_queue_data(const struct ublksrv_queue * q)73*94c4a1e1SFrank Piva nbd_get_queue_data(const struct ublksrv_queue *q)
74*94c4a1e1SFrank Piva {
75*94c4a1e1SFrank Piva return (struct nbd_queue_data *)q->private_data;
76*94c4a1e1SFrank Piva }
77*94c4a1e1SFrank Piva
78*94c4a1e1SFrank Piva static inline struct nbd_io_data *
io_tgt_to_nbd_data(const struct ublk_io_tgt * io)79*94c4a1e1SFrank Piva io_tgt_to_nbd_data(const struct ublk_io_tgt *io)
80*94c4a1e1SFrank Piva {
81*94c4a1e1SFrank Piva return (struct nbd_io_data *)(io + 1);
82*94c4a1e1SFrank Piva }
83*94c4a1e1SFrank Piva
req_to_nbd_cmd_type(const struct ublksrv_io_desc * iod)84*94c4a1e1SFrank Piva static int req_to_nbd_cmd_type(const struct ublksrv_io_desc *iod)
85*94c4a1e1SFrank Piva {
86*94c4a1e1SFrank Piva switch (ublksrv_get_op(iod)) {
87*94c4a1e1SFrank Piva case UBLK_IO_OP_DISCARD:
88*94c4a1e1SFrank Piva return NBD_CMD_TRIM;
89*94c4a1e1SFrank Piva case UBLK_IO_OP_FLUSH:
90*94c4a1e1SFrank Piva return NBD_CMD_FLUSH;
91*94c4a1e1SFrank Piva case UBLK_IO_OP_WRITE:
92*94c4a1e1SFrank Piva return NBD_CMD_WRITE;
93*94c4a1e1SFrank Piva case UBLK_IO_OP_READ:
94*94c4a1e1SFrank Piva return NBD_CMD_READ;
95*94c4a1e1SFrank Piva default:
96*94c4a1e1SFrank Piva return -1;
97*94c4a1e1SFrank Piva }
98*94c4a1e1SFrank Piva }
99*94c4a1e1SFrank Piva
is_recv_io(const struct ublksrv_queue * q,const struct ublk_io_data * data)100*94c4a1e1SFrank Piva static inline bool is_recv_io(const struct ublksrv_queue *q,
101*94c4a1e1SFrank Piva const struct ublk_io_data *data)
102*94c4a1e1SFrank Piva {
103*94c4a1e1SFrank Piva return data->tag >= q->q_depth;
104*94c4a1e1SFrank Piva }
105*94c4a1e1SFrank Piva
106*94c4a1e1SFrank Piva #define NBD_COOKIE_BITS 32
nbd_cmd_handle(const struct ublksrv_queue * q,const struct ublk_io_data * data,const struct nbd_io_data * nbd_data)107*94c4a1e1SFrank Piva static inline u64 nbd_cmd_handle(const struct ublksrv_queue *q,
108*94c4a1e1SFrank Piva const struct ublk_io_data *data,
109*94c4a1e1SFrank Piva const struct nbd_io_data *nbd_data)
110*94c4a1e1SFrank Piva {
111*94c4a1e1SFrank Piva u64 cookie = nbd_data->cmd_cookie;
112*94c4a1e1SFrank Piva
113*94c4a1e1SFrank Piva return (cookie << NBD_COOKIE_BITS) | ublk_unique_tag(q->q_id, data->tag);
114*94c4a1e1SFrank Piva }
115*94c4a1e1SFrank Piva
nbd_handle_to_cookie(u64 handle)116*94c4a1e1SFrank Piva static inline u32 nbd_handle_to_cookie(u64 handle)
117*94c4a1e1SFrank Piva {
118*94c4a1e1SFrank Piva return (u32)(handle >> NBD_COOKIE_BITS);
119*94c4a1e1SFrank Piva }
120*94c4a1e1SFrank Piva
nbd_handle_to_tag(u64 handle)121*94c4a1e1SFrank Piva static inline u32 nbd_handle_to_tag(u64 handle)
122*94c4a1e1SFrank Piva {
123*94c4a1e1SFrank Piva return (u32)handle;
124*94c4a1e1SFrank Piva }
125*94c4a1e1SFrank Piva
__nbd_build_req(const struct ublksrv_queue * q,const struct ublk_io_data * data,const struct nbd_io_data * nbd_data,u32 type,struct nbd_request * req)126*94c4a1e1SFrank Piva static inline void __nbd_build_req(const struct ublksrv_queue *q,
127*94c4a1e1SFrank Piva const struct ublk_io_data *data,
128*94c4a1e1SFrank Piva const struct nbd_io_data *nbd_data,
129*94c4a1e1SFrank Piva u32 type, struct nbd_request *req)
130*94c4a1e1SFrank Piva {
131*94c4a1e1SFrank Piva u32 nbd_cmd_flags = 0;
132*94c4a1e1SFrank Piva u64 handle;
133*94c4a1e1SFrank Piva
134*94c4a1e1SFrank Piva if (data->iod->op_flags & UBLK_IO_F_FUA)
135*94c4a1e1SFrank Piva nbd_cmd_flags |= NBD_CMD_FLAG_FUA;
136*94c4a1e1SFrank Piva
137*94c4a1e1SFrank Piva req->type = htonl(type | nbd_cmd_flags);
138*94c4a1e1SFrank Piva
139*94c4a1e1SFrank Piva if (type != NBD_CMD_FLUSH) {
140*94c4a1e1SFrank Piva req->from = cpu_to_be64((u64)data->iod->start_sector << 9);
141*94c4a1e1SFrank Piva req->len = htonl(data->iod->nr_sectors << 9);
142*94c4a1e1SFrank Piva }
143*94c4a1e1SFrank Piva
144*94c4a1e1SFrank Piva handle = nbd_cmd_handle(q, data, nbd_data);
145*94c4a1e1SFrank Piva memcpy(req->handle, &handle, sizeof(handle));
146*94c4a1e1SFrank Piva }
147*94c4a1e1SFrank Piva
nbd_queue_req(const struct ublksrv_queue * q,const struct ublk_io_data * data,const struct nbd_request * req,const struct msghdr * msg)148*94c4a1e1SFrank Piva static int nbd_queue_req(const struct ublksrv_queue *q,
149*94c4a1e1SFrank Piva const struct ublk_io_data *data,
150*94c4a1e1SFrank Piva const struct nbd_request *req, const struct msghdr *msg)
151*94c4a1e1SFrank Piva {
152*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
153*94c4a1e1SFrank Piva const struct ublksrv_io_desc *iod = data->iod;
154*94c4a1e1SFrank Piva struct io_uring_sqe *sqe = io_uring_get_sqe(q->ring_ptr);
155*94c4a1e1SFrank Piva unsigned ublk_op = ublksrv_get_op(iod);
156*94c4a1e1SFrank Piva unsigned msg_flags = MSG_NOSIGNAL;
157*94c4a1e1SFrank Piva
158*94c4a1e1SFrank Piva if (!sqe) {
159*94c4a1e1SFrank Piva nbd_err("%s: get sqe failed, tag %d op %d\n",
160*94c4a1e1SFrank Piva __func__, data->tag, ublk_op);
161*94c4a1e1SFrank Piva return -ENOMEM;
162*94c4a1e1SFrank Piva }
163*94c4a1e1SFrank Piva
164*94c4a1e1SFrank Piva /*
165*94c4a1e1SFrank Piva * Always set WAITALL, so io_uring will handle retry in case of
166*94c4a1e1SFrank Piva * short send, see below link:
167*94c4a1e1SFrank Piva *
168*94c4a1e1SFrank Piva * https://lore.kernel.org/io-uring/[email protected]/
169*94c4a1e1SFrank Piva *
170*94c4a1e1SFrank Piva * note: It was added for recv* in 5.18 and send* in 5.19.
171*94c4a1e1SFrank Piva */
172*94c4a1e1SFrank Piva msg_flags |= MSG_WAITALL;
173*94c4a1e1SFrank Piva
174*94c4a1e1SFrank Piva if (ublk_op != UBLK_IO_OP_WRITE) {
175*94c4a1e1SFrank Piva io_uring_prep_send(sqe, q->q_id + 1, req,
176*94c4a1e1SFrank Piva sizeof(*req), msg_flags);
177*94c4a1e1SFrank Piva } else {
178*94c4a1e1SFrank Piva if (q_data->use_send_zc)
179*94c4a1e1SFrank Piva io_uring_prep_sendmsg_zc(sqe, q->q_id + 1, msg,
180*94c4a1e1SFrank Piva msg_flags);
181*94c4a1e1SFrank Piva else
182*94c4a1e1SFrank Piva io_uring_prep_sendmsg(sqe, q->q_id + 1, msg,
183*94c4a1e1SFrank Piva msg_flags);
184*94c4a1e1SFrank Piva }
185*94c4a1e1SFrank Piva
186*94c4a1e1SFrank Piva if (ublk_op == UBLK_IO_OP_READ)
187*94c4a1e1SFrank Piva ublk_op = NBD_OP_READ_REQ;
188*94c4a1e1SFrank Piva
189*94c4a1e1SFrank Piva /*
190*94c4a1e1SFrank Piva * The encoded nr_sectors should only be used for validating write req
191*94c4a1e1SFrank Piva * when its cqe is completed, since iod data isn't available at that time
192*94c4a1e1SFrank Piva * because request can be reused.
193*94c4a1e1SFrank Piva */
194*94c4a1e1SFrank Piva sqe->user_data = build_user_data(data->tag, ublk_op, ublk_op ==
195*94c4a1e1SFrank Piva UBLK_IO_OP_WRITE ? data->iod->nr_sectors : 0, 1);
196*94c4a1e1SFrank Piva io_uring_sqe_set_flags(sqe, /*IOSQE_CQE_SKIP_SUCCESS |*/
197*94c4a1e1SFrank Piva IOSQE_FIXED_FILE | IOSQE_IO_LINK);
198*94c4a1e1SFrank Piva q_data->last_send_sqe = sqe;
199*94c4a1e1SFrank Piva q_data->chained_send_ios += 1;
200*94c4a1e1SFrank Piva
201*94c4a1e1SFrank Piva NBD_IO_DBG("%s: queue io op %d(%llu %x %llx) ios(%u %u)"
202*94c4a1e1SFrank Piva " (qid %d tag %u, cmd_op %u target: %d, user_data %llx)\n",
203*94c4a1e1SFrank Piva __func__, ublk_op, data->iod->start_sector,
204*94c4a1e1SFrank Piva data->iod->nr_sectors, sqe->addr,
205*94c4a1e1SFrank Piva q_data->in_flight_ios, q_data->chained_send_ios,
206*94c4a1e1SFrank Piva q->q_id, data->tag, ublk_op, 1, sqe->user_data);
207*94c4a1e1SFrank Piva
208*94c4a1e1SFrank Piva return 1;
209*94c4a1e1SFrank Piva }
210*94c4a1e1SFrank Piva
__nbd_handle_io_async(const struct ublksrv_queue * q,const struct ublk_io_data * data,struct ublk_io_tgt * io)211*94c4a1e1SFrank Piva static co_io_job __nbd_handle_io_async(const struct ublksrv_queue *q,
212*94c4a1e1SFrank Piva const struct ublk_io_data *data, struct ublk_io_tgt *io)
213*94c4a1e1SFrank Piva {
214*94c4a1e1SFrank Piva int ret = -EIO;
215*94c4a1e1SFrank Piva struct nbd_request req = {.magic = htonl(NBD_REQUEST_MAGIC),};
216*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
217*94c4a1e1SFrank Piva struct nbd_io_data *nbd_data = io_tgt_to_nbd_data(io);
218*94c4a1e1SFrank Piva int type = req_to_nbd_cmd_type(data->iod);
219*94c4a1e1SFrank Piva struct iovec iov[2] = {
220*94c4a1e1SFrank Piva [0] = {
221*94c4a1e1SFrank Piva .iov_base = (void *)&req,
222*94c4a1e1SFrank Piva .iov_len = sizeof(req),
223*94c4a1e1SFrank Piva },
224*94c4a1e1SFrank Piva [1] = {
225*94c4a1e1SFrank Piva .iov_base = (void *)data->iod->addr,
226*94c4a1e1SFrank Piva .iov_len = data->iod->nr_sectors << 9,
227*94c4a1e1SFrank Piva },
228*94c4a1e1SFrank Piva };
229*94c4a1e1SFrank Piva struct msghdr msg = {
230*94c4a1e1SFrank Piva .msg_iov = iov,
231*94c4a1e1SFrank Piva .msg_iovlen = 2,
232*94c4a1e1SFrank Piva };
233*94c4a1e1SFrank Piva
234*94c4a1e1SFrank Piva if (type == -1)
235*94c4a1e1SFrank Piva goto fail;
236*94c4a1e1SFrank Piva
237*94c4a1e1SFrank Piva nbd_data->cmd_cookie += 1;
238*94c4a1e1SFrank Piva
239*94c4a1e1SFrank Piva __nbd_build_req(q, data, nbd_data, type, &req);
240*94c4a1e1SFrank Piva q_data->in_flight_ios += 1;
241*94c4a1e1SFrank Piva
242*94c4a1e1SFrank Piva nbd_data->done = 0;
243*94c4a1e1SFrank Piva
244*94c4a1e1SFrank Piva again:
245*94c4a1e1SFrank Piva ret = nbd_queue_req(q, data, &req, &msg);
246*94c4a1e1SFrank Piva if (ret < 0)
247*94c4a1e1SFrank Piva goto fail;
248*94c4a1e1SFrank Piva
249*94c4a1e1SFrank Piva co_await__suspend_always(data->tag);
250*94c4a1e1SFrank Piva if (io->tgt_io_cqe->res == -EAGAIN)
251*94c4a1e1SFrank Piva goto again;
252*94c4a1e1SFrank Piva ret = io->tgt_io_cqe->res;
253*94c4a1e1SFrank Piva fail:
254*94c4a1e1SFrank Piva if (ret < 0)
255*94c4a1e1SFrank Piva nbd_err("%s: err %d\n", __func__, ret);
256*94c4a1e1SFrank Piva else
257*94c4a1e1SFrank Piva ret += nbd_data->done;
258*94c4a1e1SFrank Piva ublksrv_complete_io(q, data->tag, ret);
259*94c4a1e1SFrank Piva q_data->in_flight_ios -= 1;
260*94c4a1e1SFrank Piva NBD_IO_DBG("%s: tag %d res %d\n", __func__, data->tag, ret);
261*94c4a1e1SFrank Piva
262*94c4a1e1SFrank Piva co_return;
263*94c4a1e1SFrank Piva }
264*94c4a1e1SFrank Piva
nbd_handle_recv_reply(const struct ublksrv_queue * q,struct nbd_io_data * nbd_data,const struct io_uring_cqe * cqe,const struct ublk_io_data ** io_data)265*94c4a1e1SFrank Piva static int nbd_handle_recv_reply(const struct ublksrv_queue *q,
266*94c4a1e1SFrank Piva struct nbd_io_data *nbd_data,
267*94c4a1e1SFrank Piva const struct io_uring_cqe *cqe,
268*94c4a1e1SFrank Piva const struct ublk_io_data **io_data)
269*94c4a1e1SFrank Piva {
270*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
271*94c4a1e1SFrank Piva const struct ublk_io_data *data;
272*94c4a1e1SFrank Piva struct ublk_io_tgt *io;
273*94c4a1e1SFrank Piva u64 handle;
274*94c4a1e1SFrank Piva int tag, hwq;
275*94c4a1e1SFrank Piva unsigned ublk_op;
276*94c4a1e1SFrank Piva int ret = -EINVAL;
277*94c4a1e1SFrank Piva
278*94c4a1e1SFrank Piva if (cqe->res < 0) {
279*94c4a1e1SFrank Piva nbd_err("%s %d: reply cqe %d\n", __func__,
280*94c4a1e1SFrank Piva __LINE__, cqe->res);
281*94c4a1e1SFrank Piva ret = cqe->res;
282*94c4a1e1SFrank Piva goto fail;
283*94c4a1e1SFrank Piva } else if (cqe->res == 0 && !nbd_data->done) {
284*94c4a1e1SFrank Piva //return 0;
285*94c4a1e1SFrank Piva nbd_err("%s %d: zero reply cqe %d %llx\n", __func__,
286*94c4a1e1SFrank Piva __LINE__, cqe->res, cqe->user_data);
287*94c4a1e1SFrank Piva }
288*94c4a1e1SFrank Piva
289*94c4a1e1SFrank Piva if (ntohl(q_data->reply.magic) != NBD_REPLY_MAGIC) {
290*94c4a1e1SFrank Piva nbd_err("%s %d: reply bad magic %x res %d\n",
291*94c4a1e1SFrank Piva __func__, __LINE__,
292*94c4a1e1SFrank Piva ntohl(q_data->reply.magic), cqe->res);
293*94c4a1e1SFrank Piva ret = -EPROTO;
294*94c4a1e1SFrank Piva goto fail;
295*94c4a1e1SFrank Piva }
296*94c4a1e1SFrank Piva
297*94c4a1e1SFrank Piva if (cqe->res + nbd_data->done != sizeof(struct nbd_reply)) {
298*94c4a1e1SFrank Piva nbd_err("%s %d: bad reply cqe %d %llx, done %u\n",
299*94c4a1e1SFrank Piva __func__, __LINE__,
300*94c4a1e1SFrank Piva cqe->res, cqe->user_data,
301*94c4a1e1SFrank Piva nbd_data->done);
302*94c4a1e1SFrank Piva }
303*94c4a1e1SFrank Piva ublk_assert(cqe->res + nbd_data->done == sizeof(struct nbd_reply));
304*94c4a1e1SFrank Piva
305*94c4a1e1SFrank Piva memcpy(&handle, q_data->reply.handle, sizeof(handle));
306*94c4a1e1SFrank Piva tag = nbd_handle_to_tag(handle);
307*94c4a1e1SFrank Piva hwq = ublk_unique_tag_to_hwq(tag);
308*94c4a1e1SFrank Piva tag = ublk_unique_tag_to_tag(tag);
309*94c4a1e1SFrank Piva
310*94c4a1e1SFrank Piva if (tag >= q->q_depth) {
311*94c4a1e1SFrank Piva nbd_err("%s %d: tag is too big %d\n", __func__,
312*94c4a1e1SFrank Piva __LINE__, tag);
313*94c4a1e1SFrank Piva goto fail;
314*94c4a1e1SFrank Piva }
315*94c4a1e1SFrank Piva
316*94c4a1e1SFrank Piva if (hwq != q->q_id) {
317*94c4a1e1SFrank Piva nbd_err("%s %d: hwq is too big %d\n", __func__,
318*94c4a1e1SFrank Piva __LINE__, hwq);
319*94c4a1e1SFrank Piva goto fail;
320*94c4a1e1SFrank Piva }
321*94c4a1e1SFrank Piva
322*94c4a1e1SFrank Piva data = ublksrv_queue_get_io_data(q, tag);
323*94c4a1e1SFrank Piva io = __ublk_get_io_tgt_data(data);
324*94c4a1e1SFrank Piva nbd_data = io_tgt_to_nbd_data(io);
325*94c4a1e1SFrank Piva if (nbd_data->cmd_cookie != nbd_handle_to_cookie(handle)) {
326*94c4a1e1SFrank Piva nbd_err("%s %d: cookie not match tag %d: %x %lx\n",
327*94c4a1e1SFrank Piva __func__, __LINE__, data->tag,
328*94c4a1e1SFrank Piva nbd_data->cmd_cookie, handle);
329*94c4a1e1SFrank Piva goto fail;
330*94c4a1e1SFrank Piva }
331*94c4a1e1SFrank Piva
332*94c4a1e1SFrank Piva ublk_op = ublksrv_get_op(data->iod);
333*94c4a1e1SFrank Piva if (ublk_op == UBLK_IO_OP_READ) {
334*94c4a1e1SFrank Piva *io_data = data;
335*94c4a1e1SFrank Piva return 1;
336*94c4a1e1SFrank Piva } else {
337*94c4a1e1SFrank Piva int err = ntohl(q_data->reply.error);
338*94c4a1e1SFrank Piva struct io_uring_cqe fake_cqe;
339*94c4a1e1SFrank Piva
340*94c4a1e1SFrank Piva NBD_IO_DBG("%s: got write reply, tag %d res %d\n",
341*94c4a1e1SFrank Piva __func__, data->tag, err);
342*94c4a1e1SFrank Piva
343*94c4a1e1SFrank Piva if (err) {
344*94c4a1e1SFrank Piva fake_cqe.res = -EIO;
345*94c4a1e1SFrank Piva } else {
346*94c4a1e1SFrank Piva if (ublk_op == UBLK_IO_OP_WRITE)
347*94c4a1e1SFrank Piva fake_cqe.res = data->iod->nr_sectors << 9;
348*94c4a1e1SFrank Piva else
349*94c4a1e1SFrank Piva fake_cqe.res = 0;
350*94c4a1e1SFrank Piva }
351*94c4a1e1SFrank Piva
352*94c4a1e1SFrank Piva io->tgt_io_cqe = &fake_cqe;
353*94c4a1e1SFrank Piva io->co.resume();
354*94c4a1e1SFrank Piva return 0;
355*94c4a1e1SFrank Piva }
356*94c4a1e1SFrank Piva fail:
357*94c4a1e1SFrank Piva return ret;
358*94c4a1e1SFrank Piva }
359*94c4a1e1SFrank Piva
__nbd_resume_read_req(const struct ublk_io_data * data,const struct io_uring_cqe * cqe,unsigned done)360*94c4a1e1SFrank Piva static void __nbd_resume_read_req(const struct ublk_io_data *data,
361*94c4a1e1SFrank Piva const struct io_uring_cqe *cqe, unsigned done)
362*94c4a1e1SFrank Piva {
363*94c4a1e1SFrank Piva struct ublk_io_tgt *io = __ublk_get_io_tgt_data(data);
364*94c4a1e1SFrank Piva struct nbd_io_data *nbd_data = io_tgt_to_nbd_data(io);
365*94c4a1e1SFrank Piva
366*94c4a1e1SFrank Piva nbd_data->done = done;
367*94c4a1e1SFrank Piva io->tgt_io_cqe = cqe;
368*94c4a1e1SFrank Piva io->co.resume();
369*94c4a1e1SFrank Piva }
370*94c4a1e1SFrank Piva
371*94c4a1e1SFrank Piva /* recv completion drives the whole IO flow */
nbd_start_recv(const struct ublksrv_queue * q,struct nbd_io_data * nbd_data,void * buf,int len,bool reply,unsigned done)372*94c4a1e1SFrank Piva static inline int nbd_start_recv(const struct ublksrv_queue *q,
373*94c4a1e1SFrank Piva struct nbd_io_data *nbd_data, void *buf, int len,
374*94c4a1e1SFrank Piva bool reply, unsigned done)
375*94c4a1e1SFrank Piva {
376*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
377*94c4a1e1SFrank Piva struct io_uring_sqe *sqe = io_uring_get_sqe(q->ring_ptr);
378*94c4a1e1SFrank Piva unsigned int op = reply ? NBD_OP_READ_REPLY : UBLK_IO_OP_READ;
379*94c4a1e1SFrank Piva unsigned int tag = q->q_depth; //recv always use this extra tag
380*94c4a1e1SFrank Piva
381*94c4a1e1SFrank Piva if (!sqe) {
382*94c4a1e1SFrank Piva nbd_err("%s: get sqe failed, len %d reply %d done %d\n",
383*94c4a1e1SFrank Piva __func__, len, reply, done);
384*94c4a1e1SFrank Piva return -ENOMEM;
385*94c4a1e1SFrank Piva }
386*94c4a1e1SFrank Piva
387*94c4a1e1SFrank Piva nbd_data->done = done;
388*94c4a1e1SFrank Piva io_uring_prep_recv(sqe, q->q_id + 1, (char *)buf + done, len - done, MSG_WAITALL);
389*94c4a1e1SFrank Piva io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
390*94c4a1e1SFrank Piva
391*94c4a1e1SFrank Piva /* bit63 marks us as tgt io */
392*94c4a1e1SFrank Piva sqe->user_data = build_user_data(tag, op, 0, 1);
393*94c4a1e1SFrank Piva
394*94c4a1e1SFrank Piva ublk_assert(q_data->in_flight_ios);
395*94c4a1e1SFrank Piva NBD_IO_DBG("%s: q_inflight %d queue recv %s"
396*94c4a1e1SFrank Piva "(qid %d tag %u, target: %d, user_data %llx)\n",
397*94c4a1e1SFrank Piva __func__, q_data->in_flight_ios, reply ? "reply" : "io",
398*94c4a1e1SFrank Piva q->q_id, tag, 1, sqe->user_data);
399*94c4a1e1SFrank Piva
400*94c4a1e1SFrank Piva return 0;
401*94c4a1e1SFrank Piva }
402*94c4a1e1SFrank Piva
403*94c4a1e1SFrank Piva /*
404*94c4a1e1SFrank Piva * Submit recv worker for reading nbd reply or read io data
405*94c4a1e1SFrank Piva *
406*94c4a1e1SFrank Piva * return value:
407*94c4a1e1SFrank Piva *
408*94c4a1e1SFrank Piva * 0 : queued via io_uring
409*94c4a1e1SFrank Piva * len : data read already, must be same with len
410*94c4a1e1SFrank Piva * < 0 : failure
411*94c4a1e1SFrank Piva */
nbd_do_recv(const struct ublksrv_queue * q,struct nbd_io_data * nbd_data,int fd,void * buf,unsigned len)412*94c4a1e1SFrank Piva static int nbd_do_recv(const struct ublksrv_queue *q,
413*94c4a1e1SFrank Piva struct nbd_io_data *nbd_data, int fd,
414*94c4a1e1SFrank Piva void *buf, unsigned len)
415*94c4a1e1SFrank Piva {
416*94c4a1e1SFrank Piva unsigned msg_flags = MSG_DONTWAIT | MSG_WAITALL;
417*94c4a1e1SFrank Piva int i = 0, done = 0;
418*94c4a1e1SFrank Piva const int loops = len < 512 ? 16 : 32;
419*94c4a1e1SFrank Piva int ret;
420*94c4a1e1SFrank Piva
421*94c4a1e1SFrank Piva while (i++ < loops && done < len) {
422*94c4a1e1SFrank Piva ret = recv(fd, (char *)buf + done, len - done, msg_flags);
423*94c4a1e1SFrank Piva if (ret > 0)
424*94c4a1e1SFrank Piva done += ret;
425*94c4a1e1SFrank Piva
426*94c4a1e1SFrank Piva if (!done)
427*94c4a1e1SFrank Piva break;
428*94c4a1e1SFrank Piva }
429*94c4a1e1SFrank Piva if (done == len)
430*94c4a1e1SFrank Piva return done;
431*94c4a1e1SFrank Piva
432*94c4a1e1SFrank Piva NBD_IO_DBG("%s: sync(non-blocking) recv %d(%s)/%d/%u\n",
433*94c4a1e1SFrank Piva __func__, ret, strerror(errno), done, len);
434*94c4a1e1SFrank Piva ret = nbd_start_recv(q, nbd_data, buf, len, len < 512, done);
435*94c4a1e1SFrank Piva
436*94c4a1e1SFrank Piva return ret;
437*94c4a1e1SFrank Piva }
438*94c4a1e1SFrank Piva
439*94c4a1e1SFrank Piva /*
440*94c4a1e1SFrank Piva * Every request will be responded with one reply, and we complete the
441*94c4a1e1SFrank Piva * request after the reply is received.
442*94c4a1e1SFrank Piva *
443*94c4a1e1SFrank Piva * Read request is a bit special, since the data returned are received
444*94c4a1e1SFrank Piva * with the reply together, so we have to handle read IO data here.
445*94c4a1e1SFrank Piva */
__nbd_handle_recv(const struct ublksrv_queue * q,const struct ublk_io_data * data,struct ublk_io_tgt * io)446*94c4a1e1SFrank Piva static co_io_job __nbd_handle_recv(const struct ublksrv_queue *q,
447*94c4a1e1SFrank Piva const struct ublk_io_data *data, struct ublk_io_tgt *io)
448*94c4a1e1SFrank Piva {
449*94c4a1e1SFrank Piva struct nbd_io_data *nbd_data = io_tgt_to_nbd_data(io);
450*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
451*94c4a1e1SFrank Piva int fd = q->dev->tgt.fds[q->q_id + 1];
452*94c4a1e1SFrank Piva unsigned int len;
453*94c4a1e1SFrank Piva u64 cqe_buf[2] = {0};
454*94c4a1e1SFrank Piva struct io_uring_cqe *fake_cqe = (struct io_uring_cqe *)cqe_buf;
455*94c4a1e1SFrank Piva
456*94c4a1e1SFrank Piva q_data->recv_started = 1;
457*94c4a1e1SFrank Piva
458*94c4a1e1SFrank Piva while (q_data->in_flight_ios > 0) {
459*94c4a1e1SFrank Piva const struct ublk_io_data *io_data = NULL;
460*94c4a1e1SFrank Piva int ret;
461*94c4a1e1SFrank Piva read_reply:
462*94c4a1e1SFrank Piva ret = nbd_do_recv(q, nbd_data, fd, &q_data->reply,
463*94c4a1e1SFrank Piva sizeof(q_data->reply));
464*94c4a1e1SFrank Piva if (ret == sizeof(q_data->reply)) {
465*94c4a1e1SFrank Piva nbd_data->done = ret;
466*94c4a1e1SFrank Piva fake_cqe->res = 0;
467*94c4a1e1SFrank Piva io->tgt_io_cqe = fake_cqe;
468*94c4a1e1SFrank Piva goto handle_recv;
469*94c4a1e1SFrank Piva } else if (ret < 0)
470*94c4a1e1SFrank Piva break;
471*94c4a1e1SFrank Piva
472*94c4a1e1SFrank Piva co_await__suspend_always(data->tag);
473*94c4a1e1SFrank Piva if (io->tgt_io_cqe->res == -EAGAIN)
474*94c4a1e1SFrank Piva goto read_reply;
475*94c4a1e1SFrank Piva
476*94c4a1e1SFrank Piva handle_recv:
477*94c4a1e1SFrank Piva ret = nbd_handle_recv_reply(q, nbd_data, io->tgt_io_cqe, &io_data);
478*94c4a1e1SFrank Piva if (ret < 0)
479*94c4a1e1SFrank Piva break;
480*94c4a1e1SFrank Piva if (!ret)
481*94c4a1e1SFrank Piva continue;
482*94c4a1e1SFrank Piva read_io:
483*94c4a1e1SFrank Piva ublk_assert(io_data != NULL);
484*94c4a1e1SFrank Piva
485*94c4a1e1SFrank Piva len = io_data->iod->nr_sectors << 9;
486*94c4a1e1SFrank Piva ret = nbd_do_recv(q, nbd_data, fd, (void *)io_data->iod->addr, len);
487*94c4a1e1SFrank Piva if (ret == len) {
488*94c4a1e1SFrank Piva nbd_data->done = ret;
489*94c4a1e1SFrank Piva fake_cqe->res = 0;
490*94c4a1e1SFrank Piva io->tgt_io_cqe = fake_cqe;
491*94c4a1e1SFrank Piva goto handle_read_io;
492*94c4a1e1SFrank Piva } else if (ret < 0)
493*94c4a1e1SFrank Piva break;
494*94c4a1e1SFrank Piva
495*94c4a1e1SFrank Piva /* still wait on recv coroutine context */
496*94c4a1e1SFrank Piva co_await__suspend_always(data->tag);
497*94c4a1e1SFrank Piva
498*94c4a1e1SFrank Piva ret = io->tgt_io_cqe->res;
499*94c4a1e1SFrank Piva if (ret == -EAGAIN)
500*94c4a1e1SFrank Piva goto read_io;
501*94c4a1e1SFrank Piva
502*94c4a1e1SFrank Piva handle_read_io:
503*94c4a1e1SFrank Piva __nbd_resume_read_req(io_data, io->tgt_io_cqe, nbd_data->done);
504*94c4a1e1SFrank Piva }
505*94c4a1e1SFrank Piva q_data->recv_started = 0;
506*94c4a1e1SFrank Piva co_return;
507*94c4a1e1SFrank Piva }
508*94c4a1e1SFrank Piva
nbd_handle_io_async(const struct ublksrv_queue * q,const struct ublk_io_data * data)509*94c4a1e1SFrank Piva static int nbd_handle_io_async(const struct ublksrv_queue *q,
510*94c4a1e1SFrank Piva const struct ublk_io_data *data)
511*94c4a1e1SFrank Piva {
512*94c4a1e1SFrank Piva struct ublk_io_tgt *io = __ublk_get_io_tgt_data(data);
513*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
514*94c4a1e1SFrank Piva
515*94c4a1e1SFrank Piva /*
516*94c4a1e1SFrank Piva * Put the io in the queue and submit them after
517*94c4a1e1SFrank Piva * the current chain becomes idle.
518*94c4a1e1SFrank Piva */
519*94c4a1e1SFrank Piva if (q_data->send_sqe_chain_busy)
520*94c4a1e1SFrank Piva q_data->next_chain.push_back(data);
521*94c4a1e1SFrank Piva else
522*94c4a1e1SFrank Piva io->co = __nbd_handle_io_async(q, data, io);
523*94c4a1e1SFrank Piva
524*94c4a1e1SFrank Piva return 0;
525*94c4a1e1SFrank Piva }
526*94c4a1e1SFrank Piva
527*94c4a1e1SFrank Piva /*
528*94c4a1e1SFrank Piva * Don't touch @data because the pointed ublk io request may have been
529*94c4a1e1SFrank Piva * completed before this send cqe is handled. And ublk io request completion
530*94c4a1e1SFrank Piva * is triggered by reply received from nbd server.
531*94c4a1e1SFrank Piva */
nbd_send_req_done(const struct ublksrv_queue * q,const struct ublk_io_data * data,const struct io_uring_cqe * cqe)532*94c4a1e1SFrank Piva static void nbd_send_req_done(const struct ublksrv_queue *q,
533*94c4a1e1SFrank Piva const struct ublk_io_data *data,
534*94c4a1e1SFrank Piva const struct io_uring_cqe *cqe)
535*94c4a1e1SFrank Piva {
536*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
537*94c4a1e1SFrank Piva unsigned ublk_op = user_data_to_op(cqe->user_data);
538*94c4a1e1SFrank Piva int tag = user_data_to_tag(cqe->user_data);
539*94c4a1e1SFrank Piva unsigned int nr_sects = user_data_to_tgt_data(cqe->user_data);
540*94c4a1e1SFrank Piva unsigned total;
541*94c4a1e1SFrank Piva
542*94c4a1e1SFrank Piva /* nothing to do for send_zc notification */
543*94c4a1e1SFrank Piva if (cqe->flags & IORING_CQE_F_NOTIF)
544*94c4a1e1SFrank Piva return;
545*94c4a1e1SFrank Piva
546*94c4a1e1SFrank Piva ublk_assert(q_data->chained_send_ios);
547*94c4a1e1SFrank Piva if (!--q_data->chained_send_ios) {
548*94c4a1e1SFrank Piva if (q_data->send_sqe_chain_busy)
549*94c4a1e1SFrank Piva q_data->send_sqe_chain_busy = 0;
550*94c4a1e1SFrank Piva }
551*94c4a1e1SFrank Piva
552*94c4a1e1SFrank Piva /*
553*94c4a1e1SFrank Piva * In case of failure, how to tell recv work to handle the
554*94c4a1e1SFrank Piva * request? So far just warn it, maybe nbd server will
555*94c4a1e1SFrank Piva * send one err reply.
556*94c4a1e1SFrank Piva */
557*94c4a1e1SFrank Piva if (cqe->res < 0)
558*94c4a1e1SFrank Piva nbd_err("%s: tag %d cqe fail %d %llx\n",
559*94c4a1e1SFrank Piva __func__, tag, cqe->res, cqe->user_data);
560*94c4a1e1SFrank Piva
561*94c4a1e1SFrank Piva /*
562*94c4a1e1SFrank Piva * We have set MSG_WAITALL, so short send shouldn't be possible,
563*94c4a1e1SFrank Piva * but just warn in case of io_uring regression
564*94c4a1e1SFrank Piva */
565*94c4a1e1SFrank Piva if (ublk_op == UBLK_IO_OP_WRITE)
566*94c4a1e1SFrank Piva total = sizeof(nbd_request) + (nr_sects << 9);
567*94c4a1e1SFrank Piva else
568*94c4a1e1SFrank Piva total = sizeof(nbd_request);
569*94c4a1e1SFrank Piva if (cqe->res < total)
570*94c4a1e1SFrank Piva nbd_err("%s: short send/receive tag %d op %d %llx, len %u written %u cqe flags %x\n",
571*94c4a1e1SFrank Piva __func__, tag, ublk_op, cqe->user_data,
572*94c4a1e1SFrank Piva total, cqe->res, cqe->flags);
573*94c4a1e1SFrank Piva }
574*94c4a1e1SFrank Piva
nbd_tgt_io_done(const struct ublksrv_queue * q,const struct ublk_io_data * data,const struct io_uring_cqe * cqe)575*94c4a1e1SFrank Piva static void nbd_tgt_io_done(const struct ublksrv_queue *q,
576*94c4a1e1SFrank Piva const struct ublk_io_data *data,
577*94c4a1e1SFrank Piva const struct io_uring_cqe *cqe)
578*94c4a1e1SFrank Piva {
579*94c4a1e1SFrank Piva int tag = user_data_to_tag(cqe->user_data);
580*94c4a1e1SFrank Piva
581*94c4a1e1SFrank Piva ublk_assert(tag == data->tag);
582*94c4a1e1SFrank Piva #if NBD_DEBUG_CQE == 1
583*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
584*94c4a1e1SFrank Piva nbd_err("%s: tag %d queue(ios %u %u) cqe(res %d flags %x user data %llx)\n",
585*94c4a1e1SFrank Piva __func__, tag,
586*94c4a1e1SFrank Piva q_data->in_flight_ios, q_data->chained_send_ios,
587*94c4a1e1SFrank Piva cqe->res, cqe->flags, cqe->user_data);
588*94c4a1e1SFrank Piva #endif
589*94c4a1e1SFrank Piva
590*94c4a1e1SFrank Piva /* both reply and read io is done in recv io coroutine */
591*94c4a1e1SFrank Piva if (is_recv_io(q, data)) {
592*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
593*94c4a1e1SFrank Piva
594*94c4a1e1SFrank Piva /*
595*94c4a1e1SFrank Piva * Delay recv data handling into nbd_handle_io_bg(), so
596*94c4a1e1SFrank Piva * any recv sqe won't cut in the send sqe chain.
597*94c4a1e1SFrank Piva *
598*94c4a1e1SFrank Piva * So far, recv is strictly serialized, so saving
599*94c4a1e1SFrank Piva * this single cqe works; in the future, if
600*94c4a1e1SFrank Piva * recv becomes batched, here has to be fixed
601*94c4a1e1SFrank Piva */
602*94c4a1e1SFrank Piva q_data->recv_cqe = *cqe;
603*94c4a1e1SFrank Piva q_data->need_handle_recv = 1;
604*94c4a1e1SFrank Piva return;
605*94c4a1e1SFrank Piva }
606*94c4a1e1SFrank Piva
607*94c4a1e1SFrank Piva nbd_send_req_done(q, data, cqe);
608*94c4a1e1SFrank Piva }
609*94c4a1e1SFrank Piva
nbd_handle_send_bg(const struct ublksrv_queue * q,struct nbd_queue_data * q_data)610*94c4a1e1SFrank Piva static void nbd_handle_send_bg(const struct ublksrv_queue *q,
611*94c4a1e1SFrank Piva struct nbd_queue_data *q_data)
612*94c4a1e1SFrank Piva {
613*94c4a1e1SFrank Piva if (!q_data->send_sqe_chain_busy) {
614*94c4a1e1SFrank Piva std::vector<const struct ublk_io_data *> &ios =
615*94c4a1e1SFrank Piva q_data->next_chain;
616*94c4a1e1SFrank Piva
617*94c4a1e1SFrank Piva for (auto it = ios.cbegin(); it != ios.cend(); ++it) {
618*94c4a1e1SFrank Piva auto data = *it;
619*94c4a1e1SFrank Piva struct ublk_io_tgt *io = __ublk_get_io_tgt_data(data);
620*94c4a1e1SFrank Piva
621*94c4a1e1SFrank Piva ublk_assert(data->tag < q->q_depth);
622*94c4a1e1SFrank Piva io->co = __nbd_handle_io_async(q, data, io);
623*94c4a1e1SFrank Piva }
624*94c4a1e1SFrank Piva
625*94c4a1e1SFrank Piva ios.clear();
626*94c4a1e1SFrank Piva
627*94c4a1e1SFrank Piva if (q_data->chained_send_ios && !q_data->send_sqe_chain_busy)
628*94c4a1e1SFrank Piva q_data->send_sqe_chain_busy = 1;
629*94c4a1e1SFrank Piva }
630*94c4a1e1SFrank Piva if (q_data->last_send_sqe) {
631*94c4a1e1SFrank Piva q_data->last_send_sqe->flags &= ~IOSQE_IO_LINK;
632*94c4a1e1SFrank Piva q_data->last_send_sqe = NULL;
633*94c4a1e1SFrank Piva }
634*94c4a1e1SFrank Piva }
635*94c4a1e1SFrank Piva
nbd_handle_recv_bg(const struct ublksrv_queue * q,struct nbd_queue_data * q_data)636*94c4a1e1SFrank Piva static void nbd_handle_recv_bg(const struct ublksrv_queue *q,
637*94c4a1e1SFrank Piva struct nbd_queue_data *q_data)
638*94c4a1e1SFrank Piva {
639*94c4a1e1SFrank Piva if (q_data->in_flight_ios && !q_data->recv_started) {
640*94c4a1e1SFrank Piva const struct ublk_io_data *data =
641*94c4a1e1SFrank Piva ublksrv_queue_get_io_data(q, q->q_depth);
642*94c4a1e1SFrank Piva struct ublk_io_tgt *io = __ublk_get_io_tgt_data(data);
643*94c4a1e1SFrank Piva
644*94c4a1e1SFrank Piva ublk_assert(data->tag == q->q_depth);
645*94c4a1e1SFrank Piva
646*94c4a1e1SFrank Piva io->co = __nbd_handle_recv(q, data, io);
647*94c4a1e1SFrank Piva }
648*94c4a1e1SFrank Piva
649*94c4a1e1SFrank Piva /* reply or read io data is comming */
650*94c4a1e1SFrank Piva if (q_data->need_handle_recv) {
651*94c4a1e1SFrank Piva const struct ublk_io_data *data =
652*94c4a1e1SFrank Piva ublksrv_queue_get_io_data(q, q->q_depth);
653*94c4a1e1SFrank Piva struct ublk_io_tgt *io = __ublk_get_io_tgt_data(data);
654*94c4a1e1SFrank Piva
655*94c4a1e1SFrank Piva ublk_assert(data->tag == q->q_depth);
656*94c4a1e1SFrank Piva
657*94c4a1e1SFrank Piva io->tgt_io_cqe = &q_data->recv_cqe;
658*94c4a1e1SFrank Piva io->co.resume();
659*94c4a1e1SFrank Piva q_data->need_handle_recv = 0;
660*94c4a1e1SFrank Piva }
661*94c4a1e1SFrank Piva }
662*94c4a1e1SFrank Piva
__nbd_handle_io_bg(const struct ublksrv_queue * q,struct nbd_queue_data * q_data)663*94c4a1e1SFrank Piva static void __nbd_handle_io_bg(const struct ublksrv_queue *q,
664*94c4a1e1SFrank Piva struct nbd_queue_data *q_data)
665*94c4a1e1SFrank Piva {
666*94c4a1e1SFrank Piva nbd_handle_send_bg(q, q_data);
667*94c4a1e1SFrank Piva
668*94c4a1e1SFrank Piva /* stop to queue send now since we need to recv now */
669*94c4a1e1SFrank Piva if (q_data->chained_send_ios && !q_data->send_sqe_chain_busy)
670*94c4a1e1SFrank Piva q_data->send_sqe_chain_busy = 1;
671*94c4a1e1SFrank Piva
672*94c4a1e1SFrank Piva /*
673*94c4a1e1SFrank Piva * recv SQE can't cut in send SQE chain, so it has to be
674*94c4a1e1SFrank Piva * moved here after the send SQE chain is built
675*94c4a1e1SFrank Piva *
676*94c4a1e1SFrank Piva * Also queuing ublk io command may allocate sqe too.
677*94c4a1e1SFrank Piva */
678*94c4a1e1SFrank Piva nbd_handle_recv_bg(q, q_data);
679*94c4a1e1SFrank Piva }
680*94c4a1e1SFrank Piva
681*94c4a1e1SFrank Piva /*
682*94c4a1e1SFrank Piva * The initial send request batch should be in same send sqe batch, before
683*94c4a1e1SFrank Piva * this batch isn't done, all new send requests are staggered into next_chain
684*94c4a1e1SFrank Piva * which will be flushed after the current chain is completed.
685*94c4a1e1SFrank Piva *
686*94c4a1e1SFrank Piva * Also recv work is always started after send requests are queued, because
687*94c4a1e1SFrank Piva * the recv sqe may cut the send sqe chain, and the ublk io cmd sqe may cut
688*94c4a1e1SFrank Piva * the send sqe chain too.
689*94c4a1e1SFrank Piva *
690*94c4a1e1SFrank Piva * This is why nbd_handle_recv_bg() always follows nbd_handle_send_bg().
691*94c4a1e1SFrank Piva */
nbd_handle_io_bg(const struct ublksrv_queue * q,int nr_queued_io)692*94c4a1e1SFrank Piva static void nbd_handle_io_bg(const struct ublksrv_queue *q, int nr_queued_io)
693*94c4a1e1SFrank Piva {
694*94c4a1e1SFrank Piva struct nbd_queue_data *q_data = nbd_get_queue_data(q);
695*94c4a1e1SFrank Piva
696*94c4a1e1SFrank Piva NBD_IO_DBG("%s: pending ios %d/%d chain_busy %d next_chain %ld recv(%d) sqes %u\n",
697*94c4a1e1SFrank Piva __func__, q_data->in_flight_ios,
698*94c4a1e1SFrank Piva q_data->chained_send_ios,
699*94c4a1e1SFrank Piva q_data->send_sqe_chain_busy,
700*94c4a1e1SFrank Piva q_data->next_chain.size(),
701*94c4a1e1SFrank Piva q_data->recv_started,
702*94c4a1e1SFrank Piva nr_queued_io);
703*94c4a1e1SFrank Piva
704*94c4a1e1SFrank Piva __nbd_handle_io_bg(q, q_data);
705*94c4a1e1SFrank Piva
706*94c4a1e1SFrank Piva /*
707*94c4a1e1SFrank Piva * io can be completed in recv work since we do sync recv, so
708*94c4a1e1SFrank Piva * io could be completed before the send seq's cqe is returned.
709*94c4a1e1SFrank Piva *
710*94c4a1e1SFrank Piva * When this happens, simply clear chain busy, so that we can
711*94c4a1e1SFrank Piva * queue more requests.
712*94c4a1e1SFrank Piva */
713*94c4a1e1SFrank Piva if (!q_data->in_flight_ios && q_data->send_sqe_chain_busy) {
714*94c4a1e1SFrank Piva /* all inflight ios are done, so it is safe to send request */
715*94c4a1e1SFrank Piva q_data->send_sqe_chain_busy = 0;
716*94c4a1e1SFrank Piva
717*94c4a1e1SFrank Piva if (!q_data->next_chain.empty())
718*94c4a1e1SFrank Piva __nbd_handle_io_bg(q, q_data);
719*94c4a1e1SFrank Piva }
720*94c4a1e1SFrank Piva
721*94c4a1e1SFrank Piva if (!q_data->recv_started && !q_data->send_sqe_chain_busy &&
722*94c4a1e1SFrank Piva !q_data->next_chain.empty())
723*94c4a1e1SFrank Piva nbd_err("%s: hang risk: pending ios %d/%d\n",
724*94c4a1e1SFrank Piva __func__, q_data->in_flight_ios,
725*94c4a1e1SFrank Piva q_data->chained_send_ios);
726*94c4a1e1SFrank Piva }
727*94c4a1e1SFrank Piva
nbd_usage_for_add(void)728*94c4a1e1SFrank Piva static void nbd_usage_for_add(void)
729*94c4a1e1SFrank Piva {
730*94c4a1e1SFrank Piva printf(" nbd: --host=$HOST [--port=$PORT] | --unix=$UNIX_PATH\n");
731*94c4a1e1SFrank Piva }
732*94c4a1e1SFrank Piva
nbd_init_queue(const struct ublksrv_queue * q,void ** queue_data_ptr)733*94c4a1e1SFrank Piva static int nbd_init_queue(const struct ublksrv_queue *q,
734*94c4a1e1SFrank Piva void **queue_data_ptr)
735*94c4a1e1SFrank Piva {
736*94c4a1e1SFrank Piva struct nbd_queue_data *data =
737*94c4a1e1SFrank Piva (struct nbd_queue_data *)calloc(sizeof(*data), 1);
738*94c4a1e1SFrank Piva struct nbd_tgt_data *ddata = (struct nbd_tgt_data*)q->dev->tgt.tgt_data;
739*94c4a1e1SFrank Piva
740*94c4a1e1SFrank Piva if (!data)
741*94c4a1e1SFrank Piva return -ENOMEM;
742*94c4a1e1SFrank Piva
743*94c4a1e1SFrank Piva data->next_chain.clear();
744*94c4a1e1SFrank Piva data->use_send_zc = ddata->unix_sock ? false : ddata->use_send_zc;
745*94c4a1e1SFrank Piva data->use_unix_sock = ddata->unix_sock;
746*94c4a1e1SFrank Piva data->recv_started = 0;
747*94c4a1e1SFrank Piva //nbd_err("%s send zc %d\n", __func__, data->use_send_zc);
748*94c4a1e1SFrank Piva
749*94c4a1e1SFrank Piva *queue_data_ptr = (void *)data;
750*94c4a1e1SFrank Piva return 0;
751*94c4a1e1SFrank Piva }
752*94c4a1e1SFrank Piva
nbd_deinit_queue(const struct ublksrv_queue * q)753*94c4a1e1SFrank Piva static void nbd_deinit_queue(const struct ublksrv_queue *q)
754*94c4a1e1SFrank Piva {
755*94c4a1e1SFrank Piva struct nbd_queue_data *data = nbd_get_queue_data(q);
756*94c4a1e1SFrank Piva
757*94c4a1e1SFrank Piva free(data);
758*94c4a1e1SFrank Piva }
759*94c4a1e1SFrank Piva
nbd_deinit_tgt(const struct ublksrv_dev * dev)760*94c4a1e1SFrank Piva static void nbd_deinit_tgt(const struct ublksrv_dev *dev)
761*94c4a1e1SFrank Piva {
762*94c4a1e1SFrank Piva const struct ublksrv_tgt_info *tgt = &dev->tgt;
763*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
764*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
765*94c4a1e1SFrank Piva int i;
766*94c4a1e1SFrank Piva
767*94c4a1e1SFrank Piva free(tgt->tgt_data);
768*94c4a1e1SFrank Piva
769*94c4a1e1SFrank Piva for (i = 0; i < info->nr_hw_queues; i++) {
770*94c4a1e1SFrank Piva int fd = tgt->fds[i + 1];
771*94c4a1e1SFrank Piva
772*94c4a1e1SFrank Piva shutdown(fd, SHUT_RDWR);
773*94c4a1e1SFrank Piva close(fd);
774*94c4a1e1SFrank Piva }
775*94c4a1e1SFrank Piva }
776*94c4a1e1SFrank Piva
nbd_setup_tgt(struct ublksrv_dev * dev,int type,bool recovery,const char * jbuf,uint16_t * flags)777*94c4a1e1SFrank Piva static int nbd_setup_tgt(struct ublksrv_dev *dev, int type, bool recovery,
778*94c4a1e1SFrank Piva const char *jbuf, uint16_t *flags)
779*94c4a1e1SFrank Piva {
780*94c4a1e1SFrank Piva struct ublksrv_tgt_info *tgt = &dev->tgt;
781*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
782*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
783*94c4a1e1SFrank Piva int i;
784*94c4a1e1SFrank Piva struct nbd_tgt_data *data = (struct nbd_tgt_data *)dev->tgt.tgt_data;
785*94c4a1e1SFrank Piva
786*94c4a1e1SFrank Piva const char *port = NBD_DEFAULT_PORT;
787*94c4a1e1SFrank Piva uint16_t needed_flags = 0;
788*94c4a1e1SFrank Piva uint32_t cflags = NBD_FLAG_C_FIXED_NEWSTYLE;
789*94c4a1e1SFrank Piva
790*94c4a1e1SFrank Piva char host_name[NBD_MAX_NAME] = {0};
791*94c4a1e1SFrank Piva char exp_name[NBD_MAX_NAME] = {0};
792*94c4a1e1SFrank Piva char unix_path[NBD_MAX_NAME] = {0};
793*94c4a1e1SFrank Piva u64 size64 = 0;
794*94c4a1e1SFrank Piva bool can_opt_go = true;
795*94c4a1e1SFrank Piva
796*94c4a1e1SFrank Piva /* todo: support tls */
797*94c4a1e1SFrank Piva char *certfile = NULL;
798*94c4a1e1SFrank Piva char *keyfile = NULL;
799*94c4a1e1SFrank Piva char *cacertfile = NULL;
800*94c4a1e1SFrank Piva char *tlshostname = NULL;
801*94c4a1e1SFrank Piva bool tls = false;
802*94c4a1e1SFrank Piva
803*94c4a1e1SFrank Piva long send_zc = 0;
804*94c4a1e1SFrank Piva
805*94c4a1e1SFrank Piva
806*94c4a1e1SFrank Piva if (info->flags & UBLK_F_USER_COPY)
807*94c4a1e1SFrank Piva return -EINVAL;
808*94c4a1e1SFrank Piva
809*94c4a1e1SFrank Piva ublk_assert(jbuf);
810*94c4a1e1SFrank Piva ublk_assert(type == UBLKSRV_TGT_TYPE_NBD);
811*94c4a1e1SFrank Piva ublk_assert(!recovery || info->state == UBLK_S_DEV_QUIESCED);
812*94c4a1e1SFrank Piva
813*94c4a1e1SFrank Piva ublksrv_json_read_target_str_info(jbuf, NBD_MAX_NAME, "host",
814*94c4a1e1SFrank Piva host_name);
815*94c4a1e1SFrank Piva ublksrv_json_read_target_str_info(jbuf, NBD_MAX_NAME, "unix",
816*94c4a1e1SFrank Piva unix_path);
817*94c4a1e1SFrank Piva ublksrv_json_read_target_str_info(jbuf, NBD_MAX_NAME, "export_name",
818*94c4a1e1SFrank Piva exp_name);
819*94c4a1e1SFrank Piva ublksrv_json_read_target_ulong_info(jbuf, "send_zc", &send_zc);
820*94c4a1e1SFrank Piva
821*94c4a1e1SFrank Piva NBD_HS_DBG("%s: host %s unix %s exp_name %s send_zc\n", __func__,
822*94c4a1e1SFrank Piva host_name, unix_path, exp_name, send_zc);
823*94c4a1e1SFrank Piva for (i = 0; i < info->nr_hw_queues; i++) {
824*94c4a1e1SFrank Piva int sock;
825*94c4a1e1SFrank Piva unsigned int opts = 0;
826*94c4a1e1SFrank Piva
827*94c4a1e1SFrank Piva if (strlen(unix_path))
828*94c4a1e1SFrank Piva sock = openunix(unix_path);
829*94c4a1e1SFrank Piva else
830*94c4a1e1SFrank Piva sock = opennet(host_name, port, false);
831*94c4a1e1SFrank Piva
832*94c4a1e1SFrank Piva if (sock >= 0)
833*94c4a1e1SFrank Piva negotiate(&sock, &size64, flags, exp_name,
834*94c4a1e1SFrank Piva needed_flags, cflags, opts, certfile,
835*94c4a1e1SFrank Piva keyfile, cacertfile, tlshostname, tls,
836*94c4a1e1SFrank Piva can_opt_go);
837*94c4a1e1SFrank Piva else
838*94c4a1e1SFrank Piva ublk_err("%s: open socket failed %d\n", __func__, sock);
839*94c4a1e1SFrank Piva
840*94c4a1e1SFrank Piva tgt->fds[i + 1] = sock;
841*94c4a1e1SFrank Piva NBD_HS_DBG("%s:qid %d %s-%s size %luMB flags %x sock %d\n",
842*94c4a1e1SFrank Piva __func__, i, host_name, port,
843*94c4a1e1SFrank Piva size64 >> 20, *flags, sock);
844*94c4a1e1SFrank Piva }
845*94c4a1e1SFrank Piva
846*94c4a1e1SFrank Piva tgt->dev_size = size64;
847*94c4a1e1SFrank Piva
848*94c4a1e1SFrank Piva /*
849*94c4a1e1SFrank Piva * one extra slot for receiving reply & read io, so
850*94c4a1e1SFrank Piva * the preferred queue depth should be 127 or 255,
851*94c4a1e1SFrank Piva * then half of SQ memory consumption can be saved
852*94c4a1e1SFrank Piva * especially we use IORING_SETUP_SQE128
853*94c4a1e1SFrank Piva */
854*94c4a1e1SFrank Piva tgt->tgt_ring_depth = info->queue_depth + 1;
855*94c4a1e1SFrank Piva tgt->nr_fds = info->nr_hw_queues;
856*94c4a1e1SFrank Piva tgt->extra_ios = 1; //one extra slot for receiving nbd reply
857*94c4a1e1SFrank Piva data->unix_sock = strlen(unix_path) > 0 ? true : false;
858*94c4a1e1SFrank Piva data->use_send_zc = !!send_zc;
859*94c4a1e1SFrank Piva
860*94c4a1e1SFrank Piva tgt->io_data_size = sizeof(struct ublk_io_tgt) +
861*94c4a1e1SFrank Piva sizeof(struct nbd_io_data);
862*94c4a1e1SFrank Piva
863*94c4a1e1SFrank Piva ublksrv_dev_set_cq_depth(dev, 2 * tgt->tgt_ring_depth);
864*94c4a1e1SFrank Piva
865*94c4a1e1SFrank Piva return 0;
866*94c4a1e1SFrank Piva }
867*94c4a1e1SFrank Piva
nbd_parse_flags(struct ublk_params * p,uint16_t flags,uint32_t bs)868*94c4a1e1SFrank Piva static void nbd_parse_flags(struct ublk_params *p, uint16_t flags, uint32_t bs)
869*94c4a1e1SFrank Piva {
870*94c4a1e1SFrank Piva __u32 attrs = 0;
871*94c4a1e1SFrank Piva
872*94c4a1e1SFrank Piva NBD_HS_DBG("%s: negotiated flags %x\n", __func__, flags);
873*94c4a1e1SFrank Piva
874*94c4a1e1SFrank Piva if (flags & NBD_FLAG_READ_ONLY)
875*94c4a1e1SFrank Piva attrs |= UBLK_ATTR_READ_ONLY;
876*94c4a1e1SFrank Piva if (flags & NBD_FLAG_SEND_FLUSH) {
877*94c4a1e1SFrank Piva if (flags & NBD_FLAG_SEND_FUA)
878*94c4a1e1SFrank Piva attrs |= UBLK_ATTR_FUA;
879*94c4a1e1SFrank Piva else
880*94c4a1e1SFrank Piva attrs |= UBLK_ATTR_VOLATILE_CACHE;
881*94c4a1e1SFrank Piva }
882*94c4a1e1SFrank Piva
883*94c4a1e1SFrank Piva p->basic.attrs |= attrs;
884*94c4a1e1SFrank Piva
885*94c4a1e1SFrank Piva if (flags & NBD_FLAG_SEND_TRIM) {
886*94c4a1e1SFrank Piva p->discard.discard_granularity = bs;
887*94c4a1e1SFrank Piva p->discard.max_discard_sectors = UINT_MAX >> 9;
888*94c4a1e1SFrank Piva p->discard.max_discard_segments = 1;
889*94c4a1e1SFrank Piva p->types |= UBLK_PARAM_TYPE_DISCARD;
890*94c4a1e1SFrank Piva }
891*94c4a1e1SFrank Piva }
892*94c4a1e1SFrank Piva
nbd_init_tgt(struct ublksrv_dev * dev,int type,int argc,char * argv[])893*94c4a1e1SFrank Piva static int nbd_init_tgt(struct ublksrv_dev *dev, int type, int argc,
894*94c4a1e1SFrank Piva char *argv[])
895*94c4a1e1SFrank Piva {
896*94c4a1e1SFrank Piva int send_zc = 0;
897*94c4a1e1SFrank Piva int read_only = 0;
898*94c4a1e1SFrank Piva static const struct option nbd_longopts[] = {
899*94c4a1e1SFrank Piva { "host", required_argument, 0, 0},
900*94c4a1e1SFrank Piva { "unix", required_argument, 0, 0},
901*94c4a1e1SFrank Piva { "export_name", required_argument, 0, 0},
902*94c4a1e1SFrank Piva { "send_zc", 0, &send_zc, 1},
903*94c4a1e1SFrank Piva { "read_only", 0, &read_only, 1},
904*94c4a1e1SFrank Piva { NULL }
905*94c4a1e1SFrank Piva };
906*94c4a1e1SFrank Piva struct ublksrv_tgt_info *tgt = &dev->tgt;
907*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
908*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
909*94c4a1e1SFrank Piva int jbuf_size;
910*94c4a1e1SFrank Piva char *jbuf = ublksrv_tgt_return_json_buf(dev, &jbuf_size);
911*94c4a1e1SFrank Piva struct ublksrv_tgt_base_json tgt_json = {
912*94c4a1e1SFrank Piva .type = type,
913*94c4a1e1SFrank Piva };
914*94c4a1e1SFrank Piva int opt;
915*94c4a1e1SFrank Piva int option_index = 0;
916*94c4a1e1SFrank Piva unsigned char bs_shift = 9;
917*94c4a1e1SFrank Piva const char *host_name = NULL;
918*94c4a1e1SFrank Piva const char *unix_path = NULL;
919*94c4a1e1SFrank Piva const char *exp_name = NULL;
920*94c4a1e1SFrank Piva uint16_t flags = 0;
921*94c4a1e1SFrank Piva int ret;
922*94c4a1e1SFrank Piva
923*94c4a1e1SFrank Piva strcpy(tgt_json.name, "nbd");
924*94c4a1e1SFrank Piva
925*94c4a1e1SFrank Piva if (type != UBLKSRV_TGT_TYPE_NBD)
926*94c4a1e1SFrank Piva return -1;
927*94c4a1e1SFrank Piva
928*94c4a1e1SFrank Piva while ((opt = getopt_long(argc, argv, "-:f:",
929*94c4a1e1SFrank Piva nbd_longopts, &option_index)) != -1) {
930*94c4a1e1SFrank Piva if (opt < 0)
931*94c4a1e1SFrank Piva break;
932*94c4a1e1SFrank Piva if (opt > 0)
933*94c4a1e1SFrank Piva continue;
934*94c4a1e1SFrank Piva
935*94c4a1e1SFrank Piva if (!strcmp(nbd_longopts[option_index].name, "host"))
936*94c4a1e1SFrank Piva host_name = optarg;
937*94c4a1e1SFrank Piva if (!strcmp(nbd_longopts[option_index].name, "unix"))
938*94c4a1e1SFrank Piva unix_path = optarg;
939*94c4a1e1SFrank Piva if (!strcmp(nbd_longopts[option_index].name, "export_name"))
940*94c4a1e1SFrank Piva exp_name = optarg;
941*94c4a1e1SFrank Piva }
942*94c4a1e1SFrank Piva
943*94c4a1e1SFrank Piva #ifndef HAVE_LIBURING_SEND_ZC
944*94c4a1e1SFrank Piva if (send_zc)
945*94c4a1e1SFrank Piva return -EINVAL;
946*94c4a1e1SFrank Piva #endif
947*94c4a1e1SFrank Piva
948*94c4a1e1SFrank Piva ublk_json_write_dev_info(dev, &jbuf, &jbuf_size);
949*94c4a1e1SFrank Piva ublk_json_write_tgt_str(dev, &jbuf, &jbuf_size, "host", host_name);
950*94c4a1e1SFrank Piva ublk_json_write_tgt_str(dev, &jbuf, &jbuf_size, "unix", unix_path);
951*94c4a1e1SFrank Piva ublk_json_write_tgt_str(dev, &jbuf, &jbuf_size, "export_name", exp_name);
952*94c4a1e1SFrank Piva ublk_json_write_tgt_long(dev, &jbuf, &jbuf_size, "send_zc", send_zc);
953*94c4a1e1SFrank Piva
954*94c4a1e1SFrank Piva tgt->tgt_data = calloc(sizeof(struct nbd_tgt_data), 1);
955*94c4a1e1SFrank Piva
956*94c4a1e1SFrank Piva ret = nbd_setup_tgt(dev, type, false, jbuf, &flags);
957*94c4a1e1SFrank Piva if (ret)
958*94c4a1e1SFrank Piva return ret;
959*94c4a1e1SFrank Piva
960*94c4a1e1SFrank Piva tgt_json.dev_size = tgt->dev_size;
961*94c4a1e1SFrank Piva ublk_json_write_target_base(dev, &jbuf, &jbuf_size, &tgt_json);
962*94c4a1e1SFrank Piva
963*94c4a1e1SFrank Piva struct ublk_params p = {
964*94c4a1e1SFrank Piva .types = UBLK_PARAM_TYPE_BASIC,
965*94c4a1e1SFrank Piva .basic = {
966*94c4a1e1SFrank Piva .attrs = read_only ? UBLK_ATTR_READ_ONLY : 0U,
967*94c4a1e1SFrank Piva .logical_bs_shift = bs_shift,
968*94c4a1e1SFrank Piva .physical_bs_shift = 12,
969*94c4a1e1SFrank Piva .io_opt_shift = 12,
970*94c4a1e1SFrank Piva .io_min_shift = bs_shift,
971*94c4a1e1SFrank Piva .max_sectors = info->max_io_buf_bytes >> 9,
972*94c4a1e1SFrank Piva .dev_sectors = tgt->dev_size >> 9,
973*94c4a1e1SFrank Piva },
974*94c4a1e1SFrank Piva };
975*94c4a1e1SFrank Piva
976*94c4a1e1SFrank Piva nbd_parse_flags(&p, flags, 1U << bs_shift);
977*94c4a1e1SFrank Piva ublk_json_write_params(dev, &jbuf, &jbuf_size, &p);
978*94c4a1e1SFrank Piva
979*94c4a1e1SFrank Piva return 0;
980*94c4a1e1SFrank Piva }
981*94c4a1e1SFrank Piva
nbd_recovery_tgt(struct ublksrv_dev * dev,int type)982*94c4a1e1SFrank Piva static int nbd_recovery_tgt(struct ublksrv_dev *dev, int type)
983*94c4a1e1SFrank Piva {
984*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev *cdev = ublksrv_get_ctrl_dev(dev);
985*94c4a1e1SFrank Piva const char *jbuf = ublksrv_ctrl_get_recovery_jbuf(cdev);
986*94c4a1e1SFrank Piva uint16_t flags = 0;
987*94c4a1e1SFrank Piva
988*94c4a1e1SFrank Piva dev->tgt.tgt_data = calloc(sizeof(struct nbd_tgt_data), 1);
989*94c4a1e1SFrank Piva
990*94c4a1e1SFrank Piva return nbd_setup_tgt(dev, type, true, jbuf, &flags);
991*94c4a1e1SFrank Piva }
992*94c4a1e1SFrank Piva
993*94c4a1e1SFrank Piva struct ublksrv_tgt_type nbd_tgt_type = {
994*94c4a1e1SFrank Piva .handle_io_async = nbd_handle_io_async,
995*94c4a1e1SFrank Piva .tgt_io_done = nbd_tgt_io_done,
996*94c4a1e1SFrank Piva .handle_io_background = nbd_handle_io_bg,
997*94c4a1e1SFrank Piva .usage_for_add = nbd_usage_for_add,
998*94c4a1e1SFrank Piva .init_tgt = nbd_init_tgt,
999*94c4a1e1SFrank Piva .deinit_tgt = nbd_deinit_tgt,
1000*94c4a1e1SFrank Piva .type = UBLKSRV_TGT_TYPE_NBD,
1001*94c4a1e1SFrank Piva .name = "nbd",
1002*94c4a1e1SFrank Piva .recovery_tgt = nbd_recovery_tgt,
1003*94c4a1e1SFrank Piva .init_queue = nbd_init_queue,
1004*94c4a1e1SFrank Piva .deinit_queue = nbd_deinit_queue,
1005*94c4a1e1SFrank Piva };
1006*94c4a1e1SFrank Piva
1007*94c4a1e1SFrank Piva static void tgt_nbd_init() __attribute__((constructor));
1008*94c4a1e1SFrank Piva
tgt_nbd_init(void)1009*94c4a1e1SFrank Piva static void tgt_nbd_init(void)
1010*94c4a1e1SFrank Piva {
1011*94c4a1e1SFrank Piva ublksrv_register_tgt_type(&nbd_tgt_type);
1012*94c4a1e1SFrank Piva }
1013*94c4a1e1SFrank Piva
1014