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

Re: [Minios-devel] [UNIKRAFT PATCH 4/7] lib/ukconsdev: Configure console device



On 19.06.19 15:35, Birlea Costin wrote:
This patch introduces the API for configuring a Unikraft console device.
The configuration is done in the following order:
     (1) Configure main aspects of device (placeholder for future work)
     (2) Configure both rx and tx (tx is also a placeholder)
Configure API initializes the main data structures for rx/tx.

Signed-off-by: Birlea Costin <costin.birlea@xxxxxxxxx>
---
  lib/ukconsdev/Config.uk                   |  14 ++
  lib/ukconsdev/consdev.c                   | 213 ++++++++++++++++++++++++++++++
  lib/ukconsdev/exportsyms.uk               |   4 +
  lib/ukconsdev/include/uk/consdev.h        |  66 +++++++++
  lib/ukconsdev/include/uk/consdev_core.h   |  90 +++++++++++++
  lib/ukconsdev/include/uk/consdev_driver.h |  24 ++++
  6 files changed, 411 insertions(+)

diff --git a/lib/ukconsdev/Config.uk b/lib/ukconsdev/Config.uk
index bf231b66..c71247a1 100644
--- a/lib/ukconsdev/Config.uk
+++ b/lib/ukconsdev/Config.uk
@@ -3,4 +3,18 @@ menuconfig LIBUKCONSDEV
        default n
        select LIBUKALLOC
        select LIBNOLIBC if !HAVE_LIBC
+
+if LIBUKCONSDEV
+       config LIBUKCONSDEV_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.

Two comments:
(1) Either adopt the description with the "ring" naming if you want to keep it (otherwise call everything queue). (2) We do not support multiple ring/queue pairs for a single device, right? So it is fine to write something like "[...] one dispatcher thread is allocated for each device to handle receive events".

+                       libuksched is required for this option.
  endif
diff --git a/lib/ukconsdev/consdev.c b/lib/ukconsdev/consdev.c
index d61d42a8..8542b0e8 100644
--- a/lib/ukconsdev/consdev.c
+++ b/lib/ukconsdev/consdev.c
@@ -84,6 +84,215 @@ int uk_consdev_tx_info_get(struct uk_consdev *dev,
        return dev->ops->tx_info_get(dev, ring_info);
  }
+int uk_consdev_configure(struct uk_consdev *dev,
+               const struct uk_consdev_conf *dev_conf)
+{
+       int rc = 0;
+
+       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_CONSDEV_UNCONFIGURED)
+               return -EINVAL;
+
+       rc = dev->ops->configure(dev, dev_conf);
+       if (rc < 0) {
+               uk_pr_err("consdev%"PRIu16": Failed to configure device: %d\n",
+                               dev->_data->id, rc);
+               goto out;
+       }
+
+       dev->_data->state = UK_CONSDEV_CONFIGURED;
+
+out:
+       return rc;
+}
+
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+static void _dispatcher(void *arg)
+{
+       struct uk_consdev_event_handler *handler =
+               (struct uk_consdev_event_handler *) arg;
+
+       UK_ASSERT(handler);
+       UK_ASSERT(handler->callback);
+
+       for (;;) {
+               uk_semaphore_down(&handler->events);
+               handler->callback(handler->dev, handler->cb_cookie);
+       }
+}
+#endif
+
+static int _create_event_handler(uk_consdev_event_t callback,
+               void *cb_cookie,
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+               struct uk_consdev *dev,
+               const char *queue_type_str,
+               struct uk_sched *s,
+#endif
+               struct uk_consdev_event_handler *h)
+{
+       UK_ASSERT(h);
+       UK_ASSERT(callback || (!callback && !cb_cookie));
+
+       h->callback = callback;
+       h->cb_cookie = cb_cookie;
+
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+       UK_ASSERT(!h->dispatcher);
+
+       /* If we do not have a callback, we do not need a thread */
+       if (!callback)
+               return 0;
+
+       h->dev = dev;
+       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,
+                        "consdev%"PRIu16"-%s",
+                        dev->_data->id, queue_type_str) < 0) {
+               h->dispatcher_name = NULL;
+       }
+
+       h->dispatcher = uk_thread_create(h->dispatcher_s,
+               h->dispatcher_name, NULL, _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_consdev_event_handler *h
+               __maybe_unused)
+{
+       UK_ASSERT(h);
+
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+       if (h->dispatcher) {
+               UK_ASSERT(h->dispatcher_s);
+               uk_thread_kill(h->dispatcher);
+               uk_thread_wait(h->dispatcher);
+               h->dispatcher = NULL;
+       }
+
+       if (h->dispatcher_name) {
+               free(h->dispatcher_name);
+               h->dispatcher_name = NULL;
+       }
+#endif
+}
+
+int uk_consdev_rx_configure(struct uk_consdev *dev,
+               uint16_t nb_desc,
+               const struct uk_consdev_rx_conf *rx_conf)

