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