Search Linux Wireless

[PATCH 4/4] wmediumd: add vhost-user support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Johannes Berg <johannes.berg@xxxxxxxxx>

Add support for listening on a vhost-user socket, which can
then be used e.g. together with user-mode-linux or qemu/kvm
and hwsim running inside a virtual machine.

It also supports a combination of local (netlink) and vhost-
user, just because we can :-)

---
 wmediumd/config.c   |   4 +-
 wmediumd/wmediumd.c | 201 +++++++++++++++++++++++++++++++++-----------
 wmediumd/wmediumd.h |  23 +++++
 3 files changed, 177 insertions(+), 51 deletions(-)

diff --git a/wmediumd/config.c b/wmediumd/config.c
index 971493780568..c9a6f5ca9ca0 100644
--- a/wmediumd/config.c
+++ b/wmediumd/config.c
@@ -438,7 +438,7 @@ int load_config(struct wmediumd *ctx, const char *file, const char *per_file)
 	w_logf(ctx, LOG_NOTICE, "#_if = %d\n", count_ids);
 
 	/* Fill the mac_addr */
-	ctx->sta_array = malloc(sizeof(struct station *) * count_ids);
+	ctx->sta_array = calloc(count_ids, sizeof(struct station *));
 	if (!ctx->sta_array) {
 		w_flogf(ctx, LOG_ERR, stderr, "Out of memory(sta_array)!\n");
 		return -ENOMEM;
@@ -448,7 +448,7 @@ int load_config(struct wmediumd *ctx, const char *file, const char *per_file)
 		const char *str =  config_setting_get_string_elem(ids, i);
 		string_to_mac_address(str, addr);
 
-		station = malloc(sizeof(*station));
+		station = calloc(1, sizeof(*station));
 		if (!station) {
 			w_flogf(ctx, LOG_ERR, stderr, "Out of memory!\n");
 			return -ENOMEM;
diff --git a/wmediumd/wmediumd.c b/wmediumd/wmediumd.c
index 70979fca09d4..8fb0ed49bbb1 100644
--- a/wmediumd/wmediumd.c
+++ b/wmediumd/wmediumd.c
@@ -38,6 +38,7 @@
 #include <usfstl/loop.h>
 #include <usfstl/sched.h>
 #include <usfstl/schedctrl.h>
+#include <usfstl/vhost.h>
 
 #include "wmediumd.h"
 #include "ieee80211.h"
@@ -47,6 +48,12 @@ USFSTL_SCHEDULER(scheduler);
 
 static void wmediumd_deliver_frame(struct usfstl_job *job);
 
+enum {
+	HWSIM_VQ_TX,
+	HWSIM_VQ_RX,
+	HWSIM_NUM_VQS,
+};
+
 static inline int div_round(int a, int b)
 {
 	return (a + b - 1) / b;
@@ -361,6 +368,8 @@ void queue_frame(struct wmediumd *ctx, struct station *station,
 	target += send_time;
 
 	frame->duration = send_time;
+	frame->dest = deststa ? deststa->client : NULL;
+	frame->src = station->client;
 	frame->job.start = target;
 	frame->job.callback = wmediumd_deliver_frame;
 	frame->job.data = ctx;
@@ -369,26 +378,44 @@ void queue_frame(struct wmediumd *ctx, struct station *station,
 	list_add_tail(&frame->list, &queue->frames);
 }
 
+static void wmediumd_send_to_client(struct wmediumd *ctx,
+				    struct client *client,
+				    struct nl_msg *msg)
+{
+	size_t len;
+	int ret;
+
+	switch (client->type) {
+	case CLIENT_NETLINK:
+		ret = nl_send_auto_complete(ctx->sock, msg);
+		if (ret < 0)
+			w_logf(ctx, LOG_ERR, "%s: nl_send_auto failed\n", __func__);
+		break;
+	case CLIENT_VHOST_USER:
+		len = nlmsg_total_size(nlmsg_datalen(nlmsg_hdr(msg)));
+		usfstl_vhost_user_dev_notify(client->dev, HWSIM_VQ_RX,
+					     (void *)nlmsg_hdr(msg), len);
+		break;
+	}
+}
+
 /*
  * Report transmit status to the kernel.
  */
-static int send_tx_info_frame_nl(struct wmediumd *ctx, struct frame *frame)
+static void send_tx_info_frame_nl(struct wmediumd *ctx, struct frame *frame)
 {
-	struct nl_sock *sock = ctx->sock;
 	struct nl_msg *msg;
-	int ret;
 
 	msg = nlmsg_alloc();
 	if (!msg) {
 		w_logf(ctx, LOG_ERR, "Error allocating new message MSG!\n");
-		return -1;
+		return;
 	}
 
 	if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, ctx->family_id,
 			0, NLM_F_REQUEST, HWSIM_CMD_TX_INFO_FRAME,
 			VERSION_NR) == NULL) {
 		w_logf(ctx, LOG_ERR, "%s: genlmsg_put failed\n", __func__);
-		ret = -1;
 		goto out;
 	}
 
@@ -401,45 +428,34 @@ static int send_tx_info_frame_nl(struct wmediumd *ctx, struct frame *frame)
 		    frame->tx_rates) ||
 	    nla_put_u64(msg, HWSIM_ATTR_COOKIE, frame->cookie)) {
 		w_logf(ctx, LOG_ERR, "%s: Failed to fill a payload\n", __func__);
-		ret = -1;
 		goto out;
 	}
 
-	ret = nl_send_auto_complete(sock, msg);
-	if (ret < 0) {
-		w_logf(ctx, LOG_ERR, "%s: nl_send_auto failed\n", __func__);
-		ret = -1;
-		goto out;
-	}
-	ret = 0;
+	wmediumd_send_to_client(ctx, frame->src, msg);
 
 out:
 	nlmsg_free(msg);
-	return ret;
 }
 
 /*
  * Send a data frame to the kernel for reception at a specific radio.
  */
