|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH 3/6] lib/ukblkdev: Blkdev initialization
This patch introduces the API for initializing a Unikraft block device.
The initialization is done in the following order:
(1) Configure main aspects of device (e.g., number of queues)
(2) Configure each queue
(3) Start the device.
Signed-off-by: Roxana Nicolescu <nicolescu.roxana1996@xxxxxxxxx>
---
lib/ukblkdev/Config.uk | 25 +++-
lib/ukblkdev/blkdev.c | 240 ++++++++++++++++++++++++++++++++++
lib/ukblkdev/exportsyms.uk | 5 +
lib/ukblkdev/include/uk/blkdev.h | 100 ++++++++++++++
lib/ukblkdev/include/uk/blkdev_core.h | 134 +++++++++++++++++++
5 files changed, 503 insertions(+), 1 deletion(-)
diff --git a/lib/ukblkdev/Config.uk b/lib/ukblkdev/Config.uk
index b17d6167..31b1a1d2 100644
--- a/lib/ukblkdev/Config.uk
+++ b/lib/ukblkdev/Config.uk
@@ -1,6 +1,29 @@
-config LIBUKBLKDEV
+menuconfig LIBUKBLKDEV
bool "ukblkdev: Block driver interface"
default n
select LIBUKALLOC
select LIBNOLIBC if !HAVE_LIBC
select LIBUKDEBUG
+
+if LIBUKBLKDEV
+ config LIBUKBLKDEV_MAXNBQUEUES
+ int "Maximum number of request-response queues"
+ default 1
+ help
+ Upper limit for supported number of transmit and receive
+ queues with the ukblkdev API.
+
+ config LIBUKBLKDEV_DISPATCHERTHREADS
+ bool "Dispatcher threads for event callbacks"
+ default n
+ select LIBUKSCHED
+ select LIBUKLOCK
+ select LIBUKLOCK_SEMAPHORE
+ 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 queue.
+ libuksched is required for this option.
+
+endif
diff --git a/lib/ukblkdev/blkdev.c b/lib/ukblkdev/blkdev.c
index dfacf1ca..6b82cf8d 100644
--- a/lib/ukblkdev/blkdev.c
+++ b/lib/ukblkdev/blkdev.c
@@ -77,6 +77,13 @@ int uk_blkdev_drv_register(struct uk_blkdev *dev, struct
uk_alloc *a,
/* Data must be unallocated. */
UK_ASSERT(PTRISERR(dev->_data));
+ /* Assert mandatory configuration. */
+ UK_ASSERT(dev->dev_ops);
+ UK_ASSERT(dev->dev_ops->dev_configure);
+ UK_ASSERT(dev->dev_ops->dev_start);
+ UK_ASSERT(dev->dev_ops->queue_setup);
+ UK_ASSERT(dev->dev_ops->get_info);
+ UK_ASSERT(dev->dev_ops->queue_get_info);
dev->_data = _alloc_data(a, blkdev_count, drv_name);
if (!dev->_data)
@@ -131,3 +138,236 @@ enum uk_blkdev_state uk_blkdev_state_get(struct uk_blkdev
*dev)
return dev->_data->state;
}
+
+int uk_blkdev_get_info(struct uk_blkdev *dev,
+ struct uk_blkdev_info *dev_info)
+{
+ int rc = 0;
+
+ UK_ASSERT(dev);
+ UK_ASSERT(dev->dev_ops);
+ UK_ASSERT(dev->dev_ops->get_info);
+ UK_ASSERT(dev_info);
+
+ /* Clear values before querying driver for capabilities */
+ memset(dev_info, 0, sizeof(*dev_info));
+ dev->dev_ops->get_info(dev, dev_info);
+
+ /* Limit the maximum number of queues
+ * according to the API configuration
+ */
+ dev_info->max_queues = MIN(CONFIG_LIBUKBLKDEV_MAXNBQUEUES,
+ dev_info->max_queues);
+
+ return rc;
+}
+
+int uk_blkdev_configure(struct uk_blkdev *dev,
+ const struct uk_blkdev_conf *conf)
+{
+ int rc = 0;
+ struct uk_blkdev_info dev_info;
+
+ UK_ASSERT(dev);
+ UK_ASSERT(dev->_data);
+ UK_ASSERT(dev->dev_ops);
+ UK_ASSERT(dev->dev_ops->dev_configure);
+ UK_ASSERT(conf);
+
+ rc = uk_blkdev_get_info(dev, &dev_info);
+ if (rc) {
+ uk_pr_err("blkdev-%"PRIu16": Failed to get initial info: %d\n",
+ dev->_data->id, rc);
+ return rc;
+ }
+
+ if (conf->nb_queues > dev_info.max_queues)
+ return -EINVAL;
+
+ rc = dev->dev_ops->dev_configure(dev, conf);
+ if (!rc) {
+ uk_pr_info("blkdev%"PRIu16": Configured interface\n",
+ dev->_data->id);
+ dev->_data->state = UK_BLKDEV_CONFIGURED;
+ } else
+ uk_pr_err("blkdev%"PRIu16": Failed to configure interface %d\n",
+ dev->_data->id, rc);
+
+ return rc;
+}
+
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+static void _dispatcher(void *args)
+{
+ struct uk_blkdev_event_handler *handler =
+ (struct uk_blkdev_event_handler *) args;
+
+ UK_ASSERT(handler);
+ UK_ASSERT(handler->callback);
+
+ while (1) {
+ uk_semaphore_down(&handler->events);
+ handler->callback(handler->dev,
+ handler->queue_id, handler->cookie);
+ }
+}
+#endif
+
+static int _create_event_handler(uk_blkdev_queue_event_t callback,
+ void *cookie,
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+ struct uk_blkdev *dev, uint16_t queue_id,
+ struct uk_sched *s,
+#endif
+ struct uk_blkdev_event_handler *event_handler)
+{
+ UK_ASSERT(event_handler);
+ UK_ASSERT(callback || (!callback && !cookie));
+
+ event_handler->callback = callback;
+ event_handler->cookie = cookie;
+
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+ /* If we do not have a callback, we do not need a thread */
+ if (!callback)
+ return 0;
+
+ event_handler->dev = dev;
+ event_handler->queue_id = queue_id;
+ uk_semaphore_init(&event_handler->events, 0);
+ event_handler->dispatcher_s = s;
+
+ /* Create a name for the dispatcher thread.
+ * In case of errors, we just continue without a name
+ */
+ if (asprintf(&event_handler->dispatcher_name,
+ "blkdev%"PRIu16"-q%"PRIu16"]",
+ dev->_data->id, queue_id) < 0) {
+ event_handler->dispatcher_name = NULL;
+ }
+
+ /* Create thread */
+ event_handler->dispatcher = uk_sched_thread_create(
+ event_handler->dispatcher_s,
+ event_handler->dispatcher_name, NULL,
+ _dispatcher, (void *)event_handler);
+ if (event_handler->dispatcher == NULL) {
+ if (event_handler->dispatcher_name) {
+ free(event_handler->dispatcher);
+ event_handler->dispatcher = NULL;
+ }
+
+ return -ENOMEM;
+ }
+#endif
+
+ return 0;
+}
+
+static void _destroy_event_handler(struct uk_blkdev_event_handler *h
+ __maybe_unused)
+{
+ UK_ASSERT(h);
+
+#if CONFIG_LIBUKBLKDEV_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_blkdev_queue_get_info(struct uk_blkdev *dev, uint16_t queue_id,
+ struct uk_blkdev_queue_info *q_info)
+{
+ UK_ASSERT(dev);
+ UK_ASSERT(queue_id < CONFIG_LIBUKBLKDEV_MAXNBQUEUES);
+ UK_ASSERT(q_info);
+
+ return dev->dev_ops->queue_get_info(dev, queue_id, q_info);
+}
+
+int uk_blkdev_queue_configure(struct uk_blkdev *dev, uint16_t queue_id,
+ uint16_t nb_desc,
+ const struct uk_blkdev_queue_conf *queue_conf)
+{
+ int err = 0;
+
+ UK_ASSERT(dev);
+ UK_ASSERT(dev->_data);
+ UK_ASSERT(dev->dev_ops);
+ UK_ASSERT(dev->dev_ops->queue_setup);
+ UK_ASSERT(dev->finish_reqs);
+ UK_ASSERT(queue_id < CONFIG_LIBUKBLKDEV_MAXNBQUEUES);
+ UK_ASSERT(queue_conf);
+
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+ UK_ASSERT((queue_conf->callback && queue_conf->s)
+ || !queue_conf->callback);
+#endif
+
+ if (dev->_data->state != UK_BLKDEV_CONFIGURED)
+ return -EINVAL;
+
+ /* Make sure that we are not initializing this queue a second time */
+ if (!PTRISERR(dev->_queue[queue_id]))
+ return -EBUSY;
+
+ err = _create_event_handler(queue_conf->callback,
+ queue_conf->callback_cookie,
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+ dev, queue_id, queue_conf->s,
+#endif
+ &dev->_data->queue_handler[queue_id]);
+ if (err)
+ goto err_out;
+
+ dev->_queue[queue_id] = dev->dev_ops->queue_setup(dev, queue_id,
+ nb_desc,
+ queue_conf);
+ if (PTRISERR(dev->_queue[queue_id])) {
+ err = PTR2ERR(dev->_queue[queue_id]);
+ uk_pr_err("blkdev%"PRIu16"-q%"PRIu16": Failed to configure:
%d\n",
+ dev->_data->id, queue_id, err);
+ goto err_destroy_handler;
+ }
+
+ uk_pr_info("blkdev%"PRIu16": Configured queue %"PRIu16"\n",
+ dev->_data->id, queue_id);
+ return 0;
+
+err_destroy_handler:
+ _destroy_event_handler(&dev->_data->queue_handler[queue_id]);
+err_out:
+ return err;
+}
+
+int uk_blkdev_start(struct uk_blkdev *dev)
+{
+ int rc = 0;
+
+ UK_ASSERT(dev);
+ UK_ASSERT(dev->_data);
+ UK_ASSERT(dev->dev_ops);
+ UK_ASSERT(dev->dev_ops->dev_start);
+ UK_ASSERT(dev->_data->state == UK_BLKDEV_CONFIGURED);
+
+ rc = dev->dev_ops->dev_start(dev);
+ if (rc)
+ uk_pr_err("blkdev%"PRIu16": Failed to start interface %d\n",
+ dev->_data->id, rc);
+ else {
+ uk_pr_info("blkdev%"PRIu16": Started interface\n",
+ dev->_data->id);
+ dev->_data->state = UK_BLKDEV_RUNNING;
+ }
+
+ return rc;
+}
diff --git a/lib/ukblkdev/exportsyms.uk b/lib/ukblkdev/exportsyms.uk
index 34e22426..077994f0 100644
--- a/lib/ukblkdev/exportsyms.uk
+++ b/lib/ukblkdev/exportsyms.uk
@@ -4,3 +4,8 @@ uk_blkdev_get
uk_blkdev_id_get
uk_blkdev_drv_name_get
uk_blkdev_state_get
+uk_blkdev_get_info
+uk_blkdev_configure
+uk_blkdev_queue_get_info
+uk_blkdev_queue_configure
+uk_blkdev_start
diff --git a/lib/ukblkdev/include/uk/blkdev.h b/lib/ukblkdev/include/uk/blkdev.h
index d678677f..d32f62fc 100644
--- a/lib/ukblkdev/include/uk/blkdev.h
+++ b/lib/ukblkdev/include/uk/blkdev.h
@@ -48,6 +48,12 @@
* to uk_blkdev_get(). The block app should store this reference and
* use it for all subsequent API calls.
*
+ * The functions exported by the Unikraft BLK API to setup a device
+ * designated by its ID must be invoked in the following order:
+ * - uk_blkdev_configure()
+ * - uk_blkdev_queue_setup()
+ * - uk_blkdev_start()
+ *
* There are 4 states in which a block device can be found:
* - UK_BLKDEV_UNREGISTERED
* - UK_BLKDEV_UNCONFIGURED
@@ -121,6 +127,100 @@ const char *uk_blkdev_drv_name_get(struct uk_blkdev *dev);
*/
enum uk_blkdev_state uk_blkdev_state_get(struct uk_blkdev *dev);
+/**
+ * Query device capabilities.
+ * Information that is useful for device initialization (e.g.,
+ * maximum number of supported queues).
+ *
+ * @param dev
+ * The Unikraft Block Device.
+ * @param dev_info
+ * A pointer to a structure of type *uk_blkdev_info* to be filled with
+ * the contextual information of a block device.
+ * @return
+ * - 0: Success
+ * - <0: Error in driver
+ */
+int uk_blkdev_get_info(struct uk_blkdev *dev,
+ struct uk_blkdev_info *dev_info);
+
+/**
+ * Configure an Unikraft block device.
+ * This function must be invoked first before any other function in the
+ * Unikraft BLK API. This function can also be re-invoked when a device is
+ * in the stopped state.
+ *
+ * @param dev
+ * The Unikraft Block Device.
+
+ * @return
+ * - 0: Success, device configured.
+ * - <0: Error code returned by the driver configuration function.
+ */
+int uk_blkdev_configure(struct uk_blkdev *dev,
+ const struct uk_blkdev_conf *conf);
+
+/**
+ * Query device queue capabilities.
+ * Information that is useful for device queue initialization (e.g.,
+ * maximum number of supported descriptors on queues).
+ *
+ * @param dev
+ * The Unikraft Block Device in configured state.
+ * @param queue_id
+ * The index of the queue to set up.
+ * The value must be in the range [0, nb_queue - 1] previously supplied
+ * to uk_blkdev_configure().
+ * @param queue_info
+ * A pointer to a structure of type *uk_blkdev_queue_info* to be filled out
+ * @return
+ * - (0): Success, queue_info is filled out.
+ * - (<0): Error code of the drivers function.
+ */
+int uk_blkdev_queue_get_info(struct uk_blkdev *dev, uint16_t queue_id,
+ struct uk_blkdev_queue_info *q_info);
+
+/**
+ * Allocate and set up a queue for an Unikraft block device.
+ * The queue is responsible for both requests and responses.
+ *
+ * @param dev
+ * The Unikraft Block Device.
+ * @param queue_id
+ * The index of the queue to set up.
+ * The value must be in range [0, nb_queue -1] previously supplied
+ * to uk_blkdev_configure()
+ * @param nb_desc
+ * Number of descriptors for the queue. Inspect uk_blkdev_queue_get_info()
+ * to retrieve limitations.
+ * @param queue_conf
+ * The pointer to the configuration data to be used for the queue.
+ * @return
+ * - 0: Success, receive queue correctly set up.
+ * - <0: Unable to allocate and set up the ring descriptors.
+ */
+int uk_blkdev_queue_configure(struct uk_blkdev *dev,
+ uint16_t queue_id,
+ uint16_t nb_desc,
+ const struct uk_blkdev_queue_conf *queue_conf);
+
+/**
+ * Start a Block device.
+ *
+ * The device start step is the last one and consists of setting the configured
+ * offload features and in starting the transmit and the receive units of the
+ * device.
+ * On success, all basic functions exported by the Unikraft BLK API
+ * can be invoked.
+ *
+ * @param dev
+ * The Unikraft Block Device.
+ * @return
+ * - 0: Success, Unikraft block device started.
+ * - <0: Error code of the driver device start function.
+ */
+int uk_blkdev_start(struct uk_blkdev *dev);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/ukblkdev/include/uk/blkdev_core.h
b/lib/ukblkdev/include/uk/blkdev_core.h
index 3cccf87e..364c05d7 100644
--- a/lib/ukblkdev/include/uk/blkdev_core.h
+++ b/lib/ukblkdev/include/uk/blkdev_core.h
@@ -38,6 +38,10 @@
#include <uk/list.h>
#include <uk/config.h>
+#if defined(CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS)
+#include <uk/sched.h>
+#include <uk/semaphore.h>
+#endif
/**
* Unikraft block API common declarations.
@@ -72,6 +76,129 @@ enum uk_blkdev_state {
};
/**
+ * Structure used to configure an Unikraft block device.
+ */
+struct uk_blkdev_conf {
+ uint16_t nb_queues;
+};
+
+/**
+ * Structure used to describe block device capabilities
+ * before negotiation
+ */
+struct uk_blkdev_info {
+ /* Max nb of supported queues by device. */
+ uint16_t max_queues;
+};
+
+/**
+ * Structure used to describe device descriptor ring limitations.
+ */
+struct uk_blkdev_queue_info {
+ /* Max allowed number of descriptors. */
+ uint16_t nb_max;
+ /* Min allowed number of descriptors. */
+ uint16_t nb_min;
+ /* Number should be a multiple of nb_align. */
+ uint16_t nb_align;
+ /* Number should be a power of two. */
+ int nb_is_power_of_two;
+};
+
+/**
+ * Queue Structure used for both requests and responses.
+ */
+struct uk_blkdev_queue;
+
+/**
+ * Function type used for queue event callbacks.
+ *
+ * @param dev
+ * The Unikraft Block Device.
+ * @param queue
+ * The queue on the Unikraft block device on which the event happened.
+ * @param argp
+ * Extra argument that can be defined on callback registration.
+ */
+typedef void (*uk_blkdev_queue_event_t)(struct uk_blkdev *dev,
+ uint16_t queue_id, void *argp);
+
+/**
+ * Structure used to configure an Unikraft block device queue.
+ *
+ */
+struct uk_blkdev_queue_conf {
+ /* Allocator used for descriptor rings */
+ struct uk_alloc *a;
+ /* Event callback function */
+ uk_blkdev_queue_event_t callback;
+ /* Argument pointer for callback*/
+ void *callback_cookie;
+
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+ /* Scheduler for dispatcher. */
+ struct uk_sched *s;
+#endif
+};
+
+/** Driver callback type to get initial device capabilities */
+typedef void (*uk_blkdev_get_info_t)(struct uk_blkdev *dev,
+ struct uk_blkdev_info *dev_info);
+
+/** Driver callback type to configure a block device. */
+typedef int (*uk_blkdev_configure_t)(struct uk_blkdev *dev,
+ const struct uk_blkdev_conf *conf);
+
+/* Driver callback type to get info about a device queue */
+typedef int (*uk_blkdev_queue_get_info_t)(struct uk_blkdev *dev,
+ uint16_t queue_id,
+ struct uk_blkdev_queue_info *q_info);
+
+/** Driver callback type to set up a queue of an Unikraft block device. */
+typedef struct uk_blkdev_queue * (*uk_blkdev_queue_configure_t)(
+ struct uk_blkdev *dev,
+ uint16_t queue_id,
+ uint16_t nb_desc,
+ const struct uk_blkdev_queue_conf *queue_conf);
+
+/** Driver callback type to start a configured Unikraft block device. */
+typedef int (*uk_blkdev_start_t)(struct uk_blkdev *dev);
+
+struct uk_blkdev_ops {
+ uk_blkdev_get_info_t get_info;
+ uk_blkdev_configure_t dev_configure;
+ uk_blkdev_queue_get_info_t queue_get_info;
+ uk_blkdev_queue_configure_t queue_setup;
+ uk_blkdev_start_t dev_start;
+};
+
+/**
+ * @internal
+ * Event handler configuration (internal to libukblkdev)
+ */
+struct uk_blkdev_event_handler {
+ /* Callback */
+ uk_blkdev_queue_event_t callback;
+ /* Parameter for callback */
+ void *cookie;
+
+#if CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS
+ /* Semaphore to trigger events. */
+ struct uk_semaphore events;
+ /* Reference to blk device. */
+ struct uk_blkdev *dev;
+ /* Queue id which caused event. */
+ uint16_t queue_id;
+ /* Dispatcher thread. */
+ struct uk_thread *dispatcher;
+ /* Reference to thread name. */
+ char *dispatcher_name;
+ /* Scheduler for dispatcher. */
+ struct uk_sched *dispatcher_s;
+#endif
+};
+
+/**
* @internal
* libukblkdev internal data associated with each block device.
*/
@@ -80,6 +207,9 @@ struct uk_blkdev_data {
const uint16_t id;
/* Device state */
enum uk_blkdev_state state;
+ /* Event handler for each queue */
+ struct uk_blkdev_event_handler
+ queue_handler[CONFIG_LIBUKBLKDEV_MAXNBQUEUES];
/* Name of device*/
const char *drv_name;
};
@@ -87,6 +217,10 @@ struct uk_blkdev_data {
struct uk_blkdev {
/* Pointer to API-internal state data. */
struct uk_blkdev_data *_data;
+ /* Functions callbacks by driver. */
+ const struct uk_blkdev_ops *dev_ops;
+ /* Pointers to queues (API-private) */
+ struct uk_blkdev_queue *_queue[CONFIG_LIBUKBLKDEV_MAXNBQUEUES];
/* Entry for list of block devices */
UK_TAILQ_ENTRY(struct uk_blkdev) _list;
};
--
2.11.0
_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |