1/* 2 * vmnet-common.m - network client wrapper for Apple vmnet.framework 3 * 4 * Copyright(c) 2022 Vladislav Yaroshchuk <vladislav.yaroshchuk@jetbrains.com> 5 * Copyright(c) 2021 Phillip Tennen <phillip@axleos.com> 6 * 7 * This work is licensed under the terms of the GNU GPL, version 2 or later. 8 * See the COPYING file in the top-level directory. 9 * 10 */ 11 12#include "qemu/osdep.h" 13#include "qemu/main-loop.h" 14#include "qemu/log.h" 15#include "qapi/qapi-types-net.h" 16#include "vmnet_int.h" 17#include "clients.h" 18#include "qemu/error-report.h" 19#include "qapi/error.h" 20#include "sysemu/runstate.h" 21 22#include <vmnet/vmnet.h> 23#include <dispatch/dispatch.h> 24 25 26static void vmnet_send_completed(NetClientState *nc, ssize_t len); 27 28 29const char *vmnet_status_map_str(vmnet_return_t status) 30{ 31 switch (status) { 32 case VMNET_SUCCESS: 33 return "success"; 34 case VMNET_FAILURE: 35 return "general failure (possibly not enough privileges)"; 36 case VMNET_MEM_FAILURE: 37 return "memory allocation failure"; 38 case VMNET_INVALID_ARGUMENT: 39 return "invalid argument specified"; 40 case VMNET_SETUP_INCOMPLETE: 41 return "interface setup is not complete"; 42 case VMNET_INVALID_ACCESS: 43 return "invalid access, permission denied"; 44 case VMNET_PACKET_TOO_BIG: 45 return "packet size is larger than MTU"; 46 case VMNET_BUFFER_EXHAUSTED: 47 return "buffers exhausted in kernel"; 48 case VMNET_TOO_MANY_PACKETS: 49 return "packet count exceeds limit"; 50 case VMNET_SHARING_SERVICE_BUSY: 51 return "conflict, sharing service is in use"; 52 default: 53 return "unknown vmnet error"; 54 } 55} 56 57 58/** 59 * Write packets from QEMU to vmnet interface. 60 * 61 * vmnet.framework supports iov, but writing more than 62 * one iov into vmnet interface fails with 63 * 'VMNET_INVALID_ARGUMENT'. Collecting provided iovs into 64 * one and passing it to vmnet works fine. That's the 65 * reason why receive_iov() left unimplemented. But it still 66 * works with good performance having .receive() only. 67 */ 68ssize_t vmnet_receive_common(NetClientState *nc, 69 const uint8_t *buf, 70 size_t size) 71{ 72 VmnetState *s = DO_UPCAST(VmnetState, nc, nc); 73 struct vmpktdesc packet; 74 struct iovec iov; 75 int pkt_cnt; 76 vmnet_return_t if_status; 77 78 if (size > s->max_packet_size) { 79 warn_report("vmnet: packet is too big, %zu > %" PRIu64, 80 packet.vm_pkt_size, 81 s->max_packet_size); 82 return -1; 83 } 84 85 iov.iov_base = (char *) buf; 86 iov.iov_len = size; 87 88 packet.vm_pkt_iovcnt = 1; 89 packet.vm_flags = 0; 90 packet.vm_pkt_size = size; 91 packet.vm_pkt_iov = &iov; 92 pkt_cnt = 1; 93 94 if_status = vmnet_write(s->vmnet_if, &packet, &pkt_cnt); 95 if (if_status != VMNET_SUCCESS) { 96 error_report("vmnet: write error: %s\n", 97 vmnet_status_map_str(if_status)); 98 return -1; 99 } 100 101 if (pkt_cnt) { 102 return size; 103 } 104 return 0; 105} 106 107 108/** 109 * Read packets from vmnet interface and write them 110 * to temporary buffers in VmnetState. 111 * 112 * Returns read packets number (may be 0) on success, 113 * -1 on error 114 */ 115static int vmnet_read_packets(VmnetState *s) 116{ 117 assert(s->packets_send_current_pos == s->packets_send_end_pos); 118 119 struct vmpktdesc *packets = s->packets_buf; 120 vmnet_return_t status; 121 int i; 122 123 /* Read as many packets as present */ 124 s->packets_send_current_pos = 0; 125 s->packets_send_end_pos = VMNET_PACKETS_LIMIT; 126 for (i = 0; i < s->packets_send_end_pos; ++i) { 127 packets[i].vm_pkt_size = s->max_packet_size; 128 packets[i].vm_pkt_iovcnt = 1; 129 packets[i].vm_flags = 0; 130 } 131 132 status = vmnet_read(s->vmnet_if, packets, &s->packets_send_end_pos); 133 if (status != VMNET_SUCCESS) { 134 error_printf("vmnet: read failed: %s\n", 135 vmnet_status_map_str(status)); 136 s->packets_send_current_pos = 0; 137 s->packets_send_end_pos = 0; 138 return -1; 139 } 140 return s->packets_send_end_pos; 141} 142 143 144/** 145 * Write packets from temporary buffers in VmnetState 146 * to QEMU. 147 */ 148static void vmnet_write_packets_to_qemu(VmnetState *s) 149{ 150 while (s->packets_send_current_pos < s->packets_send_end_pos) { 151 ssize_t size = qemu_send_packet_async(&s->nc, 152 s->iov_buf[s->packets_send_current_pos].iov_base, 153 s->packets_buf[s->packets_send_current_pos].vm_pkt_size, 154 vmnet_send_completed); 155 156 if (size == 0) { 157 /* QEMU is not ready to consume more packets - 158 * stop and wait for completion callback call */ 159 return; 160 } 161 ++s->packets_send_current_pos; 162 } 163} 164 165 166/** 167 * Bottom half callback that transfers packets from vmnet interface 168 * to QEMU. 169 * 170 * The process of transferring packets is three-staged: 171 * 1. Handle vmnet event; 172 * 2. Read packets from vmnet interface into temporary buffer; 173 * 3. Write packets from temporary buffer to QEMU. 174 * 175 * QEMU may suspend this process on the last stage, returning 0 from 176 * qemu_send_packet_async function. If this happens, we should 177 * respectfully wait until it is ready to consume more packets, 178 * write left ones in temporary buffer and only after this 179 * continue reading more packets from vmnet interface. 180 * 181 * Packets to be transferred are stored into packets_buf, 182 * in the window [packets_send_current_pos..packets_send_end_pos) 183 * including current_pos, excluding end_pos. 184 * 185 * Thus, if QEMU is not ready, buffer is not read and 186 * packets_send_current_pos < packets_send_end_pos. 187 */ 188static void vmnet_send_bh(void *opaque) 189{ 190 NetClientState *nc = (NetClientState *) opaque; 191 VmnetState *s = DO_UPCAST(VmnetState, nc, nc); 192 193 /* 194 * Do nothing if QEMU is not ready - wait 195 * for completion callback invocation 196 */ 197 if (s->packets_send_current_pos < s->packets_send_end_pos) { 198 return; 199 } 200 201 /* Read packets from vmnet interface */ 202 if (vmnet_read_packets(s) > 0) { 203 /* Send them to QEMU */ 204 vmnet_write_packets_to_qemu(s); 205 } 206} 207 208 209/** 210 * Completion callback to be invoked by QEMU when it becomes 211 * ready to consume more packets. 212 */ 213static void vmnet_send_completed(NetClientState *nc, ssize_t len) 214{ 215 VmnetState *s = DO_UPCAST(VmnetState, nc, nc); 216 217 /* Callback is invoked eq queued packet is sent */ 218 ++s->packets_send_current_pos; 219 220 /* Complete sending packets left in VmnetState buffers */ 221 vmnet_write_packets_to_qemu(s); 222 223 /* And read new ones from vmnet if VmnetState buffer is ready */ 224 if (s->packets_send_current_pos < s->packets_send_end_pos) { 225 qemu_bh_schedule(s->send_bh); 226 } 227} 228 229 230static void vmnet_bufs_init(VmnetState *s) 231{ 232 struct vmpktdesc *packets = s->packets_buf; 233 struct iovec *iov = s->iov_buf; 234 int i; 235 236 for (i = 0; i < VMNET_PACKETS_LIMIT; ++i) { 237 iov[i].iov_len = s->max_packet_size; 238 iov[i].iov_base = g_malloc0(iov[i].iov_len); 239 packets[i].vm_pkt_iov = iov + i; 240 } 241} 242 243/** 244 * Called on state change to un-register/re-register handlers 245 */ 246static void vmnet_vm_state_change_cb(void *opaque, bool running, RunState state) 247{ 248 VmnetState *s = opaque; 249 250 if (running) { 251 vmnet_interface_set_event_callback( 252 s->vmnet_if, 253 VMNET_INTERFACE_PACKETS_AVAILABLE, 254 s->if_queue, 255 ^(interface_event_t event_id, xpc_object_t event) { 256 assert(event_id == VMNET_INTERFACE_PACKETS_AVAILABLE); 257 /* 258 * This function is being called from a non qemu thread, so 259 * we only schedule a BH, and do the rest of the io completion 260 * handling from vmnet_send_bh() which runs in a qemu context. 261 */ 262 qemu_bh_schedule(s->send_bh); 263 }); 264 } else { 265 vmnet_interface_set_event_callback( 266 s->vmnet_if, 267 VMNET_INTERFACE_PACKETS_AVAILABLE, 268 NULL, 269 NULL); 270 } 271} 272 273int vmnet_if_create(NetClientState *nc, 274 xpc_object_t if_desc, 275 Error **errp) 276{ 277 VmnetState *s = DO_UPCAST(VmnetState, nc, nc); 278 dispatch_semaphore_t if_created_sem = dispatch_semaphore_create(0); 279 __block vmnet_return_t if_status; 280 281 s->if_queue = dispatch_queue_create( 282 "org.qemu.vmnet.if_queue", 283 DISPATCH_QUEUE_SERIAL 284 ); 285 286 xpc_dictionary_set_bool( 287 if_desc, 288 vmnet_allocate_mac_address_key, 289 false 290 ); 291 292#ifdef DEBUG 293 qemu_log("vmnet.start.interface_desc:\n"); 294 xpc_dictionary_apply(if_desc, 295 ^bool(const char *k, xpc_object_t v) { 296 char *desc = xpc_copy_description(v); 297 qemu_log(" %s=%s\n", k, desc); 298 free(desc); 299 return true; 300 }); 301#endif /* DEBUG */ 302 303 s->vmnet_if = vmnet_start_interface( 304 if_desc, 305 s->if_queue, 306 ^(vmnet_return_t status, xpc_object_t interface_param) { 307 if_status = status; 308 if (status != VMNET_SUCCESS || !interface_param) { 309 dispatch_semaphore_signal(if_created_sem); 310 return; 311 } 312 313#ifdef DEBUG 314 qemu_log("vmnet.start.interface_param:\n"); 315 xpc_dictionary_apply(interface_param, 316 ^bool(const char *k, xpc_object_t v) { 317 char *desc = xpc_copy_description(v); 318 qemu_log(" %s=%s\n", k, desc); 319 free(desc); 320 return true; 321 }); 322#endif /* DEBUG */ 323 324 s->mtu = xpc_dictionary_get_uint64( 325 interface_param, 326 vmnet_mtu_key); 327 s->max_packet_size = xpc_dictionary_get_uint64( 328 interface_param, 329 vmnet_max_packet_size_key); 330 331 dispatch_semaphore_signal(if_created_sem); 332 }); 333 334 if (s->vmnet_if == NULL) { 335 dispatch_release(s->if_queue); 336 dispatch_release(if_created_sem); 337 error_setg(errp, 338 "unable to create interface with requested params"); 339 return -1; 340 } 341 342 dispatch_semaphore_wait(if_created_sem, DISPATCH_TIME_FOREVER); 343 dispatch_release(if_created_sem); 344 345 if (if_status != VMNET_SUCCESS) { 346 dispatch_release(s->if_queue); 347 error_setg(errp, 348 "cannot create vmnet interface: %s", 349 vmnet_status_map_str(if_status)); 350 return -1; 351 } 352 353 s->send_bh = aio_bh_new(qemu_get_aio_context(), vmnet_send_bh, nc); 354 vmnet_bufs_init(s); 355 356 s->packets_send_current_pos = 0; 357 s->packets_send_end_pos = 0; 358 359 vmnet_vm_state_change_cb(s, 1, RUN_STATE_RUNNING); 360 361 s->change = qemu_add_vm_change_state_handler(vmnet_vm_state_change_cb, s); 362 363 return 0; 364} 365 366 367void vmnet_cleanup_common(NetClientState *nc) 368{ 369 VmnetState *s = DO_UPCAST(VmnetState, nc, nc); 370 dispatch_semaphore_t if_stopped_sem; 371 372 if (s->vmnet_if == NULL) { 373 return; 374 } 375 376 vmnet_vm_state_change_cb(s, 0, RUN_STATE_SHUTDOWN); 377 qemu_del_vm_change_state_handler(s->change); 378 if_stopped_sem = dispatch_semaphore_create(0); 379 vmnet_stop_interface( 380 s->vmnet_if, 381 s->if_queue, 382 ^(vmnet_return_t status) { 383 assert(status == VMNET_SUCCESS); 384 dispatch_semaphore_signal(if_stopped_sem); 385 }); 386 dispatch_semaphore_wait(if_stopped_sem, DISPATCH_TIME_FOREVER); 387 388 qemu_purge_queued_packets(nc); 389 390 qemu_bh_delete(s->send_bh); 391 dispatch_release(if_stopped_sem); 392 dispatch_release(s->if_queue); 393 394 for (int i = 0; i < VMNET_PACKETS_LIMIT; ++i) { 395 g_free(s->iov_buf[i].iov_base); 396 } 397} 398