1 // SPDX-License-Identifier: MIT or GPL-2.0-only
2
3 #include "config.h"
4 #include "ublksrv_tgt.h"
5
6 /* per-task variable */
7 static pthread_mutex_t jbuf_lock;
8 static int jbuf_size = 0;
9 static int queues_stored = 0;
10 static char *jbuf = NULL;
11
12 struct ublksrv_queue_info {
13 const struct ublksrv_dev *dev;
14 int qid;
15 pthread_t thread;
16 };
17
18
19 /********************cmd handling************************/
20 static char *full_cmd;
21 static struct ublksrv_tgt_type *tgt_list[UBLKSRV_TGT_TYPE_MAX] = {};
22
ublk_json_write_dev_info(struct ublksrv_dev * dev,char ** jbuf,int * len)23 int ublk_json_write_dev_info(struct ublksrv_dev *dev, char **jbuf, int *len)
24 {
25 int ret = 0;
26
27 do {
28 ret = ublksrv_json_write_dev_info(ublksrv_get_ctrl_dev(dev),
29 *jbuf, *len);
30 if (ret < 0)
31 *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
32 } while (ret < 0);
33
34 return ret;
35 }
36
ublk_json_write_params(struct ublksrv_dev * dev,char ** jbuf,int * len,const struct ublk_params * p)37 int ublk_json_write_params(struct ublksrv_dev *dev, char **jbuf, int *len,
38 const struct ublk_params *p)
39 {
40 int ret = 0;
41
42 do {
43 ret = ublksrv_json_write_params(p, *jbuf, *len);
44 if (ret < 0)
45 *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
46 } while (ret < 0);
47
48 return ret;
49 }
50
ublk_json_write_target_base(struct ublksrv_dev * dev,char ** jbuf,int * len,const struct ublksrv_tgt_base_json * tgt)51 int ublk_json_write_target_base(struct ublksrv_dev *dev, char **jbuf, int *len,
52 const struct ublksrv_tgt_base_json *tgt)
53 {
54 int ret = 0;
55
56 do {
57 ret = ublksrv_json_write_target_base_info(*jbuf, *len, tgt);
58 if (ret < 0)
59 *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
60 } while (ret < 0);
61
62 return ret;
63
64 }
65
ublk_json_write_tgt_str(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,const char * val)66 int ublk_json_write_tgt_str(struct ublksrv_dev *dev, char **jbuf,
67 int *len, const char *name, const char *val)
68 {
69 int ret = 0;
70
71 do {
72 if (val)
73 ret = ublksrv_json_write_target_str_info(*jbuf,
74 *len, name, val);
75
76 if (ret < 0)
77 *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
78 } while (ret < 0);
79
80 return ret;
81 }
82
ublk_json_write_tgt_ulong(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,unsigned long val)83 int ublk_json_write_tgt_ulong(struct ublksrv_dev *dev, char **jbuf,
84 int *len, const char *name, unsigned long val)
85 {
86 int ret = 0;
87
88 do {
89 ret = ublksrv_json_write_target_ulong_info(*jbuf,
90 *len, name, val);
91 if (ret < 0)
92 *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
93 } while (ret < 0);
94
95 return ret;
96 }
97
ublk_json_write_tgt_long(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,long val)98 int ublk_json_write_tgt_long(struct ublksrv_dev *dev, char **jbuf,
99 int *len, const char *name, long val)
100 {
101 int ret = 0;
102
103 do {
104 ret = ublksrv_json_write_target_long_info(*jbuf,
105 *len, name, val);
106 if (ret < 0)
107 *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
108 } while (ret < 0);
109
110 return ret;
111 }
112
ublksrv_find_tgt_type(const char * name)113 static const struct ublksrv_tgt_type *ublksrv_find_tgt_type(const char *name)
114 {
115 int i;
116
117 for (i = 0; i < UBLKSRV_TGT_TYPE_MAX; i++) {
118 const struct ublksrv_tgt_type *type = tgt_list[i];
119
120 if (type == NULL)
121 continue;
122
123 if (!strcmp(type->name, name))
124 return type;
125 }
126
127 return NULL;
128 }
129
ublksrv_for_each_tgt_type(void (* handle_tgt_type)(unsigned idx,const struct ublksrv_tgt_type * type,void * data),void * data)130 static void ublksrv_for_each_tgt_type(void (*handle_tgt_type)(unsigned idx,
131 const struct ublksrv_tgt_type *type, void *data),
132 void *data)
133 {
134 int i;
135
136 for (i = 0; i < UBLKSRV_TGT_TYPE_MAX; i++) {
137 const struct ublksrv_tgt_type *type = tgt_list[i];
138
139 if (!type)
140 continue;
141 handle_tgt_type(i, type, data);
142 }
143 }
144
ublksrv_register_tgt_type(struct ublksrv_tgt_type * type)145 int ublksrv_register_tgt_type(struct ublksrv_tgt_type *type)
146 {
147 if (type->type < UBLKSRV_TGT_TYPE_MAX && !tgt_list[type->type]) {
148 tgt_list[type->type] = type;
149 return 0;
150 }
151 return -1;
152 }
153
ublksrv_unregister_tgt_type(struct ublksrv_tgt_type * type)154 void ublksrv_unregister_tgt_type(struct ublksrv_tgt_type *type)
155 {
156 if (type->type < UBLKSRV_TGT_TYPE_MAX && tgt_list[type->type]) {
157 tgt_list[type->type] = NULL;
158 }
159 }
160
161
mprintf(const char * fmt,...)162 static char *mprintf(const char *fmt, ...)
163 {
164 va_list args;
165 char *str;
166 int ret;
167
168 va_start(args, fmt);
169 ret = vasprintf(&str, fmt, args);
170 va_end(args);
171
172 if (ret < 0) {
173 return NULL;
174 }
175
176 return str;
177 }
178
pop_cmd(int * argc,char * argv[])179 static char *pop_cmd(int *argc, char *argv[])
180 {
181 char *cmd = argv[1];
182 if (*argc < 2) {
183 return NULL;
184 }
185
186 memmove(&argv[1], &argv[2], *argc * sizeof(argv[0]));
187 (*argc)--;
188
189 full_cmd = mprintf("%s %s", full_cmd, cmd);
190 return cmd;
191 }
192
start_daemon(void (* child_fn)(void *),void * data)193 static int start_daemon(void (*child_fn)(void *), void *data)
194 {
195 char path[PATH_MAX];
196 int fd;
197 char *res;
198
199 if (setsid() == -1)
200 return -1;
201
202 res = getcwd(path, PATH_MAX);
203 if (!res)
204 ublk_err("%s: %d getcwd failed %m\n", __func__, __LINE__);
205
206 switch (fork()) {
207 case -1: return -1;
208 case 0: break;
209 default: _exit(EXIT_SUCCESS);
210 }
211
212 if (chdir(path) != 0)
213 ublk_err("%s: %d chdir failed %m\n", __func__, __LINE__);
214
215 close(STDIN_FILENO);
216 fd = open("/dev/null", O_RDWR);
217 if (fd != STDIN_FILENO)
218 return -1;
219 if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO)
220 return -1;
221 if (dup2(fd, STDERR_FILENO) != STDERR_FILENO)
222 return -1;
223
224 child_fn(data);
225 return 0;
226 }
227
__ublksrv_tgt_return_json_buf(struct ublksrv_dev * dev,int * size)228 char *__ublksrv_tgt_return_json_buf(struct ublksrv_dev *dev, int *size)
229 {
230 if (jbuf == NULL) {
231 jbuf_size = 1024;
232 jbuf = (char *)realloc((void *)jbuf, jbuf_size);
233 }
234 *size = jbuf_size;
235
236 return jbuf;
237 }
238
ublksrv_tgt_return_json_buf(struct ublksrv_dev * dev,int * size)239 char *ublksrv_tgt_return_json_buf(struct ublksrv_dev *dev, int *size)
240 {
241 char *buf;
242
243 pthread_mutex_lock(&jbuf_lock);
244 buf = __ublksrv_tgt_return_json_buf(dev, size);
245 pthread_mutex_unlock(&jbuf_lock);
246
247 return buf;
248 }
249
__ublksrv_tgt_realloc_json_buf(const struct ublksrv_dev * dev,int * size)250 static char *__ublksrv_tgt_realloc_json_buf(const struct ublksrv_dev *dev, int *size)
251 {
252 if (jbuf == NULL)
253 jbuf_size = 1024;
254 else
255 jbuf_size += 1024;
256
257 jbuf = (char *)realloc((void *)jbuf, jbuf_size);
258 *size = jbuf_size;
259
260 return jbuf;
261 }
262
ublksrv_tgt_realloc_json_buf(struct ublksrv_dev * dev,int * size)263 char *ublksrv_tgt_realloc_json_buf(struct ublksrv_dev *dev, int *size)
264 {
265 char *buf;
266
267 pthread_mutex_lock(&jbuf_lock);
268 buf = __ublksrv_tgt_realloc_json_buf(dev, size);
269 pthread_mutex_unlock(&jbuf_lock);
270
271 return buf;
272 }
273
ublksrv_tgt_store_dev_data(const struct ublksrv_dev * dev,const char * buf)274 static int ublksrv_tgt_store_dev_data(const struct ublksrv_dev *dev,
275 const char *buf)
276 {
277 int ret;
278 int len = ublksrv_json_get_length(buf);
279 int fd = ublksrv_get_pidfile_fd(dev);
280
281 if (fd < 0) {
282 ublk_err( "fail to get fd of pid file, ret %d\n",
283 fd);
284 return fd;
285 }
286
287 ret = pwrite(fd, buf, len, JSON_OFFSET);
288 if (ret <= 0)
289 ublk_err( "fail to write json data to pid file, ret %d\n",
290 ret);
291
292 return ret;
293 }
294
ublksrv_tgt_get_dev_data(struct ublksrv_ctrl_dev * cdev)295 static char *ublksrv_tgt_get_dev_data(struct ublksrv_ctrl_dev *cdev)
296 {
297 const struct ublksrv_ctrl_dev_info *info =
298 ublksrv_ctrl_get_dev_info(cdev);
299 int dev_id = info->dev_id;
300 struct stat st;
301 char pid_file[256];
302 char *buf;
303 int size, fd, ret;
304 const char *run_dir = ublksrv_ctrl_get_run_dir(cdev);
305
306 if (!run_dir)
307 return 0;
308
309 snprintf(pid_file, 256, "%s/%d.pid", run_dir, dev_id);
310 fd = open(pid_file, O_RDONLY);
311
312 if (fd <= 0)
313 return NULL;
314
315 if (fstat(fd, &st) < 0)
316 return NULL;
317
318 if (st.st_size <= JSON_OFFSET)
319 return NULL;
320
321 size = st.st_size - JSON_OFFSET;
322 buf = (char *)malloc(size);
323 ret = pread(fd, buf, size, JSON_OFFSET);
324 if (ret <= 0)
325 fprintf(stderr, "fail to read json from %s ret %d\n",
326 pid_file, ret);
327 close(fd);
328
329 return buf;
330 }
331
ublksrv_io_handler_fn(void * data)332 static void *ublksrv_io_handler_fn(void *data)
333 {
334 struct ublksrv_queue_info *info = (struct ublksrv_queue_info *)data;
335 const struct ublksrv_dev *dev = info->dev;
336 const struct ublksrv_ctrl_dev *cdev = ublksrv_get_ctrl_dev(dev);
337 const struct ublksrv_ctrl_dev_info *dinfo =
338 ublksrv_ctrl_get_dev_info(cdev);
339 unsigned dev_id = dinfo->dev_id;
340 unsigned short q_id = info->qid;
341 const struct ublksrv_queue *q;
342 int ret;
343 int buf_size;
344 char *buf;
345 const char *jbuf;
346
347 pthread_mutex_lock(&jbuf_lock);
348
349 if (!ublksrv_is_recovering(cdev)) {
350 do {
351 buf = __ublksrv_tgt_realloc_json_buf(dev, &buf_size);
352 ret = ublksrv_json_write_queue_info(cdev, buf, buf_size,
353 q_id, ublksrv_gettid());
354 } while (ret < 0);
355 jbuf = buf;
356 } else {
357 jbuf = ublksrv_ctrl_get_recovery_jbuf(cdev);
358 }
359 queues_stored++;
360
361 /*
362 * A bit ugly to store json buffer to pid file here, but no easy
363 * way to do it in control task side, so far, so good
364 */
365 if (queues_stored == dinfo->nr_hw_queues)
366 ublksrv_tgt_store_dev_data(dev, jbuf);
367 pthread_mutex_unlock(&jbuf_lock);
368
369 q = ublksrv_queue_init(dev, q_id, NULL);
370 if (!q) {
371 ublk_err("ublk dev %d queue %d init queue failed",
372 dev_id, q_id);
373 return NULL;
374 }
375
376 ublk_log("tid %d: ublk dev %d queue %d started", ublksrv_gettid(),
377 dev_id, q->q_id);
378 do {
379 if (ublksrv_process_io(q) < 0)
380 break;
381 } while (1);
382
383 ublk_log("ublk dev %d queue %d exited", dev_id, q->q_id);
384 ublksrv_queue_deinit(q);
385 return NULL;
386 }
387
sig_handler(int sig)388 static void sig_handler(int sig)
389 {
390 if (sig == SIGTERM)
391 ublk_log("got TERM signal");
392 }
393
setup_pthread_sigmask()394 static void setup_pthread_sigmask()
395 {
396 sigset_t signal_mask;
397
398 if (signal(SIGTERM, sig_handler) == SIG_ERR)
399 return;
400
401 /* make sure SIGTERM won't be blocked */
402 sigemptyset(&signal_mask);
403 sigaddset(&signal_mask, SIGINT);
404 sigaddset(&signal_mask, SIGTERM);
405 pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
406 }
407
408 /*
409 * Now STOP DEV ctrl command has been sent to /dev/ublk-control,
410 * and wait until all pending fetch commands are canceled
411 */
ublksrv_drain_fetch_commands(const struct ublksrv_dev * dev,struct ublksrv_queue_info * info)412 static void ublksrv_drain_fetch_commands(const struct ublksrv_dev *dev,
413 struct ublksrv_queue_info *info)
414 {
415 const struct ublksrv_ctrl_dev_info *dinfo =
416 ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
417 unsigned nr_queues = dinfo->nr_hw_queues;
418 int i;
419 void *ret;
420
421 for (i = 0; i < nr_queues; i++)
422 pthread_join(info[i].thread, &ret);
423 }
424
425
ublksrv_io_handler(void * data)426 static void ublksrv_io_handler(void *data)
427 {
428 const struct ublksrv_ctrl_dev *ctrl_dev = (struct ublksrv_ctrl_dev *)data;
429 const struct ublksrv_ctrl_dev_info *dinfo =
430 ublksrv_ctrl_get_dev_info(ctrl_dev);
431 int dev_id = dinfo->dev_id;
432 int i;
433 char buf[32];
434 const struct ublksrv_dev *dev;
435 struct ublksrv_queue_info *info_array;
436
437 snprintf(buf, 32, "%s-%d", "ublksrvd", dev_id);
438 openlog(buf, LOG_PID, LOG_USER);
439
440 ublk_log("start ublksrv io daemon %s\n", buf);
441
442 pthread_mutex_init(&jbuf_lock, NULL);
443
444 dev = ublksrv_dev_init(ctrl_dev);
445 if (!dev) {
446 ublk_err( "dev-%d start ubsrv failed", dev_id);
447 goto out;
448 }
449
450 setup_pthread_sigmask();
451
452 if (!(dinfo->flags & UBLK_F_UNPRIVILEGED_DEV))
453 ublksrv_apply_oom_protection();
454
455 info_array = (struct ublksrv_queue_info *)calloc(sizeof(
456 struct ublksrv_queue_info),
457 dinfo->nr_hw_queues);
458
459 for (i = 0; i < dinfo->nr_hw_queues; i++) {
460 info_array[i].dev = dev;
461 info_array[i].qid = i;
462 pthread_create(&info_array[i].thread, NULL,
463 ublksrv_io_handler_fn,
464 &info_array[i]);
465 }
466
467 /* wait until we are terminated */
468 ublksrv_drain_fetch_commands(dev, info_array);
469 free(info_array);
470 free(jbuf);
471
472 ublksrv_dev_deinit(dev);
473 out:
474 ublk_log("end ublksrv io daemon");
475 closelog();
476 }
477
478 /* Not called from ublksrv daemon */
ublksrv_start_io_daemon(const struct ublksrv_ctrl_dev * dev)479 static int ublksrv_start_io_daemon(const struct ublksrv_ctrl_dev *dev)
480 {
481 start_daemon(ublksrv_io_handler, (void *)dev);
482 return 0;
483 }
484
ublksrv_check_dev_data(const char * buf,int size)485 static int ublksrv_check_dev_data(const char *buf, int size)
486 {
487 struct ublk_params p;
488
489 if (size < JSON_OFFSET)
490 return -EINVAL;
491
492 return ublksrv_json_read_params(&p, &buf[JSON_OFFSET]);
493 }
494
ublksrv_get_io_daemon_pid(const struct ublksrv_ctrl_dev * ctrl_dev,bool check_data)495 static int ublksrv_get_io_daemon_pid(const struct ublksrv_ctrl_dev *ctrl_dev,
496 bool check_data)
497 {
498 const char *run_dir = ublksrv_ctrl_get_run_dir(ctrl_dev);
499 const struct ublksrv_ctrl_dev_info *info =
500 ublksrv_ctrl_get_dev_info(ctrl_dev);
501 int ret = -1, pid_fd;
502 char path[256];
503 char *buf = NULL;
504 int size = JSON_OFFSET;
505 int daemon_pid;
506 struct stat st;
507
508 if (!run_dir)
509 return -EINVAL;
510
511 snprintf(path, 256, "%s/%d.pid", run_dir, info->dev_id);
512
513 pid_fd = open(path, O_RDONLY);
514 if (pid_fd < 0)
515 goto out;
516
517 if (fstat(pid_fd, &st) < 0)
518 goto out;
519
520 if (check_data)
521 size = st.st_size;
522 else
523 size = JSON_OFFSET;
524
525 buf = (char *)malloc(size);
526 if (read(pid_fd, buf, size) <= 0)
527 goto out;
528
529 daemon_pid = strtol(buf, NULL, 10);
530 if (daemon_pid < 0)
531 goto out;
532
533 ret = kill(daemon_pid, 0);
534 if (ret)
535 goto out;
536
537 if (check_data) {
538 ret = ublksrv_check_dev_data(buf, size);
539 if (ret)
540 goto out;
541 }
542 ret = daemon_pid;
543 out:
544 if (pid_fd > 0)
545 close(pid_fd);
546 free(buf);
547 return ret;
548 }
549
550 /* Not called from ublksrv daemon */
ublksrv_stop_io_daemon(const struct ublksrv_ctrl_dev * ctrl_dev)551 static int ublksrv_stop_io_daemon(const struct ublksrv_ctrl_dev *ctrl_dev)
552 {
553 int daemon_pid, cnt = 0;
554
555 /* wait until daemon is exited, or timeout after 3 seconds */
556 do {
557 daemon_pid = ublksrv_get_io_daemon_pid(ctrl_dev, false);
558 if (daemon_pid > 0) {
559 usleep(100000);
560 cnt++;
561 }
562 } while (daemon_pid > 0 && cnt < 30);
563
564 if (daemon_pid > 0)
565 return -1;
566
567 return 0;
568 }
569
570 /* Wait until ublk device is setup by udev */
ublksrv_check_dev(const struct ublksrv_ctrl_dev_info * info)571 static void ublksrv_check_dev(const struct ublksrv_ctrl_dev_info *info)
572 {
573 unsigned int max_time = 1000000, wait = 0;
574 char buf[64];
575
576 snprintf(buf, 64, "%s%d", "/dev/ublkc", info->dev_id);
577
578 while (wait < max_time) {
579 int fd = open(buf, O_RDWR);
580
581 if (fd > 0) {
582 close(fd);
583 break;
584 }
585
586 usleep(100000);
587 wait += 100000;
588 }
589 }
590
ublksrv_start_daemon(struct ublksrv_ctrl_dev * ctrl_dev)591 static int ublksrv_start_daemon(struct ublksrv_ctrl_dev *ctrl_dev)
592 {
593 const struct ublksrv_ctrl_dev_info *dinfo =
594 ublksrv_ctrl_get_dev_info(ctrl_dev);
595 int cnt = 0, daemon_pid, ret;
596
597 ublksrv_check_dev(dinfo);
598
599 ret = ublksrv_ctrl_get_affinity(ctrl_dev);
600 if (ret < 0) {
601 fprintf(stderr, "dev %d get affinity failed %d\n",
602 dinfo->dev_id, ret);
603 return -1;
604 }
605
606 switch (fork()) {
607 case -1:
608 return -1;
609 case 0:
610 ublksrv_start_io_daemon(ctrl_dev);
611 break;
612 }
613
614 /* wait until daemon is started, or timeout after 3 seconds */
615 do {
616 daemon_pid = ublksrv_get_io_daemon_pid(ctrl_dev, true);
617 if (daemon_pid < 0) {
618 usleep(100000);
619 cnt++;
620 }
621 } while (daemon_pid < 0 && cnt < 30);
622
623 return daemon_pid;
624 }
625
626 //todo: resolve stack usage warning for mkpath/__mkpath
627 #pragma GCC diagnostic push
628 #pragma GCC diagnostic ignored "-Wstack-usage="
__mkpath(char * dir,mode_t mode)629 static int __mkpath(char *dir, mode_t mode)
630 {
631 struct stat sb;
632 int ret;
633 mode_t mask;
634
635 if (!dir)
636 return -EINVAL;
637
638 if (!stat(dir, &sb))
639 return 0;
640
641 __mkpath(dirname(strdupa(dir)), mode);
642
643 mask = umask(0);
644 ret = mkdir(dir, mode);
645 umask(mask);
646
647 return ret;
648 }
649
mkpath(const char * dir)650 static int mkpath(const char *dir)
651 {
652 return __mkpath(strdupa(dir), S_IRWXU | S_IRWXG | S_IRWXO);
653 }
654 #pragma GCC diagnostic pop
655
ublksrv_tgt_set_params(struct ublksrv_ctrl_dev * cdev,const char * jbuf)656 static void ublksrv_tgt_set_params(struct ublksrv_ctrl_dev *cdev,
657 const char *jbuf)
658 {
659 const struct ublksrv_ctrl_dev_info *info =
660 ublksrv_ctrl_get_dev_info(cdev);
661 int dev_id = info->dev_id;
662 struct ublk_params p;
663 int ret;
664
665 ret = ublksrv_json_read_params(&p, jbuf);
666 if (ret >= 0) {
667 ret = ublksrv_ctrl_set_params(cdev, &p);
668 if (ret)
669 fprintf(stderr, "set param for dev %d failed %d\n",
670 dev_id, ret);
671 } else {
672 fprintf(stderr, "params not found for dev %d failed %d\n",
673 dev_id, ret);
674 }
675 }
676
cmd_dev_add(int argc,char * argv[])677 static int cmd_dev_add(int argc, char *argv[])
678 {
679 static const struct option longopts[] = {
680 { "type", 1, NULL, 't' },
681 { "number", 1, NULL, 'n' },
682 { "queues", 1, NULL, 'q' },
683 { "depth", 1, NULL, 'd' },
684 { "zero_copy", 1, NULL, 'z' },
685 { "uring_comp", 1, NULL, 'u' },
686 { "need_get_data", 1, NULL, 'g' },
687 { "user_recovery", 1, NULL, 'r'},
688 { "user_recovery_reissue", 1, NULL, 'i'},
689 { "debug_mask", 1, NULL, 0},
690 { "unprivileged", 0, NULL, 0},
691 { "usercopy", 0, NULL, 0},
692 { NULL }
693 };
694 struct ublksrv_dev_data data = {0};
695 struct ublksrv_ctrl_dev *dev;
696 const struct ublksrv_tgt_type *tgt_type;
697 int opt, ret;
698 int uring_comp = 0;
699 int need_get_data = 0;
700 int user_recovery = 0;
701 int user_recovery_reissue = 0;
702 int unprivileged = 0;
703 const char *dump_buf;
704 int option_index = 0;
705 unsigned int debug_mask = 0;
706
707 data.queue_depth = DEF_QD;
708 data.nr_hw_queues = DEF_NR_HW_QUEUES;
709 data.dev_id = -1;
710 data.run_dir = UBLKSRV_PID_DIR;
711
712 mkpath(data.run_dir);
713
714 while ((opt = getopt_long(argc, argv, "-:t:n:d:q:u:g:r:i:z",
715 longopts, &option_index)) != -1) {
716 switch (opt) {
717 case 'n':
718 data.dev_id = strtol(optarg, NULL, 10);
719 break;
720 case 't':
721 data.tgt_type = optarg;
722 break;
723 case 'z':
724 data.flags |= UBLK_F_SUPPORT_ZERO_COPY;
725 break;
726 case 'q':
727 data.nr_hw_queues = strtol(optarg, NULL, 10);
728 break;
729 case 'd':
730 data.queue_depth = strtol(optarg, NULL, 10);
731 break;
732 case 'u':
733 uring_comp = strtol(optarg, NULL, 10);
734 break;
735 case 'g':
736 need_get_data = strtol(optarg, NULL, 10);
737 break;
738 case 'r':
739 user_recovery = strtol(optarg, NULL, 10);
740 break;
741 case 'i':
742 user_recovery_reissue = strtol(optarg, NULL, 10);
743 break;
744 case 0:
745 if (!strcmp(longopts[option_index].name, "debug_mask"))
746 debug_mask = strtol(optarg, NULL, 16);
747 if (!strcmp(longopts[option_index].name, "unprivileged"))
748 unprivileged = 1;
749 if (!strcmp(longopts[option_index].name, "usercopy"))
750 data.flags |= UBLK_F_USER_COPY;
751 break;
752 }
753 }
754
755 ublk_set_debug_mask(debug_mask);
756
757 data.max_io_buf_bytes = DEF_BUF_SIZE;
758 if (data.nr_hw_queues > MAX_NR_HW_QUEUES)
759 data.nr_hw_queues = MAX_NR_HW_QUEUES;
760 if (data.queue_depth > MAX_QD)
761 data.queue_depth = MAX_QD;
762 if (uring_comp)
763 data.flags |= UBLK_F_URING_CMD_COMP_IN_TASK;
764 if (need_get_data)
765 data.flags |= UBLK_F_NEED_GET_DATA;
766 if (user_recovery)
767 data.flags |= UBLK_F_USER_RECOVERY;
768 if (user_recovery_reissue)
769 data.flags |= UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_REISSUE;
770 if (unprivileged)
771 data.flags |= UBLK_F_UNPRIVILEGED_DEV;
772
773 if (data.tgt_type == NULL) {
774 fprintf(stderr, "no dev type specified\n");
775 return -EINVAL;
776 }
777 tgt_type = ublksrv_find_tgt_type(data.tgt_type);
778 if (tgt_type == NULL) {
779 fprintf(stderr, "unknown dev type: %s\n", data.tgt_type);
780 return -EINVAL;
781 }
782 data.tgt_ops = tgt_type;
783 data.flags |= tgt_type->ublk_flags;
784 data.ublksrv_flags |= tgt_type->ublksrv_flags;
785
786 //optind = 0; /* so that tgt code can parse their arguments */
787 data.tgt_argc = argc;
788 data.tgt_argv = argv;
789 dev = ublksrv_ctrl_init(&data);
790 if (!dev) {
791 fprintf(stderr, "can't init dev %d\n", data.dev_id);
792 return -ENODEV;
793 }
794
795 ret = ublksrv_ctrl_add_dev(dev);
796 if (ret < 0) {
797 fprintf(stderr, "can't add dev %d, ret %d\n", data.dev_id, ret);
798 goto fail;
799 }
800
801 {
802 const struct ublksrv_ctrl_dev_info *info =
803 ublksrv_ctrl_get_dev_info(dev);
804 data.dev_id = info->dev_id;
805 }
806 ret = ublksrv_start_daemon(dev);
807 if (ret <= 0) {
808 fprintf(stderr, "start dev %d daemon failed, ret %d\n",
809 data.dev_id, ret);
810 goto fail_del_dev;
811 }
812
813 dump_buf = ublksrv_tgt_get_dev_data(dev);
814 ublksrv_tgt_set_params(dev, dump_buf);
815
816 ret = ublksrv_ctrl_start_dev(dev, ret);
817 if (ret < 0) {
818 fprintf(stderr, "start dev %d failed, ret %d\n", data.dev_id,
819 ret);
820 goto fail_stop_daemon;
821 }
822 ret = ublksrv_ctrl_get_info(dev);
823 ublksrv_ctrl_dump(dev, dump_buf);
824 ublksrv_ctrl_deinit(dev);
825 return 0;
826
827 fail_stop_daemon:
828 ublksrv_stop_io_daemon(dev);
829 fail_del_dev:
830 ublksrv_ctrl_del_dev(dev);
831 fail:
832 ublksrv_ctrl_deinit(dev);
833
834 return ret;
835 }
836
837 struct tgt_types_name {
838 unsigned pos;
839 char names[4096 - sizeof(unsigned)];
840 };
841
collect_tgt_types(unsigned int idx,const struct ublksrv_tgt_type * type,void * pdata)842 static void collect_tgt_types(unsigned int idx,
843 const struct ublksrv_tgt_type *type, void *pdata)
844 {
845 struct tgt_types_name *data = (struct tgt_types_name *)pdata;
846
847 if (idx > 0)
848 data->pos += snprintf(data->names + data->pos,
849 sizeof(data->names) - data->pos, "|");
850 data->pos += snprintf(data->names + data->pos,
851 sizeof(data->names) - data->pos, "%s", type->name);
852 }
853
show_tgt_add_usage(unsigned int idx,const struct ublksrv_tgt_type * type,void * data)854 static void show_tgt_add_usage(unsigned int idx,
855 const struct ublksrv_tgt_type *type, void *data)
856 {
857 if (type->usage_for_add)
858 type->usage_for_add();
859 }
860
cmd_dev_add_usage(const char * cmd)861 static void cmd_dev_add_usage(const char *cmd)
862 {
863 struct tgt_types_name data = {
864 .pos = 0,
865 };
866
867 data.pos += snprintf(data.names + data.pos, sizeof(data.names) - data.pos, "{");
868 ublksrv_for_each_tgt_type(collect_tgt_types, &data);
869 data.pos += snprintf(data.names + data.pos, sizeof(data.names) - data.pos, "}");
870
871 printf("%s add -t %s\n", cmd, data.names);
872 printf("\t-n DEV_ID -q NR_HW_QUEUES -d QUEUE_DEPTH\n");
873 printf("\t-u URING_COMP -g NEED_GET_DATA -r USER_RECOVERY\n");
874 printf("\t-i USER_RECOVERY_REISSUE --debug_mask=0x{DBG_MASK}\n");
875 printf("\t--unprivileged\n\n");
876 printf("\ttarget specific command line:\n");
877 ublksrv_for_each_tgt_type(show_tgt_add_usage, NULL);
878 }
879
__cmd_dev_del(int number,bool log,bool async)880 static int __cmd_dev_del(int number, bool log, bool async)
881 {
882 struct ublksrv_ctrl_dev *dev;
883 int ret;
884 struct ublksrv_dev_data data = {
885 .dev_id = number,
886 .run_dir = UBLKSRV_PID_DIR,
887 };
888
889 dev = ublksrv_ctrl_init(&data);
890
891 ret = ublksrv_ctrl_get_info(dev);
892 if (ret < 0) {
893 ret = 0;
894 if (log)
895 fprintf(stderr, "can't get dev info from %d: %d\n", number, ret);
896 goto fail;
897 }
898
899 ret = ublksrv_ctrl_stop_dev(dev);
900 if (ret < 0) {
901 fprintf(stderr, "stop dev %d failed\n", number);
902 goto fail;
903 }
904
905 ret = ublksrv_stop_io_daemon(dev);
906 if (ret < 0)
907 fprintf(stderr, "stop daemon %d failed\n", number);
908
909 if (async)
910 ret = ublksrv_ctrl_del_dev_async(dev);
911 else
912 ret = ublksrv_ctrl_del_dev(dev);
913 if (ret < 0) {
914 fprintf(stderr, "delete dev %d failed %d\n", number, ret);
915 goto fail;
916 }
917
918 fail:
919 ublksrv_ctrl_deinit(dev);
920 return ret;
921 }
922
cmd_dev_del(int argc,char * argv[])923 static int cmd_dev_del(int argc, char *argv[])
924 {
925 static const struct option longopts[] = {
926 { "number", 1, NULL, 'n' },
927 { "all", 0, NULL, 'a' },
928 { "async", 0, NULL, 0 },
929 { NULL }
930 };
931 int number = -1;
932 int opt, ret, i;
933 unsigned async = 0;
934 int option_index = 0;
935
936 while ((opt = getopt_long(argc, argv, "n:a",
937 longopts, &option_index)) != -1) {
938 switch (opt) {
939 case 'a':
940 break;
941
942 case 'n':
943 number = strtol(optarg, NULL, 10);
944 break;
945 case 0:
946 if (!strcmp(longopts[option_index].name, "async"))
947 async = 1;
948 }
949 }
950
951 if (number >= 0)
952 return __cmd_dev_del(number, true, async);
953
954 for (i = 0; i < MAX_NR_UBLK_DEVS; i++)
955 ret = __cmd_dev_del(i, false, async);
956
957 return ret;
958 }
959
cmd_dev_del_usage(const char * cmd)960 static void cmd_dev_del_usage(const char *cmd)
961 {
962 printf("%s del -n DEV_ID [-a | --all]\n", cmd);
963 }
964
list_one_dev(int number,bool log,bool verbose)965 static int list_one_dev(int number, bool log, bool verbose)
966 {
967 struct ublksrv_dev_data data = {
968 .dev_id = number,
969 .run_dir = UBLKSRV_PID_DIR,
970 };
971 struct ublksrv_ctrl_dev *dev = ublksrv_ctrl_init(&data);
972 int ret;
973
974 ret = ublksrv_ctrl_get_info(dev);
975 if (ret < 0) {
976 if (log)
977 fprintf(stderr, "can't get dev info from %d: %d\n", number, ret);
978 } else {
979 const char *buf = ublksrv_tgt_get_dev_data(dev);
980
981 if (verbose)
982 ublksrv_json_dump(buf);
983 else
984 ublksrv_ctrl_dump(dev, buf);
985 }
986
987 ublksrv_ctrl_deinit(dev);
988
989 return ret;
990 }
991
cmd_list_dev_info(int argc,char * argv[])992 static int cmd_list_dev_info(int argc, char *argv[])
993 {
994 static const struct option longopts[] = {
995 { "number", 0, NULL, 'n' },
996 { "verbose", 0, NULL, 'v' },
997 { NULL }
998 };
999 int number = -1;
1000 int opt, i;
1001 bool verbose = false;
1002
1003 while ((opt = getopt_long(argc, argv, "n:v",
1004 longopts, NULL)) != -1) {
1005 switch (opt) {
1006 case 'n':
1007 number = strtol(optarg, NULL, 10);
1008 break;
1009 case 'v':
1010 verbose = 1;
1011 break;
1012 }
1013 }
1014
1015 if (number >= 0)
1016 return list_one_dev(number, true, verbose);
1017
1018 for (i = 0; i < MAX_NR_UBLK_DEVS; i++)
1019 list_one_dev(i, false, verbose);
1020
1021 return 0;
1022 }
1023
cmd_dev_list_usage(const char * cmd)1024 static void cmd_dev_list_usage(const char *cmd)
1025 {
1026 printf("%s list [-n DEV_ID]\n", cmd);
1027 }
1028
1029 #define const_ilog2(x) (63 - __builtin_clzll(x))
1030
cmd_dev_get_features(int argc,char * argv[])1031 static int cmd_dev_get_features(int argc, char *argv[])
1032 {
1033 struct ublksrv_dev_data data = {
1034 .dev_id = -1,
1035 .run_dir = UBLKSRV_PID_DIR,
1036 };
1037 struct ublksrv_ctrl_dev *dev = ublksrv_ctrl_init(&data);
1038 __u64 features = 0;
1039 int ret;
1040 static const char *feat_map[] = {
1041 [const_ilog2(UBLK_F_SUPPORT_ZERO_COPY)] = "ZERO_COPY",
1042 [const_ilog2(UBLK_F_URING_CMD_COMP_IN_TASK)] = "COMP_IN_TASK",
1043 [const_ilog2(UBLK_F_NEED_GET_DATA)] = "GET_DATA",
1044 [const_ilog2(UBLK_F_USER_RECOVERY)] = "USER_RECOVERY",
1045 [const_ilog2(UBLK_F_USER_RECOVERY_REISSUE)] = "RECOVERY_REISSUE",
1046 [const_ilog2(UBLK_F_UNPRIVILEGED_DEV)] = "UNPRIVILEGED_DEV",
1047 [const_ilog2(UBLK_F_CMD_IOCTL_ENCODE)] = "CMD_IOCTL_ENCODE",
1048 };
1049
1050 ret = ublksrv_ctrl_get_features(dev, &features);
1051 if (!ret) {
1052 int i;
1053
1054 printf("ublk_drv features: 0x%llx\n", features);
1055
1056 for (i = 0; i < sizeof(features); i++) {
1057 const char *feat;
1058
1059 if (!((1ULL << i) & features))
1060 continue;
1061 if (i < sizeof(feat_map) / sizeof(feat_map[0]))
1062 feat = feat_map[i];
1063 else
1064 feat = "unknown";
1065 printf("\t%-20s: 0x%llx\n", feat, 1ULL << i);
1066 }
1067 }
1068
1069 return ret;
1070 }
1071
cmd_dev_get_features_help(const char * cmd)1072 static void cmd_dev_get_features_help(const char *cmd)
1073 {
1074 printf("%s features\n", cmd);
1075 }
1076
__cmd_dev_user_recover(int number,bool verbose)1077 static int __cmd_dev_user_recover(int number, bool verbose)
1078 {
1079 const struct ublksrv_tgt_type *tgt_type;
1080 struct ublksrv_dev_data data = {
1081 .dev_id = number,
1082 .run_dir = UBLKSRV_PID_DIR,
1083 };
1084 struct ublksrv_ctrl_dev_info dev_info;
1085 struct ublksrv_ctrl_dev *dev;
1086 struct ublksrv_tgt_base_json tgt_json = {0};
1087 char *buf = NULL;
1088 char pid_file[64];
1089 int ret;
1090 unsigned elapsed = 0;
1091
1092 dev = ublksrv_ctrl_init(&data);
1093 if (!dev) {
1094 fprintf(stderr, "ublksrv_ctrl_init failure dev %d\n", number);
1095 return -ENOMEM;
1096 }
1097
1098 ret = ublksrv_ctrl_get_info(dev);
1099 if (ret < 0) {
1100 fprintf(stderr, "can't get dev info from %d\n", number);
1101 goto fail;
1102 }
1103
1104 while (elapsed < 30000000) {
1105 unsigned unit = 100000;
1106 ret = ublksrv_ctrl_start_recovery(dev);
1107 if (ret < 0 && ret != -EBUSY) {
1108 fprintf(stderr, "can't start recovery for %d ret %d\n",
1109 number, ret);
1110 goto fail;
1111 }
1112 if (ret >= 0)
1113 break;
1114 usleep(unit);
1115 elapsed += unit;
1116 }
1117
1118 buf = ublksrv_tgt_get_dev_data(dev);
1119 if (!buf) {
1120 fprintf(stderr, "get dev %d data failed\n", number);
1121 ret = -1;
1122 goto fail;
1123 }
1124
1125 ret = ublksrv_json_read_dev_info(buf, &dev_info);
1126 if (ret < 0) {
1127 fprintf(stderr, "can't read dev info for %d\n", number);
1128 goto fail;
1129 }
1130
1131 if (dev_info.dev_id != number) {
1132 fprintf(stderr, "dev id doesn't match read %d for dev %d\n",
1133 dev_info.dev_id, number);
1134 goto fail;
1135 }
1136
1137 ret = ublksrv_json_read_target_base_info(buf, &tgt_json);
1138 if (ret < 0) {
1139 fprintf(stderr, "can't read dev info for %d\n", number);
1140 goto fail;
1141 }
1142
1143 snprintf(pid_file, 64, "%s/%d.pid", data.run_dir, number);
1144 ret = unlink(pid_file);
1145 if (ret < 0) {
1146 fprintf(stderr, "can't delete old pid_file for %d, error:%s\n",
1147 number, strerror(errno));
1148 goto fail;
1149 }
1150
1151 tgt_type = ublksrv_find_tgt_type(tgt_json.name);
1152 if (!tgt_type) {
1153 fprintf(stderr, "can't find target type %s\n", tgt_json.name);
1154 goto fail;
1155 }
1156
1157 ublksrv_ctrl_prep_recovery(dev, tgt_json.name, tgt_type, buf);
1158
1159 ret = ublksrv_start_daemon(dev);
1160 if (ret < 0) {
1161 fprintf(stderr, "start daemon %d failed\n", number);
1162 goto fail;
1163 }
1164
1165 ret = ublksrv_ctrl_end_recovery(dev, ret);
1166 if (ret < 0) {
1167 fprintf(stderr, "end recovery for %d failed\n", number);
1168 goto fail;
1169 }
1170
1171 ret = ublksrv_ctrl_get_info(dev);
1172 if (ret < 0) {
1173 fprintf(stderr, "can't get dev info from %d\n", number);
1174 goto fail;
1175 }
1176
1177 if (verbose) {
1178 free(buf);
1179 buf = ublksrv_tgt_get_dev_data(dev);
1180 ublksrv_ctrl_dump(dev, buf);
1181 }
1182
1183 fail:
1184 free(buf);
1185 ublksrv_ctrl_deinit(dev);
1186 return ret;
1187 }
1188
cmd_dev_user_recover(int argc,char * argv[])1189 static int cmd_dev_user_recover(int argc, char *argv[])
1190 {
1191 static const struct option longopts[] = {
1192 { "number", 0, NULL, 'n' },
1193 { "verbose", 0, NULL, 'v' },
1194 { NULL }
1195 };
1196 int number = -1;
1197 int opt;
1198 bool verbose = false;
1199
1200 while ((opt = getopt_long(argc, argv, "n:v",
1201 longopts, NULL)) != -1) {
1202 switch (opt) {
1203 case 'n':
1204 number = strtol(optarg, NULL, 10);
1205 break;
1206 case 'v':
1207 verbose = true;
1208 break;
1209 }
1210 }
1211
1212 return __cmd_dev_user_recover(number, verbose);
1213 }
1214
cmd_dev_recover_usage(const char * cmd)1215 static void cmd_dev_recover_usage(const char *cmd)
1216 {
1217 printf("%s recover [-n DEV_ID]\n", cmd);
1218 }
1219
cmd_usage(const char * cmd)1220 static void cmd_usage(const char *cmd)
1221 {
1222 cmd_dev_add_usage(cmd);
1223 cmd_dev_del_usage(cmd);
1224 cmd_dev_list_usage(cmd);
1225 cmd_dev_recover_usage(cmd);
1226 cmd_dev_get_features_help(cmd);
1227
1228 printf("%s -v [--version]\n", cmd);
1229 printf("%s -h [--help]\n", cmd);
1230 }
1231
main(int argc,char * argv[])1232 int main(int argc, char *argv[])
1233 {
1234 const char *prog_name = "ublk";
1235
1236 char *cmd;
1237 int ret;
1238 char exe[PATH_MAX];
1239
1240 full_cmd = argv[0];
1241 strncpy(exe, full_cmd, PATH_MAX - 1);
1242
1243 setvbuf(stdout, NULL, _IOLBF, 0);
1244
1245 cmd = pop_cmd(&argc, argv);
1246 if (cmd == NULL) {
1247 printf("%s: missing command\n", argv[0]);
1248 cmd_usage(prog_name);
1249 return EXIT_FAILURE;
1250 }
1251
1252 if (!strcmp(cmd, "add"))
1253 ret = cmd_dev_add(argc, argv);
1254 else if (!strcmp(cmd, "del"))
1255 ret = cmd_dev_del(argc, argv);
1256 else if (!strcmp(cmd, "list"))
1257 ret = cmd_list_dev_info(argc, argv);
1258 else if (!strcmp(cmd, "recover"))
1259 ret = cmd_dev_user_recover(argc, argv);
1260 else if (!strcmp(cmd, "features"))
1261 ret = cmd_dev_get_features(argc, argv);
1262 else if (!strcmp(cmd, "help") || !strcmp(cmd, "-h") || !strcmp(cmd, "--help")) {
1263 cmd_usage(prog_name);
1264 ret = EXIT_SUCCESS;
1265 } else if (!strcmp(cmd, "-v") || !strcmp(cmd, "--version")) {
1266 fprintf(stdout, "%s\n", PACKAGE_STRING);
1267 ret = EXIT_SUCCESS;
1268 } else {
1269 fprintf(stderr, "unknown command: %s\n", cmd);
1270 cmd_usage(prog_name);
1271 ret = EXIT_FAILURE;
1272 }
1273
1274 ublk_ctrl_dbg(UBLK_DBG_CTRL_CMD, "cmd %s: result %d\n", cmd, ret);
1275
1276 return ret;
1277 }
1278