_rxr_configure or _rxq_configure?

+{
+       int rc = 0;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->rx_configure);
+       UK_ASSERT(rx_conf);
+
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+       UK_ASSERT((rx_conf->callback && rx_conf->s)
+                       || !rx_conf->callback);
+#endif
+
+       if (dev->_data->state != UK_CONSDEV_CONFIGURED)
+               return -EINVAL;
+
+       rc = _create_event_handler(rx_conf->callback, rx_conf->cb_cookie,
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+                       dev, "rx", rx_conf->s,
+#endif
+                       &dev->_data->rx_handler);
+       if (rc < 0)
+               goto out;
+
+       rc = dev->ops->rx_configure(dev, nb_desc, rx_conf);
+       if (rc < 0) {
+               uk_pr_err("consdev%"PRIu16": Failed to configure rx: %d\n",
+                                               dev->_data->id, rc);

Please also adopt the messages by saying
"[...] Failed to configure rx ring: [...]" or with "queue"...
The same for tx ring messages.

+               goto err_destroy_handler;
+       }
+
+out:
+       return rc;
+
+err_destroy_handler:
+       _destroy_event_handler(&dev->_data->rx_handler);
+       goto out;
+}
+
+int uk_consdev_tx_configure(struct uk_consdev *dev,
+               uint16_t nb_desc,
+               const struct uk_consdev_tx_conf *tx_conf)

_txr_configure or _txq_configure?

