]> git.proxmox.com Git - mirror_qemu.git/blob - contrib/vhost-user-scsi/vhost-user-scsi.c
Merge remote-tracking branch 'remotes/famz/tags/docker-and-block-pull-request' into...
[mirror_qemu.git] / contrib / vhost-user-scsi / vhost-user-scsi.c
1 /*
2 * vhost-user-scsi sample application
3 *
4 * Copyright (c) 2016 Nutanix Inc. All rights reserved.
5 *
6 * Author:
7 * Felipe Franciosi <felipe@nutanix.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 only.
10 * See the COPYING file in the top-level directory.
11 */
12
13 #include "qemu/osdep.h"
14 #include "contrib/libvhost-user/libvhost-user.h"
15 #include "hw/virtio/virtio-scsi.h"
16 #include "iscsi/iscsi.h"
17
18 #include <glib.h>
19
20 /* Small compat shim from glib 2.32 */
21 #ifndef G_SOURCE_CONTINUE
22 #define G_SOURCE_CONTINUE TRUE
23 #endif
24 #ifndef G_SOURCE_REMOVE
25 #define G_SOURCE_REMOVE FALSE
26 #endif
27
28 /* #define VUS_DEBUG 1 */
29
30 /** Log helpers **/
31
32 #define PPRE \
33 struct timespec ts; \
34 char timebuf[64]; \
35 struct tm tm; \
36 (void)clock_gettime(CLOCK_REALTIME, &ts); \
37 (void)strftime(timebuf, 64, "%Y%m%d %T", gmtime_r(&ts.tv_sec, &tm))
38
39 #define PEXT(lvl, msg, ...) do { \
40 PPRE; \
41 fprintf(stderr, "%s.%06ld " lvl ": %s:%s():%d: " msg "\n", \
42 timebuf, ts.tv_nsec / 1000, \
43 __FILE__, __func__, __LINE__, ## __VA_ARGS__); \
44 } while (0)
45
46 #define PNOR(lvl, msg, ...) do { \
47 PPRE; \
48 fprintf(stderr, "%s.%06ld " lvl ": " msg "\n", \
49 timebuf, ts.tv_nsec / 1000, ## __VA_ARGS__); \
50 } while (0)
51
52 #ifdef VUS_DEBUG
53 #define PDBG(msg, ...) PEXT("DBG", msg, ## __VA_ARGS__)
54 #define PERR(msg, ...) PEXT("ERR", msg, ## __VA_ARGS__)
55 #define PLOG(msg, ...) PEXT("LOG", msg, ## __VA_ARGS__)
56 #else
57 #define PDBG(msg, ...) { }
58 #define PERR(msg, ...) PNOR("ERR", msg, ## __VA_ARGS__)
59 #define PLOG(msg, ...) PNOR("LOG", msg, ## __VA_ARGS__)
60 #endif
61
62 /** vhost-user-scsi specific definitions **/
63
64 /* Only 1 LUN and device supported today */
65 #define VUS_MAX_LUNS 1
66 #define VUS_MAX_DEVS 1
67
68 #define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi"
69
70 typedef struct iscsi_lun {
71 struct iscsi_context *iscsi_ctx;
72 int iscsi_lun;
73 } iscsi_lun_t;
74
75 typedef struct vhost_scsi_dev {
76 VuDev vu_dev;
77 int server_sock;
78 GMainLoop *loop;
79 GTree *fdmap; /* fd -> gsource context id */
80 iscsi_lun_t luns[VUS_MAX_LUNS];
81 } vhost_scsi_dev_t;
82
83 static vhost_scsi_dev_t *vhost_scsi_devs[VUS_MAX_DEVS];
84
85 /** glib event loop integration for libvhost-user and misc callbacks **/
86
87 QEMU_BUILD_BUG_ON((int)G_IO_IN != (int)VU_WATCH_IN);
88 QEMU_BUILD_BUG_ON((int)G_IO_OUT != (int)VU_WATCH_OUT);
89 QEMU_BUILD_BUG_ON((int)G_IO_PRI != (int)VU_WATCH_PRI);
90 QEMU_BUILD_BUG_ON((int)G_IO_ERR != (int)VU_WATCH_ERR);
91 QEMU_BUILD_BUG_ON((int)G_IO_HUP != (int)VU_WATCH_HUP);
92
93 typedef struct vus_gsrc {
94 GSource parent;
95 vhost_scsi_dev_t *vdev_scsi;
96 GPollFD gfd;
97 vu_watch_cb vu_cb;
98 } vus_gsrc_t;
99
100 static gint vus_fdmap_compare(gconstpointer a, gconstpointer b)
101 {
102 return (b > a) - (b < a);
103 }
104
105 static gboolean vus_gsrc_prepare(GSource *src, gint *timeout)
106 {
107 assert(timeout);
108
109 *timeout = -1;
110 return FALSE;
111 }
112
113 static gboolean vus_gsrc_check(GSource *src)
114 {
115 vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
116
117 assert(vus_src);
118
119 return vus_src->gfd.revents & vus_src->gfd.events;
120 }
121
122 static gboolean vus_gsrc_dispatch(GSource *src, GSourceFunc cb, gpointer data)
123 {
124 vhost_scsi_dev_t *vdev_scsi;
125 vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
126
127 assert(vus_src);
128 assert(!(vus_src->vu_cb && cb));
129
130 vdev_scsi = vus_src->vdev_scsi;
131
132 assert(vdev_scsi);
133
134 if (cb) {
135 return cb(data);
136 }
137 if (vus_src->vu_cb) {
138 vus_src->vu_cb(&vdev_scsi->vu_dev, vus_src->gfd.revents, data);
139 }
140 return G_SOURCE_CONTINUE;
141 }
142
143 static GSourceFuncs vus_gsrc_funcs = {
144 vus_gsrc_prepare,
145 vus_gsrc_check,
146 vus_gsrc_dispatch,
147 NULL
148 };
149
150 static int vus_gsrc_new(vhost_scsi_dev_t *vdev_scsi, int fd, GIOCondition cond,
151 vu_watch_cb vu_cb, GSourceFunc gsrc_cb, gpointer data)
152 {
153 GSource *vus_gsrc;
154 vus_gsrc_t *vus_src;
155 guint id;
156
157 assert(vdev_scsi);
158 assert(fd >= 0);
159 assert(vu_cb || gsrc_cb);
160 assert(!(vu_cb && gsrc_cb));
161
162 vus_gsrc = g_source_new(&vus_gsrc_funcs, sizeof(vus_gsrc_t));
163 if (!vus_gsrc) {
164 PERR("Error creating GSource for new watch");
165 return -1;
166 }
167 vus_src = (vus_gsrc_t *)vus_gsrc;
168
169 vus_src->vdev_scsi = vdev_scsi;
170 vus_src->gfd.fd = fd;
171 vus_src->gfd.events = cond;
172 vus_src->vu_cb = vu_cb;
173
174 g_source_add_poll(vus_gsrc, &vus_src->gfd);
175 g_source_set_callback(vus_gsrc, gsrc_cb, data, NULL);
176 id = g_source_attach(vus_gsrc, NULL);
177 assert(id);
178 g_source_unref(vus_gsrc);
179
180 g_tree_insert(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd,
181 (gpointer)(uintptr_t)id);
182
183 return 0;
184 }
185
186 /* from libiscsi's scsi-lowlevel.h **
187 *
188 * nb. We can't directly include scsi-lowlevel.h due to a namespace conflict:
189 * QEMU's scsi.h also defines "SCSI_XFER_NONE".
190 */
191
192 #define SCSI_CDB_MAX_SIZE 16
193
194 struct scsi_iovector {
195 struct scsi_iovec *iov;
196 int niov;
197 int nalloc;
198 size_t offset;
199 int consumed;
200 };
201
202 struct scsi_allocated_memory {
203 struct scsi_allocated_memory *next;
204 char buf[0];
205 };
206
207 struct scsi_data {
208 int size;
209 unsigned char *data;
210 };
211
212 enum scsi_sense_key {
213 SCSI_SENSE_NO_SENSE = 0x00,
214 SCSI_SENSE_RECOVERED_ERROR = 0x01,
215 SCSI_SENSE_NOT_READY = 0x02,
216 SCSI_SENSE_MEDIUM_ERROR = 0x03,
217 SCSI_SENSE_HARDWARE_ERROR = 0x04,
218 SCSI_SENSE_ILLEGAL_REQUEST = 0x05,
219 SCSI_SENSE_UNIT_ATTENTION = 0x06,
220 SCSI_SENSE_DATA_PROTECTION = 0x07,
221 SCSI_SENSE_BLANK_CHECK = 0x08,
222 SCSI_SENSE_VENDOR_SPECIFIC = 0x09,
223 SCSI_SENSE_COPY_ABORTED = 0x0a,
224 SCSI_SENSE_COMMAND_ABORTED = 0x0b,
225 SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
226 SCSI_SENSE_OVERFLOW_COMMAND = 0x0d,
227 SCSI_SENSE_MISCOMPARE = 0x0e
228 };
229
230 struct scsi_sense {
231 unsigned char error_type;
232 enum scsi_sense_key key;
233 int ascq;
234 unsigned sense_specific:1;
235 unsigned ill_param_in_cdb:1;
236 unsigned bit_pointer_valid:1;
237 unsigned char bit_pointer;
238 uint16_t field_pointer;
239 };
240
241 enum scsi_residual {
242 SCSI_RESIDUAL_NO_RESIDUAL = 0,
243 SCSI_RESIDUAL_UNDERFLOW,
244 SCSI_RESIDUAL_OVERFLOW
245 };
246
247 struct scsi_task {
248 int status;
249 int cdb_size;
250 int xfer_dir;
251 int expxferlen;
252 unsigned char cdb[SCSI_CDB_MAX_SIZE];
253 enum scsi_residual residual_status;
254 size_t residual;
255 struct scsi_sense sense;
256 struct scsi_data datain;
257 struct scsi_allocated_memory *mem;
258 void *ptr;
259
260 uint32_t itt;
261 uint32_t cmdsn;
262 uint32_t lun;
263
264 struct scsi_iovector iovector_in;
265 struct scsi_iovector iovector_out;
266 };
267
268 /** libiscsi integration **/
269
270 static int iscsi_add_lun(iscsi_lun_t *lun, char *iscsi_uri)
271 {
272 struct iscsi_url *iscsi_url;
273 struct iscsi_context *iscsi_ctx;
274 int ret = 0;
275
276 assert(lun);
277 assert(iscsi_uri);
278
279 iscsi_ctx = iscsi_create_context(VUS_ISCSI_INITIATOR);
280 if (!iscsi_ctx) {
281 PERR("Unable to create iSCSI context");
282 return -1;
283 }
284
285 iscsi_url = iscsi_parse_full_url(iscsi_ctx, iscsi_uri);
286 if (!iscsi_url) {
287 PERR("Unable to parse iSCSI URL: %s", iscsi_get_error(iscsi_ctx));
288 goto fail;
289 }
290
291 iscsi_set_session_type(iscsi_ctx, ISCSI_SESSION_NORMAL);
292 iscsi_set_header_digest(iscsi_ctx, ISCSI_HEADER_DIGEST_NONE_CRC32C);
293 if (iscsi_full_connect_sync(iscsi_ctx, iscsi_url->portal, iscsi_url->lun)) {
294 PERR("Unable to login to iSCSI portal: %s", iscsi_get_error(iscsi_ctx));
295 goto fail;
296 }
297
298 lun->iscsi_ctx = iscsi_ctx;
299 lun->iscsi_lun = iscsi_url->lun;
300
301 PDBG("Context %p created for lun 0: %s", iscsi_ctx, iscsi_uri);
302
303 out:
304 if (iscsi_url) {
305 iscsi_destroy_url(iscsi_url);
306 }
307 return ret;
308
309 fail:
310 (void)iscsi_destroy_context(iscsi_ctx);
311 ret = -1;
312 goto out;
313 }
314
315 static struct scsi_task *scsi_task_new(int cdb_len, uint8_t *cdb, int dir,
316 int xfer_len) {
317 struct scsi_task *task;
318
319 assert(cdb_len > 0);
320 assert(cdb);
321
322 task = calloc(1, sizeof(struct scsi_task));
323 if (!task) {
324 PERR("Error allocating task: %s", strerror(errno));
325 return NULL;
326 }
327
328 memcpy(task->cdb, cdb, cdb_len);
329 task->cdb_size = cdb_len;
330 task->xfer_dir = dir;
331 task->expxferlen = xfer_len;
332
333 return task;
334 }
335
336 static int get_cdb_len(uint8_t *cdb)
337 {
338 assert(cdb);
339
340 switch (cdb[0] >> 5) {
341 case 0: return 6;
342 case 1: /* fall through */
343 case 2: return 10;
344 case 4: return 16;
345 case 5: return 12;
346 }
347 PERR("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5);
348 return -1;
349 }
350
351 static int handle_cmd_sync(struct iscsi_context *ctx,
352 VirtIOSCSICmdReq *req,
353 struct iovec *out, unsigned int out_len,
354 VirtIOSCSICmdResp *rsp,
355 struct iovec *in, unsigned int in_len) {
356 struct scsi_task *task;
357 uint32_t dir;
358 uint32_t len;
359 int cdb_len;
360 int i;
361
362 assert(ctx);
363 assert(req);
364 assert(rsp);
365
366 if (!(!req->lun[1] && req->lun[2] == 0x40 && !req->lun[3])) {
367 /* Ignore anything different than target=0, lun=0 */
368 PDBG("Ignoring unconnected lun (0x%hhX, 0x%hhX)",
369 req->lun[1], req->lun[3]);
370 rsp->status = SCSI_STATUS_CHECK_CONDITION;
371 memset(rsp->sense, 0, sizeof(rsp->sense));
372 rsp->sense_len = 18;
373 rsp->sense[0] = 0x70;
374 rsp->sense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
375 rsp->sense[7] = 10;
376 rsp->sense[12] = 0x24;
377
378 return 0;
379 }
380
381 cdb_len = get_cdb_len(req->cdb);
382 if (cdb_len == -1) {
383 return -1;
384 }
385
386 len = 0;
387 if (!out_len && !in_len) {
388 dir = SCSI_XFER_NONE;
389 } else if (out_len) {
390 dir = SCSI_XFER_TO_DEV;
391 for (i = 0; i < out_len; i++) {
392 len += out[i].iov_len;
393 }
394 } else {
395 dir = SCSI_XFER_FROM_DEV;
396 for (i = 0; i < in_len; i++) {
397 len += in[i].iov_len;
398 }
399 }
400
401 task = scsi_task_new(cdb_len, req->cdb, dir, len);
402 if (!task) {
403 PERR("Unable to create iscsi task");
404 return -1;
405 }
406
407 if (dir == SCSI_XFER_TO_DEV) {
408 task->iovector_out.iov = (struct scsi_iovec *)out;
409 task->iovector_out.niov = out_len;
410 } else if (dir == SCSI_XFER_FROM_DEV) {
411 task->iovector_in.iov = (struct scsi_iovec *)in;
412 task->iovector_in.niov = in_len;
413 }
414
415 PDBG("Sending iscsi cmd (cdb_len=%d, dir=%d, task=%p)",
416 cdb_len, dir, task);
417 if (!iscsi_scsi_command_sync(ctx, 0, task, NULL)) {
418 PERR("Error serving SCSI command");
419 free(task);
420 return -1;
421 }
422
423 memset(rsp, 0, sizeof(*rsp));
424
425 rsp->status = task->status;
426 rsp->resid = task->residual;
427
428 if (task->status == SCSI_STATUS_CHECK_CONDITION) {
429 rsp->response = VIRTIO_SCSI_S_FAILURE;
430 rsp->sense_len = task->datain.size - 2;
431 memcpy(rsp->sense, &task->datain.data[2], rsp->sense_len);
432 }
433
434 free(task);
435
436 PDBG("Filled in rsp: status=%hhX, resid=%u, response=%hhX, sense_len=%u",
437 rsp->status, rsp->resid, rsp->response, rsp->sense_len);
438
439 return 0;
440 }
441
442 /** libvhost-user callbacks **/
443
444 static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev);
445
446 static void vus_panic_cb(VuDev *vu_dev, const char *buf)
447 {
448 vhost_scsi_dev_t *vdev_scsi;
449
450 assert(vu_dev);
451
452 vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
453
454 if (buf) {
455 PERR("vu_panic: %s", buf);
456 }
457
458 if (vdev_scsi) {
459 assert(vdev_scsi->loop);
460 g_main_loop_quit(vdev_scsi->loop);
461 }
462 }
463
464 static void vus_add_watch_cb(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb,
465 void *pvt) {
466 vhost_scsi_dev_t *vdev_scsi;
467 guint id;
468
469 assert(vu_dev);
470 assert(fd >= 0);
471 assert(cb);
472
473 vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
474 if (!vdev_scsi) {
475 vus_panic_cb(vu_dev, NULL);
476 return;
477 }
478
479 id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
480 (gpointer)(uintptr_t)fd);
481 if (id) {
482 GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
483 assert(vus_src);
484 g_source_destroy(vus_src);
485 (void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
486 }
487
488 if (vus_gsrc_new(vdev_scsi, fd, vu_evt, cb, NULL, pvt)) {
489 vus_panic_cb(vu_dev, NULL);
490 }
491 }
492
493 static void vus_del_watch_cb(VuDev *vu_dev, int fd)
494 {
495 vhost_scsi_dev_t *vdev_scsi;
496 guint id;
497
498 assert(vu_dev);
499 assert(fd >= 0);
500
501 vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
502 if (!vdev_scsi) {
503 vus_panic_cb(vu_dev, NULL);
504 return;
505 }
506
507 id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
508 (gpointer)(uintptr_t)fd);
509 if (id) {
510 GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
511 assert(vus_src);
512 g_source_destroy(vus_src);
513 (void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
514 }
515 }
516
517 static void vus_proc_ctl(VuDev *vu_dev, int idx)
518 {
519 /* Control VQ not implemented */
520 }
521
522 static void vus_proc_evt(VuDev *vu_dev, int idx)
523 {
524 /* Event VQ not implemented */
525 }
526
527 static void vus_proc_req(VuDev *vu_dev, int idx)
528 {
529 vhost_scsi_dev_t *vdev_scsi;
530 VuVirtq *vq;
531
532 assert(vu_dev);
533
534 vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
535 if (!vdev_scsi) {
536 vus_panic_cb(vu_dev, NULL);
537 return;
538 }
539
540 if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
541 PERR("VQ Index out of range: %d", idx);
542 vus_panic_cb(vu_dev, NULL);
543 return;
544 }
545
546 vq = vu_get_queue(vu_dev, idx);
547 if (!vq) {
548 PERR("Error fetching VQ (dev=%p, idx=%d)", vu_dev, idx);
549 vus_panic_cb(vu_dev, NULL);
550 return;
551 }
552
553 PDBG("Got kicked on vq[%d]@%p", idx, vq);
554
555 while (1) {
556 VuVirtqElement *elem;
557 VirtIOSCSICmdReq *req;
558 VirtIOSCSICmdResp *rsp;
559
560 elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
561 if (!elem) {
562 PDBG("No more elements pending on vq[%d]@%p", idx, vq);
563 break;
564 }
565 PDBG("Popped elem@%p", elem);
566
567 assert(!((elem->out_num > 1) && (elem->in_num > 1)));
568 assert((elem->out_num > 0) && (elem->in_num > 0));
569
570 if (elem->out_sg[0].iov_len < sizeof(VirtIOSCSICmdReq)) {
571 PERR("Invalid virtio-scsi req header");
572 vus_panic_cb(vu_dev, NULL);
573 break;
574 }
575 req = (VirtIOSCSICmdReq *)elem->out_sg[0].iov_base;
576
577 if (elem->in_sg[0].iov_len < sizeof(VirtIOSCSICmdResp)) {
578 PERR("Invalid virtio-scsi rsp header");
579 vus_panic_cb(vu_dev, NULL);
580 break;
581 }
582 rsp = (VirtIOSCSICmdResp *)elem->in_sg[0].iov_base;
583
584 if (handle_cmd_sync(vdev_scsi->luns[0].iscsi_ctx,
585 req, &elem->out_sg[1], elem->out_num - 1,
586 rsp, &elem->in_sg[1], elem->in_num - 1) != 0) {
587 vus_panic_cb(vu_dev, NULL);
588 break;
589 }
590
591 vu_queue_push(vu_dev, vq, elem, 0);
592 vu_queue_notify(vu_dev, vq);
593
594 free(elem);
595 }
596 }
597
598 static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)
599 {
600 VuVirtq *vq;
601
602 assert(vu_dev);
603
604 if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
605 PERR("VQ Index out of range: %d", idx);
606 vus_panic_cb(vu_dev, NULL);
607 return;
608 }
609
610 vq = vu_get_queue(vu_dev, idx);
611
612 switch (idx) {
613 case 0:
614 vu_set_queue_handler(vu_dev, vq, started ? vus_proc_ctl : NULL);
615 break;
616 case 1:
617 vu_set_queue_handler(vu_dev, vq, started ? vus_proc_evt : NULL);
618 break;
619 default:
620 vu_set_queue_handler(vu_dev, vq, started ? vus_proc_req : NULL);
621 }
622 }
623
624 static const VuDevIface vus_iface = {
625 .queue_set_started = vus_queue_set_started,
626 };
627
628 static gboolean vus_vhost_cb(gpointer data)
629 {
630 VuDev *vu_dev = (VuDev *)data;
631
632 assert(vu_dev);
633
634 if (!vu_dispatch(vu_dev) != 0) {
635 PERR("Error processing vhost message");
636 vus_panic_cb(vu_dev, NULL);
637 return G_SOURCE_REMOVE;
638 }
639
640 return G_SOURCE_CONTINUE;
641 }
642
643 /** misc helpers **/
644
645 static int unix_sock_new(char *unix_fn)
646 {
647 int sock;
648 struct sockaddr_un un;
649 size_t len;
650
651 assert(unix_fn);
652
653 sock = socket(AF_UNIX, SOCK_STREAM, 0);
654 if (sock <= 0) {
655 perror("socket");
656 return -1;
657 }
658
659 un.sun_family = AF_UNIX;
660 (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
661 len = sizeof(un.sun_family) + strlen(un.sun_path);
662
663 (void)unlink(unix_fn);
664 if (bind(sock, (struct sockaddr *)&un, len) < 0) {
665 perror("bind");
666 goto fail;
667 }
668
669 if (listen(sock, 1) < 0) {
670 perror("listen");
671 goto fail;
672 }
673
674 return sock;
675
676 fail:
677 (void)close(sock);
678
679 return -1;
680 }
681
682 /** vhost-user-scsi **/
683
684 static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev)
685 {
686 int i;
687
688 assert(vu_dev);
689
690 for (i = 0; i < VUS_MAX_DEVS; i++) {
691 if (&vhost_scsi_devs[i]->vu_dev == vu_dev) {
692 return vhost_scsi_devs[i];
693 }
694 }
695
696 PERR("Unknown VuDev %p", vu_dev);
697 return NULL;
698 }
699
700 static void vdev_scsi_deinit(vhost_scsi_dev_t *vdev_scsi)
701 {
702 if (!vdev_scsi) {
703 return;
704 }
705
706 if (vdev_scsi->server_sock >= 0) {
707 struct sockaddr_storage ss;
708 socklen_t sslen = sizeof(ss);
709
710 if (getsockname(vdev_scsi->server_sock, (struct sockaddr *)&ss,
711 &sslen) == 0) {
712 struct sockaddr_un *su = (struct sockaddr_un *)&ss;
713 (void)unlink(su->sun_path);
714 }
715
716 (void)close(vdev_scsi->server_sock);
717 vdev_scsi->server_sock = -1;
718 }
719
720 if (vdev_scsi->loop) {
721 g_main_loop_unref(vdev_scsi->loop);
722 vdev_scsi->loop = NULL;
723 }
724 }
725
726 static vhost_scsi_dev_t *vdev_scsi_new(char *unix_fn)
727 {
728 vhost_scsi_dev_t *vdev_scsi = NULL;
729
730 assert(unix_fn);
731
732 vdev_scsi = calloc(1, sizeof(vhost_scsi_dev_t));
733 if (!vdev_scsi) {
734 PERR("calloc: %s", strerror(errno));
735 return NULL;
736 }
737
738 vdev_scsi->server_sock = unix_sock_new(unix_fn);
739 if (vdev_scsi->server_sock < 0) {
740 goto err;
741 }
742
743 vdev_scsi->loop = g_main_loop_new(NULL, FALSE);
744 if (!vdev_scsi->loop) {
745 PERR("Error creating glib event loop");
746 goto err;
747 }
748
749 vdev_scsi->fdmap = g_tree_new(vus_fdmap_compare);
750 if (!vdev_scsi->fdmap) {
751 PERR("Error creating glib tree for fdmap");
752 goto err;
753 }
754
755 return vdev_scsi;
756
757 err:
758 vdev_scsi_deinit(vdev_scsi);
759 free(vdev_scsi);
760
761 return NULL;
762 }
763
764 static int vdev_scsi_add_iscsi_lun(vhost_scsi_dev_t *vdev_scsi,
765 char *iscsi_uri, uint32_t lun) {
766 assert(vdev_scsi);
767 assert(iscsi_uri);
768 assert(lun < VUS_MAX_LUNS);
769
770 if (vdev_scsi->luns[lun].iscsi_ctx) {
771 PERR("Lun %d already configured", lun);
772 return -1;
773 }
774
775 if (iscsi_add_lun(&vdev_scsi->luns[lun], iscsi_uri) != 0) {
776 return -1;
777 }
778
779 return 0;
780 }
781
782 static int vdev_scsi_run(vhost_scsi_dev_t *vdev_scsi)
783 {
784 int cli_sock;
785 int ret = 0;
786
787 assert(vdev_scsi);
788 assert(vdev_scsi->server_sock >= 0);
789 assert(vdev_scsi->loop);
790
791 cli_sock = accept(vdev_scsi->server_sock, (void *)0, (void *)0);
792 if (cli_sock < 0) {
793 perror("accept");
794 return -1;
795 }
796
797 vu_init(&vdev_scsi->vu_dev,
798 cli_sock,
799 vus_panic_cb,
800 vus_add_watch_cb,
801 vus_del_watch_cb,
802 &vus_iface);
803
804 if (vus_gsrc_new(vdev_scsi, cli_sock, G_IO_IN, NULL, vus_vhost_cb,
805 &vdev_scsi->vu_dev)) {
806 goto fail;
807 }
808
809 g_main_loop_run(vdev_scsi->loop);
810
811 out:
812 vu_deinit(&vdev_scsi->vu_dev);
813
814 return ret;
815
816 fail:
817 ret = -1;
818 goto out;
819 }
820
821 int main(int argc, char **argv)
822 {
823 vhost_scsi_dev_t *vdev_scsi = NULL;
824 char *unix_fn = NULL;
825 char *iscsi_uri = NULL;
826 int opt, err = EXIT_SUCCESS;
827
828 while ((opt = getopt(argc, argv, "u:i:")) != -1) {
829 switch (opt) {
830 case 'h':
831 goto help;
832 case 'u':
833 unix_fn = strdup(optarg);
834 break;
835 case 'i':
836 iscsi_uri = strdup(optarg);
837 break;
838 default:
839 goto help;
840 }
841 }
842 if (!unix_fn || !iscsi_uri) {
843 goto help;
844 }
845
846 vdev_scsi = vdev_scsi_new(unix_fn);
847 if (!vdev_scsi) {
848 goto err;
849 }
850 vhost_scsi_devs[0] = vdev_scsi;
851
852 if (vdev_scsi_add_iscsi_lun(vdev_scsi, iscsi_uri, 0) != 0) {
853 goto err;
854 }
855
856 if (vdev_scsi_run(vdev_scsi) != 0) {
857 goto err;
858 }
859
860 out:
861 if (vdev_scsi) {
862 vdev_scsi_deinit(vdev_scsi);
863 free(vdev_scsi);
864 }
865 if (unix_fn) {
866 free(unix_fn);
867 }
868 if (iscsi_uri) {
869 free(iscsi_uri);
870 }
871
872 return err;
873
874 err:
875 err = EXIT_FAILURE;
876 goto out;
877
878 help:
879 fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
880 argv[0]);
881 fprintf(stderr, " -u path to unix socket\n");
882 fprintf(stderr, " -i iscsi uri for lun 0\n");
883 fprintf(stderr, " -h print help and quit\n");
884
885 goto err;
886 }