vhost-user.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. * vhost-user.c
  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. #include "qemu/osdep.h"
  11. #include "clients.h"
  12. #include "net/vhost_net.h"
  13. #include "net/vhost-user.h"
  14. #include "hw/virtio/vhost-user.h"
  15. #include "chardev/char-fe.h"
  16. #include "qapi/error.h"
  17. #include "qapi/qapi-commands-net.h"
  18. #include "qapi/qapi-events-net.h"
  19. #include "qemu/config-file.h"
  20. #include "qemu/error-report.h"
  21. #include "qemu/option.h"
  22. #include "trace.h"
  23. typedef struct NetVhostUserState {
  24. NetClientState nc;
  25. CharBackend chr; /* only queue index 0 */
  26. VhostUserState *vhost_user;
  27. VHostNetState *vhost_net;
  28. guint watch;
  29. uint64_t acked_features;
  30. bool started;
  31. } NetVhostUserState;
  32. VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
  33. {
  34. NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
  35. assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  36. return s->vhost_net;
  37. }
  38. uint64_t vhost_user_get_acked_features(NetClientState *nc)
  39. {
  40. NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
  41. assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  42. return s->acked_features;
  43. }
  44. void vhost_user_save_acked_features(NetClientState *nc)
  45. {
  46. NetVhostUserState *s;
  47. s = DO_UPCAST(NetVhostUserState, nc, nc);
  48. if (s->vhost_net) {
  49. uint64_t features = vhost_net_get_acked_features(s->vhost_net);
  50. if (features) {
  51. s->acked_features = features;
  52. }
  53. }
  54. }
  55. static void vhost_user_stop(int queues, NetClientState *ncs[])
  56. {
  57. int i;
  58. NetVhostUserState *s;
  59. for (i = 0; i < queues; i++) {
  60. assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  61. s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
  62. if (s->vhost_net) {
  63. vhost_user_save_acked_features(ncs[i]);
  64. vhost_net_cleanup(s->vhost_net);
  65. }
  66. }
  67. }
  68. static int vhost_user_start(int queues, NetClientState *ncs[],
  69. VhostUserState *be)
  70. {
  71. VhostNetOptions options;
  72. struct vhost_net *net = NULL;
  73. NetVhostUserState *s;
  74. int max_queues;
  75. int i;
  76. options.backend_type = VHOST_BACKEND_TYPE_USER;
  77. for (i = 0; i < queues; i++) {
  78. assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  79. s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
  80. options.net_backend = ncs[i];
  81. options.opaque = be;
  82. options.busyloop_timeout = 0;
  83. options.nvqs = 2;
  84. net = vhost_net_init(&options);
  85. if (!net) {
  86. error_report("failed to init vhost_net for queue %d", i);
  87. goto err;
  88. }
  89. if (i == 0) {
  90. max_queues = vhost_net_get_max_queues(net);
  91. if (queues > max_queues) {
  92. error_report("you are asking more queues than supported: %d",
  93. max_queues);
  94. goto err;
  95. }
  96. }
  97. if (s->vhost_net) {
  98. vhost_net_cleanup(s->vhost_net);
  99. g_free(s->vhost_net);
  100. }
  101. s->vhost_net = net;
  102. }
  103. return 0;
  104. err:
  105. if (net) {
  106. vhost_net_cleanup(net);
  107. g_free(net);
  108. }
  109. vhost_user_stop(i, ncs);
  110. return -1;
  111. }
  112. static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf,
  113. size_t size)
  114. {
  115. /* In case of RARP (message size is 60) notify backup to send a fake RARP.
  116. This fake RARP will be sent by backend only for guest
  117. without GUEST_ANNOUNCE capability.
  118. */
  119. if (size == 60) {
  120. NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
  121. int r;
  122. static int display_rarp_failure = 1;
  123. char mac_addr[6];
  124. /* extract guest mac address from the RARP message */
  125. memcpy(mac_addr, &buf[6], 6);
  126. r = vhost_net_notify_migration_done(s->vhost_net, mac_addr);
  127. if ((r != 0) && (display_rarp_failure)) {
  128. fprintf(stderr,
  129. "Vhost user backend fails to broadcast fake RARP\n");
  130. fflush(stderr);
  131. display_rarp_failure = 0;
  132. }
  133. }
  134. return size;
  135. }
  136. static void net_vhost_user_cleanup(NetClientState *nc)
  137. {
  138. NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
  139. if (s->vhost_net) {
  140. vhost_net_cleanup(s->vhost_net);
  141. g_free(s->vhost_net);
  142. s->vhost_net = NULL;
  143. }
  144. if (nc->queue_index == 0) {
  145. if (s->watch) {
  146. g_source_remove(s->watch);
  147. s->watch = 0;
  148. }
  149. qemu_chr_fe_deinit(&s->chr, true);
  150. if (s->vhost_user) {
  151. vhost_user_cleanup(s->vhost_user);
  152. g_free(s->vhost_user);
  153. s->vhost_user = NULL;
  154. }
  155. }
  156. qemu_purge_queued_packets(nc);
  157. }
  158. static int vhost_user_set_vnet_endianness(NetClientState *nc,
  159. bool enable)
  160. {
  161. /* Nothing to do. If the server supports
  162. * VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, it will get the
  163. * vnet header endianness from there. If it doesn't, negotiation
  164. * fails.
  165. */
  166. return 0;
  167. }
  168. static bool vhost_user_has_vnet_hdr(NetClientState *nc)
  169. {
  170. assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  171. return true;
  172. }
  173. static bool vhost_user_has_ufo(NetClientState *nc)
  174. {
  175. assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  176. return true;
  177. }
  178. static bool vhost_user_check_peer_type(NetClientState *nc, ObjectClass *oc,
  179. Error **errp)
  180. {
  181. const char *driver = object_class_get_name(oc);
  182. if (!g_str_has_prefix(driver, "virtio-net-")) {
  183. error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
  184. return false;
  185. }
  186. return true;
  187. }
  188. static NetClientInfo net_vhost_user_info = {
  189. .type = NET_CLIENT_DRIVER_VHOST_USER,
  190. .size = sizeof(NetVhostUserState),
  191. .receive = vhost_user_receive,
  192. .cleanup = net_vhost_user_cleanup,
  193. .has_vnet_hdr = vhost_user_has_vnet_hdr,
  194. .has_ufo = vhost_user_has_ufo,
  195. .set_vnet_be = vhost_user_set_vnet_endianness,
  196. .set_vnet_le = vhost_user_set_vnet_endianness,
  197. .check_peer_type = vhost_user_check_peer_type,
  198. };
  199. static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond,
  200. void *opaque)
  201. {
  202. NetVhostUserState *s = opaque;
  203. qemu_chr_fe_disconnect(&s->chr);
  204. return G_SOURCE_CONTINUE;
  205. }
  206. static void net_vhost_user_event(void *opaque, QEMUChrEvent event);
  207. static void chr_closed_bh(void *opaque)
  208. {
  209. const char *name = opaque;
  210. NetClientState *ncs[MAX_QUEUE_NUM];
  211. NetVhostUserState *s;
  212. Error *err = NULL;
  213. int queues, i;
  214. queues = qemu_find_net_clients_except(name, ncs,
  215. NET_CLIENT_DRIVER_NIC,
  216. MAX_QUEUE_NUM);
  217. assert(queues < MAX_QUEUE_NUM);
  218. s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
  219. for (i = queues -1; i >= 0; i--) {
  220. vhost_user_save_acked_features(ncs[i]);
  221. }
  222. qmp_set_link(name, false, &err);
  223. qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
  224. NULL, opaque, NULL, true);
  225. if (err) {
  226. error_report_err(err);
  227. }
  228. qapi_event_send_netdev_vhost_user_disconnected(name);
  229. }
  230. static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
  231. {
  232. const char *name = opaque;
  233. NetClientState *ncs[MAX_QUEUE_NUM];
  234. NetVhostUserState *s;
  235. Chardev *chr;
  236. Error *err = NULL;
  237. int queues;
  238. queues = qemu_find_net_clients_except(name, ncs,
  239. NET_CLIENT_DRIVER_NIC,
  240. MAX_QUEUE_NUM);
  241. assert(queues < MAX_QUEUE_NUM);
  242. s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
  243. chr = qemu_chr_fe_get_driver(&s->chr);
  244. trace_vhost_user_event(chr->label, event);
  245. switch (event) {
  246. case CHR_EVENT_OPENED:
  247. if (vhost_user_start(queues, ncs, s->vhost_user) < 0) {
  248. qemu_chr_fe_disconnect(&s->chr);
  249. return;
  250. }
  251. s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
  252. net_vhost_user_watch, s);
  253. qmp_set_link(name, true, &err);
  254. s->started = true;
  255. qapi_event_send_netdev_vhost_user_connected(name, chr->label);
  256. break;
  257. case CHR_EVENT_CLOSED:
  258. /* a close event may happen during a read/write, but vhost
  259. * code assumes the vhost_dev remains setup, so delay the
  260. * stop & clear to idle.
  261. * FIXME: better handle failure in vhost code, remove bh
  262. */
  263. if (s->watch) {
  264. AioContext *ctx = qemu_get_current_aio_context();
  265. g_source_remove(s->watch);
  266. s->watch = 0;
  267. qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL,
  268. NULL, NULL, false);
  269. aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque);
  270. }
  271. break;
  272. case CHR_EVENT_BREAK:
  273. case CHR_EVENT_MUX_IN:
  274. case CHR_EVENT_MUX_OUT:
  275. /* Ignore */
  276. break;
  277. }
  278. if (err) {
  279. error_report_err(err);
  280. }
  281. }
  282. static int net_vhost_user_init(NetClientState *peer, const char *device,
  283. const char *name, Chardev *chr,
  284. int queues)
  285. {
  286. Error *err = NULL;
  287. NetClientState *nc, *nc0 = NULL;
  288. NetVhostUserState *s = NULL;
  289. VhostUserState *user;
  290. int i;
  291. assert(name);
  292. assert(queues > 0);
  293. user = g_new0(struct VhostUserState, 1);
  294. for (i = 0; i < queues; i++) {
  295. nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
  296. qemu_set_info_str(nc, "vhost-user%d to %s", i, chr->label);
  297. nc->queue_index = i;
  298. if (!nc0) {
  299. nc0 = nc;
  300. s = DO_UPCAST(NetVhostUserState, nc, nc);
  301. if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
  302. !vhost_user_init(user, &s->chr, &err)) {
  303. error_report_err(err);
  304. goto err;
  305. }
  306. }
  307. s = DO_UPCAST(NetVhostUserState, nc, nc);
  308. s->vhost_user = user;
  309. }
  310. s = DO_UPCAST(NetVhostUserState, nc, nc0);
  311. do {
  312. if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
  313. error_report_err(err);
  314. goto err;
  315. }
  316. qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
  317. net_vhost_user_event, NULL, nc0->name, NULL,
  318. true);
  319. } while (!s->started);
  320. assert(s->vhost_net);
  321. return 0;
  322. err:
  323. if (user) {
  324. vhost_user_cleanup(user);
  325. g_free(user);
  326. if (s) {
  327. s->vhost_user = NULL;
  328. }
  329. }
  330. if (nc0) {
  331. qemu_del_net_client(nc0);
  332. }
  333. return -1;
  334. }
  335. static Chardev *net_vhost_claim_chardev(
  336. const NetdevVhostUserOptions *opts, Error **errp)
  337. {
  338. Chardev *chr = qemu_chr_find(opts->chardev);
  339. if (chr == NULL) {
  340. error_setg(errp, "chardev \"%s\" not found", opts->chardev);
  341. return NULL;
  342. }
  343. if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE)) {
  344. error_setg(errp, "chardev \"%s\" is not reconnectable",
  345. opts->chardev);
  346. return NULL;
  347. }
  348. if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_FD_PASS)) {
  349. error_setg(errp, "chardev \"%s\" does not support FD passing",
  350. opts->chardev);
  351. return NULL;
  352. }
  353. return chr;
  354. }
  355. int net_init_vhost_user(const Netdev *netdev, const char *name,
  356. NetClientState *peer, Error **errp)
  357. {
  358. int queues;
  359. const NetdevVhostUserOptions *vhost_user_opts;
  360. Chardev *chr;
  361. assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
  362. vhost_user_opts = &netdev->u.vhost_user;
  363. chr = net_vhost_claim_chardev(vhost_user_opts, errp);
  364. if (!chr) {
  365. return -1;
  366. }
  367. queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
  368. if (queues < 1 || queues > MAX_QUEUE_NUM) {
  369. error_setg(errp,
  370. "vhost-user number of queues must be in range [1, %d]",
  371. MAX_QUEUE_NUM);
  372. return -1;
  373. }
  374. return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
  375. }