191cb1c9bSFam Zheng /*
291cb1c9bSFam Zheng * Virtio SCSI dataplane
391cb1c9bSFam Zheng *
491cb1c9bSFam Zheng * Copyright Red Hat, Inc. 2014
591cb1c9bSFam Zheng *
691cb1c9bSFam Zheng * Authors:
791cb1c9bSFam Zheng * Fam Zheng <famz@redhat.com>
891cb1c9bSFam Zheng *
991cb1c9bSFam Zheng * This work is licensed under the terms of the GNU GPL, version 2 or later.
1091cb1c9bSFam Zheng * See the COPYING file in the top-level directory.
1191cb1c9bSFam Zheng *
1291cb1c9bSFam Zheng */
1391cb1c9bSFam Zheng
149b8bfe21SPeter Maydell #include "qemu/osdep.h"
15ad07cd69SPaolo Bonzini #include "qapi/error.h"
1691cb1c9bSFam Zheng #include "hw/virtio/virtio-scsi.h"
1791cb1c9bSFam Zheng #include "qemu/error-report.h"
184be74634SMarkus Armbruster #include "sysemu/block-backend.h"
19a9c94277SMarkus Armbruster #include "hw/scsi/scsi.h"
2008e2c9f1SPaolo Bonzini #include "scsi/constants.h"
21a9c94277SMarkus Armbruster #include "hw/virtio/virtio-bus.h"
2291cb1c9bSFam Zheng
23*0b2675c4SStefan Hajnoczi /* Context: BQL held */
virtio_scsi_dataplane_setup(VirtIOSCSI * s,Error ** errp)24ad07cd69SPaolo Bonzini void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
2591cb1c9bSFam Zheng {
2691cb1c9bSFam Zheng VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
27ad07cd69SPaolo Bonzini VirtIODevice *vdev = VIRTIO_DEVICE(s);
28ad07cd69SPaolo Bonzini BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
29ad07cd69SPaolo Bonzini VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
3091cb1c9bSFam Zheng
31ad07cd69SPaolo Bonzini if (vs->conf.iothread) {
32b13d3962SPaolo Bonzini if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
33ad07cd69SPaolo Bonzini error_setg(errp,
34ad07cd69SPaolo Bonzini "device is incompatible with iothread "
3591cb1c9bSFam Zheng "(transport does not support notifiers)");
36ad07cd69SPaolo Bonzini return;
37ad07cd69SPaolo Bonzini }
38ad07cd69SPaolo Bonzini if (!virtio_device_ioeventfd_enabled(vdev)) {
39ad07cd69SPaolo Bonzini error_setg(errp, "ioeventfd is required for iothread");
40ad07cd69SPaolo Bonzini return;
41ad07cd69SPaolo Bonzini }
42ad07cd69SPaolo Bonzini s->ctx = iothread_get_aio_context(vs->conf.iothread);
43ad07cd69SPaolo Bonzini } else {
44ad07cd69SPaolo Bonzini if (!virtio_device_ioeventfd_enabled(vdev)) {
45ad07cd69SPaolo Bonzini return;
46ad07cd69SPaolo Bonzini }
47ad07cd69SPaolo Bonzini s->ctx = qemu_get_aio_context();
4891cb1c9bSFam Zheng }
4991cb1c9bSFam Zheng }
5091cb1c9bSFam Zheng
virtio_scsi_set_host_notifier(VirtIOSCSI * s,VirtQueue * vq,int n)5161fc57bfSGreg Kurz static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n)
5291cb1c9bSFam Zheng {
5391cb1c9bSFam Zheng BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
546d2c8316SCornelia Huck int rc;
5591cb1c9bSFam Zheng
5691cb1c9bSFam Zheng /* Set up virtqueue notify */
57b1f0a33dSCornelia Huck rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true);
586d2c8316SCornelia Huck if (rc != 0) {
596d2c8316SCornelia Huck fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
606d2c8316SCornelia Huck rc);
614adea804SCornelia Huck s->dataplane_fenced = true;
62e24a47c5SPaolo Bonzini return rc;
6391cb1c9bSFam Zheng }
64196d4fc5SBo Su
65e24a47c5SPaolo Bonzini return 0;
6691cb1c9bSFam Zheng }
6791cb1c9bSFam Zheng
68184b9623SStefan Hajnoczi /* Context: BH in IOThread */
virtio_scsi_dataplane_stop_bh(void * opaque)69184b9623SStefan Hajnoczi static void virtio_scsi_dataplane_stop_bh(void *opaque)
70361dcc79SCornelia Huck {
71184b9623SStefan Hajnoczi VirtIOSCSI *s = opaque;
72361dcc79SCornelia Huck VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
73bd58ab40SStefan Hajnoczi EventNotifier *host_notifier;
74361dcc79SCornelia Huck int i;
75361dcc79SCornelia Huck
76db608fb7SStefan Hajnoczi virtio_queue_aio_detach_host_notifier(vs->ctrl_vq, s->ctx);
77bd58ab40SStefan Hajnoczi host_notifier = virtio_queue_get_host_notifier(vs->ctrl_vq);
78bd58ab40SStefan Hajnoczi
79bd58ab40SStefan Hajnoczi /*
80bd58ab40SStefan Hajnoczi * Test and clear notifier after disabling event, in case poll callback
81bd58ab40SStefan Hajnoczi * didn't have time to run.
82bd58ab40SStefan Hajnoczi */
83bd58ab40SStefan Hajnoczi virtio_queue_host_notifier_read(host_notifier);
84bd58ab40SStefan Hajnoczi
85db608fb7SStefan Hajnoczi virtio_queue_aio_detach_host_notifier(vs->event_vq, s->ctx);
86bd58ab40SStefan Hajnoczi host_notifier = virtio_queue_get_host_notifier(vs->event_vq);
87bd58ab40SStefan Hajnoczi virtio_queue_host_notifier_read(host_notifier);
88bd58ab40SStefan Hajnoczi
89e24a47c5SPaolo Bonzini for (i = 0; i < vs->conf.num_queues; i++) {
90db608fb7SStefan Hajnoczi virtio_queue_aio_detach_host_notifier(vs->cmd_vqs[i], s->ctx);
91bd58ab40SStefan Hajnoczi host_notifier = virtio_queue_get_host_notifier(vs->cmd_vqs[i]);
92bd58ab40SStefan Hajnoczi virtio_queue_host_notifier_read(host_notifier);
93361dcc79SCornelia Huck }
94361dcc79SCornelia Huck }
95361dcc79SCornelia Huck
96*0b2675c4SStefan Hajnoczi /* Context: BQL held */
virtio_scsi_dataplane_start(VirtIODevice * vdev)97ad07cd69SPaolo Bonzini int virtio_scsi_dataplane_start(VirtIODevice *vdev)
9891cb1c9bSFam Zheng {
9991cb1c9bSFam Zheng int i;
10091cb1c9bSFam Zheng int rc;
101dec2bb14SMaxim Levitsky int vq_init_count = 0;
102ad07cd69SPaolo Bonzini BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
10391cb1c9bSFam Zheng VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
104ad07cd69SPaolo Bonzini VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
105ad07cd69SPaolo Bonzini VirtIOSCSI *s = VIRTIO_SCSI(vdev);
10691cb1c9bSFam Zheng
10791cb1c9bSFam Zheng if (s->dataplane_started ||
10891cb1c9bSFam Zheng s->dataplane_starting ||
109ad07cd69SPaolo Bonzini s->dataplane_fenced) {
110ad07cd69SPaolo Bonzini return 0;
11191cb1c9bSFam Zheng }
11291cb1c9bSFam Zheng
11391cb1c9bSFam Zheng s->dataplane_starting = true;
11491cb1c9bSFam Zheng
11591cb1c9bSFam Zheng /* Set up guest notifier (irq) */
11691cb1c9bSFam Zheng rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
11791cb1c9bSFam Zheng if (rc != 0) {
118a1d30f28SThomas Huth error_report("virtio-scsi: Failed to set guest notifiers (%d), "
119a1d30f28SThomas Huth "ensure -accel kvm is set.", rc);
120361dcc79SCornelia Huck goto fail_guest_notifiers;
12191cb1c9bSFam Zheng }
12291cb1c9bSFam Zheng
1239cf4fd87SGreg Kurz /*
1249cf4fd87SGreg Kurz * Batch all the host notifiers in a single transaction to avoid
1259cf4fd87SGreg Kurz * quadratic time complexity in address_space_update_ioeventfds().
1269cf4fd87SGreg Kurz */
127c4f5dcc4SGreg Kurz memory_region_transaction_begin();
128c4f5dcc4SGreg Kurz
12961fc57bfSGreg Kurz rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0);
13061fc57bfSGreg Kurz if (rc != 0) {
13161fc57bfSGreg Kurz goto fail_host_notifiers;
132361dcc79SCornelia Huck }
133dec2bb14SMaxim Levitsky
134dec2bb14SMaxim Levitsky vq_init_count++;
13561fc57bfSGreg Kurz rc = virtio_scsi_set_host_notifier(s, vs->event_vq, 1);
13661fc57bfSGreg Kurz if (rc != 0) {
13761fc57bfSGreg Kurz goto fail_host_notifiers;
138361dcc79SCornelia Huck }
139dec2bb14SMaxim Levitsky
140dec2bb14SMaxim Levitsky vq_init_count++;
14161fc57bfSGreg Kurz
14291cb1c9bSFam Zheng for (i = 0; i < vs->conf.num_queues; i++) {
14361fc57bfSGreg Kurz rc = virtio_scsi_set_host_notifier(s, vs->cmd_vqs[i], i + 2);
144e24a47c5SPaolo Bonzini if (rc) {
14561fc57bfSGreg Kurz goto fail_host_notifiers;
146361dcc79SCornelia Huck }
147dec2bb14SMaxim Levitsky vq_init_count++;
14891cb1c9bSFam Zheng }
14991cb1c9bSFam Zheng
150c4f5dcc4SGreg Kurz memory_region_transaction_commit();
151c4f5dcc4SGreg Kurz
1529a4b6a63SStefan Hajnoczi s->dataplane_starting = false;
1539a4b6a63SStefan Hajnoczi s->dataplane_started = true;
154765ca516SStefan Hajnoczi smp_wmb(); /* paired with aio_notify_accept() */
1559a4b6a63SStefan Hajnoczi
156766aa2deSStefan Hajnoczi if (s->bus.drain_count == 0) {
157db608fb7SStefan Hajnoczi virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
15838738f7dSStefan Hajnoczi virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx);
15961fc57bfSGreg Kurz
16061fc57bfSGreg Kurz for (i = 0; i < vs->conf.num_queues; i++) {
161db608fb7SStefan Hajnoczi virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
16261fc57bfSGreg Kurz }
163766aa2deSStefan Hajnoczi }
164ad07cd69SPaolo Bonzini return 0;
165361dcc79SCornelia Huck
16661fc57bfSGreg Kurz fail_host_notifiers:
167dec2bb14SMaxim Levitsky for (i = 0; i < vq_init_count; i++) {
16821a4d962SCornelia Huck virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
169c4f5dcc4SGreg Kurz }
170c4f5dcc4SGreg Kurz
1719cf4fd87SGreg Kurz /*
1729cf4fd87SGreg Kurz * The transaction expects the ioeventfds to be open when it
1739cf4fd87SGreg Kurz * commits. Do it now, before the cleanup loop.
1749cf4fd87SGreg Kurz */
175c4f5dcc4SGreg Kurz memory_region_transaction_commit();
176c4f5dcc4SGreg Kurz
177c4f5dcc4SGreg Kurz for (i = 0; i < vq_init_count; i++) {
17876143618SGal Hammer virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
179b1f0a33dSCornelia Huck }
180361dcc79SCornelia Huck k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
181361dcc79SCornelia Huck fail_guest_notifiers:
182e24a47c5SPaolo Bonzini s->dataplane_fenced = true;
183361dcc79SCornelia Huck s->dataplane_starting = false;
184e24a47c5SPaolo Bonzini s->dataplane_started = true;
185ad07cd69SPaolo Bonzini return -ENOSYS;
18691cb1c9bSFam Zheng }
18791cb1c9bSFam Zheng
188*0b2675c4SStefan Hajnoczi /* Context: BQL held */
virtio_scsi_dataplane_stop(VirtIODevice * vdev)189ad07cd69SPaolo Bonzini void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
19091cb1c9bSFam Zheng {
191ad07cd69SPaolo Bonzini BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
19291cb1c9bSFam Zheng VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
193ad07cd69SPaolo Bonzini VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
194ad07cd69SPaolo Bonzini VirtIOSCSI *s = VIRTIO_SCSI(vdev);
19521a4d962SCornelia Huck int i;
19691cb1c9bSFam Zheng
197e24a47c5SPaolo Bonzini if (!s->dataplane_started || s->dataplane_stopping) {
198e24a47c5SPaolo Bonzini return;
199e24a47c5SPaolo Bonzini }
200e24a47c5SPaolo Bonzini
2014adea804SCornelia Huck /* Better luck next time. */
2024adea804SCornelia Huck if (s->dataplane_fenced) {
2034adea804SCornelia Huck s->dataplane_fenced = false;
204e24a47c5SPaolo Bonzini s->dataplane_started = false;
20591cb1c9bSFam Zheng return;
20691cb1c9bSFam Zheng }
20791cb1c9bSFam Zheng s->dataplane_stopping = true;
20891cb1c9bSFam Zheng
209766aa2deSStefan Hajnoczi if (s->bus.drain_count == 0) {
210184b9623SStefan Hajnoczi aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
211766aa2deSStefan Hajnoczi }
21291cb1c9bSFam Zheng
2134be74634SMarkus Armbruster blk_drain_all(); /* ensure there are no in-flight requests */
21491cb1c9bSFam Zheng
2159cf4fd87SGreg Kurz /*
2169cf4fd87SGreg Kurz * Batch all the host notifiers in a single transaction to avoid
2179cf4fd87SGreg Kurz * quadratic time complexity in address_space_update_ioeventfds().
2189cf4fd87SGreg Kurz */
219c4f5dcc4SGreg Kurz memory_region_transaction_begin();
220c4f5dcc4SGreg Kurz
22191cb1c9bSFam Zheng for (i = 0; i < vs->conf.num_queues + 2; i++) {
22221a4d962SCornelia Huck virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
223c4f5dcc4SGreg Kurz }
224c4f5dcc4SGreg Kurz
2259cf4fd87SGreg Kurz /*
2269cf4fd87SGreg Kurz * The transaction expects the ioeventfds to be open when it
2279cf4fd87SGreg Kurz * commits. Do it now, before the cleanup loop.
2289cf4fd87SGreg Kurz */
229c4f5dcc4SGreg Kurz memory_region_transaction_commit();
230c4f5dcc4SGreg Kurz
231c4f5dcc4SGreg Kurz for (i = 0; i < vs->conf.num_queues + 2; i++) {
23276143618SGal Hammer virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
233b1f0a33dSCornelia Huck }
23491cb1c9bSFam Zheng
23591cb1c9bSFam Zheng /* Clean up guest notifier (irq) */
23691cb1c9bSFam Zheng k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
23791cb1c9bSFam Zheng s->dataplane_stopping = false;
23891cb1c9bSFam Zheng s->dataplane_started = false;
23991cb1c9bSFam Zheng }
240