[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT PATCH v5 07/11] lib/uknetdev: Netdev initialization



From: Razvan Cojocaru <razvan.cojocaru93@xxxxxxxxx>

Introduce a 'DPDK RTE Ethernet' inspired API for initializing a
Unikraft network device. The initialization is done in the following
order: (1) Configure main aspects of device (e.g., number of
queues), (2) configure each transmit and receive queue, and (3) start
the device. We also introduce an interface for querying the underlying
device capabilities for each configuration step.

Signed-off-by: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
Signed-off-by: Razvan Cojocaru <razvan.cojocaru93@xxxxxxxxx>
---
 lib/uknetdev/Config.uk                |  28 +++-
 lib/uknetdev/exportsyms.uk            |   8 +
 lib/uknetdev/include/uk/netdev.h      | 174 ++++++++++++++++++++
 lib/uknetdev/include/uk/netdev_core.h | 196 +++++++++++++++++++++++
 lib/uknetdev/netdev.c                 | 290 ++++++++++++++++++++++++++++++++++
 5 files changed, 695 insertions(+), 1 deletion(-)

diff --git a/lib/uknetdev/Config.uk b/lib/uknetdev/Config.uk
index 878b822..618e8e6 100644
--- a/lib/uknetdev/Config.uk
+++ b/lib/uknetdev/Config.uk
@@ -1,6 +1,32 @@
-config LIBUKNETDEV
+menuconfig LIBUKNETDEV
        bool "uknetdev: Network driver interface"
        default n
        select LIBNOLIBC if !HAVE_LIBC
        select LIBUKDEBUG
        select LIBUKALLOC
+
+if LIBUKNETDEV
+       config LIBUKNETDEV_MAXNBQUEUES
+               int "Maximum number of receive-transmit queue pairs"
+               default 1
+               help
+                       Upper limit for supported number of transmit and receive
+                       queues with the uknetdev API. Please note that drivers
+                       have their own limits (use API getters to figure out
+                       device capabilities). As example, one driver may support
+                       only a single receive-transmit queue pair although
+                       uknetdev would support 16.
+
+       config LIBUKNETDEV_DISPATCHERTHREADS
+               bool "Dispatcher threads for event callbacks"
+               select LIBUKSCHED
+               select LIBUKLOCK
+               select LIBUKLOCK_SEMAPHORE
+               default n
+               help
+                       Event callbacks are dispatched in a bottom half
+                       thread context instead of the device interrupt context.
+                       When this option is enabled a dispatcher thread is
+                       allocated for each configured receive queue.
+                       libuksched is required for this option.
+endif
diff --git a/lib/uknetdev/exportsyms.uk b/lib/uknetdev/exportsyms.uk
index 96b5bfd..4e4d306 100644
--- a/lib/uknetdev/exportsyms.uk
+++ b/lib/uknetdev/exportsyms.uk
@@ -13,3 +13,11 @@ uk_netdev_get
 uk_netdev_id_get
 uk_netdev_drv_name_get
 uk_netdev_state_get
+uk_netdev_info_get
+uk_netdev_einfo_get
+uk_netdev_rxq_info_get
+uk_netdev_txq_info_get
+uk_netdev_configure
+uk_netdev_rxq_configure
+uk_netdev_txq_configure
+uk_netdev_start
diff --git a/lib/uknetdev/include/uk/netdev.h b/lib/uknetdev/include/uk/netdev.h
index 75a8e66..a3bd44a 100644
--- a/lib/uknetdev/include/uk/netdev.h
+++ b/lib/uknetdev/include/uk/netdev.h
@@ -52,6 +52,22 @@
  * Unikraft Network Device (struct uk_netdev) which can be initially obtained
  * by its ID by calling uk_netdev_get(). The network application should store
  * this reference and use it for subsequent API calls.
