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