From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm0-f67.google.com (mail-wm0-f67.google.com [74.125.82.67]) by dpdk.org (Postfix) with ESMTP id 08B262C1A for ; Tue, 22 Mar 2016 10:56:16 +0100 (CET) Received: by mail-wm0-f67.google.com with SMTP id l68so28145586wml.3 for ; Tue, 22 Mar 2016 02:56:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=5C3WpqJPFKa0yjgdv/R4B6sbcMGKasKrppu577BVOok=; b=XLwkYl9x30+Y8RJDNGaF3vVIIvC5J17ORJCwFnpJRjYxo+Y/T6ikGR9eDJTXlhUS6X 6iGOh//1XKk+0DtqBuAgZiApExznay5c8Vu/JqzgtmsBrXIrD9xrRlB9iz9lb9qiTU4q OZK8BG+Gzha2yDJyRFRYMhylQLmX/BgUZP+v5phE4XugFrz5/f25xBK0Vld164iuBXZA N/5/7LdCxm1eyqLtSybzJ5+nLyVFVYBW9HnauV2KHKtFzW+YXabVnzpOmfAlky4zuYtI TcX/Nym/WsDhdy7rS+CbB/KjszbX/Bnq9ULmUVAXuDm1OV/sQiyAuDPcGPG9D4paw7Ip wGxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=5C3WpqJPFKa0yjgdv/R4B6sbcMGKasKrppu577BVOok=; b=YaYr0qKlWWB9o5mjAyPyT3WOaFgkoZfJO914EvOk5iluxlu2Fat3qKuyY/RMA2bqE3 EQGeeJ7X+UNBjbJskEImkAmch4ygyHiyI7AuEsIOP9PbjtPWrv8bS8oovzLteTEtKFA/ mG5PKZIAYzh5/bkUdZ9iEW/Uv1IRki8z1rKBaVnEQhUBiyvp4cvNZb/kHNNDLpi4jvz4 8D5Pa5LtGCfFdaG2qea/JSL1wr6ogBYCugpQFPuZSfbSW3JfC+2hDhmsYbCSdjRUuz8L /q2S00fTXkXAyEnTNbA7pe5OjfqnGvNuVYvc1ytm9F7NcjDCrLA/nP4llFyBn9oEn6YU 8L6g== X-Gm-Message-State: AD7BkJKEii7q/kJaj8Rsk69pqdSeKLBA2Ta0Pqd7abWuh9ILAgioDfQi3Cps5ayG8pSlSg== X-Received: by 10.28.147.72 with SMTP id v69mr18007119wmd.79.1458640575802; Tue, 22 Mar 2016 02:56:15 -0700 (PDT) Received: from weierstrass.local.net (port-92-196-120-98.dynamic.qsc.de. [92.196.120.98]) by smtp.gmail.com with ESMTPSA id k125sm16439551wmb.14.2016.03.22.02.56.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 22 Mar 2016 02:56:14 -0700 (PDT) Sender: Jan Blunck From: Jan Blunck To: dev@dpdk.org Cc: jblunck@brocade.com, shemming@brocade.com, Stephen Hemminger Date: Tue, 22 Mar 2016 10:55:27 +0100 Message-Id: <1458640529-9183-2-git-send-email-jblunck@infradead.org> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1458640529-9183-1-git-send-email-jblunck@infradead.org> References: <1424013889-2226-4-git-send-email-shemming@brocade.com> <1458640529-9183-1-git-send-email-jblunck@infradead.org> Subject: [dpdk-dev] [PATCH v3 1/3] xen: Add UIO kernel 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: , X-List-Received-Date: Tue, 22 Mar 2016 09:56:16 -0000 New UIO helper kernel driver for Xen netfront UIO poll mode driver. Signed-off-by: Stephen Hemminger Signed-off-by: Jan Blunck --- 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 . + */ + +#ifndef _XEN_UIO_COMPAT_H_ +#define _XEN_UIO_COMPAT_H_ + +#include + +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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(¬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], + (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