vmnet-common.m 12 KB

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