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