[dpdk-dev] [PATCH v3 1/3] xen: Add UIO kernel driver

Jan Blunck jblunck at infradead.org
Tue Mar 22 10:55:27 CET 2016


New UIO helper kernel driver for Xen netfront UIO poll mode driver.

Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
Signed-off-by: Jan Blunck <jblunck at infradead.org>
---
 lib/librte_eal/linuxapp/Makefile          |   1 +
 lib/librte_eal/linuxapp/xen_uio/Makefile  |  56 ++
 lib/librte_eal/linuxapp/xen_uio/compat.h  |  47 ++
 lib/librte_eal/linuxapp/xen_uio/xen_uio.c | 954 ++++++++++++++++++++++++++++++
 4 files changed, 1058 insertions(+)
 create mode 100644 lib/librte_eal/linuxapp/xen_uio/Makefile
 create mode 100644 lib/librte_eal/linuxapp/xen_uio/compat.h
 create mode 100644 lib/librte_eal/linuxapp/xen_uio/xen_uio.c

diff --git a/lib/librte_eal/linuxapp/Makefile b/lib/librte_eal/linuxapp/Makefile
index 20d2a91..6b33e87 100644
--- a/lib/librte_eal/linuxapp/Makefile
+++ b/lib/librte_eal/linuxapp/Makefile
@@ -35,5 +35,6 @@ DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal
 DIRS-$(CONFIG_RTE_EAL_IGB_UIO) += igb_uio
 DIRS-$(CONFIG_RTE_KNI_KMOD) += kni
 DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += xen_dom0
+DIRS-$(CONFIG_RTE_LIBRTE_PMD_XEN) += xen_uio
 
 include $(RTE_SDK)/mk/rte.subdir.mk
