1*94c4a1e1SFrank Piva // SPDX-License-Identifier: MIT or GPL-2.0-only
2*94c4a1e1SFrank Piva
3*94c4a1e1SFrank Piva #include <config.h>
4*94c4a1e1SFrank Piva #include <stdio.h>
5*94c4a1e1SFrank Piva #include <stdlib.h>
6*94c4a1e1SFrank Piva #include <sched.h>
7*94c4a1e1SFrank Piva #include <pthread.h>
8*94c4a1e1SFrank Piva #include <getopt.h>
9*94c4a1e1SFrank Piva #include <stdarg.h>
10*94c4a1e1SFrank Piva #include <errno.h>
11*94c4a1e1SFrank Piva #include <error.h>
12*94c4a1e1SFrank Piva #include <string.h>
13*94c4a1e1SFrank Piva #include <sys/types.h>
14*94c4a1e1SFrank Piva #include <unistd.h>
15*94c4a1e1SFrank Piva
16*94c4a1e1SFrank Piva #include "ublksrv.h"
17*94c4a1e1SFrank Piva #include "ublksrv_utils.h"
18*94c4a1e1SFrank Piva
19*94c4a1e1SFrank Piva #define UBLKSRV_TGT_TYPE_DEMO 0
20*94c4a1e1SFrank Piva
21*94c4a1e1SFrank Piva struct demo_queue_info {
22*94c4a1e1SFrank Piva const struct ublksrv_dev *dev;
23*94c4a1e1SFrank Piva int qid;
24*94c4a1e1SFrank Piva pthread_t thread;
25*94c4a1e1SFrank Piva };
26*94c4a1e1SFrank Piva
27*94c4a1e1SFrank Piva static struct ublksrv_ctrl_dev *this_dev;
28*94c4a1e1SFrank Piva
29*94c4a1e1SFrank Piva static pthread_mutex_t jbuf_lock;
30*94c4a1e1SFrank Piva static char jbuf[4096];
31*94c4a1e1SFrank Piva
sig_handler(int sig)32*94c4a1e1SFrank Piva static void sig_handler(int sig)
33*94c4a1e1SFrank Piva {
34*94c4a1e1SFrank Piva fprintf(stderr, "got signal %d\n", sig);
35*94c4a1e1SFrank Piva ublksrv_ctrl_stop_dev(this_dev);
36*94c4a1e1SFrank Piva }
37*94c4a1e1SFrank Piva
38*94c4a1e1SFrank Piva /*
39*94c4a1e1SFrank Piva * io handler for each ublkdev's queue
40*94c4a1e1SFrank Piva *
41*94c4a1e1SFrank Piva * Just for showing how to build ublksrv target's io handling, so callers
42*94c4a1e1SFrank Piva * can apply these APIs in their own thread context for making one ublk
43*94c4a1e1SFrank Piva * block device.
44*94c4a1e1SFrank Piva */
demo_null_io_handler_fn(void * data)45*94c4a1e1SFrank Piva static void *demo_null_io_handler_fn(void *data)
46*94c4a1e1SFrank Piva {
47*94c4a1e1SFrank Piva struct demo_queue_info *info = (struct demo_queue_info *)data;
48*94c4a1e1SFrank Piva const struct ublksrv_dev *dev = info->dev;
49*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *dinfo =
50*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
51*94c4a1e1SFrank Piva unsigned dev_id = dinfo->dev_id;
52*94c4a1e1SFrank Piva unsigned short q_id = info->qid;
53*94c4a1e1SFrank Piva const struct ublksrv_queue *q;
54*94c4a1e1SFrank Piva
55*94c4a1e1SFrank Piva sched_setscheduler(getpid(), SCHED_RR, NULL);
56*94c4a1e1SFrank Piva
57*94c4a1e1SFrank Piva pthread_mutex_lock(&jbuf_lock);
58*94c4a1e1SFrank Piva ublksrv_json_write_queue_info(ublksrv_get_ctrl_dev(dev), jbuf, sizeof jbuf,
59*94c4a1e1SFrank Piva q_id, ublksrv_gettid());
60*94c4a1e1SFrank Piva pthread_mutex_unlock(&jbuf_lock);
61*94c4a1e1SFrank Piva q = ublksrv_queue_init(dev, q_id, NULL);
62*94c4a1e1SFrank Piva if (!q) {
63*94c4a1e1SFrank Piva fprintf(stderr, "ublk dev %d queue %d init queue failed\n",
64*94c4a1e1SFrank Piva dinfo->dev_id, q_id);
65*94c4a1e1SFrank Piva return NULL;
66*94c4a1e1SFrank Piva }
67*94c4a1e1SFrank Piva
68*94c4a1e1SFrank Piva fprintf(stdout, "tid %d: ublk dev %d queue %d started\n",
69*94c4a1e1SFrank Piva ublksrv_gettid(),
70*94c4a1e1SFrank Piva dev_id, q->q_id);
71*94c4a1e1SFrank Piva do {
72*94c4a1e1SFrank Piva if (ublksrv_process_io(q) < 0)
73*94c4a1e1SFrank Piva break;
74*94c4a1e1SFrank Piva } while (1);
75*94c4a1e1SFrank Piva
76*94c4a1e1SFrank Piva fprintf(stdout, "ublk dev %d queue %d exited\n", dev_id, q->q_id);
77*94c4a1e1SFrank Piva ublksrv_queue_deinit(q);
78*94c4a1e1SFrank Piva return NULL;
79*94c4a1e1SFrank Piva }
80*94c4a1e1SFrank Piva
demo_null_set_parameters(struct ublksrv_ctrl_dev * cdev,const struct ublksrv_dev * dev)81*94c4a1e1SFrank Piva static void demo_null_set_parameters(struct ublksrv_ctrl_dev *cdev,
82*94c4a1e1SFrank Piva const struct ublksrv_dev *dev)
83*94c4a1e1SFrank Piva {
84*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
85*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(cdev);
86*94c4a1e1SFrank Piva struct ublk_params p = {
87*94c4a1e1SFrank Piva .types = UBLK_PARAM_TYPE_BASIC,
88*94c4a1e1SFrank Piva .basic = {
89*94c4a1e1SFrank Piva .logical_bs_shift = 9,
90*94c4a1e1SFrank Piva .physical_bs_shift = 12,
91*94c4a1e1SFrank Piva .io_opt_shift = 12,
92*94c4a1e1SFrank Piva .io_min_shift = 9,
93*94c4a1e1SFrank Piva .max_sectors = info->max_io_buf_bytes >> 9,
94*94c4a1e1SFrank Piva .dev_sectors = dev->tgt.dev_size >> 9,
95*94c4a1e1SFrank Piva },
96*94c4a1e1SFrank Piva };
97*94c4a1e1SFrank Piva int ret;
98*94c4a1e1SFrank Piva
99*94c4a1e1SFrank Piva pthread_mutex_lock(&jbuf_lock);
100*94c4a1e1SFrank Piva ublksrv_json_write_params(&p, jbuf, sizeof jbuf);
101*94c4a1e1SFrank Piva pthread_mutex_unlock(&jbuf_lock);
102*94c4a1e1SFrank Piva
103*94c4a1e1SFrank Piva ret = ublksrv_ctrl_set_params(cdev, &p);
104*94c4a1e1SFrank Piva if (ret)
105*94c4a1e1SFrank Piva fprintf(stderr, "dev %d set basic parameter failed %d\n",
106*94c4a1e1SFrank Piva info->dev_id, ret);
107*94c4a1e1SFrank Piva }
108*94c4a1e1SFrank Piva
demo_null_io_handler(struct ublksrv_ctrl_dev * ctrl_dev)109*94c4a1e1SFrank Piva static int demo_null_io_handler(struct ublksrv_ctrl_dev *ctrl_dev)
110*94c4a1e1SFrank Piva {
111*94c4a1e1SFrank Piva int ret, i;
112*94c4a1e1SFrank Piva const struct ublksrv_dev *dev;
113*94c4a1e1SFrank Piva struct demo_queue_info *info_array;
114*94c4a1e1SFrank Piva void *thread_ret;
115*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *dinfo =
116*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ctrl_dev);
117*94c4a1e1SFrank Piva
118*94c4a1e1SFrank Piva info_array = (struct demo_queue_info *)
119*94c4a1e1SFrank Piva calloc(sizeof(struct demo_queue_info), dinfo->nr_hw_queues);
120*94c4a1e1SFrank Piva if (!info_array)
121*94c4a1e1SFrank Piva return -ENOMEM;
122*94c4a1e1SFrank Piva
123*94c4a1e1SFrank Piva dev = ublksrv_dev_init(ctrl_dev);
124*94c4a1e1SFrank Piva if (!dev) {
125*94c4a1e1SFrank Piva free(info_array);
126*94c4a1e1SFrank Piva return -ENOMEM;
127*94c4a1e1SFrank Piva }
128*94c4a1e1SFrank Piva
129*94c4a1e1SFrank Piva for (i = 0; i < dinfo->nr_hw_queues; i++) {
130*94c4a1e1SFrank Piva info_array[i].dev = dev;
131*94c4a1e1SFrank Piva info_array[i].qid = i;
132*94c4a1e1SFrank Piva pthread_create(&info_array[i].thread, NULL,
133*94c4a1e1SFrank Piva demo_null_io_handler_fn,
134*94c4a1e1SFrank Piva &info_array[i]);
135*94c4a1e1SFrank Piva }
136*94c4a1e1SFrank Piva
137*94c4a1e1SFrank Piva demo_null_set_parameters(ctrl_dev, dev);
138*94c4a1e1SFrank Piva
139*94c4a1e1SFrank Piva /* everything is fine now, start us */
140*94c4a1e1SFrank Piva ret = ublksrv_ctrl_start_dev(ctrl_dev, getpid());
141*94c4a1e1SFrank Piva if (ret < 0)
142*94c4a1e1SFrank Piva goto fail;
143*94c4a1e1SFrank Piva
144*94c4a1e1SFrank Piva ublksrv_ctrl_get_info(ctrl_dev);
145*94c4a1e1SFrank Piva ublksrv_ctrl_dump(ctrl_dev, jbuf);
146*94c4a1e1SFrank Piva
147*94c4a1e1SFrank Piva /* wait until we are terminated */
148*94c4a1e1SFrank Piva for (i = 0; i < dinfo->nr_hw_queues; i++)
149*94c4a1e1SFrank Piva pthread_join(info_array[i].thread, &thread_ret);
150*94c4a1e1SFrank Piva fail:
151*94c4a1e1SFrank Piva ublksrv_dev_deinit(dev);
152*94c4a1e1SFrank Piva
153*94c4a1e1SFrank Piva free(info_array);
154*94c4a1e1SFrank Piva
155*94c4a1e1SFrank Piva return ret;
156*94c4a1e1SFrank Piva }
157*94c4a1e1SFrank Piva
ublksrv_start_daemon(struct ublksrv_ctrl_dev * ctrl_dev)158*94c4a1e1SFrank Piva static int ublksrv_start_daemon(struct ublksrv_ctrl_dev *ctrl_dev)
159*94c4a1e1SFrank Piva {
160*94c4a1e1SFrank Piva int ret;
161*94c4a1e1SFrank Piva
162*94c4a1e1SFrank Piva if (ublksrv_ctrl_get_affinity(ctrl_dev) < 0)
163*94c4a1e1SFrank Piva return -1;
164*94c4a1e1SFrank Piva
165*94c4a1e1SFrank Piva ret = demo_null_io_handler(ctrl_dev);
166*94c4a1e1SFrank Piva
167*94c4a1e1SFrank Piva return ret;
168*94c4a1e1SFrank Piva }
169*94c4a1e1SFrank Piva
170*94c4a1e1SFrank Piva
171*94c4a1e1SFrank Piva
demo_init_tgt(struct ublksrv_dev * dev,int type,int argc,char * argv[])172*94c4a1e1SFrank Piva static int demo_init_tgt(struct ublksrv_dev *dev, int type, int argc,
173*94c4a1e1SFrank Piva char *argv[])
174*94c4a1e1SFrank Piva {
175*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
176*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
177*94c4a1e1SFrank Piva struct ublksrv_tgt_info *tgt = &dev->tgt;
178*94c4a1e1SFrank Piva struct ublksrv_tgt_base_json tgt_json = {
179*94c4a1e1SFrank Piva .type = type,
180*94c4a1e1SFrank Piva };
181*94c4a1e1SFrank Piva strcpy(tgt_json.name, "null");
182*94c4a1e1SFrank Piva
183*94c4a1e1SFrank Piva
184*94c4a1e1SFrank Piva if (type != UBLKSRV_TGT_TYPE_DEMO)
185*94c4a1e1SFrank Piva return -1;
186*94c4a1e1SFrank Piva
187*94c4a1e1SFrank Piva tgt_json.dev_size = tgt->dev_size = 250UL * 1024 * 1024 * 1024;
188*94c4a1e1SFrank Piva tgt->tgt_ring_depth = info->queue_depth;
189*94c4a1e1SFrank Piva tgt->nr_fds = 0;
190*94c4a1e1SFrank Piva
191*94c4a1e1SFrank Piva ublksrv_json_write_dev_info(ublksrv_get_ctrl_dev(dev), jbuf, sizeof jbuf);
192*94c4a1e1SFrank Piva ublksrv_json_write_target_base_info(jbuf, sizeof jbuf, &tgt_json);
193*94c4a1e1SFrank Piva
194*94c4a1e1SFrank Piva return 0;
195*94c4a1e1SFrank Piva }
196*94c4a1e1SFrank Piva
demo_handle_io_async(const struct ublksrv_queue * q,const struct ublk_io_data * data)197*94c4a1e1SFrank Piva static int demo_handle_io_async(const struct ublksrv_queue *q,
198*94c4a1e1SFrank Piva const struct ublk_io_data *data)
199*94c4a1e1SFrank Piva {
200*94c4a1e1SFrank Piva const struct ublksrv_io_desc *iod = data->iod;
201*94c4a1e1SFrank Piva
202*94c4a1e1SFrank Piva ublksrv_complete_io(q, data->tag, iod->nr_sectors << 9);
203*94c4a1e1SFrank Piva
204*94c4a1e1SFrank Piva return 0;
205*94c4a1e1SFrank Piva }
206*94c4a1e1SFrank Piva
null_alloc_io_buf(const struct ublksrv_queue * q,int tag,int size)207*94c4a1e1SFrank Piva void *null_alloc_io_buf(const struct ublksrv_queue *q, int tag, int size)
208*94c4a1e1SFrank Piva {
209*94c4a1e1SFrank Piva return malloc(size);
210*94c4a1e1SFrank Piva }
211*94c4a1e1SFrank Piva
null_free_io_buf(const struct ublksrv_queue * q,void * buf,int tag)212*94c4a1e1SFrank Piva void null_free_io_buf(const struct ublksrv_queue *q, void *buf, int tag)
213*94c4a1e1SFrank Piva {
214*94c4a1e1SFrank Piva free(buf);
215*94c4a1e1SFrank Piva }
216*94c4a1e1SFrank Piva
217*94c4a1e1SFrank Piva static struct ublksrv_tgt_type demo_tgt_type = {
218*94c4a1e1SFrank Piva .type = UBLKSRV_TGT_TYPE_DEMO,
219*94c4a1e1SFrank Piva .name = "demo_null",
220*94c4a1e1SFrank Piva .init_tgt = demo_init_tgt,
221*94c4a1e1SFrank Piva .handle_io_async = demo_handle_io_async,
222*94c4a1e1SFrank Piva //.alloc_io_buf = null_alloc_io_buf,
223*94c4a1e1SFrank Piva //.free_io_buf = null_free_io_buf,
224*94c4a1e1SFrank Piva };
225*94c4a1e1SFrank Piva
main(int argc,char * argv[])226*94c4a1e1SFrank Piva int main(int argc, char *argv[])
227*94c4a1e1SFrank Piva {
228*94c4a1e1SFrank Piva struct ublksrv_dev_data data = {
229*94c4a1e1SFrank Piva .dev_id = -1,
230*94c4a1e1SFrank Piva .max_io_buf_bytes = DEF_BUF_SIZE,
231*94c4a1e1SFrank Piva .nr_hw_queues = DEF_NR_HW_QUEUES,
232*94c4a1e1SFrank Piva .queue_depth = DEF_QD,
233*94c4a1e1SFrank Piva .tgt_type = "demo_null",
234*94c4a1e1SFrank Piva .tgt_ops = &demo_tgt_type,
235*94c4a1e1SFrank Piva .flags = 0,
236*94c4a1e1SFrank Piva };
237*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *dev;
238*94c4a1e1SFrank Piva int ret;
239*94c4a1e1SFrank Piva static const struct option longopts[] = {
240*94c4a1e1SFrank Piva { "buf", 1, NULL, 'b' },
241*94c4a1e1SFrank Piva { "need_get_data", 1, NULL, 'g' },
242*94c4a1e1SFrank Piva { NULL }
243*94c4a1e1SFrank Piva };
244*94c4a1e1SFrank Piva int opt;
245*94c4a1e1SFrank Piva bool use_buf = false;
246*94c4a1e1SFrank Piva
247*94c4a1e1SFrank Piva while ((opt = getopt_long(argc, argv, ":bg",
248*94c4a1e1SFrank Piva longopts, NULL)) != -1) {
249*94c4a1e1SFrank Piva switch (opt) {
250*94c4a1e1SFrank Piva case 'b':
251*94c4a1e1SFrank Piva use_buf = true;
252*94c4a1e1SFrank Piva break;
253*94c4a1e1SFrank Piva case 'g':
254*94c4a1e1SFrank Piva data.flags |= UBLK_F_NEED_GET_DATA;
255*94c4a1e1SFrank Piva break;
256*94c4a1e1SFrank Piva }
257*94c4a1e1SFrank Piva }
258*94c4a1e1SFrank Piva
259*94c4a1e1SFrank Piva if (signal(SIGTERM, sig_handler) == SIG_ERR)
260*94c4a1e1SFrank Piva error(EXIT_FAILURE, errno, "signal");
261*94c4a1e1SFrank Piva if (signal(SIGINT, sig_handler) == SIG_ERR)
262*94c4a1e1SFrank Piva error(EXIT_FAILURE, errno, "signal");
263*94c4a1e1SFrank Piva
264*94c4a1e1SFrank Piva if (use_buf) {
265*94c4a1e1SFrank Piva demo_tgt_type.alloc_io_buf = null_alloc_io_buf;
266*94c4a1e1SFrank Piva demo_tgt_type.free_io_buf = null_free_io_buf;
267*94c4a1e1SFrank Piva }
268*94c4a1e1SFrank Piva
269*94c4a1e1SFrank Piva pthread_mutex_init(&jbuf_lock, NULL);
270*94c4a1e1SFrank Piva dev = ublksrv_ctrl_init(&data);
271*94c4a1e1SFrank Piva if (!dev)
272*94c4a1e1SFrank Piva error(EXIT_FAILURE, ENODEV, "ublksrv_ctrl_init");
273*94c4a1e1SFrank Piva /* ugly, but signal handler needs this_dev */
274*94c4a1e1SFrank Piva this_dev = dev;
275*94c4a1e1SFrank Piva
276*94c4a1e1SFrank Piva ret = ublksrv_ctrl_add_dev(dev);
277*94c4a1e1SFrank Piva if (ret < 0) {
278*94c4a1e1SFrank Piva error(0, -ret, "can't add dev %d", data.dev_id);
279*94c4a1e1SFrank Piva goto fail;
280*94c4a1e1SFrank Piva }
281*94c4a1e1SFrank Piva
282*94c4a1e1SFrank Piva ret = ublksrv_start_daemon(dev);
283*94c4a1e1SFrank Piva if (ret < 0) {
284*94c4a1e1SFrank Piva error(0, -ret, "can't start daemon");
285*94c4a1e1SFrank Piva goto fail_del_dev;
286*94c4a1e1SFrank Piva }
287*94c4a1e1SFrank Piva
288*94c4a1e1SFrank Piva ublksrv_ctrl_del_dev(dev);
289*94c4a1e1SFrank Piva ublksrv_ctrl_deinit(dev);
290*94c4a1e1SFrank Piva exit(EXIT_SUCCESS);
291*94c4a1e1SFrank Piva
292*94c4a1e1SFrank Piva fail_del_dev:
293*94c4a1e1SFrank Piva ublksrv_ctrl_del_dev(dev);
294*94c4a1e1SFrank Piva fail:
295*94c4a1e1SFrank Piva ublksrv_ctrl_deinit(dev);
296*94c4a1e1SFrank Piva
297*94c4a1e1SFrank Piva exit(EXIT_FAILURE);
298*94c4a1e1SFrank Piva }
299