mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 09:42:19 +03:00
gpu: host1x-fence: Add pollfd support
Add support for pollfds, which are essentially file descriptors with SYNC_FILE polling characteristics but that can have new fences assigned to after the previous expires. Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com> Change-Id: I6471720089041d3cc72dfaf2d1de221d80d28c27 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2806032 Reviewed-by: Jonathan Hunter <jonathanh@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
c6a9a40f1f
commit
f4a3e6b49a
@@ -13,6 +13,8 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/poll.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/sync_file.h>
|
#include <linux/sync_file.h>
|
||||||
|
|
||||||
#include "include/uapi/linux/host1x-fence.h"
|
#include "include/uapi/linux/host1x-fence.h"
|
||||||
@@ -170,6 +172,203 @@ put_fence:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct host1x_pollfd {
|
||||||
|
struct kref ref;
|
||||||
|
struct mutex lock;
|
||||||
|
|
||||||
|
struct dma_fence *fence;
|
||||||
|
wait_queue_head_t wq;
|
||||||
|
struct dma_fence_cb callback;
|
||||||
|
bool callback_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int host1x_pollfd_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct host1x_pollfd *pollfd = file->private_data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&pollfd->lock);
|
||||||
|
|
||||||
|
if (pollfd->fence) {
|
||||||
|
if (pollfd->callback_set) {
|
||||||
|
err = dma_fence_remove_callback(pollfd->fence, &pollfd->callback);
|
||||||
|
if (err) {
|
||||||
|
/* callback could be executing concurrently */
|
||||||
|
/*
|
||||||
|
* take fence lock. once we acquire the lock we know
|
||||||
|
* callbacks have finished.
|
||||||
|
*/
|
||||||
|
spin_lock_irq(pollfd->fence->lock);
|
||||||
|
spin_unlock_irq(pollfd->fence->lock);
|
||||||
|
} else {
|
||||||
|
host1x_fence_cancel(pollfd->fence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dma_fence_put(pollfd->fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&pollfd->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int host1x_pollfd_poll(struct file *file, poll_table *wait)
|
||||||
|
{
|
||||||
|
struct host1x_pollfd *pollfd = file->private_data;
|
||||||
|
unsigned int mask = 0;
|
||||||
|
|
||||||
|
poll_wait(file, &pollfd->wq, wait);
|
||||||
|
|
||||||
|
mutex_lock(&pollfd->lock);
|
||||||
|
|
||||||
|
if (pollfd->fence && dma_fence_is_signaled(pollfd->fence)) {
|
||||||
|
mask = POLLPRI | POLLIN;
|
||||||
|
dma_fence_put(pollfd->fence);
|
||||||
|
pollfd->fence = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&pollfd->lock);
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations host1x_pollfd_ops = {
|
||||||
|
.release = host1x_pollfd_release,
|
||||||
|
.poll = host1x_pollfd_poll,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dev_file_ioctl_create_pollfd(struct host1x *host1x, void __user *data)
|
||||||
|
{
|
||||||
|
struct host1x_create_pollfd args;
|
||||||
|
struct host1x_pollfd *pollfd;
|
||||||
|
unsigned long copy_err;
|
||||||
|
struct file *file;
|
||||||
|
int fd, err;
|
||||||
|
|
||||||
|
copy_err = copy_from_user(&args, data, sizeof(args));
|
||||||
|
if (copy_err)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
pollfd = kzalloc(sizeof(*pollfd), GFP_KERNEL);
|
||||||
|
if (!pollfd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
file = anon_inode_getfile("host1x_pollfd", &host1x_pollfd_ops, pollfd, 0);
|
||||||
|
if (IS_ERR(file)) {
|
||||||
|
err = PTR_ERR(file);
|
||||||
|
goto free_pollfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_waitqueue_head(&pollfd->wq);
|
||||||
|
mutex_init(&pollfd->lock);
|
||||||
|
kref_init(&pollfd->ref);
|
||||||
|
|
||||||
|
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
err = fd;
|
||||||
|
goto put_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd_install(fd, file);
|
||||||
|
|
||||||
|
args.fd = fd;
|
||||||
|
|
||||||
|
copy_err = copy_to_user(data, &args, sizeof(args));
|
||||||
|
if (copy_err)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
put_file:
|
||||||
|
fput(file);
|
||||||
|
free_pollfd:
|
||||||
|
kfree(pollfd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void host1x_pollfd_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
|
||||||
|
{
|
||||||
|
struct host1x_pollfd *pollfd = container_of(cb, struct host1x_pollfd, callback);
|
||||||
|
|
||||||
|
wake_up_all(&pollfd->wq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev_file_ioctl_trigger_pollfd(struct host1x *host1x, void __user *data)
|
||||||
|
{
|
||||||
|
struct host1x_trigger_pollfd args;
|
||||||
|
struct host1x_syncpt *syncpt;
|
||||||
|
struct host1x_pollfd *pollfd;
|
||||||
|
struct dma_fence *fence;
|
||||||
|
unsigned long copy_err;
|
||||||
|
struct file *file;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
copy_err = copy_from_user(&args, data, sizeof(args));
|
||||||
|
if (copy_err)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
file = fget(args.fd);
|
||||||
|
if (!file)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (file->f_op != &host1x_pollfd_ops) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto put_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
pollfd = file->private_data;
|
||||||
|
|
||||||
|
syncpt = host1x_syncpt_get_by_id_noref(host1x, args.id);
|
||||||
|
if (!syncpt) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto put_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&pollfd->lock);
|
||||||
|
|
||||||
|
if (pollfd->fence) {
|
||||||
|
mutex_unlock(&pollfd->lock);
|
||||||
|
err = -EBUSY;
|
||||||
|
goto put_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
fence = host1x_fence_create(syncpt, args.threshold, false);
|
||||||
|
if (IS_ERR(fence)) {
|
||||||
|
mutex_unlock(&pollfd->lock);
|
||||||
|
err = PTR_ERR(fence);
|
||||||
|
goto put_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
pollfd->fence = fence;
|
||||||
|
|
||||||
|
pollfd->callback_set = false;
|
||||||
|
err = dma_fence_add_callback(fence, &pollfd->callback, host1x_pollfd_callback);
|
||||||
|
if (err == -ENOENT) {
|
||||||
|
/*
|
||||||
|
* We don't free the fence here -- it will be done from the poll
|
||||||
|
* handler. This way the logic is same whether the through callback
|
||||||
|
* or this shortcut.
|
||||||
|
*/
|
||||||
|
wake_up_all(&pollfd->wq);
|
||||||
|
} else if (err != 0) {
|
||||||
|
mutex_unlock(&pollfd->lock);
|
||||||
|
goto put_fence;
|
||||||
|
}
|
||||||
|
pollfd->callback_set = true;
|
||||||
|
|
||||||
|
mutex_unlock(&pollfd->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
put_fence:
|
||||||
|
dma_fence_put(fence);
|
||||||
|
put_file:
|
||||||
|
fput(file);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static long dev_file_ioctl(struct file *file, unsigned int cmd,
|
static long dev_file_ioctl(struct file *file, unsigned int cmd,
|
||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
@@ -181,6 +380,14 @@ static long dev_file_ioctl(struct file *file, unsigned int cmd,
|
|||||||
err = dev_file_ioctl_create_fence(file->private_data, data);
|
err = dev_file_ioctl_create_fence(file->private_data, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HOST1X_IOCTL_CREATE_POLLFD:
|
||||||
|
err = dev_file_ioctl_create_pollfd(file->private_data, data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HOST1X_IOCTL_TRIGGER_POLLFD:
|
||||||
|
err = dev_file_ioctl_trigger_pollfd(file->private_data, data);
|
||||||
|
break;
|
||||||
|
|
||||||
case HOST1X_IOCTL_FENCE_EXTRACT:
|
case HOST1X_IOCTL_FENCE_EXTRACT:
|
||||||
err = dev_file_ioctl_fence_extract(file->private_data, data);
|
err = dev_file_ioctl_fence_extract(file->private_data, data);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -72,8 +72,22 @@ struct host1x_fence_extract {
|
|||||||
__u32 reserved[2];
|
__u32 reserved[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct host1x_create_pollfd {
|
||||||
|
__s32 fd;
|
||||||
|
__u32 reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct host1x_trigger_pollfd {
|
||||||
|
__s32 fd;
|
||||||
|
__u32 id;
|
||||||
|
__u32 threshold;
|
||||||
|
__u32 reserved;
|
||||||
|
};
|
||||||
|
|
||||||
#define HOST1X_IOCTL_CREATE_FENCE _IOWR('X', 0x02, struct host1x_create_fence)
|
#define HOST1X_IOCTL_CREATE_FENCE _IOWR('X', 0x02, struct host1x_create_fence)
|
||||||
#define HOST1X_IOCTL_FENCE_EXTRACT _IOWR('X', 0x05, struct host1x_fence_extract)
|
#define HOST1X_IOCTL_FENCE_EXTRACT _IOWR('X', 0x05, struct host1x_fence_extract)
|
||||||
|
#define HOST1X_IOCTL_CREATE_POLLFD _IOWR('X', 0x10, struct host1x_create_pollfd)
|
||||||
|
#define HOST1X_IOCTL_TRIGGER_POLLFD _IOWR('X', 0x11, struct host1x_trigger_pollfd)
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user