]> git.proxmox.com Git - mirror_qemu.git/blob - contrib/vhost-user-scsi/vhost-user-scsi.c
vhost-user-scsi: drop extra callback pointer
[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 "standard-headers/linux/virtio_scsi.h"
16 #include "iscsi/iscsi.h"
17 #include "iscsi/scsi-lowlevel.h"
18
19 #include <glib.h>
20
21 /* #define VUS_DEBUG 1 */
22
23 /** Log helpers **/
24
25 #define PPRE \
26 struct timespec ts; \
27 char timebuf[64]; \
28 struct tm tm; \
29 (void)clock_gettime(CLOCK_REALTIME, &ts); \
30 (void)strftime(timebuf, 64, "%Y%m%d %T", gmtime_r(&ts.tv_sec, &tm))
31
32 #define PEXT(lvl, msg, ...) do { \
33 PPRE; \
34 fprintf(stderr, "%s.%06ld " lvl ": %s:%s():%d: " msg "\n", \
35 timebuf, ts.tv_nsec / 1000, \
36 __FILE__, __func__, __LINE__, ## __VA_ARGS__); \
37 } while (0)
38
39 #define PNOR(lvl, msg, ...) do { \
40 PPRE; \
41 fprintf(stderr, "%s.%06ld " lvl ": " msg "\n", \
42 timebuf, ts.tv_nsec / 1000, ## __VA_ARGS__); \
43 } while (0)
44
45 #ifdef VUS_DEBUG
46 #define PDBG(msg, ...) PEXT("DBG", msg, ## __VA_ARGS__)
47 #define PERR(msg, ...) PEXT("ERR", msg, ## __VA_ARGS__)
48 #define PLOG(msg, ...) PEXT("LOG", msg, ## __VA_ARGS__)
49 #else
50 #define PDBG(msg, ...) { }
51 #define PERR(msg, ...) PNOR("ERR", msg, ## __VA_ARGS__)
52 #define PLOG(msg, ...) PNOR("LOG", msg, ## __VA_ARGS__)
53 #endif
54
55 /** vhost-user-scsi specific definitions **/
56
57 #define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi"
58
59 typedef struct VusIscsiLun {
60 struct iscsi_context *iscsi_ctx;
61 int iscsi_lun;
62 } VusIscsiLun;
63
64 typedef struct VusDev {
65 VuDev vu_dev;
66 int server_sock;
67 GMainLoop *loop;
68 GTree *fdmap; /* fd -> gsource context id */
69 VusIscsiLun lun;
70 } VusDev;
71
72 /** glib event loop integration for libvhost-user and misc callbacks **/
73
74 QEMU_BUILD_BUG_ON((int)G_IO_IN != (int)VU_WATCH_IN);
75 QEMU_BUILD_BUG_ON((int)G_IO_OUT != (int)VU_WATCH_OUT);
76 QEMU_BUILD_BUG_ON((int)G_IO_PRI != (int)VU_WATCH_PRI);
77 QEMU_BUILD_BUG_ON((int)G_IO_ERR != (int)VU_WATCH_ERR);
78 QEMU_BUILD_BUG_ON((int)G_IO_HUP != (int)VU_WATCH_HUP);
79
80 typedef struct vus_gsrc {
81 GSource parent;
82 VusDev *vdev_scsi;
83 GPollFD gfd;
84 } vus_gsrc_t;
85
86 static gint vus_fdmap_compare(gconstpointer a, gconstpointer b)
87 {
88 return (b > a) - (b < a);
89 }
90
91 static gboolean vus_gsrc_prepare(GSource *src, gint *timeout)
92 {
93 assert(timeout);
94
95 *timeout = -1;
96 return FALSE;
97 }
98
99 static gboolean vus_gsrc_check(GSource *src)
100 {
101 vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
102
103 assert(vus_src);
104
105 return vus_src->gfd.revents & vus_src->gfd.events;
106 }
107
108 static gboolean vus_gsrc_dispatch(GSource *src, GSourceFunc cb, gpointer data)
109 {
110 VusDev *vdev_scsi;
111 vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
112
113 assert(vus_src);
114
115 vdev_scsi = vus_src->vdev_scsi;
116
117 assert(vdev_scsi);
118
119 ((vu_watch_cb)cb)(&vdev_scsi->vu_dev, vus_src->gfd.revents, data);
120
121 return G_SOURCE_CONTINUE;
122 }
123
124 static GSourceFuncs vus_gsrc_funcs = {
125 vus_gsrc_prepare,
126 vus_gsrc_check,
127 vus_gsrc_dispatch,
128 NULL
129 };
130
131 static void vus_gsrc_new(VusDev *vdev_scsi, int fd, GIOCondition cond,
132 vu_watch_cb vu_cb, GSourceFunc gsrc_cb, gpointer data)
133 {
134 GSource *vus_gsrc;
135 vus_gsrc_t *vus_src;
136 guint id;
137
138 assert(vdev_scsi);
139 assert(fd >= 0);
140 assert(vu_cb || gsrc_cb);
141 assert(!(vu_cb && gsrc_cb));
142
143 vus_gsrc = g_source_new(&vus_gsrc_funcs, sizeof(vus_gsrc_t));
144 g_source_set_callback(vus_gsrc, (GSourceFunc) vu_cb, data, NULL);
145 vus_src = (vus_gsrc_t *)vus_gsrc;
146
147 vus_src->vdev_scsi = vdev_scsi;
148 vus_src->gfd.fd = fd;
149 vus_src->gfd.events = cond;
150
151 g_source_add_poll(vus_gsrc, &vus_src->gfd);
152 g_source_set_callback(vus_gsrc, gsrc_cb, data, NULL);
153 id = g_source_attach(vus_gsrc, NULL);
154 assert(id);
155 g_source_unref(vus_gsrc);
156
157 g_tree_insert(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd,
158 (gpointer)(uintptr_t)id);
159 }
160
161 /** libiscsi integration **/
162
163 typedef struct virtio_scsi_cmd_req VirtIOSCSICmdReq;
164 typedef struct virtio_scsi_cmd_resp VirtIOSCSICmdResp;
165
166 static int vus_iscsi_add_lun(VusIscsiLun *lun, char *iscsi_uri)
167 {
168 struct iscsi_url *iscsi_url;
169 struct iscsi_context *iscsi_ctx;
170 int ret = 0;
171
172 assert(lun);
173 assert(iscsi_uri);
174 assert(!lun->iscsi_ctx);
175
176 iscsi_ctx = iscsi_create_context(VUS_ISCSI_INITIATOR);
177 if (!iscsi_ctx) {
178 PERR("Unable to create iSCSI context");
179 return -1;
180 }
181
182 iscsi_url = iscsi_parse_full_url(iscsi_ctx, iscsi_uri);
183 if (!iscsi_url) {
184 PERR("Unable to parse iSCSI URL: %s", iscsi_get_error(iscsi_ctx));
185 goto fail;
186 }
187
188 iscsi_set_session_type(iscsi_ctx, ISCSI_SESSION_NORMAL);
189 iscsi_set_header_digest(iscsi_ctx, ISCSI_HEADER_DIGEST_NONE_CRC32C);
190 if (iscsi_full_connect_sync(iscsi_ctx, iscsi_url->portal, iscsi_url->lun)) {
191 PERR("Unable to login to iSCSI portal: %s", iscsi_get_error(iscsi_ctx));
192 goto fail;
193 }
194
195 lun->iscsi_ctx = iscsi_ctx;
196 lun->iscsi_lun = iscsi_url->lun;
197
198 PDBG("Context %p created for lun 0: %s", iscsi_ctx, iscsi_uri);
199
200 out:
201 if (iscsi_url) {
202 iscsi_destroy_url(iscsi_url);
203 }
204 return ret;
205
206 fail:
207 (void)iscsi_destroy_context(iscsi_ctx);
208 ret = -1;
209 goto out;
210 }
211
212 static struct scsi_task *scsi_task_new(int cdb_len, uint8_t *cdb, int dir,
213 int xfer_len)
214 {
215 struct scsi_task *task;
216
217 assert(cdb_len > 0);
218 assert(cdb);
219
220 task = g_new0(struct scsi_task, 1);
221 memcpy(task->cdb, cdb, cdb_len);
222 task->cdb_size = cdb_len;
223 task->xfer_dir = dir;
224 task->expxferlen = xfer_len;
225
226 return task;
227 }
228
229 static int get_cdb_len(uint8_t *cdb)
230 {
231 assert(cdb);
232
233 switch (cdb[0] >> 5) {
234 case 0: return 6;
235 case 1: /* fall through */
236 case 2: return 10;
237 case 4: return 16;
238 case 5: return 12;
239 }
240 PERR("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5);
241 return -1;
242 }
243
244 static int handle_cmd_sync(struct iscsi_context *ctx,
245 VirtIOSCSICmdReq *req,
246 struct iovec *out, unsigned int out_len,
247 VirtIOSCSICmdResp *rsp,
248 struct iovec *in, unsigned int in_len)
249 {
250 struct scsi_task *task;
251 uint32_t dir;
252 uint32_t len;
253 int cdb_len;
254 int i;
255
256 assert(ctx);
257 assert(req);
258 assert(rsp);
259
260 if (!(!req->lun[1] && req->lun[2] == 0x40 && !req->lun[3])) {
261 /* Ignore anything different than target=0, lun=0 */
262 PDBG("Ignoring unconnected lun (0x%hhX, 0x%hhX)",
263 req->lun[1], req->lun[3]);
264 rsp->status = SCSI_STATUS_CHECK_CONDITION;
265 memset(rsp->sense, 0, sizeof(rsp->sense));
266 rsp->sense_len = 18;
267 rsp->sense[0] = 0x70;
268 rsp->sense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
269 rsp->sense[7] = 10;
270 rsp->sense[12] = 0x24;
271
272 return 0;
273 }
274
275 cdb_len = get_cdb_len(req->cdb);
276 if (cdb_len == -1) {
277 return -1;
278 }
279
280 len = 0;
281 if (!out_len && !in_len) {
282 dir = SCSI_XFER_NONE;
283 } else if (out_len) {
284 dir = SCSI_XFER_WRITE;
285 for (i = 0; i < out_len; i++) {
286 len += out[i].iov_len;
287 }
288 } else {
289 dir = SCSI_XFER_READ;
290 for (i = 0; i < in_len; i++) {
291 len += in[i].iov_len;
292 }
293 }
294
295 task = scsi_task_new(cdb_len, req->cdb, dir, len);
296
297 if (dir == SCSI_XFER_WRITE) {
298 task->iovector_out.iov = (struct scsi_iovec *)out;
299 task->iovector_out.niov = out_len;
300 } else if (dir == SCSI_XFER_READ) {
301 task->iovector_in.iov = (struct scsi_iovec *)in;
302 task->iovector_in.niov = in_len;
303 }
304
305 PDBG("Sending iscsi cmd (cdb_len=%d, dir=%d, task=%p)",
306 cdb_len, dir, task);
307 if (!iscsi_scsi_command_sync(ctx, 0, task, NULL)) {
308 PERR("Error serving SCSI command");
309 g_free(task);
310 return -1;
311 }
312
313 memset(rsp, 0, sizeof(*rsp));
314
315 rsp->status = task->status;
316 rsp->resid = task->residual;
317
318 if (task->status == SCSI_STATUS_CHECK_CONDITION) {
319 rsp->response = VIRTIO_SCSI_S_FAILURE;
320 rsp->sense_len = task->datain.size - 2;
321 memcpy(rsp->sense, &task->datain.data[2], rsp->sense_len);
322 }
323
324 g_free(task);
325
326 PDBG("Filled in rsp: status=%hhX, resid=%u, response=%hhX, sense_len=%u",
327 rsp->status, rsp->resid, rsp->response, rsp->sense_len);
328
329 return 0;
330 }
331
332 /** libvhost-user callbacks **/
333
334 static void vus_panic_cb(VuDev *vu_dev, const char *buf)
335 {
336 VusDev *vdev_scsi;
337
338 assert(vu_dev);
339
340 vdev_scsi = container_of(vu_dev, VusDev, vu_dev);
341 if (buf) {
342 PERR("vu_panic: %s", buf);
343 }
344
345 g_main_loop_quit(vdev_scsi->loop);
346 }
347
348 static void vus_add_watch_cb(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb,
349 void *pvt)
350 {
351 VusDev *vdev_scsi;
352 guint id;
353
354 assert(vu_dev);
355 assert(fd >= 0);
356 assert(cb);
357
358 vdev_scsi = container_of(vu_dev, VusDev, vu_dev);
359 id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
360 (gpointer)(uintptr_t)fd);
361 if (id) {
362 GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
363 assert(vus_src);
364 g_source_destroy(vus_src);
365 (void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
366 }
367
368 vus_gsrc_new(vdev_scsi, fd, vu_evt, cb, NULL, pvt);
369 }
370
371 static void vus_del_watch_cb(VuDev *vu_dev, int fd)
372 {
373 VusDev *vdev_scsi;
374 guint id;
375
376 assert(vu_dev);
377 assert(fd >= 0);
378
379 vdev_scsi = container_of(vu_dev, VusDev, vu_dev);
380 id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
381 (gpointer)(uintptr_t)fd);
382 if (id) {
383 GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
384 assert(vus_src);
385 g_source_destroy(vus_src);
386 (void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
387 }
388 }
389
390 static void vus_proc_req(VuDev *vu_dev, int idx)
391 {
392 VusDev *vdev_scsi;
393 VuVirtq *vq;
394
395 assert(vu_dev);
396
397 vdev_scsi = container_of(vu_dev, VusDev, vu_dev);
398 if (idx < 0 || idx >= VHOST_MAX_NR_VIRTQUEUE) {
399 PERR("VQ Index out of range: %d", idx);
400 vus_panic_cb(vu_dev, NULL);
401 return;
402 }
403
404 vq = vu_get_queue(vu_dev, idx);
405 if (!vq) {
406 PERR("Error fetching VQ (dev=%p, idx=%d)", vu_dev, idx);
407 vus_panic_cb(vu_dev, NULL);
408 return;
409 }
410
411 PDBG("Got kicked on vq[%d]@%p", idx, vq);
412
413 while (1) {
414 VuVirtqElement *elem;
415 VirtIOSCSICmdReq *req;
416 VirtIOSCSICmdResp *rsp;
417
418 elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
419 if (!elem) {
420 PDBG("No more elements pending on vq[%d]@%p", idx, vq);
421 break;
422 }
423 PDBG("Popped elem@%p", elem);
424
425 assert(!(elem->out_num > 1 && elem->in_num > 1));
426 assert(elem->out_num > 0 && elem->in_num > 0);
427
428 if (elem->out_sg[0].iov_len < sizeof(VirtIOSCSICmdReq)) {
429 PERR("Invalid virtio-scsi req header");
430 vus_panic_cb(vu_dev, NULL);
431 break;
432 }
433 req = (VirtIOSCSICmdReq *)elem->out_sg[0].iov_base;
434
435 if (elem->in_sg[0].iov_len < sizeof(VirtIOSCSICmdResp)) {
436 PERR("Invalid virtio-scsi rsp header");
437 vus_panic_cb(vu_dev, NULL);
438 break;
439 }
440 rsp = (VirtIOSCSICmdResp *)elem->in_sg[0].iov_base;
441
442 if (handle_cmd_sync(vdev_scsi->lun.iscsi_ctx,
443 req, &elem->out_sg[1], elem->out_num - 1,
444 rsp, &elem->in_sg[1], elem->in_num - 1) != 0) {
445 vus_panic_cb(vu_dev, NULL);
446 break;
447 }
448
449 vu_queue_push(vu_dev, vq, elem, 0);
450 vu_queue_notify(vu_dev, vq);
451
452 free(elem);
453 }
454 }
455
456 static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)
457 {
458 VuVirtq *vq;
459
460 assert(vu_dev);
461
462 if (idx < 0 || idx >= VHOST_MAX_NR_VIRTQUEUE) {
463 PERR("VQ Index out of range: %d", idx);
464 vus_panic_cb(vu_dev, NULL);
465 return;
466 }
467
468 vq = vu_get_queue(vu_dev, idx);
469
470 if (idx == 0 || idx == 1) {
471 PDBG("queue %d unimplemented", idx);
472 } else {
473 vu_set_queue_handler(vu_dev, vq, started ? vus_proc_req : NULL);
474 }
475 }
476
477 static const VuDevIface vus_iface = {
478 .queue_set_started = vus_queue_set_started,
479 };
480
481 static gboolean vus_vhost_cb(gpointer data)
482 {
483 VuDev *vu_dev = (VuDev *)data;
484
485 assert(vu_dev);
486
487 if (!vu_dispatch(vu_dev) != 0) {
488 PERR("Error processing vhost message");
489 vus_panic_cb(vu_dev, NULL);
490 return G_SOURCE_REMOVE;
491 }
492
493 return G_SOURCE_CONTINUE;
494 }
495
496 /** misc helpers **/
497
498 static int unix_sock_new(char *unix_fn)
499 {
500 int sock;
501 struct sockaddr_un un;
502 size_t len;
503
504 assert(unix_fn);
505
506 sock = socket(AF_UNIX, SOCK_STREAM, 0);
507 if (sock <= 0) {
508 perror("socket");
509 return -1;
510 }
511
512 un.sun_family = AF_UNIX;
513 (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
514 len = sizeof(un.sun_family) + strlen(un.sun_path);
515
516 (void)unlink(unix_fn);
517 if (bind(sock, (struct sockaddr *)&un, len) < 0) {
518 perror("bind");
519 goto fail;
520 }
521
522 if (listen(sock, 1) < 0) {
523 perror("listen");
524 goto fail;
525 }
526
527 return sock;
528
529 fail:
530 (void)close(sock);
531
532 return -1;
533 }
534
535 /** vhost-user-scsi **/
536
537 static void vdev_scsi_free(VusDev *vdev_scsi)
538 {
539 if (vdev_scsi->server_sock >= 0) {
540 close(vdev_scsi->server_sock);
541 }
542 g_main_loop_unref(vdev_scsi->loop);
543 g_tree_destroy(vdev_scsi->fdmap);
544 g_free(vdev_scsi);
545 }
546
547 static VusDev *vdev_scsi_new(int server_sock)
548 {
549 VusDev *vdev_scsi;
550
551 vdev_scsi = g_new0(VusDev, 1);
552 vdev_scsi->server_sock = server_sock;
553 vdev_scsi->loop = g_main_loop_new(NULL, FALSE);
554 vdev_scsi->fdmap = g_tree_new(vus_fdmap_compare);
555
556 return vdev_scsi;
557 }
558
559 static int vdev_scsi_run(VusDev *vdev_scsi)
560 {
561 int cli_sock;
562 int ret = 0;
563
564 assert(vdev_scsi);
565 assert(vdev_scsi->server_sock >= 0);
566 assert(vdev_scsi->loop);
567
568 cli_sock = accept(vdev_scsi->server_sock, NULL, NULL);
569 if (cli_sock < 0) {
570 perror("accept");
571 return -1;
572 }
573
574 vu_init(&vdev_scsi->vu_dev,
575 cli_sock,
576 vus_panic_cb,
577 vus_add_watch_cb,
578 vus_del_watch_cb,
579 &vus_iface);
580
581 vus_gsrc_new(vdev_scsi, cli_sock, G_IO_IN, NULL, vus_vhost_cb,
582 &vdev_scsi->vu_dev);
583
584 g_main_loop_run(vdev_scsi->loop);
585
586 vu_deinit(&vdev_scsi->vu_dev);
587
588 return ret;
589 }
590
591 int main(int argc, char **argv)
592 {
593 VusDev *vdev_scsi = NULL;
594 char *unix_fn = NULL;
595 char *iscsi_uri = NULL;
596 int sock, opt, err = EXIT_SUCCESS;
597
598 while ((opt = getopt(argc, argv, "u:i:")) != -1) {
599 switch (opt) {
600 case 'h':
601 goto help;
602 case 'u':
603 unix_fn = g_strdup(optarg);
604 break;
605 case 'i':
606 iscsi_uri = g_strdup(optarg);
607 break;
608 default:
609 goto help;
610 }
611 }
612 if (!unix_fn || !iscsi_uri) {
613 goto help;
614 }
615
616 sock = unix_sock_new(unix_fn);
617 if (sock < 0) {
618 goto err;
619 }
620 vdev_scsi = vdev_scsi_new(sock);
621
622 if (vus_iscsi_add_lun(&vdev_scsi->lun, iscsi_uri) != 0) {
623 goto err;
624 }
625
626 if (vdev_scsi_run(vdev_scsi) != 0) {
627 goto err;
628 }
629
630 out:
631 if (vdev_scsi) {
632 vdev_scsi_free(vdev_scsi);
633 unlink(unix_fn);
634 }
635 g_free(unix_fn);
636 g_free(iscsi_uri);
637
638 return err;
639
640 err:
641 err = EXIT_FAILURE;
642 goto out;
643
644 help:
645 fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
646 argv[0]);
647 fprintf(stderr, " -u path to unix socket\n");
648 fprintf(stderr, " -i iscsi uri for lun 0\n");
649 fprintf(stderr, " -h print help and quit\n");
650
651 goto err;
652 }