diff options
author | Roman Kiryanov <rkir@google.com> | 2018-12-27 13:43:38 -0800 |
---|---|---|
committer | Roman Kiryanov <rkir@google.com> | 2019-04-17 14:53:19 -0700 |
commit | 07ba6846730d9bb71345a3b431efd02d9e8b631d (patch) | |
tree | 5185874500120e2b214be29c53d0770f5fefced5 | |
parent | 272441c1299ca276b9792a92d7ab0184a934fc17 (diff) | |
download | goldfish-07ba6846730d9bb71345a3b431efd02d9e8b631d.tar.gz |
goldfish_sync: cleanup
* merged all files in to one;
* moved out from staging;
* moved userpace api to the UAPI file;
* removed mutable global variables;
* removed redundant locking;
* removed redundant memory allocations;
* minor fixes to error handling.
Bug: 122325172
Bug: 122078786
Bug: 122052506
Change-Id: I11f23565e6557866a681ef7c945f7737a3d5357c
Signed-off-by: Roman Kiryanov <rkir@google.com>
-rw-r--r-- | drivers/platform/goldfish/Kconfig | 10 | ||||
-rw-r--r-- | drivers/platform/goldfish/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/goldfish/goldfish_sync.c | 833 | ||||
-rw-r--r-- | drivers/staging/goldfish/Kconfig | 9 | ||||
-rw-r--r-- | drivers/staging/goldfish/Makefile | 6 | ||||
-rw-r--r-- | drivers/staging/goldfish/goldfish_sync_timeline.c | 962 | ||||
-rw-r--r-- | drivers/staging/goldfish/goldfish_sync_timeline_fence.c | 254 | ||||
-rw-r--r-- | drivers/staging/goldfish/goldfish_sync_timeline_fence.h | 58 | ||||
-rw-r--r-- | include/uapi/linux/goldfish/goldfish_sync.h | 28 |
9 files changed, 873 insertions, 1289 deletions
diff --git a/drivers/platform/goldfish/Kconfig b/drivers/platform/goldfish/Kconfig index 7dcd7ea71c53..72aa806e6f02 100644 --- a/drivers/platform/goldfish/Kconfig +++ b/drivers/platform/goldfish/Kconfig @@ -17,4 +17,14 @@ config GOLDFISH_PIPE This is a virtual device to drive the QEMU pipe interface used by the Goldfish Android Virtual Device. +config GOLDFISH_SYNC + tristate "Goldfish AVD Sync Driver" + depends on GOLDFISH + depends on HAS_IOMEM + depends on SW_SYNC + depends on SYNC_FILE + ---help--- + Emulated sync fences for the Goldfish Android Virtual Device + + endif # GOLDFISH diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile index 354b4a7d18bb..01e8d34bd313 100644 --- a/drivers/platform/goldfish/Makefile +++ b/drivers/platform/goldfish/Makefile @@ -3,3 +3,5 @@ # obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe_all.o goldfish_pipe_all-objs := goldfish_pipe.o goldfish_pipe_v1.o goldfish_pipe_v2.o + +obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync.o diff --git a/drivers/platform/goldfish/goldfish_sync.c b/drivers/platform/goldfish/goldfish_sync.c new file mode 100644 index 000000000000..5219c53ff189 --- /dev/null +++ b/drivers/platform/goldfish/goldfish_sync.c @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* The Goldfish sync driver is designed to provide a interface + * between the underlying host's sync device and the kernel's + * fence sync framework. + * + * The purpose of the device/driver is to enable lightweight creation and + * signaling of timelines and fences in order to synchronize the guest with + * host-side graphics events. + * + * Each time the interrupt trips, the driver may perform a sync operation. + */ + +#include <linux/acpi.h> +#include <linux/dma-fence.h> +#include <linux/fdtable.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/kref.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/sync_file.h> +#include <linux/syscalls.h> +#include <linux/types.h> +#include <linux/uaccess.h> + +#include <uapi/linux/goldfish/goldfish_sync.h> + +struct sync_pt { + struct dma_fence base; /* must be the first field in this struct */ + struct list_head active_list; /* see active_list_head below */ +}; + +struct goldfish_sync_state; + +struct goldfish_sync_timeline { + struct goldfish_sync_state *sync_state; + + /* This object is owned by userspace from open() calls and also each + * sync_pt refers to it. + */ + struct kref kref; + char name[32]; /* for debugging */ + + u64 context; + unsigned int seqno; + /* list of active (unsignaled/errored) sync_pts */ + struct list_head active_list_head; + spinlock_t lock; /* protects the fields above */ +}; + +/* The above definitions (command codes, register layout, ioctl definitions) + * need to be in sync with the following files: + * + * Host-side (emulator): + * external/qemu/android/emulation/goldfish_sync.h + * external/qemu-android/hw/misc/goldfish_sync.c + * + * Guest-side (system image): + * device/generic/goldfish-opengl/system/egl/goldfish_sync.h + * device/generic/goldfish/ueventd.ranchu.rc + * platform/build/target/board/generic/sepolicy/file_contexts + */ +struct goldfish_sync_hostcmd { + /* sorted for alignment */ + u64 handle; + u64 hostcmd_handle; + u32 cmd; + u32 time_arg; +}; + +struct goldfish_sync_guestcmd { + u64 host_command; /* u64 for alignment */ + u64 glsync_handle; + u64 thread_handle; + u64 guest_timeline_handle; +}; + +/* The host operations are: */ +enum cmd_id { + /* Ready signal - used to mark when irq should lower */ + CMD_SYNC_READY = 0, + + /* Create a new timeline. writes timeline handle */ + CMD_CREATE_SYNC_TIMELINE = 1, + + /* Create a fence object. reads timeline handle and time argument. + * Writes fence fd to the SYNC_REG_HANDLE register. + */ + CMD_CREATE_SYNC_FENCE = 2, + + /* Increments timeline. reads timeline handle and time argument */ + CMD_SYNC_TIMELINE_INC = 3, + + /* Destroys a timeline. reads timeline handle */ + CMD_DESTROY_SYNC_TIMELINE = 4, + + /* Starts a wait on the host with the given glsync object and + * sync thread handle. + */ + CMD_TRIGGER_HOST_WAIT = 5, +}; + +/* The host register layout is: */ +enum sync_reg_id { + /* host->guest batch commands */ + SYNC_REG_BATCH_COMMAND = 0x00, + + /* guest->host batch commands */ + SYNC_REG_BATCH_GUESTCOMMAND = 0x04, + + /* communicate physical address of host->guest batch commands */ + SYNC_REG_BATCH_COMMAND_ADDR = 0x08, + SYNC_REG_BATCH_COMMAND_ADDR_HIGH = 0x0C, /* 64-bit part */ + + /* communicate physical address of guest->host commands */ + SYNC_REG_BATCH_GUESTCOMMAND_ADDR = 0x10, + SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH = 0x14, /* 64-bit part */ + + /* signals that the device has been probed */ + SYNC_REG_INIT = 0x18, +}; + +#define GOLDFISH_SYNC_MAX_CMDS 32 + +/* The driver state: */ +struct goldfish_sync_state { + struct miscdevice miscdev; + + char __iomem *reg_base; + int irq; + + /* Used to generate unique names, see goldfish_sync_timeline::name. */ + u64 id_counter; + + /* |mutex_lock| protects all concurrent access + * to timelines for both kernel and user space. + */ + struct mutex mutex_lock; + + /* Buffer holding commands issued from host. */ + struct goldfish_sync_hostcmd to_do[GOLDFISH_SYNC_MAX_CMDS]; + u32 to_do_end; + /* Protects to_do and to_do_end */ + spinlock_t to_do_lock; + + /* Buffers for the reading or writing + * of individual commands. The host can directly write + * to |batch_hostcmd| (and then this driver immediately + * copies contents to |to_do|). This driver either replies + * through |batch_hostcmd| or simply issues a + * guest->host command through |batch_guestcmd|. + */ + struct goldfish_sync_hostcmd batch_hostcmd; + struct goldfish_sync_guestcmd batch_guestcmd; + + /* Used to give this struct itself to a work queue + * function for executing actual sync commands. + */ + struct work_struct work_item; +}; + +static struct goldfish_sync_timeline +*goldfish_dma_fence_parent(struct dma_fence *fence) +{ + return container_of(fence->lock, struct goldfish_sync_timeline, lock); +} + +static struct sync_pt *goldfish_sync_fence_to_sync_pt(struct dma_fence *fence) +{ + return container_of(fence, struct sync_pt, base); +} + +/* sync_state->mutex_lock must be locked. */ +struct goldfish_sync_timeline __must_check +*goldfish_sync_timeline_create(struct goldfish_sync_state *sync_state) +{ + struct goldfish_sync_timeline *tl; + + tl = kzalloc(sizeof(*tl), GFP_KERNEL); + if (!tl) + return NULL; + + tl->sync_state = sync_state; + kref_init(&tl->kref); + snprintf(tl->name, sizeof(tl->name), + "%s:%llu", GOLDFISH_SYNC_DEVICE_NAME, + ++sync_state->id_counter); + tl->context = dma_fence_context_alloc(1); + tl->seqno = 0; + INIT_LIST_HEAD(&tl->active_list_head); + spin_lock_init(&tl->lock); + + return tl; +} + +static void goldfish_sync_timeline_free(struct kref *kref) +{ + struct goldfish_sync_timeline *tl = + container_of(kref, struct goldfish_sync_timeline, kref); + + kfree(tl); +} + +static void goldfish_sync_timeline_get(struct goldfish_sync_timeline *tl) +{ + kref_get(&tl->kref); +} + +void goldfish_sync_timeline_put(struct goldfish_sync_timeline *tl) +{ + kref_put(&tl->kref, goldfish_sync_timeline_free); +} + +void goldfish_sync_timeline_signal(struct goldfish_sync_timeline *tl, + unsigned int inc) +{ + unsigned long flags; + struct sync_pt *pt, *next; + + spin_lock_irqsave(&tl->lock, flags); + tl->seqno += inc; + + list_for_each_entry_safe(pt, next, &tl->active_list_head, active_list) { + /* dma_fence_is_signaled_locked has side effects */ + if (dma_fence_is_signaled_locked(&pt->base)) + list_del_init(&pt->active_list); + } + spin_unlock_irqrestore(&tl->lock, flags); +} + +static const struct dma_fence_ops goldfish_sync_timeline_fence_ops; + +static struct sync_pt __must_check +*goldfish_sync_pt_create(struct goldfish_sync_timeline *tl, + unsigned int value) +{ + struct sync_pt *pt = kzalloc(sizeof(*pt), GFP_KERNEL); + + if (!pt) + return NULL; + + dma_fence_init(&pt->base, + &goldfish_sync_timeline_fence_ops, + &tl->lock, + tl->context, + value); + INIT_LIST_HEAD(&pt->active_list); + goldfish_sync_timeline_get(tl); /* pt refers to tl */ + + return pt; +} + +static void goldfish_sync_pt_destroy(struct sync_pt *pt) +{ + struct goldfish_sync_timeline *tl = + goldfish_dma_fence_parent(&pt->base); + unsigned long flags; + + spin_lock_irqsave(&tl->lock, flags); + if (!list_empty(&pt->active_list)) + list_del(&pt->active_list); + spin_unlock_irqrestore(&tl->lock, flags); + + goldfish_sync_timeline_put(tl); /* unref pt from tl */ + dma_fence_free(&pt->base); +} + +static const char +*goldfish_sync_timeline_fence_get_driver_name(struct dma_fence *fence) +{ + return "sw_sync"; +} + +static const char +*goldfish_sync_timeline_fence_get_timeline_name(struct dma_fence *fence) +{ + struct goldfish_sync_timeline *tl = goldfish_dma_fence_parent(fence); + + return tl->name; +} + +static void goldfish_sync_timeline_fence_release(struct dma_fence *fence) +{ + goldfish_sync_pt_destroy(goldfish_sync_fence_to_sync_pt(fence)); +} + +static bool goldfish_sync_timeline_fence_signaled(struct dma_fence *fence) +{ + struct goldfish_sync_timeline *tl = goldfish_dma_fence_parent(fence); + + return tl->seqno >= fence->seqno; +} + +static bool +goldfish_sync_timeline_fence_enable_signaling(struct dma_fence *fence) +{ + struct sync_pt *pt; + struct goldfish_sync_timeline *tl; + + if (goldfish_sync_timeline_fence_signaled(fence)) + return false; + + pt = goldfish_sync_fence_to_sync_pt(fence); + tl = goldfish_dma_fence_parent(fence); + list_add_tail(&pt->active_list, &tl->active_list_head); + return true; +} + +static void goldfish_sync_timeline_fence_value_str(struct dma_fence *fence, + char *str, int size) +{ + snprintf(str, size, "%d", fence->seqno); +} + +static void goldfish_sync_timeline_fence_timeline_value_str( + struct dma_fence *fence, + char *str, int size) +{ + struct goldfish_sync_timeline *tl = goldfish_dma_fence_parent(fence); + + snprintf(str, size, "%d", tl->seqno); +} + +static const struct dma_fence_ops goldfish_sync_timeline_fence_ops = { + .get_driver_name = goldfish_sync_timeline_fence_get_driver_name, + .get_timeline_name = goldfish_sync_timeline_fence_get_timeline_name, + .enable_signaling = goldfish_sync_timeline_fence_enable_signaling, + .signaled = goldfish_sync_timeline_fence_signaled, + .wait = dma_fence_default_wait, + .release = goldfish_sync_timeline_fence_release, + .fence_value_str = goldfish_sync_timeline_fence_value_str, + .timeline_value_str = goldfish_sync_timeline_fence_timeline_value_str, +}; + +static int __must_check +goldfish_sync_fence_create(struct goldfish_sync_timeline *tl, u32 val) +{ + struct sync_pt *pt; + struct sync_file *sync_file_obj = NULL; + int fd; + + pt = goldfish_sync_pt_create(tl, val); + if (!pt) + return -1; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + goto err_cleanup_pt; + + sync_file_obj = sync_file_create(&pt->base); + if (!sync_file_obj) + goto err_cleanup_fd_pt; + + fd_install(fd, sync_file_obj->file); + + dma_fence_put(&pt->base); /* sync_file_obj now owns the fence */ + return fd; + +err_cleanup_fd_pt: + put_unused_fd(fd); +err_cleanup_pt: + goldfish_sync_pt_destroy(pt); + + return -1; +} + +static inline void +goldfish_sync_cmd_queue(struct goldfish_sync_state *sync_state, + u32 cmd, + u64 handle, + u32 time_arg, + u64 hostcmd_handle) +{ + struct goldfish_sync_hostcmd *to_add; + + WARN_ON(sync_state->to_do_end == GOLDFISH_SYNC_MAX_CMDS); + + to_add = &sync_state->to_do[sync_state->to_do_end]; + + to_add->cmd = cmd; + to_add->handle = handle; + to_add->time_arg = time_arg; + to_add->hostcmd_handle = hostcmd_handle; + + ++sync_state->to_do_end; +} + +static inline void +goldfish_sync_hostcmd_reply(struct goldfish_sync_state *sync_state, + u32 cmd, + u64 handle, + u32 time_arg, + u64 hostcmd_handle) +{ + unsigned long irq_flags; + struct goldfish_sync_hostcmd *batch_hostcmd = + &sync_state->batch_hostcmd; + + spin_lock_irqsave(&sync_state->to_do_lock, irq_flags); + + batch_hostcmd->cmd = cmd; + batch_hostcmd->handle = handle; + batch_hostcmd->time_arg = time_arg; + batch_hostcmd->hostcmd_handle = hostcmd_handle; + writel(0, sync_state->reg_base + SYNC_REG_BATCH_COMMAND); + + spin_unlock_irqrestore(&sync_state->to_do_lock, irq_flags); +} + +static inline void +goldfish_sync_send_guestcmd(struct goldfish_sync_state *sync_state, + u32 cmd, + u64 glsync_handle, + u64 thread_handle, + u64 timeline_handle) +{ + unsigned long irq_flags; + struct goldfish_sync_guestcmd *batch_guestcmd = + &sync_state->batch_guestcmd; + + spin_lock_irqsave(&sync_state->to_do_lock, irq_flags); + + batch_guestcmd->host_command = cmd; + batch_guestcmd->glsync_handle = glsync_handle; + batch_guestcmd->thread_handle = thread_handle; + batch_guestcmd->guest_timeline_handle = timeline_handle; + writel(0, sync_state->reg_base + SYNC_REG_BATCH_GUESTCOMMAND); + + spin_unlock_irqrestore(&sync_state->to_do_lock, irq_flags); +} + +/* |goldfish_sync_interrupt| handles IRQ raises from the virtual device. + * In the context of OpenGL, this interrupt will fire whenever we need + * to signal a fence fd in the guest, with the command + * |CMD_SYNC_TIMELINE_INC|. + * However, because this function will be called in an interrupt context, + * it is necessary to do the actual work of signaling off of interrupt context. + * The shared work queue is used for this purpose. At the end when + * all pending commands are intercepted by the interrupt handler, + * we call |schedule_work|, which will later run the actual + * desired sync command in |goldfish_sync_work_item_fn|. + */ +static irqreturn_t +goldfish_sync_interrupt_impl(struct goldfish_sync_state *sync_state) +{ + struct goldfish_sync_hostcmd *batch_hostcmd = + &sync_state->batch_hostcmd; + + spin_lock(&sync_state->to_do_lock); + for (;;) { + u32 nextcmd; + u32 command_r; + u64 handle_rw; + u32 time_r; + u64 hostcmd_handle_rw; + + readl(sync_state->reg_base + SYNC_REG_BATCH_COMMAND); + nextcmd = batch_hostcmd->cmd; + + if (nextcmd == 0) + break; + + command_r = nextcmd; + handle_rw = batch_hostcmd->handle; + time_r = batch_hostcmd->time_arg; + hostcmd_handle_rw = batch_hostcmd->hostcmd_handle; + + goldfish_sync_cmd_queue(sync_state, + command_r, + handle_rw, + time_r, + hostcmd_handle_rw); + } + spin_unlock(&sync_state->to_do_lock); + + schedule_work(&sync_state->work_item); + return IRQ_HANDLED; +} + +static const struct file_operations goldfish_sync_fops; + +static irqreturn_t goldfish_sync_interrupt(int irq, void *dev_id) +{ + struct goldfish_sync_state *sync_state = dev_id; + + return (sync_state->miscdev.fops == &goldfish_sync_fops) ? + goldfish_sync_interrupt_impl(sync_state) : IRQ_NONE; +} + +/* We expect that commands will come in at a slow enough rate + * so that incoming items will not be more than + * GOLDFISH_SYNC_MAX_CMDS. + * + * This is because the way the sync device is used, + * it's only for managing buffer data transfers per frame, + * with a sequential dependency between putting things in + * to_do and taking them out. Once a set of commands is + * queued up in to_do, the user of the device waits for + * them to be processed before queuing additional commands, + * which limits the rate at which commands come in + * to the rate at which we take them out here. + * + * We also don't expect more than MAX_CMDS to be issued + * at once; there is a correspondence between + * which buffers need swapping to the (display / buffer queue) + * to particular commands, and we don't expect there to be + * enough display or buffer queues in operation at once + * to overrun GOLDFISH_SYNC_MAX_CMDS. + */ +static u32 __must_check +goldfish_sync_grab_commands(struct goldfish_sync_state *sync_state, + struct goldfish_sync_hostcmd *dst) +{ + u32 to_do_end; + u32 i; + unsigned long irq_flags; + + spin_lock_irqsave(&sync_state->to_do_lock, irq_flags); + + to_do_end = sync_state->to_do_end; + for (i = 0; i < to_do_end; i++) + dst[i] = sync_state->to_do[i]; + sync_state->to_do_end = 0; + + spin_unlock_irqrestore(&sync_state->to_do_lock, irq_flags); + + return to_do_end; +} + +void goldfish_sync_run_hostcmd(struct goldfish_sync_state *sync_state, + struct goldfish_sync_hostcmd *todo) +{ + struct goldfish_sync_timeline *tl = + (struct goldfish_sync_timeline *)(uintptr_t)todo->handle; + int sync_fence_fd; + + switch (todo->cmd) { + case CMD_SYNC_READY: + break; + + case CMD_CREATE_SYNC_TIMELINE: + tl = goldfish_sync_timeline_create(sync_state); + WARN_ON(!tl); + goldfish_sync_hostcmd_reply(sync_state, + CMD_CREATE_SYNC_TIMELINE, + (uintptr_t)tl, + 0, + todo->hostcmd_handle); + break; + + case CMD_CREATE_SYNC_FENCE: + WARN_ON(!tl); + sync_fence_fd = goldfish_sync_fence_create(tl, todo->time_arg); + goldfish_sync_hostcmd_reply(sync_state, + CMD_CREATE_SYNC_FENCE, + sync_fence_fd, + 0, + todo->hostcmd_handle); + break; + + case CMD_SYNC_TIMELINE_INC: + WARN_ON(!tl); + goldfish_sync_timeline_signal(tl, todo->time_arg); + break; + + case CMD_DESTROY_SYNC_TIMELINE: + WARN_ON(!tl); + goldfish_sync_timeline_put(tl); + break; + } +} + +/* |goldfish_sync_work_item_fn| does the actual work of servicing + * host->guest sync commands. This function is triggered whenever + * the IRQ for the goldfish sync device is raised. Once it starts + * running, it grabs the contents of the buffer containing the + * commands it needs to execute (there may be multiple, because + * our IRQ is active high and not edge triggered), and then + * runs all of them one after the other. + */ +static void goldfish_sync_work_item_fn(struct work_struct *input) +{ + struct goldfish_sync_state *sync_state = + container_of(input, struct goldfish_sync_state, work_item); + + struct goldfish_sync_hostcmd to_run[GOLDFISH_SYNC_MAX_CMDS]; + u32 to_do_end; + u32 i; + + mutex_lock(&sync_state->mutex_lock); + + to_do_end = goldfish_sync_grab_commands(sync_state, to_run); + + for (i = 0; i < to_do_end; i++) + goldfish_sync_run_hostcmd(sync_state, &to_run[i]); + + mutex_unlock(&sync_state->mutex_lock); +} + +static int goldfish_sync_open(struct inode *inode, struct file *filp) +{ + struct goldfish_sync_state *sync_state = + container_of(filp->private_data, + struct goldfish_sync_state, + miscdev); + + if (mutex_lock_interruptible(&sync_state->mutex_lock)) + return -ERESTARTSYS; + + filp->private_data = goldfish_sync_timeline_create(sync_state); + mutex_unlock(&sync_state->mutex_lock); + + return filp->private_data ? 0 : -ENOMEM; +} + +static int goldfish_sync_release(struct inode *inode, struct file *filp) +{ + struct goldfish_sync_timeline *tl = filp->private_data; + + goldfish_sync_timeline_put(tl); + return 0; +} + +/* |goldfish_sync_ioctl| is the guest-facing interface of goldfish sync + * and is used in conjunction with eglCreateSyncKHR to queue up the + * actual work of waiting for the EGL sync command to complete, + * possibly returning a fence fd to the guest. + */ +static long +goldfish_sync_ioctl_locked(struct goldfish_sync_timeline *tl, + unsigned int cmd, + unsigned long arg) +{ + struct goldfish_sync_ioctl_info ioctl_data; + int fd_out = -1; + + switch (cmd) { + case GOLDFISH_SYNC_IOC_QUEUE_WORK: + if (copy_from_user(&ioctl_data, + (void __user *)arg, + sizeof(ioctl_data))) + return -EFAULT; + + if (!ioctl_data.host_syncthread_handle_in) + return -EFAULT; + + fd_out = goldfish_sync_fence_create(tl, tl->seqno + 1); + ioctl_data.fence_fd_out = fd_out; + + if (copy_to_user((void __user *)arg, + &ioctl_data, + sizeof(ioctl_data))) { + sys_close(fd_out); + return -EFAULT; + } + + /* We are now about to trigger a host-side wait; + * accumulate on |pending_waits|. + */ + goldfish_sync_send_guestcmd(tl->sync_state, + CMD_TRIGGER_HOST_WAIT, + ioctl_data.host_glsync_handle_in, + ioctl_data.host_syncthread_handle_in, + (u64)(uintptr_t)tl); + return 0; + + default: + return -ENOTTY; + } +} + +static long goldfish_sync_ioctl(struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + struct goldfish_sync_timeline *tl = filp->private_data; + struct goldfish_sync_state *x = tl->sync_state; + long res; + + if (mutex_lock_interruptible(&x->mutex_lock)) + return -ERESTARTSYS; + + res = goldfish_sync_ioctl_locked(tl, cmd, arg); + mutex_unlock(&x->mutex_lock); + + return res; +} + +static bool setup_verify_batch_cmd_addr(char *reg_base, + void *batch_addr, + u32 addr_offset, + u32 addr_offset_high) +{ + u64 batch_addr_phys; + u64 batch_addr_phys_test_lo; + u64 batch_addr_phys_test_hi; + + batch_addr_phys = virt_to_phys(batch_addr); + writel(lower_32_bits(batch_addr_phys), reg_base + addr_offset); + writel(upper_32_bits(batch_addr_phys), reg_base + addr_offset_high); + + batch_addr_phys_test_lo = readl(reg_base + addr_offset); + batch_addr_phys_test_hi = readl(reg_base + addr_offset_high); + + batch_addr_phys = batch_addr_phys_test_lo | + (batch_addr_phys_test_hi << 32); + + return virt_to_phys(batch_addr) == batch_addr_phys; +} + +static const struct file_operations goldfish_sync_fops = { + .owner = THIS_MODULE, + .open = goldfish_sync_open, + .release = goldfish_sync_release, + .unlocked_ioctl = goldfish_sync_ioctl, + .compat_ioctl = goldfish_sync_ioctl, +}; + +static void fill_miscdevice(struct miscdevice *misc) +{ + misc->name = GOLDFISH_SYNC_DEVICE_NAME; + misc->minor = MISC_DYNAMIC_MINOR; + misc->fops = &goldfish_sync_fops; +} + +static int goldfish_sync_probe(struct platform_device *pdev) +{ + struct goldfish_sync_state *sync_state; + struct resource *ioresource; + int result; + + sync_state = devm_kzalloc(&pdev->dev, sizeof(*sync_state), GFP_KERNEL); + if (!sync_state) + return -ENOMEM; + + spin_lock_init(&sync_state->to_do_lock); + mutex_init(&sync_state->mutex_lock); + INIT_WORK(&sync_state->work_item, goldfish_sync_work_item_fn); + + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ioresource) + return -ENODEV; + + sync_state->reg_base = + devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE); + if (!sync_state->reg_base) + return -ENOMEM; + + result = platform_get_irq(pdev, 0); + if (result < 0) + return -ENODEV; + + sync_state->irq = result; + + result = devm_request_irq(&pdev->dev, + sync_state->irq, + goldfish_sync_interrupt, + IRQF_SHARED, + pdev->name, + sync_state); + if (result) + return -ENODEV; + + if (!setup_verify_batch_cmd_addr(sync_state->reg_base, + &sync_state->batch_hostcmd, + SYNC_REG_BATCH_COMMAND_ADDR, + SYNC_REG_BATCH_COMMAND_ADDR_HIGH)) + return -ENODEV; + + if (!setup_verify_batch_cmd_addr(sync_state->reg_base, + &sync_state->batch_guestcmd, + SYNC_REG_BATCH_GUESTCOMMAND_ADDR, + SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH)) + return -ENODEV; + + fill_miscdevice(&sync_state->miscdev); + result = misc_register(&sync_state->miscdev); + if (result) + return -ENODEV; + + writel(0, sync_state->reg_base + SYNC_REG_INIT); + + platform_set_drvdata(pdev, sync_state); + + return 0; +} + +static int goldfish_sync_remove(struct platform_device *pdev) +{ + struct goldfish_sync_state *sync_state = platform_get_drvdata(pdev); + + misc_deregister(&sync_state->miscdev); + return 0; +} + +static const struct of_device_id goldfish_sync_of_match[] = { + { .compatible = "google,goldfish-sync", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_sync_of_match); + +static const struct acpi_device_id goldfish_sync_acpi_match[] = { + { "GFSH0006", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_sync_acpi_match); + +static struct platform_driver goldfish_sync = { + .probe = goldfish_sync_probe, + .remove = goldfish_sync_remove, + .driver = { + .name = GOLDFISH_SYNC_DEVICE_NAME, + .of_match_table = goldfish_sync_of_match, + .acpi_match_table = ACPI_PTR(goldfish_sync_acpi_match), + } +}; +module_platform_driver(goldfish_sync); + +MODULE_AUTHOR("Google, Inc."); +MODULE_DESCRIPTION("Android QEMU Sync Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("2.0"); diff --git a/drivers/staging/goldfish/Kconfig b/drivers/staging/goldfish/Kconfig index ab05e8927e9e..633a02db7f21 100644 --- a/drivers/staging/goldfish/Kconfig +++ b/drivers/staging/goldfish/Kconfig @@ -4,12 +4,3 @@ config GOLDFISH_AUDIO ---help--- Emulated audio channel for the Goldfish Android Virtual Device -config GOLDFISH_SYNC - tristate "Goldfish AVD Sync Driver" - depends on GOLDFISH - depends on HAS_IOMEM - depends on SW_SYNC - depends on SYNC_FILE - ---help--- - Emulated sync fences for the Goldfish Android Virtual Device - diff --git a/drivers/staging/goldfish/Makefile b/drivers/staging/goldfish/Makefile index 787836e04655..054eeb82151e 100644 --- a/drivers/staging/goldfish/Makefile +++ b/drivers/staging/goldfish/Makefile @@ -3,9 +3,3 @@ # obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o - -# and sync - -ccflags-y := -Idrivers/staging/android -goldfish_sync-objs := goldfish_sync_timeline_fence.o goldfish_sync_timeline.o -obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync.o diff --git a/drivers/staging/goldfish/goldfish_sync_timeline.c b/drivers/staging/goldfish/goldfish_sync_timeline.c deleted file mode 100644 index 880d6e2e5b34..000000000000 --- a/drivers/staging/goldfish/goldfish_sync_timeline.c +++ /dev/null @@ -1,962 +0,0 @@ -/* - * Copyright (C) 2016 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/fdtable.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> - -#include <linux/interrupt.h> -#include <linux/kref.h> -#include <linux/spinlock.h> -#include <linux/types.h> - -#include <linux/io.h> -#include <linux/mm.h> -#include <linux/acpi.h> - -#include <linux/string.h> - -#include <linux/fs.h> -#include <linux/syscalls.h> -#include <linux/sync_file.h> -#include <linux/dma-fence.h> - -#include "goldfish_sync_timeline_fence.h" - -#define ERR(...) printk(KERN_ERR __VA_ARGS__); - -#define INFO(...) printk(KERN_INFO __VA_ARGS__); - -#define DPRINT(...) pr_debug(__VA_ARGS__); - -#define DTRACE() DPRINT("%s: enter", __func__) - -/* The Goldfish sync driver is designed to provide a interface - * between the underlying host's sync device and the kernel's - * fence sync framework.. - * The purpose of the device/driver is to enable lightweight - * creation and signaling of timelines and fences - * in order to synchronize the guest with host-side graphics events. - * - * Each time the interrupt trips, the driver - * may perform a sync operation. - */ - -/* The operations are: */ - -/* Ready signal - used to mark when irq should lower */ -#define CMD_SYNC_READY 0 - -/* Create a new timeline. writes timeline handle */ -#define CMD_CREATE_SYNC_TIMELINE 1 - -/* Create a fence object. reads timeline handle and time argument. - * Writes fence fd to the SYNC_REG_HANDLE register. */ -#define CMD_CREATE_SYNC_FENCE 2 - -/* Increments timeline. reads timeline handle and time argument */ -#define CMD_SYNC_TIMELINE_INC 3 - -/* Destroys a timeline. reads timeline handle */ -#define CMD_DESTROY_SYNC_TIMELINE 4 - -/* Starts a wait on the host with - * the given glsync object and sync thread handle. */ -#define CMD_TRIGGER_HOST_WAIT 5 - -/* The register layout is: */ - -#define SYNC_REG_BATCH_COMMAND 0x00 /* host->guest batch commands */ -#define SYNC_REG_BATCH_GUESTCOMMAND 0x04 /* guest->host batch commands */ -#define SYNC_REG_BATCH_COMMAND_ADDR 0x08 /* communicate physical address of host->guest batch commands */ -#define SYNC_REG_BATCH_COMMAND_ADDR_HIGH 0x0c /* 64-bit part */ -#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR 0x10 /* communicate physical address of guest->host commands */ -#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH 0x14 /* 64-bit part */ -#define SYNC_REG_INIT 0x18 /* signals that the device has been probed */ - -/* There is an ioctl associated with goldfish sync driver. - * Make it conflict with ioctls that are not likely to be used - * in the emulator. - * - * '@' 00-0F linux/radeonfb.h conflict! - * '@' 00-0F drivers/video/aty/aty128fb.c conflict! - */ -#define GOLDFISH_SYNC_IOC_MAGIC '@' - -#define GOLDFISH_SYNC_IOC_QUEUE_WORK _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info) - -/* The above definitions (command codes, register layout, ioctl definitions) - * need to be in sync with the following files: - * - * Host-side (emulator): - * external/qemu/android/emulation/goldfish_sync.h - * external/qemu-android/hw/misc/goldfish_sync.c - * - * Guest-side (system image): - * device/generic/goldfish-opengl/system/egl/goldfish_sync.h - * device/generic/goldfish/ueventd.ranchu.rc - * platform/build/target/board/generic/sepolicy/file_contexts - */ -struct goldfish_sync_hostcmd { - /* sorted for alignment */ - uint64_t handle; - uint64_t hostcmd_handle; - uint32_t cmd; - uint32_t time_arg; -}; - -struct goldfish_sync_guestcmd { - uint64_t host_command; /* uint64_t for alignment */ - uint64_t glsync_handle; - uint64_t thread_handle; - uint64_t guest_timeline_handle; -}; - -#define GOLDFISH_SYNC_MAX_CMDS 32 - -struct goldfish_sync_state { - char __iomem *reg_base; - int irq; - - /* Spinlock protects |to_do| / |to_do_end|. */ - spinlock_t lock; - /* |mutex_lock| protects all concurrent access - * to timelines for both kernel and user space. */ - struct mutex mutex_lock; - - /* Buffer holding commands issued from host. */ - struct goldfish_sync_hostcmd to_do[GOLDFISH_SYNC_MAX_CMDS]; - uint32_t to_do_end; - - /* Addresses for the reading or writing - * of individual commands. The host can directly write - * to |batch_hostcmd| (and then this driver immediately - * copies contents to |to_do|). This driver either replies - * through |batch_hostcmd| or simply issues a - * guest->host command through |batch_guestcmd|. - */ - struct goldfish_sync_hostcmd *batch_hostcmd; - struct goldfish_sync_guestcmd *batch_guestcmd; - - /* Used to give this struct itself to a work queue - * function for executing actual sync commands. */ - struct work_struct work_item; -}; - -static struct goldfish_sync_state global_sync_state[1]; - -struct goldfish_sync_timeline_obj { - struct goldfish_sync_timeline *sync_tl; - uint32_t current_time; - /* We need to be careful about when we deallocate - * this |goldfish_sync_timeline_obj| struct. - * In order to ensure proper cleanup, we need to - * consider the triggered host-side wait that may - * still be in flight when the guest close()'s a - * goldfish_sync device's sync context fd (and - * destroys the |sync_tl| field above). - * The host-side wait may raise IRQ - * and tell the kernel to increment the timeline _after_ - * the |sync_tl| has already been set to null. - * - * From observations on OpenGL apps and CTS tests, this - * happens at some very low probability upon context - * destruction or process close, but it does happen - * and it needs to be handled properly. Otherwise, - * if we clean up the surrounding |goldfish_sync_timeline_obj| - * too early, any |handle| field of any host->guest command - * might not even point to a null |sync_tl| field, - * but to garbage memory or even a reclaimed |sync_tl|. - * If we do not count such "pending waits" and kfree the object - * immediately upon |goldfish_sync_timeline_destroy|, - * we might get mysterous RCU stalls after running a long - * time because the garbage memory that is being read - * happens to be interpretable as a |spinlock_t| struct - * that is currently in the locked state. - * - * To track when to free the |goldfish_sync_timeline_obj| - * itself, we maintain a kref. - * The kref essentially counts the timeline itself plus - * the number of waits in flight. kref_init/kref_put - * are issued on - * |goldfish_sync_timeline_create|/|goldfish_sync_timeline_destroy| - * and kref_get/kref_put are issued on - * |goldfish_sync_fence_create|/|goldfish_sync_timeline_inc|. - * - * The timeline is destroyed after reference count - * reaches zero, which would happen after - * |goldfish_sync_timeline_destroy| and all pending - * |goldfish_sync_timeline_inc|'s are fulfilled. - * - * NOTE (1): We assume that |fence_create| and - * |timeline_inc| calls are 1:1, otherwise the kref scheme - * will not work. This is a valid assumption as long - * as the host-side virtual device implementation - * does not insert any timeline increments - * that we did not trigger from here. - * - * NOTE (2): The use of kref by itself requires no locks, - * but this does not mean everything works without locks. - * Related timeline operations do require a lock of some sort, - * or at least are not proven to work without it. - * In particualr, we assume that all the operations - * done on the |kref| field above are done in contexts where - * |global_sync_state->mutex_lock| is held. Do not - * remove that lock until everything is proven to work - * without it!!! */ - struct kref kref; -}; - -/* We will call |delete_timeline_obj| when the last reference count - * of the kref is decremented. This deletes the sync - * timeline object along with the wrapper itself. */ -static void delete_timeline_obj(struct kref* kref) { - struct goldfish_sync_timeline_obj* obj = - container_of(kref, struct goldfish_sync_timeline_obj, kref); - - goldfish_sync_timeline_put_internal(obj->sync_tl); - obj->sync_tl = NULL; - kfree(obj); -} - -static uint64_t gensym_ctr; -static void gensym(char *dst) -{ - sprintf(dst, "goldfish_sync:gensym:%llu", gensym_ctr); - gensym_ctr++; -} - -/* |goldfish_sync_timeline_create| assumes that |global_sync_state->mutex_lock| - * is held. */ -static struct goldfish_sync_timeline_obj* -goldfish_sync_timeline_create(void) -{ - - char timeline_name[256]; - struct goldfish_sync_timeline *res_sync_tl = NULL; - struct goldfish_sync_timeline_obj *res; - - DTRACE(); - - gensym(timeline_name); - - res_sync_tl = goldfish_sync_timeline_create_internal(timeline_name); - if (!res_sync_tl) { - ERR("Failed to create goldfish_sw_sync timeline."); - return NULL; - } - - res = kzalloc(sizeof(struct goldfish_sync_timeline_obj), GFP_KERNEL); - res->sync_tl = res_sync_tl; - res->current_time = 0; - kref_init(&res->kref); - - DPRINT("new timeline_obj=0x%p", res); - return res; -} - -/* |goldfish_sync_fence_create| assumes that |global_sync_state->mutex_lock| - * is held. */ -static int -goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj, - uint32_t val) -{ - - int fd; - char fence_name[256]; - struct sync_pt *syncpt = NULL; - struct sync_file *sync_file_obj = NULL; - struct goldfish_sync_timeline *tl; - - DTRACE(); - - if (!obj) return -1; - - tl = obj->sync_tl; - - syncpt = goldfish_sync_pt_create_internal( - tl, sizeof(struct sync_pt) + 4, val); - if (!syncpt) { - ERR("could not create sync point! " - "goldfish_sync_timeline=0x%p val=%d", - tl, val); - return -1; - } - - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - ERR("could not get unused fd for sync fence. " - "errno=%d", fd); - goto err_cleanup_pt; - } - - gensym(fence_name); - - sync_file_obj = sync_file_create(&syncpt->base); - if (!sync_file_obj) { - ERR("could not create sync fence! " - "goldfish_sync_timeline=0x%p val=%d sync_pt=0x%p", - tl, val, syncpt); - goto err_cleanup_fd_pt; - } - - DPRINT("installing sync fence into fd %d sync_file_obj=0x%p", - fd, sync_file_obj); - fd_install(fd, sync_file_obj->file); - kref_get(&obj->kref); - - return fd; - -err_cleanup_fd_pt: - put_unused_fd(fd); -err_cleanup_pt: - dma_fence_put(&syncpt->base); - return -1; -} - -/* |goldfish_sync_timeline_inc| assumes that |global_sync_state->mutex_lock| - * is held. */ -static void -goldfish_sync_timeline_inc(struct goldfish_sync_timeline_obj *obj, uint32_t inc) -{ - DTRACE(); - /* Just give up if someone else nuked the timeline. - * Whoever it was won't care that it doesn't get signaled. */ - if (!obj) return; - - DPRINT("timeline_obj=0x%p", obj); - goldfish_sync_timeline_signal_internal(obj->sync_tl, inc); - DPRINT("incremented timeline. increment max_time"); - obj->current_time += inc; - - /* Here, we will end up deleting the timeline object if it - * turns out that this call was a pending increment after - * |goldfish_sync_timeline_destroy| was called. */ - kref_put(&obj->kref, delete_timeline_obj); - DPRINT("done"); -} - -/* |goldfish_sync_timeline_destroy| assumes - * that |global_sync_state->mutex_lock| is held. */ -static void -goldfish_sync_timeline_destroy(struct goldfish_sync_timeline_obj *obj) -{ - DTRACE(); - /* See description of |goldfish_sync_timeline_obj| for why we - * should not immediately destroy |obj| */ - kref_put(&obj->kref, delete_timeline_obj); -} - -static inline void -goldfish_sync_cmd_queue(struct goldfish_sync_state *sync_state, - uint32_t cmd, - uint64_t handle, - uint32_t time_arg, - uint64_t hostcmd_handle) -{ - struct goldfish_sync_hostcmd *to_add; - - DTRACE(); - - BUG_ON(sync_state->to_do_end == GOLDFISH_SYNC_MAX_CMDS); - - to_add = &sync_state->to_do[sync_state->to_do_end]; - - to_add->cmd = cmd; - to_add->handle = handle; - to_add->time_arg = time_arg; - to_add->hostcmd_handle = hostcmd_handle; - - sync_state->to_do_end += 1; -} - -static inline void -goldfish_sync_hostcmd_reply(struct goldfish_sync_state *sync_state, - uint32_t cmd, - uint64_t handle, - uint32_t time_arg, - uint64_t hostcmd_handle) -{ - unsigned long irq_flags; - struct goldfish_sync_hostcmd *batch_hostcmd = - sync_state->batch_hostcmd; - - DTRACE(); - - spin_lock_irqsave(&sync_state->lock, irq_flags); - - batch_hostcmd->cmd = cmd; - batch_hostcmd->handle = handle; - batch_hostcmd->time_arg = time_arg; - batch_hostcmd->hostcmd_handle = hostcmd_handle; - writel(0, sync_state->reg_base + SYNC_REG_BATCH_COMMAND); - - spin_unlock_irqrestore(&sync_state->lock, irq_flags); -} - -static inline void -goldfish_sync_send_guestcmd(struct goldfish_sync_state *sync_state, - uint32_t cmd, - uint64_t glsync_handle, - uint64_t thread_handle, - uint64_t timeline_handle) -{ - unsigned long irq_flags; - struct goldfish_sync_guestcmd *batch_guestcmd = - sync_state->batch_guestcmd; - - DTRACE(); - - spin_lock_irqsave(&sync_state->lock, irq_flags); - - batch_guestcmd->host_command = (uint64_t)cmd; - batch_guestcmd->glsync_handle = (uint64_t)glsync_handle; - batch_guestcmd->thread_handle = (uint64_t)thread_handle; - batch_guestcmd->guest_timeline_handle = (uint64_t)timeline_handle; - writel(0, sync_state->reg_base + SYNC_REG_BATCH_GUESTCOMMAND); - - spin_unlock_irqrestore(&sync_state->lock, irq_flags); -} - -/* |goldfish_sync_interrupt| handles IRQ raises from the virtual device. - * In the context of OpenGL, this interrupt will fire whenever we need - * to signal a fence fd in the guest, with the command - * |CMD_SYNC_TIMELINE_INC|. - * However, because this function will be called in an interrupt context, - * it is necessary to do the actual work of signaling off of interrupt context. - * The shared work queue is used for this purpose. At the end when - * all pending commands are intercepted by the interrupt handler, - * we call |schedule_work|, which will later run the actual - * desired sync command in |goldfish_sync_work_item_fn|. - */ -static irqreturn_t goldfish_sync_interrupt(int irq, void *dev_id) -{ - - struct goldfish_sync_state *sync_state = dev_id; - - uint32_t nextcmd; - uint32_t command_r; - uint64_t handle_rw; - uint32_t time_r; - uint64_t hostcmd_handle_rw; - - int count = 0; - - DTRACE(); - - sync_state = dev_id; - - spin_lock(&sync_state->lock); - - for (;;) { - - readl(sync_state->reg_base + SYNC_REG_BATCH_COMMAND); - nextcmd = sync_state->batch_hostcmd->cmd; - - if (nextcmd == 0) - break; - - command_r = nextcmd; - handle_rw = sync_state->batch_hostcmd->handle; - time_r = sync_state->batch_hostcmd->time_arg; - hostcmd_handle_rw = sync_state->batch_hostcmd->hostcmd_handle; - - goldfish_sync_cmd_queue( - sync_state, - command_r, - handle_rw, - time_r, - hostcmd_handle_rw); - - count++; - } - - spin_unlock(&sync_state->lock); - - schedule_work(&sync_state->work_item); - - return (count == 0) ? IRQ_NONE : IRQ_HANDLED; -} - -/* |goldfish_sync_work_item_fn| does the actual work of servicing - * host->guest sync commands. This function is triggered whenever - * the IRQ for the goldfish sync device is raised. Once it starts - * running, it grabs the contents of the buffer containing the - * commands it needs to execute (there may be multiple, because - * our IRQ is active high and not edge triggered), and then - * runs all of them one after the other. - */ -static void goldfish_sync_work_item_fn(struct work_struct *input) -{ - - struct goldfish_sync_state *sync_state; - int sync_fence_fd; - - struct goldfish_sync_timeline_obj *timeline; - uint64_t timeline_ptr; - - uint64_t hostcmd_handle; - - uint32_t cmd; - uint64_t handle; - uint32_t time_arg; - - struct goldfish_sync_hostcmd *todo; - uint32_t todo_end; - - unsigned long irq_flags; - - struct goldfish_sync_hostcmd to_run[GOLDFISH_SYNC_MAX_CMDS]; - uint32_t i = 0; - - sync_state = container_of(input, struct goldfish_sync_state, work_item); - - mutex_lock(&sync_state->mutex_lock); - - spin_lock_irqsave(&sync_state->lock, irq_flags); { - - todo_end = sync_state->to_do_end; - - DPRINT("num sync todos: %u", sync_state->to_do_end); - - for (i = 0; i < todo_end; i++) - to_run[i] = sync_state->to_do[i]; - - /* We expect that commands will come in at a slow enough rate - * so that incoming items will not be more than - * GOLDFISH_SYNC_MAX_CMDS. - * - * This is because the way the sync device is used, - * it's only for managing buffer data transfers per frame, - * with a sequential dependency between putting things in - * to_do and taking them out. Once a set of commands is - * queued up in to_do, the user of the device waits for - * them to be processed before queuing additional commands, - * which limits the rate at which commands come in - * to the rate at which we take them out here. - * - * We also don't expect more than MAX_CMDS to be issued - * at once; there is a correspondence between - * which buffers need swapping to the (display / buffer queue) - * to particular commands, and we don't expect there to be - * enough display or buffer queues in operation at once - * to overrun GOLDFISH_SYNC_MAX_CMDS. - */ - sync_state->to_do_end = 0; - - } spin_unlock_irqrestore(&sync_state->lock, irq_flags); - - for (i = 0; i < todo_end; i++) { - DPRINT("todo index: %u", i); - - todo = &to_run[i]; - - cmd = todo->cmd; - - handle = (uint64_t)todo->handle; - time_arg = todo->time_arg; - hostcmd_handle = (uint64_t)todo->hostcmd_handle; - - DTRACE(); - - timeline = (struct goldfish_sync_timeline_obj *)(uintptr_t)handle; - - switch (cmd) { - case CMD_SYNC_READY: - break; - case CMD_CREATE_SYNC_TIMELINE: - DPRINT("exec CMD_CREATE_SYNC_TIMELINE: " - "handle=0x%llx time_arg=%d", - handle, time_arg); - timeline = goldfish_sync_timeline_create(); - timeline_ptr = (uintptr_t)timeline; - goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_TIMELINE, - timeline_ptr, - 0, - hostcmd_handle); - DPRINT("sync timeline created: %p", timeline); - break; - case CMD_CREATE_SYNC_FENCE: - DPRINT("exec CMD_CREATE_SYNC_FENCE: " - "handle=0x%llx time_arg=%d", - handle, time_arg); - sync_fence_fd = goldfish_sync_fence_create(timeline, time_arg); - goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_FENCE, - sync_fence_fd, - 0, - hostcmd_handle); - break; - case CMD_SYNC_TIMELINE_INC: - DPRINT("exec CMD_SYNC_TIMELINE_INC: " - "handle=0x%llx time_arg=%d", - handle, time_arg); - goldfish_sync_timeline_inc(timeline, time_arg); - break; - case CMD_DESTROY_SYNC_TIMELINE: - DPRINT("exec CMD_DESTROY_SYNC_TIMELINE: " - "handle=0x%llx time_arg=%d", - handle, time_arg); - goldfish_sync_timeline_destroy(timeline); - break; - } - DPRINT("Done executing sync command"); - } - mutex_unlock(&sync_state->mutex_lock); -} - -/* Guest-side interface: file operations */ - -/* Goldfish sync context and ioctl info. - * - * When a sync context is created by open()-ing the goldfish sync device, we - * create a sync context (|goldfish_sync_context|). - * - * Currently, the only data required to track is the sync timeline itself - * along with the current time, which are all packed up in the - * |goldfish_sync_timeline_obj| field. We use a |goldfish_sync_context| - * as the filp->private_data. - * - * Next, when a sync context user requests that work be queued and a fence - * fd provided, we use the |goldfish_sync_ioctl_info| struct, which holds - * information about which host handles to touch for this particular - * queue-work operation. We need to know about the host-side sync thread - * and the particular host-side GLsync object. We also possibly write out - * a file descriptor. - */ -struct goldfish_sync_context { - struct goldfish_sync_timeline_obj *timeline; -}; - -struct goldfish_sync_ioctl_info { - uint64_t host_glsync_handle_in; - uint64_t host_syncthread_handle_in; - int fence_fd_out; -}; - -static int goldfish_sync_open(struct inode *inode, struct file *file) -{ - - struct goldfish_sync_context *sync_context; - - DTRACE(); - - mutex_lock(&global_sync_state->mutex_lock); - - sync_context = kzalloc(sizeof(struct goldfish_sync_context), GFP_KERNEL); - - if (sync_context == NULL) { - ERR("Creation of goldfish sync context failed!"); - mutex_unlock(&global_sync_state->mutex_lock); - return -ENOMEM; - } - - sync_context->timeline = NULL; - - file->private_data = sync_context; - - DPRINT("successfully create a sync context @0x%p", sync_context); - - mutex_unlock(&global_sync_state->mutex_lock); - - return 0; -} - -static int goldfish_sync_release(struct inode *inode, struct file *file) -{ - - struct goldfish_sync_context *sync_context; - - DTRACE(); - - mutex_lock(&global_sync_state->mutex_lock); - - sync_context = file->private_data; - - if (sync_context->timeline) - goldfish_sync_timeline_destroy(sync_context->timeline); - - sync_context->timeline = NULL; - - kfree(sync_context); - - mutex_unlock(&global_sync_state->mutex_lock); - - return 0; -} - -/* |goldfish_sync_ioctl| is the guest-facing interface of goldfish sync - * and is used in conjunction with eglCreateSyncKHR to queue up the - * actual work of waiting for the EGL sync command to complete, - * possibly returning a fence fd to the guest. - */ -static long goldfish_sync_ioctl(struct file *file, - unsigned int cmd, - unsigned long arg) -{ - struct goldfish_sync_context *sync_context_data; - struct goldfish_sync_timeline_obj *timeline; - int fd_out; - struct goldfish_sync_ioctl_info ioctl_data; - - DTRACE(); - - sync_context_data = file->private_data; - fd_out = -1; - - switch (cmd) { - case GOLDFISH_SYNC_IOC_QUEUE_WORK: - - DPRINT("exec GOLDFISH_SYNC_IOC_QUEUE_WORK"); - - mutex_lock(&global_sync_state->mutex_lock); - - if (copy_from_user(&ioctl_data, - (void __user *)arg, - sizeof(ioctl_data))) { - ERR("Failed to copy memory for ioctl_data from user."); - mutex_unlock(&global_sync_state->mutex_lock); - return -EFAULT; - } - - if (ioctl_data.host_syncthread_handle_in == 0) { - DPRINT("Error: zero host syncthread handle!!!"); - mutex_unlock(&global_sync_state->mutex_lock); - return -EFAULT; - } - - if (!sync_context_data->timeline) { - DPRINT("no timeline yet, create one."); - sync_context_data->timeline = goldfish_sync_timeline_create(); - DPRINT("timeline: 0x%p", &sync_context_data->timeline); - } - - timeline = sync_context_data->timeline; - fd_out = goldfish_sync_fence_create(timeline, - timeline->current_time + 1); - DPRINT("Created fence with fd %d and current time %u (timeline: 0x%p)", - fd_out, - sync_context_data->timeline->current_time + 1, - sync_context_data->timeline); - - ioctl_data.fence_fd_out = fd_out; - - if (copy_to_user((void __user *)arg, - &ioctl_data, - sizeof(ioctl_data))) { - DPRINT("Error, could not copy to user!!!"); - - sys_close(fd_out); - /* We won't be doing an increment, kref_put immediately. */ - kref_put(&timeline->kref, delete_timeline_obj); - mutex_unlock(&global_sync_state->mutex_lock); - return -EFAULT; - } - - /* We are now about to trigger a host-side wait; - * accumulate on |pending_waits|. */ - goldfish_sync_send_guestcmd(global_sync_state, - CMD_TRIGGER_HOST_WAIT, - ioctl_data.host_glsync_handle_in, - ioctl_data.host_syncthread_handle_in, - (uint64_t)(uintptr_t)(sync_context_data->timeline)); - - mutex_unlock(&global_sync_state->mutex_lock); - return 0; - default: - return -ENOTTY; - } -} - -static const struct file_operations goldfish_sync_fops = { - .owner = THIS_MODULE, - .open = goldfish_sync_open, - .release = goldfish_sync_release, - .unlocked_ioctl = goldfish_sync_ioctl, - .compat_ioctl = goldfish_sync_ioctl, -}; - -static struct miscdevice goldfish_sync_device = { - .name = "goldfish_sync", - .fops = &goldfish_sync_fops, -}; - - -static bool setup_verify_batch_cmd_addr(struct goldfish_sync_state *sync_state, - void *batch_addr, - uint32_t addr_offset, - uint32_t addr_offset_high) -{ - uint64_t batch_addr_phys; - uint32_t batch_addr_phys_test_lo; - uint32_t batch_addr_phys_test_hi; - - if (!batch_addr) { - ERR("Could not use batch command address!"); - return false; - } - - batch_addr_phys = virt_to_phys(batch_addr); - writel((uint32_t)(batch_addr_phys), - sync_state->reg_base + addr_offset); - writel((uint32_t)(batch_addr_phys >> 32), - sync_state->reg_base + addr_offset_high); - - batch_addr_phys_test_lo = - readl(sync_state->reg_base + addr_offset); - batch_addr_phys_test_hi = - readl(sync_state->reg_base + addr_offset_high); - - if (virt_to_phys(batch_addr) != - (((uint64_t)batch_addr_phys_test_hi << 32) | - batch_addr_phys_test_lo)) { - ERR("Invalid batch command address!"); - return false; - } - - return true; -} - -int goldfish_sync_probe(struct platform_device *pdev) -{ - struct resource *ioresource; - struct goldfish_sync_state *sync_state = global_sync_state; - int status; - - DTRACE(); - - sync_state->to_do_end = 0; - - spin_lock_init(&sync_state->lock); - mutex_init(&sync_state->mutex_lock); - - platform_set_drvdata(pdev, sync_state); - - ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (ioresource == NULL) { - ERR("platform_get_resource failed"); - return -ENODEV; - } - - sync_state->reg_base = - devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE); - if (sync_state->reg_base == NULL) { - ERR("Could not ioremap"); - return -ENOMEM; - } - - sync_state->irq = platform_get_irq(pdev, 0); - if (sync_state->irq < 0) { - ERR("Could not platform_get_irq"); - return -ENODEV; - } - - status = devm_request_irq(&pdev->dev, - sync_state->irq, - goldfish_sync_interrupt, - IRQF_SHARED, - pdev->name, - sync_state); - if (status) { - ERR("request_irq failed"); - return -ENODEV; - } - - INIT_WORK(&sync_state->work_item, - goldfish_sync_work_item_fn); - - misc_register(&goldfish_sync_device); - - /* Obtain addresses for batch send/recv of commands. */ - { - struct goldfish_sync_hostcmd *batch_addr_hostcmd; - struct goldfish_sync_guestcmd *batch_addr_guestcmd; - - batch_addr_hostcmd = - devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd), - GFP_KERNEL); - batch_addr_guestcmd = - devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd), - GFP_KERNEL); - - if (!setup_verify_batch_cmd_addr(sync_state, - batch_addr_hostcmd, - SYNC_REG_BATCH_COMMAND_ADDR, - SYNC_REG_BATCH_COMMAND_ADDR_HIGH)) { - ERR("goldfish_sync: Could not setup batch command address"); - return -ENODEV; - } - - if (!setup_verify_batch_cmd_addr(sync_state, - batch_addr_guestcmd, - SYNC_REG_BATCH_GUESTCOMMAND_ADDR, - SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH)) { - ERR("goldfish_sync: Could not setup batch guest command address"); - return -ENODEV; - } - - sync_state->batch_hostcmd = batch_addr_hostcmd; - sync_state->batch_guestcmd = batch_addr_guestcmd; - } - - INFO("goldfish_sync: Initialized goldfish sync device"); - - writel(0, sync_state->reg_base + SYNC_REG_INIT); - - return 0; -} - -static int goldfish_sync_remove(struct platform_device *pdev) -{ - struct goldfish_sync_state *sync_state = global_sync_state; - - DTRACE(); - - misc_deregister(&goldfish_sync_device); - memset(sync_state, 0, sizeof(struct goldfish_sync_state)); - return 0; -} - -static const struct of_device_id goldfish_sync_of_match[] = { - { .compatible = "google,goldfish-sync", }, - {}, -}; -MODULE_DEVICE_TABLE(of, goldfish_sync_of_match); - -static const struct acpi_device_id goldfish_sync_acpi_match[] = { - { "GFSH0006", 0 }, - { }, -}; - -MODULE_DEVICE_TABLE(acpi, goldfish_sync_acpi_match); - -static struct platform_driver goldfish_sync = { - .probe = goldfish_sync_probe, - .remove = goldfish_sync_remove, - .driver = { - .name = "goldfish_sync", - .of_match_table = goldfish_sync_of_match, - .acpi_match_table = ACPI_PTR(goldfish_sync_acpi_match), - } -}; - -module_platform_driver(goldfish_sync); - -MODULE_AUTHOR("Google, Inc."); -MODULE_DESCRIPTION("Android QEMU Sync Driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("1.0"); diff --git a/drivers/staging/goldfish/goldfish_sync_timeline_fence.c b/drivers/staging/goldfish/goldfish_sync_timeline_fence.c deleted file mode 100644 index a5bc2deab780..000000000000 --- a/drivers/staging/goldfish/goldfish_sync_timeline_fence.c +++ /dev/null @@ -1,254 +0,0 @@ -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/syscalls.h> -#include <linux/sync_file.h> -#include <linux/dma-fence.h> - -#include "goldfish_sync_timeline_fence.h" - -/* - * Timeline-based sync for Goldfish Sync - * Based on "Sync File validation framework" - * (drivers/dma-buf/sw_sync.c) - * - * Copyright (C) 2017 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -/** - * struct goldfish_sync_timeline - sync object - * @kref: reference count on fence. - * @name: name of the goldfish_sync_timeline. Useful for debugging - * @child_list_head: list of children sync_pts for this goldfish_sync_timeline - * @child_list_lock: lock protecting @child_list_head and fence.status - * @active_list_head: list of active (unsignaled/errored) sync_pts - */ -struct goldfish_sync_timeline { - struct kref kref; - char name[32]; - - /* protected by child_list_lock */ - u64 context; - int value; - - struct list_head child_list_head; - spinlock_t child_list_lock; - - struct list_head active_list_head; -}; - -static inline struct goldfish_sync_timeline *goldfish_dma_fence_parent(struct dma_fence *fence) -{ - return container_of(fence->lock, struct goldfish_sync_timeline, - child_list_lock); -} - -static const struct dma_fence_ops goldfish_sync_timeline_fence_ops; - -static inline struct sync_pt *goldfish_sync_fence_to_sync_pt(struct dma_fence *fence) -{ - if (fence->ops != &goldfish_sync_timeline_fence_ops) - return NULL; - return container_of(fence, struct sync_pt, base); -} - -/** - * goldfish_sync_timeline_create_internal() - creates a sync object - * @name: sync_timeline name - * - * Creates a new sync_timeline. Returns the sync_timeline object or NULL in - * case of error. - */ -struct goldfish_sync_timeline -*goldfish_sync_timeline_create_internal(const char *name) -{ - struct goldfish_sync_timeline *obj; - - obj = kzalloc(sizeof(*obj), GFP_KERNEL); - if (!obj) - return NULL; - - kref_init(&obj->kref); - obj->context = dma_fence_context_alloc(1); - strlcpy(obj->name, name, sizeof(obj->name)); - - INIT_LIST_HEAD(&obj->child_list_head); - INIT_LIST_HEAD(&obj->active_list_head); - spin_lock_init(&obj->child_list_lock); - - return obj; -} - -static void goldfish_sync_timeline_free_internal(struct kref *kref) -{ - struct goldfish_sync_timeline *obj = - container_of(kref, struct goldfish_sync_timeline, kref); - - kfree(obj); -} - -static void goldfish_sync_timeline_get_internal( - struct goldfish_sync_timeline *obj) -{ - kref_get(&obj->kref); -} - -void goldfish_sync_timeline_put_internal(struct goldfish_sync_timeline *obj) -{ - kref_put(&obj->kref, goldfish_sync_timeline_free_internal); -} - -/** - * goldfish_sync_timeline_signal() - - * signal a status change on a goldfish_sync_timeline - * @obj: sync_timeline to signal - * @inc: num to increment on timeline->value - * - * A sync implementation should call this any time one of it's fences - * has signaled or has an error condition. - */ -void goldfish_sync_timeline_signal_internal(struct goldfish_sync_timeline *obj, - unsigned int inc) -{ - unsigned long flags; - struct sync_pt *pt, *next; - - spin_lock_irqsave(&obj->child_list_lock, flags); - - obj->value += inc; - - list_for_each_entry_safe(pt, next, &obj->active_list_head, - active_list) { - if (dma_fence_is_signaled_locked(&pt->base)) - list_del_init(&pt->active_list); - } - - spin_unlock_irqrestore(&obj->child_list_lock, flags); -} - -/** - * goldfish_sync_pt_create_internal() - creates a sync pt - * @parent: fence's parent sync_timeline - * @size: size to allocate for this pt - * @inc: value of the fence - * - * Creates a new sync_pt as a child of @parent. @size bytes will be - * allocated allowing for implementation specific data to be kept after - * the generic sync_timeline struct. Returns the sync_pt object or - * NULL in case of error. - */ -struct sync_pt *goldfish_sync_pt_create_internal( - struct goldfish_sync_timeline *obj, int size, - unsigned int value) -{ - unsigned long flags; - struct sync_pt *pt; - - if (size < sizeof(*pt)) - return NULL; - - pt = kzalloc(size, GFP_KERNEL); - if (!pt) - return NULL; - - spin_lock_irqsave(&obj->child_list_lock, flags); - goldfish_sync_timeline_get_internal(obj); - dma_fence_init(&pt->base, &goldfish_sync_timeline_fence_ops, &obj->child_list_lock, - obj->context, value); - list_add_tail(&pt->child_list, &obj->child_list_head); - INIT_LIST_HEAD(&pt->active_list); - spin_unlock_irqrestore(&obj->child_list_lock, flags); - return pt; -} - -static const char *goldfish_sync_timeline_fence_get_driver_name( - struct dma_fence *fence) -{ - return "sw_sync"; -} - -static const char *goldfish_sync_timeline_fence_get_timeline_name( - struct dma_fence *fence) -{ - struct goldfish_sync_timeline *parent = goldfish_dma_fence_parent(fence); - - return parent->name; -} - -static void goldfish_sync_timeline_fence_release(struct dma_fence *fence) -{ - struct sync_pt *pt = goldfish_sync_fence_to_sync_pt(fence); - struct goldfish_sync_timeline *parent = goldfish_dma_fence_parent(fence); - unsigned long flags; - - spin_lock_irqsave(fence->lock, flags); - list_del(&pt->child_list); - if (!list_empty(&pt->active_list)) - list_del(&pt->active_list); - spin_unlock_irqrestore(fence->lock, flags); - - goldfish_sync_timeline_put_internal(parent); - dma_fence_free(fence); -} - -static bool goldfish_sync_timeline_fence_signaled(struct dma_fence *fence) -{ - struct goldfish_sync_timeline *parent = goldfish_dma_fence_parent(fence); - - return (fence->seqno > parent->value) ? false : true; -} - -static bool goldfish_sync_timeline_fence_enable_signaling(struct dma_fence *fence) -{ - struct sync_pt *pt = goldfish_sync_fence_to_sync_pt(fence); - struct goldfish_sync_timeline *parent = goldfish_dma_fence_parent(fence); - - if (goldfish_sync_timeline_fence_signaled(fence)) - return false; - - list_add_tail(&pt->active_list, &parent->active_list_head); - return true; -} - -static void goldfish_sync_timeline_fence_disable_signaling(struct dma_fence *fence) -{ - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - - list_del_init(&pt->active_list); -} - -static void goldfish_sync_timeline_fence_value_str(struct dma_fence *fence, - char *str, int size) -{ - snprintf(str, size, "%d", fence->seqno); -} - -static void goldfish_sync_timeline_fence_timeline_value_str( - struct dma_fence *fence, - char *str, int size) -{ - struct goldfish_sync_timeline *parent = goldfish_dma_fence_parent(fence); - - snprintf(str, size, "%d", parent->value); -} - -static const struct dma_fence_ops goldfish_sync_timeline_fence_ops = { - .get_driver_name = goldfish_sync_timeline_fence_get_driver_name, - .get_timeline_name = goldfish_sync_timeline_fence_get_timeline_name, - .enable_signaling = goldfish_sync_timeline_fence_enable_signaling, - .disable_signaling = goldfish_sync_timeline_fence_disable_signaling, - .signaled = goldfish_sync_timeline_fence_signaled, - .wait = dma_fence_default_wait, - .release = goldfish_sync_timeline_fence_release, - .fence_value_str = goldfish_sync_timeline_fence_value_str, - .timeline_value_str = goldfish_sync_timeline_fence_timeline_value_str, -}; diff --git a/drivers/staging/goldfish/goldfish_sync_timeline_fence.h b/drivers/staging/goldfish/goldfish_sync_timeline_fence.h deleted file mode 100644 index 638c6fb68f1e..000000000000 --- a/drivers/staging/goldfish/goldfish_sync_timeline_fence.h +++ /dev/null @@ -1,58 +0,0 @@ -#include <linux/sync_file.h> -#include <linux/dma-fence.h> - -/** - * struct sync_pt - sync_pt object - * @base: base dma_fence object - * @child_list: sync timeline child's list - * @active_list: sync timeline active child's list - */ -struct sync_pt { - struct dma_fence base; - struct list_head child_list; - struct list_head active_list; -}; - -/** - * goldfish_sync_timeline_create_internal() - creates a sync object - * @name: goldfish_sync_timeline name - * - * Creates a new goldfish_sync_timeline. - * Returns the goldfish_sync_timeline object or NULL in case of error. - */ -struct goldfish_sync_timeline -*goldfish_sync_timeline_create_internal(const char *name); - -/** - * goldfish_sync_pt_create_internal() - creates a sync pt - * @parent: fence's parent goldfish_sync_timeline - * @size: size to allocate for this pt - * @inc: value of the fence - * - * Creates a new sync_pt as a child of @parent. @size bytes will be - * allocated allowing for implementation specific data to be kept after - * the generic sync_timeline struct. Returns the sync_pt object or - * NULL in case of error. - */ -struct sync_pt -*goldfish_sync_pt_create_internal(struct goldfish_sync_timeline *obj, - int size, unsigned int value); - -/** - * goldfish_sync_timeline_signal_internal() - - * signal a status change on a sync_timeline - * @obj: goldfish_sync_timeline to signal - * @inc: num to increment on timeline->value - * - * A sync implementation should call this any time one of it's fences - * has signaled or has an error condition. - */ -void goldfish_sync_timeline_signal_internal(struct goldfish_sync_timeline *obj, - unsigned int inc); - -/** - * goldfish_sync_timeline_put_internal() - dec refcount of a sync_timeline - * and clean up memory if it was the last ref. - * @obj: goldfish_sync_timeline to decref - */ -void goldfish_sync_timeline_put_internal(struct goldfish_sync_timeline *obj); diff --git a/include/uapi/linux/goldfish/goldfish_sync.h b/include/uapi/linux/goldfish/goldfish_sync.h new file mode 100644 index 000000000000..01d762f77308 --- /dev/null +++ b/include/uapi/linux/goldfish/goldfish_sync.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef UAPI_GOLDFISH_SYNC_H +#define UAPI_GOLDFISH_SYNC_H + +#include <linux/types.h> + +#define GOLDFISH_SYNC_DEVICE_NAME "goldfish_sync" + +struct goldfish_sync_ioctl_info { + __u64 host_glsync_handle_in; + __u64 host_syncthread_handle_in; + __s32 fence_fd_out; +}; + +/* There is an ioctl associated with goldfish sync driver. + * Make it conflict with ioctls that are not likely to be used + * in the emulator. + * + * '@' 00-0F linux/radeonfb.h conflict! + * '@' 00-0F drivers/video/aty/aty128fb.c conflict! + */ +#define GOLDFISH_SYNC_IOC_MAGIC '@' + +#define GOLDFISH_SYNC_IOC_QUEUE_WORK \ + _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info) + +#endif /* UAPI_GOLDFISH_SYNC_H */ |