From d953923286680b8a5ce64213ef6d1a98a623f2db Mon Sep 17 00:00:00 2001 From: Mingqiang Ling Date: Wed, 24 Apr 2019 18:08:43 +0800 Subject: [PATCH] arm64: uacce: Add fasync to send signal when uacce device reset driver inclusion category: bugfix bugzilla: 13683 CVE: NA ------------------------------------------------- Orinal commit message: Currently we only support acc controller reset in kernel. When acc controller is resetting, users in user space indeed can not send request, otherwise unexpected error may happen. This patch adds two APIs for uacce: uacce_reset_prepare, uacce_reset_done. Acc driver can call them before/after controller reset to do some uacce related operations. A new UACCE_ST_RST is added to indicate uacce reset state. uacce_reset_prepare will send SIGIO to all processes which are using uacce_queues of current uacce after setting UACCE_ST_RST. By default, handler of SIGIO will terminate processes, which helps to release all wd fd queue related resources. After reset done, UACCE_ST_RST can be change back to UACCE_ST_INIT by uacce_reset_done. Current uacce states will be as below: +--------------------------------------+ v | +-------------+ +----------------+ | |UACCE_ST_INIT| ------> |UACCE_ST_OPENNED| | +-------------+ <----- +----------------+ | ^ | | | | +--------------+ | | | v v | +------------+ +--------------+ | |UACCE_ST_RST| <------- |UACCE_ST_START| ---+ +------------+ +--------------+ Known issues: 1. Current state is only for uacce mode1. We need fix to make state model right in uacce mode2 2. kernel will delay to call release callback in fops, which will lead that release of hardware queues delay. e.g. cat /sys/class/uacce/dev_name/attr/available_instances after reset done, will show instances are still not available in a short time. Signed-off-by: Zhou Wang Reviewed-by: xuzaibo Reviewed-by: fanghao Signed-off-by: Mingqiang Ling Reviewed-by: Xie XiuQi Signed-off-by: Yang Yingliang --- drivers/uacce/uacce.c | 83 ++++++++++++++++++++++++++++++++++++++++++- include/linux/uacce.h | 15 ++++++-- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/drivers/uacce/uacce.c b/drivers/uacce/uacce.c index df8d0a81fcc2..15585b460131 100644 --- a/drivers/uacce/uacce.c +++ b/drivers/uacce/uacce.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ #include +#include #include #include #include @@ -42,6 +43,7 @@ static DEFINE_RWLOCK(uacce_qs_lock); #endif static const struct file_operations uacce_fops; +static int uacce_fops_fasync(int fd, struct file *file, int mode); /* match with enum uacce_qfrt */ static const char *const qfrt_str[] = { @@ -63,6 +65,52 @@ const char *uacce_qfrt_str(struct uacce_qfile_region *qfr) } EXPORT_SYMBOL_GPL(uacce_qfrt_str); +/** + * uacce_reset_prepare - notify users uacce will be reset and release queues. + * @uacce: the uacce which will be reset. + * + * This function walks all uacce_queues and sends signal to processes which + * are using these uacce_queues. Before a hardware which driver is registered + * to uacce framework resets, this function can be used to send a signal to + * userspace. + */ +void uacce_reset_prepare(struct uacce *uacce) +{ + struct uacce_queue *q; + + atomic_set(&uacce->state, UACCE_ST_RST); + + mutex_lock(&uacce->q_lock); + + if (list_empty(&uacce->qs)) { + mutex_unlock(&uacce->q_lock); + return; + } + + list_for_each_entry(q, &uacce->qs, q_dev) { + kill_fasync(&q->async_queue, SIGIO, POLL_IN); + } + + mutex_unlock(&uacce->q_lock); + + /* make sure above single been handled */ + mdelay(10); +} +EXPORT_SYMBOL_GPL(uacce_reset_prepare); + +/** + * uacce_reset_done - Set uacce as normal state. + * @uacce: the uacce which reset is done. + * + * This function set uacce as normal state, after this uacce can be opened + * again. + */ +void uacce_reset_done(struct uacce *uacce) +{ + atomic_set(&uacce->state, UACCE_ST_INIT); +} +EXPORT_SYMBOL_GPL(uacce_reset_done); + /** * uacce_wake_up - Wake up the process who is waiting this queue * @q the accelerator queue to wake up @@ -586,8 +634,20 @@ static int uacce_queue_drain(struct uacce_queue *q) static int uacce_fops_flush(struct file *filep, fl_owner_t id) { struct uacce_queue *q = filep->private_data; - + struct uacce *uacce = q->uacce; filep->private_data = NULL; + + /* + * It is different between CI and kernel-dev here, so delete list + * entry in flush callback and release callback. After flush is called + * uacce_queue will be NULL, and same code will not be called in + * release, so it is safe. + */ + uacce_fops_fasync(-1, filep, 0); + mutex_lock(&uacce->q_lock); + list_del(&q->q_dev); + mutex_unlock(&uacce->q_lock); + return uacce_queue_drain(q); } @@ -602,6 +662,9 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) if (!uacce) return -ENODEV; + if (atomic_read(&uacce->state) == UACCE_ST_RST) + return -EINVAL; + if (!uacce->ops->get_queue) return -EINVAL; @@ -630,6 +693,9 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) INIT_LIST_HEAD(&q->list); init_waitqueue_head(&q->wait); filep->private_data = q; + mutex_lock(&uacce->q_lock); + list_add(&q->q_dev, &uacce->qs); + mutex_unlock(&uacce->q_lock); return 0; open_err: @@ -645,6 +711,11 @@ static int uacce_fops_release(struct inode *inode, struct file *filep) if (!q) return 0; + uacce_fops_fasync(-1, filep, 0); + mutex_lock(&q->uacce->q_lock); + list_del(&q->q_dev); + mutex_unlock(&q->uacce->q_lock); + /* As user space exception(without release queue), it will fall into * this logic as the task exits to prevent hardware resources leaking */ @@ -821,6 +892,13 @@ static __poll_t uacce_fops_poll(struct file *file, poll_table *wait) return 0; } +static int uacce_fops_fasync(int fd, struct file *file, int mode) +{ + struct uacce_queue *q = file->private_data; + + return fasync_helper(fd, file, mode, &q->async_queue); +} + static const struct file_operations uacce_fops = { .owner = THIS_MODULE, .open = uacce_fops_open, @@ -832,6 +910,7 @@ static const struct file_operations uacce_fops = { #endif .mmap = uacce_fops_mmap, .poll = uacce_fops_poll, + .fasync = uacce_fops_fasync, }; #define UACCE_FROM_CDEV_ATTR(dev) container_of(dev, struct uacce, dev) @@ -1214,6 +1293,8 @@ int uacce_register(struct uacce *uacce) dev_dbg(dev, "uacce state initialized to INIT\n"); atomic_set(&uacce->state, UACCE_ST_INIT); atomic_set(&uacce->ref, 0); + INIT_LIST_HEAD(&uacce->qs); + mutex_init(&uacce->q_lock); mutex_unlock(&uacce_mutex); return 0; diff --git a/include/linux/uacce.h b/include/linux/uacce.h index 5d690cefdab3..b1d50f6530c4 100644 --- a/include/linux/uacce.h +++ b/include/linux/uacce.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -76,11 +77,15 @@ struct uacce_queue { struct mm_struct *mm; struct uacce_qfile_region *qfrs[UACCE_QFRT_MAX]; + + struct fasync_struct *async_queue; + struct list_head q_dev; }; -#define UACCE_ST_INIT 0 -#define UACCE_ST_OPENNED 1 -#define UACCE_ST_STARTED 2 +#define UACCE_ST_INIT 0 +#define UACCE_ST_OPENNED 1 +#define UACCE_ST_STARTED 2 +#define UACCE_ST_RST 3 struct uacce { const char *name; @@ -97,11 +102,15 @@ struct uacce { atomic_t state; atomic_t ref; int prot; + struct mutex q_lock; + struct list_head qs; }; int uacce_register(struct uacce *uacce); void uacce_unregister(struct uacce *uacce); void uacce_wake_up(struct uacce_queue *q); +void uacce_reset_prepare(struct uacce *uacce); +void uacce_reset_done(struct uacce *uacce); const char *uacce_qfrt_str(struct uacce_qfile_region *qfr); #endif -- GitLab