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