diff options
author | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:56:32 -0700 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:56:32 -0700 |
commit | fa9a236458e9c22e1f64ab7150b1e38cd93330db (patch) | |
tree | 2c9ac47f02b5ff1fa30fb6f36b0d953aa038e65e /net | |
parent | 68396ea9c0fe4f75ce30b1eba2c44c43c13344bb (diff) | |
download | illumos-kvm-cmd-fa9a236458e9c22e1f64ab7150b1e38cd93330db.tar.gz |
HVM-388 Run native kvm with qemu-v0.14.x
Diffstat (limited to 'net')
-rw-r--r-- | net/vnic.c | 316 | ||||
-rw-r--r-- | net/vnic.h | 34 |
2 files changed, 350 insertions, 0 deletions
diff --git a/net/vnic.c b/net/vnic.c new file mode 100644 index 0000000..c6a7e8f --- /dev/null +++ b/net/vnic.c @@ -0,0 +1,316 @@ +/* + * QEMU System Emulator + * Solaris VNIC support + * + * Copyright (c) 2011 Joyent, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <assert.h> +#include <errno.h> +#include <libdlpi.h> +#include <string.h> +#include <stdio.h> +#include <stropts.h> +#include <unistd.h> + +#include <net/if_dl.h> +#include <sys/ethernet.h> +#include <sys/dlpi.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "net/vnic.h" + +#include "config-host.h" +#include "qemu-common.h" +#include "qemu-error.h" +#include "qemu-option.h" +#include "qemu-char.h" + +/* + * XXX We should determine a good way to get this buffer size. 64k feels like + * such an arbitrary number... + */ +#define VNIC_BUFFSIZE 65536 + +typedef struct VNICState { + VLANClientState vns_nc; + int vns_fd; + unsigned int vns_rpoll; + unsigned int vns_wpoll; + uint8_t vns_buf[VNIC_BUFFSIZE]; + uint_t vns_sap; + dlpi_handle_t vns_hdl; +} VNICState; + +static void vnic_update_fd_handler(VNICState *); + +static void +vnic_read_poll(VNICState *vsp, int enable) +{ + vsp->vns_rpoll = enable; + vnic_update_fd_handler(vsp); +} + +static void +vnic_write_poll(VNICState *vsp, int enable) +{ + vsp->vns_wpoll = enable; + vnic_update_fd_handler(vsp); +} + +static void +vnic_poll(VLANClientState *ncp, bool enable) +{ + VNICState *vsp = DO_UPCAST(VNICState, vns_nc, ncp); + vnic_read_poll(vsp, 1); + vnic_write_poll(vsp, 1); +} + +static int +vnic_read_packet(VNICState *vsp, uint8_t *buf, int len) +{ + struct strbuf sbuf; + int flags, ret; + + flags = 0; + sbuf.maxlen = len; + sbuf.buf = (char *)buf; + + do { + ret = getmsg(vsp->vns_fd, NULL, &sbuf, &flags); + } while (ret == -1 && errno == EINTR); + + if (ret == -1 && errno == EAGAIN) { + vnic_write_poll(vsp, 1); + return (0); + } + + if (ret == -1) { + return (-1); + } + + return (sbuf.len); +} + +static int +vnic_write_packet(VNICState *vsp, const uint8_t *buf, int len) +{ + struct strbuf sbuf; + int flags, ret; + + flags = 0; + sbuf.len = len; + sbuf.buf = (char *)buf; + + do { + ret = putmsg(vsp->vns_fd, NULL, &sbuf, flags); + } while (ret == -1 && errno == EINTR); + + if (ret == -1 && errno == EAGAIN) { + vnic_write_poll(vsp, 1); + return (0); + } + + if (ret == -1) + return (-1); + + return (len); +} + +static int +vnic_can_send(void *opaque) +{ + VNICState *vsp = opaque; + return (qemu_can_send_packet(&vsp->vns_nc)); +} + +static void +vnic_send_completed(VLANClientState *nc, ssize_t len) +{ + VNICState *vsp = DO_UPCAST(VNICState, vns_nc, nc); + vnic_read_poll(vsp, 1); +} + +static void +vnic_send(void *opaque) +{ + VNICState *vsp = opaque; + int ret; + + do { + ret = vnic_read_packet(vsp, vsp->vns_buf, + sizeof (vsp->vns_buf)); + if (ret <= 0) + break; + + ret = qemu_send_packet_async(&vsp->vns_nc, vsp->vns_buf, ret, + vnic_send_completed); + + if (ret == 0) + vnic_read_poll(vsp, 0); + + } while (ret > 0 && qemu_can_send_packet(&vsp->vns_nc)); +} + +static void +vnic_writable(void *opaque) +{ + VNICState *vsp = opaque; + vnic_write_poll(vsp, 0); + qemu_flush_queued_packets(&vsp->vns_nc); +} + +static ssize_t +vnic_receive(VLANClientState *ncp, const uint8_t *buf, size_t size) +{ + VNICState *vsp = DO_UPCAST(VNICState, vns_nc, ncp); + + return (vnic_write_packet(vsp, buf, size)); +} + +static void +vnic_cleanup(VLANClientState *ncp) +{ + VNICState *vsp; + + vsp = DO_UPCAST(VNICState, vns_nc, ncp); + + qemu_purge_queued_packets(ncp); + + dlpi_close(vsp->vns_hdl); +} + +static void +vnic_update_fd_handler(VNICState *vsp) +{ + qemu_set_fd_handler2(vsp->vns_fd, + vsp->vns_rpoll ? vnic_can_send : NULL, + vsp->vns_rpoll ? vnic_send : NULL, + vsp->vns_wpoll ? vnic_writable : NULL, + vsp); +} + +static NetClientInfo net_vnic_info = { + .type = NET_CLIENT_TYPE_VNIC, + .size = sizeof (VNICState), + .receive = vnic_receive, + .poll = vnic_poll, + .cleanup = vnic_cleanup +}; + +static int +net_init_kvm(int vfd) +{ + int kfd; + + if ((kfd = open("/dev/kvm", O_RDWR)) < 0) { + error_report("can't open /dev/kvm for vnic: %s\n", + strerror(errno)); + return (-1); + } + + /* XXX We shouldn't be embedding the KVM_NET_QUEUE fd */ + if (ioctl(kfd, 0x2000ae21, vfd) < 0) { + error_report("can't ioctl: %s\n", strerror(errno)); + return (-1); + } + + (void) close(kfd); + + return (0); +} + +int +net_init_vnic(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +{ + int fd, len; + const char *ifname, *mac; + uchar_t *macaddr; + VLANClientState *ncp; + VNICState *vsp; + + if ((ifname = qemu_opt_get(opts, "ifname")) == NULL) { + error_report("missing ifname required for vnic\n"); + return (-1); + } + + if ((mac = qemu_opt_get(opts, "macaddr")) == NULL) { + error_report("missing macaddr required for vnic\n"); + return (-1); + } + + macaddr = _link_aton(mac, &len); + if (macaddr == NULL || len != ETHERADDRL) { + error_report("invalid macaddr for vnic: %s\n", mac); + return (-1); + } + + ncp = qemu_new_net_client(&net_vnic_info, vlan, NULL, "vnic", name); + vsp = DO_UPCAST(VNICState, vns_nc, ncp); + + if (dlpi_open(ifname, &vsp->vns_hdl, DLPI_RAW) != DLPI_SUCCESS) { + error_report("vnic: failed to open interface %s", ifname); + return (-1); + } + + if (dlpi_bind(vsp->vns_hdl, DLPI_ANY_SAP, &vsp->vns_sap) != DLPI_SUCCESS) { + error_report("vnic: failed to bind interface %s", ifname); + return (-1); + } + + assert(len == ETHERADDRL); + if (dlpi_set_physaddr(vsp->vns_hdl, DL_CURR_PHYS_ADDR, macaddr, + ETHERADDRL) != DLPI_SUCCESS) { + error_report("vnic: failed to set mac address\n"); + return (-1); + } + + if (dlpi_promiscon(vsp->vns_hdl, DL_PROMISC_SAP) != DLPI_SUCCESS) { + error_report("vnic: failed to be promiscous with interface %s", + ifname); + return (-1); + } + + fd = dlpi_fd(vsp->vns_hdl); + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + error_report("vnic: failed to set fd on interface %s to " + "non-blocking: %s\n", ifname, strerror(errno)); + return (-1); + } + + vsp->vns_fd = fd; + + snprintf(vsp->vns_nc.info_str, sizeof (vsp->vns_nc.info_str), "ifname=%s", + qemu_opt_get(opts, "ifname")); + + /* XXX This should be ifdef'd around whether or not KVM is present */ +#ifdef CONFIG_KVM + net_init_kvm(fd); +#endif + + /* We have to manually intialize the polling for read */ + vnic_read_poll(vsp, 1); + + return (0); +} diff --git a/net/vnic.h b/net/vnic.h new file mode 100644 index 0000000..1fd6748 --- /dev/null +++ b/net/vnic.h @@ -0,0 +1,34 @@ +/* + * QEMU System Emulator + * Solaris VNIC support + * + * Copyright (c) 2011 Joyent, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_VNIC_H +#define QEMU_NET_VNIC_H + +#include "net.h" +#include "qemu-common.h" + +int net_init_vnic(QemuOpts *opts, Monitor *mon, const char *name, + VLANState *vlan); + +#endif /* QEMU_NET_VNIC_H */ |