vhost-user.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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 "sysemu/char.h"
  15. #include "qemu/config-file.h"
  16. #include "qemu/error-report.h"
  17. #include "qmp-commands.h"
  18. #include "trace.h"
  19. typedef struct VhostUserState {
  20. NetClientState nc;
  21. CharDriverState *chr;
  22. VHostNetState *vhost_net;
  23. } VhostUserState;
  24. typedef struct VhostUserChardevProps {
  25. bool is_socket;
  26. bool is_unix;
  27. bool is_server;
  28. } VhostUserChardevProps;
  29. VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
  30. {
  31. VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
  32. assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
  33. return s->vhost_net;
  34. }
  35. static int vhost_user_running(VhostUserState *s)
  36. {
  37. return (s->vhost_net) ? 1 : 0;
  38. }
  39. static void vhost_user_stop(int queues, NetClientState *ncs[])
  40. {
  41. VhostUserState *s;
  42. int i;
  43. for (i = 0; i < queues; i++) {
  44. assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
  45. s = DO_UPCAST(VhostUserState, nc, ncs[i]);
  46. if (!vhost_user_running(s)) {
  47. continue;
  48. }
  49. if (s->vhost_net) {
  50. vhost_net_cleanup(s->vhost_net);
  51. s->vhost_net = NULL;
  52. }
  53. }
  54. }
  55. static int vhost_user_start(int queues, NetClientState *ncs[])
  56. {
  57. VhostNetOptions options;
  58. VhostUserState *s;
  59. int max_queues;
  60. int i;
  61. options.backend_type = VHOST_BACKEND_TYPE_USER;
  62. for (i = 0; i < queues; i++) {
  63. assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
  64. s = DO_UPCAST(VhostUserState, nc, ncs[i]);
  65. if (vhost_user_running(s)) {
  66. continue;
  67. }
  68. options.net_backend = ncs[i];
  69. options.opaque = s->chr;
  70. s->vhost_net = vhost_net_init(&options);
  71. if (!s->vhost_net) {
  72. error_report("failed to init vhost_net for queue %d", i);
  73. goto err;
  74. }
  75. if (i == 0) {
  76. max_queues = vhost_net_get_max_queues(s->vhost_net);
  77. if (queues > max_queues) {
  78. error_report("you are asking more queues than supported: %d",
  79. max_queues);
  80. goto err;
  81. }
  82. }
  83. }
  84. return 0;
  85. err:
  86. vhost_user_stop(i + 1, ncs);
  87. return -1;
  88. }
  89. static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf,
  90. size_t size)
  91. {
  92. /* In case of RARP (message size is 60) notify backup to send a fake RARP.
  93. This fake RARP will be sent by backend only for guest
  94. without GUEST_ANNOUNCE capability.
  95. */
  96. if (size == 60) {
  97. VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
  98. int r;
  99. static int display_rarp_failure = 1;
  100. char mac_addr[6];
  101. /* extract guest mac address from the RARP message */
  102. memcpy(mac_addr, &buf[6], 6);
  103. r = vhost_net_notify_migration_done(s->vhost_net, mac_addr);
  104. if ((r != 0) && (display_rarp_failure)) {
  105. fprintf(stderr,
  106. "Vhost user backend fails to broadcast fake RARP\n");
  107. fflush(stderr);
  108. display_rarp_failure = 0;
  109. }
  110. }
  111. return size;
  112. }
  113. static void vhost_user_cleanup(NetClientState *nc)
  114. {
  115. VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
  116. if (s->vhost_net) {
  117. vhost_net_cleanup(s->vhost_net);
  118. s->vhost_net = NULL;
  119. }
  120. qemu_purge_queued_packets(nc);
  121. }
  122. static bool vhost_user_has_vnet_hdr(NetClientState *nc)
  123. {
  124. assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
  125. return true;
  126. }
  127. static bool vhost_user_has_ufo(NetClientState *nc)
  128. {
  129. assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
  130. return true;
  131. }
  132. static NetClientInfo net_vhost_user_info = {
  133. .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
  134. .size = sizeof(VhostUserState),
  135. .receive = vhost_user_receive,
  136. .cleanup = vhost_user_cleanup,
  137. .has_vnet_hdr = vhost_user_has_vnet_hdr,
  138. .has_ufo = vhost_user_has_ufo,
  139. };
  140. static void net_vhost_user_event(void *opaque, int event)
  141. {
  142. const char *name = opaque;
  143. NetClientState *ncs[MAX_QUEUE_NUM];
  144. VhostUserState *s;
  145. Error *err = NULL;
  146. int queues;
  147. queues = qemu_find_net_clients_except(name, ncs,
  148. NET_CLIENT_OPTIONS_KIND_NIC,
  149. MAX_QUEUE_NUM);
  150. s = DO_UPCAST(VhostUserState, nc, ncs[0]);
  151. trace_vhost_user_event(s->chr->label, event);
  152. switch (event) {
  153. case CHR_EVENT_OPENED:
  154. if (vhost_user_start(queues, ncs) < 0) {
  155. exit(1);
  156. }
  157. qmp_set_link(name, true, &err);
  158. break;
  159. case CHR_EVENT_CLOSED:
  160. qmp_set_link(name, false, &err);
  161. vhost_user_stop(queues, ncs);
  162. break;
  163. }
  164. if (err) {
  165. error_report_err(err);
  166. }
  167. }
  168. static int net_vhost_user_init(NetClientState *peer, const char *device,
  169. const char *name, CharDriverState *chr,
  170. int queues)
  171. {
  172. NetClientState *nc;
  173. VhostUserState *s;
  174. int i;
  175. for (i = 0; i < queues; i++) {
  176. nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
  177. snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
  178. i, chr->label);
  179. nc->queue_index = i;
  180. s = DO_UPCAST(VhostUserState, nc, nc);
  181. s->chr = chr;
  182. }
  183. qemu_chr_add_handlers(chr, NULL, NULL, net_vhost_user_event, (void*)name);
  184. return 0;
  185. }
  186. static int net_vhost_chardev_opts(void *opaque,
  187. const char *name, const char *value,
  188. Error **errp)
  189. {
  190. VhostUserChardevProps *props = opaque;
  191. if (strcmp(name, "backend") == 0 && strcmp(value, "socket") == 0) {
  192. props->is_socket = true;
  193. } else if (strcmp(name, "path") == 0) {
  194. props->is_unix = true;
  195. } else if (strcmp(name, "server") == 0) {
  196. props->is_server = true;
  197. } else {
  198. error_setg(errp,
  199. "vhost-user does not support a chardev with option %s=%s",
  200. name, value);
  201. return -1;
  202. }
  203. return 0;
  204. }
  205. static CharDriverState *net_vhost_parse_chardev(
  206. const NetdevVhostUserOptions *opts, Error **errp)
  207. {
  208. CharDriverState *chr = qemu_chr_find(opts->chardev);
  209. VhostUserChardevProps props;
  210. if (chr == NULL) {
  211. error_setg(errp, "chardev \"%s\" not found", opts->chardev);
  212. return NULL;
  213. }
  214. /* inspect chardev opts */
  215. memset(&props, 0, sizeof(props));
  216. if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, errp)) {
  217. return NULL;
  218. }
  219. if (!props.is_socket || !props.is_unix) {
  220. error_setg(errp, "chardev \"%s\" is not a unix socket",
  221. opts->chardev);
  222. return NULL;
  223. }
  224. qemu_chr_fe_claim_no_fail(chr);
  225. return chr;
  226. }
  227. static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
  228. {
  229. const char *name = opaque;
  230. const char *driver, *netdev;
  231. const char virtio_name[] = "virtio-net-";
  232. driver = qemu_opt_get(opts, "driver");
  233. netdev = qemu_opt_get(opts, "netdev");
  234. if (!driver || !netdev) {
  235. return 0;
  236. }
  237. if (strcmp(netdev, name) == 0 &&
  238. strncmp(driver, virtio_name, strlen(virtio_name)) != 0) {
  239. error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
  240. return -1;
  241. }
  242. return 0;
  243. }
  244. int net_init_vhost_user(const NetClientOptions *opts, const char *name,
  245. NetClientState *peer, Error **errp)
  246. {
  247. int queues;
  248. const NetdevVhostUserOptions *vhost_user_opts;
  249. CharDriverState *chr;
  250. assert(opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
  251. vhost_user_opts = opts->u.vhost_user;
  252. chr = net_vhost_parse_chardev(vhost_user_opts, errp);
  253. if (!chr) {
  254. return -1;
  255. }
  256. /* verify net frontend */
  257. if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
  258. (char *)name, errp)) {
  259. return -1;
  260. }
  261. queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
  262. if (queues < 1) {
  263. error_setg(errp,
  264. "vhost-user number of queues must be bigger than zero");
  265. return -1;
  266. }
  267. return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
  268. }