+ *
+ * The functions exported by the Unikraft NET API to setup a device
+ * must be invoked in the following order:
+ *     - uk_netdev
+ *     - uk_netdev_configure()
+ *     - uk_netdev_txq_configure()
+ *     - uk_netdev_rxq_configure()
+ *     - uk_netdev_start()
+ * The transmit and receive functions should not be invoked when the
+ * device is not started.
+ *
+ * There are 4 states in which a network device can be found:
+ *     - UK_NETDEV_UNREGISTERED
+ *     - UK_NETDEV_UNCONFIGURED
+ *     - UK_NETDEV_CONFIGURED
+ *     - UK_NETDEV_RUNNING
  */
 #ifdef __cplusplus
 extern "C" {
@@ -110,6 +126,164 @@ const char *uk_netdev_drv_name_get(struct uk_netdev *dev);
  */
 enum uk_netdev_state uk_netdev_state_get(struct uk_netdev *dev);
 
+/**
+ * Query device capabilities.
+ * Information that is useful for device initialization (e.g.,
+ * maximum number of supported RX/TX queues).
+ *
+ * @param dev
+ *   The Unikraft Network Device.
+ * @param dev_info
+ *   A pointer to a structure of type *uk_netdev_info* to be filled with
+ *   the contextual information of the network device.
+ */
+void uk_netdev_info_get(struct uk_netdev *dev,
+                       struct uk_netdev_info *dev_info);
+
+/**
+ * Extra information query interface.
+ * The user can query the driver for any additional information (e.g,
+ * IP address configuration preferred by hypervisor), using a number of
+ * pre-defined configuration types.
+ *
+ * If the driver doesn't support the provided data type, it must return NULL.
+ *
+ * This allows the driver to provide configuration data without the need of
+ * parsing it in a pre-determined way, eliminating the need for utility
+ * functions in the API, or parsing the data multiple times both by driver
+ * and user.
+ *
+ * @param dev
+ *   The Unikraft Network Device.
+ * @param einfo
+ *   Extra configuration type selection (see enum definition).
+ * @return
+ *   - (NULL): if configuration unavailable or data type unsupported
+ *   - (void *): Reference to configuration, format specified by *einfo*
+ */
+const void *uk_netdev_einfo_get(struct uk_netdev *dev,
+                               enum uk_netdev_einfo_type einfo);
+
+/**
+ * Configures an Unikraft network device.
+ *
+ * @param dev
+ *   The Unikraft Network Device in unconfigured state.
+ * @param conf
+ *   The pointer to the configuration data to be used for the Unikraft
+ *   network device.
+ * @return
+ *   - (0): Success, device is in configured state.
+ *   - (<0): Error code returned by the driver.
+ */
+int uk_netdev_configure(struct uk_netdev *dev,
+                       const struct uk_netdev_conf *dev_conf);
+
+
+/**
+ * Query receive device queue capabilities.
+ * Information that is useful for device queue initialization (e.g.,
+ * maximum number of supported descriptors on RX queues).
+ *
+ * @param dev
+ *   The Unikraft Network Device in configured state.
+ * @param queue_id
+ *   The index of the receive queue to set up.
+ *   The value must be in the range [0, nb_rx_queue - 1] previously supplied
+ *   to uk_netdev_configure().
+ * @param queue_info
+ *   A pointer to a structure of type *uk_netdev_queues_info* to be filled out
+ * @return
+ *   - (0): Success, queue_info is filled out.
+ *   - (<0): Error code of the drivers function.
+ */
+int uk_netdev_rxq_info_get(struct uk_netdev *dev, uint16_t queue_id,
+                          struct uk_netdev_queue_info *queue_info);
+
+/**
+ * Sets up one receive queue for an Unikraft network device.
+ *
+ * @param dev
+ *   The Unikraft Network Device in configured state.
+ * @param queue_id
+ *   The index of the receive queue to set up.
+ *   The value must be in the range [0, nb_rx_queue - 1] previously supplied
+ *   to uk_netdev_configure().
+ * @param nb_desc
+ *   Number of descriptors for the queue. Inspect uk_netdev_rxq_info_get() to
+ *   retrieve limitations. If nb_desc is set to 0, the driver chooses a default
+ *   value.
+ * @param rx_conf
+ *   The pointer to the configuration data to be used for the receive queue.
+ *   Its memory can be released after invoking this function.
+ * @return
+ *   - (0): Success, receive queue correctly set up.
+ *   - (-ENOMEM): Unable to allocate the receive ring descriptors.
+ */
+int uk_netdev_rxq_configure(struct uk_netdev *dev, uint16_t queue_id,
+                           uint16_t nb_desc,
+                           struct uk_netdev_rxqueue_conf *rx_conf);
+
+/**
+ * Query device transmit queue capabilities.
+ * Information that is useful for device queue initialization (e.g.,
+ * maximum number of supported descriptors on TX queues).
+ *
+ * @param dev
+ *   The Unikraft Network Device in configured state.
+ * @param queue_id
+ *   The index of the receive queue to set up.
+ *   The value must be in the range [0, nb_tx_queue - 1] previously supplied
+ *   to uk_netdev_configure().
+ * @param queue_info
+ *   A pointer to a structure of type *uk_netdev_queues_info* to be filled out
+ * @return
+ *   - (0): Success, queue_info is filled out.
+ *   - (<0): Error code of the drivers function.
+ */
+int uk_netdev_txq_info_get(struct uk_netdev *dev, uint16_t queue_id,
+                          struct uk_netdev_queue_info *queue_info);
+
+/**
+ * Sets up one transmit queue for an Unikraft network device.
+ *
+ * @param dev
+ *   The Unikraft Network Device in configured state.
+ * @param queue_id
+ *   The index of the transmit queue to set up.
+ *   The value must be in the range [0, nb_tx_queue - 1] previously supplied
+ *   to uk_netdev_configure().
+ * @param nb_desc
+ *   Number of descriptors for the queue. Inspect uk_netdev_txq_info_get() to
+ *   retrieve limitations. If nb_desc is set to 0, the driver chooses a default
+ *   value.
+ * @param tx_conf
+ *   The pointer to the configuration data to be used for the transmit queue.
+ *   Its memory can be released after invoking this function.
+ * @return
+ *   - (0): Success, the transmit queue is correctly set up.
+ *   - (-ENOMEM): Unable to allocate the transmit ring descriptors.
+ */
+int uk_netdev_txq_configure(struct uk_netdev *dev, uint16_t queue_id,
+                           uint16_t nb_desc,
+                           struct uk_netdev_txqueue_conf *tx_conf);
+
+/**
+ * Start a Network device.
+ *
+ * After a network device was configured and its queues are set up the device
+ * can be started. On success, all operational functions exported by the
+ * Unikraft netdev API (interrupts, receive/transmit, and so on) can be invoked
+ * afterwards.
+ *
+ * @param dev
+ *   The Unikraft Network Device in configured state.
+ * @return
+ *   - (0): Success, Unikraft network device started.
+ *   - (<0): Error code of the driver device start function.
+ */
+int uk_netdev_start(struct uk_netdev *dev);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/uknetdev/include/uk/netdev_core.h 
b/lib/uknetdev/include/uk/netdev_core.h
index 31875d1..6339c6a 100644
--- a/lib/uknetdev/include/uk/netdev_core.h
+++ b/lib/uknetdev/include/uk/netdev_core.h
@@ -45,6 +45,10 @@
 #include <uk/list.h>
 #include <uk/alloc.h>
 #include <uk/essentials.h>
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+#include <uk/sched.h>
+#include <uk/semaphore.h>
+#endif
 
 /**
  * Unikraft network API common declarations.
@@ -64,6 +68,42 @@ extern "C" {
 struct uk_netdev;
 UK_TAILQ_HEAD(uk_netdev_list, struct uk_netdev);
 
+/**
+ * A structure used to describe network device capabilities.
+ */
+struct uk_netdev_info {
+       uint16_t max_rx_queues;
+       uint16_t max_tx_queues;
+       int in_queue_pairs; /**< If true, allocate queues in pairs. */
+       uint16_t max_mtu;   /**< Maximum supported MTU size. */
+       uint16_t nb_encap;  /**< Number of bytes required as headroom for tx. */
+};
+
+/**
+ * A structure used to describe device descriptor ring limitations.
+ */
+struct uk_netdev_queue_info {
+       uint16_t nb_max;        /**< Max allowed number of descriptors. */
+       uint16_t nb_min;        /**< Min allowed number of descriptors. */
+       uint16_t nb_align;      /**< Number should be a multiple of nb_align. */
+       int nb_is_power_of_two; /**< Number should be a power of two. */
+};
+
+/**
+ * A structure used to configure a network device.
+ */
+struct uk_netdev_conf {
+       uint16_t nb_rx_queues;
+       uint16_t nb_tx_queues;
+};
+
+/**
+ * @internal Queue structs that are defined internally by each driver
+ * The datatype is introduced here for having type checking on the
+ * API code
+ */
+struct uk_netdev_tx_queue;
+struct uk_netdev_rx_queue;
 
 /**
  * Enum to describe possible states of an Unikraft network device.
@@ -75,6 +115,149 @@ enum uk_netdev_state {
        UK_NETDEV_RUNNING,
 };
 
+/**
+ * Enum used by the extra information query interface.
+ *
+ * The purpose of this type is to allow drivers to forward extra configurations
+ * options such as IP information without parsing this data by themselves 
(e.g.,
+ * strings of IP address and mask found on XenStore by netfront).
+ * We do not want to introduce any additional parsing logic inside uknetdev API
+ * because we assume that most network stacks provide this functionality
+ * anyways. So one could forward this data within the glue code.
+ *
+ * This list is extensible in the future without needing the drivers to adopt
+ * any or all of the data types.
+ *
+ * The extra information can available in one of the following formats:
+ * - *_NINT16: Network-order raw int (4 bytes)
+ * - *_STR: Null-terminated string
+ */
+enum uk_netdev_einfo_type {
+       /* IPv4 address and mask */
+       UK_NETDEV_IPV4_ADDR_NINT16,
+       UK_NETDEV_IPV4_ADDR_STR,
+       UK_NETDEV_IPV4_MASK_NINT16,
+       UK_NETDEV_IPV4_MASK_STR,
+
+       /* IPv4 gateway */
+       UK_NETDEV_IPV4_GW_NINT16,
+       UK_NETDEV_IPV4_GW_STR,
+
+       /* IPv4 Primary DNS */
+       UK_NETDEV_IPV4_DNS0_NINT16,
+       UK_NETDEV_IPV4_DNS0_STR,
+
+       /* IPv4 Secondary DNS */
+       UK_NETDEV_IPV4_DNS1_NINT16,
+       UK_NETDEV_IPV4_DNS1_STR,
+};
+
+/**
+ * Function type used for queue event callbacks.
+ *
+ * @param dev
+ *   The Unikraft Network Device.
+ * @param queue
+ *   The queue on the Unikraft network device on which the event happened.
+ * @param argp
+ *   Extra argument that can be defined on callback registration.
+ */
+typedef void (*uk_netdev_queue_event_t)(struct uk_netdev *dev,
+                                       uint16_t queue_id, void *argp);
+
+/**
+ * A structure used to configure an Unikraft network device RX queue.
+ */
+struct uk_netdev_rxqueue_conf {
+       uk_netdev_queue_event_t callback; /**< Event callback function. */
+       void *callback_cookie;            /**< Argument pointer for callback. */
+
+       struct uk_alloc *a;               /**< Allocator for descriptors. */
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+       struct uk_sched *s;               /**< Scheduler for dispatcher. */
+#endif
+};
+
+/**
+ * A structure used to configure an Unikraft network device TX queue.
+ */
+struct uk_netdev_txqueue_conf {
+       struct uk_alloc *a;               /* Allocator for descriptors. */
+};
+
+/** Driver callback type to read device/driver capabilities,
+ *  used for configuring the device
+ */
+typedef void (*uk_netdev_info_get_t)(struct uk_netdev *dev,
+                                    struct uk_netdev_info *dev_info);
+
+/** Driver callback type to read any extra configuration. */
+typedef const void *(*uk_netdev_einfo_get_t)(struct uk_netdev *dev,
+                                            enum uk_netdev_einfo_type econf);
+
+/** Driver callback type to configure a network device. */
+typedef int  (*uk_netdev_configure_t)(struct uk_netdev *dev,
+                                     const struct uk_netdev_conf *conf);
+
+/** Driver callback type to retrieve RX queue limitations,
+ *  used for configuring the RX queue later
+ */
+typedef int (*uk_netdev_rxq_info_get_t)(struct uk_netdev *dev,
+       uint16_t queue_id, struct uk_netdev_queue_info *queue_info);
+
+/** Driver callback type to retrieve TX queue limitations,
+ *  used for configuring the TX queue later
+ */
+typedef int (*uk_netdev_txq_info_get_t)(struct uk_netdev *dev,
+       uint16_t queue_id, struct uk_netdev_queue_info *queue_info);
+
+/** Driver callback type to set up a RX queue of an Unikraft network device. */
+typedef struct uk_netdev_tx_queue * (*uk_netdev_txq_configure_t)(
+       struct uk_netdev *dev, uint16_t queue_id, uint16_t nb_desc,
+       struct uk_netdev_txqueue_conf *tx_conf);
+
+/** Driver callback type to set up a TX queue of an Unikraft network device. */
+typedef struct uk_netdev_rx_queue * (*uk_netdev_rxq_configure_t)(
+       struct uk_netdev *dev, uint16_t queue_id, uint16_t nb_desc,
+       struct uk_netdev_rxqueue_conf *rx_conf);
+
+/** Driver callback type to start a configured Unikraft network device. */
+typedef int  (*uk_netdev_start_t)(struct uk_netdev *dev);
+
+/**
+ * A structure containing the functions exported by a driver.
+ */
+struct uk_netdev_ops {
+       /** Device/driver capabilities and info. */
+       uk_netdev_info_get_t            info_get;
+       uk_netdev_txq_info_get_t        txq_info_get;
+       uk_netdev_rxq_info_get_t        rxq_info_get;
+       uk_netdev_einfo_get_t           einfo_get;        /* optional */
+
+       /** Device life cycle. */
+       uk_netdev_configure_t           configure;
+       uk_netdev_txq_configure_t       txq_configure;
+       uk_netdev_rxq_configure_t       rxq_configure;
+       uk_netdev_start_t               start;
+};
+
+/**
+ * @internal
+ * Event handler configuration (internal to libuknetdev)
+ */
+struct uk_netdev_event_handler {
+       uk_netdev_queue_event_t callback;
+       void                    *cookie;
+
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+       struct uk_semaphore events;      /**< semaphore to trigger events */
+       struct uk_netdev    *dev;        /**< reference to net device */
+       uint16_t            queue_id;    /**< queue id which caused event */
+       struct uk_thread    *dispatcher; /**< dispatcher thread */
+       char                *dispatcher_name; /**< reference to thread name */
+       struct uk_sched     *dispatcher_s;    /**< Scheduler for dispatcher. */
+#endif
+};
 
 /**
  * @internal
@@ -83,6 +266,9 @@ enum uk_netdev_state {
 struct uk_netdev_data {
        enum uk_netdev_state state;
 
+       struct uk_netdev_event_handler
+                            rxq_handler[CONFIG_LIBUKNETDEV_MAXNBQUEUES];
+
        const uint16_t       id;    /**< ID is assigned during registration */
        const char           *drv_name;
 };
