提交 d9539232 编写于 作者: M Mingqiang Ling 提交者: Xie XiuQi

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: NZhou Wang <wangzhou1@hisilicon.com>
Reviewed-by: Nxuzaibo <xuzaibo@huawei.com>
Reviewed-by: Nfanghao <fanghao11@huawei.com>
Signed-off-by: NMingqiang Ling <lingmingqiang@huawei.com>
Reviewed-by: NXie XiuQi <xiexiuqi@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 1d065a75
// SPDX-License-Identifier: GPL-2.0+ // SPDX-License-Identifier: GPL-2.0+
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/delay.h>
#include <linux/dma-iommu.h> #include <linux/dma-iommu.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/file.h> #include <linux/file.h>
...@@ -42,6 +43,7 @@ static DEFINE_RWLOCK(uacce_qs_lock); ...@@ -42,6 +43,7 @@ static DEFINE_RWLOCK(uacce_qs_lock);
#endif #endif
static const struct file_operations uacce_fops; static const struct file_operations uacce_fops;
static int uacce_fops_fasync(int fd, struct file *file, int mode);
/* match with enum uacce_qfrt */ /* match with enum uacce_qfrt */
static const char *const qfrt_str[] = { static const char *const qfrt_str[] = {
...@@ -63,6 +65,52 @@ const char *uacce_qfrt_str(struct uacce_qfile_region *qfr) ...@@ -63,6 +65,52 @@ const char *uacce_qfrt_str(struct uacce_qfile_region *qfr)
} }
EXPORT_SYMBOL_GPL(uacce_qfrt_str); 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 * uacce_wake_up - Wake up the process who is waiting this queue
* @q the accelerator queue to wake up * @q the accelerator queue to wake up
...@@ -586,8 +634,20 @@ static int uacce_queue_drain(struct uacce_queue *q) ...@@ -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) static int uacce_fops_flush(struct file *filep, fl_owner_t id)
{ {
struct uacce_queue *q = filep->private_data; struct uacce_queue *q = filep->private_data;
struct uacce *uacce = q->uacce;
filep->private_data = NULL; 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); return uacce_queue_drain(q);
} }
...@@ -602,6 +662,9 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) ...@@ -602,6 +662,9 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
if (!uacce) if (!uacce)
return -ENODEV; return -ENODEV;
if (atomic_read(&uacce->state) == UACCE_ST_RST)
return -EINVAL;
if (!uacce->ops->get_queue) if (!uacce->ops->get_queue)
return -EINVAL; return -EINVAL;
...@@ -630,6 +693,9 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) ...@@ -630,6 +693,9 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
INIT_LIST_HEAD(&q->list); INIT_LIST_HEAD(&q->list);
init_waitqueue_head(&q->wait); init_waitqueue_head(&q->wait);
filep->private_data = q; filep->private_data = q;
mutex_lock(&uacce->q_lock);
list_add(&q->q_dev, &uacce->qs);
mutex_unlock(&uacce->q_lock);
return 0; return 0;
open_err: open_err:
...@@ -645,6 +711,11 @@ static int uacce_fops_release(struct inode *inode, struct file *filep) ...@@ -645,6 +711,11 @@ static int uacce_fops_release(struct inode *inode, struct file *filep)
if (!q) if (!q)
return 0; 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 /* As user space exception(without release queue), it will fall into
* this logic as the task exits to prevent hardware resources leaking * 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) ...@@ -821,6 +892,13 @@ static __poll_t uacce_fops_poll(struct file *file, poll_table *wait)
return 0; 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 = { static const struct file_operations uacce_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = uacce_fops_open, .open = uacce_fops_open,
...@@ -832,6 +910,7 @@ static const struct file_operations uacce_fops = { ...@@ -832,6 +910,7 @@ static const struct file_operations uacce_fops = {
#endif #endif
.mmap = uacce_fops_mmap, .mmap = uacce_fops_mmap,
.poll = uacce_fops_poll, .poll = uacce_fops_poll,
.fasync = uacce_fops_fasync,
}; };
#define UACCE_FROM_CDEV_ATTR(dev) container_of(dev, struct uacce, dev) #define UACCE_FROM_CDEV_ATTR(dev) container_of(dev, struct uacce, dev)
...@@ -1214,6 +1293,8 @@ int uacce_register(struct uacce *uacce) ...@@ -1214,6 +1293,8 @@ int uacce_register(struct uacce *uacce)
dev_dbg(dev, "uacce state initialized to INIT\n"); dev_dbg(dev, "uacce state initialized to INIT\n");
atomic_set(&uacce->state, UACCE_ST_INIT); atomic_set(&uacce->state, UACCE_ST_INIT);
atomic_set(&uacce->ref, 0); atomic_set(&uacce->ref, 0);
INIT_LIST_HEAD(&uacce->qs);
mutex_init(&uacce->q_lock);
mutex_unlock(&uacce_mutex); mutex_unlock(&uacce_mutex);
return 0; return 0;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/fs.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <uapi/linux/uacce.h> #include <uapi/linux/uacce.h>
...@@ -76,11 +77,15 @@ struct uacce_queue { ...@@ -76,11 +77,15 @@ struct uacce_queue {
struct mm_struct *mm; struct mm_struct *mm;
struct uacce_qfile_region *qfrs[UACCE_QFRT_MAX]; 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_INIT 0
#define UACCE_ST_OPENNED 1 #define UACCE_ST_OPENNED 1
#define UACCE_ST_STARTED 2 #define UACCE_ST_STARTED 2
#define UACCE_ST_RST 3
struct uacce { struct uacce {
const char *name; const char *name;
...@@ -97,11 +102,15 @@ struct uacce { ...@@ -97,11 +102,15 @@ struct uacce {
atomic_t state; atomic_t state;
atomic_t ref; atomic_t ref;
int prot; int prot;
struct mutex q_lock;
struct list_head qs;
}; };
int uacce_register(struct uacce *uacce); int uacce_register(struct uacce *uacce);
void uacce_unregister(struct uacce *uacce); void uacce_unregister(struct uacce *uacce);
void uacce_wake_up(struct uacce_queue *q); 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); const char *uacce_qfrt_str(struct uacce_qfile_region *qfr);
#endif #endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册