-int send_cloned_frame_msg(struct wmediumd *ctx, struct station *dst,
-			  u8 *data, int data_len, int rate_idx, int signal,
-			  int freq)
+static void send_cloned_frame_msg(struct wmediumd *ctx, struct station *dst,
+				  u8 *data, int data_len, int rate_idx,
+				  int signal, int freq)
 {
 	struct nl_msg *msg;
-	struct nl_sock *sock = ctx->sock;
-	int ret;
 
 	msg = nlmsg_alloc();
 	if (!msg) {
 		w_logf(ctx, LOG_ERR, "Error allocating new message MSG!\n");
-		return -1;
+		return;
 	}
 
 	if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, ctx->family_id,
 			0, NLM_F_REQUEST, HWSIM_CMD_FRAME,
 			VERSION_NR) == NULL) {
 		w_logf(ctx, LOG_ERR, "%s: genlmsg_put failed\n", __func__);
-		ret = -1;
 		goto out;
 	}
 
@@ -450,24 +466,23 @@ int send_cloned_frame_msg(struct wmediumd *ctx, struct station *dst,
 	    nla_put_u32(msg, HWSIM_ATTR_FREQ, freq) ||
 	    nla_put_u32(msg, HWSIM_ATTR_SIGNAL, -50)) {
 		w_logf(ctx, LOG_ERR, "%s: Failed to fill a payload\n", __func__);
-		ret = -1;
 		goto out;
 	}
 
 	w_logf(ctx, LOG_DEBUG, "cloned msg dest " MAC_FMT " (radio: " MAC_FMT ") len %d\n",
 		   MAC_ARGS(dst->addr), MAC_ARGS(dst->hwaddr), data_len);
 
-	ret = nl_send_auto_complete(sock, msg);
-	if (ret < 0) {
-		w_logf(ctx, LOG_ERR, "%s: nl_send_auto failed\n", __func__);
-		ret = -1;
-		goto out;
+	if (dst->client) {
+		wmediumd_send_to_client(ctx, dst->client, msg);
+	} else {
+		struct client *client;
+
+		list_for_each_entry(client, &ctx->clients, list)
+			wmediumd_send_to_client(ctx, client, msg);
 	}
-	ret = 0;
 
 out:
 	nlmsg_free(msg);
-	return ret;
 }
 
 void wmediumd_deliver_frame(struct usfstl_job *job)
@@ -587,9 +602,10 @@ int nl_err_cb(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg)
  * Handle events from the kernel.  Process CMD_FRAME events and queue them
  * for later delivery with the scheduler.
  */
