| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * GCIP-integrated IIF driver sync file. |
| * |
| * Copyright (C) 2023 Google LLC |
| */ |
| |
| #include <linux/anon_inodes.h> |
| #include <linux/atomic.h> |
| #include <linux/bitops.h> |
| #include <linux/file.h> |
| #include <linux/fs.h> |
| #include <linux/poll.h> |
| #include <linux/slab.h> |
| #include <linux/types.h> |
| #include <linux/uaccess.h> |
| |
| #include <gcip/iif/iif-fence.h> |
| #include <gcip/iif/iif-sync-file.h> |
| |
| static void iif_sync_file_fence_signaled(struct iif_fence *fence, struct iif_fence_poll_cb *poll_cb) |
| { |
| struct iif_sync_file *sync_file = container_of(poll_cb, struct iif_sync_file, poll_cb); |
| |
| wake_up_all(&sync_file->wq); |
| } |
| |
| static int iif_sync_file_release(struct inode *inode, struct file *file) |
| { |
| struct iif_sync_file *sync_file = file->private_data; |
| |
| if (atomic_dec_and_test(&sync_file->fence->num_sync_file)) |
| iif_fence_on_sync_file_release(sync_file->fence); |
| if (test_bit(IIF_SYNC_FILE_FLAGS_POLL_ENABLED, &sync_file->flags)) |
| iif_fence_remove_poll_callback(sync_file->fence, &sync_file->poll_cb); |
| iif_fence_put(sync_file->fence); |
| kfree(sync_file); |
| |
| return 0; |
| } |
| |
| static __poll_t iif_sync_file_poll(struct file *file, poll_table *wait) |
| { |
| struct iif_sync_file *sync_file = file->private_data; |
| int ret; |
| |
| poll_wait(file, &sync_file->wq, wait); |
| |
| if (list_empty(&sync_file->poll_cb.node) && |
| !test_and_set_bit(IIF_SYNC_FILE_FLAGS_POLL_ENABLED, &sync_file->flags)) { |
| ret = iif_fence_add_poll_callback(sync_file->fence, &sync_file->poll_cb, |
| iif_sync_file_fence_signaled); |
| /* If all signalers of the fence already signaled, just wake up all. */ |
| if (ret < 0) |
| wake_up_all(&sync_file->wq); |
| } |
| |
| return iif_fence_is_signaled(sync_file->fence) ? EPOLLIN : 0; |
| } |
| |
| static int iif_sync_file_ioctl_get_information(struct iif_sync_file *sync_file, |
| struct iif_fence_get_information_ioctl __user *argp) |
| { |
| struct iif_fence *fence = sync_file->fence; |
| const struct iif_fence_get_information_ioctl ibuf = { |
| .signaler_ip = fence->signaler_ip, |
| .total_signalers = fence->total_signalers, |
| .submitted_signalers = iif_fence_submitted_signalers(fence), |
| .signaled_signalers = iif_fence_signaled_signalers(fence), |
| .outstanding_waiters = iif_fence_outstanding_waiters(fence), |
| }; |
| |
| if (copy_to_user(argp, &ibuf, sizeof(ibuf))) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static long iif_sync_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| struct iif_sync_file *sync_file = file->private_data; |
| void __user *argp = (void __user *)arg; |
| |
| switch (cmd) { |
| case IIF_FENCE_GET_INFORMATION: |
| return iif_sync_file_ioctl_get_information(sync_file, argp); |
| default: |
| return -ENOTTY; |
| } |
| } |
| |
| static const struct file_operations iif_sync_file_fops = { |
| .release = iif_sync_file_release, |
| .poll = iif_sync_file_poll, |
| .unlocked_ioctl = iif_sync_file_ioctl, |
| }; |
| |
| struct iif_sync_file *iif_sync_file_create(struct iif_fence *fence) |
| { |
| struct iif_sync_file *sync_file; |
| int ret; |
| |
| sync_file = kzalloc(sizeof(*sync_file), GFP_KERNEL); |
| if (!sync_file) |
| return ERR_PTR(-ENOMEM); |
| |
| sync_file->file = anon_inode_getfile("iif_file", &iif_sync_file_fops, sync_file, 0); |
| if (IS_ERR(sync_file->file)) { |
| ret = PTR_ERR(sync_file->file); |
| goto err_free_sync_file; |
| } |
| |
| sync_file->fence = iif_fence_get(fence); |
| atomic_inc(&fence->num_sync_file); |
| |
| init_waitqueue_head(&sync_file->wq); |
| INIT_LIST_HEAD(&sync_file->poll_cb.node); |
| |
| return sync_file; |
| |
| err_free_sync_file: |
| kfree(sync_file); |
| return ERR_PTR(ret); |
| } |
| |
| struct iif_sync_file *iif_sync_file_fdget(int fd) |
| { |
| struct file *file = fget(fd); |
| |
| if (!file) |
| return ERR_PTR(-EBADF); |
| |
| if (file->f_op != &iif_sync_file_fops) { |
| fput(file); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| return file->private_data; |
| } |