diff --git a/lib/librte_eal/linuxapp/xen_uio/Makefile b/lib/librte_eal/linuxapp/xen_uio/Makefile
new file mode 100644
index 0000000..936e8bf
--- /dev/null
+++ b/lib/librte_eal/linuxapp/xen_uio/Makefile
@@ -0,0 +1,56 @@
+#   BSD LICENSE
+#
+#   Copyright (c) 2013-2016 Brocade Communications Systems, Inc.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+#
+# module name and path
+#
+MODULE = xen_uio
+MODULE_PATH = drivers/net//xen_uio
+
+#
+# CFLAGS
+#
+MODULE_CFLAGS += -I$(SRCDIR) --param max-inline-insns-single=100
+MODULE_CFLAGS += -I$(RTE_OUTPUT)/include
+MODULE_CFLAGS += -Winline -Wall -Werror
+MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h
+MODULE_CFLAGS += -I$(RTE_SDK)
+
+#
+# all source are stored in SRCS-y
+#
+SRCS-y := xen_uio.c
+
+
+include $(RTE_SDK)/mk/rte.module.mk
diff --git a/lib/librte_eal/linuxapp/xen_uio/compat.h b/lib/librte_eal/linuxapp/xen_uio/compat.h
new file mode 100644
index 0000000..b4f30d9
--- /dev/null
+++ b/lib/librte_eal/linuxapp/xen_uio/compat.h
@@ -0,0 +1,47 @@
+/*
+ * Minimal wrappers to allow compiling xen_uio on older kernels.
+ *
+ * Copyright (c) 2016 Brocade Communications Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _XEN_UIO_COMPAT_H_
+#define _XEN_UIO_COMPAT_H_
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
+#define INVALID_GRANT_HANDLE	(~0U)
+
+static inline int compat_xenbus_grant_ring(struct xenbus_device *dev,
+					   void *vaddr,
+					   unsigned int nr_pages,
+					   grant_ref_t *grefs)
+{
+	int ret;
+
+	ret = xenbus_grant_ring(dev, virt_to_mfn(vaddr));
+
+	if (ret >= 0)
+		*grefs = ret;
+
+	return ret;
+}
+
+#define xenbus_grant_ring(dev, vaddr, nr_pages, grefs) \
+	compat_xenbus_grant_ring(dev, vaddr, nr_pages, grefs)
+
+#endif /* < 4.1.0 */
+
+#endif /* _XEN_UIO_COMPAT_H_ */
diff --git a/lib/librte_eal/linuxapp/xen_uio/xen_uio.c b/lib/librte_eal/linuxapp/xen_uio/xen_uio.c
new file mode 100644
index 0000000..4f35956
--- /dev/null
+++ b/lib/librte_eal/linuxapp/xen_uio/xen_uio.c
@@ -0,0 +1,954 @@
+/*
+ * Virtual network driver for conversing with remote driver backends.
+ *
+ * Copyright (c) 2002-2005, K A Fraser
+ * Copyright (c) 2005, XenSource Ltd
+ * Copyright (c) 2013-2016 Brocade Communications Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/if_ether.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+
+#include <xen/xenbus.h>
+#include <xen/page.h>
+#include <xen/grant_table.h>
+#include <xen/interface/io/netif.h>
+#include <xen/platform_pci.h>
+
+#include <xen/events.h>
+#include <xen/evtchn.h>
+#include <asm/xen/hypervisor.h>
+#include <asm/xen/hypercall.h>
+
+#include <linux/uio_driver.h>
+
+#include "drivers/net/xen/xen_adapter_info.h"
+#include "compat.h"
+
+#define NET_TX_RING_SIZE \
+	__CONST_RING_SIZE(xen_netif_tx, PAGE_SIZE)
+#define NET_RX_RING_SIZE \
+	__CONST_RING_SIZE(xen_netif_rx, PAGE_SIZE)
+
+#define TX_MAX_TARGET \
+	min_t(int, NET_RX_RING_SIZE, 256)
+#define RX_MAX_TARGET \
+	min_t(int, NET_RX_RING_SIZE, 256)
+
+#define RXTX_GREFS (TX_MAX_TARGET + RX_MAX_TARGET)
+
+#define DOMAIN_PROC "xen/domain"
+struct proc_dir_entry *domain_proc;
+char domain_name[9];
+size_t domain_len = sizeof(domain_name);
+static const char * const domains[] = { "native", "pv", "hvm", "unknown" };
+
+struct netfront_info *xennet_alloc_resources(struct xenbus_device *xbdev);
+static void xennet_free_resources(struct xenbus_device *xbdev);
+static int xennet_connect_backend(struct netfront_info *info);
+static void xennet_disconnect_backend(struct netfront_info *info,
+		int deffered_free);
+
+/* some helpers */
+static int __gnttab_version(void)
+{
+	int err;
+	struct gnttab_get_version ggv;
+
+	ggv.dom = DOMID_SELF;
+
+	err = HYPERVISOR_grant_table_op(GNTTABOP_get_version, &ggv, 1);
+	if (err >= 0)
+		return (int)ggv.version;
+
+	return err;
+}
+
+static void xennet_end_access(int ref, void *page)
+{
+	/* This frees the page as a side-effect */
+	if (ref != INVALID_GRANT_HANDLE)
+		gnttab_end_foreign_access(ref, 0, (unsigned long)page);
+}
+
+static int xen_net_read_mac(struct xenbus_device *xbdev, u8 *mac)
+{
+	char *macstr;
+	int ret = 0;
+
+	macstr = xenbus_read(XBT_NIL, xbdev->nodename, "mac", NULL);
+	if (IS_ERR(macstr))
+		return PTR_ERR(macstr);
+
+	pr_info("mac addr: %s\n", macstr);
+
+	if (sscanf(macstr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac[0], &mac[1],
+			&mac[2], &mac[3], &mac[4], &mac[5])  != ETH_ALEN) {
+		pr_warn("can't parse mac address\n");
+		ret = -ENOENT;
+	}
+
+	kfree(macstr);
+	return ret;
+}
+
+struct xen_uio_dev {
+	struct uio_info info;
+};
+
+struct netfront_info {
+	struct xenbus_device *xbdev;
+
+	int tx_ring_ref;
+	struct xen_netif_tx_front_ring tx;
+
+	int rx_ring_ref;
+	struct xen_netif_rx_front_ring rx;
+
+	struct xen_netif_tx_sring *txs;
+	struct xen_netif_rx_sring *rxs;
+
+	grant_ref_t gref_rxtx_head;
+
+	struct xen_uio_dev *xen_udev;
+
+	struct xen_adapter_info *shared_info_page;
+};
+
+static int xennet_uio_init(struct xenbus_device *xbdev,
+		struct netfront_info *info)
+{
+	int err;
+	struct xen_uio_dev *udev;
+
+	udev = kzalloc(sizeof(struct xen_uio_dev), GFP_KERNEL);
+	if (!udev)
+		return -ENOMEM;
+
+	info->xen_udev = udev;
+
+	/* fill uio infos */
+	udev->info.name = "xen_uio";
+	udev->info.version = "0.1";
+	udev->info.irq = UIO_IRQ_NONE;
+	udev->info.irq_flags = 0;
+
+	/*share all working info here*/
+	udev->info.mem[INFO_MAP].name = "xennet info page";
+	udev->info.mem[INFO_MAP].memtype = UIO_MEM_LOGICAL;
+	udev->info.mem[INFO_MAP].addr = (phys_addr_t)info->shared_info_page;
+	udev->info.mem[INFO_MAP].size = PAGE_SIZE;
+
+	udev->info.mem[RX_RING_MAP].name = "xennet front rx ring";
+	udev->info.mem[RX_RING_MAP].memtype = UIO_MEM_LOGICAL;
+	udev->info.mem[RX_RING_MAP].addr = (phys_addr_t)info->rxs;
+	udev->info.mem[RX_RING_MAP].size = PAGE_SIZE;
+
+	udev->info.mem[TX_RING_MAP].name = "xennet front tx ring";
+	udev->info.mem[TX_RING_MAP].memtype = UIO_MEM_LOGICAL;
+	udev->info.mem[TX_RING_MAP].addr = (phys_addr_t)info->txs;
+	udev->info.mem[TX_RING_MAP].size = PAGE_SIZE;
+
+	err = uio_register_device(&xbdev->dev, &info->xen_udev->info);
+	if (err) {
+		pr_err("uio register failed: %d\n", err);
+		kfree(info->xen_udev);
+		info->xen_udev = NULL;
+	} else {
+		pr_info("uio device registered with irq %lx\n",
+				info->xen_udev->info.irq);
+	}
+
+	return err;
+}
+
+
+static void xennet_uio_uninit(struct netfront_info *info)
+{
+	if (info->xen_udev)
+		uio_unregister_device(&info->xen_udev->info);
+	info->xen_udev = NULL;
+}
+
+struct netfront_info *xennet_alloc_resources(struct xenbus_device *xbdev)
+{
+	int ret;
+	uint16_t i;
+	int gref = 0;
+	grant_ref_t gref_rxtx_head;
+
+	struct netfront_info *info =
+		kzalloc(sizeof(struct netfront_info), GFP_KERNEL);
+	if (!info)
+		goto exit;
+
+	info->gref_rxtx_head = INVALID_GRANT_HANDLE;
+	info->xbdev = xbdev;
+
+	/* allocate place for tx ring */
+	info->txs = (struct xen_netif_tx_sring *)get_zeroed_page(
+			GFP_NOIO | __GFP_HIGH);
+	if (!info->txs) {
+		ret = -ENOMEM;
+		xenbus_dev_fatal(xbdev, ret, "allocating tx ring page");
+		goto exit;
+	}
+
+	/* allocate place for rx ring */
+	info->rxs = (struct xen_netif_rx_sring *)get_zeroed_page(
+			GFP_NOIO | __GFP_HIGH);
+	if (!info->rxs) {
+		ret = -ENOMEM;
+		xenbus_dev_fatal(xbdev, ret, "allocating rx ring page");
+		goto exit;
+	}
+
+	/* allocate shared with user page (info page) */
+	info->shared_info_page =
+		(struct xen_adapter_info *)__get_free_page(GFP_KERNEL);
+	if (!info->shared_info_page) {
+		pr_alert("xen_uio can't alloc shared page\n");
+		goto exit;
+	}
+
+	/* just assertion */
+	if (((char *)&info->shared_info_page->rxtx_grefs[RXTX_GREFS - 1])
+			- ((char *)info->shared_info_page) > PAGE_SIZE) {
+		pr_err("ASSERT: no mem for grefs\n");
+		goto exit;
+	}
+
+	/* allocate grefs for every tx ring and rx ring slot */
+	ret = gnttab_alloc_grant_references(RXTX_GREFS, &info->gref_rxtx_head);
+	if (ret < 0) {
+		pr_err("xen_uio can't alloc rx and tx grefs\n");
+		goto exit;
+	}
+
+	/* fill in all grefs*/
+	gref_rxtx_head = info->gref_rxtx_head;
+	info->shared_info_page->rx_grefs_count = RX_MAX_TARGET;
+	info->shared_info_page->tx_grefs_count = TX_MAX_TARGET;
+	info->shared_info_page->rx_evtchn = 0;
+	info->shared_info_page->tx_evtchn = 0;
+
+	/*go through the list and collect put all grefs to array*/
+	for (i = 0; i < (RXTX_GREFS); i++) {
+		gref = gnttab_claim_grant_reference(&gref_rxtx_head);
+		if (gref < 0) {
+			pr_err("not expected end of list\n");
+			goto exit;
+		}
+		info->shared_info_page->rxtx_grefs[i] = (grant_ref_t)gref;
+	}
+
+	/*setup shared_info_page*/
+	info->shared_info_page->rx_ring = &info->rx;
+	info->shared_info_page->tx_ring = &info->tx;
+	/*it's not secure - we need here something else*/
+	info->shared_info_page->info = info;
+
+	info->shared_info_page->is_connected = 0;
+	info->shared_info_page->disconnect_count = 0;
+
+	/* share struct by UIO */
+	ret = xennet_uio_init(xbdev, info);
+	if (ret) {
+		pr_err("xennet_uio_init failed\n");
+		goto exit;
+	}
+
+	return info;
+exit:
+	if (info) {
+		if (info->gref_rxtx_head != INVALID_GRANT_HANDLE)
+			gnttab_free_grant_references(info->gref_rxtx_head);
+		if (info->shared_info_page)
+			free_page((unsigned long)info->shared_info_page);
+		if (info->rxs)
+			free_page((unsigned long)info->rxs);
+		if (info->txs)
+			free_page((unsigned long)info->txs);
+		kfree(info);
+	}
+	return NULL;
+}
+
+void xennet_free_resources(struct xenbus_device *xbdev)
+{
+	struct netfront_info *info = dev_get_drvdata(&xbdev->dev);
+
+	xennet_uio_uninit(info);
+
+	gnttab_free_grant_references(info->gref_rxtx_head);
+
+	free_page((unsigned long)info->shared_info_page);
+	/*can be deferred free- in that case these pointers are NULL*/
+	if (info->rxs)
+		free_page((unsigned long)info->rxs);
+	if (info->txs)
+		free_page((unsigned long)info->txs);
+
+	kfree(info);
+}
+
+static int setup_netfront(struct xenbus_device *xbdev,
+		struct netfront_info *info)
+{
+	unsigned int feature_split_evtchn;
+	unsigned int max_queues;
+	grant_ref_t gref;
+	int err;
+
+	info->tx_ring_ref = INVALID_GRANT_HANDLE;
+	info->rx_ring_ref = INVALID_GRANT_HANDLE;
+	info->rx.sring = NULL;
+	info->tx.sring = NULL;
+
+	/* share otherend_id with user */
+	info->shared_info_page->otherend_id = xbdev->otherend_id;
+
+	err = xenbus_scanf(XBT_NIL, xbdev->otherend,
+			   "multi-queue-max-queues", "%u", &max_queues);
+	if (err < 0)
+		max_queues = 1;
+
+	pr_info("multi-queue-max-queues: %u\n", max_queues);
+
+	err = xenbus_scanf(XBT_NIL, xbdev->otherend,
+			"feature-split-event-channels", "%u",
+			&feature_split_evtchn);
+	if (err < 0)
+		feature_split_evtchn = 0;
+
+	/* read mac */
+	err = xen_net_read_mac(xbdev, info->shared_info_page->mac);
+	if (err) {
+		xenbus_dev_fatal(xbdev, err, "parsing %s/mac",
+				xbdev->nodename);
+		goto fail;
+	}
+
+	/* set up queues */
+	SHARED_RING_INIT(info->txs);
+	FRONT_RING_INIT(&info->tx, info->txs, PAGE_SIZE);
+
+	SHARED_RING_INIT(info->rxs);
+	FRONT_RING_INIT(&info->rx, info->rxs, PAGE_SIZE);
+
+	err = xenbus_grant_ring(info->xbdev, info->txs, 1, &gref);
+	if (err < 0) {
+		pr_err("xenbus_grant_ring for txs failed!\n");
+		goto fail;
+	}
+	info->tx_ring_ref = gref;
+
+	err = xenbus_grant_ring(info->xbdev, info->rxs, 1, &gref);
+	if (err < 0) {
+		pr_err("xenbus_grant_ring for rxs failed!\n");
+		goto fail;
+	}
+	info->rx_ring_ref = gref;
+
+	/* alloc eventchn */
+	pr_info("feature_split_evtchn: %d\n",
+			(int)feature_split_evtchn);
+
+	err = xenbus_alloc_evtchn(xbdev, &info->shared_info_page->tx_evtchn);
+	if (err)
+		goto fail;
+
+	if (feature_split_evtchn) {
+		err = xenbus_alloc_evtchn(xbdev,
+				&info->shared_info_page->rx_evtchn);
+		if (err)
+			goto fail_split;
+	} else {
+		info->shared_info_page->rx_evtchn =
+			info->shared_info_page->tx_evtchn;
+	}
+
+	return 0;
+fail_split:
+	xenbus_free_evtchn(info->xbdev, info->shared_info_page->tx_evtchn);
+fail:
+	pr_err("setup_netfront failed\n");
+	return err;
+}
+
+/* Common code used when first setting up, and when resuming. */
+static int talk_to_netback(struct xenbus_device *xbdev,
+		struct netfront_info *info)
+{
+	const char *message;
+	struct xenbus_transaction xbt;
+	int err;
+
+	/* Create shared ring, alloc event channel. */
+	err = setup_netfront(xbdev, info);
+	if (err)
+		goto out;
+
+again:
+	err = xenbus_transaction_start(&xbt);
+	if (err) {
+		xenbus_dev_fatal(xbdev, err, "starting transaction");
+		goto destroy_ring;
+	}
+
+	if (xenbus_exists(XBT_NIL, xbdev->otherend,
+			  "multi-queue-max-queues")) {
+		/* Write the number of queues */
+		err = xenbus_printf(xbt, xbdev->nodename,
+				    "multi-queue-num-queues", "%u", 1);
+		if (err) {
+			message = "writing multi-queue-num-queues";
+			goto abort_transaction;
+		}
+	}
+
+	err = xenbus_printf(xbt, xbdev->nodename, "tx-ring-ref",
+			"%u", info->tx_ring_ref);
+	if (err) {
+		message = "writing tx ring-ref";
+		goto abort_transaction;
+	}
+	err = xenbus_printf(xbt, xbdev->nodename, "rx-ring-ref",
+			"%u", info->rx_ring_ref);
+	if (err) {
+		message = "writing rx ring-ref";
+		goto abort_transaction;
+	}
+
+	if (info->shared_info_page->tx_evtchn ==
+			info->shared_info_page->rx_evtchn) {
+		err = xenbus_printf(xbt, xbdev->nodename, "event-channel",
+				"%u", info->shared_info_page->tx_evtchn);
+		if (err) {
+			message = "writing event-channel";
+			goto abort_transaction;
+		}
+	} else {
+		err = xenbus_printf(xbt, xbdev->nodename, "event-channel-tx",
+				"%u", info->shared_info_page->tx_evtchn);
+		if (err) {
+			message = "writing event-channel";
+			goto abort_transaction;
+		}
+		err = xenbus_printf(xbt, xbdev->nodename, "event-channel-rx",
+				"%u", info->shared_info_page->rx_evtchn);
+		if (err) {
+			message = "writing event-channel";
+			goto abort_transaction;
+		}
+	}
+
+	err = xenbus_printf(xbt, xbdev->nodename, "request-rx-copy", "%u", 1);
+	if (err) {
+		message = "writing request-rx-copy";
+		goto abort_transaction;
+	}
+
+	err = xenbus_printf(xbt, xbdev->nodename, "feature-rx-notify",
+			"%d", 1);
+	if (err) {
+		message = "writing feature-rx-notify";
+		goto abort_transaction;
+	}
+
+	err = xenbus_printf(xbt, xbdev->nodename, "feature-sg", "%d", 1);
+	if (err) {
+		message = "writing feature-sg";
+		goto abort_transaction;
+	}
+
+	err = xenbus_printf(xbt, xbdev->nodename, "feature-gso-tcpv4",
+			"%d", 0);
+	if (err) {
+		message = "writing feature-gso-tcpv4";
+		goto abort_transaction;
+	}
+
+	err = xenbus_transaction_end(xbt, 0);
+	if (err) {
+		if (err == -EAGAIN)
+			goto again;
+		xenbus_dev_fatal(xbdev, err, "completing transaction");
+		goto destroy_ring;
+	}
+
+	return 0;
+abort_transaction:
+	xenbus_transaction_end(xbt, 1);
+	xenbus_dev_fatal(xbdev, err, "%s", message);
+destroy_ring:
+	xennet_disconnect_backend(info, 1);
+out:
+	pr_err("talk_to_netback failed\n");
+	return err;
+}
+
+static int xennet_connect_backend(struct netfront_info *info)
+{
+	int err;
+	unsigned int feature_rx_copy;
+
+	err = xenbus_scanf(XBT_NIL, info->xbdev->otherend, "feature-rx-copy",
+			"%u", &feature_rx_copy);
+	if (err != 1)
+		feature_rx_copy = 0;
+
+	if (!feature_rx_copy) {
+		pr_info("backend does not support copying receive path\n");
+		return -ENODEV;
+	}
+
+	err = talk_to_netback(info->xbdev, info);
+	if (err)
+		pr_err("talk_to_netback failed!\n");
+
+	info->shared_info_page->is_connected = 1;
+
+	return err;
+}
+
+static void xennet_disconnect_backend(struct netfront_info *info,
+		int deffered_free)
+{
+	xenbus_switch_state(info->xbdev, XenbusStateClosing);
+
+	if (info->shared_info_page->tx_evtchn !=
+			info->shared_info_page->rx_evtchn) {
+		xenbus_free_evtchn(info->xbdev,
+				info->shared_info_page->rx_evtchn);
+	}
+	xenbus_free_evtchn(info->xbdev, info->shared_info_page->tx_evtchn);
+
+	if (deffered_free) {
+		xennet_end_access(info->tx_ring_ref, info->txs);
+		xennet_end_access(info->rx_ring_ref, info->rxs);
+		info->txs = NULL;
+		info->rxs = NULL;
+	} else {
+		xennet_end_access(info->tx_ring_ref, NULL);
+		xennet_end_access(info->rx_ring_ref, NULL);
+	}
+
+	info->tx_ring_ref = INVALID_GRANT_HANDLE;
+	info->rx_ring_ref = INVALID_GRANT_HANDLE;
+	info->rx.sring = NULL;
+	info->tx.sring = NULL;
+
+	info->shared_info_page->is_connected = 0;
+	info->shared_info_page->disconnect_count++;
+}
+
+struct xenbus_backend_state_adapter {
+	struct xenbus_watch watch;
+	int state;
+	struct xenbus_device *xbdev;
+};
+
+static DECLARE_WAIT_QUEUE_HEAD(backend_state_wq);
+
+static void xenbus_backend_state_changed(struct xenbus_watch *xbw,
+					 const char **vec, unsigned int len)
+{
+	struct xenbus_backend_state_adapter *adapter =
+		container_of(xbw, struct xenbus_backend_state_adapter, watch);
+	struct xenbus_device *xbdev = adapter->xbdev;
+
+	xenbus_scanf(XBT_NIL, vec[XS_WATCH_PATH], "", "%i", &adapter->state);
+	dev_dbg(&xbdev->dev, "backend %s %s\n", vec[XS_WATCH_PATH],
+		xenbus_strstate(adapter->state));
+	wake_up(&backend_state_wq);
+}
+
+static void xenbus_wait_for_backend_state(
+	struct xenbus_backend_state_adapter *adapter, int expected)
+{
+	struct xenbus_device *xbdev = adapter->xbdev;
+	long timeout;
+
+	timeout = wait_event_interruptible_timeout(backend_state_wq,
+						   adapter->state == expected,
+						   5 * HZ);
+	if (timeout <= 0)
+		dev_info(&xbdev->dev, "backend %s timed out\n",
+			 xbdev->otherend);
+}
+
+/*
+ * Lets move through XenbusStateClosing due to bugs in other xen_netfront
+ * implementations that move directly from XenbusStateConnected to
+ * XenbusStateClosed.
+ */
+static int
+xennet_reconnect_frontend(struct xenbus_device *xbdev)
+{
+	struct xenbus_backend_state_adapter adapter = {
+		.state = XenbusStateUnknown,
+		.xbdev = xbdev,
+	};
+	int err;
+
+	dev_dbg(&xbdev->dev, "%s: reconnecting to backend %s\n", __func__,
+		xbdev->otherend);
+
+	err = xenbus_watch_pathfmt(xbdev, &adapter.watch,
+				   xenbus_backend_state_changed,
+				   "%s/state", xbdev->otherend);
+	if (err)
+		return err;
+
+	xenbus_switch_state(xbdev, XenbusStateClosing);
+	xenbus_wait_for_backend_state(&adapter, XenbusStateClosing);
+
+	xenbus_switch_state(xbdev, XenbusStateClosed);
+	xenbus_wait_for_backend_state(&adapter, XenbusStateClosed);
+
+	xenbus_switch_state(xbdev, XenbusStateInitialising);
+	xenbus_wait_for_backend_state(&adapter, XenbusStateInitWait);
+
+	unregister_xenbus_watch(&adapter.watch);
+	dev_info(&xbdev->dev, "reconnect done on %s\n", xbdev->otherend);
+	kfree(adapter.watch.node);
+	return 0;
+}
+
+/**
+ * Entry point to this code when a new device is created.  Allocate the basic
+ * structures and the ring buffers for communication with the backend, and
+ * inform the backend of the appropriate details for those.
+ */
+static int xennet_probe(struct xenbus_device *xbdev,
+		const struct xenbus_device_id *id)
+{
+	struct netfront_info *info;
+	int backend_state = XenbusStateUnknown;
+	int err;
+
+	err = xennet_reconnect_frontend(xbdev);
+	if (err)
+		return err;
+
+	err = xenbus_scanf(XBT_NIL, xbdev->otherend, "state", "%i",
+			   &backend_state);
+	if (err != 1)
+		backend_state = XenbusStateUnknown;
+
+	if (backend_state != XenbusStateInitWait) {
+		dev_err(&xbdev->dev, "%s, stuck in state %s\n",
+			xbdev->nodename, xenbus_strstate(backend_state));
+		return -ENODEV;
+	}
+
+	info = xennet_alloc_resources(xbdev);
+	dev_set_drvdata(&xbdev->dev, info);
+	return 0;
+}
+
+/**
+ * We are reconnecting to the backend, due to a suspend/resume, or a backend
+ * driver restart.  We tear down our netif structure and recreate it, but
+ * leave the device-layer structures intact so that this is transparent to the
+ * rest of the kernel.
+ */
+static int xennet_resume(struct xenbus_device *xbdev)
+{
+	struct netfront_info *info = dev_get_drvdata(&xbdev->dev);
+
+	pr_devel("%s\n", xbdev->nodename);
+
+	/*we can use the same memory region - disable deffered free*/
+	xennet_disconnect_backend(info, 0);
+
+	return 0;
+}
+
+/**
+ * Callback received when the backend's state changes.
+ */
+static void netback_changed(struct xenbus_device *xbdev,
+		enum xenbus_state backend_state)
+{
+	struct netfront_info *info = dev_get_drvdata(&xbdev->dev);
+
+	pr_devel("%s\n", xenbus_strstate(backend_state));
+
+	switch (backend_state) {
+	case XenbusStateInitialising:
+	case XenbusStateInitialised:
+	case XenbusStateReconfiguring:
+	case XenbusStateReconfigured:
+		break;
+	case XenbusStateUnknown:
+		break;
+
+	case XenbusStateInitWait:
+		if (xbdev->state != XenbusStateInitialising)
+			break;
+		if (xennet_connect_backend(info) != 0) {
+			pr_err("%s\n", xbdev->nodename);
+			break;
+		}
+		xenbus_switch_state(xbdev, XenbusStateConnected);
+		break;
+
+	case XenbusStateConnected:
+		break;
+
+	case XenbusStateClosed:
+		if (xbdev->state == XenbusStateClosed) {
+			xenbus_switch_state(xbdev, XenbusStateInitialising);
+			break;
+		}
+
+	case XenbusStateClosing:
+		xenbus_frontend_closed(xbdev);
+		break;
+	}
+}
+
+static const struct xenbus_device_id netfront_ids[] = {
+	{ "vif" },
+	{ "" }
+};
+
+static int xennet_remove(struct xenbus_device *xbdev)
+{
+	struct netfront_info *info = dev_get_drvdata(&xbdev->dev);
+
+	pr_devel("%s\n", xbdev->nodename);
+
+	xennet_disconnect_backend(info, 1);
+
+	xennet_free_resources(xbdev);
+
+	return 0;
+}
+
+static struct xenbus_driver xenuio_driver = {
+	.ids  = netfront_ids,
+	.probe = xennet_probe,
+	.remove = xennet_remove,
+	.resume = xennet_resume,
+	.otherend_changed = netback_changed,
+#ifndef DEFINE_XENBUS_DRIVER
+	.name = "xen_uio",
+#endif
+	.driver = {
+		.name = "xen_uio",
+	},
+};
+
+/*operations that we can't do through the shared memory*/
+static long xennet_ioctl(struct file *file,
+		unsigned int cmd, unsigned long arg) {
+	int rc;
+	void __user *uarg = (void __user *) arg;
+
+	switch (cmd) {
+	case IOCTL_EVTCHN_NOTIFY:
+		{
+			struct ioctl_evtchn_notify notify;
+
+			rc = -EFAULT;
+			if (copy_from_user(&notify, uarg, sizeof(notify)))
+				break;
+			notify_remote_via_evtchn(notify.port);
+			rc = 0;
+		}
+		break;
+	case IOCTL_EVTCHN_NOTIFY_GRANT:
+		{
+			uint16_t i;
+			int notify;
+			struct ioctl_evtchn_notify_grant *ng;
+
+			rc = -EFAULT;
+
+			if (access_ok(VERIFY_READ, uarg, sizeof(ng)))
+				ng = uarg;
+			else
+				break;
+
+			for (i = 0; i < ng->rel_count; i++) {
+				gnttab_end_foreign_access_ref(
+					ng->rel_gref[i],
+					(ng->is_rx ? 0 : GNTMAP_readonly));
+			}
+
+			if (ng->count) {
+				union {
+					struct xen_netif_rx_front_ring *rx;
+					struct xen_netif_tx_front_ring *tx;
+				} ring;
+
+				for (i = 0; i < ng->count; i++) {
+					gnttab_grant_foreign_access_ref(
+						ng->s[i].gref,
+						ng->otherend_id,
+						pfn_to_mfn(ng->s[i].paddr),
+						(ng->is_rx ? 0 :
+						 GNTMAP_readonly));
+				}
+
+				if (ng->is_rx) {
+					ring.rx = ng->u.rx_ring;
+					if (&ng->info->rx != ring.rx) {
+						pr_err(
+						"bad info or rx ring addr\n");
+						return -EINVAL;
+					}
+					ring.rx->req_prod_pvt += ng->count;
+					RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(
+							ring.rx, notify);
+				} else {
+					ring.tx = ng->u.tx_ring;
+					if (&ng->info->tx != ring.tx) {
+						pr_err(
+						"bad info or tx ring addr\n");
+						return -EINVAL;
+					}
+					ring.tx->req_prod_pvt += ng->count;
+					RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(
+							ring.tx, notify);
+				}
+
+				if (notify)
+					notify_remote_via_evtchn(ng->port);
+			}
+
+			rc = 0;
+		}
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static const struct file_operations xennet_fops = {
+	.owner   = THIS_MODULE,
+	.read    = NULL/*xennet_read*/,
+	.write   = NULL/*xennet_write*/,
+	.unlocked_ioctl = xennet_ioctl,
+	.poll    = NULL/*xennet_poll*/,
+	.fasync  = NULL/*xennet_fasync*/,
+	.open    = NULL/*xennet_open*/,
+	.mmap    = NULL/*xennet_mmap*/,
+	.release = NULL/*xennet_release*/,
+	.llseek  = no_llseek,
+};
+
+static struct miscdevice xennet_miscdev = {
+	.minor        = MISC_DYNAMIC_MINOR,
+	.name         = XEN_PMD_UIO_NAME,
+	.fops         = &xennet_fops,
+};
+
+static ssize_t read_domain(struct file *f, char __user *buf,
+		size_t count, loff_t *off)
+{
+	if (count > domain_len)
+		count = domain_len;
+
+	if (copy_to_user(buf, domain_name, count))
+		return -EFAULT;
+
+	domain_len = (count ? domain_len - count : sizeof(domain_name));
+
+	return count;
+}
+
+static const struct file_operations domain_fops = {
+	.owner = THIS_MODULE,
+	.read = read_domain,
+};
+
+static int __init netif_init(void)
+{
+	int err;
+
+	if (!xen_domain()) {
+		pr_err("xen bare hw\n");
+		return -ENODEV;
+	}
+
+	pr_info("xen %s domain\n", domains[xen_domain_type]);
+
+	snprintf(domain_name, sizeof(domain_name),
+			"%s\n", domains[xen_domain_type]);
+
+	if (!xen_feature(XENFEAT_auto_translated_physmap))
+		pr_info("feature auto_translated_physmap is disabled\n");
+
+	pr_info("gnttab version: %d\n", (int)__gnttab_version());
+
+	domain_proc = proc_create(DOMAIN_PROC, S_IRUGO, NULL, &domain_fops);
+	if (domain_proc == NULL) {
+		pr_err("could not create /proc/%s\n", DOMAIN_PROC);
+		return -ENOMEM;
+	}
+
+	pr_info("/proc/%s created\n", DOMAIN_PROC);
+
+	err = misc_register(&xennet_miscdev);
+	if (err != 0) {
+		pr_err("could not register char device\n");
+		return err;
+	}
+
+	pr_info("initialising xen virtual ethernet driver\n");
+
+	err = xenbus_register_frontend(&xenuio_driver);
+
+	return err;
+}
+module_init(netif_init);
+
+static void __exit netif_exit(void)
+{
+	remove_proc_entry(DOMAIN_PROC, NULL);
+
+	xenbus_unregister_driver(&xenuio_driver);
+
+	misc_deregister(&xennet_miscdev);
+}
+module_exit(netif_exit);
+
+MODULE_DESCRIPTION("Xen virtual network device frontend");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vif");
+MODULE_ALIAS("xennet");
-- 
2.5.5



More information about the dev mailing list