2 * Vhost-user filesystem virtio device
4 * Copyright 2018-2019 Red Hat, Inc.
7 * Stefan Hajnoczi <stefanha@redhat.com>
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.
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"
25 static void vuf_get_config(VirtIODevice
*vdev
, uint8_t *config
)
27 VHostUserFS
*fs
= VHOST_USER_FS(vdev
);
28 struct virtio_fs_config fscfg
= {};
30 memcpy((char *)fscfg
.tag
, fs
->conf
.tag
,
31 MIN(strlen(fs
->conf
.tag
) + 1, sizeof(fscfg
.tag
)));
33 virtio_stl_p(vdev
, &fscfg
.num_request_queues
, fs
->conf
.num_request_queues
);
35 memcpy(config
, &fscfg
, sizeof(fscfg
));
38 static void vuf_start(VirtIODevice
*vdev
)
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
);
46 if (!k
->set_guest_notifiers
) {
47 error_report("binding does not support guest notifiers");
51 ret
= vhost_dev_enable_notifiers(&fs
->vhost_dev
, vdev
);
53 error_report("Error enabling host notifiers: %d", -ret
);
57 ret
= k
->set_guest_notifiers(qbus
->parent
, fs
->vhost_dev
.nvqs
, true);
59 error_report("Error binding guest notifier: %d", -ret
);
60 goto err_host_notifiers
;
63 fs
->vhost_dev
.acked_features
= vdev
->guest_features
;
64 ret
= vhost_dev_start(&fs
->vhost_dev
, vdev
);
66 error_report("Error starting vhost: %d", -ret
);
67 goto err_guest_notifiers
;
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.
75 for (i
= 0; i
< fs
->vhost_dev
.nvqs
; i
++) {
76 vhost_virtqueue_mask(&fs
->vhost_dev
, vdev
, i
, false);
82 k
->set_guest_notifiers(qbus
->parent
, fs
->vhost_dev
.nvqs
, false);
84 vhost_dev_disable_notifiers(&fs
->vhost_dev
, vdev
);
87 static void vuf_stop(VirtIODevice
*vdev
)
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
);
94 if (!k
->set_guest_notifiers
) {
98 vhost_dev_stop(&fs
->vhost_dev
, vdev
);
100 ret
= k
->set_guest_notifiers(qbus
->parent
, fs
->vhost_dev
.nvqs
, false);
102 error_report("vhost guest notifier cleanup failed: %d", ret
);
106 vhost_dev_disable_notifiers(&fs
->vhost_dev
, vdev
);
109 static void vuf_set_status(VirtIODevice
*vdev
, uint8_t status
)
111 VHostUserFS
*fs
= VHOST_USER_FS(vdev
);
112 bool should_start
= status
& VIRTIO_CONFIG_S_DRIVER_OK
;
114 if (!vdev
->vm_running
) {
115 should_start
= false;
118 if (fs
->vhost_dev
.started
== should_start
) {
129 static uint64_t vuf_get_features(VirtIODevice
*vdev
,
130 uint64_t requested_features
,
133 /* No feature bits used yet */
134 return requested_features
;
137 static void vuf_handle_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
140 * Not normally called; it's the daemon that handles the queue;
141 * however virtio's cleanup path can call this.
145 static void vuf_guest_notifier_mask(VirtIODevice
*vdev
, int idx
,
148 VHostUserFS
*fs
= VHOST_USER_FS(vdev
);
150 vhost_virtqueue_mask(&fs
->vhost_dev
, vdev
, idx
, mask
);
153 static bool vuf_guest_notifier_pending(VirtIODevice
*vdev
, int idx
)
155 VHostUserFS
*fs
= VHOST_USER_FS(vdev
);
157 return vhost_virtqueue_pending(&fs
->vhost_dev
, idx
);
160 static void vuf_device_realize(DeviceState
*dev
, Error
**errp
)
162 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
163 VHostUserFS
*fs
= VHOST_USER_FS(dev
);
168 if (!fs
->conf
.chardev
.chr
) {
169 error_setg(errp
, "missing chardev");
174 error_setg(errp
, "missing tag property");
177 len
= strlen(fs
->conf
.tag
);
179 error_setg(errp
, "tag property cannot be empty");
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
));
188 if (fs
->conf
.num_request_queues
== 0) {
189 error_setg(errp
, "num-request-queues property must be larger than 0");
193 if (!is_power_of_2(fs
->conf
.queue_size
)) {
194 error_setg(errp
, "queue-size property must be a power of 2");
198 if (fs
->conf
.queue_size
> VIRTQUEUE_MAX_SIZE
) {
199 error_setg(errp
, "queue-size property must be %u or smaller",
204 if (!vhost_user_init(&fs
->vhost_user
, &fs
->conf
.chardev
, errp
)) {
208 virtio_init(vdev
, "vhost-user-fs", VIRTIO_ID_FS
,
209 sizeof(struct virtio_fs_config
));
212 fs
->hiprio_vq
= virtio_add_queue(vdev
, fs
->conf
.queue_size
, vuf_handle_output
);
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
);
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);
226 error_setg_errno(errp
, -ret
, "vhost_dev_init failed");
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
]);
239 virtio_cleanup(vdev
);
240 g_free(fs
->vhost_dev
.vqs
);
244 static void vuf_device_unrealize(DeviceState
*dev
, Error
**errp
)
246 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
247 VHostUserFS
*fs
= VHOST_USER_FS(dev
);
250 /* This will stop vhost backend if appropriate. */
251 vuf_set_status(vdev
, 0);
253 vhost_dev_cleanup(&fs
->vhost_dev
);
255 vhost_user_cleanup(&fs
->vhost_user
);
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
]);
262 virtio_cleanup(vdev
);
263 g_free(fs
->vhost_dev
.vqs
);
264 fs
->vhost_dev
.vqs
= NULL
;
267 static const VMStateDescription vuf_vmstate
= {
268 .name
= "vhost-user-fs",
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(),
281 static void vuf_class_init(ObjectClass
*klass
, void *data
)
283 DeviceClass
*dc
= DEVICE_CLASS(klass
);
284 VirtioDeviceClass
*vdc
= VIRTIO_DEVICE_CLASS(klass
);
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
;
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
,
305 static void vuf_register_types(void)
307 type_register_static(&vuf_info
);
310 type_init(vuf_register_types
)