+{
+       int rc = 0;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->tx_configure);
+       UK_ASSERT(tx_conf);
+
+       if (dev->_data->state != UK_CONSDEV_CONFIGURED)
+               return -EINVAL;
+
+       rc = dev->ops->tx_configure(dev, nb_desc, tx_conf);
+       if (rc < 0) {
+               uk_pr_err("consdev%"PRIu16": Failed to configure tx: %d\n",
+                                               dev->_data->id, rc);
+               goto out;
+       }
+
+out:
+       return rc;
+}
+
+void uk_consdev_release(struct uk_consdev *dev)
+{

I think `uk_consdev_release()` should be called `uk_consdev_unconfigure()` because it transists the device from CONFIGURED to UNCONFIGURED state.

+       int rc = 0;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->release);
+       UK_ASSERT(dev->_data->state != UK_CONSDEV_RUNNING);
+
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+       if (dev->_data->rx_handler.callback)
+               _destroy_event_handler(&dev->_data->rx_handler);
+#endif
+
+       rc = dev->ops->release(dev);
+       if (rc < 0) {
+               uk_pr_err("Failed to release consdev%"PRIu16" device %d\n",
+                               dev->_data->id, rc);
+               return;
+       }
+
+       dev->_data->state = UK_CONSDEV_UNCONFIGURED;
+       uk_pr_info("Released consdev%"PRIu16" device\n",
+                       dev->_data->id);
+}
+
  unsigned int uk_consdev_count(void)
  {
        return (unsigned int) consdev_count;
@@ -154,6 +363,10 @@ int uk_consdev_drv_register(struct uk_consdev *dev, struct 
uk_alloc *a,
        UK_ASSERT(dev->ops->info_get);
        UK_ASSERT(dev->ops->rx_info_get);
        UK_ASSERT(dev->ops->tx_info_get);
+       UK_ASSERT(dev->ops->configure);
+       UK_ASSERT(dev->ops->rx_configure);
+       UK_ASSERT(dev->ops->tx_configure);
+       UK_ASSERT(dev->ops->release);
        UK_ASSERT(dev->ops->close);
dev->_data = _alloc_data(a, consdev_count, drv_name);
diff --git a/lib/ukconsdev/exportsyms.uk b/lib/ukconsdev/exportsyms.uk
index f456b117..6fc1b3f3 100644
--- a/lib/ukconsdev/exportsyms.uk
+++ b/lib/ukconsdev/exportsyms.uk
@@ -1,6 +1,10 @@
  uk_consdev_info_get
  uk_consdev_rx_info_get
  uk_consdev_tx_info_get
+uk_consdev_configure
+uk_consdev_rx_configure
+uk_consdev_tx_configure
+uk_consdev_release
  uk_consdev_count
  uk_consdev_get
  uk_consdev_id_get
diff --git a/lib/ukconsdev/include/uk/consdev.h 
b/lib/ukconsdev/include/uk/consdev.h
index 3d98e5cc..04b4c0dc 100644
--- a/lib/ukconsdev/include/uk/consdev.h
+++ b/lib/ukconsdev/include/uk/consdev.h
@@ -124,6 +124,72 @@ int uk_consdev_tx_info_get(struct uk_consdev *dev,
                struct uk_consdev_ring_info *ring_info);
/**
+ * Configure an Unikraft console device.
+ * This function must be invoked first before any other function in the
+ * Unikraft Console API. This function can also be re-invoked when a device is
+ * in the stopped state.
+ *
+ * @param dev
+ *     The Unikraft Console Device in configured state.
+ * @param dev_conf
+ *   The pointer to the configuration data to be used for the Unikraft
+ *   console device.
+ * @return
+ *     - (0): Success, device configured.
+ *     - (<0): Error code returned by the driver configuration function.
+ */
+int uk_consdev_configure(struct uk_consdev *dev,
+               const struct uk_consdev_conf *dev_conf);
+
+/**
+ * Sets up receive for an Unikraft console device.
+ *
+ * @param dev
+ *   The Unikraft Console Device in unconfigured state.
+ * @param nb_desc
+ *   Number of descriptors for the queue. Inspect uk_consdev_rx_info_get() to
+ *   retrieve limitations. If nb_desc is set to 0, the driver chooses a default
+ *   value.

I think nb_desc is probably misleading. Read my comment below (type definition `uk_consdev_rx_configure_t`). You may want to update the API description, too.

+ * @param rx_conf
+ *   The pointer to the configuration data to be used for receive.
+ *   Its memory can be released after invoking this function.

rxr_conf or rxq_conf?

+ * @return
+ *   - (0): Success, receive correctly set up.
+ *   - (<0): Unable to allocate the receive ring descriptors.
+ */
+int uk_consdev_rx_configure(struct uk_consdev *dev,
+               uint16_t nb_desc,
+               const struct uk_consdev_rx_conf *rx_conf);
+
+/**
+ * Sets up receive for an Unikraft console device.
+ *
+ * @param dev
+ *   The Unikraft Console Device in unconfigured state.
+ * @param nb_desc
+ *   Number of descriptors for the queue. Inspect uk_consdev_tx_info_get() to
+ *   retrieve limitations. If nb_desc is set to 0, the driver chooses a default
+ *   value.

Same here.

+ * @param tx_conf
+ *   The pointer to the configuration data to be used for transmit.
+ *   Its memory can be released after invoking this function.
+ * @return
+ *   - (0): Success, receive correctly set up.
+ *   - (<0): Unable to allocate the transmit ring descriptors.
+ */
+int uk_consdev_tx_configure(struct uk_consdev *dev,
+               uint16_t nb_desc,
+               const struct uk_consdev_tx_conf *tx_conf);
+
+/**
+ * Free rx and tx rings for an Unikraft console device.
+ *
+ * @param dev
+ *     The Unikraft Console Device in configured state.
+ */
+void uk_consdev_release(struct uk_consdev *dev);
+
+/**
   * Get the number of available Unikraft Console devices.
   *
   * @return
diff --git a/lib/ukconsdev/include/uk/consdev_core.h 
b/lib/ukconsdev/include/uk/consdev_core.h
index 5edc159a..1295a913 100644
--- a/lib/ukconsdev/include/uk/consdev_core.h
+++ b/lib/ukconsdev/include/uk/consdev_core.h
@@ -38,6 +38,10 @@
#include <uk/list.h>
  #include <uk/config.h>
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+#include <uk/sched.h>
+#include <uk/semaphore.h>
+#endif
/**
   * Unikraft console API common declarations.
@@ -92,6 +96,45 @@ struct uk_consdev_ring_info {
        int nb_is_power_of_two;
  };
+/**
+ * Structure used to configure a console device.
+ */
+struct uk_consdev_conf {
+
+};
+
+/**
+ * Function type used for event callbacks.
+ *
+ * @param dev
+ *   The Unikraft Console Device.
+ * @param argp
+ *   Extra argument that can be defined on callback registration.
+ */
+typedef void (*uk_consdev_event_t)(struct uk_consdev *dev, void *argp);
+
+/**
+ * Structure used to configure an Unikraft console device RX queue.
+ */
+struct uk_consdev_rx_conf {
+       /* Event callback function. */
+       uk_consdev_event_t callback;
+       /* Argument pointer for callback. */
+       void *cb_cookie;

Call it just "cookie" or "callback_cookie" in order to avoid confusion. It seems we choose the later in uknetdev.

+
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+       /* Scheduler for dispatcher. */
+       struct uk_sched *s;
+#endif
+};
+
+/**
+ * Structure used to configure an Unikraft console device TX queue.
+ */
+struct uk_consdev_tx_conf {
+
+};
+
  /** Driver callback type to read device/driver capabilities,
   *  used for configuring the device
   */
@@ -110,6 +153,23 @@ typedef int (*uk_consdev_rx_info_t) (struct uk_consdev 
*dev,
  typedef int (*uk_consdev_tx_info_t) (struct uk_consdev *dev,
                struct uk_consdev_ring_info *ring_info);
+/** Driver callback type to configure a console device. */
+typedef int (*uk_consdev_configure_t)(struct uk_consdev *dev,
+               const struct uk_consdev_conf *dev_conf);
+
+/** Driver callback type to set up a RX ring of an Unikraft console device. */
+typedef int (*uk_consdev_rx_configure_t)(struct uk_consdev *dev,
+               uint16_t nb_desc,
+               const struct uk_consdev_rx_conf *rx_conf);

I think nb_desc may be not the right name for console devices. It is the ring size (or queue size) right? The number of characters a ring can hold, right? Would it be better to call it
`uint16_t rlen` or `uint16_t qlen`?
If this makes sense you should replace the name in all corresponding interfaces, too (e.g., uk_consdev_rxr_configure(), uk_consdev_txr_configure())

+
+/** Driver callback type to set up a TX ring of an Unikraft console device. */
+typedef int (*uk_consdev_tx_configure_t)(struct uk_consdev *dev,
+               uint16_t nb_desc,
+               const struct uk_consdev_tx_conf *tx_conf);
+
+/** Driver callback type to release a configured Unikraft console device */
+typedef int (*uk_consdev_release_t)(struct uk_consdev *dev);
+
  /** Driver callback type to close an Unikraft console device. */
  typedef void (*uk_consdev_close_t)(struct uk_consdev *dev);
@@ -117,11 +177,39 @@ struct uk_consdev_ops {
        uk_consdev_info_t                   info_get;
        uk_consdev_rx_info_t                rx_info_get;
        uk_consdev_tx_info_t                tx_info_get;
+       uk_consdev_configure_t              configure;
+       uk_consdev_release_t                release;
+       uk_consdev_rx_configure_t           rx_configure;

`uk_consdev_rxr_configure_t rxr_configure;` or
`uk_consdev_rxq_configure_t rxq_configure;` ?

+       uk_consdev_tx_configure_t           tx_configure;

Same here.

        uk_consdev_close_t                  close;
  };
/**
   * @internal
+ * Event handler configuration (internal to libukconsdev)
+ */
+struct uk_consdev_event_handler {
+       /* Callback */
+       uk_consdev_event_t callback;
+       /* Parameter for callback */
+       void                  *cb_cookie;

You can call the field just `cookie` like we did in uknetdev.

+
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+       /* Semaphore to trigger events. */
+       struct uk_semaphore events;
+       /* Reference to console device */
+       struct uk_consdev    *dev;
+       /* Dispatcher thread. */
+       struct uk_thread    *dispatcher;
+       /* reference to thread name. */
+       char                *dispatcher_name;
+       /* Scheduler for dispatcher. */
+       struct uk_sched     *dispatcher_s;
+#endif
+};
+
+/**
+ * @internal
   * libukconsdev internal data associated with each console device.
   */
  struct uk_consdev_data {
@@ -129,6 +217,8 @@ struct uk_consdev_data {
        const uint16_t id;
        /* Device state */
        enum uk_consdev_state state;
+       /* Event handler for rx */
+       struct uk_consdev_event_handler rx_handler;
        /* Name of device*/
        const char *drv_name;
        /* Device allocator */
diff --git a/lib/ukconsdev/include/uk/consdev_driver.h 
b/lib/ukconsdev/include/uk/consdev_driver.h
index 75f12b43..a90778c3 100644
--- a/lib/ukconsdev/include/uk/consdev_driver.h
+++ b/lib/ukconsdev/include/uk/consdev_driver.h
@@ -51,6 +51,30 @@ extern "C" {
  #endif
/**
+ * Forwards an rx event to the API user
+ * Can (and should) be called from device interrupt context
+ *
+ * @param dev
+ *     Unikraft console device to which the event relates to
+ */
+static inline void uk_consdev_drv_rx_event(struct uk_consdev *dev)
+{
+       struct uk_consdev_event_handler *rx_handler;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+
+       rx_handler = &dev->_data->rx_handler;
+
+#ifdef CONFIG_LIBUKCONSDEV_DISPATCHERTHREADS
+       uk_semaphore_up(&rx_handler->events);
+#else
+       if (rx_handler->callback)
+               rx_handler->callback(dev, rx_handler->cb_cookie);
+#endif
+}
+
+/**
   * Adds a Unikraft console device to the device list.
   * This should be called whenever a driver adds a new found device.
   *


_______________________________________________
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®.