[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_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
+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
|