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