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

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



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.
+                       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)
+{
+       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);
+               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)
+{
+       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)
+{
+       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.
+ * @param rx_conf
+ *   The pointer to the configuration data to be used for receive.
+ *   Its memory can be released after invoking this function.
+ * @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.
+ * @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;
+
+#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);
+
+/** 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_tx_configure_t           tx_configure;
        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;
+
+#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.
  *
-- 
2.11.0


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