-static int process_messages_cb(struct nl_msg *msg, void *arg)
+static void _process_messages(struct nl_msg *msg,
+			      struct wmediumd *ctx,
+			      struct client *client)
 {
-	struct wmediumd *ctx = arg;
 	struct nlattr *attrs[HWSIM_ATTR_MAX+1];
 	/* netlink header */
 	struct nlmsghdr *nlh = nlmsg_hdr(msg);
@@ -627,18 +643,20 @@ static int process_messages_cb(struct nl_msg *msg, void *arg)
 			src = hdr->addr2;
 
 			if (data_len < 6 + 6 + 4)
-				goto out;
+				return;
 
 			sender = get_station_by_addr(ctx, src);
 			if (!sender) {
 				w_flogf(ctx, LOG_ERR, stderr, "Unable to find sender station " MAC_FMT "\n", MAC_ARGS(src));
-				goto out;
+				return;
 			}
 			memcpy(sender->hwaddr, hwaddr, ETH_ALEN);
+			if (!sender->client)
+				sender->client = client;
 
 			frame = calloc(1, sizeof(*frame) + data_len);
 			if (!frame)
-				goto out;
+				return;
 
 			memcpy(frame->data, data, data_len);
 			frame->data_len = data_len;
@@ -653,10 +671,64 @@ static int process_messages_cb(struct nl_msg *msg, void *arg)
 			queue_frame(ctx, sender, frame);
 		}
 	}
-out:
+}
+
+static int process_messages_cb(struct nl_msg *msg, void *arg)
+{
+	struct wmediumd *ctx = arg;
+
+	_process_messages(msg, ctx, &ctx->nl_client);
 	return 0;
 }
 
+static void wmediumd_vu_connected(struct usfstl_vhost_user_dev *dev)
+{
+	struct wmediumd *ctx = dev->server->data;
+	struct client *client;
+
+	client = calloc(1, sizeof(*client));
+	dev->data = client;
+	client->type = CLIENT_VHOST_USER;
+	client->dev = dev;
+	list_add(&client->list, &ctx->clients);
+}
+
+static void wmediumd_vu_handle(struct usfstl_vhost_user_dev *dev,
+			       struct usfstl_vhost_user_buf *buf,
+			       unsigned int vring)
+{
+	struct nl_msg *nlmsg;
+	char data[4096];
+	size_t len;
+
+	len = iov_read(data, sizeof(data), buf->out_sg, buf->n_out_sg);
+
+	if (!nlmsg_ok((const struct nlmsghdr *)data, len))
+		return;
+	nlmsg = nlmsg_convert((struct nlmsghdr *)data);
+	if (!nlmsg)
+		return;
+
+	_process_messages(nlmsg, dev->server->data, dev->data);
+
+	nlmsg_free(nlmsg);
+}
+
+static void wmediumd_vu_disconnected(struct usfstl_vhost_user_dev *dev)
+{
+	struct client *client = dev->data;
+
+	dev->data = NULL;
+	list_del(&client->list);
+	free(client);
+}
+
+static const struct usfstl_vhost_user_ops wmediumd_vu_ops = {
+	.connected = wmediumd_vu_connected,
+	.handle = wmediumd_vu_handle,
+	.disconnected = wmediumd_vu_disconnected,
+};
+
 /*
  * Register with the kernel to start receiving new frames.
  */
@@ -760,6 +832,8 @@ void print_help(int exval)
 	printf("  -c FILE         set input config file\n");
 	printf("  -x FILE         set input PER file\n");
 	printf("  -t socket       set the time control socket\n");
+	printf("  -u socket       expose vhost-user socket, don't use netlink\n");
+	printf("  -n              force netlink use even with vhost-user\n");
 
 	exit(exval);
 }
@@ -772,6 +846,15 @@ int main(int argc, char *argv[])
 	char *per_file = NULL;
 	const char *time_socket = NULL;
 	struct usfstl_sched_ctrl ctrl = {};
+	struct usfstl_vhost_user_server vusrv = {
+		.ops = &wmediumd_vu_ops,
+		.max_queues = HWSIM_NUM_VQS,
+		.input_queues = 1 << HWSIM_VQ_TX,
+		.protocol_features =
+			1ULL << VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS,
+		.data = &ctx,
+	};
+	bool use_netlink, force_netlink = false;
 
 	setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
 
@@ -784,7 +867,7 @@ int main(int argc, char *argv[])
 	unsigned long int parse_log_lvl;
 	char* parse_end_token;
 
