sync: oneshot_sync: Add oneshot_sync driver

Add the oneshot_sync driver as of msm-3.18 commit
7892968f (sync: oneshot_sync: Update oneshot_sync for new sync APIs).

Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
This commit is contained in:
Jordan Crouse 2016-03-15 16:17:01 -06:00 committed by David Keitel
parent 39d07c3f5e
commit 881d8ec58a
8 changed files with 561 additions and 1 deletions

View file

@ -432,7 +432,7 @@ int kgsl_sync_fence_async_cancel(struct kgsl_sync_fence_waiter *kwaiter)
#ifdef CONFIG_ONESHOT_SYNC
#include <linux/oneshot_sync.h>
#include "oneshot_sync.h"
struct kgsl_syncsource {
struct kref refcount;

View file

@ -75,6 +75,21 @@ config SW_SYNC_USER
*WARNING* improper use of this can result in deadlocking kernel
drivers from userspace.
config ONESHOT_SYNC
bool "One shot sync objects"
depends on SYNC
help
This sync driver provides a way to create sync objects that may
be signaled by userspace. Unlike other sync objects, the
sync objects created by this driver may be signaled in any order
without changing the state of other sync objects on the timeline.
config ONESHOT_SYNC_USER
bool "Userspace API for ONESHOT_SYNC"
depends on SYNC
help
Provide a userspace API for creating oneshot sync objects.
source "drivers/staging/android/ion/Kconfig"
source "drivers/staging/android/fiq_debugger/Kconfig"

View file

@ -9,3 +9,4 @@ obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
obj-$(CONFIG_SYNC) += sync.o sync_debug.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o
obj-$(CONFIG_ONESHOT_SYNC) += oneshot_sync.o

View file

@ -0,0 +1,435 @@
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include "sync.h"
#include "oneshot_sync.h"
/**
* struct oneshot_sync_timeline - a userspace signaled, out of order, timeline
* @obj: base sync timeline
* @lock: spinlock to guard other members
* @state_list: list of oneshot_sync_states.
* @id: next id for points creating oneshot_sync_pts
*/
struct oneshot_sync_timeline {
struct sync_timeline obj;
spinlock_t lock;
struct list_head state_list;
unsigned int id;
};
#define to_oneshot_timeline(_p) \
container_of((_p), struct oneshot_sync_timeline, obj)
/**
* struct oneshot_sync_state - signal state for a group of oneshot points
* @refcount: reference count for this structure.
* @signaled: is this signaled or not?
* @id: identifier for this state
* @orig_fence: fence used to create this state, no is reference count held.
* @timeline: back pointer to the timeline.
*/
struct oneshot_sync_state {
struct kref refcount;
struct list_head node;
bool signaled;
unsigned int id;
struct sync_fence *orig_fence;
struct oneshot_sync_timeline *timeline;
};
/**
* struct oneshot_sync_pt
* @sync_pt: base sync point structure
* @state: reference counted pointer to the state of this pt
*/
struct oneshot_sync_pt {
struct sync_pt sync_pt;
struct oneshot_sync_state *state;
bool dup;
};
#define to_oneshot_pt(_p) container_of((_p), struct oneshot_sync_pt, sync_pt)
static void oneshot_state_destroy(struct kref *ref)
{
struct oneshot_sync_state *state =
container_of(ref, struct oneshot_sync_state, refcount);
spin_lock(&state->timeline->lock);
list_del(&state->node);
spin_unlock(&state->timeline->lock);
kfree(state);
}
static void oneshot_state_put(struct oneshot_sync_state *state)
{
kref_put(&state->refcount, oneshot_state_destroy);
}
static struct oneshot_sync_pt *
oneshot_pt_create(struct oneshot_sync_timeline *timeline)
{
struct oneshot_sync_pt *pt = NULL;
pt = (struct oneshot_sync_pt *)sync_pt_create(&timeline->obj,
sizeof(*pt));
if (pt == NULL)
return NULL;
pt->state = kzalloc(sizeof(struct oneshot_sync_state), GFP_KERNEL);
if (pt->state == NULL)
goto error;
kref_init(&pt->state->refcount);
pt->state->signaled = false;
pt->state->timeline = timeline;
spin_lock(&timeline->lock);
/* assign an id to the state, which could be shared by several pts. */
pt->state->id = ++(timeline->id);
/* add this pt to the list of pts that can be signaled by userspace */
list_add_tail(&pt->state->node, &timeline->state_list);
spin_unlock(&timeline->lock);
return pt;
error:
if (pt)
sync_pt_free(&pt->sync_pt);
return NULL;
}
static struct sync_pt *oneshot_pt_dup(struct sync_pt *sync_pt)
{
struct oneshot_sync_pt *out_pt;
struct oneshot_sync_pt *pt = to_oneshot_pt(sync_pt);
if (!kref_get_unless_zero(&pt->state->refcount))
return NULL;
out_pt = (struct oneshot_sync_pt *)
sync_pt_create(sync_pt_parent(sync_pt), sizeof(*out_pt));
if (out_pt == NULL) {
oneshot_state_put(pt->state);
return NULL;
}
out_pt->state = pt->state;
out_pt->dup = true;
return &out_pt->sync_pt;
}
static int oneshot_pt_has_signaled(struct sync_pt *sync_pt)
{
struct oneshot_sync_pt *pt = to_oneshot_pt(sync_pt);
return pt->state->signaled;
}
static int oneshot_pt_compare(struct sync_pt *a, struct sync_pt *b)
{
struct oneshot_sync_pt *pt_a = to_oneshot_pt(a);
struct oneshot_sync_pt *pt_b = to_oneshot_pt(b);
/*
* since oneshot sync points are order-independent,
* return an arbitrary order which just happens to
* prevent sync.c from collapsing the points.
*/
return (pt_a->state == pt_b->state) ? 0 : 1;
}
static void oneshot_pt_free(struct sync_pt *sync_pt)
{
struct oneshot_sync_pt *pt = to_oneshot_pt(sync_pt);
struct sync_timeline *parent = sync_pt_parent(sync_pt);
struct oneshot_sync_timeline *timeline = parent != NULL?
to_oneshot_timeline(parent) : NULL;
if (timeline != NULL) {
spin_lock(&timeline->lock);
/*
* If this is the original pt (and fence), signal to avoid
* deadlock. Unfornately, we can't signal the timeline here
* safely, so there could be a delay until the pt's
* state change is noticed.
*/
if (pt->dup == false) {
/*
* If the original pt goes away, force it signaled to
* avoid deadlock.
*/
if (!pt->state->signaled) {
pr_debug("id %d: fence closed before signal.\n",
pt->state->id);
pt->state->signaled = true;
}
}
spin_unlock(&timeline->lock);
}
oneshot_state_put(pt->state);
}
static void oneshot_pt_value_str(struct sync_pt *sync_pt, char *str, int size)
{
struct oneshot_sync_pt *pt = to_oneshot_pt(sync_pt);
snprintf(str, size, "%u", pt->state->id);
}
static struct sync_timeline_ops oneshot_timeline_ops = {
.driver_name = "oneshot",
.dup = oneshot_pt_dup,
.has_signaled = oneshot_pt_has_signaled,
.compare = oneshot_pt_compare,
.free_pt = oneshot_pt_free,
.pt_value_str = oneshot_pt_value_str,
};
struct oneshot_sync_timeline *oneshot_timeline_create(const char *name)
{
struct oneshot_sync_timeline *timeline = NULL;
static const char *default_name = "oneshot-timeline";
if (name == NULL)
name = default_name;
timeline = (struct oneshot_sync_timeline *)
sync_timeline_create(&oneshot_timeline_ops,
sizeof(*timeline),
name);
if (timeline == NULL)
return NULL;
INIT_LIST_HEAD(&timeline->state_list);
spin_lock_init(&timeline->lock);
return timeline;
}
EXPORT_SYMBOL(oneshot_timeline_create);
void oneshot_timeline_destroy(struct oneshot_sync_timeline *timeline)
{
if (timeline)
sync_timeline_destroy(&timeline->obj);
}
EXPORT_SYMBOL(oneshot_timeline_destroy);
struct sync_fence *oneshot_fence_create(struct oneshot_sync_timeline *timeline,
const char *name)
{
struct sync_fence *fence = NULL;
struct oneshot_sync_pt *pt = NULL;
pt = oneshot_pt_create(timeline);
if (pt == NULL)
return NULL;
fence = sync_fence_create(name, &pt->sync_pt);
if (fence == NULL) {
sync_pt_free(&pt->sync_pt);
return NULL;
}
pt->state->orig_fence = fence;
return fence;
}
EXPORT_SYMBOL(oneshot_fence_create);
int oneshot_fence_signal(struct oneshot_sync_timeline *timeline,
struct sync_fence *fence)
{
int ret = -EINVAL;
struct oneshot_sync_state *state = NULL;
bool signaled = false;
if (timeline == NULL || fence == NULL)
return -EINVAL;
spin_lock(&timeline->lock);
list_for_each_entry(state, &timeline->state_list, node) {
/*
* If we have the point from this fence on our list,
* this is is the original fence we created, so signal it.
*/
if (state->orig_fence == fence) {
/* ignore attempts to signal multiple times */
if (!state->signaled) {
state->signaled = true;
signaled = true;
}
ret = 0;
break;
}
}
spin_unlock(&timeline->lock);
if (ret == -EINVAL)
pr_debug("fence: %p not from this timeline\n", fence);
if (signaled)
sync_timeline_signal(&timeline->obj);
return ret;
}
EXPORT_SYMBOL(oneshot_fence_signal);
#ifdef CONFIG_ONESHOT_SYNC_USER
static int oneshot_open(struct inode *inode, struct file *file)
{
struct oneshot_sync_timeline *timeline = NULL;
char name[32];
char task_comm[TASK_COMM_LEN];
get_task_comm(task_comm, current);
snprintf(name, sizeof(name), "%s-oneshot", task_comm);
timeline = oneshot_timeline_create(name);
if (timeline == NULL)
return -ENOMEM;
file->private_data = timeline;
return 0;
}
static int oneshot_release(struct inode *inode, struct file *file)
{
struct oneshot_sync_timeline *timeline = file->private_data;
oneshot_timeline_destroy(timeline);
return 0;
}
static long oneshot_ioctl_fence_create(struct oneshot_sync_timeline *timeline,
unsigned long arg)
{
struct oneshot_sync_create_fence param;
int ret = -ENOMEM;
struct sync_fence *fence = NULL;
int fd = get_unused_fd_flags(0);
if (fd < 0)
return fd;
if (copy_from_user(&param, (void __user *)arg, sizeof(param))) {
ret = -EFAULT;
goto out;
}
fence = oneshot_fence_create(timeline, param.name);
if (fence == NULL) {
ret = -ENOMEM;
goto out;
}
param.fence_fd = fd;
if (copy_to_user((void __user *)arg, &param, sizeof(param))) {
ret = -EFAULT;
goto out;
}
sync_fence_install(fence, fd);
ret = 0;
out:
if (ret) {
if (fence)
sync_fence_put(fence);
put_unused_fd(fd);
}
return ret;
}
static long oneshot_ioctl_fence_signal(struct oneshot_sync_timeline *timeline,
unsigned long arg)
{
int ret = -EINVAL;
int fd = -1;
struct sync_fence *fence = NULL;
if (get_user(fd, (int __user *)arg))
return -EFAULT;
fence = sync_fence_fdget(fd);
if (fence == NULL)
return -EBADF;
ret = oneshot_fence_signal(timeline, fence);
sync_fence_put(fence);
return ret;
}
static long oneshot_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct oneshot_sync_timeline *timeline = file->private_data;
switch (cmd) {
case ONESHOT_SYNC_IOC_CREATE_FENCE:
return oneshot_ioctl_fence_create(timeline, arg);
case ONESHOT_SYNC_IOC_SIGNAL_FENCE:
return oneshot_ioctl_fence_signal(timeline, arg);
default:
return -ENOTTY;
}
}
static const struct file_operations oneshot_fops = {
.owner = THIS_MODULE,
.open = oneshot_open,
.release = oneshot_release,
.unlocked_ioctl = oneshot_ioctl,
.compat_ioctl = oneshot_ioctl,
};
static struct miscdevice oneshot_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "oneshot_sync",
.fops = &oneshot_fops,
};
static int __init oneshot_init(void)
{
return misc_register(&oneshot_dev);
}
static void __exit oneshot_remove(void)
{
misc_deregister(&oneshot_dev);
}
module_init(oneshot_init);
module_exit(oneshot_remove);
#endif /* CONFIG_ONESHOT_SYNC_USER */
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,58 @@
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*
*/
#ifndef LINUX_ONESHOT_SYNC_H
#define LINUX_ONESHOT_SYNC_H
#include <uapi/linux/oneshot_sync.h>
struct oneshot_sync_timeline;
struct sync_fence;
#ifdef CONFIG_ONESHOT_SYNC
struct oneshot_sync_timeline *oneshot_timeline_create(const char *name);
void oneshot_timeline_destroy(struct oneshot_sync_timeline *);
struct sync_fence *oneshot_fence_create(struct oneshot_sync_timeline *,
const char *name);
int oneshot_fence_signal(struct oneshot_sync_timeline *, struct sync_fence *);
#else
static inline struct oneshot_sync_timeline *
oneshot_timeline_create(const char *name)
{
return NULL;
}
void oneshot_timeline_destroy(struct oneshot_sync_timeline *timeline)
{
}
struct sync_fence *oneshot_fence_create(struct oneshot_sync_timeline *timeline,
const char *name)
{
return NULL;
}
int oneshot_fence_signal(struct oneshot_sync_timeline *timeline,
struct sync_fence *fence)
{
return -EINVAL;
}
#endif
#endif /* LINUX_ONESHOT_SYNC_H */

