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