diff --git a/drivers/uacce/uacce.c b/drivers/uacce/uacce.c index df8d0a81fcc21468753f3da9c1dc919c1b759314..15585b460131377132b82862eb12b42b1d96b1c8 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 5d690cefdab31ca7223f0f387e56443a93ab9654..b1d50f6530c4e1d76ccdc91ae141e986027dd7cb 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