]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
kvm tools: Support multiple net devices
authorSasha Levin <levinsasha928@gmail.com>
Wed, 28 Sep 2011 05:45:55 +0000 (08:45 +0300)
committerPekka Enberg <penberg@kernel.org>
Wed, 28 Sep 2011 14:13:48 +0000 (17:13 +0300)
This patch adds support for multiple network devices. The command line syntax
changes to the following:

--network/-n [mode=tap/user/none][,guest_ip=ip][,host_ip=
ip][,guest_mac=mac][,script=file]

Each of the parameters is optional, and the config defaults to a TAP based
networking with a sequential MAC.

Acked-by: Asias He <asias.hejun@gmail.com>
Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
tools/kvm/builtin-run.c
tools/kvm/include/kvm/virtio-net.h
tools/kvm/virtio/net.c

index 28dc95aef54c3644ae57d8fae77aba26dfa31846..a88503fab964dfb4d04765cb29cec1c3be619a1d 100644 (file)
@@ -65,6 +65,7 @@ __thread struct kvm_cpu *current_kvm_cpu;
 
 static u64 ram_size;
 static u8  image_count;
+static u8 num_net_devices;
 static bool virtio_rng;
 static const char *kernel_cmdline;
 static const char *kernel_filename;
@@ -80,6 +81,7 @@ static const char *guest_mac;
 static const char *host_mac;
 static const char *script;
 static const char *guest_name;
+static struct virtio_net_params *net_params;
 static bool single_step;
 static bool readonly_image[MAX_DISK_IMAGES];
 static bool vnc;
@@ -87,6 +89,7 @@ static bool sdl;
 static bool balloon;
 static bool using_rootfs;
 static bool custom_rootfs;
+static bool no_net;
 extern bool ioport_debug;
 extern int  active_console;
 extern int  debug_iodelay;