@@ -90,11 +276,21 @@ struct uk_netdev_data {
 /**
  * NETDEV
  * A structure used to interact with a network device.
+ *
+ * Function callbacks (ops) are registered by the driver before
+ * registering the netdev. They change during device life time.
  */
 struct uk_netdev {
        /** Pointer to API-internal state data. */
        struct uk_netdev_data       *_data;
 
+       /** Functions callbacks by driver. */
+       const struct uk_netdev_ops  *ops;   /* by driver */
+
+       /** Pointers to queues (API-private) */
+       struct uk_netdev_rx_queue   *_rx_queue[CONFIG_LIBUKNETDEV_MAXNBQUEUES];
+       struct uk_netdev_tx_queue   *_tx_queue[CONFIG_LIBUKNETDEV_MAXNBQUEUES];
+
        UK_TAILQ_ENTRY(struct uk_netdev) _list;
 };
 
diff --git a/lib/uknetdev/netdev.c b/lib/uknetdev/netdev.c
index 7c2d9bf..844bb6c 100644
--- a/lib/uknetdev/netdev.c
+++ b/lib/uknetdev/netdev.c
@@ -69,6 +69,16 @@ int uk_netdev_drv_register(struct uk_netdev *dev, struct 
uk_alloc *a,
        UK_ASSERT(dev);
        UK_ASSERT(!dev->_data);
 
+       /* Assert mandatory configuration */
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->info_get);
+       UK_ASSERT(dev->ops->configure);
+       UK_ASSERT(dev->ops->rxq_info_get);
+       UK_ASSERT(dev->ops->rxq_configure);
+       UK_ASSERT(dev->ops->txq_info_get);
+       UK_ASSERT(dev->ops->txq_configure);
+       UK_ASSERT(dev->ops->start);
+
        dev->_data = _alloc_data(a, netdev_count,  drv_name);
        if (!dev->_data)
                return -ENOMEM;
@@ -121,3 +131,283 @@ enum uk_netdev_state uk_netdev_state_get(struct uk_netdev 
*dev)
 
        return dev->_data->state;
 }
