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