From 074ef905e834cff238e708f2e673e100884218ba Mon Sep 17 00:00:00 2001 From: Kristofer Jonsson Date: Mon, 23 Jan 2023 13:05:36 +0100 Subject: Create device for Ethos-U kernel driver When the Ethos-U kernel driver is probed it creates a /dev/ethosu device node, which user space use ioctl to communicate with. When the driver is removed the Ethos-U resources must live on until all open file handles have been closed. The rpmsg device can be removed for a number of reasons, for example if the firmware is stopped or the remoteproc driver is removed. To allow the remove to complete without waiting for all file handles to close, a new 'struct device' is created by the kernel driver. This device will be used to memory allocations and will live on until the last file handle has been closed. Change-Id: I790d8219960f25fe64f58c11a865eb65c7b08974 --- kernel/ethosu_buffer.c | 28 ++------ kernel/ethosu_cancel_inference.c | 6 ++ kernel/ethosu_device.c | 134 +++++++++++++++++++++++++++++++++------ kernel/ethosu_device.h | 2 +- kernel/ethosu_driver.c | 19 +----- 5 files changed, 128 insertions(+), 61 deletions(-) diff --git a/kernel/ethosu_buffer.c b/kernel/ethosu_buffer.c index 0fcbf3b..fb4a8b4 100644 --- a/kernel/ethosu_buffer.c +++ b/kernel/ethosu_buffer.c @@ -62,24 +62,6 @@ static const struct file_operations ethosu_buffer_fops = { * Functions ****************************************************************************/ -__attribute__((used)) -static dma_addr_t ethosu_pa_to_da(struct device *dev, - phys_addr_t pa, - size_t len) -{ - struct rproc *rproc = rproc_get_by_child(dev); - struct rproc_mem_entry *mem; - - list_for_each_entry(mem, &rproc->carveouts, node) { - ssize_t offset = pa - mem->dma; - - if (offset >= 0 && offset + len <= mem->len) - return mem->da + offset; - } - - return (dma_addr_t)-1; -} - static bool ethosu_buffer_verify(struct file *file) { return file->f_op == ðosu_buffer_fops; @@ -90,11 +72,10 @@ static void ethosu_buffer_destroy(struct kref *kref) struct ethosu_buffer *buf = container_of(kref, struct ethosu_buffer, kref); struct device *dev = buf->dev; - struct rproc *rproc = rproc_get_by_child(dev); - dev_info(dev, "Buffer destroy. buf=0x%pK\n", buf); + dev_info(dev, "Buffer destroy. buf=0x%pK", buf); - dma_free_coherent(rproc->dev.parent, buf->capacity, buf->cpu_addr, + dma_free_coherent(dev, buf->capacity, buf->cpu_addr, buf->dma_addr); devm_kfree(dev, buf); @@ -192,7 +173,6 @@ static long ethosu_buffer_ioctl(struct file *file, int ethosu_buffer_create(struct device *dev, size_t capacity) { - struct rproc *rproc = rproc_get_by_child(dev); struct ethosu_buffer *buf; int ret = -ENOMEM; @@ -209,7 +189,7 @@ int ethosu_buffer_create(struct device *dev, buf->size = 0; kref_init(&buf->kref); - buf->cpu_addr = dma_alloc_coherent(rproc->dev.parent, capacity, + buf->cpu_addr = dma_alloc_coherent(dev, capacity, &buf->dma_addr, GFP_KERNEL); if (!buf->cpu_addr) goto free_buf; @@ -230,7 +210,7 @@ int ethosu_buffer_create(struct device *dev, return ret; free_dma: - dma_free_coherent(rproc->dev.parent, buf->capacity, buf->cpu_addr, + dma_free_coherent(dev, buf->capacity, buf->cpu_addr, buf->dma_addr); free_buf: diff --git a/kernel/ethosu_cancel_inference.c b/kernel/ethosu_cancel_inference.c index ee630f5..6661522 100644 --- a/kernel/ethosu_cancel_inference.c +++ b/kernel/ethosu_cancel_inference.c @@ -28,6 +28,7 @@ #include "ethosu_device.h" #include "ethosu_inference.h" +#include #include /**************************************************************************** @@ -110,6 +111,7 @@ int ethosu_cancel_inference_request(struct device *dev, /* 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( @@ -123,11 +125,14 @@ int ethosu_cancel_inference_request(struct device *dev, 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; } @@ -138,6 +143,7 @@ deregister: kfree: dev_info(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); diff --git a/kernel/ethosu_device.c b/kernel/ethosu_device.c index e99f8c0..96bd8b4 100644 --- a/kernel/ethosu_device.c +++ b/kernel/ethosu_device.c @@ -33,7 +33,6 @@ #include "ethosu_network_info.h" #include "uapi/ethosu.h" -#include #include #include #include @@ -42,6 +41,19 @@ #include #include +/**************************************************************************** + * Defines + ****************************************************************************/ + +#define MINOR_BASE 0 /* Minor version starts at 0 */ +#define MINOR_COUNT 64 /* Allocate minor versions */ + +/**************************************************************************** + * Variables + ****************************************************************************/ + +static DECLARE_BITMAP(minors, MINOR_COUNT); + /**************************************************************************** * Functions ****************************************************************************/ @@ -54,7 +66,7 @@ static int ethosu_handle_rpmsg(struct rpmsg_device *rpdev, u32 src) { struct ethosu_device *edev = dev_get_drvdata(&rpdev->dev); - struct device *dev = &rpdev->dev; + struct device *dev = &edev->dev; struct ethosu_core_rpmsg *rpmsg = data; int length = len - sizeof(rpmsg->header); int ret; @@ -64,7 +76,7 @@ static int ethosu_handle_rpmsg(struct rpmsg_device *rpdev, rpmsg->header.magic, rpmsg->header.type, rpmsg->header.msg_id); switch (rpmsg->header.type) { - case ETHOSU_CORE_MSG_ERR: { + case ETHOSU_CORE_MSG_ERR: if (length != sizeof(rpmsg->error)) { dev_warn(dev, "Msg: Error message of incorrect size. size=%u, expected=%zu", length, @@ -76,8 +88,9 @@ static int ethosu_handle_rpmsg(struct rpmsg_device *rpdev, rpmsg->error.msg[sizeof(rpmsg->error.msg) - 1] = '\0'; dev_warn(dev, "Msg: Error. type=%u, msg=\"%s\"", rpmsg->error.type, rpmsg->error.msg); + + rproc_report_crash(rproc_get_by_child(dev), RPROC_FATAL_ERROR); break; - } case ETHOSU_CORE_MSG_PING: dev_info(dev, "Msg: Ping"); ret = ethosu_mailbox_pong(&edev->mailbox); @@ -205,7 +218,7 @@ static int ethosu_open(struct inode *inode, struct ethosu_device *edev = container_of(cdev, struct ethosu_device, cdev); struct rpmsg_device *rpdev = edev->rpdev; - struct device *dev = &rpdev->dev; + struct device *dev = &edev->dev; dev_info(dev, "Device open. file=0x%pK", file); @@ -219,8 +232,8 @@ static long ethosu_ioctl(struct file *file, unsigned long arg) { struct rpmsg_device *rpdev = file->private_data; - struct device *dev = &rpdev->dev; - struct ethosu_device *edev = dev_get_drvdata(dev); + struct ethosu_device *edev = dev_get_drvdata(&rpdev->dev); + struct device *dev = &edev->dev; void __user *udata = (void __user *)arg; int ret = -EINVAL; @@ -325,6 +338,58 @@ static const struct file_operations fops = { #endif }; +static void ethosu_dev_release(struct device *dev) +{ + struct ethosu_device *edev = dev_get_drvdata(dev); + + dev_info(dev, "%s", __FUNCTION__); + + clear_bit(MINOR(edev->cdev.dev), minors); + + ethosu_mailbox_deinit(&edev->mailbox); + device_destroy(edev->class, edev->cdev.dev); + kfree(edev); +} + +static int ethosu_device_register(struct device *dev, + struct device *parent, + void *drvdata, + dev_t devt) +{ + struct rproc *rproc = rproc_get_by_child(parent); + int ret; + + dev->parent = parent; + dev->release = ethosu_dev_release; + dev_set_drvdata(dev, drvdata); + + ret = dev_set_name(dev, "ethosu%d", MINOR(devt)); + if (ret) { + dev_err(parent, "Failed to set device name. ret=%d", ret); + + return ret; + } + + /* Inherit DMA settings for rproc device */ + ret = of_reserved_mem_device_init_by_idx(dev, + rproc->dev.parent->of_node, 0); + if (ret) { + dev_err(parent, "Failed to initialize reserved memory. ret=%d", + ret); + + return ret; + } + + ret = device_register(dev); + if (ret) { + dev_err(parent, "Failed to register device. ret=%d", ret); + + return ret; + } + + return 0; +} + int ethosu_dev_init(struct rpmsg_device *rpdev, struct class *class, dev_t devt) @@ -332,51 +397,76 @@ int ethosu_dev_init(struct rpmsg_device *rpdev, struct device *dev = &rpdev->dev; struct ethosu_device *edev; struct device *sysdev; + int minor; int ret; dev_info(dev, "%s", __FUNCTION__); + /* Reserve minor number for device node */ + minor = find_first_zero_bit(minors, MINOR_COUNT); + if (minor >= MINOR_COUNT) { + dev_err(dev, "No more minor numbers."); + + return -ENOMEM; + } + + devt = MKDEV(MAJOR(devt), minor); + /* Allocate and create Ethos-U device */ - edev = devm_kzalloc(dev, sizeof(*edev), GFP_KERNEL); + edev = kzalloc(sizeof(*edev), GFP_KERNEL); if (!edev) return -ENOMEM; - dev_set_drvdata(dev, edev); + dev_set_drvdata(&rpdev->dev, edev); edev->rpdev = rpdev; edev->class = class; - edev->devt = devt; + + /* Create device object */ + ret = ethosu_device_register(&edev->dev, &rpdev->dev, edev, + devt); + if (ret) + goto free_edev; + + /* Continue with new device */ + dev = &edev->dev; /* Create RPMsg endpoint */ edev->ept = ethosu_create_ept(rpdev); - if (IS_ERR(edev->ept)) - return PTR_ERR(edev->ept); + if (IS_ERR(edev->ept)) { + ret = PTR_ERR(edev->ept); + goto device_unregister; + } ret = ethosu_mailbox_init(&edev->mailbox, dev, edev->ept); if (ret) - return ret; + goto device_unregister; /* Create device node */ cdev_init(&edev->cdev, &fops); edev->cdev.owner = THIS_MODULE; - ret = cdev_add(&edev->cdev, edev->devt, 1); + cdev_set_parent(&edev->cdev, &dev->kobj); + + ret = cdev_add(&edev->cdev, devt, 1); if (ret) { dev_err(dev, "Failed to add character device."); goto deinit_mailbox; } - sysdev = device_create(edev->class, NULL, edev->devt, rpdev, - "ethosu%d", MINOR(edev->devt)); + sysdev = device_create(edev->class, NULL, devt, rpdev, + "ethosu%d", MINOR(devt)); if (IS_ERR(sysdev)) { dev_err(dev, "Failed to create device."); ret = PTR_ERR(sysdev); goto del_cdev; } + set_bit(minor, minors); + dev_info(dev, "Created Arm Ethos-U device. name=%s, major=%d, minor=%d", - dev_name(sysdev), MAJOR(edev->devt), MINOR(edev->devt)); + dev_name(sysdev), MAJOR(devt), MINOR(devt)); ethosu_mailbox_ping(&edev->mailbox); @@ -388,6 +478,12 @@ del_cdev: deinit_mailbox: ethosu_mailbox_deinit(&edev->mailbox); +device_unregister: + device_unregister(dev); + +free_edev: + kfree(edev); + return ret; } @@ -398,8 +494,8 @@ void ethosu_dev_deinit(struct rpmsg_device *rpdev) dev_info(dev, "%s", __FUNCTION__); - ethosu_mailbox_deinit(&edev->mailbox); + ethosu_mailbox_fail(&edev->mailbox); rpmsg_destroy_ept(edev->ept); - device_destroy(edev->class, edev->cdev.dev); cdev_del(&edev->cdev); + device_unregister(&edev->dev); } diff --git a/kernel/ethosu_device.h b/kernel/ethosu_device.h index d02f29d..d866514 100644 --- a/kernel/ethosu_device.h +++ b/kernel/ethosu_device.h @@ -42,11 +42,11 @@ * struct ethosu_device - Device structure */ struct ethosu_device { + struct device dev; struct rpmsg_device *rpdev; struct rpmsg_endpoint *ept; struct cdev cdev; struct class *class; - dev_t devt; struct ethosu_mailbox mailbox; }; diff --git a/kernel/ethosu_driver.c b/kernel/ethosu_driver.c index 6e5bfb9..342f501 100644 --- a/kernel/ethosu_driver.c +++ b/kernel/ethosu_driver.c @@ -38,8 +38,6 @@ #define MINOR_BASE 0 /* Minor version starts at 0 */ #define MINOR_COUNT 64 /* Allocate minor versions */ -#define DMA_ADDR_BITS 32 /* Number of address bits */ - /**************************************************************************** * Variables ****************************************************************************/ @@ -48,8 +46,6 @@ static struct class *ethosu_class; static dev_t devt; -static DECLARE_BITMAP(minors, MINOR_COUNT); - /**************************************************************************** * Rpmsg driver ****************************************************************************/ @@ -57,35 +53,24 @@ static DECLARE_BITMAP(minors, MINOR_COUNT); static int ethosu_rpmsg_probe(struct rpmsg_device *rpdev) { struct device *dev = &rpdev->dev; - int minor; int ret; - /* Reserve minor number for device node */ - minor = find_first_zero_bit(minors, MINOR_COUNT); - if (minor >= MINOR_COUNT) { - dev_err(dev, "No more minor numbers."); - - return -ENOMEM; - } + dev_info(dev, "%s", __FUNCTION__); /* Initialize device */ - ret = ethosu_dev_init(rpdev, ethosu_class, MKDEV(MAJOR(devt), minor)); + ret = ethosu_dev_init(rpdev, ethosu_class, devt); if (ret) return ret; - set_bit(minor, minors); - return 0; } static void ethosu_rpmsg_remove(struct rpmsg_device *rpdev) { struct device *dev = &rpdev->dev; - struct ethosu_device *edev = dev_get_drvdata(dev); dev_info(dev, "%s", __FUNCTION__); - clear_bit(MINOR(edev->devt), minors); ethosu_dev_deinit(rpdev); } -- cgit v1.2.1