@@ -14,6 +14,7 @@ doc/guides/compressdevs/overview_feature_table.txt
doc/guides/regexdevs/overview_feature_table.txt
doc/guides/vdpadevs/overview_feature_table.txt
doc/guides/bbdevs/overview_feature_table.txt
+doc/guides/hcdevs/overview_feature_table.txt
# ignore generated ctags/cscope files
cscope.out.po
@@ -452,6 +452,12 @@ F: app/test-regex/
F: doc/guides/prog_guide/regexdev.rst
F: doc/guides/regexdevs/features/default.ini
+Heterogeneous Computing API - EXPERIMENTAL
+M: Elena Agostini <eagostini@nvidia.com>
+F: lib/hcdev/
+F: doc/guides/prog_guide/hcdev.rst
+F: doc/guides/hcdevs/features/default.ini
+
Eventdev API
M: Jerin Jacob <jerinj@marvell.com>
T: git://dpdk.org/next/dpdk-next-eventdev
@@ -21,6 +21,7 @@ The public API headers are grouped by topics:
[compressdev] (@ref rte_compressdev.h),
[compress] (@ref rte_comp.h),
[regexdev] (@ref rte_regexdev.h),
+ [hcdev] (@ref rte_hcdev.h),
[eventdev] (@ref rte_eventdev.h),
[event_eth_rx_adapter] (@ref rte_event_eth_rx_adapter.h),
[event_eth_tx_adapter] (@ref rte_event_eth_tx_adapter.h),
@@ -44,6 +44,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \
@TOPDIR@/lib/gro \
@TOPDIR@/lib/gso \
@TOPDIR@/lib/hash \
+ @TOPDIR@/lib/hcdev \
@TOPDIR@/lib/ip_frag \
@TOPDIR@/lib/ipsec \
@TOPDIR@/lib/jobstats \
@@ -152,6 +152,9 @@ def generate_overview_table(output_filename, table_id, section, table_name, titl
name = ini_filename[:-4]
name = name.replace('_vf', 'vf')
pmd_names.append(name)
+ if not pmd_names:
+ # Add an empty column if table is empty (required by RST syntax)
+ pmd_names.append(' ')
# Pad the table header names.
max_header_len = len(max(pmd_names, key=len))
@@ -388,6 +391,11 @@ def setup(app):
'Features',
'Features availability in bbdev drivers',
'Feature')
+ table_file = dirname(__file__) + '/hcdevs/overview_feature_table.txt'
+ generate_overview_table(table_file, 1,
+ 'Features',
+ 'Features availability in hcdev drivers',
+ 'Feature')
if LooseVersion(sphinx_version) < LooseVersion('1.3.1'):
print('Upgrade sphinx to version >= 1.3.1 for '
new file mode 100644
@@ -0,0 +1,10 @@
+;
+; Features of heterogeneous device driver.
+;
+; This file defines the features that are valid for inclusion in
+; the other driver files and also the order that they appear in
+; the features table in the documentation. The feature description
+; string should not exceed feature_str_len defined in conf.py.
+;
+[Features]
+Get device info =
new file mode 100644
@@ -0,0 +1,11 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright (c) 2021 NVIDIA Corporation & Affiliates
+
+Heterogeneous Computing Device Drivers
+======================================
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+ overview
new file mode 100644
@@ -0,0 +1,11 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright (c) 2021 NVIDIA Corporation & Affiliates
+
+Overview of Heterogeneous Computing Drivers
+===========================================
+
+Heterogeneous computing device may refer to any computing unit
+able to process data and to share some memory with the CPU.
+Examples are GPU or specialized processor in a SoC.
+
+.. include:: overview_feature_table.txt
@@ -21,6 +21,7 @@ DPDK documentation
compressdevs/index
vdpadevs/index
regexdevs/index
+ hcdevs/index
eventdevs/index
rawdevs/index
mempool/index
new file mode 100644
@@ -0,0 +1,5 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright (c) 2021 NVIDIA Corporation & Affiliates
+
+Heterogeneous Computing Device Library
+======================================
@@ -27,6 +27,7 @@ Programmer's Guide
cryptodev_lib
compressdev
regexdev
+ hcdev
rte_security
rawdev
link_bonding_poll_mode_drv_lib
@@ -55,6 +55,10 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Introduced Heterogeneous Computing Device library with first features:**
+
+ * Device information
+
* **Added auxiliary bus support.**
Auxiliary bus provides a way to split function into child-devices
new file mode 100644
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021 NVIDIA Corporation & Affiliates
+
+drivers = []
@@ -18,6 +18,7 @@ subdirs = [
'vdpa', # depends on common, bus and mempool.
'event', # depends on common, bus, mempool and net.
'baseband', # depends on common and bus.
+ 'hc', # depends on common and bus.
]
if meson.is_cross_build()
new file mode 100644
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 NVIDIA Corporation & Affiliates
+ */
+
+#include <rte_eal.h>
+#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_log.h>
+
+#include "rte_hcdev.h"
+#include "hcdev_driver.h"
+
+/* Logging */
+RTE_LOG_REGISTER_DEFAULT(hcdev_logtype, NOTICE);
+#define HCDEV_LOG(level, ...) \
+ rte_log(RTE_LOG_ ## level, hcdev_logtype, RTE_FMT("hcdev: " \
+ RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,)))
+
+/* Set any driver error as EPERM */
+#define HCDEV_DRV_RET(function) \
+ ((function != 0) ? -(rte_errno = EPERM) : (rte_errno = 0))
+
+/* Array of devices */
+static struct rte_hcdev *hcdevs;
+/* Number of currently valid devices */
+static int16_t hcdev_max;
+/* Number of currently valid devices */
+static int16_t hcdev_count;
+
+int
+rte_hcdev_init(size_t dev_max)
+{
+ if (dev_max == 0 || dev_max > INT16_MAX) {
+ HCDEV_LOG(ERR, "invalid array size");
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+
+ /* No lock, it must be called before or during first probing. */
+ if (hcdevs != NULL) {
+ HCDEV_LOG(ERR, "already initialized");
+ rte_errno = EBUSY;
+ return -rte_errno;
+ }
+
+ hcdevs = calloc(dev_max, sizeof(struct rte_hcdev));
+ if (hcdevs == NULL) {
+ HCDEV_LOG(ERR, "cannot initialize library");
+ rte_errno = ENOMEM;
+ return -rte_errno;
+ }
+
+ hcdev_max = dev_max;
+ return 0;
+}
+
+uint16_t
+rte_hcdev_count_avail(void)
+{
+ return hcdev_count;
+}
+
+bool
+rte_hcdev_is_valid(int16_t dev_id)
+{
+ if (dev_id >= 0 && dev_id < hcdev_max &&
+ hcdevs[dev_id].state == RTE_HCDEV_STATE_INITIALIZED)
+ return true;
+ return false;
+}
+
+int16_t
+rte_hcdev_find_next(int16_t dev_id)
+{
+ if (dev_id < 0)
+ dev_id = 0;
+ while (dev_id < hcdev_max &&
+ hcdevs[dev_id].state == RTE_HCDEV_STATE_UNUSED)
+ dev_id++;
+
+ if (dev_id >= hcdev_max)
+ return RTE_HCDEV_ID_NONE;
+ return dev_id;
+}
+
+static int16_t
+hcdev_find_free_id(void)
+{
+ int16_t dev_id;
+
+ for (dev_id = 0; dev_id < hcdev_max; dev_id++) {
+ if (hcdevs[dev_id].state == RTE_HCDEV_STATE_UNUSED)
+ return dev_id;
+ }
+ return RTE_HCDEV_ID_NONE;
+}
+
+static struct rte_hcdev *
+hcdev_get_by_id(int16_t dev_id)
+{
+ if (!rte_hcdev_is_valid(dev_id))
+ return NULL;
+ return &hcdevs[dev_id];
+}
+
+struct rte_hcdev *
+rte_hcdev_get_by_name(const char *name)
+{
+ int16_t dev_id;
+ struct rte_hcdev *dev;
+
+ if (name == NULL) {
+ rte_errno = EINVAL;
+ return NULL;
+ }
+
+ RTE_HCDEV_FOREACH(dev_id) {
+ dev = &hcdevs[dev_id];
+ if (strncmp(name, dev->name, RTE_DEV_NAME_MAX_LEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+struct rte_hcdev *
+rte_hcdev_allocate(const char *name)
+{
+ int16_t dev_id;
+ struct rte_hcdev *dev;
+
+ if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+ HCDEV_LOG(ERR, "only primary process can allocate device");
+ rte_errno = EPERM;
+ return NULL;
+ }
+ if (name == NULL) {
+ HCDEV_LOG(ERR, "allocate device without a name");
+ rte_errno = EINVAL;
+ return NULL;
+ }
+
+ /* implicit initialization of library before adding first device */
+ if (hcdevs == NULL && rte_hcdev_init(RTE_HCDEV_DEFAULT_MAX) < 0)
+ return NULL;
+
+ if (rte_hcdev_get_by_name(name) != NULL) {
+ HCDEV_LOG(ERR, "device with name %s already exists", name);
+ rte_errno = EEXIST;
+ return NULL;
+ }
+ dev_id = hcdev_find_free_id();
+ if (dev_id == RTE_HCDEV_ID_NONE) {
+ HCDEV_LOG(ERR, "reached maximum number of devices");
+ rte_errno = ENOENT;
+ return NULL;
+ }
+
+ dev = &hcdevs[dev_id];
+ memset(dev, 0, sizeof(*dev));
+
+ if (rte_strscpy(dev->name, name, RTE_DEV_NAME_MAX_LEN) < 0) {
+ HCDEV_LOG(ERR, "device name too long: %s", name);
+ rte_errno = ENAMETOOLONG;
+ return NULL;
+ }
+ dev->info.name = dev->name;
+ dev->info.dev_id = dev_id;
+ dev->info.numa_node = -1;
+
+ hcdev_count++;
+ HCDEV_LOG(DEBUG, "new device %s (id %d) of total %d",
+ name, dev_id, hcdev_count);
+ return dev;
+}
+
+void
+rte_hcdev_complete_new(struct rte_hcdev *dev)
+{
+ if (dev == NULL)
+ return;
+
+ dev->state = RTE_HCDEV_STATE_INITIALIZED;
+}
+
+int
+rte_hcdev_release(struct rte_hcdev *dev)
+{
+ if (dev == NULL) {
+ rte_errno = ENODEV;
+ return -rte_errno;
+ }
+
+ HCDEV_LOG(DEBUG, "free device %s (id %d)",
+ dev->info.name, dev->info.dev_id);
+ dev->state = RTE_HCDEV_STATE_UNUSED;
+ hcdev_count--;
+
+ return 0;
+}
+
+int
+rte_hcdev_close(int16_t dev_id)
+{
+ int firsterr, binerr;
+ int *lasterr = &firsterr;
+ struct rte_hcdev *dev;
+
+ dev = hcdev_get_by_id(dev_id);
+ if (dev == NULL) {
+ HCDEV_LOG(ERR, "close invalid device ID %d", dev_id);
+ rte_errno = ENODEV;
+ return -rte_errno;
+ }
+
+ if (dev->ops.dev_close != NULL) {
+ *lasterr = HCDEV_DRV_RET(dev->ops.dev_close(dev));
+ if (*lasterr != 0)
+ lasterr = &binerr;
+ }
+
+ *lasterr = rte_hcdev_release(dev);
+
+ rte_errno = -firsterr;
+ return firsterr;
+}
+
+int
+rte_hcdev_info_get(int16_t dev_id, struct rte_hcdev_info *info)
+{
+ struct rte_hcdev *dev;
+
+ dev = hcdev_get_by_id(dev_id);
+ if (dev == NULL) {
+ HCDEV_LOG(ERR, "query invalid device ID %d", dev_id);
+ rte_errno = ENODEV;
+ return -rte_errno;
+ }
+ if (info == NULL) {
+ HCDEV_LOG(ERR, "query without storage");
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+
+ if (dev->ops.dev_info_get == NULL) {
+ *info = dev->info;
+ return 0;
+ }
+ return HCDEV_DRV_RET(dev->ops.dev_info_get(dev, info));
+}
new file mode 100644
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 NVIDIA Corporation & Affiliates
+ */
+
+/*
+ * This header file must be included only by drivers.
+ * It is considered internal, i.e. hidden for the application.
+ * The prefix rte_ is used to avoid namespace clash in drivers.
+ */
+
+#ifndef RTE_HCDEV_DRIVER_H
+#define RTE_HCDEV_DRIVER_H
+
+#include <stdint.h>
+
+#include <rte_dev.h>
+
+#include "rte_hcdev.h"
+
+/* Flags indicate current state of device. */
+enum rte_hcdev_state {
+ RTE_HCDEV_STATE_UNUSED, /* not initialized */
+ RTE_HCDEV_STATE_INITIALIZED, /* initialized */
+};
+
+struct rte_hcdev;
+typedef int (rte_hcdev_close_t)(struct rte_hcdev *dev);
+typedef int (rte_hcdev_info_get_t)(struct rte_hcdev *dev, struct rte_hcdev_info *info);
+
+struct rte_hcdev_ops {
+ /* Get device info. If NULL, info is just copied. */
+ rte_hcdev_info_get_t *dev_info_get;
+ /* Close device. */
+ rte_hcdev_close_t *dev_close;
+};
+
+struct rte_hcdev {
+ /* Backing device. */
+ struct rte_device *device;
+ /* Unique identifier name. */
+ char name[RTE_DEV_NAME_MAX_LEN]; /* Updated by this library. */
+ /* Device info structure. */
+ struct rte_hcdev_info info;
+ /* Driver functions. */
+ struct rte_hcdev_ops ops;
+ /* Current state (used or not) in the running process. */
+ enum rte_hcdev_state state; /* Updated by this library. */
+ /* Driver-specific private data for the running process. */
+ void *process_private;
+} __rte_cache_aligned;
+
+__rte_internal
+struct rte_hcdev *rte_hcdev_get_by_name(const char *name);
+
+/* First step of initialization */
+__rte_internal
+struct rte_hcdev *rte_hcdev_allocate(const char *name);
+
+/* Last step of initialization. */
+__rte_internal
+void rte_hcdev_complete_new(struct rte_hcdev *dev);
+
+/* Last step of removal. */
+__rte_internal
+int rte_hcdev_release(struct rte_hcdev *dev);
+
+#endif /* RTE_HCDEV_DRIVER_H */
new file mode 100644
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2021 NVIDIA Corporation & Affiliates
+
+headers = files(
+ 'rte_hcdev.h',
+)
+
+sources = files(
+ 'hcdev.c',
+)
new file mode 100644
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 NVIDIA Corporation & Affiliates
+ */
+
+#ifndef RTE_HCDEV_H
+#define RTE_HCDEV_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <rte_compat.h>
+
+/**
+ * @file
+ * Generic library to interact with heterogeneous computing device.
+ *
+ * The API is not thread-safe.
+ * Device management must be done by a single thread.
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Maximum number of devices if rte_hcdev_init() is not called. */
+#define RTE_HCDEV_DEFAULT_MAX 32
+
+/** Empty device ID. */
+#define RTE_HCDEV_ID_NONE -1
+
+/** Store device info. */
+struct rte_hcdev_info {
+ /** Unique identifier name. */
+ const char *name;
+ /** Device ID. */
+ int16_t dev_id;
+ /** Total processors available on device. */
+ uint32_t processor_count;
+ /** Total memory available on device. */
+ size_t total_memory;
+ /* Local NUMA memory ID. -1 if unknown. */
+ int16_t numa_node;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Initialize the device array before probing devices.
+ * If not called, the maximum of probed devices is RTE_HCDEV_DEFAULT_MAX.
+ *
+ * @param dev_max
+ * Maximum number of devices.
+ *
+ * @return
+ * 0 on success, -rte_errno otherwise:
+ * - ENOMEM if out of memory
+ * - EINVAL if 0 size
+ * - EBUSY if already initialized
+ */
+__rte_experimental
+int rte_hcdev_init(size_t dev_max);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Return the number of heterogeneous computing devices detected
+ * and associated to DPDK.
+ *
+ * @return
+ * The number of available computing devices.
+ */
+__rte_experimental
+uint16_t rte_hcdev_count_avail(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Check if the device is valid and initialized in DPDK.
+ *
+ * @param dev_id
+ * The input device ID.
+ *
+ * @return
+ * - True if dev_id is a valid and initialized computing device.
+ * - False otherwise.
+ */
+__rte_experimental
+bool rte_hcdev_is_valid(int16_t dev_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Get the ID of the next valid computing device initialized in DPDK.
+ *
+ * @param dev_id
+ * The initial device ID to start the research.
+ *
+ * @return
+ * Next device ID corresponding to a valid and initialized computing device,
+ * RTE_HCDEV_ID_NONE if there is none.
+ */
+__rte_experimental
+int16_t rte_hcdev_find_next(int16_t dev_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Macro to iterate over all valid computing devices.
+ *
+ * @param dev_id
+ * The ID of the next possible valid device, usually 0 to iterate all.
+ */
+#define RTE_HCDEV_FOREACH(dev_id) \
+ for (dev_id = rte_hcdev_find_next(0); \
+ dev_id > 0; \
+ dev_id = rte_hcdev_find_next(dev_id + 1))
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Close device.
+ * All resources are released.
+ *
+ * @param dev_id
+ * Device ID to close.
+ *
+ * @return
+ * 0 on success, -rte_errno otherwise:
+ * - ENODEV if invalid dev_id
+ * - EPERM if driver error
+ */
+__rte_experimental
+int rte_hcdev_close(int16_t dev_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Return device specific info.
+ *
+ * @param dev_id
+ * Device ID to get info.
+ * @param info
+ * Memory structure to fill with the info.
+ *
+ * @return
+ * 0 on success, -rte_errno otherwise:
+ * - ENODEV if invalid dev_id
+ * - EINVAL if NULL info
+ * - EPERM if driver error
+ */
+__rte_experimental
+int rte_hcdev_info_get(int16_t dev_id, struct rte_hcdev_info *info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTE_HCDEV_H */
new file mode 100644
@@ -0,0 +1,20 @@
+EXPERIMENTAL {
+ global:
+
+ # added in 21.11
+ rte_hcdev_close;
+ rte_hcdev_count_avail;
+ rte_hcdev_find_next;
+ rte_hcdev_info_get;
+ rte_hcdev_init;
+ rte_hcdev_is_valid;
+};
+
+INTERNAL {
+ global:
+
+ rte_hcdev_allocate;
+ rte_hcdev_complete_new;
+ rte_hcdev_get_by_name;
+ rte_hcdev_release;
+};
@@ -35,6 +35,7 @@ libraries = [
'eventdev',
'gro',
'gso',
+ 'hcdev',
'ip_frag',
'jobstats',
'kni',