xref: /openbmc/qemu/hw/virtio/vhost-user-fs.c (revision 4b9fa0b4)
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     virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
213 
214     /* Request queues */
215     for (i = 0; i < fs->conf.num_request_queues; i++) {
216         virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
217     }
218 
219     /* 1 high prio queue, plus the number configured */
220     fs->vhost_dev.nvqs = 1 + fs->conf.num_request_queues;
221     fs->vhost_dev.vqs = g_new0(struct vhost_virtqueue, fs->vhost_dev.nvqs);
222     ret = vhost_dev_init(&fs->vhost_dev, &fs->vhost_user,
223                          VHOST_BACKEND_TYPE_USER, 0);
224     if (ret < 0) {
225         error_setg_errno(errp, -ret, "vhost_dev_init failed");
226         goto err_virtio;
227     }
228 
229     return;
230 
231 err_virtio:
232     vhost_user_cleanup(&fs->vhost_user);
233     virtio_cleanup(vdev);
234     g_free(fs->vhost_dev.vqs);
235     return;
236 }
237 
238 static void vuf_device_unrealize(DeviceState *dev, Error **errp)
239 {
240     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
241     VHostUserFS *fs = VHOST_USER_FS(dev);
242 
243     /* This will stop vhost backend if appropriate. */
244     vuf_set_status(vdev, 0);
245 
246     vhost_dev_cleanup(&fs->vhost_dev);
247 
248     vhost_user_cleanup(&fs->vhost_user);
249 
250     virtio_cleanup(vdev);
251     g_free(fs->vhost_dev.vqs);
252     fs->vhost_dev.vqs = NULL;
253 }
254 
255 static const VMStateDescription vuf_vmstate = {
256     .name = "vhost-user-fs",
257     .unmigratable = 1,
258 };
259 
260 static Property vuf_properties[] = {
261     DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev),
262     DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag),
263     DEFINE_PROP_UINT16("num-request-queues", VHostUserFS,
264                        conf.num_request_queues, 1),
265     DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128),
266     DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd),
267     DEFINE_PROP_END_OF_LIST(),
268 };
269 
270 static void vuf_class_init(ObjectClass *klass, void *data)
271 {
272     DeviceClass *dc = DEVICE_CLASS(klass);
273     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
274 
275     dc->props = vuf_properties;
276     dc->vmsd = &vuf_vmstate;
277     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
278     vdc->realize = vuf_device_realize;
279     vdc->unrealize = vuf_device_unrealize;
280     vdc->get_features = vuf_get_features;
281     vdc->get_config = vuf_get_config;
282     vdc->set_status = vuf_set_status;
283     vdc->guest_notifier_mask = vuf_guest_notifier_mask;
284     vdc->guest_notifier_pending = vuf_guest_notifier_pending;
285 }
286 
287 static const TypeInfo vuf_info = {
288     .name = TYPE_VHOST_USER_FS,
289     .parent = TYPE_VIRTIO_DEVICE,
290     .instance_size = sizeof(VHostUserFS),
291     .class_init = vuf_class_init,
292 };
293 
294 static void vuf_register_types(void)
295 {
296     type_register_static(&vuf_info);
297 }
298 
299 type_init(vuf_register_types)
300