[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Minios-devel] [UNIKRAFT/LIBLWIP PATCH 3/5] Netif driver for libuknetdev devices
Hello Simon, Please find the review comments inline. Thanks & Regards Sharan On 2/8/19 12:00 AM, Simon Kuenzer wrote: Introduces a netif driver for libuknetdev devices. Packet buffers are allocated on the heap for now. Receive interrupts are supported and enabled when lwIP is operated in "threaded" mode. Zero-copy receive is implemented with packet buffers having two personalities: pbuf and netbuf. Signed-off-by: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> --- Config.uk | 17 ++ Makefile.uk | 2 + include/netif/uknetdev.h | 80 +++++ netbuf.c | 104 +++++++ netbuf.h | 107 +++++++ uknetdev.c | 611 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 921 insertions(+) create mode 100644 include/netif/uknetdev.h create mode 100644 netbuf.c create mode 100644 netbuf.h create mode 100644 uknetdev.c diff --git a/Config.uk b/Config.uk index dce5607..23813de 100644 --- a/Config.uk +++ b/Config.uk @@ -7,6 +7,23 @@ menuconfig LIBLWIP select HAVE_NW_STACKif LIBLWIP+menu "Netif drivers" +config LWIP_UKNETDEV + bool "Unikraft Netdev Ethernet (libuknetdev)" + default y + select LIBUKNETDEV + select LIBUKNETDEV_DISPATCHERTHREADS if LWIP_THREADS + help + A generic driver that operates network drivers through + libuknetdev API. + Note: When lwIP is configured with mainloop mode, receive + interrupts are not enabled by this driver (there is also no + receive interrupt handler registered). + In case threaded mode is selected and the underlying device + driver does not support receive interrupts the network + interfaces have to be polled manually (uknetdev_poll()). +endmenu + choice prompt "Operation mode" default LWIP_THREADS diff --git a/Makefile.uk b/Makefile.uk index ae3fbea..e7b7cc6 100644 --- a/Makefile.uk +++ b/Makefile.uk @@ -111,6 +111,8 @@ LIBLWIP_SRCS-$(CONFIG_LWIP_SOCKET) += $(LIBLWIP_EXTRACTED)/api/sockets.c # NETIFs ################################################################################ LIBLWIP_SRCS-y += $(LIBLWIP_EXTRACTED)/netif/ethernet.c +LIBLWIP_SRCS-$(CONFIG_LWIP_UKNETDEV) += $(LIBLWIP_BASE)/netbuf.c|unikraft +LIBLWIP_SRCS-$(CONFIG_LWIP_UKNETDEV) += $(LIBLWIP_BASE)/uknetdev.c|unikraft################################################################################# IPv4 diff --git a/include/netif/uknetdev.h b/include/netif/uknetdev.h new file mode 100644 index 0000000..77aaa6d --- /dev/null +++ b/include/netif/uknetdev.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> + * + * Copyright (c) 2019, NEC Laboratories Europe GmbH, 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 _LWIP_UKNETDEV_ +#define _LWIP_UKNETDEV_ + +#include <uk/config.h> +#include "lwip/opt.h" + +#if CONFIG_LWIP_UKNETDEV /* do not define anything if not configured for use */ + +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "netif/ethernet.h" +#include <uk/netdev.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Configures and brings up net device */ +err_t uknetdev_init(struct netif *nf); + +/* + * Note: When threading mode is enabled, only uknetdev netifs that do not + * support receive interrupts should be polled for packets. + */ +void uknetdev_poll(struct netif *nf); + +#ifdef CONFIG_LWIP_NOTHREADS +void uknetdev_poll_all(void); +#endif /* CONFIG_LWIP_NOTHREADS */ + +struct netif *uknetdev_addif(struct uk_netdev *n +#if LWIP_IPV4 + , + const ip4_addr_t *ipaddr, + const ip4_addr_t *netmask, + const ip4_addr_t *gw +#endif /* LWIP_IPV4 */ + ); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_LWIP_UKNETDEV */ +#endif /* _LWIP_NETDEV_ */ diff --git a/netbuf.c b/netbuf.c new file mode 100644 index 0000000..4878f74 --- /dev/null +++ b/netbuf.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> + * + * Copyright (c) 2019, NEC Laboratories Europe GmbH, 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 "netbuf.h" +#include <string.h> + +/** + * This function is called by lwip when our pbuf is free'd + * At this point, lwip should have no reference to the pbuf + * anymore, so we can just release the encapsulating netbuf + */ +static void _netbuf_free(struct pbuf *p) +{ + struct uk_netbuf *nb; + + nb = lwip_pbuf_to_netbuf(p); + uk_netbuf_free_single(nb); +} + +struct uk_netbuf *lwip_alloc_netbuf(struct uk_alloc *a, size_t alloc_size, + size_t headroom) +{ + void *allocation; + struct uk_netbuf *b; + struct _netbuf_pbuf *np; + + allocation = uk_malloc(a, alloc_size); + if (unlikely(!allocation)) + goto err_out; + + b = uk_netbuf_prepare_buf(allocation, alloc_size, + headroom, sizeof(struct _netbuf_pbuf), NULL); + if (unlikely(!b)) { + LWIP_DEBUGF(PBUF_DEBUG, + ("Failed to initialize netbuf with encapsulated pbuf: requested headroom: %"__PRIsz", alloc_size: %"__PRIsz"\n", + headroom, alloc_size)); + goto err_free_allocation; + } + + /* + * Register allocator so that uk_netbuf_free() will + * return our memory back to this allocator when free'ing + * this netbuf + */ + b->_a = a; + + /* Fill-out meta data */ + np = (struct _netbuf_pbuf *) uk_netbuf_get_priv(b); + memset(np, 0, sizeof(struct _netbuf_pbuf)); + np->pbuf_custom.pbuf.type_internal = PBUF_ROM; + np->pbuf_custom.pbuf.flags = PBUF_FLAG_IS_CUSTOM; + np->pbuf_custom.pbuf.payload = b->data; + np->pbuf_custom.pbuf.ref = 1; + np->pbuf_custom.custom_free_function = _netbuf_free; + np->netbuf = b; + + /* + * Set length of netbuf to available space so that it + * can be used as receive buffer + */ + b->len = b->buflen - headroom; + + LWIP_DEBUGF(PBUF_DEBUG, + ("Allocated netbuf with encapsulated pbuf %p (buflen: %"__PRIsz", headroom: %"__PRIsz")\n", + b, b->buflen, uk_netbuf_headroom(b))); + return b; + +err_free_allocation: + uk_free(a, allocation); +err_out: + return NULL; +} diff --git a/netbuf.h b/netbuf.h new file mode 100644 index 0000000..1492065 --- /dev/null +++ b/netbuf.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> + * + * Copyright (c) 2019, NEC Laboratories Europe GmbH, 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 _LWIP_NETBUF_ +#define _LWIP_NETBUF_ + +#include <uk/config.h> +#include "lwip/opt.h" + +#if CONFIG_LWIP_UKNETDEV /* do not define anything if not configured for use */ + +#include "lwip/pbuf.h" +#include <uk/netbuf.h> +#include <uk/alloc.h> +#include <uk/assert.h> +#include <uk/essentials.h> + +struct _netbuf_pbuf { + struct pbuf_custom pbuf_custom; + struct uk_netbuf *netbuf; +}; + +/** + * Allocates a netbuf that embeds a pbuf. Both will point + * to the same packet data buffer. + * Such buffers are used to implement zero-copy receive with the + * uknetdev API. + * NOTE: Make sure that netbuf and pbuf meta data are kept in sync + * before handing over the embedded pbuf to the network stack. + */ +struct uk_netbuf *lwip_alloc_netbuf(struct uk_alloc *a, size_t alloc_size, + size_t headroom); + +/** + * Returns the reference of the embedded pbuf of a netbuf + */ +static inline struct pbuf *lwip_netbuf_to_pbuf(struct uk_netbuf *b) +{ + struct _netbuf_pbuf *np; + + UK_ASSERT(b); + np = (struct _netbuf_pbuf *) uk_netbuf_get_priv(b); + UK_ASSERT(np); + + /* + * Sanity check: We use the PBUF_CUSTOM_FLAG as a way + * to identify lwip_alloc_netbuf() was allocating this pbuf. + * We know it may fail but it is still better than nothing. + */ + UK_ASSERT(np->pbuf_custom.pbuf.flags & PBUF_FLAG_IS_CUSTOM); + + return &np->pbuf_custom.pbuf; +} + +/** + * Returns the reference of the encapsulating netbuf of a pbuf + */ +static inline struct uk_netbuf *lwip_pbuf_to_netbuf(struct pbuf *p) +{ + struct pbuf_custom *pc; + struct _netbuf_pbuf *np; + + UK_ASSERT(p); + UK_ASSERT(p->flags & PBUF_FLAG_IS_CUSTOM); + + pc = (struct pbuf_custom *) __containerof(p, struct pbuf_custom, pbuf); + UK_ASSERT(pc); + np = (struct _netbuf_pbuf *) __containerof(pc, struct _netbuf_pbuf, + pbuf_custom); + UK_ASSERT(np); + return np->netbuf; +} + +#endif /* CONFIG_LWIP_UKNETDEV */ +#endif /* _LWIP_NETBUF_ */ diff --git a/uknetdev.c b/uknetdev.c new file mode 100644 index 0000000..1de202d --- /dev/null +++ b/uknetdev.c @@ -0,0 +1,611 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> + * + * Copyright (c) 2019, NEC Laboratories Europe GmbH, 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 <uk/config.h> +#include <stdio.h> +#include <string.h> + +#include <uk/alloc.h> +#include <uk/print.h> +#include "netif/uknetdev.h" +#include "netbuf.h" + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ethernet.h" +#include <uk/arch/atomic.h> + +#include <uk/essentials.h> + +#define UKNETDEV_BPS 1000000000u +#define UKNETDEV_BUFLEN 2048 + +#define UKNETDEV_NETIF_NAME0 'e' +#define UKNETDEV_NETIF_NAME1 'n' + +/* + * Global headroom settings for buffer allocations used on receive + * and transmit. We are taking the maximum of all uknetdev devices as + * simplification. + * TODO: A per-device setting might be more efficient but requires more data + * fields for `netif->state`. For now we point directly to the according + * `struct uk_netdev` in order to avoid another allocation for these + * per-device fields. + */ +static uint16_t rx_headroom = ETH_PAD_SIZE; +static uint16_t tx_headroom = ETH_PAD_SIZE; + +#define netif_to_uknetdev(nf) \ + ((struct uk_netdev *) (nf)->state) + +static uint16_t netif_alloc_rxpkts(void *argp, struct uk_netbuf *nb[], + uint16_t count) +{ + struct uk_alloc *a; + uint16_t i; + + UK_ASSERT(argp); + + a = (struct uk_alloc *) argp; + + for (i = 0; i < count; ++i) { + nb[i] = lwip_alloc_netbuf(a, UKNETDEV_BUFLEN, rx_headroom); + if (!nb[i]) { + /* we run out of memory */ + break; + } + } + + return i; +} + +static err_t uknetdev_output(struct netif *nf, struct pbuf *p) +{ + struct uk_alloc *a; + struct uk_netdev *dev; + struct pbuf *q; + struct uk_netbuf *nb; + void *allocation; + char *wpos; + int ret; + + UK_ASSERT(nf); + dev = netif_to_uknetdev(nf); + UK_ASSERT(dev); + + a = uk_alloc_get_default(); + if (!a) + return ERR_MEM; + + allocation = uk_malloc(a, UKNETDEV_BUFLEN); + if (!allocation) + return ERR_MEM; + nb = uk_netbuf_prepare_buf(allocation, UKNETDEV_BUFLEN, + tx_headroom, 0, NULL); + UK_ASSERT(nb); + nb->_a = a; /* register allocator for free operation */ + + if (unlikely(p->tot_len > uk_netbuf_tailroom(nb))) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Cannot send %"PRIu16" bytes, too big (> %"__PRIsz")\n", + __func__, nf->name[0], nf->name[1], nf->num, + p->tot_len, uk_netbuf_tailroom(nb))); + uk_netbuf_free_single(nb); + return ERR_MEM; + } + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif /* ETH_PAD_SIZE */ + /* + * Copy pbuf to netbuf + * NOTE: Unfortunately, lwIP seems not to support zero-copy transmit, + * yet. As long as we do not have this, we have to copy. + */ + wpos = nb->data; + for (q = p; q != NULL; q = p->next) { + memcpy(wpos, q->payload, q->len); + wpos += q->len; + } + nb->len = p->tot_len; +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif /* ETH_PAD_SIZE */ + + /* Transmit packet */ + do { + ret = uk_netdev_tx_one(dev, 0, nb); + } while (uk_netdev_status_notready(ret)); + if (unlikely(ret < 0)) { +#if ETH_PAD_SIZE + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Failed to send %"PRIu16" bytes\n", + __func__, nf->name[0], nf->name[1], nf->num, + p->tot_len - ETH_PAD_SIZE)); +#else /* ETH_PAD_SIZE */ + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Failed to send %"PRIu16" bytes\n", + __func__, nf->name[0], nf->name[1], nf->num, + p->tot_len)); +#endif /* ETH_PAD_SIZE */ + /* + * Decrease refcount again because in + * the error case the netdev did not consume the pbuf + */ + uk_netbuf_free_single(nb); + return ERR_IF; + } +#if ETH_PAD_SIZE + LWIP_DEBUGF(NETIF_DEBUG, ("%s: %c%c%u: Sent %"PRIu16" bytes\n", + __func__, nf->name[0], nf->name[1], nf->num, + p->tot_len - ETH_PAD_SIZE)); +#else /* ETH_PAD_SIZE */ + LWIP_DEBUGF(NETIF_DEBUG, ("%s: %c%c%u: Sent %"PRIu16" bytes\n", + __func__, nf->name[0], nf->name[1], nf->num, + p->tot_len)); +#endif /* ETH_PAD_SIZE */ + + return ERR_OK; +} + +static void uknetdev_input(struct uk_netdev *dev, + uint16_t queue_id __unused, void *argp) +{ + struct netif *nf = (struct netif *) argp; + struct uk_netbuf *nb; + struct pbuf *p; + int ret; + + UK_ASSERT(dev); + UK_ASSERT(nf); + UK_ASSERT(nf->input); + + LWIP_DEBUGF(NETIF_DEBUG, ("%s: %c%c%u: Poll receive queue...\n", + __func__, nf->name[0], nf->name[1], nf->num)); + do { + ret = uk_netdev_rx_one(dev, 0, &nb); + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Input status %d (%c%c%c)\n", + __func__, nf->name[0], nf->name[1], nf->num, ret, + uk_netdev_status_test_set(ret, + UK_NETDEV_STATUS_SUCCESS) + ? 'S' : '-', + uk_netdev_status_test_set(ret, + UK_NETDEV_STATUS_MORE) + ? 'M' : '-', + uk_netdev_status_test_set(ret, + UK_NETDEV_STATUS_UNDERRUN) + ? 'U' : '-')); + if (unlikely(ret < 0)) { + /* + * Ouch, an error happened. We cannot recover from it + * currently, so we will throw an error message, bring + * the interface down, and leave our loop. + */ + uk_pr_crit("%c%c%u: Receive error %d. Stopping interface...\n", + nf->name[0], nf->name[1], nf->num, ret); + netif_set_down(nf); + break; + } + if (uk_netdev_status_notready(ret)) { + /* No (more) packets received */ + break; + } + + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Received %"PRIu16" bytes\n", + __func__, nf->name[0], nf->name[1], nf->num, + nb->len)); + + /* Send packet to lwip */ +#if ETH_PAD_SIZE + uk_netbuf_header(nb, ETH_PAD_SIZE); +#endif /* ETH_PAD_SIZE */ + p = lwip_netbuf_to_pbuf(nb); + p->payload = nb->data; + p->tot_len = p->len = nb->len; + if (unlikely(nf->input(p, nf) != ERR_OK)) { + /* + * Drop the packet that we could not send to the stack + */ + uk_pr_err("%c%c%u: Failed to forward packet to lwIP\n", + nf->name[0], nf->name[1], nf->num); + uk_netbuf_free_single(nb); + } + } while (uk_netdev_status_more(ret)); +} + Would be better if poll function returns an error. The user of the poll as of now is blind in case rx_one fails +void uknetdev_poll(struct netif *nf) +{ + struct uk_netdev *dev; + + UK_ASSERT(nf); + /* + * TODO: Unfortunately, checking the interface name is a weak sanity + * check that uknetdev_poll() is called on a netif that is driven by + * this driver... + */ + UK_ASSERT(nf->name[0] == UKNETDEV_NETIF_NAME0); + UK_ASSERT(nf->name[1] == UKNETDEV_NETIF_NAME1); + + dev = netif_to_uknetdev(nf); + UK_ASSERT(dev); + + uknetdev_input(dev, 0, nf); +} + +#ifdef CONFIG_LWIP_NOTHREADS +void uknetdev_poll_all(void) +{ + struct netif *nf; + + /* + * TODO: We are going through all netifs and check the interface name. + * This way we are figuring out that the netif is provided by our + * driver. Probably we should find a better and more stable solution at + * some point... + */ + NETIF_FOREACH(nf) { + if (nf->name[0] == UKNETDEV_NETIF_NAME0 + && nf->name[1] == UKNETDEV_NETIF_NAME1) + uknetdev_poll(nf); + } +} + +#else /* CONFIG_LWIP_NOTHREADS */ + +static void uknetdev_updown(struct netif *nf) +{ + struct uk_netdev *dev; + int ret; + + UK_ASSERT(nf); + dev = netif_to_uknetdev(nf); + UK_ASSERT(dev); + + /* Enable and disable interrupts according to netif's up/down status */ + if (nf->flags & NETIF_FLAG_UP) { + ret = uk_netdev_rxq_intr_enable(dev, 0); + if (ret < 0) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Failed to enable rx interrrupt mode on netdev %u\n", + __func__, nf->name[0], nf->name[1], + nf->num, uk_netdev_id_get(dev))); + } else { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Enabled rx interrupt mode on netdev %u\n", + __func__, nf->name[0], nf->name[1], + nf->num, uk_netdev_id_get(dev))); + } + + if (ret == 1) { + /* + * uk_netdev_rxq_intr_enable() told us that we need to + * flush the receieve queue before interrupts are + * enabled. For this purpose we do an initial poll. + */ + uknetdev_poll(nf); + } + } else { + uk_netdev_rxq_intr_disable(dev, 0); + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Disabled rx interrupts on netdev %u\n", + __func__, nf->name[0], nf->name[1], + nf->num, uk_netdev_id_get(dev))); + + } +} +#endif /* CONFIG_LWIP_NOTHREADS */ + +err_t uknetdev_init(struct netif *nf) +{ + struct uk_alloc *a = NULL; + struct uk_netdev *dev; + struct uk_netdev_conf dev_conf; + struct uk_netdev_rxqueue_conf rxq_conf; + struct uk_netdev_txqueue_conf txq_conf; + struct uk_netdev_info info; + const struct uk_hwaddr *hwaddr; + unsigned int i; + int ret; + + UK_ASSERT(nf); + dev = netif_to_uknetdev(nf); + UK_ASSERT(dev); + + LWIP_ASSERT("uknetdev needs an input callback (netif_input or tcpip_input)", + nf->input != NULL); + + /* Netdev has to be in unconfigured state */ + if (uk_netdev_state_get(dev) != UK_NETDEV_UNCONFIGURED) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: Netdev %u not in uncofigured state\n", + __func__, uk_netdev_id_get(dev))); + return ERR_ISCONN; + } + + /* Interface name, the interface number (nf->num) is assigned by lwip */ + nf->name[0] = UKNETDEV_NETIF_NAME0; + nf->name[1] = UKNETDEV_NETIF_NAME1; + + /* + * Bring up uknetdev + * Note: We use the default allocator for setting up the rx/tx queues + */ + /* TODO: In case the device initialization should happen manually before + * attaching to lwip, we require another init function that skips + * this initialization steps. + */ + a = uk_alloc_get_default(); + if (!a) + return ERR_MEM; + + /* Get device information */ + uk_netdev_info_get(dev, &info); + if (!info.max_rx_queues || !info.max_tx_queues) + return ERR_IF; + + /* + * Update our global (rx|tx)_headroom setting that we use for + * buffer allocations + */ + rx_headroom = (rx_headroom < info.nb_encap_rx) + ? info.nb_encap_rx : rx_headroom; + tx_headroom = (tx_headroom < info.nb_encap_tx) + ? info.nb_encap_tx : tx_headroom; + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Need headroom rx:%"PRIu16", tx:%"PRIu16"\n", + __func__, nf->name[0], nf->name[1], nf->num, + info.nb_encap_rx, info.nb_encap_tx)); + + /* + * Device configuration, + * we want to use just one queue for each direction + */ + dev_conf.nb_rx_queues = 1; + dev_conf.nb_tx_queues = 1; + ret = uk_netdev_configure(dev, &dev_conf); + if (ret < 0) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Failed to configure netdev %u\n", + __func__, nf->name[0], nf->name[1], nf->num, + uk_netdev_id_get(dev))); + return ERR_IF; + } + + /* + * Receive queue, + * use driver default descriptors + */ + rxq_conf.a = a; + rxq_conf.alloc_rxpkts = netif_alloc_rxpkts; + rxq_conf.alloc_rxpkts_argp = a; +#ifdef CONFIG_LWIP_NOTHREADS + /* + * In mainloop mode, we will not use interrupts. + */ + rxq_conf.callback = NULL; + rxq_conf.callback_cookie = NULL; +#else /* CONFIG_LWIP_NOTHREADS */ + rxq_conf.callback = uknetdev_input; + rxq_conf.callback_cookie = nf; +#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS + rxq_conf.s = uk_sched_get_default(); + if (!rxq_conf.s) + return ERR_IF; + +#endif /* CONFIG_LIBUKNETDEV_DISPATCHERTHREADS */ +#endif /* CONFIG_LWIP_NOTHREADS */ + ret = uk_netdev_rxq_configure(dev, 0, 0, &rxq_conf); + if (ret < 0) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Failed to configure rx queue of netdev %u\n", + __func__, nf->name[0], nf->name[1], nf->num, + uk_netdev_id_get(dev))); + return ERR_IF; + } + + /* + * Transmit queue, + * use driver default descriptors + */ + txq_conf.a = a; + ret = uk_netdev_txq_configure(dev, 0, 0, &txq_conf); + if (ret < 0) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Failed to configure tx queue of netdev %u\n", + __func__, nf->name[0], nf->name[1], nf->num, + uk_netdev_id_get(dev))); + return ERR_IF; + } + + /* Start interface */ + ret = uk_netdev_start(dev); + if (ret < 0) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Failed to start netdev %u\n", + __func__, nf->name[0], nf->name[1], nf->num, + uk_netdev_id_get(dev))); + return ERR_IF; + } + + /* Driver callbacks */ +#if LWIP_IPV4 + nf->output = etharp_output; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + nf->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ + nf->linkoutput = uknetdev_output; + + /* TODO: Set remove callback */ + + /* Device capabilities */ + netif_set_flags(nf, (NETIF_FLAG_BROADCAST + | NETIF_FLAG_ETHARP + | NETIF_FLAG_LINK_UP)); + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: flags: %"PRIx8"\n", + __func__, nf->name[0], nf->name[1], nf->num, nf->flags)); + +#if LWIP_CHECKSUM_CTRL_PER_NETIF + /* + * Checksum settings + * TODO: libuknetdev does not support checksum capabilities yet. + * Because of this, we need to calculate the checksum for every + * outgoing packet in software. We assume that we receive packets + * from a virtual interface, so the host was doing a check for us + * already. In case of guest-to-guest communication, the checksum + * field may be incorrect because the other guest expects that the + * host is offloading the calculation to hardware as soon as a + * packet leaves the physical host machine. At this point, the + * best we can do is not to check any checksums on incoming + * traffic and assume everything is fine. + */ + NETIF_SET_CHECKSUM_CTRL(nf, (NETIF_CHECKSUM_GEN_IP + | NETIF_CHECKSUM_GEN_UDP + | NETIF_CHECKSUM_GEN_TCP + | NETIF_CHECKSUM_GEN_ICMP + | NETIF_CHECKSUM_GEN_ICMP6)); + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: chksum_flags: %"PRIx16"\n", + __func__, nf->name[0], nf->name[1], nf->num, + nf->chksum_flags)); +#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */ + + /* MAC address */ + UK_ASSERT(NETIF_MAX_HWADDR_LEN >= UK_NETDEV_HWADDR_LEN); + hwaddr = uk_netdev_hwaddr_get(dev); + UK_ASSERT(hwaddr); + nf->hwaddr_len = UK_NETDEV_HWADDR_LEN; + for (i = 0; i < UK_NETDEV_HWADDR_LEN; ++i) + nf->hwaddr[i] = hwaddr->addr_bytes[i]; +#if UK_NETDEV_HWADDR_LEN == 6 + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Hardware address: %02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8"\n", + __func__, nf->name[0], nf->name[1], nf->num, + nf->hwaddr[0], nf->hwaddr[1], nf->hwaddr[2], + nf->hwaddr[3], nf->hwaddr[4], nf->hwaddr[5])); +#else /* UK_NETDEV_HWADDR_LEN */ + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Hardware address set\n", + __func__, nf->name[0], nf->name[1], nf->num)); +#endif /* UK_NETDEV_HWADDR_LEN */ + + /* Maximum transfer unit */ + nf->mtu = uk_netdev_mtu_get(dev); + UK_ASSERT(nf->mtu); + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: MTU: %u\n", + __func__, nf->name[0], nf->name[1], nf->num, + nf->mtu)); + +#ifndef CONFIG_LWIP_NOTHREADS + /* + * We will use the status update callback to enable and disabled + * receive queue interrupts + */ + netif_set_status_callback(nf, uknetdev_updown); +#endif /* !CONFIG_LWIP_NOTHREADS */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument is the link speed, in units of bits per second. + */ + NETIF_INIT_SNMP(nf, snmp_ifType_ethernet_csmacd, UKNETDEV_BPS); + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: %c%c%u: Link speed: %"PRIu32" bps\n", + __func__, nf->name[0], nf->name[1], nf->num, + UKNETDEV_BPS)); + + return ERR_OK; +} + +#if CONFIG_LWIP_NOTHREADS +#define NETIF_INPUT ethernet_input +#else /* CONFIG_LWIP_NOTHREADS */ +#define NETIF_INPUT tcpip_input +#endif /*CONFIG_LWIP_NOTHREADS */ + +struct netif *uknetdev_addif(struct uk_netdev *n +#if LWIP_IPV4 + , + const ip4_addr_t *ipaddr, + const ip4_addr_t *netmask, + const ip4_addr_t *gw +#endif /* LWIP_IPV4 */ + ) +{ + /* + * This pointer and UK_READ_ONCE on it is an ugly workaround + * against a pretty weird problem. Without it, the last + * parameter of netif_add passed as NULL. It seems to be a + * build time problem because: + * + * - Moving "input" parameter to the first positions helps + * - Removing one parameter helps. Seems does not matter which + * - An extra parameter added after "input" works well. And + * input is still NULL in this case + * - Swapping "init" and "input" helps + */ + static const void *pethernet_input = NETIF_INPUT; + struct netif *nf; + struct netif *ret; + + nf = mem_calloc(1, sizeof(*nf)); + if (!nf) + return NULL; + + ret = netif_add(nf, +#if LWIP_IPV4 + ipaddr, netmask, gw, +#endif /* LWIP_IPV4 */ + n, uknetdev_init, UK_READ_ONCE(pethernet_input)); + UK_ASSERT(nf->input); + + if (!ret) { + mem_free(nf); + return NULL; + } + + return ret; +} _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |