xref: /aosp_15_r20/external/ublksrv/demo_event.c (revision 94c4a1e103eb1715230460aab379dff275992c20)
1*94c4a1e1SFrank Piva // SPDX-License-Identifier: MIT or GPL-2.0-only
2*94c4a1e1SFrank Piva 
3*94c4a1e1SFrank Piva #include <config.h>
4*94c4a1e1SFrank Piva 
5*94c4a1e1SFrank Piva #include <stdlib.h>
6*94c4a1e1SFrank Piva #include <stdio.h>
7*94c4a1e1SFrank Piva #include <poll.h>
8*94c4a1e1SFrank Piva #include <sys/epoll.h>
9*94c4a1e1SFrank Piva #include <sched.h>
10*94c4a1e1SFrank Piva #include <pthread.h>
11*94c4a1e1SFrank Piva #include <getopt.h>
12*94c4a1e1SFrank Piva #include <stdarg.h>
13*94c4a1e1SFrank Piva #include <errno.h>
14*94c4a1e1SFrank Piva #include <error.h>
15*94c4a1e1SFrank Piva #include <string.h>
16*94c4a1e1SFrank Piva #include <sys/ioctl.h>
17*94c4a1e1SFrank Piva #include <sys/types.h>
18*94c4a1e1SFrank Piva #include <unistd.h>
19*94c4a1e1SFrank Piva 
20*94c4a1e1SFrank Piva #include "ublksrv.h"
21*94c4a1e1SFrank Piva #include "ublksrv_utils.h"
22*94c4a1e1SFrank Piva #include "ublksrv_aio.h"
23*94c4a1e1SFrank Piva 
24*94c4a1e1SFrank Piva #define UBLKSRV_TGT_TYPE_DEMO  0
25*94c4a1e1SFrank Piva 
26*94c4a1e1SFrank Piva static bool use_aio = 0;
27*94c4a1e1SFrank Piva static int backing_fd = -1;
28*94c4a1e1SFrank Piva 
29*94c4a1e1SFrank Piva static struct ublksrv_aio_ctx *aio_ctx = NULL;
30*94c4a1e1SFrank Piva static pthread_t io_thread;
31*94c4a1e1SFrank Piva struct demo_queue_info {
32*94c4a1e1SFrank Piva 	const struct ublksrv_dev *dev;
33*94c4a1e1SFrank Piva 	const struct ublksrv_queue *q;
34*94c4a1e1SFrank Piva 	int qid;
35*94c4a1e1SFrank Piva 
36*94c4a1e1SFrank Piva 	pthread_t thread;
37*94c4a1e1SFrank Piva };
38*94c4a1e1SFrank Piva 
39*94c4a1e1SFrank Piva static struct ublksrv_ctrl_dev *this_ctrl_dev;
40*94c4a1e1SFrank Piva static const struct ublksrv_dev *this_dev;
41*94c4a1e1SFrank Piva 
42*94c4a1e1SFrank Piva static pthread_mutex_t jbuf_lock;
43*94c4a1e1SFrank Piva static char jbuf[4096];
44*94c4a1e1SFrank Piva 
sig_handler(int sig)45*94c4a1e1SFrank Piva static void sig_handler(int sig)
46*94c4a1e1SFrank Piva {
47*94c4a1e1SFrank Piva 	const struct ublksrv_queue *q = ublksrv_get_queue(this_dev, 0);
48*94c4a1e1SFrank Piva 	unsigned state = ublksrv_queue_state(q);
49*94c4a1e1SFrank Piva 
50*94c4a1e1SFrank Piva 	fprintf(stderr, "got signal %d, stopping %d\n", sig,
51*94c4a1e1SFrank Piva 			state & UBLKSRV_QUEUE_STOPPING);
52*94c4a1e1SFrank Piva 	ublksrv_ctrl_stop_dev(this_ctrl_dev);
53*94c4a1e1SFrank Piva }
54*94c4a1e1SFrank Piva 
queue_fallocate_async(struct io_uring_sqe * sqe,struct ublksrv_aio * req)55*94c4a1e1SFrank Piva static void queue_fallocate_async(struct io_uring_sqe *sqe,
56*94c4a1e1SFrank Piva 		struct ublksrv_aio *req)
57*94c4a1e1SFrank Piva {
58*94c4a1e1SFrank Piva 	__u16 ublk_op = ublksrv_get_op(&req->io);
59*94c4a1e1SFrank Piva 	__u32 flags = ublksrv_get_flags(&req->io);
60*94c4a1e1SFrank Piva 	__u32 mode = FALLOC_FL_KEEP_SIZE;
61*94c4a1e1SFrank Piva 
62*94c4a1e1SFrank Piva 	/* follow logic of linux kernel loop */
63*94c4a1e1SFrank Piva 	if (ublk_op == UBLK_IO_OP_DISCARD) {
64*94c4a1e1SFrank Piva 		mode |= FALLOC_FL_PUNCH_HOLE;
65*94c4a1e1SFrank Piva 	} else if (ublk_op == UBLK_IO_OP_WRITE_ZEROES) {
66*94c4a1e1SFrank Piva 		if (flags & UBLK_IO_F_NOUNMAP)
67*94c4a1e1SFrank Piva 			mode |= FALLOC_FL_ZERO_RANGE;
68*94c4a1e1SFrank Piva 		else
69*94c4a1e1SFrank Piva 			mode |= FALLOC_FL_PUNCH_HOLE;
70*94c4a1e1SFrank Piva 	} else {
71*94c4a1e1SFrank Piva 		mode |= FALLOC_FL_ZERO_RANGE;
72*94c4a1e1SFrank Piva 	}
73*94c4a1e1SFrank Piva 	io_uring_prep_fallocate(sqe, req->fd, mode, req->io.start_sector << 9,
74*94c4a1e1SFrank Piva 			req->io.nr_sectors << 9);
75*94c4a1e1SFrank Piva }
76*94c4a1e1SFrank Piva 
async_io_submitter(struct ublksrv_aio_ctx * ctx,struct ublksrv_aio * req)77*94c4a1e1SFrank Piva int async_io_submitter(struct ublksrv_aio_ctx *ctx,
78*94c4a1e1SFrank Piva 		struct ublksrv_aio *req)
79*94c4a1e1SFrank Piva {
80*94c4a1e1SFrank Piva 	struct io_uring *ring = (struct io_uring*)
81*94c4a1e1SFrank Piva 		ublksrv_aio_get_ctx_data(ctx);
82*94c4a1e1SFrank Piva 	const struct ublksrv_io_desc *iod = &req->io;
83*94c4a1e1SFrank Piva 	unsigned op = ublksrv_get_op(iod);
84*94c4a1e1SFrank Piva 	struct io_uring_sqe *sqe;
85*94c4a1e1SFrank Piva 
86*94c4a1e1SFrank Piva 	sqe = io_uring_get_sqe(ring);
87*94c4a1e1SFrank Piva 	if (!sqe) {
88*94c4a1e1SFrank Piva 		fprintf(stderr, "%s: uring run out of sqe\n", __func__);
89*94c4a1e1SFrank Piva 		return -ENOMEM;
90*94c4a1e1SFrank Piva 	}
91*94c4a1e1SFrank Piva 
92*94c4a1e1SFrank Piva 	if (op == -1 || req->fd < 0) {
93*94c4a1e1SFrank Piva 		fprintf(stderr, "%s: wrong op %d, fd %d, id %x\n", __func__, op,
94*94c4a1e1SFrank Piva 				req->fd, req->id);
95*94c4a1e1SFrank Piva 		return -EINVAL;
96*94c4a1e1SFrank Piva 	}
97*94c4a1e1SFrank Piva 
98*94c4a1e1SFrank Piva 	io_uring_sqe_set_data(sqe, req);
99*94c4a1e1SFrank Piva 	switch (op) {
100*94c4a1e1SFrank Piva 	case UBLK_IO_OP_DISCARD:
101*94c4a1e1SFrank Piva 	case UBLK_IO_OP_WRITE_ZEROES:
102*94c4a1e1SFrank Piva 		queue_fallocate_async(sqe, req);
103*94c4a1e1SFrank Piva 		break;
104*94c4a1e1SFrank Piva 	case UBLK_IO_OP_FLUSH:
105*94c4a1e1SFrank Piva 		io_uring_prep_fsync(sqe, req->fd, IORING_FSYNC_DATASYNC);
106*94c4a1e1SFrank Piva 		break;
107*94c4a1e1SFrank Piva 	case UBLK_IO_OP_READ:
108*94c4a1e1SFrank Piva 		io_uring_prep_read(sqe, req->fd, (void *)iod->addr,
109*94c4a1e1SFrank Piva 				iod->nr_sectors << 9, iod->start_sector << 9);
110*94c4a1e1SFrank Piva 		break;
111*94c4a1e1SFrank Piva 	case UBLK_IO_OP_WRITE:
112*94c4a1e1SFrank Piva 		io_uring_prep_write(sqe, req->fd, (void *)iod->addr,
113*94c4a1e1SFrank Piva 				iod->nr_sectors << 9, iod->start_sector << 9);
114*94c4a1e1SFrank Piva 		break;
115*94c4a1e1SFrank Piva 	default:
116*94c4a1e1SFrank Piva 		fprintf(stderr, "%s: wrong op %d, fd %d, id %x\n", __func__,
117*94c4a1e1SFrank Piva 				op, req->fd, req->id);
118*94c4a1e1SFrank Piva 		return -EINVAL;
119*94c4a1e1SFrank Piva 	}
120*94c4a1e1SFrank Piva 
121*94c4a1e1SFrank Piva 	return 0;
122*94c4a1e1SFrank Piva }
123*94c4a1e1SFrank Piva 
sync_io_submitter(struct ublksrv_aio_ctx * ctx,struct ublksrv_aio * req)124*94c4a1e1SFrank Piva int sync_io_submitter(struct ublksrv_aio_ctx *ctx,
125*94c4a1e1SFrank Piva 		struct ublksrv_aio *req)
126*94c4a1e1SFrank Piva {
127*94c4a1e1SFrank Piva 	const struct ublksrv_io_desc *iod = &req->io;
128*94c4a1e1SFrank Piva 	unsigned ublk_op = ublksrv_get_op(iod);
129*94c4a1e1SFrank Piva 	void *buf = (void *)iod->addr;
130*94c4a1e1SFrank Piva 	unsigned len = iod->nr_sectors << 9;
131*94c4a1e1SFrank Piva 	unsigned long long offset = iod->start_sector << 9;
132*94c4a1e1SFrank Piva 	int mode = FALLOC_FL_KEEP_SIZE;
133*94c4a1e1SFrank Piva 	int ret;
134*94c4a1e1SFrank Piva 
135*94c4a1e1SFrank Piva 	switch (ublk_op) {
136*94c4a1e1SFrank Piva 	case UBLK_IO_OP_READ:
137*94c4a1e1SFrank Piva 		ret = pread(req->fd, buf, len, offset);
138*94c4a1e1SFrank Piva 		break;
139*94c4a1e1SFrank Piva 	case UBLK_IO_OP_WRITE:
140*94c4a1e1SFrank Piva 		ret = pwrite(req->fd, buf, len, offset);
141*94c4a1e1SFrank Piva 		break;
142*94c4a1e1SFrank Piva 	case UBLK_IO_OP_FLUSH:
143*94c4a1e1SFrank Piva 		ret = fdatasync(req->fd);
144*94c4a1e1SFrank Piva 		break;
145*94c4a1e1SFrank Piva 	case UBLK_IO_OP_WRITE_ZEROES:
146*94c4a1e1SFrank Piva 		mode |= FALLOC_FL_ZERO_RANGE;
147*94c4a1e1SFrank Piva 	case UBLK_IO_OP_DISCARD:
148*94c4a1e1SFrank Piva 		ret = fallocate(req->fd, mode, offset, len);
149*94c4a1e1SFrank Piva 		break;
150*94c4a1e1SFrank Piva 	default:
151*94c4a1e1SFrank Piva 		fprintf(stderr, "%s: wrong op %d, fd %d, id %x\n", __func__,
152*94c4a1e1SFrank Piva 				ublk_op, req->fd, req->id);
153*94c4a1e1SFrank Piva 		return -EINVAL;
154*94c4a1e1SFrank Piva 	}
155*94c4a1e1SFrank Piva 
156*94c4a1e1SFrank Piva 	req->res = ret;
157*94c4a1e1SFrank Piva 	return 1;
158*94c4a1e1SFrank Piva }
159*94c4a1e1SFrank Piva 
io_submit_worker(struct ublksrv_aio_ctx * ctx,struct ublksrv_aio * req)160*94c4a1e1SFrank Piva static int io_submit_worker(struct ublksrv_aio_ctx *ctx,
161*94c4a1e1SFrank Piva 		struct ublksrv_aio *req)
162*94c4a1e1SFrank Piva {
163*94c4a1e1SFrank Piva 	/* simulate null target */
164*94c4a1e1SFrank Piva 	if (req->fd < 0)
165*94c4a1e1SFrank Piva 		req->res = req->io.nr_sectors << 9;
166*94c4a1e1SFrank Piva 	else
167*94c4a1e1SFrank Piva 		return sync_io_submitter(ctx, req);
168*94c4a1e1SFrank Piva 
169*94c4a1e1SFrank Piva 	return 1;
170*94c4a1e1SFrank Piva }
171*94c4a1e1SFrank Piva 
queue_event(struct ublksrv_aio_ctx * ctx)172*94c4a1e1SFrank Piva static int queue_event(struct ublksrv_aio_ctx *ctx)
173*94c4a1e1SFrank Piva {
174*94c4a1e1SFrank Piva 	struct io_uring *ring = (struct io_uring *)
175*94c4a1e1SFrank Piva 		ublksrv_aio_get_ctx_data(ctx);
176*94c4a1e1SFrank Piva 	struct io_uring_sqe *sqe;
177*94c4a1e1SFrank Piva 	int ctx_efd = ublksrv_aio_get_efd(ctx);
178*94c4a1e1SFrank Piva 
179*94c4a1e1SFrank Piva 	sqe = io_uring_get_sqe(ring);
180*94c4a1e1SFrank Piva 	if (!sqe) {
181*94c4a1e1SFrank Piva 		fprintf(stderr, "%s: uring run out of sqe\n", __func__);
182*94c4a1e1SFrank Piva 		return -1;
183*94c4a1e1SFrank Piva 	}
184*94c4a1e1SFrank Piva 
185*94c4a1e1SFrank Piva 	io_uring_prep_poll_add(sqe, ctx_efd, POLLIN);
186*94c4a1e1SFrank Piva 	io_uring_sqe_set_data64(sqe, 0);
187*94c4a1e1SFrank Piva 
188*94c4a1e1SFrank Piva 	return 0;
189*94c4a1e1SFrank Piva }
190*94c4a1e1SFrank Piva 
reap_uring(struct ublksrv_aio_ctx * ctx,struct aio_list * list,int * got_efd)191*94c4a1e1SFrank Piva static int reap_uring(struct ublksrv_aio_ctx *ctx, struct aio_list *list, int
192*94c4a1e1SFrank Piva 		*got_efd)
193*94c4a1e1SFrank Piva {
194*94c4a1e1SFrank Piva 	struct io_uring *r = (struct io_uring *)ublksrv_aio_get_ctx_data(ctx);
195*94c4a1e1SFrank Piva 	struct io_uring_cqe *cqe;
196*94c4a1e1SFrank Piva 	unsigned head;
197*94c4a1e1SFrank Piva 	int count = 0;
198*94c4a1e1SFrank Piva 
199*94c4a1e1SFrank Piva 	io_uring_for_each_cqe(r, head, cqe) {
200*94c4a1e1SFrank Piva 		if (cqe->user_data) {
201*94c4a1e1SFrank Piva 			struct ublksrv_aio *req = (struct ublksrv_aio *)
202*94c4a1e1SFrank Piva 				cqe->user_data;
203*94c4a1e1SFrank Piva 
204*94c4a1e1SFrank Piva 			if (cqe->res == -EAGAIN)
205*94c4a1e1SFrank Piva 				async_io_submitter(ctx, req);
206*94c4a1e1SFrank Piva 			else {
207*94c4a1e1SFrank Piva 				req->res = cqe->res;
208*94c4a1e1SFrank Piva 				aio_list_add(list, req);
209*94c4a1e1SFrank Piva 			}
210*94c4a1e1SFrank Piva 		} else {
211*94c4a1e1SFrank Piva 			if (cqe->res < 0)
212*94c4a1e1SFrank Piva 				fprintf(stderr, "eventfd result %d\n",
213*94c4a1e1SFrank Piva 						cqe->res);
214*94c4a1e1SFrank Piva 			*got_efd = 1;
215*94c4a1e1SFrank Piva 		}
216*94c4a1e1SFrank Piva 	        count += 1;
217*94c4a1e1SFrank Piva 	}
218*94c4a1e1SFrank Piva 	io_uring_cq_advance(r, count);
219*94c4a1e1SFrank Piva 
220*94c4a1e1SFrank Piva 	return count;
221*94c4a1e1SFrank Piva }
222*94c4a1e1SFrank Piva 
demo_event_uring_io_handler_fn(void * data)223*94c4a1e1SFrank Piva static void *demo_event_uring_io_handler_fn(void *data)
224*94c4a1e1SFrank Piva {
225*94c4a1e1SFrank Piva 	struct ublksrv_aio_ctx *ctx = (struct ublksrv_aio_ctx *)data;
226*94c4a1e1SFrank Piva 	const struct ublksrv_dev *dev = ublksrv_aio_get_dev(ctx);
227*94c4a1e1SFrank Piva 	const struct ublksrv_ctrl_dev_info *info =
228*94c4a1e1SFrank Piva 		ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
229*94c4a1e1SFrank Piva 	unsigned dev_id = info->dev_id;
230*94c4a1e1SFrank Piva 	struct io_uring ring;
231*94c4a1e1SFrank Piva 	unsigned qd;
232*94c4a1e1SFrank Piva 	int ret;
233*94c4a1e1SFrank Piva 	int ctx_efd = ublksrv_aio_get_efd(ctx);
234*94c4a1e1SFrank Piva 
235*94c4a1e1SFrank Piva 	qd = info->queue_depth * info->nr_hw_queues * 2;
236*94c4a1e1SFrank Piva 
237*94c4a1e1SFrank Piva 	io_uring_queue_init(qd, &ring, 0);
238*94c4a1e1SFrank Piva 	ret = io_uring_register_eventfd(&ring, ctx_efd);
239*94c4a1e1SFrank Piva 	if (ret) {
240*94c4a1e1SFrank Piva 		fprintf(stdout, "ublk dev %d fails to register eventfd\n",
241*94c4a1e1SFrank Piva 			dev_id);
242*94c4a1e1SFrank Piva 		return NULL;
243*94c4a1e1SFrank Piva 	}
244*94c4a1e1SFrank Piva 
245*94c4a1e1SFrank Piva 	ublksrv_aio_set_ctx_data(ctx, (void *)&ring);
246*94c4a1e1SFrank Piva 
247*94c4a1e1SFrank Piva 	fprintf(stdout, "ublk dev %d aio(io_uring submitter) context started tid %d\n",
248*94c4a1e1SFrank Piva 			dev_id, ublksrv_gettid());
249*94c4a1e1SFrank Piva 
250*94c4a1e1SFrank Piva 	queue_event(ctx);
251*94c4a1e1SFrank Piva 	io_uring_submit_and_wait(&ring, 0);
252*94c4a1e1SFrank Piva 
253*94c4a1e1SFrank Piva 	while (!ublksrv_aio_ctx_dead(ctx)) {
254*94c4a1e1SFrank Piva 		struct aio_list compl;
255*94c4a1e1SFrank Piva 		int got_efd = 0;
256*94c4a1e1SFrank Piva 
257*94c4a1e1SFrank Piva 		aio_list_init(&compl);
258*94c4a1e1SFrank Piva 		ublksrv_aio_submit_worker(ctx, async_io_submitter, &compl);
259*94c4a1e1SFrank Piva 
260*94c4a1e1SFrank Piva 		reap_uring(ctx, &compl, &got_efd);
261*94c4a1e1SFrank Piva 		ublksrv_aio_complete_worker(ctx, &compl);
262*94c4a1e1SFrank Piva 
263*94c4a1e1SFrank Piva 		if (got_efd)
264*94c4a1e1SFrank Piva 			queue_event(ctx);
265*94c4a1e1SFrank Piva 		io_uring_submit_and_wait(&ring, 1);
266*94c4a1e1SFrank Piva 	}
267*94c4a1e1SFrank Piva 
268*94c4a1e1SFrank Piva 	return NULL;
269*94c4a1e1SFrank Piva }
270*94c4a1e1SFrank Piva 
271*94c4a1e1SFrank Piva #define EPOLL_NR_EVENTS 1
demo_event_real_io_handler_fn(void * data)272*94c4a1e1SFrank Piva static void *demo_event_real_io_handler_fn(void *data)
273*94c4a1e1SFrank Piva {
274*94c4a1e1SFrank Piva 	struct ublksrv_aio_ctx *ctx = (struct ublksrv_aio_ctx *)data;
275*94c4a1e1SFrank Piva 	const struct ublksrv_dev *dev = ublksrv_aio_get_dev(ctx);
276*94c4a1e1SFrank Piva 	const struct ublksrv_ctrl_dev_info *info =
277*94c4a1e1SFrank Piva 		ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
278*94c4a1e1SFrank Piva 
279*94c4a1e1SFrank Piva 	unsigned dev_id = info->dev_id;
280*94c4a1e1SFrank Piva 	struct epoll_event events[EPOLL_NR_EVENTS];
281*94c4a1e1SFrank Piva 	int epoll_fd = epoll_create(EPOLL_NR_EVENTS);
282*94c4a1e1SFrank Piva 	struct epoll_event read_event;
283*94c4a1e1SFrank Piva 	int ctx_efd = ublksrv_aio_get_efd(ctx);
284*94c4a1e1SFrank Piva 
285*94c4a1e1SFrank Piva 	if (epoll_fd < 0) {
286*94c4a1e1SFrank Piva 	        fprintf(stderr, "ublk dev %d create epoll fd failed\n", dev_id);
287*94c4a1e1SFrank Piva 	        return NULL;
288*94c4a1e1SFrank Piva 	}
289*94c4a1e1SFrank Piva 
290*94c4a1e1SFrank Piva 	fprintf(stdout, "ublk dev %d aio context(sync io submitter) started tid %d\n",
291*94c4a1e1SFrank Piva 			dev_id, ublksrv_gettid());
292*94c4a1e1SFrank Piva 
293*94c4a1e1SFrank Piva 	read_event.events = EPOLLIN;
294*94c4a1e1SFrank Piva 	read_event.data.fd = ctx_efd;
295*94c4a1e1SFrank Piva 	(void)epoll_ctl(epoll_fd, EPOLL_CTL_ADD, ctx_efd, &read_event);
296*94c4a1e1SFrank Piva 
297*94c4a1e1SFrank Piva 	while (!ublksrv_aio_ctx_dead(ctx)) {
298*94c4a1e1SFrank Piva 		struct aio_list compl;
299*94c4a1e1SFrank Piva 
300*94c4a1e1SFrank Piva 		aio_list_init(&compl);
301*94c4a1e1SFrank Piva 
302*94c4a1e1SFrank Piva 		ublksrv_aio_submit_worker(ctx, io_submit_worker, &compl);
303*94c4a1e1SFrank Piva 
304*94c4a1e1SFrank Piva 		ublksrv_aio_complete_worker(ctx, &compl);
305*94c4a1e1SFrank Piva 
306*94c4a1e1SFrank Piva 		epoll_wait(epoll_fd, events, EPOLL_NR_EVENTS, -1);
307*94c4a1e1SFrank Piva 	}
308*94c4a1e1SFrank Piva 
309*94c4a1e1SFrank Piva 	return NULL;
310*94c4a1e1SFrank Piva }
311*94c4a1e1SFrank Piva 
312*94c4a1e1SFrank Piva /*
313*94c4a1e1SFrank Piva  * io handler for each ublkdev's queue
314*94c4a1e1SFrank Piva  *
315*94c4a1e1SFrank Piva  * Just for showing how to build ublksrv target's io handling, so callers
316*94c4a1e1SFrank Piva  * can apply these APIs in their own thread context for making one ublk
317*94c4a1e1SFrank Piva  * block device.
318*94c4a1e1SFrank Piva  */
demo_event_io_handler_fn(void * data)319*94c4a1e1SFrank Piva static void *demo_event_io_handler_fn(void *data)
320*94c4a1e1SFrank Piva {
321*94c4a1e1SFrank Piva 	struct demo_queue_info *info = (struct demo_queue_info *)data;
322*94c4a1e1SFrank Piva 	const struct ublksrv_dev *dev = info->dev;
323*94c4a1e1SFrank Piva 	const struct ublksrv_ctrl_dev_info *dinfo =
324*94c4a1e1SFrank Piva 		ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
325*94c4a1e1SFrank Piva 	unsigned dev_id = dinfo->dev_id;
326*94c4a1e1SFrank Piva 	unsigned short q_id = info->qid;
327*94c4a1e1SFrank Piva 	const struct ublksrv_queue *q;
328*94c4a1e1SFrank Piva 
329*94c4a1e1SFrank Piva 	pthread_mutex_lock(&jbuf_lock);
330*94c4a1e1SFrank Piva 	ublksrv_json_write_queue_info(ublksrv_get_ctrl_dev(dev), jbuf, sizeof jbuf,
331*94c4a1e1SFrank Piva 			q_id, ublksrv_gettid());
332*94c4a1e1SFrank Piva 	pthread_mutex_unlock(&jbuf_lock);
333*94c4a1e1SFrank Piva 
334*94c4a1e1SFrank Piva 	q = ublksrv_queue_init(dev, q_id, info);
335*94c4a1e1SFrank Piva 	if (!q) {
336*94c4a1e1SFrank Piva 		fprintf(stderr, "ublk dev %d queue %d init queue failed\n",
337*94c4a1e1SFrank Piva 				dinfo->dev_id, q_id);
338*94c4a1e1SFrank Piva 		return NULL;
339*94c4a1e1SFrank Piva 	}
340*94c4a1e1SFrank Piva 	info->q = q;
341*94c4a1e1SFrank Piva 
342*94c4a1e1SFrank Piva 	fprintf(stdout, "tid %d: ublk dev %d queue %d started\n", ublksrv_gettid(),
343*94c4a1e1SFrank Piva 			dev_id, q->q_id);
344*94c4a1e1SFrank Piva 	do {
345*94c4a1e1SFrank Piva 		if (ublksrv_process_io(q) < 0)
346*94c4a1e1SFrank Piva 			break;
347*94c4a1e1SFrank Piva 	} while (1);
348*94c4a1e1SFrank Piva 
349*94c4a1e1SFrank Piva 	fprintf(stdout, "ublk dev %d queue %d exited\n", dev_id, q->q_id);
350*94c4a1e1SFrank Piva 	ublksrv_queue_deinit(q);
351*94c4a1e1SFrank Piva 	return NULL;
352*94c4a1e1SFrank Piva }
353*94c4a1e1SFrank Piva 
demo_event_set_parameters(struct ublksrv_ctrl_dev * cdev,const struct ublksrv_dev * dev)354*94c4a1e1SFrank Piva static void demo_event_set_parameters(struct ublksrv_ctrl_dev *cdev,
355*94c4a1e1SFrank Piva 		const struct ublksrv_dev *dev)
356*94c4a1e1SFrank Piva  {
357*94c4a1e1SFrank Piva 	const struct ublksrv_ctrl_dev_info *info =
358*94c4a1e1SFrank Piva 		ublksrv_ctrl_get_dev_info(cdev);
359*94c4a1e1SFrank Piva 	struct ublk_params p = {
360*94c4a1e1SFrank Piva 		.types = UBLK_PARAM_TYPE_BASIC,
361*94c4a1e1SFrank Piva 		.basic = {
362*94c4a1e1SFrank Piva 			.logical_bs_shift	= 9,
363*94c4a1e1SFrank Piva 			.physical_bs_shift	= 12,
364*94c4a1e1SFrank Piva 			.io_opt_shift		= 12,
365*94c4a1e1SFrank Piva 			.io_min_shift		= 9,
366*94c4a1e1SFrank Piva 			.max_sectors		= info->max_io_buf_bytes >> 9,
367*94c4a1e1SFrank Piva 			.dev_sectors		= dev->tgt.dev_size >> 9,
368*94c4a1e1SFrank Piva 		},
369*94c4a1e1SFrank Piva 	};
370*94c4a1e1SFrank Piva 	int ret;
371*94c4a1e1SFrank Piva 
372*94c4a1e1SFrank Piva 	pthread_mutex_lock(&jbuf_lock);
373*94c4a1e1SFrank Piva 	ublksrv_json_write_params(&p, jbuf, sizeof jbuf);
374*94c4a1e1SFrank Piva 	pthread_mutex_unlock(&jbuf_lock);
375*94c4a1e1SFrank Piva 
376*94c4a1e1SFrank Piva 	ret = ublksrv_ctrl_set_params(cdev, &p);
377*94c4a1e1SFrank Piva 	if (ret)
378*94c4a1e1SFrank Piva 		fprintf(stderr, "dev %d set basic parameter failed %d\n",
379*94c4a1e1SFrank Piva 				info->dev_id, ret);
380*94c4a1e1SFrank Piva }
381*94c4a1e1SFrank Piva 
382*94c4a1e1SFrank Piva 
demo_event_io_handler(struct ublksrv_ctrl_dev * ctrl_dev)383*94c4a1e1SFrank Piva static int demo_event_io_handler(struct ublksrv_ctrl_dev *ctrl_dev)
384*94c4a1e1SFrank Piva {
385*94c4a1e1SFrank Piva 	const struct ublksrv_ctrl_dev_info *dinfo =
386*94c4a1e1SFrank Piva 		ublksrv_ctrl_get_dev_info(ctrl_dev);
387*94c4a1e1SFrank Piva 	int dev_id = dinfo->dev_id;
388*94c4a1e1SFrank Piva 	int ret, i;
389*94c4a1e1SFrank Piva 	const struct ublksrv_dev *dev;
390*94c4a1e1SFrank Piva 	struct demo_queue_info *info_array;
391*94c4a1e1SFrank Piva 	void *thread_ret;
392*94c4a1e1SFrank Piva 
393*94c4a1e1SFrank Piva 	info_array = (struct demo_queue_info *)
394*94c4a1e1SFrank Piva 		calloc(sizeof(struct demo_queue_info), dinfo->nr_hw_queues);
395*94c4a1e1SFrank Piva 	if (!info_array)
396*94c4a1e1SFrank Piva 		return -ENOMEM;
397*94c4a1e1SFrank Piva 
398*94c4a1e1SFrank Piva 	dev = ublksrv_dev_init(ctrl_dev);
399*94c4a1e1SFrank Piva 	if (!dev) {
400*94c4a1e1SFrank Piva 		free(info_array);
401*94c4a1e1SFrank Piva 		return -ENOMEM;
402*94c4a1e1SFrank Piva 	}
403*94c4a1e1SFrank Piva 	this_dev = dev;
404*94c4a1e1SFrank Piva 
405*94c4a1e1SFrank Piva 
406*94c4a1e1SFrank Piva 	aio_ctx = ublksrv_aio_ctx_init(dev, 0);
407*94c4a1e1SFrank Piva 	if (!aio_ctx) {
408*94c4a1e1SFrank Piva 		fprintf(stderr, "dev %d call ublk_aio_ctx_init failed\n", dev_id);
409*94c4a1e1SFrank Piva 		ret = -ENOMEM;
410*94c4a1e1SFrank Piva 		goto fail;
411*94c4a1e1SFrank Piva 	}
412*94c4a1e1SFrank Piva 
413*94c4a1e1SFrank Piva 	if (!use_aio)
414*94c4a1e1SFrank Piva 		pthread_create(&io_thread, NULL, demo_event_real_io_handler_fn,
415*94c4a1e1SFrank Piva 				aio_ctx);
416*94c4a1e1SFrank Piva 	else
417*94c4a1e1SFrank Piva 		pthread_create(&io_thread, NULL, demo_event_uring_io_handler_fn,
418*94c4a1e1SFrank Piva 				aio_ctx);
419*94c4a1e1SFrank Piva 	for (i = 0; i < dinfo->nr_hw_queues; i++) {
420*94c4a1e1SFrank Piva 		info_array[i].dev = dev;
421*94c4a1e1SFrank Piva 		info_array[i].qid = i;
422*94c4a1e1SFrank Piva 
423*94c4a1e1SFrank Piva 		pthread_create(&info_array[i].thread, NULL,
424*94c4a1e1SFrank Piva 				demo_event_io_handler_fn,
425*94c4a1e1SFrank Piva 				&info_array[i]);
426*94c4a1e1SFrank Piva 	}
427*94c4a1e1SFrank Piva 
428*94c4a1e1SFrank Piva 	demo_event_set_parameters(ctrl_dev, dev);
429*94c4a1e1SFrank Piva 
430*94c4a1e1SFrank Piva 	/* everything is fine now, start us */
431*94c4a1e1SFrank Piva 	ret = ublksrv_ctrl_start_dev(ctrl_dev, getpid());
432*94c4a1e1SFrank Piva 	if (ret < 0)
433*94c4a1e1SFrank Piva 		goto fail;
434*94c4a1e1SFrank Piva 
435*94c4a1e1SFrank Piva 	ublksrv_ctrl_get_info(ctrl_dev);
436*94c4a1e1SFrank Piva 	ublksrv_ctrl_dump(ctrl_dev, jbuf);
437*94c4a1e1SFrank Piva 
438*94c4a1e1SFrank Piva 	/* wait until we are terminated */
439*94c4a1e1SFrank Piva 	for (i = 0; i < dinfo->nr_hw_queues; i++) {
440*94c4a1e1SFrank Piva 		pthread_join(info_array[i].thread, &thread_ret);
441*94c4a1e1SFrank Piva 	}
442*94c4a1e1SFrank Piva 	ublksrv_aio_ctx_shutdown(aio_ctx);
443*94c4a1e1SFrank Piva 	pthread_join(io_thread, &thread_ret);
444*94c4a1e1SFrank Piva 	ublksrv_aio_ctx_deinit(aio_ctx);
445*94c4a1e1SFrank Piva 
446*94c4a1e1SFrank Piva fail:
447*94c4a1e1SFrank Piva 	ublksrv_dev_deinit(dev);
448*94c4a1e1SFrank Piva 
449*94c4a1e1SFrank Piva 	free(info_array);
450*94c4a1e1SFrank Piva 
451*94c4a1e1SFrank Piva 	return ret;
452*94c4a1e1SFrank Piva }
453*94c4a1e1SFrank Piva 
ublksrv_start_daemon(struct ublksrv_ctrl_dev * ctrl_dev)454*94c4a1e1SFrank Piva static int ublksrv_start_daemon(struct ublksrv_ctrl_dev *ctrl_dev)
455*94c4a1e1SFrank Piva {
456*94c4a1e1SFrank Piva 	if (ublksrv_ctrl_get_affinity(ctrl_dev) < 0)
457*94c4a1e1SFrank Piva 		return -1;
458*94c4a1e1SFrank Piva 
459*94c4a1e1SFrank Piva 	return demo_event_io_handler(ctrl_dev);
460*94c4a1e1SFrank Piva }
461*94c4a1e1SFrank Piva 
demo_init_tgt(struct ublksrv_dev * dev,int type,int argc,char * argv[])462*94c4a1e1SFrank Piva static int demo_init_tgt(struct ublksrv_dev *dev, int type, int argc,
463*94c4a1e1SFrank Piva 		char *argv[])
464*94c4a1e1SFrank Piva {
465*94c4a1e1SFrank Piva 	const struct ublksrv_ctrl_dev_info *info =
466*94c4a1e1SFrank Piva 		ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
467*94c4a1e1SFrank Piva 	struct ublksrv_tgt_info *tgt = &dev->tgt;
468*94c4a1e1SFrank Piva 	struct ublksrv_tgt_base_json tgt_json = {
469*94c4a1e1SFrank Piva 		.type = type,
470*94c4a1e1SFrank Piva 	};
471*94c4a1e1SFrank Piva 	struct stat st;
472*94c4a1e1SFrank Piva 
473*94c4a1e1SFrank Piva 	strcpy(tgt_json.name, "demo_event");
474*94c4a1e1SFrank Piva 
475*94c4a1e1SFrank Piva 	if (type != UBLKSRV_TGT_TYPE_DEMO)
476*94c4a1e1SFrank Piva 		return -1;
477*94c4a1e1SFrank Piva 
478*94c4a1e1SFrank Piva 	if (backing_fd > 0) {
479*94c4a1e1SFrank Piva 		unsigned long long bytes;
480*94c4a1e1SFrank Piva 
481*94c4a1e1SFrank Piva 		fstat(backing_fd, &st);
482*94c4a1e1SFrank Piva 		if (S_ISBLK(st.st_mode)) {
483*94c4a1e1SFrank Piva 			if (ioctl(backing_fd, BLKGETSIZE64, &bytes) != 0)
484*94c4a1e1SFrank Piva 				return -1;
485*94c4a1e1SFrank Piva 		} else if (S_ISREG(st.st_mode)) {
486*94c4a1e1SFrank Piva 			bytes = st.st_size;
487*94c4a1e1SFrank Piva 		} else {
488*94c4a1e1SFrank Piva 			bytes = 0;
489*94c4a1e1SFrank Piva 		}
490*94c4a1e1SFrank Piva 
491*94c4a1e1SFrank Piva 		tgt->dev_size = bytes;
492*94c4a1e1SFrank Piva 	} else {
493*94c4a1e1SFrank Piva 		tgt->dev_size = 250UL * 1024 * 1024 * 1024;
494*94c4a1e1SFrank Piva 	}
495*94c4a1e1SFrank Piva 
496*94c4a1e1SFrank Piva 	tgt_json.dev_size = tgt->dev_size;
497*94c4a1e1SFrank Piva 	tgt->tgt_ring_depth = info->queue_depth;
498*94c4a1e1SFrank Piva 	tgt->nr_fds = 0;
499*94c4a1e1SFrank Piva 
500*94c4a1e1SFrank Piva 	ublksrv_json_write_dev_info(ublksrv_get_ctrl_dev(dev), jbuf, sizeof jbuf);
501*94c4a1e1SFrank Piva 	ublksrv_json_write_target_base_info(jbuf, sizeof jbuf, &tgt_json);
502*94c4a1e1SFrank Piva 
503*94c4a1e1SFrank Piva 	return 0;
504*94c4a1e1SFrank Piva }
505*94c4a1e1SFrank Piva 
demo_handle_io_async(const struct ublksrv_queue * q,const struct ublk_io_data * data)506*94c4a1e1SFrank Piva static int demo_handle_io_async(const struct ublksrv_queue *q,
507*94c4a1e1SFrank Piva 		const struct ublk_io_data *data)
508*94c4a1e1SFrank Piva {
509*94c4a1e1SFrank Piva 	struct ublksrv_aio *req = ublksrv_aio_alloc_req(aio_ctx, 0);
510*94c4a1e1SFrank Piva 
511*94c4a1e1SFrank Piva 	req->io = *data->iod;
512*94c4a1e1SFrank Piva 	req->fd = backing_fd;
513*94c4a1e1SFrank Piva 	req->id = ublksrv_aio_pid_tag(q->q_id, data->tag);
514*94c4a1e1SFrank Piva 	ublksrv_aio_submit_req(aio_ctx, q, req);
515*94c4a1e1SFrank Piva 
516*94c4a1e1SFrank Piva 	return 0;
517*94c4a1e1SFrank Piva }
518*94c4a1e1SFrank Piva 
demo_handle_event(const struct ublksrv_queue * q)519*94c4a1e1SFrank Piva static void demo_handle_event(const struct ublksrv_queue *q)
520*94c4a1e1SFrank Piva {
521*94c4a1e1SFrank Piva 	ublksrv_aio_handle_event(aio_ctx, q);
522*94c4a1e1SFrank Piva }
523*94c4a1e1SFrank Piva 
524*94c4a1e1SFrank Piva static const struct ublksrv_tgt_type demo_event_tgt_type = {
525*94c4a1e1SFrank Piva 	.type	= UBLKSRV_TGT_TYPE_DEMO,
526*94c4a1e1SFrank Piva 	.name	=  "demo_event",
527*94c4a1e1SFrank Piva 	.init_tgt = demo_init_tgt,
528*94c4a1e1SFrank Piva 	.handle_io_async = demo_handle_io_async,
529*94c4a1e1SFrank Piva 	.handle_event = demo_handle_event,
530*94c4a1e1SFrank Piva };
531*94c4a1e1SFrank Piva 
main(int argc,char * argv[])532*94c4a1e1SFrank Piva int main(int argc, char *argv[])
533*94c4a1e1SFrank Piva {
534*94c4a1e1SFrank Piva 	static const struct option longopts[] = {
535*94c4a1e1SFrank Piva 		{ "need_get_data",	1,	NULL, 'g' },
536*94c4a1e1SFrank Piva 		{ "backing_file",	1,	NULL, 'f' },
537*94c4a1e1SFrank Piva 		{ "use_aio",		1,	NULL, 'a' },
538*94c4a1e1SFrank Piva 		{ NULL }
539*94c4a1e1SFrank Piva 	};
540*94c4a1e1SFrank Piva 	struct ublksrv_dev_data data = {
541*94c4a1e1SFrank Piva 		.dev_id = -1,
542*94c4a1e1SFrank Piva 		.max_io_buf_bytes = DEF_BUF_SIZE,
543*94c4a1e1SFrank Piva 		.nr_hw_queues = DEF_NR_HW_QUEUES,
544*94c4a1e1SFrank Piva 		.queue_depth = DEF_QD,
545*94c4a1e1SFrank Piva 		.tgt_type = "demo_event",
546*94c4a1e1SFrank Piva 		.tgt_ops = &demo_event_tgt_type,
547*94c4a1e1SFrank Piva 		.flags = 0,
548*94c4a1e1SFrank Piva 	};
549*94c4a1e1SFrank Piva 	struct ublksrv_ctrl_dev *dev;
550*94c4a1e1SFrank Piva 	int ret, opt;
551*94c4a1e1SFrank Piva 
552*94c4a1e1SFrank Piva 	while ((opt = getopt_long(argc, argv, "f:ga",
553*94c4a1e1SFrank Piva 				  longopts, NULL)) != -1) {
554*94c4a1e1SFrank Piva 		switch (opt) {
555*94c4a1e1SFrank Piva 		case 'g':
556*94c4a1e1SFrank Piva 			data.flags |= UBLK_F_NEED_GET_DATA;
557*94c4a1e1SFrank Piva 			break;
558*94c4a1e1SFrank Piva 		case 'f':
559*94c4a1e1SFrank Piva 			backing_fd = open(optarg, O_RDWR | O_DIRECT);
560*94c4a1e1SFrank Piva 			if (backing_fd < 0)
561*94c4a1e1SFrank Piva 				backing_fd = -1;
562*94c4a1e1SFrank Piva 			break;
563*94c4a1e1SFrank Piva 		case 'a':
564*94c4a1e1SFrank Piva 			use_aio = true;
565*94c4a1e1SFrank Piva 			break;
566*94c4a1e1SFrank Piva 		}
567*94c4a1e1SFrank Piva 	}
568*94c4a1e1SFrank Piva 
569*94c4a1e1SFrank Piva 	if (backing_fd < 0)
570*94c4a1e1SFrank Piva 		use_aio = false;
571*94c4a1e1SFrank Piva 
572*94c4a1e1SFrank Piva 	if (signal(SIGTERM, sig_handler) == SIG_ERR)
573*94c4a1e1SFrank Piva 		error(EXIT_FAILURE, errno, "signal");
574*94c4a1e1SFrank Piva 	if (signal(SIGINT, sig_handler) == SIG_ERR)
575*94c4a1e1SFrank Piva 		error(EXIT_FAILURE, errno, "signal");
576*94c4a1e1SFrank Piva 
577*94c4a1e1SFrank Piva 	pthread_mutex_init(&jbuf_lock, NULL);
578*94c4a1e1SFrank Piva 
579*94c4a1e1SFrank Piva 	data.ublksrv_flags = UBLKSRV_F_NEED_EVENTFD;
580*94c4a1e1SFrank Piva 	dev = ublksrv_ctrl_init(&data);
581*94c4a1e1SFrank Piva 	if (!dev)
582*94c4a1e1SFrank Piva 		error(EXIT_FAILURE, ENODEV, "ublksrv_ctrl_init");
583*94c4a1e1SFrank Piva 	/* ugly, but signal handler needs this_dev */
584*94c4a1e1SFrank Piva 	this_ctrl_dev = dev;
585*94c4a1e1SFrank Piva 
586*94c4a1e1SFrank Piva 	ret = ublksrv_ctrl_add_dev(dev);
587*94c4a1e1SFrank Piva 	if (ret < 0) {
588*94c4a1e1SFrank Piva 		error(0, -ret, "can't add dev %d", data.dev_id);
589*94c4a1e1SFrank Piva 		goto fail;
590*94c4a1e1SFrank Piva 	}
591*94c4a1e1SFrank Piva 
592*94c4a1e1SFrank Piva 	ret = ublksrv_start_daemon(dev);
593*94c4a1e1SFrank Piva 	if (ret < 0) {
594*94c4a1e1SFrank Piva 		error(0, -ret, "can't start daemon");
595*94c4a1e1SFrank Piva 		goto fail_del_dev;
596*94c4a1e1SFrank Piva 	}
597*94c4a1e1SFrank Piva 
598*94c4a1e1SFrank Piva 	ublksrv_ctrl_del_dev(dev);
599*94c4a1e1SFrank Piva 	ublksrv_ctrl_deinit(dev);
600*94c4a1e1SFrank Piva 	exit(EXIT_SUCCESS);
601*94c4a1e1SFrank Piva 
602*94c4a1e1SFrank Piva  fail_del_dev:
603*94c4a1e1SFrank Piva 	ublksrv_ctrl_del_dev(dev);
604*94c4a1e1SFrank Piva  fail:
605*94c4a1e1SFrank Piva 	ublksrv_ctrl_deinit(dev);
606*94c4a1e1SFrank Piva 
607*94c4a1e1SFrank Piva 	exit(EXIT_FAILURE);
608*94c4a1e1SFrank Piva }
609