From patchwork Sun Feb 15 15:24:48 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 3361 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [IPv6:::1]) by dpdk.org (Postfix) with ESMTP id 764D9B58E; Sun, 15 Feb 2015 16:25:06 +0100 (CET) Received: from mail-ig0-f170.google.com (mail-ig0-f170.google.com [209.85.213.170]) by dpdk.org (Postfix) with ESMTP id 21B52B581 for ; Sun, 15 Feb 2015 16:25:03 +0100 (CET) Received: by mail-ig0-f170.google.com with SMTP id l13so25829377iga.1 for ; Sun, 15 Feb 2015 07:25:02 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=wIPI6Il1ob6cagE/tjdNj/SV1QQVawlfYD6owy3if2E=; b=dblZUv5/XDlpuRHMmszS8oaXHVvsieJf8ETDay6H9u+c+u7RjzAlYm8qwmuuLf4qQD UjAH1yIqJTRMhW2t87hdOYTipfqHsRgOlH8FH0RITTRk9dPyc0AxCb/wBpjbTzBqolKB 8AKA1cDzm34idatakQEGxTtTuYh7EO9SoAew8gmy0+laj8l+9j1nwSeYKDiOL8xwIBmD 961ioW4MuotPLcNhFaUKrp38NklaVVX4dFsSe0eCarDmCTq06eJEthCNJTnS6Qv4wa1B /5eOcldHE8bhYNQX6T2+2ci82XJ5ZpIyXuEGiS7odtPZCssJDKHNaes8BdsOjvOw4mgl UStw== X-Gm-Message-State: ALoCoQma1qemG9yUVC+lcXzl1vQHUTFETHjQsCZuqtczRGwf/CgNk1GhbLoV3Oj48MCG4RLadSUw X-Received: by 10.107.164.15 with SMTP id n15mr22357474ioe.82.1424013902326; Sun, 15 Feb 2015 07:25:02 -0800 (PST) Received: from uryu.home.lan ([67.210.173.2]) by mx.google.com with ESMTPSA id i2sm7949373ioi.41.2015.02.15.07.25.01 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 15 Feb 2015 07:25:01 -0800 (PST) From: Stephen Hemminger X-Google-Original-From: Stephen Hemminger To: dev@dpdk.org Date: Sun, 15 Feb 2015 10:24:48 -0500 Message-Id: <1424013889-2226-4-git-send-email-shemming@brocade.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1424013889-2226-1-git-send-email-shemming@brocade.com> References: <1424013889-2226-1-git-send-email-shemming@brocade.com> Cc: Stephen Hemminger Subject: [dpdk-dev] [PATCH 4/5] xen: add uio driver X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" New uio helper kernel driver for Xen netfront UIO poll mode driver. Signed-off-by: Stephen Hemminger --- v2 -- use PMD_REGISTER 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 --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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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(¬ify, 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");