xref: /openbmc/qemu/hw/virtio/vhost-user-fs.c (revision bd6ce9a6)
1 /*
2  * Vhost-user filesystem virtio device
3  *
4  * Copyright 2018-2019 Red Hat, Inc.
5  *
6  * Authors:
7  *  Stefan Hajnoczi <stefanha@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or
10  * (at your option) any later version.  See the COPYING file in the
11  * top-level directory.
12  */
13 
14 #include "qemu/osdep.h"
15 #include <sys/ioctl.h>
16 #include "standard-headers/linux/virtio_fs.h"
17 #include "qapi/error.h"
18 #include "hw/qdev-properties.h"
19 #include "hw/virtio/virtio-bus.h"
20 #include "hw/virtio/virtio-access.h"
21 #include "qemu/error-report.h"
22 #include "hw/virtio/vhost-user-fs.h"
23 #include "monitor/monitor.h"
24 
25 static void vuf_get_config(VirtIODevice *vdev, uint8_t *config)
26 {
27     VHostUserFS *fs = VHOST_USER_FS(vdev);
28     struct virtio_fs_config fscfg = {};
29 
30     memcpy((char *)fscfg.tag, fs->conf.tag,
31            MIN(strlen(fs->conf.tag) + 1, sizeof(fscfg.tag)));
32 
33     virtio_stl_p(vdev, &fscfg.num_request_queues, fs->conf.num_request_queues);
34 
35     memcpy(config, &fscfg, sizeof(fscfg));
36 }
37 
38 static void vuf_start(VirtIODevice *vdev)
39 {
40     VHostUserFS *fs = VHOST_USER_FS(vdev);
41     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
42     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
43     int ret;
44     int i;
45 
46     if (!k->set_guest_notifiers) {
47         error_report("binding does not support guest notifiers");
48         return;
49     }
50 
51     ret = vhost_dev_enable_notifiers(&fs->vhost_dev, vdev);
52     if (ret < 0) {
53         error_report("Error enabling host notifiers: %d", -ret);
54         return;
55     }
56 
57     ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, true);
58     if (ret < 0) {
59         error_report("Error binding guest notifier: %d", -ret);
60         goto err_host_notifiers;
61     }
62 
63     fs->vhost_dev.acked_features = vdev->guest_features;
64     ret = vhost_dev_start(&fs->vhost_dev, vdev);
65     if (ret < 0) {
66         error_report("Error starting vhost: %d", -ret);
67         goto err_guest_notifiers;
68     }
69 
70     /*
71      * guest_notifier_mask/pending not used yet, so just unmask
72      * everything here.  virtio-pci will do the right thing by
73      * enabling/disabling irqfd.
74      */
75     for (i = 0; i < fs->vhost_dev.nvqs; i++) {
76         vhost_virtqueue_mask(&fs->vhost_dev, vdev, i, false);
77     }
78 
79     return;
80 
81 err_guest_notifiers:
82     k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
83 err_host_notifiers:
84     vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
85 }
86 
87 static void vuf_stop(VirtIODevice *vdev)
88 {
89     VHostUserFS *fs = VHOST_USER_FS(vdev);
90     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
91     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
92     int ret;
93 
94     if (!k->set_guest_notifiers) {
95         return;
96     }
97 
98     vhost_dev_stop(&fs->vhost_dev, vdev);
99 
100     ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
101     if (ret < 0) {
102         error_report("vhost guest notifier cleanup failed: %d", ret);
103         return;
104     }
105 
106     vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
107 }
108 
109 static void vuf_set_status(VirtIODevice *vdev, uint8_t status)
110 {
111     VHostUserFS *fs = VHOST_USER_FS(vdev);
112     bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
113 
114     if (!vdev->vm_running) {
115         should_start = false;
116     }
117 
118     if (fs->vhost_dev.started == should_start) {
119         return;
120     }
121 
122     if (should_start) {
123         vuf_start(vdev);
124     } else {
125         vuf_stop(vdev);
126     }
127 }
128 
129 static uint64_t vuf_get_features(VirtIODevice *vdev,
130                                       uint64_t requested_features,
131                                       Error **errp)
132 {
133     /* No feature bits used yet */
134     return requested_features;
135 }
136 
137 static void vuf_handle_output(VirtIODevice *vdev, VirtQueue *vq)
138 {
139     /*
140      * Not normally called; it's the daemon that handles the queue;
141      * however virtio's cleanup path can call this.
142      */
143 }
144 
145 static void vuf_guest_notifier_mask(VirtIODevice *vdev, int idx,
146                                             bool mask)
147 {
148     VHostUserFS *fs = VHOST_USER_FS(vdev);
149 
150     vhost_virtqueue_mask(&fs->vhost_dev, vdev, idx, mask);
151 }
152 
153 static bool vuf_guest_notifier_pending(VirtIODevice *vdev, int idx)
154 {
155     VHostUserFS *fs = VHOST_USER_FS(vdev);
156 
157     return vhost_virtqueue_pending(&fs->vhost_dev, idx);
158 }
159 
160 static void vuf_device_realize(DeviceState *dev, Error **errp)
161 {
162     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
163     VHostUserFS *fs = VHOST_USER_FS(dev);
164     unsigned int i;
165     size_t len;
166     int ret;
167 
168     if (!fs->conf.chardev.chr) {
169         error_setg(errp, "missing chardev");
170         return;
171     }
172 
173     if (!fs->conf.tag) {
174         error_setg(errp, "missing tag property");
175         return;
176     }
177     len = strlen(fs->conf.tag);
178     if (len == 0) {
179         error_setg(errp, "tag property cannot be empty");
180         return;
181     }
182     if (len > sizeof_field(struct virtio_fs_config, tag)) {
183         error_setg(errp, "tag property must be %zu bytes or less",
184                    sizeof_field(struct virtio_fs_config, tag));
185         return;
186     }
187 
188     if (fs->conf.num_request_queues == 0) {
189         error_setg(errp, "num-request-queues property must be larger than 0");
190         return;
191     }
192 
193     if (!is_power_of_2(fs->conf.queue_size)) {
194         error_setg(errp, "queue-size property must be a power of 2");
195         return;
196     }
197 
198     if (fs->conf.queue_size > VIRTQUEUE_MAX_SIZE) {
199         error_setg(errp, "queue-size property must be %u or smaller",
200                    VIRTQUEUE_MAX_SIZE);
201         return;
202     }
203 
204     if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev, errp)) {
205         return;
206     }
207 
208     virtio_init(vdev, "vhost-user-fs", VIRTIO_ID_FS,
209                 sizeof(struct virtio_fs_config));
210 
211     /* Hiprio queue */
212     fs->hiprio_vq = virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
213 
214     /* Request queues */
215     fs->req_vqs = g_new(VirtQueue *, fs->conf.num_request_queues);
216     for (i = 0; i < fs->conf.num_request_queues; i++) {
217         fs->req_vqs[i] = virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
218     }
219 
220     /* 1 high prio queue, plus the number configured */
221     fs->vhost_dev.nvqs = 1 + fs->conf.num_request_queues;
222     fs->vhost_dev.vqs = g_new0(struct vhost_virtqueue, fs->vhost_dev.nvqs);
223     ret = vhost_dev_init(&fs->vhost_dev, &fs->vhost_user,
224                          VHOST_BACKEND_TYPE_USER, 0);
225     if (ret < 0) {
226         error_setg_errno(errp, -ret, "vhost_dev_init failed");
227         goto err_virtio;
228     }
229 
230     return;
231 
232 err_virtio:
233     vhost_user_cleanup(&fs->vhost_user);
234     virtio_delete_queue(fs->hiprio_vq);
235     for (i = 0; i < fs->conf.num_request_queues; i++) {
236         virtio_delete_queue(fs->req_vqs[i]);
237     }
238     g_free(fs->req_vqs);
239     virtio_cleanup(vdev);
240     g_free(fs->vhost_dev.vqs);
241     return;
242 }
243 
244 static void vuf_device_unrealize(DeviceState *dev, Error **errp)
245 {
246     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
247     VHostUserFS *fs = VHOST_USER_FS(dev);
248     int i;
249 
250     /* This will stop vhost backend if appropriate. */
251     vuf_set_status(vdev, 0);
252 
253     vhost_dev_cleanup(&fs->vhost_dev);
254 
255     vhost_user_cleanup(&fs->vhost_user);
256 
257     virtio_delete_queue(fs->hiprio_vq);
258     for (i = 0; i < fs->conf.num_request_queues; i++) {
259         virtio_delete_queue(fs->req_vqs[i]);
260     }
261     g_free(fs->req_vqs);
262     virtio_cleanup(vdev);
263     g_free(fs->vhost_dev.vqs);
264     fs->vhost_dev.vqs = NULL;
265 }
266 
267 static const VMStateDescription vuf_vmstate = {
268     .name = "vhost-user-fs",
269     .unmigratable = 1,
270 };
271 
272 static Property vuf_properties[] = {
273     DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev),
274     DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag),
275     DEFINE_PROP_UINT16("num-request-queues", VHostUserFS,
276                        conf.num_request_queues, 1),
277     DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128),
278     DEFINE_PROP_END_OF_LIST(),
279 };
280 
281 static void vuf_class_init(ObjectClass *klass, void *data)
282 {
283     DeviceClass *dc = DEVICE_CLASS(klass);
284     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
285 
286     device_class_set_props(dc, vuf_properties);
287     dc->vmsd = &vuf_vmstate;
288     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
289     vdc->realize = vuf_device_realize;
290     vdc->unrealize = vuf_device_unrealize;
291     vdc->get_features = vuf_get_features;
292     vdc->get_config = vuf_get_config;
293     vdc->set_status = vuf_set_status;
294     vdc->guest_notifier_mask = vuf_guest_notifier_mask;
295     vdc->guest_notifier_pending = vuf_guest_notifier_pending;
296 }
297 
298 static const TypeInfo vuf_info = {
299     .name = TYPE_VHOST_USER_FS,
300     .parent = TYPE_VIRTIO_DEVICE,
301     .instance_size = sizeof(VHostUserFS),
302     .class_init = vuf_class_init,
303 };
304 
305 static void vuf_register_types(void)
306 {
307     type_register_static(&vuf_info);
308 }
309 
310 type_init(vuf_register_types)
311