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

Reply via email to