View file

@ -0,0 +1,49 @@
#ifndef ONESHOT_SYNC_H
#define ONESHOT_SYNC_H
/**
* DOC: Oneshot sync Userspace API
*
* Opening a file descriptor from /dev/oneshot_sync creates a * sync timeline
* for userspace signaled fences. Userspace may create new fences from a
* /dev/oneshot_sync file descriptor and then signal them by passing the fence
* file descriptor in an ioctl() call on the fd used to create the fence.
* Unlike most sync timelines, there is no ordering on a oneshot timeline.
* Each fence may be signaled in any order without affecting the state of other
* fences on the timeline.
*/
#define ONESHOT_SYNC_IOC_MAGIC '1'
/**
* struct oneshot_sync_create_fence - argument to create fence ioctl
* @name: name of the new fence, to aid debugging.
* @fence_fd: returned sync_fence file descriptor
*/
struct oneshot_sync_create_fence {
char name[32];
int fence_fd;
};
/**
* DOC: ONESHOT_SYNC_IOC_CREATE_FENCE - create a userspace signaled fence
*
* Create a fence that may be signaled by userspace by calling
* ONESHOT_SYNC_IOC_SIGNAL_FENCE. There are no order dependencies between
* these fences, but otherwise they behave like normal sync fences.
* Argument is struct oneshot_sync_create_fence.
*/
#define ONESHOT_SYNC_IOC_CREATE_FENCE _IOWR(ONESHOT_SYNC_IOC_MAGIC, 1,\
struct oneshot_sync_create_fence)
/**
* DOC: ONESHOT_SYNC_IOC_SIGNAL_FENCE - signal a fence
*
* Signal a fence that was created by a ONESHOT_SYNC_IOC_CREATE_FENCE
* call on the same file descriptor. This allows a fence to be shared
* to other processes but only signaled by the process owning the fd
* used to create the fence. Argument is the fence file descriptor.
*/
#define ONESHOT_SYNC_IOC_SIGNAL_FENCE _IOWR(ONESHOT_SYNC_IOC_MAGIC, 2,\
int)
#endif

View file

@ -350,6 +350,7 @@ header-y += nvme.h
header-y += nvram.h
header-y += omap3isp.h
header-y += omapfb.h
header-y += oneshot_sync.h
header-y += oom.h
header-y += openvswitch.h
header-y += packet_diag.h

View file

@ -0,0 +1 @@
../../../drivers/staging/android/uapi/oneshot_sync.h