public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] kvm tools: Support multiple net devices
@ 2011-09-24  9:17 Sasha Levin
  2011-09-25  2:49 ` Asias He
  0 siblings, 1 reply; 2+ messages in thread
From: Sasha Levin @ 2011-09-24  9:17 UTC (permalink / raw)
  To: penberg; +Cc: kvm, mingo, asias.hejun, gorcunov, Sasha Levin

This patch adds support for multiple network devices. The command line syntax
changes to the following:

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

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

Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
---
 tools/kvm/builtin-run.c |  118 ++++++++++++++++++++++++++++++------
 tools/kvm/virtio/net.c  |  150 +++++++++++++++++++++++++++--------------------
 2 files changed, 185 insertions(+), 83 deletions(-)

diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
index 28dc95a..6e5ae5f 100644
--- a/tools/kvm/builtin-run.c
+++ b/tools/kvm/builtin-run.c
@@ -87,9 +87,12 @@ 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;
+struct virtio_net_parameters *net_params;
+int num_net_devices;
 
 bool do_debug_print = false;
 
@@ -182,6 +185,89 @@ static int tty_parser(const struct option *opt, const char *arg, int unset)
 	return 0;
 }
 
+static int set_net_param(struct virtio_net_parameters *p, const char *param,
+				const char *val)
+{
+	if (strcmp(param, "guest_mac") == 0) {
+		sscanf(val, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+			p->guest_mac,
+			p->guest_mac+1,
+			p->guest_mac+2,
+			p->guest_mac+3,
+			p->guest_mac+4,
+			p->guest_mac+5);
+	} 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 = val;
+	} else if (strcmp(param, "guest_ip") == 0) {
+		p->guest_ip = val;
+	} else if (strcmp(param, "host_ip") == 0) {
+		p->host_ip = val;
+	}
+
+	return 0;
+}
+
+static int netdev_parser(const struct option *opt, const char *arg, int unset)
+{
+	struct virtio_net_parameters p;
+	char *buf, *cmd = NULL, *cur = NULL;
+	bool on_cmd = true;
+	int i;
+
+	if (arg) {
+		buf = strdup(arg);
+		if (buf == NULL)
+			die("Failed allocating new net buffer");
+		cur = strtok(buf, ",=");
+	}
+
+	p.guest_ip = DEFAULT_GUEST_ADDR;
+	p.host_ip = DEFAULT_HOST_ADDR;
+	p.script = DEFAULT_SCRIPT;
+	p.mode = NET_MODE_TAP;
+	p.guest_mac[0] = 0x02;
+	for (i = 1; i < 6; i++)
+		p.guest_mac[i] = rand();
+
+	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:
+	return 0;
+}
+
 static int shmem_parser(const struct option *opt, const char *arg, int unset)
 {
 	const u64 default_size = SHMEM_DEFAULT_SIZE;
@@ -339,18 +425,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 +692,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,11 +899,19 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 
 	virtio_9p__init(kvm);
 
-	if (strncmp(network, "none", 4)) {
+	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_parameters net_params;
+
 		net_params.guest_ip = guest_ip;
 		net_params.host_ip = host_ip;
 		net_params.kvm = kvm;
 		net_params.script = script;
+		net_params.mode = NET_MODE_USER;
 		sscanf(guest_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
 			net_params.guest_mac,
 			net_params.guest_mac+1,
@@ -843,12 +927,6 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 			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);
 		virtio_net__init(&net_params);
 	}
 
diff --git a/tools/kvm/virtio/net.c b/tools/kvm/virtio/net.c
index 4700483..26fed2b 100644
--- a/tools/kvm/virtio/net.c
+++ b/tools/kvm/virtio/net.c
@@ -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,9 +65,12 @@ struct net_dev {
 
 	struct uip_info			info;
 	struct net_dev_operations	*ops;
+	struct kvm			*kvm;
 };
 
-static struct net_dev ndev = {
+static LIST_HEAD(ndevs);
+
+/*static struct net_dev ndev = {
 	.mutex	= PTHREAD_MUTEX_INITIALIZER,
 
 	.config = {
@@ -75,37 +79,38 @@ static struct net_dev ndev = {
 	.info = {
 		.buf_nr			= 20,
 	}
-};
+};*/
 
 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 +125,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 +157,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_parameters *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 +216,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 +227,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 +238,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 +251,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 +351,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;
 }
@@ -365,27 +373,43 @@ static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
 void virtio_net__init(const struct virtio_net_parameters *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) {
+		virtio_net__tap_init(params, ndev);
+		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 +420,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 "
-- 
1.7.6.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] kvm tools: Support multiple net devices
  2011-09-24  9:17 [PATCH] kvm tools: Support multiple net devices Sasha Levin
@ 2011-09-25  2:49 ` Asias He
  0 siblings, 0 replies; 2+ messages in thread
From: Asias He @ 2011-09-25  2:49 UTC (permalink / raw)
  To: Sasha Levin; +Cc: penberg, kvm, mingo, gorcunov