-	while ((opt = getopt(argc, argv, "hVc:l:x:t:")) != -1) {
+	while ((opt = getopt(argc, argv, "hVc:l:x:t:u:n")) != -1) {
 		switch (opt) {
 		case 'h':
 			print_help(EXIT_SUCCESS);
@@ -819,6 +902,12 @@ int main(int argc, char *argv[])
 		case 't':
 			time_socket = optarg;
 			break;
+		case 'u':
+			vusrv.socket = optarg;
+			break;
+		case 'n':
+			force_netlink = true;
+			break;
 		case '?':
 			printf("wmediumd: Error - No such option: "
 			       "`%c'\n\n", optopt);
@@ -839,11 +928,15 @@ int main(int argc, char *argv[])
 	w_logf(&ctx, LOG_NOTICE, "Input configuration file: %s\n", config_file);
 
 	INIT_LIST_HEAD(&ctx.stations);
+	INIT_LIST_HEAD(&ctx.clients);
+
 	if (load_config(&ctx, config_file, per_file))
 		return EXIT_FAILURE;
 
+	use_netlink = force_netlink || !vusrv.socket;
+
 	/* init netlink */
-	if (init_netlink(&ctx) < 0)
+	if (use_netlink && init_netlink(&ctx) < 0)
 		return EXIT_FAILURE;
 
 	if (ctx.intf) {
@@ -854,22 +947,32 @@ int main(int argc, char *argv[])
 		usfstl_sched_add_job(&scheduler, &ctx.intf_job);
 	}
 
-	if (time_socket)
+	if (time_socket) {
 		usfstl_sched_ctrl_start(&ctrl, time_socket,
-					1000 /* nsec per usec */,
-					(uint64_t)-1 /* no ID */,
-					&scheduler);
-	else
+				      1000 /* nsec per usec */,
+				      (uint64_t)-1 /* no ID */,
+				      &scheduler);
+		vusrv.scheduler = &scheduler;
+		vusrv.ctrl = &ctrl;
+	} else {
 		usfstl_sched_wallclock_init(&scheduler, 1000);
+	}
+
+	if (vusrv.socket)
+		usfstl_vhost_user_server_start(&vusrv);
+
+	if (use_netlink) {
+		ctx.nl_client.type = CLIENT_NETLINK;
+		list_add(&ctx.nl_client.list, &ctx.clients);
 
-	ctx.nl_loop.handler = sock_event_cb;
-	ctx.nl_loop.data = &ctx;
-	ctx.nl_loop.fd = nl_socket_get_fd(ctx.sock);
-	usfstl_loop_register(&ctx.nl_loop);
+		ctx.nl_loop.handler = sock_event_cb;
+		ctx.nl_loop.data = &ctx;
+		ctx.nl_loop.fd = nl_socket_get_fd(ctx.sock);
+		usfstl_loop_register(&ctx.nl_loop);
 
-	/* register for new frames */
-	if (send_register_msg(&ctx) == 0) {
-		w_logf(&ctx, LOG_NOTICE, "REGISTER SENT!\n");
+		/* register for new frames */
+		if (send_register_msg(&ctx) == 0)
+			w_logf(&ctx, LOG_NOTICE, "REGISTER SENT!\n");
 	}
 
 	while (1) {
diff --git a/wmediumd/wmediumd.h b/wmediumd/wmediumd.h
index 077c17163afc..d4ce3a1df15d 100644
--- a/wmediumd/wmediumd.h
+++ b/wmediumd/wmediumd.h
@@ -144,6 +144,25 @@ struct station {
 	int tx_power;			/* transmission power [dBm] */
 	struct wqueue queues[IEEE80211_NUM_ACS];
 	struct list_head list;
+	struct client *client;
+};
+
+enum client_type {
+	CLIENT_NETLINK,
+	CLIENT_VHOST_USER,
+};
+
+struct client {
+	struct list_head list;
+	enum client_type type;
+
+	/*
+	 * There's no additional data for the netlink client, we
+	 * just have it as such for the link from struct station.
+	 */
+
+	/* for vhost-user */
+	struct usfstl_vhost_user_dev *dev;
 };
 
 struct wmediumd {
@@ -152,6 +171,9 @@ struct wmediumd {
 	struct nl_sock *sock;
 	struct usfstl_loop_entry nl_loop;
 
+	struct list_head clients;
+	struct client nl_client;
+
 	int num_stas;
 	struct list_head stations;
 	struct station **sta_array;
@@ -188,6 +210,7 @@ struct hwsim_tx_rate {
 struct frame {
 	struct list_head list;		/* frame queue list */
 	struct usfstl_job job;
+	struct client *src, *dest;
 	bool acked;
 	u64 cookie;
 	u32 freq;
-- 
2.24.1




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux