xref: /openbmc/qemu/hw/block/vhost-user-blk.c (revision c2387413)
1 /*
2  * vhost-user-blk host device
3  *
4  * Copyright(C) 2017 Intel Corporation.
5  *
6  * Authors:
7  *  Changpeng Liu <changpeng.liu@intel.com>
8  *
9  * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by:
10  * Felipe Franciosi <felipe@nutanix.com>
11  * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
12  * Nicholas Bellinger <nab@risingtidesystems.com>
13  *
14  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
15  * See the COPYING.LIB file in the top-level directory.
16  *
17  */
18 
19 #include "qemu/osdep.h"
20 #include "qapi/error.h"
21 #include "qemu/error-report.h"
22 #include "qemu/cutils.h"
23 #include "hw/qdev-core.h"
24 #include "hw/qdev-properties.h"
25 #include "hw/qdev-properties-system.h"
26 #include "hw/virtio/vhost.h"
27 #include "hw/virtio/vhost-user-blk.h"
28 #include "hw/virtio/virtio.h"
29 #include "hw/virtio/virtio-bus.h"
30 #include "hw/virtio/virtio-access.h"
31 #include "sysemu/sysemu.h"
32 #include "sysemu/runstate.h"
33 
34 static const int user_feature_bits[] = {
35     VIRTIO_BLK_F_SIZE_MAX,
36     VIRTIO_BLK_F_SEG_MAX,
37     VIRTIO_BLK_F_GEOMETRY,
38     VIRTIO_BLK_F_BLK_SIZE,
39     VIRTIO_BLK_F_TOPOLOGY,
40     VIRTIO_BLK_F_MQ,
41     VIRTIO_BLK_F_RO,
42     VIRTIO_BLK_F_FLUSH,
43     VIRTIO_BLK_F_CONFIG_WCE,
44     VIRTIO_BLK_F_DISCARD,
45     VIRTIO_BLK_F_WRITE_ZEROES,
46     VIRTIO_F_VERSION_1,
47     VIRTIO_RING_F_INDIRECT_DESC,
48     VIRTIO_RING_F_EVENT_IDX,
49     VIRTIO_F_NOTIFY_ON_EMPTY,
50     VHOST_INVALID_FEATURE_BIT
51 };
52 
53 static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
54 {
55     VHostUserBlk *s = VHOST_USER_BLK(vdev);
56 
57     memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
58 }
59 
60 static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
61 {
62     VHostUserBlk *s = VHOST_USER_BLK(vdev);
63     struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
64     int ret;
65 
66     if (blkcfg->wce == s->blkcfg.wce) {
67         return;
68     }
69 
70     ret = vhost_dev_set_config(&s->dev, &blkcfg->wce,
71                                offsetof(struct virtio_blk_config, wce),
72                                sizeof(blkcfg->wce),
73                                VHOST_SET_CONFIG_TYPE_MASTER);
74     if (ret) {
75         error_report("set device config space failed");
76         return;
77     }
78 
79     s->blkcfg.wce = blkcfg->wce;
80 }
81 
82 static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
83 {
84     int ret;
85     struct virtio_blk_config blkcfg;
86     VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
87 
88     ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
89                                sizeof(struct virtio_blk_config));
90     if (ret < 0) {
91         error_report("get config space failed");
92         return -1;
93     }
94 
95     /* valid for resize only */
96     if (blkcfg.capacity != s->blkcfg.capacity) {
97         s->blkcfg.capacity = blkcfg.capacity;
98         memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config));
99         virtio_notify_config(dev->vdev);
100     }
101 
102     return 0;
103 }
104 
105 const VhostDevConfigOps blk_ops = {
106     .vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
107 };
108 
109 static int vhost_user_blk_start(VirtIODevice *vdev)
110 {
111     VHostUserBlk *s = VHOST_USER_BLK(vdev);
112     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
113     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
114     int i, ret;
115 
116     if (!k->set_guest_notifiers) {
117         error_report("binding does not support guest notifiers");
118         return -ENOSYS;
119     }
120 
121     ret = vhost_dev_enable_notifiers(&s->dev, vdev);
122     if (ret < 0) {
123         error_report("Error enabling host notifiers: %d", -ret);
124         return ret;
125     }
126 
127     ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
128     if (ret < 0) {
129         error_report("Error binding guest notifier: %d", -ret);
130         goto err_host_notifiers;
131     }
132 
133     s->dev.acked_features = vdev->guest_features;
134 
135     ret = vhost_dev_prepare_inflight(&s->dev, vdev);
136     if (ret < 0) {
137         error_report("Error set inflight format: %d", -ret);
138         goto err_guest_notifiers;
139     }
140 
141     if (!s->inflight->addr) {
142         ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
143         if (ret < 0) {
144             error_report("Error get inflight: %d", -ret);
145             goto err_guest_notifiers;
146         }
147     }
148 
149     ret = vhost_dev_set_inflight(&s->dev, s->inflight);
150     if (ret < 0) {
151         error_report("Error set inflight: %d", -ret);
152         goto err_guest_notifiers;
153     }
154 
155     ret = vhost_dev_start(&s->dev, vdev);
156     if (ret < 0) {
157         error_report("Error starting vhost: %d", -ret);
158         goto err_guest_notifiers;
159     }
160     s->started_vu = true;
161 
162     /* guest_notifier_mask/pending not used yet, so just unmask
163      * everything here. virtio-pci will do the right thing by
164      * enabling/disabling irqfd.
165      */
166     for (i = 0; i < s->dev.nvqs; i++) {
167         vhost_virtqueue_mask(&s->dev, vdev, i, false);
168     }
169 
170     return ret;
171 
172 err_guest_notifiers:
173     k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
174 err_host_notifiers:
175     vhost_dev_disable_notifiers(&s->dev, vdev);
176     return ret;
177 }
178 
179 static void vhost_user_blk_stop(VirtIODevice *vdev)
180 {
181     VHostUserBlk *s = VHOST_USER_BLK(vdev);
182     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
183     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
184     int ret;
185 
186     if (!s->started_vu) {
187         return;
188     }
189     s->started_vu = false;
190 
191     if (!k->set_guest_notifiers) {
192         return;
193     }
194 
195     vhost_dev_stop(&s->dev, vdev);
196 
197     ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
198     if (ret < 0) {
199         error_report("vhost guest notifier cleanup failed: %d", ret);
200         return;
201     }
202 
203     vhost_dev_disable_notifiers(&s->dev, vdev);
204 }
205 
206 static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
207 {
208     VHostUserBlk *s = VHOST_USER_BLK(vdev);
209     bool should_start = virtio_device_started(vdev, status);
210     int ret;
211 
212     if (!vdev->vm_running) {
213         should_start = false;
214     }
215 
216     if (!s->connected) {
217         return;
218     }
219 
220     if (s->dev.started == should_start) {
221         return;
222     }
223 
224     if (should_start) {
225         ret = vhost_user_blk_start(vdev);
226         if (ret < 0) {
227             error_report("vhost-user-blk: vhost start failed: %s",
228                          strerror(-ret));
229             qemu_chr_fe_disconnect(&s->chardev);
230         }
231     } else {
232         vhost_user_blk_stop(vdev);
233     }
234 
235 }
236 
237 static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
238                                             uint64_t features,
239                                             Error **errp)
240 {
241     VHostUserBlk *s = VHOST_USER_BLK(vdev);
242 
243     /* Turn on pre-defined features */
244     virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
245     virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
246     virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
247     virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
248     virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH);
249     virtio_add_feature(&features, VIRTIO_BLK_F_RO);
250     virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD);
251     virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES);
252 
253     if (s->config_wce) {
254         virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
255     }
256     if (s->num_queues > 1) {
257         virtio_add_feature(&features, VIRTIO_BLK_F_MQ);
258     }
259 
260     return vhost_get_features(&s->dev, user_feature_bits, features);
261 }
262 
263 static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
264 {
265     VHostUserBlk *s = VHOST_USER_BLK(vdev);
266     int i, ret;
267 
268     if (!vdev->start_on_kick) {
269         return;
270     }
271 
272     if (!s->connected) {
273         return;
274     }
275 
276     if (s->dev.started) {
277         return;
278     }
279 
280     /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
281      * vhost here instead of waiting for .set_status().
282      */
283     ret = vhost_user_blk_start(vdev);
284     if (ret < 0) {
285         error_report("vhost-user-blk: vhost start failed: %s",
286                      strerror(-ret));
287         qemu_chr_fe_disconnect(&s->chardev);
288         return;
289     }
290 
291     /* Kick right away to begin processing requests already in vring */
292     for (i = 0; i < s->dev.nvqs; i++) {
293         VirtQueue *kick_vq = virtio_get_queue(vdev, i);
294 
295         if (!virtio_queue_get_desc_addr(vdev, i)) {
296             continue;
297         }
298         event_notifier_set(virtio_queue_get_host_notifier(kick_vq));
299     }
300 }
301 
302 static void vhost_user_blk_reset(VirtIODevice *vdev)
303 {
304     VHostUserBlk *s = VHOST_USER_BLK(vdev);
305 
306     vhost_dev_free_inflight(s->inflight);
307 }
308 
309 static int vhost_user_blk_connect(DeviceState *dev)
310 {
311     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
312     VHostUserBlk *s = VHOST_USER_BLK(vdev);
313     int ret = 0;
314 
315     if (s->connected) {
316         return 0;
317     }
318     s->connected = true;
319 
320     s->dev.nvqs = s->num_queues;
321     s->dev.vqs = s->vhost_vqs;
322     s->dev.vq_index = 0;
323     s->dev.backend_features = 0;
324 
325     vhost_dev_set_config_notifier(&s->dev, &blk_ops);
326 
327     ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
328     if (ret < 0) {
329         error_report("vhost-user-blk: vhost initialization failed: %s",
330                      strerror(-ret));
331         return ret;
332     }
333 
334     /* restore vhost state */
335     if (virtio_device_started(vdev, vdev->status)) {
336         ret = vhost_user_blk_start(vdev);
337         if (ret < 0) {
338             error_report("vhost-user-blk: vhost start failed: %s",
339                          strerror(-ret));
340             return ret;
341         }
342     }
343 
344     return 0;
345 }
346 
347 static void vhost_user_blk_disconnect(DeviceState *dev)
348 {
349     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
350     VHostUserBlk *s = VHOST_USER_BLK(vdev);
351 
352     if (!s->connected) {
353         return;
354     }
355     s->connected = false;
356 
357     vhost_user_blk_stop(vdev);
358 
359     vhost_dev_cleanup(&s->dev);
360 }
361 
362 static void vhost_user_blk_event(void *opaque, QEMUChrEvent event);
363 
364 static void vhost_user_blk_chr_closed_bh(void *opaque)
365 {
366     DeviceState *dev = opaque;
367     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
368     VHostUserBlk *s = VHOST_USER_BLK(vdev);
369 
370     vhost_user_blk_disconnect(dev);
371     qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event,
372             NULL, opaque, NULL, true);
373 }
374 
375 static void vhost_user_blk_event(void *opaque, QEMUChrEvent event)
376 {
377     DeviceState *dev = opaque;
378     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
379     VHostUserBlk *s = VHOST_USER_BLK(vdev);
380 
381     switch (event) {
382     case CHR_EVENT_OPENED:
383         if (vhost_user_blk_connect(dev) < 0) {
384             qemu_chr_fe_disconnect(&s->chardev);
385             return;
386         }
387         break;
388     case CHR_EVENT_CLOSED:
389         /*
390          * A close event may happen during a read/write, but vhost
391          * code assumes the vhost_dev remains setup, so delay the
392          * stop & clear. There are two possible paths to hit this
393          * disconnect event:
394          * 1. When VM is in the RUN_STATE_PRELAUNCH state. The
395          * vhost_user_blk_device_realize() is a caller.
396          * 2. In tha main loop phase after VM start.
397          *
398          * For p2 the disconnect event will be delayed. We can't
399          * do the same for p1, because we are not running the loop
400          * at this moment. So just skip this step and perform
401          * disconnect in the caller function.
402          *
403          * TODO: maybe it is a good idea to make the same fix
404          * for other vhost-user devices.
405          */
406         if (runstate_is_running()) {
407             AioContext *ctx = qemu_get_current_aio_context();
408 
409             qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL, NULL,
410                     NULL, NULL, false);
411             aio_bh_schedule_oneshot(ctx, vhost_user_blk_chr_closed_bh, opaque);
412         }
413 
414         /*
415          * Move vhost device to the stopped state. The vhost-user device
416          * will be clean up and disconnected in BH. This can be useful in
417          * the vhost migration code. If disconnect was caught there is an
418          * option for the general vhost code to get the dev state without
419          * knowing its type (in this case vhost-user).
420          */
421         s->dev.started = false;
422         break;
423     case CHR_EVENT_BREAK:
424     case CHR_EVENT_MUX_IN:
425     case CHR_EVENT_MUX_OUT:
426         /* Ignore */
427         break;
428     }
429 }
430 
431 static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
432 {
433     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
434     VHostUserBlk *s = VHOST_USER_BLK(vdev);
435     Error *err = NULL;
436     int i, ret;
437 
438     if (!s->chardev.chr) {
439         error_setg(errp, "vhost-user-blk: chardev is mandatory");
440         return;
441     }
442 
443     if (s->num_queues == VHOST_USER_BLK_AUTO_NUM_QUEUES) {
444         s->num_queues = 1;
445     }
446     if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) {
447         error_setg(errp, "vhost-user-blk: invalid number of IO queues");
448         return;
449     }
450 
451     if (!s->queue_size) {
452         error_setg(errp, "vhost-user-blk: queue size must be non-zero");
453         return;
454     }
455 
456     if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
457         return;
458     }
459 
460     virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
461                 sizeof(struct virtio_blk_config));
462 
463     s->virtqs = g_new(VirtQueue *, s->num_queues);
464     for (i = 0; i < s->num_queues; i++) {
465         s->virtqs[i] = virtio_add_queue(vdev, s->queue_size,
466                                         vhost_user_blk_handle_output);
467     }
468 
469     s->inflight = g_new0(struct vhost_inflight, 1);
470     s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues);
471     s->connected = false;
472 
473     qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL, vhost_user_blk_event,
474                              NULL, (void *)dev, NULL, true);
475 
476 reconnect:
477     if (qemu_chr_fe_wait_connected(&s->chardev, &err) < 0) {
478         error_report_err(err);
479         goto virtio_err;
480     }
481 
482     /* check whether vhost_user_blk_connect() failed or not */
483     if (!s->connected) {
484         goto reconnect;
485     }
486 
487     ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
488                                sizeof(struct virtio_blk_config));
489     if (ret < 0) {
490         error_report("vhost-user-blk: get block config failed");
491         goto reconnect;
492     }
493 
494     if (s->blkcfg.num_queues != s->num_queues) {
495         s->blkcfg.num_queues = s->num_queues;
496     }
497 
498     return;
499 
500 virtio_err:
501     g_free(s->vhost_vqs);
502     s->vhost_vqs = NULL;
503     g_free(s->inflight);
504     s->inflight = NULL;
505     for (i = 0; i < s->num_queues; i++) {
506         virtio_delete_queue(s->virtqs[i]);
507     }
508     g_free(s->virtqs);
509     virtio_cleanup(vdev);
510     vhost_user_cleanup(&s->vhost_user);
511 }
512 
513 static void vhost_user_blk_device_unrealize(DeviceState *dev)
514 {
515     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
516     VHostUserBlk *s = VHOST_USER_BLK(dev);
517     int i;
518 
519     virtio_set_status(vdev, 0);
520     qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL, NULL,
521                              NULL, NULL, NULL, false);
522     vhost_dev_cleanup(&s->dev);
523     vhost_dev_free_inflight(s->inflight);
524     g_free(s->vhost_vqs);
525     s->vhost_vqs = NULL;
526     g_free(s->inflight);
527     s->inflight = NULL;
528 
529     for (i = 0; i < s->num_queues; i++) {
530         virtio_delete_queue(s->virtqs[i]);
531     }
532     g_free(s->virtqs);
533     virtio_cleanup(vdev);
534     vhost_user_cleanup(&s->vhost_user);
535 }
536 
537 static void vhost_user_blk_instance_init(Object *obj)
538 {
539     VHostUserBlk *s = VHOST_USER_BLK(obj);
540 
541     device_add_bootindex_property(obj, &s->bootindex, "bootindex",
542                                   "/disk@0,0", DEVICE(obj));
543 }
544 
545 static const VMStateDescription vmstate_vhost_user_blk = {
546     .name = "vhost-user-blk",
547     .minimum_version_id = 1,
548     .version_id = 1,
549     .fields = (VMStateField[]) {
550         VMSTATE_VIRTIO_DEVICE,
551         VMSTATE_END_OF_LIST()
552     },
553 };
554 
555 static Property vhost_user_blk_properties[] = {
556     DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev),
557     DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues,
558                        VHOST_USER_BLK_AUTO_NUM_QUEUES),
559     DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
560     DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true),
561     DEFINE_PROP_END_OF_LIST(),
562 };
563 
564 static void vhost_user_blk_class_init(ObjectClass *klass, void *data)
565 {
566     DeviceClass *dc = DEVICE_CLASS(klass);
567     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
568 
569     device_class_set_props(dc, vhost_user_blk_properties);
570     dc->vmsd = &vmstate_vhost_user_blk;
571     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
572     vdc->realize = vhost_user_blk_device_realize;
573     vdc->unrealize = vhost_user_blk_device_unrealize;
574     vdc->get_config = vhost_user_blk_update_config;
575     vdc->set_config = vhost_user_blk_set_config;
576     vdc->get_features = vhost_user_blk_get_features;
577     vdc->set_status = vhost_user_blk_set_status;
578     vdc->reset = vhost_user_blk_reset;
579 }
580 
581 static const TypeInfo vhost_user_blk_info = {
582     .name = TYPE_VHOST_USER_BLK,
583     .parent = TYPE_VIRTIO_DEVICE,
584     .instance_size = sizeof(VHostUserBlk),
585     .instance_init = vhost_user_blk_instance_init,
586     .class_init = vhost_user_blk_class_init,
587 };
588 
589 static void virtio_register_types(void)
590 {
591     type_register_static(&vhost_user_blk_info);
592 }
593 
594 type_init(virtio_register_types)
595