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:
Mikko Perttunen
2022-07-01 15:43:06 +03:00
committed by mobile promotions
parent c6a9a40f1f
commit f4a3e6b49a
2 changed files with 221 additions and 0 deletions

View File

@@ -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;

View File

@@ -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)
} }