1 /*
2  * Virtio SCSI dataplane
3  *
4  * Copyright Red Hat, Inc. 2014
5  *
6  * Authors:
7  *   Fam Zheng <famz@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "hw/virtio/virtio-scsi.h"
16 #include "qemu/error-report.h"
17 #include "sysemu/block-backend.h"
18 #include <hw/scsi/scsi.h>
19 #include <block/scsi.h>
20 #include <hw/virtio/virtio-bus.h>
21 #include "hw/virtio/virtio-access.h"
22 #include "stdio.h"
23 
24 /* Context: QEMU global mutex held */
25 void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread)
26 {
27     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
28     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
29     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
30 
31     assert(!s->ctx);
32     s->ctx = iothread_get_aio_context(vs->conf.iothread);
33 
34     /* Don't try if transport does not support notifiers. */
35     if (!k->set_guest_notifiers || !k->set_host_notifier) {
36         fprintf(stderr, "virtio-scsi: Failed to set iothread "
37                    "(transport does not support notifiers)");
38         exit(1);
39     }
40 }
41 
42 static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n)
43 {
44     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
45     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
46     int rc;
47 
48     /* Set up virtqueue notify */
49     rc = k->set_host_notifier(qbus->parent, n, true);
50     if (rc != 0) {
51         fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
52                 rc);
53         s->dataplane_fenced = true;
54         return rc;
55     }
56 
57     virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, true, true);
58     return 0;
59 }
60 
61 void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req)
62 {
63     if (virtio_should_notify(vdev, req->vq)) {
64         event_notifier_set(virtio_queue_get_guest_notifier(req->vq));
65     }
66 }
67 
68 /* assumes s->ctx held */
69 static void virtio_scsi_clear_aio(VirtIOSCSI *s)
70 {
71     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
72     int i;
73 
74     virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, false, false);
75     virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, false, false);
76     for (i = 0; i < vs->conf.num_queues; i++) {
77         virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, false, false);
78     }
79 }
80 
81 /* Context: QEMU global mutex held */
82 void virtio_scsi_dataplane_start(VirtIOSCSI *s)
83 {
84     int i;
85     int rc;
86     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
87     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
88     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
89 
90     if (s->dataplane_started ||
91         s->dataplane_starting ||
92         s->dataplane_fenced ||
93         s->ctx != iothread_get_aio_context(vs->conf.iothread)) {
94         return;
95     }
96 
97     s->dataplane_starting = true;
98 
99     /* Set up guest notifier (irq) */
100     rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
101     if (rc != 0) {
102         fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), "
103                 "ensure -enable-kvm is set\n", rc);
104         goto fail_guest_notifiers;
105     }
106 
107     aio_context_acquire(s->ctx);
108     rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0);
109     if (rc) {
110         goto fail_vrings;
111     }
112     rc = virtio_scsi_vring_init(s, vs->event_vq, 1);
113     if (rc) {
114         goto fail_vrings;
115     }
116     for (i = 0; i < vs->conf.num_queues; i++) {
117         rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2);
118         if (rc) {
119             goto fail_vrings;
120         }
121     }
122 
123     s->dataplane_starting = false;
124     s->dataplane_started = true;
125     aio_context_release(s->ctx);
126     return;
127 
128 fail_vrings:
129     virtio_scsi_clear_aio(s);
130     aio_context_release(s->ctx);
131     for (i = 0; i < vs->conf.num_queues + 2; i++) {
132         k->set_host_notifier(qbus->parent, i, false);
133     }
134     k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
135 fail_guest_notifiers:
136     s->dataplane_fenced = true;
137     s->dataplane_starting = false;
138     s->dataplane_started = true;
139 }
140 
141 /* Context: QEMU global mutex held */
142 void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
143 {
144     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
145     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
146     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
147     int i;
148 
149     if (!s->dataplane_started || s->dataplane_stopping) {
150         return;
151     }
152 
153     /* Better luck next time. */
154     if (s->dataplane_fenced) {
155         s->dataplane_fenced = false;
156         s->dataplane_started = false;
157         return;
158     }
159     s->dataplane_stopping = true;
160     assert(s->ctx == iothread_get_aio_context(vs->conf.iothread));
161 
162     aio_context_acquire(s->ctx);
163 
164     virtio_scsi_clear_aio(s);
165 
166     blk_drain_all(); /* ensure there are no in-flight requests */
167 
168     aio_context_release(s->ctx);
169 
170     for (i = 0; i < vs->conf.num_queues + 2; i++) {
171         k->set_host_notifier(qbus->parent, i, false);
172     }
173 
174     /* Clean up guest notifier (irq) */
175     k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
176     s->dataplane_stopping = false;
177     s->dataplane_started = false;
178 }
179