blob: 639288c106b7269e13100e0f3a0b0ab93d317864 [file] [log] [blame]
#
# (C) COPYRIGHT 2012-2013 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.
===============================================
Kernel driver kds client design considerations
===============================================
Number of resources
--------------------
The kds api allows a client to wait for ownership of a number of resources, where by the client does not take on ownership of any of the resources in the resource set
until all of the resources in the set are released. Consideration must be made with respect to performance, as waiting on large number of resources will incur
a greater overhead and may increase system latency. It may be worth considering how independent each of the resources are, for example if the same set of resources
are waited upon by each of the clients, then it may be possible to aggregate these into one resource that each client waits upon.
Clients with shared access
---------------------------
The kds api allows a number of clients to gain shared access to a resource simultaneously, consideration must be made with respect to performance, large numbers of clients
wanting shared access can incur a performance penalty and may increase system latency, specifically when the clients are granted access. Having an excessively high
number of clients with shared access should be avoided, consideration should be made to the call back configuration being used. See Callbacks and Scenario 1 below.
Callbacks
----------
Careful consideration must be made as to which callback type is most appropriate for kds clients, direct callbacks are called immediately from the context in which the
ownership of the resource is passed to the next waiter in the list. Where as when using deferred callbacks the callback is deferred and called from outside the context
that is relinquishing ownership, while this reduces the latency in the releasing clients context it does incur a cost as there is more latency between a resource
becoming free and the new client owning the resource callback being executed.
Obviously direct callbacks have a performance advantage, as the call back is immediate and does not have to wait for the kernel to context switch to schedule in the
execution of the callback.
However as the callback is immediate and within the context that is granting ownership it is important that the callback perform the MINIMUM amount of work necessary,
long call backs could cause poor system latency. Special care and attention must be taken if the direct callbacks can be called from IRQ handlers, such as when
kds_resource_set_release is called from an IRQ handler, in this case you have to avoid any calls that may sleep.
Deferred contexts have the advantage that the call backs are deferred until they are scheduled by the kernel, therefore they are allowed to call functions that may sleep
and if scheduled from IRQ context not incur as much system latency as would be seen with direct callbacks from within the IRQ.
Once the clients callback has been called, the client is considered to be owning the resource. Within the callback the client may only need to perform a small amount of work
before the client need to give up owner ship. The kds_resource_release function may be called from with in the call back, but consideration must be made when using direct
callbacks, with both respect to execution time and stack usage. Consider the example in Scenario 2 with direct callbacks:
Scenario 1 - Shared client access - direct callbacks:
Resources: X
Clients: A(S), B(S), C(S), D(S), E(E)
where: (S) = shared user, (E) = exclusive
Clients kds callback handler:
client_<client>_cb( p1, p2 )
{
}
Where <client> is either A,B,C,D
Queue |Owner
1. E Requests X exclusive |
2. E Owns X exclusive |E
3. A Requests X shared A|E
4. B Requests X shared BA|E
5. C Requests X shared CBA|E
6. D Requests X shared DCBA|E
7. E Releases X |DCBA
8. A Owns X shared |DCBA
9. B Owns X shared |DCBA
10. C Owns X shared |DCBA
11. D Owns X shared |DCBA
At point 7 it is important to note that when E releases X; A,B,C and D become the new shared owners of X and the call back for each of the client(A,B,C,D) triggered, so consideration
must be made as to whether a direct or deferred callback is suitable, using direct callbacks would result in the call graph.
Call graph when E releases X:
kds_resource_set_release( .. )
+->client_A_cb( .. )
+->client_B_cb( .. )
+->client_C_cb( .. )
+->client_D_cb( .. )
Scenario 2 - Immediate resource release - direct callbacks:
Resource: X
Clients: A, B, C, D
Clients kds callback handler:
client_<client>_cb( p1, p2 )
{
kds_resource_set_release( .. );
}
Where <client> is either A,B,C,D
1. A Owns X exclusive
2. B Requests X exclusive (direct callback)
3. C Requests X exclusive (direct callback)
4. D Requests X exclusive (direct callback)
5. A Releases X
Call graph when A releases X:
kds_resource_set_release( .. )
+->client_B_cb( .. )
+->kds_resource_set_release( .. )
+->client_C_cb( .. )
+->kds_resource_set_release( .. )
+->client_D_cb( .. )
As can be seen when a client releases the resource, with direct call backs it is possible to create nested calls
IRQ Considerations
-------------------
Usage of kds_resource_release in IRQ handlers should be carefully considered.
Things to keep in mind:
1.) Are you using direct or deferred callbacks?
2.) How many resources are you releasing?
3.) How many shared waiters are pending on the resource?
Releasing ownership and wait cancellation
------------------------------------------
Client Wait Cancellation
-------------------------
It may be necessary in certain circumstances for the client to cancel the wait for kds resources for error handling, process termination etc. Cancellation is
performed using kds_resource_set_release or kds_resource_set_release_sync using the rset that was received from kds_async_waitall, kds_resource_set_release_sync
being used for waits which are using deferred callbacks.
It is possible that while the request to cancel the wait is being issued by the client, the client is granted access to the resources. Normally after the client
has taken ownership and finishes with that resource, it will release ownership to signal other waiters which are pending, this causes a race with the cancellation.
To prevent KDS trying to remove a wait twice from the internal list and accessing memory that is potentially freed, it is very important that all releasers use the
same rset pointer. Here is a simplified example of bad usage that must be avoided in any client implementation:
Senario 3 - Bad release from multiple contexts:
This scenaro is highlighting bad usage of the kds API
kds_resource_set * rset;
kds_resource_set * rset_copy;
kds_async_waitall( &rset, ... ... ... );
/* Don't do this */
rset_copy = rset;
Context A:
kds_resource_set_release( &rset )
Context B:
kds_resource_set_release( &rset_copy )