On 09/24/2011 05:17 PM, Sasha Levin wrote:
> This patch adds support for multiple network devices. The command line syntax
> changes to the following:
> 
> 	--network/-n [mode=[tap/user/none]] [guest_ip=[guest ip]] [host_ip=
> [host_ip]] [guest_mac=[guest_mac]] [script=[script]]
> 
> Each of the parameters is optional, and the config defaults to a TAP based
> networking with a random MAC.
> 
> Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> ---
>  tools/kvm/builtin-run.c |  118 ++++++++++++++++++++++++++++++------
>  tools/kvm/virtio/net.c  |  150 +++++++++++++++++++++++++++--------------------
>  2 files changed, 185 insertions(+), 83 deletions(-)
> 
> diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
> index 28dc95a..6e5ae5f 100644
> --- a/tools/kvm/builtin-run.c
> +++ b/tools/kvm/builtin-run.c
> @@ -87,9 +87,12 @@ 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;
> +struct virtio_net_parameters *net_params;
> +int num_net_devices;
>  
>  bool do_debug_print = false;
>  
> @@ -182,6 +185,89 @@ static int tty_parser(const struct option *opt, const char *arg, int unset)
>  	return 0;
>  }
>  
> +static int set_net_param(struct virtio_net_parameters *p, const char *param,
> +				const char *val)
> +{
> +	if (strcmp(param, "guest_mac") == 0) {
> +		sscanf(val, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
> +			p->guest_mac,
> +			p->guest_mac+1,
> +			p->guest_mac+2,
> +			p->guest_mac+3,
> +			p->guest_mac+4,
> +			p->guest_mac+5);

A lot of place are using this long sscanf code, a helper is better.

> +	} 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 = val;
> +	} else if (strcmp(param, "guest_ip") == 0) {
> +		p->guest_ip = val;
> +	} else if (strcmp(param, "host_ip") == 0) {
> +		p->host_ip = val;
> +	}
> +
> +	return 0;
> +}
> +
> +static int netdev_parser(const struct option *opt, const char *arg, int unset)
> +{
> +	struct virtio_net_parameters p;
> +	char *buf, *cmd = NULL, *cur = NULL;
> +	bool on_cmd = true;
> +	int i;
> +
> +	if (arg) {
> +		buf = strdup(arg);
> +		if (buf == NULL)
> +			die("Failed allocating new net buffer");
> +		cur = strtok(buf, ",=");
> +	}
> +
> +	p.guest_ip = DEFAULT_GUEST_ADDR;
> +	p.host_ip = DEFAULT_HOST_ADDR;
> +	p.script = DEFAULT_SCRIPT;
> +	p.mode = NET_MODE_TAP;
> +	p.guest_mac[0] = 0x02;

This block deserve a vertical alignment.

> +	for (i = 1; i < 6; i++)
> +		p.guest_mac[i] = rand();


I don't think a random  guest mac address is the best choice.
This way, the guest is gonna have random eth* instance name.

I prefer:

 default_guest_mac + 0  for guest net device  0
 default_guest_mac + 1  for guest net device  1
 default_guest_mac + 2  for guest net device  2


> +	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:
> +	return 0;
> +}
> +
>  static int shmem_parser(const struct option *opt, const char *arg, int unset)
>  {
>  	const u64 default_size = SHMEM_DEFAULT_SIZE;
> @@ -339,18 +425,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 +692,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,11 +899,19 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
>  
>  	virtio_9p__init(kvm);
>  
> -	if (strncmp(network, "none", 4)) {
> +	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_parameters net_params;
> +
>  		net_params.guest_ip = guest_ip;
>  		net_params.host_ip = host_ip;
>  		net_params.kvm = kvm;
>  		net_params.script = script;
> +		net_params.mode = NET_MODE_USER;

vertical alignment, no?

>  		sscanf(guest_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
>  			net_params.guest_mac,
>  			net_params.guest_mac+1,
> @@ -843,12 +927,6 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
>  			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);
>  		virtio_net__init(&net_params);
>  	}
>  
> diff --git a/tools/kvm/virtio/net.c b/tools/kvm/virtio/net.c
> index 4700483..26fed2b 100644
> --- a/tools/kvm/virtio/net.c
> +++ b/tools/kvm/virtio/net.c
> @@ -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,9 +65,12 @@ struct net_dev {
>  
>  	struct uip_info			info;
>  	struct net_dev_operations	*ops;
> +	struct kvm			*kvm;
>  };
>  
> -static struct net_dev ndev = {
> +static LIST_HEAD(ndevs);
> +
> +/*static struct net_dev ndev = {
>  	.mutex	= PTHREAD_MUTEX_INITIALIZER,
>  
>  	.config = {
> @@ -75,37 +79,38 @@ static struct net_dev ndev = {
>  	.info = {
>  		.buf_nr			= 20,
>  	}
> -};
> +};*/

Please remove this code which is commented out.

>  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 +125,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 +157,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_parameters *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 +216,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 +227,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 +238,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 +251,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 +351,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;
>  }
> @@ -365,27 +373,43 @@ static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
>  void virtio_net__init(const struct virtio_net_parameters *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;

This indentation is wrong.

>  	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) {
> +		virtio_net__tap_init(params, ndev);
> +		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,

Vertical indentation is needed for info.buf_nr as well.

> +		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 +420,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 "

Other changes look good to me.

-- 
Asias He

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2011-09-25  2:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-09-24  9:17 [PATCH] kvm tools: Support multiple net devices Sasha Levin
2011-09-25  2:49 ` Asias He

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox