From 35ce6c809ccf637c6bb8a00ad14b051b87d9884a Mon Sep 17 00:00:00 2001 From: Davide Grohmann Date: Tue, 1 Jun 2021 15:03:51 +0200 Subject: Add support for handling capabilities requests Change-Id: Id5aa197312c88b0c448dc085d8477ed67da24724 --- driver_library/include/ethosu.hpp | 100 ++++++++++++++++- driver_library/src/ethosu.cpp | 54 +++++++++ kernel/ethosu_core_interface.h | 30 +++++ kernel/ethosu_device.c | 166 +++++++++++++++++++++++++++- kernel/ethosu_device.h | 14 +++ kernel/ethosu_mailbox.c | 12 ++ kernel/ethosu_mailbox.h | 8 ++ kernel/uapi/ethosu.h | 51 +++++++++ utils/inference_runner/inference_runner.cpp | 15 +++ 9 files changed, 446 insertions(+), 4 deletions(-) diff --git a/driver_library/include/ethosu.hpp b/driver_library/include/ethosu.hpp index 70d0701..d3f7421 100644 --- a/driver_library/include/ethosu.hpp +++ b/driver_library/include/ethosu.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Arm Limited. All rights reserved. + * Copyright (c) 2020-2021 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -19,10 +19,25 @@ #pragma once #include +#include #include #include #include +/* + *The following undef are necessary to avoid clash with macros in GNU C Library + * if removed the following warning/error are produced: + * + * In the GNU C Library, "major" ("minor") is defined + * by . For historical compatibility, it is + * currently defined by as well, but we plan to + * remove this soon. To use "major" ("minor"), include + * directly. If you did not intend to use a system-defined macro + * "major" ("minor"), you should undefine it after including . + */ +#undef major +#undef minor + namespace EthosU { class Exception : public std::exception { @@ -35,12 +50,95 @@ private: std::string msg; }; +/** + * Sematic Version : major.minor.patch + */ +class SemanticVersion { +public: + SemanticVersion(uint32_t _major = 0, uint32_t _minor = 0, uint32_t _patch = 0) : + major(_major), minor(_minor), patch(_patch){}; + + bool operator==(const SemanticVersion &other); + bool operator<(const SemanticVersion &other); + bool operator<=(const SemanticVersion &other); + bool operator!=(const SemanticVersion &other); + bool operator>(const SemanticVersion &other); + bool operator>=(const SemanticVersion &other); + + uint32_t major; + uint32_t minor; + uint32_t patch; +}; + +std::ostream &operator<<(std::ostream &out, const SemanticVersion &v); + +/* + * Hardware Identifier + * @versionStatus: Version status + * @version: Version revision + * @product: Product revision + * @architecture: Architecture revison + */ +struct HardwareId { +public: + HardwareId(uint32_t _versionStatus, + const SemanticVersion &_version, + const SemanticVersion &_product, + const SemanticVersion &_architecture) : + versionStatus(_versionStatus), + version(_version), product(_product), architecture(_architecture) {} + + uint32_t versionStatus; + SemanticVersion version; + SemanticVersion product; + SemanticVersion architecture; +}; + +/* + * Hardware Configuration + * @macsPerClockCycle: MACs per clock cycle + * @cmdStreamVersion: NPU command stream version + * @shramSize: SHRAM size + * @customDma: Custom DMA enabled + */ +struct HardwareConfiguration { +public: + HardwareConfiguration(uint32_t _macsPerClockCycle, + uint32_t _cmdStreamVersion, + uint32_t _shramSize, + bool _customDma) : + macsPerClockCycle(_macsPerClockCycle), + cmdStreamVersion(_cmdStreamVersion), shramSize(_shramSize), customDma(_customDma) {} + + uint32_t macsPerClockCycle; + uint32_t cmdStreamVersion; + uint32_t shramSize; + bool customDma; +}; + +/** + * Device capabilities + * @hwId: Hardware + * @driver: Driver revision + * @hwCfg Hardware configuration + */ +class Capabilities { +public: + Capabilities(const HardwareId &_hwId, const HardwareConfiguration &_hwCfg, const SemanticVersion &_driver) : + hwId(_hwId), hwCfg(_hwCfg), driver(_driver) {} + + HardwareId hwId; + HardwareConfiguration hwCfg; + SemanticVersion driver; +}; + class Device { public: Device(const char *device = "/dev/ethosu0"); virtual ~Device(); int ioctl(unsigned long cmd, void *data = nullptr); + Capabilities capabilities(); private: int fd; diff --git a/driver_library/src/ethosu.cpp b/driver_library/src/ethosu.cpp index 1768271..ea1f7f4 100644 --- a/driver_library/src/ethosu.cpp +++ b/driver_library/src/ethosu.cpp @@ -111,6 +111,43 @@ const char *Exception::what() const throw() { return msg.c_str(); } +/**************************************************************************** + * Semantic Version + ****************************************************************************/ + +bool SemanticVersion::operator==(const SemanticVersion &other) { + return other.major == major && other.minor == minor && other.patch == patch; +} + +bool SemanticVersion::operator<(const SemanticVersion &other) { + if (other.major > major) + return true; + if (other.minor > minor) + return true; + return other.patch > patch; +} + +bool SemanticVersion::operator<=(const SemanticVersion &other) { + return *this < other || *this == other; +} + +bool SemanticVersion::operator!=(const SemanticVersion &other) { + return !(*this == other); +} + +bool SemanticVersion::operator>(const SemanticVersion &other) { + return !(*this <= other); +} + +bool SemanticVersion::operator>=(const SemanticVersion &other) { + return !(*this < other); +} + +ostream &operator<<(ostream &out, const SemanticVersion &v) { + return out << "{ major=" << unsigned(v.major) << ", minor=" << unsigned(v.minor) << ", patch=" << unsigned(v.patch) + << " }"; +} + /**************************************************************************** * Device ****************************************************************************/ @@ -130,6 +167,23 @@ int Device::ioctl(unsigned long cmd, void *data) { return eioctl(fd, cmd, data); } +Capabilities Device::capabilities() { + ethosu_uapi_device_capabilities uapi; + (void)eioctl(fd, ETHOSU_IOCTL_CAPABILITIES_REQ, static_cast(&uapi)); + + Capabilities capabilities( + HardwareId(uapi.hw_id.version_status, + SemanticVersion(uapi.hw_id.version_major, uapi.hw_id.version_minor), + SemanticVersion(uapi.hw_id.product_major), + SemanticVersion(uapi.hw_id.arch_major_rev, uapi.hw_id.arch_minor_rev, uapi.hw_id.arch_patch_rev)), + HardwareConfiguration(uapi.hw_cfg.macs_per_cc, + uapi.hw_cfg.cmd_stream_version, + uapi.hw_cfg.shram_size, + bool(uapi.hw_cfg.custom_dma)), + SemanticVersion(uapi.driver_major_rev, uapi.driver_minor_rev, uapi.driver_patch_rev)); + return capabilities; +} + /**************************************************************************** * Buffer ****************************************************************************/ diff --git a/kernel/ethosu_core_interface.h b/kernel/ethosu_core_interface.h index 4e7a864..ad8311c 100644 --- a/kernel/ethosu_core_interface.h +++ b/kernel/ethosu_core_interface.h @@ -55,6 +55,8 @@ enum ethosu_core_msg_type { ETHOSU_CORE_MSG_INFERENCE_RSP, ETHOSU_CORE_MSG_VERSION_REQ, ETHOSU_CORE_MSG_VERSION_RSP, + ETHOSU_CORE_MSG_CAPABILITIES_REQ, + ETHOSU_CORE_MSG_CAPABILITIES_RSP, ETHOSU_CORE_MSG_MAX }; @@ -128,6 +130,34 @@ struct ethosu_core_msg_version { uint8_t _reserved; }; +/** + * struct ethosu_core_capabilities_req - Message capabilities request + */ +struct ethosu_core_capabilities_req { + uint64_t user_arg; +}; + +/** + * struct ethosu_core_capabilities_rsp - Message capabilities response + */ +struct ethosu_core_msg_capabilities_rsp { + uint64_t user_arg; + uint32_t version_status; + uint32_t version_minor; + uint32_t version_major; + uint32_t product_major; + uint32_t arch_patch_rev; + uint32_t arch_minor_rev; + uint32_t arch_major_rev; + uint32_t driver_patch_rev; + uint32_t driver_minor_rev; + uint32_t driver_major_rev; + uint32_t macs_per_cc; + uint32_t cmd_stream_version; + uint32_t shram_size; + uint32_t custom_dma; +}; + /** * enum ethosu_core_msg_err_type - Error types */ diff --git a/kernel/ethosu_device.c b/kernel/ethosu_device.c index e82b4c0..7adc7a6 100644 --- a/kernel/ethosu_device.c +++ b/kernel/ethosu_device.c @@ -38,6 +38,7 @@ #include #include #include +#include /**************************************************************************** * Defines @@ -47,6 +48,8 @@ #define MINOR_COUNT 1 /* Allocate 1 minor version */ #define DMA_ADDR_BITS 32 /* Number of address bits */ +#define CAPABILITIES_RESP_TIMEOUT_MS 50 + /**************************************************************************** * Types ****************************************************************************/ @@ -55,6 +58,71 @@ * Functions ****************************************************************************/ +static void ethosu_capabilities_destroy(struct kref *kref) +{ + struct ethosu_capabilities *cap = + container_of(kref, struct ethosu_capabilities, refcount); + + list_del(&cap->list); + + devm_kfree(cap->edev->dev, cap); +} + +static int ethosu_capabilities_find(struct ethosu_capabilities *cap, + struct list_head *capabilties_list) +{ + struct ethosu_capabilities *cur; + + list_for_each_entry(cur, capabilties_list, list) { + if (cur == cap) + return 0; + } + + return -EINVAL; +} + +static int ethosu_capability_rsp(struct ethosu_device *edev, + struct ethosu_core_msg_capabilities_rsp *msg) +{ + struct ethosu_capabilities *cap; + struct ethosu_uapi_device_capabilities *capabilities; + int ret; + + cap = (struct ethosu_capabilities *)msg->user_arg; + ret = ethosu_capabilities_find(cap, &edev->capabilities_list); + if (0 != ret) { + dev_warn(edev->dev, + "Handle not found in capabilities list. handle=0x%p\n", + cap); + + /* NOTE: do not call complete or kref_put on invalid data! */ + return ret; + } + + capabilities = cap->capabilities; + + capabilities->hw_id.version_status = msg->version_status; + capabilities->hw_id.version_minor = msg->version_minor; + capabilities->hw_id.version_major = msg->version_major; + capabilities->hw_id.product_major = msg->product_major; + capabilities->hw_id.arch_patch_rev = msg->arch_patch_rev; + capabilities->hw_id.arch_minor_rev = msg->arch_minor_rev; + capabilities->hw_id.arch_major_rev = msg->arch_major_rev; + capabilities->driver_patch_rev = msg->driver_patch_rev; + capabilities->driver_minor_rev = msg->driver_minor_rev; + capabilities->driver_major_rev = msg->driver_major_rev; + capabilities->hw_cfg.macs_per_cc = msg->macs_per_cc; + capabilities->hw_cfg.cmd_stream_version = msg->cmd_stream_version; + capabilities->hw_cfg.shram_size = msg->shram_size; + capabilities->hw_cfg.custom_dma = msg->custom_dma; + + complete(&cap->done); + + kref_put(&cap->refcount, ethosu_capabilities_destroy); + + return 0; +} + /* Incoming messages */ static int ethosu_handle_msg(struct ethosu_device *edev) { @@ -62,9 +130,10 @@ static int ethosu_handle_msg(struct ethosu_device *edev) struct ethosu_core_msg header; union { - struct ethosu_core_msg_err error; - struct ethosu_core_inference_rsp inf; - struct ethosu_core_msg_version version; + struct ethosu_core_msg_err error; + struct ethosu_core_inference_rsp inf; + struct ethosu_core_msg_version version; + struct ethosu_core_msg_capabilities_rsp capabilities; } data; /* Read message */ @@ -133,7 +202,35 @@ static int ethosu_handle_msg(struct ethosu_device *edev) } break; + case ETHOSU_CORE_MSG_CAPABILITIES_RSP: + if (header.length != sizeof(data.capabilities)) { + dev_warn(edev->dev, + "Msg: Capabilities response of incorrect size. size=%u, expected=%zu\n", header.length, + sizeof(data.capabilities)); + ret = -EBADMSG; + break; + } + dev_info(edev->dev, + "Msg: Capabilities response ua%llx vs%hhu v%hhu.%hhu p%hhu av%hhu.%hhu.%hhu dv%hhu.%hhu.%hhu mcc%hhu csv%hhu ss%hhu cd%hhu\n", + data.capabilities.user_arg, + data.capabilities.version_status, + data.capabilities.version_major, + data.capabilities.version_minor, + data.capabilities.product_major, + data.capabilities.arch_major_rev, + data.capabilities.arch_minor_rev, + data.capabilities.arch_patch_rev, + data.capabilities.driver_major_rev, + data.capabilities.driver_minor_rev, + data.capabilities.driver_patch_rev, + data.capabilities.macs_per_cc, + data.capabilities.cmd_stream_version, + data.capabilities.shram_size, + data.capabilities.custom_dma); + + ret = ethosu_capability_rsp(edev, &data.capabilities); + break; default: /* This should not happen due to version checks */ dev_warn(edev->dev, "Msg: Protocol error\n"); @@ -157,6 +254,64 @@ static int ethosu_open(struct inode *inode, return nonseekable_open(inode, file); } +static int ethosu_send_capabilities_request(struct ethosu_device *edev, + void __user *udata) +{ + struct ethosu_uapi_device_capabilities uapi; + struct ethosu_capabilities *cap; + int ret; + int timeout; + + cap = devm_kzalloc(edev->dev, sizeof(struct ethosu_capabilities), + GFP_KERNEL); + if (!cap) + return -ENOMEM; + + cap->edev = edev; + cap->capabilities = &uapi; + kref_init(&cap->refcount); + init_completion(&cap->done); + list_add(&cap->list, &edev->capabilities_list); + + ret = ethosu_mailbox_capabilities_request(&edev->mailbox, cap); + if (0 != ret) + goto put_kref; + + /* + * Increase ref counter since we sent the pointer out to + * response handler thread. That thread is responsible to + * decrease the ref counter before exiting. So the memory + * can be freed. + * + * NOTE: if no response is received back, the memory is leaked. + */ + kref_get(&cap->refcount); + /* Unlock the mutex before going to block on the condition */ + mutex_unlock(&edev->mutex); + /* wait for response to arrive back */ + timeout = wait_for_completion_timeout(&cap->done, + msecs_to_jiffies( + CAPABILITIES_RESP_TIMEOUT_MS)); + /* take back the mutex before resuming to do anything */ + ret = mutex_lock_interruptible(&edev->mutex); + if (0 != ret) + goto put_kref; + + if (0 == timeout /* timed out*/) { + dev_warn(edev->dev, + "Msg: Capabilities response lost - timeout\n"); + ret = -EIO; + goto put_kref; + } + + ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0; + +put_kref: + kref_put(&cap->refcount, ethosu_capabilities_destroy); + + return ret; +} + static long ethosu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -176,6 +331,10 @@ static long ethosu_ioctl(struct file *file, dev_info(edev->dev, "Ioctl: Send version request\n"); ret = ethosu_mailbox_version_request(&edev->mailbox); break; + case ETHOSU_IOCTL_CAPABILITIES_REQ: + dev_info(edev->dev, "Ioctl: Send capabilities request\n"); + ret = ethosu_send_capabilities_request(edev, udata); + break; case ETHOSU_IOCTL_PING: { dev_info(edev->dev, "Ioctl: Send ping\n"); ret = ethosu_mailbox_ping(&edev->mailbox); @@ -257,6 +416,7 @@ int ethosu_dev_init(struct ethosu_device *edev, edev->dev = dev; edev->class = class; mutex_init(&edev->mutex); + INIT_LIST_HEAD(&edev->capabilities_list); INIT_LIST_HEAD(&edev->inference_list); ret = of_reserved_mem_device_init(edev->dev); diff --git a/kernel/ethosu_device.h b/kernel/ethosu_device.h index 4e4f59d..0722814 100644 --- a/kernel/ethosu_device.h +++ b/kernel/ethosu_device.h @@ -25,6 +25,7 @@ * Includes ****************************************************************************/ +#include "uapi/ethosu.h" #include "ethosu_mailbox.h" #include @@ -32,6 +33,7 @@ #include #include #include +#include /**************************************************************************** * Types @@ -47,9 +49,21 @@ struct ethosu_device { dev_t devt; struct mutex mutex; struct ethosu_mailbox mailbox; + struct list_head capabilities_list; struct list_head inference_list; }; +/** + * struct ethosu_capabilities - Capabilities internal struct + */ +struct ethosu_capabilities { + struct ethosu_device *edev; + struct completion done; + struct kref refcount; + struct ethosu_uapi_device_capabilities *capabilities; + struct list_head list; +}; + /**************************************************************************** * Functions ****************************************************************************/ diff --git a/kernel/ethosu_mailbox.c b/kernel/ethosu_mailbox.c index f61c181..158a7f5 100644 --- a/kernel/ethosu_mailbox.c +++ b/kernel/ethosu_mailbox.c @@ -213,6 +213,18 @@ int ethosu_mailbox_version_request(struct ethosu_mailbox *mbox) 0); } +int ethosu_mailbox_capabilities_request(struct ethosu_mailbox *mbox, + void *user_arg) +{ + struct ethosu_core_capabilities_req req = { + .user_arg = (ptrdiff_t)user_arg + }; + + return ethosu_queue_write_msg(mbox, ETHOSU_CORE_MSG_CAPABILITIES_REQ, + &req, + sizeof(req)); +} + int ethosu_mailbox_inference(struct ethosu_mailbox *mbox, void *user_arg, uint32_t ifm_count, diff --git a/kernel/ethosu_mailbox.h b/kernel/ethosu_mailbox.h index 0bc5ffb..5cd5e62 100644 --- a/kernel/ethosu_mailbox.h +++ b/kernel/ethosu_mailbox.h @@ -113,6 +113,14 @@ int ethosu_mailbox_pong(struct ethosu_mailbox *mbox); */ int ethosu_mailbox_version_request(struct ethosu_mailbox *mbox); +/** + * ethosu_mailbox_capabilities_request() - Send capabilities request + * + * Return: 0 on success, else error code. + */ +int ethosu_mailbox_capabilities_request(struct ethosu_mailbox *mbox, + void *user_arg); + /** * ethosu_mailbox_inference() - Send inference * diff --git a/kernel/uapi/ethosu.h b/kernel/uapi/ethosu.h index 1e53c91..3e4e7a3 100644 --- a/kernel/uapi/ethosu.h +++ b/kernel/uapi/ethosu.h @@ -44,6 +44,7 @@ namespace EthosU { #define ETHOSU_IOCTL_PING ETHOSU_IO(0x00) #define ETHOSU_IOCTL_VERSION_REQ ETHOSU_IO(0x01) +#define ETHOSU_IOCTL_CAPABILITIES_REQ ETHOSU_IO(0x02) #define ETHOSU_IOCTL_BUFFER_CREATE ETHOSU_IOR(0x10, \ struct ethosu_uapi_buffer_create) #define ETHOSU_IOCTL_BUFFER_SET ETHOSU_IOR(0x11, \ @@ -124,6 +125,56 @@ struct ethosu_uapi_pmu_counts { __u64 cycle_count; }; +/** + * struct ethosu_uapi_device_hw_id - Device hardware identification + * @version_status: Version status + * @version_minor: Version minor + * @version_major: Version major + * @product_major: Product major + * @arch_patch_rev: Architecture version patch + * @arch_minor_rev: Architecture version minor + * @arch_major_rev: Architecture version major + */ +struct ethosu_uapi_device_hw_id { + __u32 version_status; + __u32 version_minor; + __u32 version_major; + __u32 product_major; + __u32 arch_patch_rev; + __u32 arch_minor_rev; + __u32 arch_major_rev; +}; + +/** + * struct ethosu_uapi_device_hw_cfg - Device hardware configuration + * @macs_per_cc: MACs per clock cycle + * @cmd_stream_version: NPU command stream version + * @shram_size: SHRAM size + * @custom_dma: Custom DMA enabled + */ +struct ethosu_uapi_device_hw_cfg { + __u32 macs_per_cc; + __u32 cmd_stream_version; + __u32 shram_size; + __u32 custom_dma; +}; + +/** + * struct ethosu_uapi_capabilities - Device capabilities + * @hw_id: Hardware identification + * @hw_cfg: Hardware configuration + * @driver_patch_rev: Driver version patch + * @driver_minor_rev: Driver version minor + * @driver_major_rev: Driver version major + */ +struct ethosu_uapi_device_capabilities { + struct ethosu_uapi_device_hw_id hw_id; + struct ethosu_uapi_device_hw_cfg hw_cfg; + __u32 driver_patch_rev; + __u32 driver_minor_rev; + __u32 driver_major_rev; +}; + /** * struct ethosu_uapi_inference_create - Create network request * @ifm_count: Number of IFM file descriptors diff --git a/utils/inference_runner/inference_runner.cpp b/utils/inference_runner/inference_runner.cpp index 7ceec71..17969da 100644 --- a/utils/inference_runner/inference_runner.cpp +++ b/utils/inference_runner/inference_runner.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -209,6 +210,20 @@ int main(int argc, char *argv[]) { cout << "Send version request" << endl; device.ioctl(ETHOSU_IOCTL_VERSION_REQ); + cout << "Send capabilities request" << endl; + Capabilities capabilities = device.capabilities(); + + cout << "Capabilities:" << endl + << "\tversion_status:" << unsigned(capabilities.hwId.versionStatus) << endl + << "\tversion:" << capabilities.hwId.version << endl + << "\tproduct:" << capabilities.hwId.product << endl + << "\tarchitecture:" << capabilities.hwId.architecture << endl + << "\tdriver:" << capabilities.driver << endl + << "\tmacs_per_cc:" << unsigned(capabilities.hwCfg.macsPerClockCycle) << endl + << "\tcmd_stream_version:" << unsigned(capabilities.hwCfg.cmdStreamVersion) << endl + << "\tshram_size:" << unsigned(capabilities.hwCfg.shramSize) << endl + << "\tcustom_dma:" << std::boolalpha << capabilities.hwCfg.customDma << endl; + /* Create network */ cout << "Create network" << endl; shared_ptr networkBuffer = allocAndFill(device, networkArg); -- cgit v1.2.1