1 | /* |
2 | * vhost-backend |
3 | * |
4 | * Copyright (c) 2013 Virtual Open Systems Sarl. |
5 | * |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
7 | * See the COPYING file in the top-level directory. |
8 | * |
9 | */ |
10 | |
11 | #include "qemu/osdep.h" |
12 | #include "hw/virtio/vhost.h" |
13 | #include "hw/virtio/vhost-backend.h" |
14 | #include "qemu/error-report.h" |
15 | #include "qemu/main-loop.h" |
16 | #include "standard-headers/linux/vhost_types.h" |
17 | |
18 | #ifdef CONFIG_VHOST_KERNEL |
19 | #include <linux/vhost.h> |
20 | #include <sys/ioctl.h> |
21 | |
22 | static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, |
23 | void *arg) |
24 | { |
25 | int fd = (uintptr_t) dev->opaque; |
26 | |
27 | assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); |
28 | |
29 | return ioctl(fd, request, arg); |
30 | } |
31 | |
32 | static int vhost_kernel_init(struct vhost_dev *dev, void *opaque) |
33 | { |
34 | assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); |
35 | |
36 | dev->opaque = opaque; |
37 | |
38 | return 0; |
39 | } |
40 | |
41 | static int vhost_kernel_cleanup(struct vhost_dev *dev) |
42 | { |
43 | int fd = (uintptr_t) dev->opaque; |
44 | |
45 | assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); |
46 | |
47 | return close(fd); |
48 | } |
49 | |
50 | static int vhost_kernel_memslots_limit(struct vhost_dev *dev) |
51 | { |
52 | int limit = 64; |
53 | char *s; |
54 | |
55 | if (g_file_get_contents("/sys/module/vhost/parameters/max_mem_regions" , |
56 | &s, NULL, NULL)) { |
57 | uint64_t val = g_ascii_strtoull(s, NULL, 10); |
58 | if (!((val == G_MAXUINT64 || !val) && errno)) { |
59 | g_free(s); |
60 | return val; |
61 | } |
62 | error_report("ignoring invalid max_mem_regions value in vhost module:" |
63 | " %s" , s); |
64 | } |
65 | g_free(s); |
66 | return limit; |
67 | } |
68 | |
69 | static int vhost_kernel_net_set_backend(struct vhost_dev *dev, |
70 | struct vhost_vring_file *file) |
71 | { |
72 | return vhost_kernel_call(dev, VHOST_NET_SET_BACKEND, file); |
73 | } |
74 | |
75 | static int vhost_kernel_scsi_set_endpoint(struct vhost_dev *dev, |
76 | struct vhost_scsi_target *target) |
77 | { |
78 | return vhost_kernel_call(dev, VHOST_SCSI_SET_ENDPOINT, target); |
79 | } |
80 | |
81 | static int vhost_kernel_scsi_clear_endpoint(struct vhost_dev *dev, |
82 | struct vhost_scsi_target *target) |
83 | { |
84 | return vhost_kernel_call(dev, VHOST_SCSI_CLEAR_ENDPOINT, target); |
85 | } |
86 | |
87 | static int vhost_kernel_scsi_get_abi_version(struct vhost_dev *dev, int *version) |
88 | { |
89 | return vhost_kernel_call(dev, VHOST_SCSI_GET_ABI_VERSION, version); |
90 | } |
91 | |
92 | static int vhost_kernel_set_log_base(struct vhost_dev *dev, uint64_t base, |
93 | struct vhost_log *log) |
94 | { |
95 | return vhost_kernel_call(dev, VHOST_SET_LOG_BASE, &base); |
96 | } |
97 | |
98 | static int vhost_kernel_set_mem_table(struct vhost_dev *dev, |
99 | struct vhost_memory *mem) |
100 | { |
101 | return vhost_kernel_call(dev, VHOST_SET_MEM_TABLE, mem); |
102 | } |
103 | |
104 | static int vhost_kernel_set_vring_addr(struct vhost_dev *dev, |
105 | struct vhost_vring_addr *addr) |
106 | { |
107 | return vhost_kernel_call(dev, VHOST_SET_VRING_ADDR, addr); |
108 | } |
109 | |
110 | static int vhost_kernel_set_vring_endian(struct vhost_dev *dev, |
111 | struct vhost_vring_state *ring) |
112 | { |
113 | return vhost_kernel_call(dev, VHOST_SET_VRING_ENDIAN, ring); |
114 | } |
115 | |
116 | static int vhost_kernel_set_vring_num(struct vhost_dev *dev, |
117 | struct vhost_vring_state *ring) |
118 | { |
119 | return vhost_kernel_call(dev, VHOST_SET_VRING_NUM, ring); |
120 | } |
121 | |
122 | static int vhost_kernel_set_vring_base(struct vhost_dev *dev, |
123 | struct vhost_vring_state *ring) |
124 | { |
125 | return vhost_kernel_call(dev, VHOST_SET_VRING_BASE, ring); |
126 | } |
127 | |
128 | static int vhost_kernel_get_vring_base(struct vhost_dev *dev, |
129 | struct vhost_vring_state *ring) |
130 | { |
131 | return vhost_kernel_call(dev, VHOST_GET_VRING_BASE, ring); |
132 | } |
133 | |
134 | static int vhost_kernel_set_vring_kick(struct vhost_dev *dev, |
135 | struct vhost_vring_file *file) |
136 | { |
137 | return vhost_kernel_call(dev, VHOST_SET_VRING_KICK, file); |
138 | } |
139 | |
140 | static int vhost_kernel_set_vring_call(struct vhost_dev *dev, |
141 | struct vhost_vring_file *file) |
142 | { |
143 | return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file); |
144 | } |
145 | |
146 | static int vhost_kernel_set_vring_busyloop_timeout(struct vhost_dev *dev, |
147 | struct vhost_vring_state *s) |
148 | { |
149 | return vhost_kernel_call(dev, VHOST_SET_VRING_BUSYLOOP_TIMEOUT, s); |
150 | } |
151 | |
152 | static int vhost_kernel_set_features(struct vhost_dev *dev, |
153 | uint64_t features) |
154 | { |
155 | return vhost_kernel_call(dev, VHOST_SET_FEATURES, &features); |
156 | } |
157 | |
158 | static int vhost_kernel_get_features(struct vhost_dev *dev, |
159 | uint64_t *features) |
160 | { |
161 | return vhost_kernel_call(dev, VHOST_GET_FEATURES, features); |
162 | } |
163 | |
164 | static int vhost_kernel_set_owner(struct vhost_dev *dev) |
165 | { |
166 | return vhost_kernel_call(dev, VHOST_SET_OWNER, NULL); |
167 | } |
168 | |
169 | static int vhost_kernel_reset_device(struct vhost_dev *dev) |
170 | { |
171 | return vhost_kernel_call(dev, VHOST_RESET_OWNER, NULL); |
172 | } |
173 | |
174 | static int vhost_kernel_get_vq_index(struct vhost_dev *dev, int idx) |
175 | { |
176 | assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); |
177 | |
178 | return idx - dev->vq_index; |
179 | } |
180 | |
181 | #ifdef CONFIG_VHOST_VSOCK |
182 | static int vhost_kernel_vsock_set_guest_cid(struct vhost_dev *dev, |
183 | uint64_t guest_cid) |
184 | { |
185 | return vhost_kernel_call(dev, VHOST_VSOCK_SET_GUEST_CID, &guest_cid); |
186 | } |
187 | |
188 | static int vhost_kernel_vsock_set_running(struct vhost_dev *dev, int start) |
189 | { |
190 | return vhost_kernel_call(dev, VHOST_VSOCK_SET_RUNNING, &start); |
191 | } |
192 | #endif /* CONFIG_VHOST_VSOCK */ |
193 | |
194 | static void vhost_kernel_iotlb_read(void *opaque) |
195 | { |
196 | struct vhost_dev *dev = opaque; |
197 | struct vhost_msg msg; |
198 | ssize_t len; |
199 | |
200 | while ((len = read((uintptr_t)dev->opaque, &msg, sizeof msg)) > 0) { |
201 | if (len < sizeof msg) { |
202 | error_report("Wrong vhost message len: %d" , (int)len); |
203 | break; |
204 | } |
205 | if (msg.type != VHOST_IOTLB_MSG) { |
206 | error_report("Unknown vhost iotlb message type" ); |
207 | break; |
208 | } |
209 | |
210 | vhost_backend_handle_iotlb_msg(dev, &msg.iotlb); |
211 | } |
212 | } |
213 | |
214 | static int vhost_kernel_send_device_iotlb_msg(struct vhost_dev *dev, |
215 | struct vhost_iotlb_msg *imsg) |
216 | { |
217 | struct vhost_msg msg; |
218 | |
219 | msg.type = VHOST_IOTLB_MSG; |
220 | msg.iotlb = *imsg; |
221 | |
222 | if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { |
223 | error_report("Fail to update device iotlb" ); |
224 | return -EFAULT; |
225 | } |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | static void vhost_kernel_set_iotlb_callback(struct vhost_dev *dev, |
231 | int enabled) |
232 | { |
233 | if (enabled) |
234 | qemu_set_fd_handler((uintptr_t)dev->opaque, |
235 | vhost_kernel_iotlb_read, NULL, dev); |
236 | else |
237 | qemu_set_fd_handler((uintptr_t)dev->opaque, NULL, NULL, NULL); |
238 | } |
239 | |
240 | static const VhostOps kernel_ops = { |
241 | .backend_type = VHOST_BACKEND_TYPE_KERNEL, |
242 | .vhost_backend_init = vhost_kernel_init, |
243 | .vhost_backend_cleanup = vhost_kernel_cleanup, |
244 | .vhost_backend_memslots_limit = vhost_kernel_memslots_limit, |
245 | .vhost_net_set_backend = vhost_kernel_net_set_backend, |
246 | .vhost_scsi_set_endpoint = vhost_kernel_scsi_set_endpoint, |
247 | .vhost_scsi_clear_endpoint = vhost_kernel_scsi_clear_endpoint, |
248 | .vhost_scsi_get_abi_version = vhost_kernel_scsi_get_abi_version, |
249 | .vhost_set_log_base = vhost_kernel_set_log_base, |
250 | .vhost_set_mem_table = vhost_kernel_set_mem_table, |
251 | .vhost_set_vring_addr = vhost_kernel_set_vring_addr, |
252 | .vhost_set_vring_endian = vhost_kernel_set_vring_endian, |
253 | .vhost_set_vring_num = vhost_kernel_set_vring_num, |
254 | .vhost_set_vring_base = vhost_kernel_set_vring_base, |
255 | .vhost_get_vring_base = vhost_kernel_get_vring_base, |
256 | .vhost_set_vring_kick = vhost_kernel_set_vring_kick, |
257 | .vhost_set_vring_call = vhost_kernel_set_vring_call, |
258 | .vhost_set_vring_busyloop_timeout = |
259 | vhost_kernel_set_vring_busyloop_timeout, |
260 | .vhost_set_features = vhost_kernel_set_features, |
261 | .vhost_get_features = vhost_kernel_get_features, |
262 | .vhost_set_owner = vhost_kernel_set_owner, |
263 | .vhost_reset_device = vhost_kernel_reset_device, |
264 | .vhost_get_vq_index = vhost_kernel_get_vq_index, |
265 | #ifdef CONFIG_VHOST_VSOCK |
266 | .vhost_vsock_set_guest_cid = vhost_kernel_vsock_set_guest_cid, |
267 | .vhost_vsock_set_running = vhost_kernel_vsock_set_running, |
268 | #endif /* CONFIG_VHOST_VSOCK */ |
269 | .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, |
270 | .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg, |
271 | }; |
272 | #endif |
273 | |
274 | int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) |
275 | { |
276 | int r = 0; |
277 | |
278 | switch (backend_type) { |
279 | #ifdef CONFIG_VHOST_KERNEL |
280 | case VHOST_BACKEND_TYPE_KERNEL: |
281 | dev->vhost_ops = &kernel_ops; |
282 | break; |
283 | #endif |
284 | #ifdef CONFIG_VHOST_USER |
285 | case VHOST_BACKEND_TYPE_USER: |
286 | dev->vhost_ops = &user_ops; |
287 | break; |
288 | #endif |
289 | default: |
290 | error_report("Unknown vhost backend type" ); |
291 | r = -1; |
292 | } |
293 | |
294 | return r; |
295 | } |
296 | |
297 | int vhost_backend_update_device_iotlb(struct vhost_dev *dev, |
298 | uint64_t iova, uint64_t uaddr, |
299 | uint64_t len, |
300 | IOMMUAccessFlags perm) |
301 | { |
302 | struct vhost_iotlb_msg imsg; |
303 | |
304 | imsg.iova = iova; |
305 | imsg.uaddr = uaddr; |
306 | imsg.size = len; |
307 | imsg.type = VHOST_IOTLB_UPDATE; |
308 | |
309 | switch (perm) { |
310 | case IOMMU_RO: |
311 | imsg.perm = VHOST_ACCESS_RO; |
312 | break; |
313 | case IOMMU_WO: |
314 | imsg.perm = VHOST_ACCESS_WO; |
315 | break; |
316 | case IOMMU_RW: |
317 | imsg.perm = VHOST_ACCESS_RW; |
318 | break; |
319 | default: |
320 | return -EINVAL; |
321 | } |
322 | |
323 | if (dev->vhost_ops && dev->vhost_ops->vhost_send_device_iotlb_msg) |
324 | return dev->vhost_ops->vhost_send_device_iotlb_msg(dev, &imsg); |
325 | |
326 | return -ENODEV; |
327 | } |
328 | |
329 | int vhost_backend_invalidate_device_iotlb(struct vhost_dev *dev, |
330 | uint64_t iova, uint64_t len) |
331 | { |
332 | struct vhost_iotlb_msg imsg; |
333 | |
334 | imsg.iova = iova; |
335 | imsg.size = len; |
336 | imsg.type = VHOST_IOTLB_INVALIDATE; |
337 | |
338 | if (dev->vhost_ops && dev->vhost_ops->vhost_send_device_iotlb_msg) |
339 | return dev->vhost_ops->vhost_send_device_iotlb_msg(dev, &imsg); |
340 | |
341 | return -ENODEV; |
342 | } |
343 | |
344 | int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, |
345 | struct vhost_iotlb_msg *imsg) |
346 | { |
347 | int ret = 0; |
348 | |
349 | switch (imsg->type) { |
350 | case VHOST_IOTLB_MISS: |
351 | ret = vhost_device_iotlb_miss(dev, imsg->iova, |
352 | imsg->perm != VHOST_ACCESS_RO); |
353 | break; |
354 | case VHOST_IOTLB_ACCESS_FAIL: |
355 | /* FIXME: report device iotlb error */ |
356 | error_report("Access failure IOTLB message type not supported" ); |
357 | ret = -ENOTSUP; |
358 | break; |
359 | case VHOST_IOTLB_UPDATE: |
360 | case VHOST_IOTLB_INVALIDATE: |
361 | default: |
362 | error_report("Unexpected IOTLB message type" ); |
363 | ret = -EINVAL; |
364 | break; |
365 | } |
366 | |
367 | return ret; |
368 | } |
369 | |