@@ -182,6 +185,90 @@ static int tty_parser(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static inline void str_to_mac(const char *str, char *mac)
+{
+       sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+               mac, mac+1, mac+2, mac+3, mac+4, mac+5);
+}
+static int set_net_param(struct virtio_net_params *p, const char *param,
+                               const char *val)
+{
+       if (strcmp(param, "guest_mac") == 0) {
+               str_to_mac(val, p->guest_mac);
+       } else if (strcmp(param, "mode") == 0) {
+               if (!strncmp(val, "user", 4)) {
+                       int i;
+
+                       for (i = 0; i < num_net_devices; i++)
+                               if (net_params[i].mode == NET_MODE_USER)
+                                       die("Only one usermode network device allowed at a time");
+                       p->mode = NET_MODE_USER;
+               } else if (!strncmp(val, "tap", 3)) {
+                       p->mode = NET_MODE_TAP;
+               } else if (!strncmp(val, "none", 4)) {
+                       no_net = 1;
+                       return -1;
+               } else
+                       die("Unkown network mode %s, please use user, tap or none", network);
+       } else if (strcmp(param, "script") == 0) {
+               p->script = strdup(val);
+       } else if (strcmp(param, "guest_ip") == 0) {
+               p->guest_ip = strdup(val);
+       } else if (strcmp(param, "host_ip") == 0) {
+               p->host_ip = strdup(val);
+       }
+
+       return 0;
+}
+
+static int netdev_parser(const struct option *opt, const char *arg, int unset)
+{
+       struct virtio_net_params p;
+       char *buf = NULL, *cmd = NULL, *cur = NULL;
+       bool on_cmd = true;
+
+       if (arg) {
+               buf = strdup(arg);
+               if (buf == NULL)
+                       die("Failed allocating new net buffer");
+               cur = strtok(buf, ",=");
+       }
+
+       p = (struct virtio_net_params) {
+               .guest_ip       = DEFAULT_GUEST_ADDR,
+               .host_ip        = DEFAULT_HOST_ADDR,
+               .script         = DEFAULT_SCRIPT,
+               .mode           = NET_MODE_TAP,
+       };
+
+       str_to_mac(DEFAULT_GUEST_MAC, p.guest_mac);
+       p.guest_mac[5] += num_net_devices;
+
+       while (cur) {
+               if (on_cmd) {
+                       cmd = cur;
+               } else {
+                       if (set_net_param(&p, cmd, cur) < 0)
+                               goto done;
+               }
+               on_cmd = !on_cmd;
+
+               cur = strtok(NULL, ",=");
+       };
+
+       num_net_devices++;
+
+       net_params = realloc(net_params, num_net_devices * sizeof(*net_params));
+       if (net_params == NULL)
+               die("Failed adding new network device");
+
+       net_params[num_net_devices - 1] = p;
+
+done:
+       free(buf);
+       return 0;
+}
+
 static int shmem_parser(const struct option *opt, const char *arg, int unset)
 {
        const u64 default_size = SHMEM_DEFAULT_SIZE;
@@ -339,18 +426,9 @@ static const struct option options[] = {
                        "Kernel command line arguments"),
 
        OPT_GROUP("Networking options:"),
-       OPT_STRING('n', "network", &network, "user, tap, none",
-                       "Network to use"),
-       OPT_STRING('\0', "host-ip", &host_ip, "a.b.c.d",
-                       "Assign this address to the host side networking"),
-       OPT_STRING('\0', "guest-ip", &guest_ip, "a.b.c.d",
-                       "Assign this address to the guest side networking"),
-       OPT_STRING('\0', "host-mac", &host_mac, "aa:bb:cc:dd:ee:ff",
-                       "Assign this address to the host side NIC"),
-       OPT_STRING('\0', "guest-mac", &guest_mac, "aa:bb:cc:dd:ee:ff",
-                       "Assign this address to the guest side NIC"),
-       OPT_STRING('\0', "tapscript", &script, "Script path",
-                        "Assign a script to process created tap device"),
+       OPT_CALLBACK_DEFAULT('n', "network", NULL, "network params",
+                    "Create a new guest NIC",
+                    netdev_parser, NULL),
 
        OPT_GROUP("BIOS options:"),
        OPT_INTEGER('\0', "vidmode", &vidmode,
@@ -615,7 +693,6 @@ void kvm_run_help(void)
 
 int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 {
-       struct virtio_net_parameters net_params;
        static char real_cmdline[2048], default_name[20];
        struct framebuffer *fb = NULL;
        unsigned int nr_online_cpus;
@@ -823,32 +900,24 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 
        virtio_9p__init(kvm);
 
-       if (strncmp(network, "none", 4)) {
-               net_params.guest_ip = guest_ip;
-               net_params.host_ip = host_ip;
-               net_params.kvm = kvm;
-               net_params.script = script;
-               sscanf(guest_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
-                       net_params.guest_mac,
-                       net_params.guest_mac+1,
-                       net_params.guest_mac+2,
-                       net_params.guest_mac+3,
-                       net_params.guest_mac+4,
-                       net_params.guest_mac+5);
-               sscanf(host_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
-                       net_params.host_mac,
-                       net_params.host_mac+1,
-                       net_params.host_mac+2,
-                       net_params.host_mac+3,
-                       net_params.host_mac+4,
-                       net_params.host_mac+5);
-
-               if (!strncmp(network, "user", 4))
-                       net_params.mode = NET_MODE_USER;
-               else if (!strncmp(network, "tap", 3))
-                       net_params.mode = NET_MODE_TAP;
-               else
-                       die("Unkown network mode %s, please use -network user, tap, none", network);
+       for (i = 0; i < num_net_devices; i++) {
+               net_params[i].kvm = kvm;
+               virtio_net__init(&net_params[i]);
+       }
+
+       if (num_net_devices == 0 && no_net == 0) {
+               struct virtio_net_params net_params;
+
+               net_params = (struct virtio_net_params) {
+                       .guest_ip       = guest_ip,
+                       .host_ip        = host_ip,
+                       .kvm            = kvm,
+                       .script         = script,
+                       .mode           = NET_MODE_USER,
+               };
+               str_to_mac(guest_mac, net_params.guest_mac);
+               str_to_mac(host_mac, net_params.host_mac);
+
                virtio_net__init(&net_params);
        }
 
index c30deb8cdac4188c75c55ee10e28c23cfda0830c..58ae162b2a4092e4260ccc74d0c4842c0e87471c 100644 (file)
@@ -3,7 +3,7 @@
 
 struct kvm;
 
-struct virtio_net_parameters {
+struct virtio_net_params {
        const char *guest_ip;
        const char *host_ip;
        const char *script;
@@ -13,7 +13,7 @@ struct virtio_net_parameters {
        int mode;
 };
 
-void virtio_net__init(const struct virtio_net_parameters *params);
+void virtio_net__init(const struct virtio_net_params *params);
 
 #define NET_MODE_USER  0
 #define NET_MODE_TAP   1
index 4700483b24ceaa6e469cd7aace6c10f2d16a3d88..b55725df9356b3554dd697a60e71d04721bed119 100644 (file)
@@ -43,6 +43,7 @@ struct net_dev_operations {
 struct net_dev {
        pthread_mutex_t                 mutex;
        struct virtio_pci               vpci;
+       struct list_head                list;
 
        struct virt_queue               vqs[VIRTIO_NET_NUM_QUEUES];
        struct virtio_net_config        config;
@@ -64,48 +65,41 @@ struct net_dev {
 
        struct uip_info                 info;
        struct net_dev_operations       *ops;
+       struct kvm                      *kvm;
 };
 
-static struct net_dev ndev = {
-       .mutex  = PTHREAD_MUTEX_INITIALIZER,
-
-       .config = {
-               .status                 = VIRTIO_NET_S_LINK_UP,
-       },
-       .info = {
-               .buf_nr                 = 20,
-       }
-};
+static LIST_HEAD(ndevs);
 
 static void *virtio_net_rx_thread(void *p)
 {
        struct iovec iov[VIRTIO_NET_QUEUE_SIZE];
        struct virt_queue *vq;
        struct kvm *kvm;
+       struct net_dev *ndev = p;
        u16 out, in;
        u16 head;
        int len;
 
-       kvm     = p;
-       vq      = &ndev.vqs[VIRTIO_NET_RX_QUEUE];
+       kvm     = ndev->kvm;
+       vq      = &ndev->vqs[VIRTIO_NET_RX_QUEUE];
 
        while (1) {
 
-               mutex_lock(&ndev.io_rx_lock);
+               mutex_lock(&ndev->io_rx_lock);
                if (!virt_queue__available(vq))
-                       pthread_cond_wait(&ndev.io_rx_cond, &ndev.io_rx_lock);
-               mutex_unlock(&ndev.io_rx_lock);
+                       pthread_cond_wait(&ndev->io_rx_cond, &ndev->io_rx_lock);
+               mutex_unlock(&ndev->io_rx_lock);
 
                while (virt_queue__available(vq)) {
 
                        head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
 
-                       len = ndev.ops->rx(iov, in, &ndev);
+                       len = ndev->ops->rx(iov, in, ndev);
 
                        virt_queue__set_used_elem(vq, head, len);
 
                        /* We should interrupt guest right now, otherwise latency is huge. */
-                       virtio_pci__signal_vq(kvm, &ndev.vpci, VIRTIO_NET_RX_QUEUE);
+                       virtio_pci__signal_vq(kvm, &ndev->vpci, VIRTIO_NET_RX_QUEUE);
                }
 
        }
@@ -120,29 +114,30 @@ static void *virtio_net_tx_thread(void *p)
        struct iovec iov[VIRTIO_NET_QUEUE_SIZE];
        struct virt_queue *vq;
        struct kvm *kvm;
+       struct net_dev *ndev = p;
        u16 out, in;
        u16 head;
        int len;
 
-       kvm     = p;
-       vq      = &ndev.vqs[VIRTIO_NET_TX_QUEUE];
+       kvm     = ndev->kvm;
+       vq      = &ndev->vqs[VIRTIO_NET_TX_QUEUE];
 
        while (1) {
-               mutex_lock(&ndev.io_tx_lock);
+               mutex_lock(&ndev->io_tx_lock);
                if (!virt_queue__available(vq))
-                       pthread_cond_wait(&ndev.io_tx_cond, &ndev.io_tx_lock);
-               mutex_unlock(&ndev.io_tx_lock);
+                       pthread_cond_wait(&ndev->io_tx_cond, &ndev->io_tx_lock);
+               mutex_unlock(&ndev->io_tx_lock);
 
                while (virt_queue__available(vq)) {
 
                        head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
 
-                       len = ndev.ops->tx(iov, out, &ndev);
+                       len = ndev->ops->tx(iov, out, ndev);
 
                        virt_queue__set_used_elem(vq, head, len);
                }
 
-               virtio_pci__signal_vq(kvm, &ndev.vpci, VIRTIO_NET_TX_QUEUE);
+               virtio_pci__signal_vq(kvm, &ndev->vpci, VIRTIO_NET_TX_QUEUE);
        }
 
        pthread_exit(NULL);
@@ -151,58 +146,58 @@ static void *virtio_net_tx_thread(void *p)
 
 }
 
-static void virtio_net_handle_callback(struct kvm *kvm, u16 queue_index)
+static void virtio_net_handle_callback(struct kvm *kvm, struct net_dev *ndev, int queue)
 {
-       switch (queue_index) {
+       switch (queue) {
        case VIRTIO_NET_TX_QUEUE:
-               mutex_lock(&ndev.io_tx_lock);
-               pthread_cond_signal(&ndev.io_tx_cond);
-               mutex_unlock(&ndev.io_tx_lock);
+               mutex_lock(&ndev->io_tx_lock);
+               pthread_cond_signal(&ndev->io_tx_cond);
+               mutex_unlock(&ndev->io_tx_lock);
                break;
        case VIRTIO_NET_RX_QUEUE:
-               mutex_lock(&ndev.io_rx_lock);
-               pthread_cond_signal(&ndev.io_rx_cond);
-               mutex_unlock(&ndev.io_rx_lock);
+               mutex_lock(&ndev->io_rx_lock);
+               pthread_cond_signal(&ndev->io_rx_cond);
+               mutex_unlock(&ndev->io_rx_lock);
                break;
        default:
-               pr_warning("Unknown queue index %u", queue_index);
+               pr_warning("Unknown queue index %u", queue);
        }
 }
 
-static bool virtio_net__tap_init(const struct virtio_net_parameters *params)
+static bool virtio_net__tap_init(const struct virtio_net_params *params,
+                                       struct net_dev *ndev)
 {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        int pid, status, offload, hdr_len;
        struct sockaddr_in sin = {0};
        struct ifreq ifr;
 
-       ndev.tap_fd = open("/dev/net/tun", O_RDWR);
-       if (ndev.tap_fd < 0) {
+       ndev->tap_fd = open("/dev/net/tun", O_RDWR);
+       if (ndev->tap_fd < 0) {
                pr_warning("Unable to open /dev/net/tun");
                goto fail;
        }
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
-       if (ioctl(ndev.tap_fd, TUNSETIFF, &ifr) < 0) {
+       if (ioctl(ndev->tap_fd, TUNSETIFF, &ifr) < 0) {
                pr_warning("Config tap device error. Are you root?");
                goto fail;
        }
 
-       strncpy(ndev.tap_name, ifr.ifr_name, sizeof(ndev.tap_name));
+       strncpy(ndev->tap_name, ifr.ifr_name, sizeof(ndev->tap_name));
 
-       if (ioctl(ndev.tap_fd, TUNSETNOCSUM, 1) < 0) {
+       if (ioctl(ndev->tap_fd, TUNSETNOCSUM, 1) < 0) {
                pr_warning("Config tap device TUNSETNOCSUM error");
                goto fail;
        }
 
        hdr_len = sizeof(struct virtio_net_hdr);
-       if (ioctl(ndev.tap_fd, TUNSETVNETHDRSZ, &hdr_len) < 0) {
+       if (ioctl(ndev->tap_fd, TUNSETVNETHDRSZ, &hdr_len) < 0)
                pr_warning("Config tap device TUNSETVNETHDRSZ error");
-       }
 
        offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_UFO;
-       if (ioctl(ndev.tap_fd, TUNSETOFFLOAD, offload) < 0) {
+       if (ioctl(ndev->tap_fd, TUNSETOFFLOAD, offload) < 0) {
                pr_warning("Config tap device TUNSETOFFLOAD error");
                goto fail;
        }
@@ -210,7 +205,7 @@ static bool virtio_net__tap_init(const struct virtio_net_parameters *params)
        if (strcmp(params->script, "none")) {
                pid = fork();
                if (pid == 0) {
-                       execl(params->script, params->script, ndev.tap_name, NULL);
+                       execl(params->script, params->script, ndev->tap_name, NULL);
                        _exit(1);
                } else {
                        waitpid(pid, &status, 0);
@@ -221,7 +216,7 @@ static bool virtio_net__tap_init(const struct virtio_net_parameters *params)
                }
        } else {
                memset(&ifr, 0, sizeof(ifr));
-               strncpy(ifr.ifr_name, ndev.tap_name, sizeof(ndev.tap_name));
+               strncpy(ifr.ifr_name, ndev->tap_name, sizeof(ndev->tap_name));
                sin.sin_addr.s_addr = inet_addr(params->host_ip);
                memcpy(&(ifr.ifr_addr), &sin, sizeof(ifr.ifr_addr));
                ifr.ifr_addr.sa_family = AF_INET;
@@ -232,7 +227,7 @@ static bool virtio_net__tap_init(const struct virtio_net_parameters *params)
        }
 
        memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, ndev.tap_name, sizeof(ndev.tap_name));
+       strncpy(ifr.ifr_name, ndev->tap_name, sizeof(ndev->tap_name));
        ioctl(sock, SIOCGIFFLAGS, &ifr);
        ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
        if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
@@ -245,22 +240,22 @@ static bool virtio_net__tap_init(const struct virtio_net_parameters *params)
 fail:
        if (sock >= 0)
                close(sock);
-       if (ndev.tap_fd >= 0)
-               close(ndev.tap_fd);
+       if (ndev->tap_fd >= 0)
+               close(ndev->tap_fd);
 
        return 0;
 }
 
-static void virtio_net__io_thread_init(struct kvm *kvm)
+static void virtio_net__io_thread_init(struct kvm *kvm, struct net_dev *ndev)
 {
-       pthread_mutex_init(&ndev.io_rx_lock, NULL);
-       pthread_cond_init(&ndev.io_tx_cond, NULL);
+       pthread_mutex_init(&ndev->io_rx_lock, NULL);
+       pthread_cond_init(&ndev->io_tx_cond, NULL);
 
-       pthread_mutex_init(&ndev.io_rx_lock, NULL);
-       pthread_cond_init(&ndev.io_tx_cond, NULL);
+       pthread_mutex_init(&ndev->io_rx_lock, NULL);
+       pthread_cond_init(&ndev->io_tx_cond, NULL);
 
-       pthread_create(&ndev.io_rx_thread, NULL, virtio_net_rx_thread, (void *)kvm);
-       pthread_create(&ndev.io_tx_thread, NULL, virtio_net_tx_thread, (void *)kvm);
+       pthread_create(&ndev->io_rx_thread, NULL, virtio_net_rx_thread, ndev);
+       pthread_create(&ndev->io_tx_thread, NULL, virtio_net_tx_thread, ndev);
 }
 
 static inline int tap_ops_tx(struct iovec *iov, u16 out, struct net_dev *ndev)
@@ -345,7 +340,9 @@ static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
 
 static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
 {
-       virtio_net_handle_callback(kvm, vq);
+       struct net_dev *ndev = dev;
+
+       virtio_net_handle_callback(kvm, ndev, vq);
 
        return 0;
 }
@@ -362,30 +359,48 @@ static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
        return VIRTIO_NET_QUEUE_SIZE;
 }
 
-void virtio_net__init(const struct virtio_net_parameters *params)
+void virtio_net__init(const struct virtio_net_params *params)
 {
        int i;
+       struct net_dev *ndev;
+
+       if (!params)
+               return;
+
+       ndev = calloc(1, sizeof(struct net_dev));
+       if (ndev == NULL)
+               die("Failed allocating ndev");
+
+       list_add_tail(&ndev->list, &ndevs);
+
+       ndev->kvm = params->kvm;
+
+       mutex_init(&ndev->mutex);
+       ndev->config.status = VIRTIO_NET_S_LINK_UP;
 
        for (i = 0 ; i < 6 ; i++) {
-               ndev.config.mac[i]              = params->guest_mac[i];
-               ndev.info.guest_mac.addr[i]     = params->guest_mac[i];
-               ndev.info.host_mac.addr[i]      = params->host_mac[i];
+               ndev->config.mac[i]             = params->guest_mac[i];
+               ndev->info.guest_mac.addr[i]    = params->guest_mac[i];
+               ndev->info.host_mac.addr[i]     = params->host_mac[i];
        }
 
-       ndev.mode = params->mode;
-       if (ndev.mode == NET_MODE_TAP) {
-               virtio_net__tap_init(params);
-               ndev.ops = &tap_ops;
+       ndev->mode = params->mode;
+       if (ndev->mode == NET_MODE_TAP) {
+               if (!virtio_net__tap_init(params, ndev))
+                       die_perror("You have requested a TAP device, but creation of one has"
+                                       "failed because:");
+               ndev->ops = &tap_ops;
        } else {
-               ndev.info.host_ip               = ntohl(inet_addr(params->host_ip));
-               ndev.info.guest_ip              = ntohl(inet_addr(params->guest_ip));
-               ndev.info.guest_netmask         = ntohl(inet_addr("255.255.255.0"));
-               uip_init(&ndev.info);
-               ndev.ops = &uip_ops;
+               ndev->info.host_ip              = ntohl(inet_addr(params->host_ip));
+               ndev->info.guest_ip             = ntohl(inet_addr(params->guest_ip));
+               ndev->info.guest_netmask        = ntohl(inet_addr("255.255.255.0"));
+               ndev->info.buf_nr               = 20,
+               uip_init(&ndev->info);
+               ndev->ops = &uip_ops;
        }
 
-       virtio_pci__init(kvm, &ndev.vpci, &ndev, PCI_DEVICE_ID_VIRTIO_NET, VIRTIO_ID_NET);
-       ndev.vpci.ops = (struct virtio_pci_ops) {
+       virtio_pci__init(kvm, &ndev->vpci, ndev, PCI_DEVICE_ID_VIRTIO_NET, VIRTIO_ID_NET);
+       ndev->vpci.ops = (struct virtio_pci_ops) {
                .set_config             = set_config,
                .get_config             = get_config,
                .get_host_features      = get_host_features,
@@ -396,9 +411,9 @@ void virtio_net__init(const struct virtio_net_parameters *params)
                .get_size_vq            = get_size_vq,
        };
 
-       virtio_net__io_thread_init(params->kvm);
+       virtio_net__io_thread_init(params->kvm, ndev);
 
-       ndev.compat_id = compat__add_message("virtio-net device was not detected",
+       ndev->compat_id = compat__add_message("virtio-net device was not detected",
                                                "While you have requested a virtio-net device, "
                                                "the guest kernel didn't seem to detect it.\n"
                                                "Please make sure that the kernel was compiled "