diff mbox

[dpdk-dev,3/4] xen: add uio driver

Message ID 1423937208-2063-3-git-send-email-shemming@brocade.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Stephen Hemminger Feb. 14, 2015, 6:06 p.m. UTC
New uio helper kernel driver for use by Xen netfront UIO poll mode driver.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/librte_eal/linuxapp/Makefile          |   3 +
 lib/librte_eal/linuxapp/xen_uio/Makefile  |  55 ++
 lib/librte_eal/linuxapp/xen_uio/xen_uio.c | 837 ++++++++++++++++++++++++++++++
 3 files changed, 895 insertions(+)
 create mode 100644 lib/librte_eal/linuxapp/xen_uio/Makefile
 create mode 100644 lib/librte_eal/linuxapp/xen_uio/xen_uio.c
diff mbox

Patch

diff --git a/lib/librte_eal/linuxapp/Makefile b/lib/librte_eal/linuxapp/Makefile
index 8fcfdf6..d3893e5 100644
--- a/lib/librte_eal/linuxapp/Makefile
+++ b/lib/librte_eal/linuxapp/Makefile
@@ -41,5 +41,8 @@  endif
 ifeq ($(CONFIG_RTE_LIBRTE_XEN_DOM0),y)
 DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += xen_dom0
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_XEN_PMD),y)
+DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += xen_uio
+endif
 
 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..25a9f35
--- /dev/null
+++ b/lib/librte_eal/linuxapp/xen_uio/Makefile
@@ -0,0 +1,55 @@ 
+#   BSD LICENSE
+#
+#   Copyright (c) 2013-2015 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
+
+#
+# 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/xen_uio.c b/lib/librte_eal/linuxapp/xen_uio/xen_uio.c
new file mode 100644
index 0000000..b25b1f3
--- /dev/null
+++ b/lib/librte_eal/linuxapp/xen_uio/xen_uio.c
@@ -0,0 +1,837 @@ 
+/*
+ * Virtual network driver for conversing with remote driver backends.
+ *
+ * Copyright (c) 2002-2005, K A Fraser
+ * Copyright (c) 2005, XenSource Ltd
+ * Copyright (c) 2013-2015 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 <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 "../../../librte_pmd_xen/xen_adapter_info.h"
+
+#define GRANT_INVALID_REF 0
+
+#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 *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 != GRANT_INVALID_REF)
+		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 (NULL == info)
+		goto exit;
+
+	info->gref_rxtx_head = GRANT_INVALID_REF;
+	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 (NULL == 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 != GRANT_INVALID_REF)
+			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;
+	int err;
+
+	info->tx_ring_ref = GRANT_INVALID_REF;
+	info->rx_ring_ref = GRANT_INVALID_REF;
+	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,
+			"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, virt_to_mfn(info->txs));
+	if (err < 0) {
+		pr_err("xenbus_grant_ring for txs failed!\n");
+		goto fail;
+	}
+	info->tx_ring_ref = err;
+
+	err = xenbus_grant_ring(info->xbdev, virt_to_mfn(info->rxs));
+	if (err < 0) {
+		pr_err("xenbus_grant_ring for rxs failed!\n");
+		goto fail;
+	}
+	info->rx_ring_ref = err;
+
+	/* 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;
+	}
+
+	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", 1);
+	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)
+{
+	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 = GRANT_INVALID_REF;
+	info->rx_ring_ref = GRANT_INVALID_REF;
+	info->rx.sring = NULL;
+	info->tx.sring = NULL;
+
+	info->shared_info_page->is_connected = 0;
+	info->shared_info_page->disconnect_count++;
+}
+
+
+/**
+ * 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;
+
+	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,
+};
+
+/*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],
+						0);
+			}
+
+			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));
+				}
+
+				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 -(ENOSYS);
+					}
+					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 -(ENOSYS);
+					}
+					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 = -ENOSYS;
+		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(KERN_INFO "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");