[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH] plat/xen: Introduce Xen Netfront driver
Initial Xen netfront network driver based on Unikraft Network API and minios networking implementation. Supports both polling mode and RX interrupt callback. Driver can get ip configuration from xl.cfg using the vif param ip=<ip_addr> <netmask> <gateway> Signed-off-by: Razvan Cojocaru <razvan.cojocaru93@xxxxxxxxx> --- plat/xen/Config.uk | 17 ++ plat/xen/Makefile.uk | 18 ++ plat/xen/drivers/net/netfront.c | 581 +++++++++++++++++++++++++++++++++++++ plat/xen/drivers/net/netfront.h | 98 +++++++ plat/xen/drivers/net/netfront_xb.h | 46 +++ plat/xen/drivers/net/netfront_xs.c | 356 +++++++++++++++++++++++ 6 files changed, 1116 insertions(+) create mode 100644 plat/xen/drivers/net/netfront.c create mode 100644 plat/xen/drivers/net/netfront.h create mode 100644 plat/xen/drivers/net/netfront_xb.h create mode 100644 plat/xen/drivers/net/netfront_xs.c diff --git a/plat/xen/Config.uk b/plat/xen/Config.uk index 9c398f1..3d87d67 100644 --- a/plat/xen/Config.uk +++ b/plat/xen/Config.uk @@ -20,4 +20,21 @@ if (PLAT_XEN) instead of the hypervisor console. When this option is enabled the hypervisor console is used for kernel messages only. + +menuconfig XEN_XENBUS + bool "Xenbus Driver" + default n + depends on (ARCH_X86_64) + select LIBUKBUS + help + Register a Xenbus driver as uk_bus + +if (XEN_XENBUS) + config XEN_NETFRONT + bool "Xenbus Netfront Driver" + default n + select LIBUKNETDEV + help + Driver for netfront devices +endif endif diff --git a/plat/xen/Makefile.uk b/plat/xen/Makefile.uk index 45096cb..42264a0 100644 --- a/plat/xen/Makefile.uk +++ b/plat/xen/Makefile.uk @@ -10,6 +10,7 @@ $(eval $(call addplat_s,xen,$(CONFIG_PLAT_XEN))) ## $(eval $(call addplatlib,xen,libxenplat)) $(eval $(call addplatlib_s,xen,libxenbus,$(XEN_XENBUS))) +$(eval $(call addplatlib_s,xen,libxennetfront,$(XEN_NETFRONT))) ## ## Xen platform compilation settings @@ -72,3 +73,20 @@ LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/console.c LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/shutdown.c LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/events.c LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/gnttab.c + +LIBXENBUS_ASFLAGS-y += $(LIBXENPLAT_ASFLAGS-y) +LIBXENBUS_ASINCLUDES-y += $(LIBXENPLAT_ASINCLUDES-y) +LIBXENBUS_CFLAGS-y += $(LIBXENPLAT_CFLAGS-y) +LIBXENBUS_CINCLUDES-y += $(LIBXENPLAT_CINCLUDES-y) +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xenbus.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/client.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xs_comms.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xs_watch.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xs.c + +LIBXENNETFRONT_ASFLAGS-y += $(LIBXENPLAT_ASFLAGS-y) +LIBXENNETFRONT_ASINCLUDES-y += $(LIBXENPLAT_ASINCLUDES-y) +LIBXENNETFRONT_CFLAGS-y += $(LIBXENPLAT_CFLAGS-y) +LIBXENNETFRONT_CINCLUDES-y += $(LIBXENPLAT_CINCLUDES-y) +LIBXENNETFRONT_SRCS-y += $(LIBXENPLAT_BASE)/drivers/net/netfront.c +LIBXENNETFRONT_SRCS-y += $(LIBXENPLAT_BASE)/drivers/net/netfront_xs.c diff --git a/plat/xen/drivers/net/netfront.c b/plat/xen/drivers/net/netfront.c new file mode 100644 index 0000000..b05881a --- /dev/null +++ b/plat/xen/drivers/net/netfront.c @@ -0,0 +1,581 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * Razvan Cojocaru <razvan.cojocaru93@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ + +#include <inttypes.h> +#include <string.h> +#include <uk/errptr.h> +#include <uk/print.h> +#include <uk/alloc.h> +#include <uk/essentials.h> +#include <uk/assert.h> +#include <uk/arch/limits.h> +#include <common/hypervisor.h> +#include <xen-x86/mm.h> +#include <xen-x86/mm_pv.h> +#include <xenbus/xenbus.h> +#include <xenbus/xs.h> +/* Header include order is from the most general to the most particular */ + +#include "netfront.h" +#include "netfront_xb.h" + + +#define NETFRONT_RING_INIT(netdev, txrx) \ + do { \ + struct netif_##txrx##_sring *sring; \ + \ + sring = uk_malloc_page(drv_allocator); \ + if (!sring) \ + return -ENOMEM; \ + \ + memset(sring, 0, PAGE_SIZE); \ + SHARED_RING_INIT(sring); \ + FRONT_RING_INIT(&(netdev)->txrx, sring, PAGE_SIZE); \ + (netdev)->ring_ref_##txrx =\ + gnttab_grant_access((netdev)->xendev->otherend_id, \ + virt_to_mfn(sring), 0); \ + UK_ASSERT((netdev)->ring_ref_##txrx != GRANT_INVALID_REF); \ + } while (0) + +#define NETFRONT_RING_FINI(netdev, txrx) \ + do { \ + if ((netdev)->ring_ref_##txrx != GRANT_INVALID_REF) \ + gnttab_end_access((netdev)->ring_ref_##txrx); \ + if ((netdev)->txrx.sring) \ + uk_free_page(xbh.a, (netdev)->txrx.sring); \ + } while (0) + +#define DEFAULT_MTU 1526 +#define UK_NETDEV_MAX_DEVICES 10 + +struct uk_alloc *drv_allocator; + +void network_tx_buf_gc(struct netfront_dev *dev); + +void netfront_handler(evtchn_port_t port __unused, + struct __regs *regs __unused, void *arg) +{ + struct netfront_dev *dev = arg; + + uk_waitq_wake_up(&dev->wq); +} + +static inline uint16_t xennet_rxidx(RING_IDX idx) +{ + return (uint16_t)(idx & (NET_RX_RING_SIZE - 1)); +} + +void init_rx_buffers(struct netfront_dev *dev) +{ + uint16_t i, requeue_idx; + netif_rx_request_t *req; + int notify; + + /* Rebuild the RX buffer freelist and the RX ring itself. */ + for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) { + struct net_buffer *buf = &dev->rx_buffers[requeue_idx]; + + req = RING_GET_REQUEST(&dev->rx, requeue_idx); + buf->gref = req->gref = + gnttab_grant_access(dev->xendev->otherend_id, + virt_to_mfn(buf->page), 0); + req->id = requeue_idx; + requeue_idx++; + } + + dev->rx.req_prod_pvt = requeue_idx; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->rx, notify); + + if (notify) + notify_remote_via_evtchn(dev->evtchn); + + dev->rx.sring->rsp_event = dev->rx.rsp_cons + 1; +} + +void network_tx_buf_gc(struct netfront_dev *dev) +{ + RING_IDX cons, prod; + unsigned short id; + + do { + prod = dev->tx.sring->rsp_prod; + rmb(); /* Ensure we see responses up to 'rp'. */ + + for (cons = dev->tx.rsp_cons; cons != prod; cons++) { + struct netif_tx_response *txrsp; + struct net_buffer *buf; + + txrsp = RING_GET_RESPONSE(&dev->tx, cons); + if (txrsp->status == NETIF_RSP_NULL) + continue; + + if (txrsp->status == NETIF_RSP_ERROR) + uk_printk("packet error\n"); + + uk_printd(DLVL_INFO, "%s: txrsp=%d\n", __func__, + txrsp->status); + id = txrsp->id; + UK_ASSERT(id < NET_TX_RING_SIZE); + buf = &dev->tx_buffers[id]; + gnttab_end_access(buf->gref); + buf->gref = GRANT_INVALID_REF; + + add_id_to_freelist(id, dev->tx_freelist); + uk_semaphore_up(&dev->tx_sem); + } + + dev->tx.rsp_cons = prod; + + /* + * Set a new event, then check for race with update of tx_cons. + * Note that it is essential to schedule a callback, no matter + * how few tx_buffers are pending. Even if there is space in the + * transmit ring, higher layers may be blocked because too much + * data is outstanding: in such cases notification from Xen is + * likely to be the only kick that we'll get. + */ + dev->tx.sring->rsp_event = prod + + ((dev->tx.sring->req_prod - prod) >> 1) + 1; + mb(); + } while ((cons == prod) && (prod != dev->tx.sring->rsp_prod)); + + +} + +static int netfront_xmit(struct uk_netdev *n, uint16_t queue_id __unused, + const struct uk_netdev_mbuf *pkt) +{ + struct netif_tx_request *tx; + RING_IDX i; + int notify, flags; + uint16_t id; + struct net_buffer *buf; + void *page; + struct netfront_dev *dev; + + uk_printd(DLVL_INFO, "netfront xmit dev: %d, data: %p\n", + n->data->id, pkt->payload); + + UK_ASSERT(n != NULL); + dev = __containerof(n, struct netfront_dev, netdev); + + UK_ASSERT(pkt->len < PAGE_SIZE); + + uk_semaphore_down(&dev->tx_sem); + + local_irq_save(flags); + id = (uint16_t)get_id_from_freelist(dev->tx_freelist); + local_irq_restore(flags); + + buf = &dev->tx_buffers[id]; + page = buf->page; + if (!page) + page = buf->page = (char *) uk_malloc_page(drv_allocator); + + i = dev->tx.req_prod_pvt; + tx = RING_GET_REQUEST(&dev->tx, i); + + memcpy(page, pkt->payload, pkt->len); + + buf->gref = tx->gref = gnttab_grant_access(dev->xendev->otherend_id, + virt_to_mfn(page), 1); + + tx->offset = 0; + tx->size = (uint16_t)pkt->len; + tx->flags = 0; + tx->id = id; + dev->tx.req_prod_pvt = i + 1; + + wmb(); + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->tx, notify); + + if (notify) + notify_remote_via_evtchn(dev->evtchn); + + local_irq_save(flags); + network_tx_buf_gc(dev); + local_irq_restore(flags); + return 0; +} + +static int netfront_recv(struct uk_netdev *n, uint16_t queue_id __unused, + struct uk_netdev_mbuf *pkt) +{ + RING_IDX rp, cons, req_prod; + struct netfront_dev *dev; + int notify; + struct net_buffer *buf; + struct netif_rx_response *rx; + unsigned char *page; + uint16_t len = 0, id; + netif_rx_request_t *req; + + UK_ASSERT(n != NULL); + dev = __containerof(n, struct netfront_dev, netdev); + + rp = dev->rx.sring->rsp_prod; + rmb(); /* Ensure we see queued responses up to 'rp'. */ + cons = dev->rx.rsp_cons; + + if (cons != rp) { + rx = RING_GET_RESPONSE(&dev->rx, cons); + + id = rx->id; + UK_ASSERT(id < NET_RX_RING_SIZE); + + buf = &dev->rx_buffers[id]; + page = (unsigned char *)buf->page; + gnttab_end_access(buf->gref); + + if (rx->status > NETIF_RSP_NULL) { + len = (uint16_t)rx->status; + if (len > DEFAULT_MTU) + len = DEFAULT_MTU; + memcpy(pkt->payload, page+rx->offset, len); + pkt->len = len; + dev->rx.rsp_cons++; + } else { + goto out; + } + } else { + goto out; + } + + req_prod = dev->rx.req_prod_pvt; + id = xennet_rxidx(req_prod); + req = RING_GET_REQUEST(&dev->rx, req_prod); + buf = &dev->rx_buffers[id]; + page = buf->page; + + /* We are sure to have free gnttab entries + * since they got released above + */ + buf->gref = req->gref = + gnttab_grant_access(dev->xendev->otherend_id, + virt_to_mfn(page), 0); + + req->id = id; + + wmb(); + + dev->rx.req_prod_pvt = req_prod; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->rx, notify); + if (notify) + notify_remote_via_evtchn(dev->evtchn); + +out: + return len; +} + +static int netfront_rx_queue_setup(struct uk_netdev *n, + uint16_t queue_id __unused, + const struct uk_netdev_rxqueue_conf *conf __unused) +{ + struct netfront_dev *d; + unsigned int i; + + UK_ASSERT(n != NULL); + d = __containerof(n, struct netfront_dev, netdev); + + for (i = 0; i < NET_RX_RING_SIZE; i++) + d->rx_buffers[i].page = (char *) uk_malloc_page(drv_allocator); + + NETFRONT_RING_INIT(d, rx); + + init_rx_buffers(d); + return 0; +} + +static int netfront_tx_queue_setup(struct uk_netdev *n, + uint16_t queue_id __unused, + const struct uk_netdev_txqueue_conf *conf __unused) +{ + struct netfront_dev *d; + unsigned int i; + + UK_ASSERT(n != NULL); + d = __containerof(n, struct netfront_dev, netdev); + + uk_semaphore_init(&d->tx_sem, NET_TX_RING_SIZE); + for (i = 0; i < NET_TX_RING_SIZE; i++) { + add_id_to_freelist(i, d->tx_freelist); + d->tx_buffers[i].page = NULL; + } + + NETFRONT_RING_INIT(d, tx); + return 0; +} + +static int netfront_configure(struct uk_netdev *n, + const struct uk_netdev_conf *conf __unused) +{ + struct netfront_dev *d; + int err; + + UK_ASSERT(n != NULL); + d = __containerof(n, struct netfront_dev, netdev); + + uk_printd(DLVL_EXTRA, "configure netfront device\n"); + + err = netfront_xb_init(d, drv_allocator); + if (err) { + uk_printd(DLVL_ERR, + "Error initializing Xenbus data: %d.\n", err); + return err; + } + + n->data->state = UK_NETDEV_CONFIGURED; + + return err; +} + +void netfront_handler_placeholder(evtchn_port_t port, + struct __regs *regs __unused, void *arg __unused) +{ + uk_printd(DLVL_INFO, "Event port: %d\n", port); +} + +static void netfront_thread(void *arg) +{ + struct netfront_dev *dev = arg; + struct uk_netdev *n; + int more; + + UK_ASSERT(dev != NULL); + n = &dev->netdev; + + while (n->data->state == UK_NETDEV_RUNNING) { + uk_waitq_wait_event(&dev->wq, + RING_HAS_UNCONSUMED_RESPONSES(&dev->rx)); + +moretodo: + n->rx_cb(n, 0); + + RING_FINAL_CHECK_FOR_RESPONSES(&dev->rx, more); + if (more) + goto moretodo; + } +} + +int netfront_start(struct uk_netdev *n) +{ + struct netfront_dev *d; + int err = 0; + + UK_ASSERT(n != NULL); + d = __containerof(n, struct netfront_dev, netdev); + + d->thread_name = malloc(UK_NETDEV_NAME_MAX_LEN); + if (d->thread_name != NULL) + sprintf(d->thread_name, "netfront%d", n->data->id); + +#ifdef CONFIG_LIBUKNETDEV_NAME + uk_netdev_name_set(n, d->thread_name, (uint16_t)strlen(d->thread_name)); +#endif + n->data->state = UK_NETDEV_RUNNING; + + if (n->rx_cb != NULL) { + err = evtchn_alloc_unbound(d->xendev->otherend_id, + netfront_handler, d, + &d->evtchn); + if (err) { + uk_printd(DLVL_ERR, + "Error creating event channel: %d.\n", err); + return err; + } + uk_waitq_init(&d->wq); + d->thread = uk_thread_create(d->thread_name, + netfront_thread, d); + if (d->thread == NULL) { + uk_printd(DLVL_ERR, + "Error creating %s thread.", n->data->name); + return -ENOMEM; + } + } else { + err = evtchn_alloc_unbound(d->xendev->otherend_id, + netfront_handler_placeholder, d, &d->evtchn); + if (err) { + uk_printd(DLVL_ERR, + "Error creating event channel: %d.\n", err); + return err; + } + } + + err = netfront_xb_connect(d); + if (err) { + uk_printd(DLVL_ERR, "Error connecting to backend: %d.\n", err); + return err; + } + uk_printd(DLVL_EXTRA, "netfront connected to backend\n"); + + return err; +} + +int netfront_enable_rx_intr(struct uk_netdev *n, + uint16_t rx_queue_id __unused) +{ + struct netfront_dev *d; + + UK_ASSERT(n != NULL); + d = __containerof(n, struct netfront_dev, netdev); + unmask_evtchn(d->evtchn); + return 0; +} + +int netfront_disable_rx_intr(struct uk_netdev *n, + uint16_t rx_queue_id __unused) +{ + struct netfront_dev *d; + + UK_ASSERT(n != NULL); + d = __containerof(n, struct netfront_dev, netdev); + mask_evtchn(d->evtchn); + return 0; +} + +static void netfront_stop(struct uk_netdev *dev) +{ + struct netfront_dev *netdev; + int err; + + UK_ASSERT(dev != NULL); + + netdev = __containerof(dev, struct netfront_dev, netdev); + UK_ASSERT(netdev != NULL); + + uk_printd(DLVL_EXTRA, "remove netfront device\n"); + + err = netfront_xb_disconnect(netdev); + if (err) { + uk_printd(DLVL_ERR, + "Error disconnecting from backend: %d.\n", err); + return; + } + + mask_evtchn(netdev->evtchn); + unbind_evtchn(netdev->evtchn); + + NETFRONT_RING_FINI(netdev, tx); + NETFRONT_RING_FINI(netdev, rx); + + netfront_xb_fini(netdev); +} + +const void *netfront_econf_get(struct uk_netdev *n, + enum uk_netdev_extra_conf_type econf) +{ + struct netfront_dev *nf; + + UK_ASSERT(n != NULL); + nf = __containerof(n, struct netfront_dev, netdev); + + switch (econf) { + case IPv4ADDR_STR: + return nf->econf.ipv4addr; + case IPv4MASK_STR: + return nf->econf.ipv4mask; + case IPv4GW_STR: + return nf->econf.ipv4gw; + default: + break; + } + + /* type not supported */ + return NULL; +} + +static const struct uk_netdev_ops netfront_ops = { + .dev_configure = netfront_configure, + .rx_queue_setup = netfront_rx_queue_setup, + .tx_queue_setup = netfront_tx_queue_setup, + .dev_start = netfront_start, + .dev_stop = netfront_stop, + .rx_enable_intr = netfront_enable_rx_intr, + .rx_disable_intr = netfront_disable_rx_intr, + .econf_get = netfront_econf_get, +}; + +static int netfront_add_dev(struct xenbus_device *dev) +{ + struct netfront_dev *d; + + UK_ASSERT(dev != NULL); + UK_ASSERT(uk_netdev_count() < UK_NETDEV_MAX_DEVICES); + d = uk_malloc(drv_allocator, sizeof(struct netfront_dev)); + + d->xendev = dev; + + d->netdev.rx_pkt = netfront_recv; + d->netdev.tx_pkt = netfront_xmit; + d->netdev.dev_ops = &netfront_ops; + d->netdev.data = uk_malloc(drv_allocator, + sizeof(struct uk_netdev_data)); + d->netdev.data->state = UK_NETDEV_UNCONFIGURED; + d->netdev.data->mtu = DEFAULT_MTU; + + d->econf.ipv4addr = NULL; + d->econf.ipv4mask = NULL; + d->econf.ipv4gw = NULL; + + uk_netdev_register(&d->netdev); + + return 0; +} + +static int netfront_drv_init(struct uk_alloc *allocator) +{ + /* driver initialization */ + if (!allocator) + return -EINVAL; + + drv_allocator = allocator; + return 0; +} + +static const xenbus_dev_type_t netfront_devtypes[] = { + xenbus_dev_vif, + xenbus_dev_none +}; + +static struct xenbus_driver netfront_driver = { + .device_types = netfront_devtypes, + .init = netfront_drv_init, + .add_dev = netfront_add_dev +}; + +XENBUS_REGISTER_DRIVER(&netfront_driver); diff --git a/plat/xen/drivers/net/netfront.h b/plat/xen/drivers/net/netfront.h new file mode 100644 index 0000000..41ce3db --- /dev/null +++ b/plat/xen/drivers/net/netfront.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * Razvan Cojocaru <razvan.cojocaru93@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ + +#ifndef __NETFRONT_H__ +#define __NETFRONT_H__ + +#include <xen/io/netif.h> +#include <common/gnttab.h> +#include <common/events.h> +#include <uk/netdev.h> +#include <uk/semaphore.h> + + +#define NET_TX_RING_SIZE __CONST_RING_SIZE(netif_tx, PAGE_SIZE) +#define NET_RX_RING_SIZE __CONST_RING_SIZE(netif_rx, PAGE_SIZE) + + +struct net_buffer { + void *page; + grant_ref_t gref; +}; + +struct xs_econf { + char *ipv4addr; + char *ipv4mask; + char *ipv4gw; +}; + +struct netfront_dev { + struct xenbus_device *xendev; + struct uk_netdev netdev; + + unsigned int tx_freelist[NET_TX_RING_SIZE + 1]; + struct uk_semaphore tx_sem; + + struct net_buffer rx_buffers[NET_RX_RING_SIZE]; + struct net_buffer tx_buffers[NET_TX_RING_SIZE]; + + struct netif_tx_front_ring tx; + struct netif_rx_front_ring rx; + grant_ref_t ring_ref_tx; + grant_ref_t ring_ref_rx; + + evtchn_port_t evtchn; + + struct uk_thread *thread; + char *thread_name; + struct uk_waitq wq; + struct xs_econf econf; +}; + +static inline void add_id_to_freelist(unsigned int id, unsigned int *freelist) +{ + freelist[id + 1] = freelist[0]; + freelist[0] = id; +} + +static inline unsigned int get_id_from_freelist(unsigned int *freelist) +{ + unsigned int id = freelist[0]; + + freelist[0] = freelist[id + 1]; + return id; +} + +#endif /* __NETFRONT_H__ */ diff --git a/plat/xen/drivers/net/netfront_xb.h b/plat/xen/drivers/net/netfront_xb.h new file mode 100644 index 0000000..520a8c5 --- /dev/null +++ b/plat/xen/drivers/net/netfront_xb.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ + +#ifndef __NETFRONT_XB_H__ +#define __NETFRONT_XB_H__ + +#include "netfront.h" + +int netfront_xb_init(struct netfront_dev *netdev, struct uk_alloc *a); +void netfront_xb_fini(struct netfront_dev *netdev); + +int netfront_xb_connect(struct netfront_dev *netdev); +int netfront_xb_disconnect(struct netfront_dev *netdev); + +#endif /* __NETFRONT_XB_H__ */ diff --git a/plat/xen/drivers/net/netfront_xs.c b/plat/xen/drivers/net/netfront_xs.c new file mode 100644 index 0000000..c28da39 --- /dev/null +++ b/plat/xen/drivers/net/netfront_xs.c @@ -0,0 +1,356 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <uk/errptr.h> +#include <uk/print.h> +#include <uk/assert.h> +#include <xenbus/client.h> +#include <uk/netdev.h> +#include "netfront_xb.h" + + +static int netfront_xb_wait_be_connect(struct netfront_dev *netdev); +static int netfront_xb_wait_be_disconnect(struct netfront_dev *netdev); + + +static int xs_read_backend_id(const char *nodename, domid_t *domid) +{ + char path[strlen(nodename) + sizeof("/backend-id")]; + int value, err; + + sprintf(path, "%s/backend-id", nodename); + + err = xs_read_integer(path, &value); + if (!err) + *domid = (domid_t) value; + + return err; +} + +int netfront_xb_init(struct netfront_dev *dev, struct uk_alloc *a) +{ + struct xenbus_device *xendev; + char *mac_str, *p = NULL; + char *ip_str, *ip_addr = NULL, *ip_gw_str = NULL, *ip_mask_str = NULL; + int err; + + UK_ASSERT(dev != NULL); + + xendev = dev->xendev; + UK_ASSERT(xendev != NULL); + + err = xs_read_backend_id(xendev->nodename, &xendev->otherend_id); + if (err) + goto out; + + xendev->otherend = xs_readf(XBT_NIL, "%s/backend", xendev->nodename); + if (PTRISERR(xendev->otherend)) { + uk_printd(DLVL_ERR, "Error reading backend path.\n"); + err = PTR2ERR(xendev->otherend); + xendev->otherend = NULL; + goto out; + } + + mac_str = xs_readf(XBT_NIL, "%s/mac", xendev->nodename); + if (PTRISERR(mac_str)) { + uk_printd(DLVL_ERR, "Error reading MAC address.\n"); + err = PTR2ERR(mac_str); + goto out; + } + + uk_printd(DLVL_INFO, "\tMAC %s\n", mac_str); + + struct uk_hwaddr mac; + p = mac_str; + for (int i = 0; i < 6; i++) { + mac.addr_bytes[i] = (char) strtoul(p, &p, 16); + p++; + } + + memcpy(&dev->netdev.data->mac_addr, &mac, sizeof(struct uk_hwaddr)); + + ip_str = xs_readf(XBT_NIL, "%s/ip", xendev->otherend); + if (PTRISERR(ip_str)) { + uk_printd(DLVL_ERR, "Error reading IP address.\n"); + err = PTR2ERR(ip_str); + goto no_conf; + } + uk_printd(DLVL_ERR, "\tIP: %s\n", ip_str); + + ip_addr = strtok(ip_str," "); + if (ip_addr == NULL) + goto no_conf; + if (!(dev->econf.ipv4addr = uk_malloc(a, strlen(ip_addr)+1))) { + uk_printd(DLVL_ERR, "Error allocating ip config.\n"); + goto no_conf; + } + memcpy(dev->econf.ipv4addr, ip_addr, strlen(ip_addr)+1); + + ip_mask_str = strtok(NULL, " "); + if (ip_mask_str == NULL) + goto no_conf; + if (!(dev->econf.ipv4mask = uk_malloc(a, strlen(ip_mask_str)+1))) { + uk_printd(DLVL_ERR, "Error allocating ip mask config.\n"); + goto no_conf; + } + memcpy(dev->econf.ipv4mask, ip_mask_str, strlen(ip_mask_str)+1); + + ip_gw_str = strtok(NULL, " "); + if (ip_gw_str == NULL) + goto no_conf; + if (!(dev->econf.ipv4gw = uk_malloc(a, strlen(ip_gw_str)+1))) { + uk_printd(DLVL_ERR, "Error allocating ip gateway config.\n"); + goto no_conf; + } + memcpy(dev->econf.ipv4gw, ip_gw_str, strlen(ip_gw_str)+1); + + /* TODO netmap */ + + /* TODO spit event channels */ + +out: + if (!PTRISERR(mac_str)) + uk_xb_free(mac_str); + +no_conf: + if (!PTRISERR(ip_str)) + uk_xb_free(ip_str); + + return err; +} + +void netfront_xb_fini(struct netfront_dev *netdev) +{ + struct xenbus_device *xendev; + + UK_ASSERT(netdev != NULL); + + xendev = netdev->xendev; + UK_ASSERT(xendev != NULL); + + if (xendev->otherend) { + uk_xb_free(xendev->otherend); + xendev->otherend = NULL; + } +} + +static int netfront_xb_front_init(struct netfront_dev *netdev, + xenbus_transaction_t xbt) +{ + struct xenbus_device *xendev = netdev->xendev; + int err; + + err = xs_printf(xbt, xendev->nodename, + "tx-ring-ref", "%u", netdev->ring_ref_tx); + if (err) + goto out; + + err = xs_printf(xbt, xendev->nodename, + "rx-ring-ref", "%u", netdev->ring_ref_rx); + if (err) + goto out; + + err = xs_printf(xbt, xendev->nodename, + "event-channel", "%u", netdev->evtchn); + if (err) + goto out; + + err = xs_printf(xbt, xendev->nodename, + "request-rx-copy", "%u", 1); + if (err) + goto out; + +out: + return err; +} + +static void netfront_xb_front_fini(struct netfront_dev *netdev, + xenbus_transaction_t xbt) +{ + struct xenbus_device *xendev = netdev->xendev; + char path[strlen(xendev->nodename) + sizeof("/event-channel")]; + + sprintf(path, "%s/tx-ring-ref", xendev->nodename); + xs_rm(xbt, path); + + sprintf(path, "%s/rx-ring-ref", xendev->nodename); + xs_rm(xbt, path); + + sprintf(path, "%s/event-channel", xendev->nodename); + xs_rm(xbt, path); +} + +int netfront_xb_connect(struct netfront_dev *netdev) +{ + struct xenbus_device *xendev; + xenbus_transaction_t xbt; + int retry = 0; + int err; + + UK_ASSERT(netdev != NULL); + + xendev = netdev->xendev; + UK_ASSERT(xendev != NULL); + +again: + err = xs_transaction_start(&xbt); + if (err) + goto abort_transaction; + + err = netfront_xb_front_init(netdev, xbt); + if (err) + goto abort_transaction; + + err = xenbus_switch_state(xendev, XenbusStateConnected, xbt); + if (err) + goto abort_transaction; + + err = xs_transaction_end(xbt, 0, &retry); + if (retry) + goto again; + + err = netfront_xb_wait_be_connect(netdev); + if (err) + netfront_xb_front_fini(netdev, xbt); + + return err; + +abort_transaction: + xs_transaction_end(xbt, 1, &retry); + + return err; +} + +int netfront_xb_disconnect(struct netfront_dev *netdev) +{ + struct xenbus_device *xendev; + int err; + + UK_ASSERT(netdev != NULL); + + xendev = netdev->xendev; + UK_ASSERT(xendev != NULL); + + uk_printd(DLVL_INFO, "Close network: backend at %s\n", + xendev->otherend); + + err = xenbus_switch_state(xendev, XenbusStateClosing, XBT_NIL); + if (err) + goto out; + + err = netfront_xb_wait_be_disconnect(netdev); + if (err) + goto out; + + netfront_xb_front_fini(netdev, XBT_NIL); + +out: + return err; +} + +static int be_watch_start(struct xenbus_device *xendev, const char *path) +{ + return xs_watch_path_token(XBT_NIL, path, path, &xendev->watch_events); +} + +static int be_watch_stop(const char *path) +{ + return xs_unwatch_path_token(XBT_NIL, path, path); +} + +#define WAIT_BE_STATE_CHANGE_WHILE_COND(state_cond) \ + do { \ + err = xs_read_integer(be_state_path, (int *) &be_state); \ + if (err) goto out; \ + while (!err && (state_cond)) \ + err = xenbus_wait_for_state_change(be_state_path, &be_state, \ + &xendev->watch_events); \ + if (err) goto out; \ + } while (0) + +static int netfront_xb_wait_be_connect(struct netfront_dev *netdev) +{ + struct xenbus_device *xendev = netdev->xendev; + char be_state_path[strlen(xendev->otherend) + sizeof("/state")]; + XenbusState be_state; + int err; + + sprintf(be_state_path, "%s/state", xendev->otherend); + + err = be_watch_start(xendev, be_state_path); + if (err) + goto out; + + WAIT_BE_STATE_CHANGE_WHILE_COND(be_state < XenbusStateConnected); + + if (be_state != XenbusStateConnected) { + uk_printd(DLVL_ERR, "Backend not available, state=%s\n", + xenbus_state_to_str(be_state)); + be_watch_stop(be_state_path); + } + +out: + return err; +} + +static int netfront_xb_wait_be_disconnect(struct netfront_dev *netdev) +{ + struct xenbus_device *xendev = netdev->xendev; + char be_state_path[strlen(xendev->otherend) + sizeof("/state")]; + XenbusState be_state; + int err; + + sprintf(be_state_path, "%s/state", xendev->otherend); + + WAIT_BE_STATE_CHANGE_WHILE_COND(be_state < XenbusStateClosing); + + err = xenbus_switch_state(xendev, XenbusStateClosed, XBT_NIL); + if (err) + goto out; + + WAIT_BE_STATE_CHANGE_WHILE_COND(be_state < XenbusStateClosed); + + err = xenbus_switch_state(xendev, XenbusStateInitialising, XBT_NIL); + if (err) + goto out; + + WAIT_BE_STATE_CHANGE_WHILE_COND(be_state < XenbusStateInitWait || + be_state >= XenbusStateClosed); + +out: + return err; +} -- 2.7.4 _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |