new file mode 100644
@@ -0,0 +1,113 @@
+#
+# (C) COPYRIGHT 2012 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of
the GNU General Public License version 2
+# as published by the Free Software Foundation, and any use by you of this
program is subject to the terms of such GNU licence.
+#
+# A copy of the licence is included with the program, and can also be
obtained from Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
+#
+#
+
+
+==============================
+kds - Kernel Dependency System
+==============================
+
+Introduction
+------------
+kds provides a mechanism for clients to atomically lock down multiple
abstract resources.
+This can be done either synchronously or asynchronously.
+Abstract resources is used to allow a set of clients to use kds to control
access to any
+resource, an example is structured memory buffers.
+
+kds supports that buffer is locked for exclusive access and sharing of
buffers.
+
+kds can be built as either a integrated feature of the kernel or as a
module.
+It supports being compiled as a module both in-tree and out-of-tree.
+
+
+Concepts
+--------
+A core concept in kds is abstract resources.
+A kds resource is just an abstraction for some client object, kds doesn't
care what it is.
+Typically EGL will consider UMP buffers as being a resource, thus each UMP
buffer has
+a kds resource for synchronization to the buffer.
+
+kds allows a client to create and destroy the abstract resource objects.
+A new resource object is made available asap (it is just a simple malloc
with some initializations),
+while destroy it requires some external synchronization.
+
+The other core concept in kds is consumer of resources.
+kds is requested to allow a client to consume a set of resources and the
client will be notified when it can consume the resources.
+
+Exclusive access allows only one client to consume a resource.
+Shared access permits multiple consumers to acceess a resource
concurrently.
+
+
+APIs
+----
+kds provides simple resource allocate and destroy functions.
+Clients use this to instantiate and control the lifetime of the resources
kds manages.
+
+kds provides two ways to wait for resources:
+- Asynchronous wait: the client specifies a function pointer to be called
when wait is over
+- Synchronous wait: Function blocks until access is gained.
+
+The synchronous API has a timeout for the wait.
+The call can early out if a signal is delivered.
+
+After a client is done consuming the resource kds must be notified to
release the resources and let some other client take ownership.
+This is done via resource set release call.
+
+A Windows comparison:
+kds implements WaitForMultipleObjectsEx(..., bWaitAll = TRUE, ...) but also
has an asynchronous version in addition.
+kds resources can be seen as being the same as NT object manager resources.
+
+Internals
+---------
+kds guarantees atomicity when a set of resources is operated on.
+This is implemented via a global resource lock which is taken by kds when
it updates resource objects.
+
+Internally a resource in kds is a linked list head with some flags.
+
+When a consumer requests access to a set of resources it is queued on each
of the resources.
+The link from the consumer to the resources can be triggered. Once all
links are triggered
+the registered callback is called or the blocking function returns.
+A link is considered triggered if it is the first on the list of consumers
of a resource,
+or if all the links ahead of it is marked as shared and itself is of the
type shared.
+
+When the client is done consuming the consumer object is removed from the
linked lists of
+the resources and a potential new consumer becomes the head of the
resources.
+As we add and remove consumers atomically across all resources we can
guarantee that
+we never introduces a A->B + B->A type of loops/deadlocks.
+
+
+kbase/base implementation
+-------------------------
+A HW job needs access to a set of shared resources.
+EGL tracks this and encodes the set along with the atom in the ringbuffer.
+EGL allocates a (k)base dep object to represent the dependency to the set
of resources and encodes that along with the list of resources.
+This dep object is use to create a dependency from a job chain(atom) to the
resources it needs to run.
+When kbase decodes the atom in the ringbuffer it finds the set of resources
and calls kds to request all the needed resources.
+As EGL needs to know when the kds request is delivered a new base event
object is needed: atom enqueued. This event is only delivered for atoms
which uses kds.
+The callback kbase registers trigger the dependency object described which
would trigger the existing JD system to release the job chain.
+When the atom is done kds resource set release is call to release the
resources.
+
+EGL will typically use exclusive access to the render target, while all
buffers used as input can be marked as shared.
+
+
+Buffer publish/vsync
+--------------------
+EGL will use a separate ioctl or DRM flip to request the flip.
+If the LCD driver is integrated with kds EGL can do these operations early.
+The LCD driver must then implement the ioctl or DRM flip to be asynchronous
with kds async call.
+The LCD driver binds a kds resource to each virtual buffer (2 buffers in
case of double-buffering).
+EGL will make a dependency to the target kds resource in the kbase atom.
+After EGL receives a atom enqueued event it can ask the LCD driver to pan
to the target kds resource.
+When the atom is completed it'll release the resource and the LCD driver
will get its callback.
+In the callback it'll load the target buffer into the DMA unit of the LCD
hardware.
+The LCD driver will be the consumer of both buffers for a short period.
+The LCD driver will call kds resource set release on the previous on-screen
buffer when the next vsync/dma read end is handled.
+
+
new file mode 100644
@@ -0,0 +1,461 @@
+/*
+ *
+ * (C) COPYRIGHT 2012 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of
the GNU General Public License version 2
+ * as published by the Free Software Foundation, and any use by you of this
program is subject to the terms of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be
obtained from Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
+ *
+ */
+
+
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/kds.h>
+
+
+#define KDS_LINK_TRIGGERED (1u << 0)
+#define KDS_LINK_EXCLUSIVE (1u << 1)
+
+#define KDS_IGNORED NULL
+#define KDS_INVALID (void*)-2
+#define KDS_RESOURCE (void*)-1
+
+struct kds_resource_set
+{
+ unsigned long num_resources;
+ unsigned long pending;
+ unsigned long locked_resources;
+ struct kds_callback * cb;
+ void * callback_parameter;
+ void * callback_extra_parameter;
+ struct list_head callback_link;
+ struct work_struct callback_work;
+ struct kds_link resources[0];
+};
+
+static DEFINE_MUTEX(kds_lock);
+
+int kds_callback_init(struct kds_callback * cb, int direct, kds_callback_fn
user_cb)
+{
+ int ret = 0;
+
+ cb->direct = direct;
+ cb->user_cb = user_cb;
+
+ if (!direct)
+ {
+ cb->wq = alloc_workqueue("kds", WQ_UNBOUND | WQ_HIGHPRI,
WQ_UNBOUND_MAX_ACTIVE);
+ if (!cb->wq)
+ ret = -ENOMEM;
+ }
+ else
+ {
+ cb->wq = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(kds_callback_init);
+
+void kds_callback_term(struct kds_callback * cb)
+{
+ if (!cb->direct)
+ {
+ BUG_ON(!cb->wq);
+ destroy_workqueue(cb->wq);
+ }
+ else
+ {
+ BUG_ON(cb->wq);
+ }
+}
+
+EXPORT_SYMBOL(kds_callback_term);
+
+static void kds_do_user_callback(struct kds_resource_set * rset)
+{
+ rset->cb->user_cb(rset->callback_parameter,
rset->callback_extra_parameter);
+}
+
+static void kds_queued_callback(struct work_struct * work)
+{
+ struct kds_resource_set * rset;
+ rset = container_of( work, struct kds_resource_set, callback_work);
+
+ kds_do_user_callback(rset);
+}
+
+static void kds_callback_perform(struct kds_resource_set * rset)
+{
+ if (rset->cb->direct)
+ kds_do_user_callback(rset);
+ else
+ {
+ int result;
+ result = queue_work(rset->cb->wq, &rset->callback_work);
+ /* if we got a 0 return it means we've triggered the same
rset twice! */
+ BUG_ON(!result);
+ }
+}
+
+void kds_resource_init(struct kds_resource * res)
+{
+ BUG_ON(!res);
+ INIT_LIST_HEAD(&res->waiters.link);
+ res->waiters.parent = KDS_RESOURCE;
+}
+EXPORT_SYMBOL(kds_resource_init);
+
+void kds_resource_term(struct kds_resource * res)
+{
+ BUG_ON(!res);
+ BUG_ON(!list_empty(&res->waiters.link));
+ res->waiters.parent = KDS_INVALID;
+}
+EXPORT_SYMBOL(kds_resource_term);
+
+int kds_async_waitall(
+ struct kds_resource_set ** pprset,
+ unsigned long flags,
+ struct kds_callback * cb,
+ void * callback_parameter,
+ void * callback_extra_parameter,
+ int number_resources,
+ unsigned long * exclusive_access_bitmap,
+ struct kds_resource ** resource_list)
+{
+ struct kds_resource_set * rset = NULL;
+ int i;
+ int triggered;
+ int err = -EFAULT;
+
+ BUG_ON(!pprset);
+ BUG_ON(!resource_list);
+ BUG_ON(!cb);
+
+ mutex_lock(&kds_lock);
+
+ if ((flags & KDS_FLAG_LOCKED_ACTION) == KDS_FLAG_LOCKED_FAIL)
+ {
+ for (i = 0; i < number_resources; i++)
+ {
+ if (resource_list[i]->lock_count)
+ {
+ err = -EBUSY;
+ goto errout;
+ }
+ }
+ }
+
+ rset = kmalloc(sizeof(*rset) + number_resources * sizeof(struct
kds_link), GFP_KERNEL);
+ if (!rset)
+ {
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ rset->num_resources = number_resources;
+ rset->pending = number_resources;
+ rset->locked_resources = 0;
+ rset->cb = cb;
+ rset->callback_parameter = callback_parameter;
+ rset->callback_extra_parameter = callback_extra_parameter;
+ INIT_LIST_HEAD(&rset->callback_link);
+ INIT_WORK(&rset->callback_work, kds_queued_callback);
+
+ for (i = 0; i < number_resources; i++)
+ {
+ unsigned long link_state = 0;
+
+ INIT_LIST_HEAD(&rset->resources[i].link);
+ rset->resources[i].parent = rset;
+
+ if (test_bit(i, exclusive_access_bitmap))
+ {
+ link_state |= KDS_LINK_EXCLUSIVE;
+ }
+
+ /* no-one else waiting? */
+ if (list_empty(&resource_list[i]->waiters.link))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+ /* Adding a non-exclusive and the current tail is a
triggered non-exclusive? */
+ else if (((link_state & KDS_LINK_EXCLUSIVE) == 0) &&
+ (((list_entry(resource_list[i]->waiters.link.prev,
struct kds_link, link)->state & (KDS_LINK_EXCLUSIVE | KDS_LINK_TRIGGERED))
== KDS_LINK_TRIGGERED)))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+ /* locked & ignore locked? */
+ else if ((resource_list[i]->lock_count) && ((flags &
KDS_FLAG_LOCKED_ACTION) == KDS_FLAG_LOCKED_IGNORE) )
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ rset->resources[i].parent = KDS_IGNORED; /* to
disable decrementing the pending count when we get the ignored resource */
+ }
+ rset->resources[i].state = link_state;
+ list_add_tail(&rset->resources[i].link,
&resource_list[i]->waiters.link);
+ }
+
+ triggered = (rset->pending == 0);
+
+ mutex_unlock(&kds_lock);
+
+ /* set the pointer before the callback is called so it sees it */
+ *pprset = rset;
+
+ if (triggered)
+ {
+ /* all resources obtained, trigger callback */
+ kds_callback_perform(rset);
+ }
+
+ return 0;
+
+errout:
+ mutex_unlock(&kds_lock);
+ return err;
+}
+EXPORT_SYMBOL(kds_async_waitall);
+
+static void wake_up_sync_call(void * callback_parameter, void *
callback_extra_parameter)
+{
+ wait_queue_head_t * wait = (wait_queue_head_t*)callback_parameter;
+ wake_up(wait);
+}
+
+static struct kds_callback sync_cb =
+{
+ wake_up_sync_call,
+ 1,
+ NULL,
+};
+
+struct kds_resource_set * kds_waitall(
+ int number_resources,
+ unsigned long * exclusive_access_bitmap,
+ struct kds_resource ** resource_list,
+ unsigned long jiffies_timeout)
+{
+ struct kds_resource_set * rset;
+ int i;
+ int triggered = 0;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
+
+ rset = kmalloc(sizeof(*rset) + number_resources * sizeof(struct
kds_link), GFP_KERNEL);
+ if (!rset)
+ return rset;
+
+ rset->num_resources = number_resources;
+ rset->pending = number_resources;
+ rset->locked_resources = 1;
+ INIT_LIST_HEAD(&rset->callback_link);
+ INIT_WORK(&rset->callback_work, kds_queued_callback);
+
+ mutex_lock(&kds_lock);
+
+ for (i = 0; i < number_resources; i++)
+ {
+ unsigned long link_state = 0;
+
+ if (likely(resource_list[i]->lock_count < ULONG_MAX))
+ resource_list[i]->lock_count++;
+ else
+ break;
+
+ if (test_bit(i, exclusive_access_bitmap))
+ {
+ link_state |= KDS_LINK_EXCLUSIVE;
+ }
+
+ if (list_empty(&resource_list[i]->waiters.link))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+ /* Adding a non-exclusive and the current tail is a
triggered non-exclusive? */
+ else if (((link_state & KDS_LINK_EXCLUSIVE) == 0) &&
+ (((list_entry(resource_list[i]->waiters.link.prev,
struct kds_link, link)->state & (KDS_LINK_EXCLUSIVE | KDS_LINK_TRIGGERED))
== KDS_LINK_TRIGGERED)))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+
+ INIT_LIST_HEAD(&rset->resources[i].link);
+ rset->resources[i].parent = rset;
+ rset->resources[i].state = link_state;
+ list_add_tail(&rset->resources[i].link,
&resource_list[i]->waiters.link);
+ }
+
+ if (i < number_resources)
+ {
+ /* an overflow was detected, roll back */
+ while (i--)
+ {
+ list_del(&rset->resources[i].link);
+ resource_list[i]->lock_count--;
+ }
+ mutex_unlock(&kds_lock);
+ kfree(rset);
+ return ERR_PTR(-EFAULT);
+ }
+
+ if (rset->pending == 0)
+ triggered = 1;
+ else
+ {
+ rset->cb = &sync_cb;
+ rset->callback_parameter = &wake;
+ rset->callback_extra_parameter = NULL;
+ }
+
+ mutex_unlock(&kds_lock);
+
+ if (!triggered)
+ {
+ long wait_res;
+ if ( KDS_WAIT_BLOCKING == jiffies_timeout )
+ {
+ wait_res = wait_event_interruptible(wake,
rset->pending == 0);
+ }
+ else
+ {
+ wait_res = wait_event_interruptible_timeout(wake,
rset->pending == 0, jiffies_timeout);
+ }
+ if ((wait_res == -ERESTARTSYS) || (wait_res == 0))
+ {
+ /* use \a kds_resource_set_release to roll back */
+ kds_resource_set_release(&rset);
+ return ERR_PTR(wait_res);
+ }
+ }
+ return rset;
+}
+EXPORT_SYMBOL(kds_waitall);
+
+void kds_resource_set_release(struct kds_resource_set ** pprset)
+{
+ struct list_head triggered = LIST_HEAD_INIT(triggered);
+ struct kds_resource_set * rset;
+ struct kds_resource_set * it;
+ int i;
+
+ BUG_ON(!pprset);
+
+ mutex_lock(&kds_lock);
+
+ rset = *pprset;
+ if (!rset)
+ {
+ /* caught a race between a cancelation
+ * and a completion, nothing to do */
+ mutex_unlock(&kds_lock);
+ return;
+ }
+
+ /* clear user pointer so we'll be the only
+ * thread handling the release */
+ *pprset = NULL;
+
+ for (i = 0; i < rset->num_resources; i++)
+ {
+ struct kds_resource * resource;
+ struct kds_link * it = NULL;
+
+ /* fetch the previous entry on the linked list */
+ it = list_entry(rset->resources[i].link.prev, struct
kds_link, link);
+ /* unlink ourself */
+ list_del(&rset->resources[i].link);
+
+ /* any waiters? */
+ if (list_empty(&it->link))
+ continue;
+
+ /* were we the head of the list? (head if prev is a
resource) */
+ if (it->parent != KDS_RESOURCE)
+ continue;
+
+ /* we were the head, find the kds_resource */
+ resource = container_of(it, struct kds_resource, waiters);
+
+ if (rset->locked_resources)
+ {
+ resource->lock_count--;
+ }
+
+ /* we know there is someone waiting from the any-waiters
test above */
+
+ /* find the head of the waiting list */
+ it = list_first_entry(&resource->waiters.link, struct
kds_link, link);
+
+ /* new exclusive owner? */
+ if (it->state & KDS_LINK_EXCLUSIVE)
+ {
+ /* link now triggered */
+ it->state |= KDS_LINK_TRIGGERED;
+ /* a parent to update? */
+ if (it->parent != KDS_IGNORED)
+ {
+ if (0 == --it->parent->pending)
+ {
+ /* new owner now triggered, track
for callback later */
+ list_add(&it->parent->callback_link,
&triggered);
+ }
+ }
+ }
+ /* exclusive releasing ? */
+ else if (rset->resources[i].state & KDS_LINK_EXCLUSIVE)
+ {
+ /* trigger non-exclusive until end-of-list or first
exclusive */
+ list_for_each_entry(it, &resource->waiters.link,
link)
+ {
+ /* exclusive found, stop triggering */
+ if (it->state & KDS_LINK_EXCLUSIVE)
+ break;
+
+ it->state |= KDS_LINK_TRIGGERED;
+ /* a parent to update? */
+ if (it->parent != KDS_IGNORED)
+ {
+ if (0 == --it->parent->pending)
+ {
+ /* new owner now triggered,
track for callback later */
+
list_add(&it->parent->callback_link, &triggered);
+ }
+ }
+ }
+ }
+
+ }
+
+ mutex_unlock(&kds_lock);
+
+ while (!list_empty(&triggered))
+ {
+ it = list_first_entry(&triggered, struct kds_resource_set,
callback_link);
+ list_del(&it->callback_link);
+ kds_callback_perform(it);
+ }
+
+ cancel_work_sync(&rset->callback_work);
+
+ /* free the resource set */
+ kfree(rset);
+}
+EXPORT_SYMBOL(kds_resource_set_release);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_VERSION("1.0");
new file mode 100644
@@ -0,0 +1,154 @@
+/*
+ *
+ * (C) COPYRIGHT 2012 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of
the GNU General Public License version 2
+ * as published by the Free Software Foundation, and any use by you of this
program is subject to the terms of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be
obtained from Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
+ *
+ */
+
+
+
+#ifndef _KDS_H_
+#define _KDS_H_
+
+#include <linux/list.h>
+#include <linux/workqueue.h>
+
+#define KDS_WAIT_BLOCKING (ULONG_MAX)
+
+/* what to do when waitall must wait for a synchronous locked resource: */
+#define KDS_FLAG_LOCKED_FAIL (0u << 0) /* fail waitall */
+#define KDS_FLAG_LOCKED_IGNORE (1u << 0) /* don't wait, but block other
that waits */
+#define KDS_FLAG_LOCKED_WAIT (2u << 0) /* wait (normal */
+#define KDS_FLAG_LOCKED_ACTION (3u << 0) /* mask to extract the action to
do on locked resources */
+
+struct kds_resource_set;
+
+typedef void (*kds_callback_fn) (void * callback_parameter, void *
callback_extra_parameter);
+
+struct kds_callback
+{
+ kds_callback_fn user_cb; /* real cb */
+ int direct; /* do direct or queued call? */
+ struct workqueue_struct * wq;
+};
+
+struct kds_link
+{
+ struct kds_resource_set * parent;
+ struct list_head link;
+ unsigned long state;
+};
+
+struct kds_resource
+{
+ struct kds_link waiters;
+ unsigned long lock_count;
+};
+
+/* callback API */
+
+/* Initialize a callback object.
+ *
+ * Typically created per context or per hw resource.
+ *
+ * Callbacks can be performed directly if no nested locking can
+ * happen in the client.
+ *
+ * Nested locking can occur when a lock is held during the
kds_async_waitall or
+ * kds_resource_set_release call. If the callback needs to take the same
lock
+ * nested locking will happen.
+ *
+ * If nested locking could happen non-direct callbacks can be requested.
+ * Callbacks will then be called asynchronous to the triggering call.
+ */
+int kds_callback_init(struct kds_callback * cb, int direct, kds_callback_fn
user_cb);
+
+/* Terminate the use of a callback object.
+ *
+ * If the callback object was set up as non-direct
+ * any pending callbacks will be flushed first.
+ * Note that to avoid a deadlock the lock callbacks needs
+ * can't be held when a callback object is terminated.
+ */
+void kds_callback_term(struct kds_callback * cb);
+
+
+/* resource object API */
+
+/* initialize a resource handle for a shared resource */
+void kds_resource_init(struct kds_resource * resource);
+
+/*
+ * Will assert if the resource is being used or waited on.
+ * The caller should NOT try to terminate a resource that could still have
clients.
+ * After the function returns the resource is no longer known by kds.
+ */
+void kds_resource_term(struct kds_resource * resource);
+
+/* Asynchronous wait for a set of resources.
+ * Callback will be called when all resources are available.
+ * If all the resources was available the callback will be called before
kds_async_waitall returns.
+ * So one must not hold any locks the callback code-flow can take when
calling kds_async_waitall.
+ * Caller considered to own/use the resources until \a kds_rset_release is
called.
+ * flags is one or more of the KDS_FLAG_* set.
+ * exclusive_access_bitmap is a bitmap where a high bit means exclusive
access while a low bit means shared access.
+ * Use the Linux __set_bit API, where the index of the buffer to control is
used as the bit index.
+ *
+ * Standard Linux error return value.
+ */
+int kds_async_waitall(
+ struct kds_resource_set ** pprset,
+ unsigned long flags,
+ struct kds_callback * cb,
+ void * callback_parameter,
+ void * callback_extra_parameter,
+ int number_resources,
+ unsigned long * exclusive_access_bitmap,
+ struct kds_resource ** resource_list);
+
+/* Synchronous wait for a set of resources.
+ * Function will return when one of these have happened:
+ * - all resources have been obtained
+ * - timeout lapsed while waiting
+ * - a signal was received while waiting
+ *
+ * Caller considered to own/use the resources when the function returns.
+ * Caller must release the resources using \a kds_rset_release.
+ *
+ * Calling this function while holding already locked resources or other
locking primitives is dangerous.
+ * One must if this is needed decide on a lock order of the resources
and/or the other locking primitives
+ * and always take the resources/locking primitives in the specific order.
+ *
+ * Use the ERR_PTR framework to decode the return value.
+ * NULL = time out
+ * If IS_ERR then PTR_ERR gives:
+ * ERESTARTSYS = signal received, retry call after signal
+ * all other values = internal error, lock failed
+ * Other values = successful wait, now the owner, must call
kds_resource_set_release
+ */
+struct kds_resource_set * kds_waitall(
+ int number_resources,