+
+void uk_netdev_info_get(struct uk_netdev *dev,
+                       struct uk_netdev_info *dev_info)
+{
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->info_get);
+       UK_ASSERT(dev_info);
+
+       /* Clear values before querying driver for capabilities */
+       memset(dev_info, 0, sizeof(*dev_info));
+       dev->ops->info_get(dev, dev_info);
+
+       /* Limit the maximum number of rx queues and tx queues
+        * according to the API configuration
+        */
+       dev_info->max_rx_queues = MIN(CONFIG_LIBUKNETDEV_MAXNBQUEUES,
+                                     dev_info->max_rx_queues);
+       dev_info->max_tx_queues = MIN(CONFIG_LIBUKNETDEV_MAXNBQUEUES,
+                                     dev_info->max_tx_queues);
+}
+
+const void *uk_netdev_einfo_get(struct uk_netdev *dev,
+                               enum uk_netdev_einfo_type einfo)
+{
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->ops);
+
+       if (!dev->ops->einfo_get) {
+               /* driver does not provide any extra configuration */
+               return NULL;
+       }
+       return dev->ops->einfo_get(dev, einfo);
+}
+
+int uk_netdev_rxq_info_get(struct uk_netdev *dev, uint16_t queue_id,
+                          struct uk_netdev_queue_info *queue_info)
+{
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->rxq_info_get);
+       UK_ASSERT(queue_id < CONFIG_LIBUKNETDEV_MAXNBQUEUES);
+       UK_ASSERT(queue_info);
+
+       /* Clear values before querying driver for capabilities */
+       memset(queue_info, 0, sizeof(*queue_info));
+       return dev->ops->rxq_info_get(dev, queue_id, queue_info);
+}
+
+int uk_netdev_txq_info_get(struct uk_netdev *dev, uint16_t queue_id,
+                          struct uk_netdev_queue_info *queue_info)
+{
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->txq_info_get);
+       UK_ASSERT(queue_id < CONFIG_LIBUKNETDEV_MAXNBQUEUES);
+       UK_ASSERT(queue_info);
+
+       /* Clear values before querying driver for capabilities */
+       memset(queue_info, 0, sizeof(*queue_info));
+       return dev->ops->txq_info_get(dev, queue_id, queue_info);
+}
+
+int uk_netdev_configure(struct uk_netdev *dev,
+                       const struct uk_netdev_conf *dev_conf)
+{
+       struct uk_netdev_info dev_info;
+       int ret;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->configure);
+       UK_ASSERT(dev_conf);
+
+       if (dev->_data->state != UK_NETDEV_UNCONFIGURED)
+               return -EINVAL;
+
+       uk_netdev_info_get(dev, &dev_info);
+       if (dev_conf->nb_rx_queues > dev_info.max_rx_queues)
+               return -EINVAL;
+       if (dev_conf->nb_tx_queues > dev_info.max_tx_queues)
+               return -EINVAL;
+
+       ret = dev->ops->configure(dev, dev_conf);
+       if (ret >= 0) {
+               uk_pr_info("netdev%"PRIu16": Configured interface\n",
+                          dev->_data->id);
+               dev->_data->state = UK_NETDEV_CONFIGURED;
+       } else {
+               uk_pr_err("netdev%"PRIu16": Failed to configure interface: 
%d\n",
+                         dev->_data->id, ret);
+       }
+       return ret;
+}
+
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+static void _dispatcher(void *arg)
+{
+       struct uk_netdev_event_handler *handler =
+               (struct uk_netdev_event_handler *) arg;
+
+       UK_ASSERT(handler);
+       UK_ASSERT(handler->callback);
+
+       for (;;) {
+               uk_semaphore_down(&handler->events);
+               handler->callback(handler->dev,
+                                 handler->queue_id,
+                                 handler->cookie);
+       }
+}
+#endif
+
+static int _create_event_handler(uk_netdev_queue_event_t callback,
+                                void *callback_cookie,
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+                                struct uk_netdev *dev, uint16_t queue_id,
+                                const char *queue_type_str,
+                                struct uk_sched *s,
+#endif
+                                struct uk_netdev_event_handler *h)
+{
+       UK_ASSERT(h);
+       UK_ASSERT(callback || (!callback && !callback_cookie));
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+       UK_ASSERT(!h->dispatcher);
+#endif
+
+       h->callback = callback;
+       h->cookie   = callback_cookie;
+
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+       /* If we do not have a callback, we do not need a thread */
+       if (!callback)
+               return 0;
+
+       h->dev = dev;
+       h->queue_id = queue_id;
+       uk_semaphore_init(&h->events, 0);
+       h->dispatcher_s = s;
+
+       /* Create a name for the dispatcher thread.
+        * In case of errors, we just continue without a name
+        */
+       if (asprintf(&h->dispatcher_name,
+                    "netdev%"PRIu16"-%s[%"PRIu16"]",
+                    dev->_data->id, queue_type_str, queue_id) < 0) {
+               h->dispatcher_name = NULL;
+       }
+
+       h->dispatcher = uk_sched_thread_create(h->dispatcher_s,
+                                              h->dispatcher_name,
+                                              _dispatcher, h);
+       if (!h->dispatcher) {
+               if (h->dispatcher_name)
+                       free(h->dispatcher_name);
+               h->dispatcher_name = NULL;
+               return -ENOMEM;
+       }
+#endif
+
+       return 0;
+}
+
+static void _destroy_event_handler(struct uk_netdev_event_handler *h
+                                  __maybe_unused)
+{
+       UK_ASSERT(h);
+
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+       UK_ASSERT(h->dispatcher_s);
+
+       if (h->dispatcher)
+               uk_sched_thread_destroy(h->dispatcher_s, h->dispatcher);
+       h->dispatcher = NULL;
+
+       if (h->dispatcher_name)
+               free(h->dispatcher_name);
+       h->dispatcher_name = NULL;
+#endif
+}
+
+int uk_netdev_rxq_configure(struct uk_netdev *dev, uint16_t queue_id,
+                           uint16_t nb_desc,
+                           struct uk_netdev_rxqueue_conf *rx_conf)
+{
+       int err;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->rxq_configure);
+       UK_ASSERT(queue_id < CONFIG_LIBUKNETDEV_MAXNBQUEUES);
+       UK_ASSERT(rx_conf);
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+       UK_ASSERT((rx_conf->callback && rx_conf->s)
+                 || !rx_conf->callback);
+#endif
+
+       if (dev->_data->state != UK_NETDEV_CONFIGURED)
+               return -EINVAL;
+
+       /* Make sure that we are not initializing this queue a second time */
+       if (!PTRISERR(dev->_rx_queue[queue_id]))
+               return -EBUSY;
+
+       err = _create_event_handler(rx_conf->callback, rx_conf->callback_cookie,
+#ifdef CONFIG_LIBUKNETDEV_DISPATCHERTHREADS
+                                   dev, queue_id, "rxq", rx_conf->s,
+#endif
+                                   &dev->_data->rxq_handler[queue_id]);
+       if (err)
+               goto err_out;
+
+       dev->_rx_queue[queue_id] = dev->ops->rxq_configure(dev, queue_id,
+                                                          nb_desc, rx_conf);
+       if (PTRISERR(dev->_rx_queue[queue_id])) {
+               err = PTR2ERR(dev->_rx_queue[queue_id]);
+               goto err_destroy_handler;
+       }
+
+       uk_pr_info("netdev%"PRIu16": Configured receive queue %"PRIu16"\n",
+                  dev->_data->id, queue_id);
+       return 0;
+
+err_destroy_handler:
+       _destroy_event_handler(&dev->_data->rxq_handler[queue_id]);
+err_out:
+       return err;
+}
+
+int uk_netdev_txq_configure(struct uk_netdev *dev, uint16_t queue_id,
+                           uint16_t nb_desc,
+                           struct uk_netdev_txqueue_conf *tx_conf)
+{
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->txq_configure);
+       UK_ASSERT(tx_conf);
+       UK_ASSERT(queue_id < CONFIG_LIBUKNETDEV_MAXNBQUEUES);
+
+       if (dev->_data->state != UK_NETDEV_CONFIGURED)
+               return -EINVAL;
+
+       /* Make sure that we are not initializing this queue a second time */
+       if (!PTRISERR(dev->_tx_queue[queue_id]))
+               return -EBUSY;
+
+       dev->_tx_queue[queue_id] = dev->ops->txq_configure(dev, queue_id,
+                                                          nb_desc, tx_conf);
+       if (PTRISERR(dev->_tx_queue[queue_id]))
+               return PTR2ERR(dev->_tx_queue[queue_id]);
+
+       uk_pr_info("netdev%"PRIu16": Configured transmit queue %"PRIu16"\n",
+                          dev->_data->id, queue_id);
+       return 0;
+}
+
+int uk_netdev_start(struct uk_netdev *dev)
+{
+       int ret;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->start);
+
+       if (dev->_data->state != UK_NETDEV_CONFIGURED)
+               return -EINVAL;
+
+       ret = dev->ops->start(dev);
+       if (ret >= 0) {
+               uk_pr_info("netdev%"PRIu16": Started interface\n",
+                          dev->_data->id);
+               dev->_data->state = UK_NETDEV_RUNNING;
+       }
+       return ret;
+}
-- 
2.7.4


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.