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