diff options
Diffstat (limited to 'kernel/rpmsg')
-rw-r--r-- | kernel/rpmsg/ethosu_rpmsg_cancel_inference.c | 191 | ||||
-rw-r--r-- | kernel/rpmsg/ethosu_rpmsg_capabilities.c | 164 | ||||
-rw-r--r-- | kernel/rpmsg/ethosu_rpmsg_inference.c | 486 | ||||
-rw-r--r-- | kernel/rpmsg/ethosu_rpmsg_mailbox.c | 363 | ||||
-rw-r--r-- | kernel/rpmsg/ethosu_rpmsg_network.c | 255 | ||||
-rw-r--r-- | kernel/rpmsg/ethosu_rpmsg_network_info.c | 187 | ||||
-rw-r--r-- | kernel/rpmsg/ethosu_rpmsg_version.c | 147 |
7 files changed, 1793 insertions, 0 deletions
diff --git a/kernel/rpmsg/ethosu_rpmsg_cancel_inference.c b/kernel/rpmsg/ethosu_rpmsg_cancel_inference.c new file mode 100644 index 0000000..5d46434 --- /dev/null +++ b/kernel/rpmsg/ethosu_rpmsg_cancel_inference.c @@ -0,0 +1,191 @@ +/* + * SPDX-FileCopyrightText: Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <rpmsg/ethosu_rpmsg_cancel_inference.h> + +#include <common/ethosu_device.h> +#include <rpmsg/ethosu_rpmsg.h> +#include <rpmsg/ethosu_rpmsg_inference.h> + +#include <linux/remoteproc.h> +#include <linux/wait.h> + +/**************************************************************************** + * Defines + ****************************************************************************/ + +#define CANCEL_INFERENCE_RESP_TIMEOUT_MS 2000 + +/**************************************************************************** + * Functions + ****************************************************************************/ + +static int ethosu_cancel_inference_send( + struct ethosu_cancel_inference *cancellation, + struct ethosu_mailbox *mailbox) +{ + return ethosu_mailbox_cancel_inference(mailbox, + &cancellation->msg, + cancellation->inf->msg.id); +} + +static void ethosu_cancel_inference_fail(struct ethosu_mailbox_msg *msg) +{ + struct ethosu_cancel_inference *cancellation = + container_of(msg, typeof(*cancellation), msg); + + if (completion_done(&cancellation->done)) + return; + + cancellation->errno = -EFAULT; + cancellation->uapi->status = ETHOSU_UAPI_STATUS_ERROR; + complete(&cancellation->done); +} + +int ethosu_cancel_inference_request(struct device *dev, + struct ethosu_mailbox *mailbox, + struct ethosu_inference *inf, + struct ethosu_uapi_cancel_inference_status *uapi) +{ + struct ethosu_cancel_inference *cancellation; + int ret; + int timeout; + + if (inf->done) { + uapi->status = ETHOSU_UAPI_STATUS_ERROR; + + return 0; + } + + cancellation = + devm_kzalloc(dev, + sizeof(struct ethosu_cancel_inference), + GFP_KERNEL); + if (!cancellation) + return -ENOMEM; + + /* increase ref count on the inference we are refering to */ + ethosu_inference_get(inf); + /* mark inference ABORTING to avoid resending the inference message */ + inf->status = ETHOSU_UAPI_STATUS_ABORTING; + + cancellation->dev = dev; + cancellation->inf = inf; + cancellation->uapi = uapi; + init_completion(&cancellation->done); + cancellation->msg.fail = ethosu_cancel_inference_fail; + + ret = ethosu_mailbox_register(mailbox, + &cancellation->msg); + if (ret < 0) + goto kfree; + + dev_dbg(dev, + "Inference cancellation create. cancel=0x%pK, msg.id=%ddev", + cancellation, cancellation->msg.id); + + ret = ethosu_cancel_inference_send(cancellation, mailbox); + if (0 != ret) + goto deregister; + + /* Unlock the mutex before going to block on the condition */ + device_unlock(dev); + + /* wait for response to arrive back */ + timeout = wait_for_completion_timeout(&cancellation->done, + msecs_to_jiffies( + CANCEL_INFERENCE_RESP_TIMEOUT_MS)); + /* take back the mutex before resuming to do anything */ + ret = device_lock_interruptible(dev); + if (0 != ret) + goto deregister; + + if (0 == timeout /* timed out*/) { + dev_warn(dev, + "Msg: Cancel Inference response lost - timeoutdev"); + ret = -EIO; + + rproc_report_crash(rproc_get_by_child(dev), RPROC_FATAL_ERROR); + goto deregister; + } + + if (cancellation->errno) { + ret = cancellation->errno; + rproc_report_crash(rproc_get_by_child(dev), RPROC_FATAL_ERROR); + goto deregister; + } + + if (inf->status != ETHOSU_UAPI_STATUS_ABORTED) + inf->status = ETHOSU_UAPI_STATUS_ABORTED; + +deregister: + ethosu_mailbox_deregister(mailbox, + &cancellation->msg); + +kfree: + dev_dbg(dev, + "Cancel inference destroy. cancel=0x%pK", cancellation); + + /* decrease the reference on the inference we are refering to */ + ethosu_inference_put(cancellation->inf); + devm_kfree(dev, cancellation); + + return ret; +} + +void ethosu_cancel_inference_rsp(struct ethosu_mailbox *mailbox, + int msg_id, + struct ethosu_core_msg_cancel_inference_rsp *rsp) +{ + struct device *dev = mailbox->dev; + struct ethosu_mailbox_msg *msg; + struct ethosu_cancel_inference *cancellation; + + msg = ethosu_mailbox_find(mailbox, msg_id, + ETHOSU_CORE_MSG_CANCEL_INFERENCE_REQ); + if (IS_ERR(msg)) { + dev_warn(dev, + "Id for cancel inference msg not found. Id=0x%x: %ld", + msg_id, PTR_ERR(msg)); + + return; + } + + cancellation = container_of(msg, typeof(*cancellation), msg); + + if (completion_done(&cancellation->done)) + return; + + cancellation->errno = 0; + switch (rsp->status) { + case ETHOSU_CORE_STATUS_OK: + cancellation->uapi->status = ETHOSU_UAPI_STATUS_OK; + break; + case ETHOSU_CORE_STATUS_ERROR: + cancellation->uapi->status = ETHOSU_UAPI_STATUS_ERROR; + break; + } + + complete(&cancellation->done); +} diff --git a/kernel/rpmsg/ethosu_rpmsg_capabilities.c b/kernel/rpmsg/ethosu_rpmsg_capabilities.c new file mode 100644 index 0000000..73ce2dc --- /dev/null +++ b/kernel/rpmsg/ethosu_rpmsg_capabilities.c @@ -0,0 +1,164 @@ +/* + * SPDX-FileCopyrightText: Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <rpmsg/ethosu_rpmsg_capabilities.h> + +#include <common/ethosu_device.h> +#include <rpmsg/ethosu_rpmsg.h> + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> + +/**************************************************************************** + * Defines + ****************************************************************************/ + +#define CAPABILITIES_RESP_TIMEOUT_MS 2000 + +/**************************************************************************** + * Functions + ****************************************************************************/ + +static inline int ethosu_capabilities_send(struct ethosu_capabilities *cap, + struct ethosu_mailbox *mailbox) +{ + return ethosu_mailbox_capabilities_request(mailbox, + &cap->msg); +} + +static void ethosu_capabilities_fail(struct ethosu_mailbox_msg *msg) +{ + struct ethosu_capabilities *cap = + container_of(msg, typeof(*cap), msg); + + if (completion_done(&cap->done)) + return; + + cap->errno = -EFAULT; + complete(&cap->done); +} + +void ethosu_capability_rsp(struct ethosu_mailbox *mailbox, + int msg_id, + struct ethosu_core_msg_capabilities_rsp *rsp) +{ + struct device *dev = mailbox->dev; + struct ethosu_mailbox_msg *msg; + struct ethosu_capabilities *cap; + + msg = ethosu_mailbox_find(mailbox, msg_id, + ETHOSU_CORE_MSG_CAPABILITIES_REQ); + if (IS_ERR(msg)) { + dev_warn(dev, + "Id for capabilities msg not found. Id=0x%0x: %ld\n", + msg_id, PTR_ERR(msg)); + + return; + } + + cap = container_of(msg, typeof(*cap), msg); + + if (completion_done(&cap->done)) + return; + + cap->uapi->hw_id.version_status = rsp->version_status; + cap->uapi->hw_id.version_minor = rsp->version_minor; + cap->uapi->hw_id.version_major = rsp->version_major; + cap->uapi->hw_id.product_major = rsp->product_major; + cap->uapi->hw_id.arch_patch_rev = rsp->arch_patch_rev; + cap->uapi->hw_id.arch_minor_rev = rsp->arch_minor_rev; + cap->uapi->hw_id.arch_major_rev = rsp->arch_major_rev; + cap->uapi->driver_patch_rev = rsp->driver_patch_rev; + cap->uapi->driver_minor_rev = rsp->driver_minor_rev; + cap->uapi->driver_major_rev = rsp->driver_major_rev; + cap->uapi->hw_cfg.macs_per_cc = rsp->macs_per_cc; + cap->uapi->hw_cfg.cmd_stream_version = rsp->cmd_stream_version; + cap->uapi->hw_cfg.custom_dma = rsp->custom_dma; + + cap->errno = 0; + complete(&cap->done); +} + +int ethosu_capabilities_request(struct device *dev, + struct ethosu_mailbox *mailbox, + struct ethosu_uapi_device_capabilities *uapi) +{ + struct ethosu_capabilities *cap; + int ret; + int timeout; + + cap = devm_kzalloc(dev, sizeof(struct ethosu_capabilities), + GFP_KERNEL); + if (!cap) + return -ENOMEM; + + cap->dev = dev; + cap->uapi = uapi; + init_completion(&cap->done); + cap->msg.fail = ethosu_capabilities_fail; + + ret = ethosu_mailbox_register(mailbox, &cap->msg); + if (ret < 0) + goto kfree; + + dev_dbg(dev, "Capabilities create. Id=%d, handle=0x%p\n", + cap->msg.id, cap); + + ret = ethosu_capabilities_send(cap, mailbox); + if (0 != ret) + goto deregister; + + /* Unlock the mutex before going to block on the condition */ + device_unlock(dev); + + /* 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 */ + device_lock(dev); + + if (0 == timeout) { + dev_warn(dev, "Capabilities response timeout"); + ret = -ETIME; + goto deregister; + } + + if (cap->errno) { + ret = cap->errno; + goto deregister; + } + +deregister: + ethosu_mailbox_deregister(mailbox, &cap->msg); + +kfree: + dev_dbg(dev, "Capabilities destroy. Id=%d, handle=0x%p\n", + cap->msg.id, cap); + devm_kfree(dev, cap); + + return ret; +} diff --git a/kernel/rpmsg/ethosu_rpmsg_inference.c b/kernel/rpmsg/ethosu_rpmsg_inference.c new file mode 100644 index 0000000..b721fb7 --- /dev/null +++ b/kernel/rpmsg/ethosu_rpmsg_inference.c @@ -0,0 +1,486 @@ +/* + * SPDX-FileCopyrightText: Copyright 2020, 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <rpmsg/ethosu_rpmsg_inference.h> + +#include <common/ethosu_buffer.h> +#include <common/ethosu_device.h> +#include <rpmsg/ethosu_rpmsg.h> +#include <rpmsg/ethosu_rpmsg_cancel_inference.h> +#include <rpmsg/ethosu_rpmsg_network.h> + +#include <linux/anon_inodes.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/poll.h> + +/**************************************************************************** + * Variables + ****************************************************************************/ + +static int ethosu_inference_release(struct inode *inode, + struct file *file); + +static __poll_t ethosu_inference_poll(struct file *file, + poll_table *wait); + +static long ethosu_inference_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg); + +static const struct file_operations ethosu_inference_fops = { + .release = ðosu_inference_release, + .poll = ðosu_inference_poll, + .unlocked_ioctl = ðosu_inference_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ðosu_inference_ioctl, +#endif +}; + +/**************************************************************************** + * Functions + ****************************************************************************/ + +static const char *status_to_string(const enum ethosu_uapi_status status) +{ + switch (status) { + case ETHOSU_UAPI_STATUS_OK: { + return "Ok"; + } + case ETHOSU_UAPI_STATUS_ERROR: { + return "Error"; + } + case ETHOSU_UAPI_STATUS_RUNNING: { + return "Running"; + } + case ETHOSU_UAPI_STATUS_REJECTED: { + return "Rejected"; + } + case ETHOSU_UAPI_STATUS_ABORTED: { + return "Aborted"; + } + case ETHOSU_UAPI_STATUS_ABORTING: { + return "Aborting"; + } + default: { + return "Unknown"; + } + } +} + +static int ethosu_inference_send(struct ethosu_inference *inf) +{ + struct device *dev = inf->dev; + int ret; + + inf->status = ETHOSU_UAPI_STATUS_ERROR; + + ret = ethosu_mailbox_inference(inf->mailbox, &inf->msg, + inf->ifm_count, inf->ifm, + inf->ofm_count, inf->ofm, + inf->net, inf->pmu_event_config, + ETHOSU_PMU_EVENT_MAX, + inf->pmu_cycle_counter_enable); + if (ret) { + dev_warn(dev, + "Failed to send inference request. inf=0x%pK, ret=%d", + inf, ret); + + return ret; + } + + inf->status = ETHOSU_UAPI_STATUS_RUNNING; + + ethosu_inference_get(inf); + + return 0; +} + +static void ethosu_inference_fail(struct ethosu_mailbox_msg *msg) +{ + struct ethosu_inference *inf = + container_of(msg, typeof(*inf), msg); + int ret; + + if (inf->done) + return; + + /* Decrement reference count if inference was pending reponse */ + ret = ethosu_inference_put(inf); + if (ret) + return; + + /* Set status accordingly to the inference state */ + inf->status = inf->status == ETHOSU_UAPI_STATUS_ABORTING ? + ETHOSU_UAPI_STATUS_ABORTED : + ETHOSU_UAPI_STATUS_ERROR; + /* Mark it done and wake up the waiting process */ + inf->done = true; + wake_up_interruptible(&inf->waitq); +} + +static bool ethosu_inference_verify(struct file *file) +{ + return file->f_op == ðosu_inference_fops; +} + +static void ethosu_inference_kref_destroy(struct kref *kref) +{ + struct ethosu_inference *inf = + container_of(kref, struct ethosu_inference, kref); + struct device *dev = inf->dev; + + dev_dbg(dev, + "Inference destroy. inf=0x%pK, status=%d, ifm_count=%u, ofm_count=%u", + inf, inf->status, inf->ifm_count, inf->ofm_count); + + ethosu_mailbox_deregister(inf->mailbox, &inf->msg); + + while (inf->ifm_count-- > 0) + ethosu_buffer_put(inf->ifm[inf->ifm_count]); + + while (inf->ofm_count-- > 0) + ethosu_buffer_put(inf->ofm[inf->ofm_count]); + + ethosu_network_put(inf->net); + memset(inf, 0, sizeof(*inf)); + devm_kfree(dev, inf); +} + +static int ethosu_inference_release(struct inode *inode, + struct file *file) +{ + struct ethosu_inference *inf = file->private_data; + struct device *dev = inf->dev; + + dev_dbg(dev, + "Inference release. file=0x%pK, inf=0x%pK", + file, inf); + + device_lock(dev); + ethosu_inference_put(inf); + device_unlock(dev); + + return 0; +} + +static __poll_t ethosu_inference_poll(struct file *file, + poll_table *wait) +{ + struct ethosu_inference *inf = file->private_data; + __poll_t ret = 0; + + poll_wait(file, &inf->waitq, wait); + + if (inf->done) + ret |= EPOLLIN; + + return ret; +} + +static long ethosu_inference_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ethosu_inference *inf = file->private_data; + struct device *dev = inf->dev; + void __user *udata = (void __user *)arg; + int ret; + + ret = device_lock_interruptible(dev); + if (ret) + return ret; + + switch (cmd) { + case ETHOSU_IOCTL_INFERENCE_STATUS: { + struct ethosu_uapi_result_status uapi = { 0 }; + int i; + + uapi.status = inf->status; + + for (i = 0; i < ETHOSU_PMU_EVENT_MAX; i++) { + uapi.pmu_config.events[i] = + inf->pmu_event_config[i]; + uapi.pmu_count.events[i] = + inf->pmu_event_count[i]; + } + + uapi.pmu_config.cycle_count = inf->pmu_cycle_counter_enable; + uapi.pmu_count.cycle_count = inf->pmu_cycle_counter_count; + + dev_dbg(dev, + "Inference ioctl: Inference status. status=%s (%d)\n", + status_to_string(uapi.status), uapi.status); + + ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0; + + break; + } + case ETHOSU_IOCTL_INFERENCE_CANCEL: { + struct ethosu_uapi_cancel_inference_status uapi = { 0 }; + + dev_dbg(dev, + "Inference ioctl: Cancel Inference. Handle=%p\n", + inf); + + ret = ethosu_cancel_inference_request(dev, inf->mailbox, inf, + &uapi); + if (ret) + break; + + ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0; + + break; + } + default: { + dev_err(dev, "Invalid ioctl. cmd=%u, arg=%lu\n", + cmd, arg); + ret = -ENOIOCTLCMD; + break; + } + } + + device_unlock(dev); + + return ret; +} + +int ethosu_inference_create(struct device *dev, + struct ethosu_mailbox *mailbox, + struct ethosu_network *net, + struct ethosu_uapi_inference_create *uapi) +{ + struct ethosu_inference *inf; + uint32_t i; + int fd; + int ret = -ENOMEM; + + if (uapi->ifm_count > ETHOSU_FD_MAX || + uapi->ofm_count > ETHOSU_FD_MAX) { + dev_warn(dev, + "Too many IFM and/or OFM buffers for inference. ifm_count=%u, ofm_count=%u", + uapi->ifm_count, uapi->ofm_count); + + return -EFAULT; + } + + inf = devm_kzalloc(dev, sizeof(*inf), GFP_KERNEL); + if (!inf) + return -ENOMEM; + + inf->dev = dev; + inf->mailbox = mailbox; + inf->net = net; + inf->done = false; + inf->status = ETHOSU_UAPI_STATUS_ERROR; + kref_init(&inf->kref); + init_waitqueue_head(&inf->waitq); + inf->msg.fail = ethosu_inference_fail; + + /* Add inference to pending list */ + ret = ethosu_mailbox_register(mailbox, &inf->msg); + if (ret < 0) + goto kfree; + + /* Get pointer to IFM buffers */ + for (i = 0; i < uapi->ifm_count; i++) { + inf->ifm[i] = ethosu_buffer_get_from_fd(uapi->ifm_fd[i]); + if (IS_ERR(inf->ifm[i])) { + ret = PTR_ERR(inf->ifm[i]); + goto put_ifm; + } + + inf->ifm_count++; + } + + /* Get pointer to OFM buffer */ + for (i = 0; i < uapi->ofm_count; i++) { + inf->ofm[i] = ethosu_buffer_get_from_fd(uapi->ofm_fd[i]); + if (IS_ERR(inf->ofm[i])) { + ret = PTR_ERR(inf->ofm[i]); + goto put_ofm; + } + + inf->ofm_count++; + } + + /* Configure PMU and cycle counter */ + dev_dbg(dev, + "Configuring events for PMU. events=[%u, %u, %u, %u]\n", + uapi->pmu_config.events[0], uapi->pmu_config.events[1], + uapi->pmu_config.events[2], uapi->pmu_config.events[3]); + + /* Configure events and reset count for all events */ + for (i = 0; i < ETHOSU_PMU_EVENT_MAX; i++) { + inf->pmu_event_config[i] = uapi->pmu_config.events[i]; + inf->pmu_event_count[i] = 0; + } + + /* Configure cycle counter and reset any previous count */ + inf->pmu_cycle_counter_enable = uapi->pmu_config.cycle_count; + inf->pmu_cycle_counter_count = 0; + + /* Increment network reference count */ + ethosu_network_get(net); + + /* Send inference request to Arm Ethos-U subsystem */ + ret = ethosu_inference_send(inf); + if (ret) + goto put_net; + + /* Create file descriptor */ + ret = fd = anon_inode_getfd("ethosu-inference", ðosu_inference_fops, + inf, O_RDWR | O_CLOEXEC); + if (ret < 0) + goto put_net; + + /* Store pointer to file structure */ + inf->file = fget(ret); + fput(inf->file); + + dev_dbg(dev, + "Inference create. file=0x%pK, fd=%d, inf=0x%p, net=0x%pK, msg.id=0x%x", + inf->file, fd, inf, inf->net, inf->msg.id); + + return fd; + +put_net: + ethosu_network_put(inf->net); + +put_ofm: + while (inf->ofm_count-- > 0) + ethosu_buffer_put(inf->ofm[inf->ofm_count]); + +put_ifm: + while (inf->ifm_count-- > 0) + ethosu_buffer_put(inf->ifm[inf->ifm_count]); + + ethosu_mailbox_deregister(mailbox, &inf->msg); + +kfree: + memset(inf, 0, sizeof(*inf)); + devm_kfree(dev, inf); + + return ret; +} + +struct ethosu_inference *ethosu_inference_get_from_fd(int fd) +{ + struct ethosu_inference *inf; + struct file *file; + + file = fget(fd); + if (!file) + return ERR_PTR(-EINVAL); + + if (!ethosu_inference_verify(file)) { + fput(file); + + return ERR_PTR(-EINVAL); + } + + inf = file->private_data; + ethosu_inference_get(inf); + fput(file); + + return inf; +} + +void ethosu_inference_get(struct ethosu_inference *inf) +{ + kref_get(&inf->kref); +} + +int ethosu_inference_put(struct ethosu_inference *inf) +{ + return kref_put(&inf->kref, ðosu_inference_kref_destroy); +} + +void ethosu_inference_rsp(struct ethosu_mailbox *mailbox, + int msg_id, + struct ethosu_core_msg_inference_rsp *rsp) +{ + struct device *dev = mailbox->dev; + struct ethosu_mailbox_msg *msg; + struct ethosu_inference *inf; + int i; + + msg = ethosu_mailbox_find(mailbox, msg_id, + ETHOSU_CORE_MSG_INFERENCE_REQ); + if (IS_ERR(msg)) { + dev_warn(dev, + "Id for inference msg not found. Id=0x%x: %ld\n", + msg_id, PTR_ERR(msg)); + + return; + } + + inf = container_of(msg, typeof(*inf), msg); + + /* + * Don't handle the response if the inference is aborted or + * in the process of being aborted + */ + if (inf->status == ETHOSU_UAPI_STATUS_ABORTED || + inf->status == ETHOSU_UAPI_STATUS_ABORTING) { + inf->status = ETHOSU_UAPI_STATUS_ABORTED; + goto done; + } + + if (rsp->status == ETHOSU_CORE_STATUS_OK && + inf->ofm_count <= ETHOSU_CORE_BUFFER_MAX) + inf->status = ETHOSU_UAPI_STATUS_OK; + else if (rsp->status == ETHOSU_CORE_STATUS_REJECTED) + inf->status = ETHOSU_UAPI_STATUS_REJECTED; + else if (rsp->status == ETHOSU_CORE_STATUS_ABORTED) + inf->status = ETHOSU_UAPI_STATUS_ABORTED; + else + inf->status = ETHOSU_UAPI_STATUS_ERROR; + + if (inf->status == ETHOSU_UAPI_STATUS_OK) { + for (i = 0; i < ETHOSU_CORE_PMU_MAX; i++) { + inf->pmu_event_config[i] = rsp->pmu_event_config[i]; + inf->pmu_event_count[i] = rsp->pmu_event_count[i]; + } + + inf->pmu_cycle_counter_enable = rsp->pmu_cycle_counter_enable; + inf->pmu_cycle_counter_count = rsp->pmu_cycle_counter_count; + + dev_dbg(dev, + "PMU events. config=[%u, %u, %u, %u], count=[%llu, %llu, %llu, %llu]\n", + inf->pmu_event_config[0], inf->pmu_event_config[1], + inf->pmu_event_config[2], inf->pmu_event_config[3], + inf->pmu_event_count[0], inf->pmu_event_count[1], + inf->pmu_event_count[2], inf->pmu_event_count[3]); + + if (inf->pmu_cycle_counter_enable) + dev_dbg(dev, + "PMU cycle counter: count=%llu\n", + inf->pmu_cycle_counter_count); + } + +done: + inf->done = true; + wake_up_interruptible(&inf->waitq); + ethosu_inference_put(inf); +} diff --git a/kernel/rpmsg/ethosu_rpmsg_mailbox.c b/kernel/rpmsg/ethosu_rpmsg_mailbox.c new file mode 100644 index 0000000..11ac414 --- /dev/null +++ b/kernel/rpmsg/ethosu_rpmsg_mailbox.c @@ -0,0 +1,363 @@ +/* + * SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <rpmsg/ethosu_rpmsg_mailbox.h> + +#include <common/ethosu_buffer.h> +#include <common/ethosu_device.h> +#include <common/ethosu_dma_mem.h> +#include <rpmsg/ethosu_rpmsg.h> + +#include <rpmsg/ethosu_rpmsg_network.h> + +#include <linux/atomic.h> +#include <linux/jiffies.h> +#include <linux/resource.h> +#include <linux/uio.h> +#include <linux/bug.h> + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#ifndef fallthrough +#if __has_attribute(__fallthrough__) +#define fallthrough __attribute__((__fallthrough__)) +#else +#define fallthrough do {} while (0) /* fallthrough */ +#endif +#endif + +/**************************************************************************** + * Defines + ****************************************************************************/ + +#define MAILBOX_SEND_TIMEOUT_MS 15000 + +/**************************************************************************** + * Functions + ****************************************************************************/ + +/** + * ethosu_send_locked() - Blocking mailbox message sender + * + * Context: Can sleep and must be called with the device mutex locked. + * + * Return: 0 on success, else error code. + */ +static int ethosu_send_locked(struct ethosu_mailbox *mbox, + void *data, + size_t length) +{ + DEFINE_WAIT_FUNC(wait, woken_wake_function); + struct device *dev = mbox->dev; + long timeout = msecs_to_jiffies(MAILBOX_SEND_TIMEOUT_MS); + bool try_send = !wq_has_sleeper(&mbox->send_queue); + int ret; + + might_sleep(); + + /* Exclusive wait to only wake up one task at a time */ + add_wait_queue_exclusive(&mbox->send_queue, &wait); + for (;;) { + /* Stop if the mailbox is closing down */ + if (atomic_read(&mbox->done)) { + ret = -ENODEV; + break; + } + + /* Attempt to send if queue is empty or task was woken up */ + if (try_send) { + ret = rpmsg_trysend(mbox->ept, data, length); + if (ret != -ENOMEM) + break; + } else { + try_send = true; + } + + /* Unlock device mutex while waiting to not block other tasks */ + device_unlock(dev); + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); + device_lock(dev); + + /* Stop if the wait was interrupted */ + if (signal_pending(current)) { + ret = -EINTR; + break; + } + + if (!timeout) { + ret = -ETIME; + break; + } + } + + remove_wait_queue(&mbox->send_queue, &wait); + + /* + * If the message was sent successfully, there may be more TX buffers + * available so wake up the next waiting task. + */ + if (!ret && wq_has_sleeper(&mbox->send_queue)) + wake_up(&mbox->send_queue); + + return ret; +} + +static void ethosu_core_buffer_dma_mem_set(struct ethosu_dma_mem *dma_mem, + struct ethosu_core_buffer *cbuf) +{ + cbuf->ptr = (uint32_t)dma_mem->dma_addr; + cbuf->size = (uint32_t)dma_mem->size; +} + +int ethosu_mailbox_register(struct ethosu_mailbox *mbox, + struct ethosu_mailbox_msg *msg) +{ + WARN_ON_ONCE(!mutex_is_locked(&mbox->dev->mutex)); + msg->id = idr_alloc_cyclic(&mbox->msg_idr, msg, 0, INT_MAX, GFP_KERNEL); + if (msg->id < 0) + return msg->id; + + return 0; +} + +void ethosu_mailbox_deregister(struct ethosu_mailbox *mbox, + struct ethosu_mailbox_msg *msg) +{ + WARN_ON_ONCE(!mutex_is_locked(&mbox->dev->mutex)); + idr_remove(&mbox->msg_idr, msg->id); +} + +struct ethosu_mailbox_msg *ethosu_mailbox_find(struct ethosu_mailbox *mbox, + int msg_id, + uint32_t msg_type) +{ + struct ethosu_mailbox_msg *ptr; + + WARN_ON_ONCE(!mutex_is_locked(&mbox->dev->mutex)); + ptr = (struct ethosu_mailbox_msg *)idr_find(&mbox->msg_idr, msg_id); + + if (ptr == NULL) + return ERR_PTR(-ENOENT); + + if (ptr->type != msg_type) + return ERR_PTR(-EINVAL); + + return ptr; +} + +void ethosu_mailbox_fail(struct ethosu_mailbox *mbox) +{ + struct ethosu_mailbox_msg *cur; + int id; + + WARN_ON_ONCE(!mutex_is_locked(&mbox->dev->mutex)); + idr_for_each_entry(&mbox->msg_idr, cur, id) { + cur->fail(cur); + } +} + +int ethosu_mailbox_ping(struct ethosu_mailbox *mbox) +{ + struct ethosu_core_rpmsg rpmsg = { + .header = { + .magic = ETHOSU_CORE_MSG_MAGIC, + .type = ETHOSU_CORE_MSG_PING, + } + }; + + return ethosu_send_locked(mbox, &rpmsg, sizeof(rpmsg.header)); +} + +int ethosu_mailbox_pong(struct ethosu_mailbox *mbox) +{ + struct ethosu_core_rpmsg rpmsg = { + .header = { + .magic = ETHOSU_CORE_MSG_MAGIC, + .type = ETHOSU_CORE_MSG_PONG, + } + }; + + return ethosu_send_locked(mbox, &rpmsg, sizeof(rpmsg.header)); +} + +int ethosu_mailbox_version_request(struct ethosu_mailbox *mbox, + struct ethosu_mailbox_msg *msg) +{ + struct ethosu_core_rpmsg rpmsg = { + .header = { + .magic = ETHOSU_CORE_MSG_MAGIC, + .type = ETHOSU_CORE_MSG_VERSION_REQ, + .msg_id = msg->id + } + }; + + msg->type = rpmsg.header.type; + + return ethosu_send_locked(mbox, &rpmsg, sizeof(rpmsg.header)); +} + +int ethosu_mailbox_capabilities_request(struct ethosu_mailbox *mbox, + struct ethosu_mailbox_msg *msg) +{ + struct ethosu_core_rpmsg rpmsg = { + .header = { + .magic = ETHOSU_CORE_MSG_MAGIC, + .type = ETHOSU_CORE_MSG_CAPABILITIES_REQ, + .msg_id = msg->id + } + }; + + msg->type = rpmsg.header.type; + + return ethosu_send_locked(mbox, &rpmsg, sizeof(rpmsg.header)); +} + +int ethosu_mailbox_inference(struct ethosu_mailbox *mbox, + struct ethosu_mailbox_msg *msg, + uint32_t ifm_count, + struct ethosu_buffer **ifm, + uint32_t ofm_count, + struct ethosu_buffer **ofm, + struct ethosu_network *network, + uint8_t *pmu_event_config, + uint8_t pmu_event_config_count, + uint8_t pmu_cycle_counter_enable) +{ + struct ethosu_core_rpmsg rpmsg = { + .header = { + .magic = ETHOSU_CORE_MSG_MAGIC, + .type = ETHOSU_CORE_MSG_INFERENCE_REQ, + .msg_id = msg->id + } + }; + struct ethosu_core_msg_inference_req *inf_req = &rpmsg.inf_req; + uint32_t i; + + msg->type = rpmsg.header.type; + + /* Verify that the uapi and core has the same number of pmus */ + if (pmu_event_config_count != ETHOSU_CORE_PMU_MAX) { + dev_err(mbox->dev, "PMU count misconfigured."); + + return -EINVAL; + } + + inf_req->ifm_count = ifm_count; + inf_req->ofm_count = ofm_count; + inf_req->pmu_cycle_counter_enable = pmu_cycle_counter_enable; + + for (i = 0; i < ifm_count; i++) + ethosu_core_buffer_dma_mem_set(ifm[i]->dma_mem, + &inf_req->ifm[i]); + + for (i = 0; i < ofm_count; i++) + ethosu_core_buffer_dma_mem_set(ofm[i]->dma_mem, + &inf_req->ofm[i]); + + for (i = 0; i < ETHOSU_CORE_PMU_MAX; i++) + inf_req->pmu_event_config[i] = pmu_event_config[i]; + + if (network->dma_mem != NULL) { + inf_req->network.type = ETHOSU_CORE_NETWORK_BUFFER; + ethosu_core_buffer_dma_mem_set(network->dma_mem, + &inf_req->network.buffer); + } else { + inf_req->network.type = ETHOSU_CORE_NETWORK_INDEX; + inf_req->network.index = network->index; + } + + return ethosu_send_locked(mbox, &rpmsg, + sizeof(rpmsg.header) + sizeof(rpmsg.inf_req)); +} + +int ethosu_mailbox_network_info_request(struct ethosu_mailbox *mbox, + struct ethosu_mailbox_msg *msg, + struct ethosu_network *network) +{ + struct ethosu_core_rpmsg rpmsg = { + .header = { + .magic = ETHOSU_CORE_MSG_MAGIC, + .type = ETHOSU_CORE_MSG_NETWORK_INFO_REQ, + .msg_id = msg->id + } + }; + struct ethosu_core_msg_network_info_req *info_req = &rpmsg.net_info_req; + + msg->type = rpmsg.header.type; + + if (network->dma_mem != NULL) { + info_req->network.type = ETHOSU_CORE_NETWORK_BUFFER; + ethosu_core_buffer_dma_mem_set(network->dma_mem, + &info_req->network.buffer); + } else { + info_req->network.type = ETHOSU_CORE_NETWORK_INDEX; + info_req->network.index = network->index; + } + + return ethosu_send_locked(mbox, &rpmsg, + sizeof(rpmsg.header) + + sizeof(rpmsg.net_info_req)); +} + +int ethosu_mailbox_cancel_inference(struct ethosu_mailbox *mbox, + struct ethosu_mailbox_msg *msg, + int inference_handle) +{ + struct ethosu_core_rpmsg rpmsg = { + .header = { + .magic = ETHOSU_CORE_MSG_MAGIC, + .type = + ETHOSU_CORE_MSG_CANCEL_INFERENCE_REQ, + .msg_id = msg->id + }, + .cancel_req = { + .inference_handle = inference_handle + } + }; + + msg->type = rpmsg.header.type; + + return ethosu_send_locked(mbox, &rpmsg, + sizeof(rpmsg.header) + + sizeof(rpmsg.cancel_req)); +} + +int ethosu_mailbox_init(struct ethosu_mailbox *mbox, + struct device *dev, + struct rpmsg_endpoint *ept) +{ + mbox->dev = dev; + mbox->ept = ept; + idr_init(&mbox->msg_idr); + init_waitqueue_head(&mbox->send_queue); + + return 0; +} + +void ethosu_mailbox_deinit(struct ethosu_mailbox *mbox) +{ + atomic_set(&mbox->done, 1); + wake_up_all(&mbox->send_queue); +} diff --git a/kernel/rpmsg/ethosu_rpmsg_network.c b/kernel/rpmsg/ethosu_rpmsg_network.c new file mode 100644 index 0000000..f5b8265 --- /dev/null +++ b/kernel/rpmsg/ethosu_rpmsg_network.c @@ -0,0 +1,255 @@ +/* + * SPDX-FileCopyrightText: Copyright 2020, 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <rpmsg/ethosu_rpmsg_network.h> + +#include <common/ethosu_device.h> +#include <common/ethosu_dma_mem.h> +#include <rpmsg/ethosu_rpmsg_inference.h> +#include <rpmsg/ethosu_rpmsg_network_info.h> +#include <uapi/ethosu.h> + +#include <linux/anon_inodes.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/uaccess.h> + +/**************************************************************************** + * Variables + ****************************************************************************/ + +static int ethosu_network_release(struct inode *inode, + struct file *file); + +static long ethosu_network_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg); + +static const struct file_operations ethosu_network_fops = { + .release = ðosu_network_release, + .unlocked_ioctl = ðosu_network_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ðosu_network_ioctl, +#endif +}; + +/**************************************************************************** + * Functions + ****************************************************************************/ + +static bool ethosu_network_verify(struct file *file) +{ + return file->f_op == ðosu_network_fops; +} + +static void ethosu_network_destroy(struct kref *kref) +{ + struct ethosu_network *net = + container_of(kref, struct ethosu_network, kref); + struct device *dev = net->dev; + + dev_dbg(dev, "Network destroy. net=0x%pK\n", net); + + if (net->dma_mem != NULL) + ethosu_dma_mem_free(&net->dma_mem); + + memset(net, 0, sizeof(*net)); + devm_kfree(dev, net); +} + +static int ethosu_network_release(struct inode *inode, + struct file *file) +{ + struct ethosu_network *net = file->private_data; + struct device *dev = net->dev; + + dev_dbg(dev, "Network release. file=0x%pK, net=0x%pK\n", + file, net); + + ethosu_network_put(net); + + return 0; +} + +static long ethosu_network_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ethosu_network *net = file->private_data; + struct device *dev = net->dev; + void __user *udata = (void __user *)arg; + int ret; + + ret = device_lock_interruptible(net->dev); + if (ret) + return ret; + + switch (cmd) { + case ETHOSU_IOCTL_NETWORK_INFO: { + struct ethosu_uapi_network_info uapi = { 0 }; + + dev_dbg(dev, "Network ioctl: Network info. net=0x%pK\n", net); + + ret = ethosu_network_info_request(dev, net->mailbox, net, + &uapi); + if (ret) + break; + + ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0; + break; + } + case ETHOSU_IOCTL_INFERENCE_CREATE: { + struct ethosu_uapi_inference_create uapi; + + if (copy_from_user(&uapi, udata, sizeof(uapi))) { + ret = -EFAULT; + break; + } + + dev_dbg(dev, + "Network ioctl: Inference. ifm_fd=%u, ofm_fd=%u\n", + uapi.ifm_fd[0], uapi.ofm_fd[0]); + + ret = ethosu_inference_create(dev, net->mailbox, net, &uapi); + break; + } + default: { + dev_err(dev, "Invalid ioctl. cmd=%u, arg=%lu", + cmd, arg); + ret = -ENOIOCTLCMD; + break; + } + } + + device_unlock(net->dev); + + return ret; +} + +int ethosu_network_create(struct device *dev, + struct ethosu_mailbox *mailbox, + struct ethosu_uapi_network_create *uapi) +{ + struct ethosu_network *net; + const void __user *data; + int ret; + + net = devm_kzalloc(dev, sizeof(*net), GFP_KERNEL); + if (!net) + return -ENOMEM; + + net->dev = dev; + net->mailbox = mailbox; + kref_init(&net->kref); + + switch (uapi->type) { + case ETHOSU_UAPI_NETWORK_USER_BUFFER: + if (!uapi->network.data_ptr) { + dev_err(dev, "Invalid network data ptr\n"); + ret = -EINVAL; + goto free_net; + } + + if (!uapi->network.size) { + dev_err(dev, "Invalid network data size\n"); + ret = -EINVAL; + goto free_net; + } + + net->dma_mem = ethosu_dma_mem_alloc(dev, uapi->network.size); + if (IS_ERR(net->dma_mem)) { + ret = PTR_ERR(net->dma_mem); + goto free_net; + } + + data = u64_to_user_ptr(uapi->network.data_ptr); + ret = copy_from_user(net->dma_mem->cpu_addr, data, + uapi->network.size); + if (ret) + goto free_dma_mem; + + break; + case ETHOSU_UAPI_NETWORK_INDEX: + net->index = uapi->index; + break; + default: + ret = -EINVAL; + goto free_net; + } + + ret = anon_inode_getfd("ethosu-network", ðosu_network_fops, net, + O_RDWR | O_CLOEXEC); + if (ret < 0) + goto free_dma_mem; + + net->file = fget(ret); + fput(net->file); + + dev_dbg(dev, + "Network create. file=0x%pK, fd=%d, net=0x%pK, buf=0x%pK, index=%u", + net->file, ret, net, net->dma_mem, net->index); + + return ret; + +free_dma_mem: + if (net->dma_mem != NULL) + ethosu_dma_mem_free(&net->dma_mem); + +free_net: + memset(net, 0, sizeof(*net)); + devm_kfree(dev, net); + + return ret; +} + +struct ethosu_network *ethosu_network_get_from_fd(int fd) +{ + struct ethosu_network *net; + struct file *file; + + file = fget(fd); + if (!file) + return ERR_PTR(-EINVAL); + + if (!ethosu_network_verify(file)) { + fput(file); + + return ERR_PTR(-EINVAL); + } + + net = file->private_data; + ethosu_network_get(net); + fput(file); + + return net; +} + +void ethosu_network_get(struct ethosu_network *net) +{ + kref_get(&net->kref); +} + +int ethosu_network_put(struct ethosu_network *net) +{ + return kref_put(&net->kref, ethosu_network_destroy); +} diff --git a/kernel/rpmsg/ethosu_rpmsg_network_info.c b/kernel/rpmsg/ethosu_rpmsg_network_info.c new file mode 100644 index 0000000..0e54d87 --- /dev/null +++ b/kernel/rpmsg/ethosu_rpmsg_network_info.c @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <rpmsg/ethosu_rpmsg_network_info.h> + +#include <common/ethosu_device.h> +#include <rpmsg/ethosu_rpmsg_network.h> +#include <uapi/ethosu.h> + +#include <linux/bug.h> + +#define NETWORK_INFO_RESP_TIMEOUT_MS 3000 + +static inline int ethosu_network_info_send(struct ethosu_network_info *info, + struct ethosu_mailbox *mailbox) +{ + /* Send network info request to firmware */ + return ethosu_mailbox_network_info_request(mailbox, + &info->msg, + info->net); +} + +static void ethosu_network_info_fail(struct ethosu_mailbox_msg *msg) +{ + struct ethosu_network_info *info = + container_of(msg, typeof(*info), msg); + + if (completion_done(&info->done)) + return; + + info->errno = -EFAULT; + complete(&info->done); +} + +int ethosu_network_info_request(struct device *dev, + struct ethosu_mailbox *mailbox, + struct ethosu_network *net, + struct ethosu_uapi_network_info *uapi) +{ + struct ethosu_network_info *info; + int ret; + int timeout; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = dev; + info->net = net; + info->uapi = uapi; + init_completion(&info->done); + info->msg.fail = ethosu_network_info_fail; + + ret = ethosu_mailbox_register(mailbox, &info->msg); + if (ret < 0) + goto kfree; + + /* Get reference to network */ + ethosu_network_get(info->net); + + ret = ethosu_network_info_send(info, mailbox); + if (ret) + goto deregister; + + dev_dbg(dev, + "Network info create. info=0x%pK, net=0x%pK, msg.id=0x%x\n", + info, info->net, info->msg.id); + + /* Unlock the device mutex and wait for completion */ + device_unlock(dev); + timeout = wait_for_completion_timeout(&info->done, + msecs_to_jiffies( + NETWORK_INFO_RESP_TIMEOUT_MS)); + device_lock(dev); + + if (0 == timeout) { + dev_warn(dev, "Network info timed out. info=0x%pK", + info); + + ret = -ETIME; + goto deregister; + } + + ret = info->errno; + +deregister: + ethosu_mailbox_deregister(mailbox, &info->msg); + ethosu_network_put(info->net); + +kfree: + dev_dbg(dev, + "Network info destroy. info=0x%pK, msg.id=0x%x\n", + info, info->msg.id); + devm_kfree(dev, info); + + return ret; +} + +void ethosu_network_info_rsp(struct ethosu_mailbox *mailbox, + int msg_id, + struct ethosu_core_msg_network_info_rsp *rsp) +{ + int ret; + struct device *dev = mailbox->dev; + struct ethosu_mailbox_msg *msg; + struct ethosu_network_info *info; + uint32_t i; + const size_t rsp_desc_size = sizeof(rsp->desc); + + BUILD_BUG_ON(rsp_desc_size != sizeof(info->uapi->desc)); + + msg = ethosu_mailbox_find(mailbox, msg_id, + ETHOSU_CORE_MSG_NETWORK_INFO_REQ); + if (IS_ERR(msg)) { + dev_warn(dev, + "Id for network info msg not found. Id=0x%x: %ld\n", + msg_id, PTR_ERR(msg)); + + return; + } + + info = container_of(msg, typeof(*info), msg); + + if (completion_done(&info->done)) + return; + + info->errno = 0; + + if (rsp->status != ETHOSU_CORE_STATUS_OK) { + dev_err(dev, "Failed to get information about the network\n"); + info->errno = -EBADF; + goto signal_complete; + } + + if (rsp->ifm_count > ETHOSU_FD_MAX || rsp->ofm_count > ETHOSU_FD_MAX) { + dev_err(dev, + "Invalid number of IFMs/OFMs in network info: IFMs=%u OFMs=%u\n", + rsp->ifm_count, rsp->ofm_count); + info->errno = -ENFILE; + goto signal_complete; + } + + if (strnlen(rsp->desc, rsp_desc_size) == rsp_desc_size) { + dev_err(dev, + "Description in network info is not null-terminated\n"); + info->errno = -EMSGSIZE; + goto signal_complete; + } + + ret = strscpy(info->uapi->desc, rsp->desc, sizeof(info->uapi->desc)); + if (ret < 0) { + dev_err(dev, "Failed to copy network info description\n"); + info->errno = ret; + goto signal_complete; + } + + info->uapi->ifm_count = rsp->ifm_count; + for (i = 0; i < rsp->ifm_count; i++) + info->uapi->ifm_size[i] = rsp->ifm_size[i]; + + info->uapi->ofm_count = rsp->ofm_count; + for (i = 0; i < rsp->ofm_count; i++) + info->uapi->ofm_size[i] = rsp->ofm_size[i]; + +signal_complete: + complete(&info->done); +} diff --git a/kernel/rpmsg/ethosu_rpmsg_version.c b/kernel/rpmsg/ethosu_rpmsg_version.c new file mode 100644 index 0000000..03e164b --- /dev/null +++ b/kernel/rpmsg/ethosu_rpmsg_version.c @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <rpmsg/ethosu_rpmsg_version.h> +#include <rpmsg/ethosu_rpmsg.h> + +#include <linux/errno.h> + +/**************************************************************************** + * Defines + ****************************************************************************/ + +#define VERSION_RESP_TIMEOUT_MS 2000 + +/**************************************************************************** + * Functions + ****************************************************************************/ + +static void ethosu_version_fail(struct ethosu_mailbox_msg *msg) +{ + struct ethosu_version *version = + container_of(msg, typeof(*version), msg); + + if (completion_done(&version->done)) + return; + + version->errno = -EFAULT; + complete(&version->done); +} + +void ethosu_version_rsp(struct ethosu_mailbox *mailbox, + int msg_id, + struct ethosu_core_msg_version_rsp *rsp) +{ + struct device *dev = mailbox->dev; + struct ethosu_mailbox_msg *msg; + struct ethosu_version *version; + + msg = ethosu_mailbox_find(mailbox, msg_id, + ETHOSU_CORE_MSG_VERSION_REQ); + if (IS_ERR(msg)) { + dev_warn(dev, + "Id for version msg not found. Id=0x%0x: %ld\n", + msg_id, PTR_ERR(msg)); + + return; + } + + version = container_of(msg, typeof(*version), msg); + + if (completion_done(&version->done)) + return; + + if (rsp->major != ETHOSU_CORE_MSG_VERSION_MAJOR || + rsp->minor != ETHOSU_CORE_MSG_VERSION_MINOR) { + dev_warn(dev, + "Msg: Protocol version mismatch. Expected %u.%u.X but got %u.%u.%u", + ETHOSU_CORE_MSG_VERSION_MAJOR, + ETHOSU_CORE_MSG_VERSION_MINOR, + rsp->major, rsp->minor, rsp->patch); + version->errno = -EPROTO; + } else { + version->errno = 0; + } + + complete(&version->done); +} + +int ethosu_version_check_request(struct device *dev, + struct ethosu_mailbox *mailbox) +{ + struct ethosu_version *version; + int ret; + int timeout; + + version = devm_kzalloc(dev, sizeof(*version), GFP_KERNEL); + if (!version) + return -ENOMEM; + + version->dev = dev; + init_completion(&version->done); + version->msg.fail = ethosu_version_fail; + + ret = ethosu_mailbox_register(mailbox, &version->msg); + if (ret < 0) + goto free_version; + + dev_dbg(dev, "Protocol version request created. Id=0x%x, handle=%pK\n", + version->msg.id, version); + + ret = ethosu_mailbox_version_request(mailbox, &version->msg); + if (ret) + goto deregister; + + /* Unlock the mutex to not block other messages while waiting */ + device_unlock(dev); + + /* Wait for version response */ + timeout = wait_for_completion_timeout(&version->done, + msecs_to_jiffies( + VERSION_RESP_TIMEOUT_MS)); + + /* Take back the mutex before resuming to do anything */ + device_lock(dev); + + if (0 == timeout) { + dev_warn(dev, "Protocol version response timeout"); + ret = -ETIME; + goto deregister; + } + + if (version->errno) { + ret = version->errno; + goto deregister; + } + +deregister: + ethosu_mailbox_deregister(mailbox, &version->msg); + +free_version: + dev_dbg(dev, "Protocol version destroy. Id=0x%x, handle=%pK\n", + version->msg.id, + version); + devm_kfree(dev, version); + + return ret; +} |