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