|
[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
Hey Sharan,
Thanks for looking at this!
> On 11. Feb 2019, at 18:42, Sharan Santhanam <Sharan.Santhanam@xxxxxxxxx>
> wrote:
>
> 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_STACK
>> if 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
Hum... I actually took the same function signature for polling as I saw it on
lwip code: slipif_poll() and netif_poll(). In this sense it is inline with what
someone would expect from lwip. At the same time, as long we do not have net if
remove support, a user can’t really do much in error cases.
How would you proceed?
>> +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 |