Jordan Crouse | 29f66af | 2011-11-17 13:39:20 -0700 | [diff] [blame] | 1 | Introduction |
| 2 | |
| 3 | 'genlock' is an in-kernel API and optional userspace interface for a generic |
| 4 | cross-process locking mechanism. The API is designed for situations where |
| 5 | multiple user space processes and/or kernel drivers need to coordinate access |
| 6 | to a shared resource, such as a graphics buffer. The API was designed with |
| 7 | graphics buffers in mind, but is sufficiently generic to allow it to be |
| 8 | independently used with different types of resources. The chief advantage |
| 9 | of genlock over other cross-process locking mechanisms is that the resources |
| 10 | can be accessed by both userspace and kernel drivers which allows resources |
| 11 | to be locked or unlocked by asynchronous events in the kernel without the |
| 12 | intervention of user space. |
| 13 | |
| 14 | As an example, consider a graphics buffer that is shared between a rendering |
| 15 | application and a compositing window manager. The application renders into a |
| 16 | buffer. That buffer is reused by the compositing window manager as a texture. |
| 17 | To avoid corruption, access to the buffer needs to be restricted so that one |
| 18 | is not drawing on the surface while the other is reading. Locks can be |
| 19 | explicitly added between the rendering stages in the processes, but explicit |
| 20 | locks require that the application wait for rendering and purposely release the |
| 21 | lock. An implicit release triggered by an asynchronous event from the GPU |
| 22 | kernel driver, however, will let execution continue without requiring the |
| 23 | intercession of user space. |
| 24 | |
| 25 | SW Goals |
| 26 | |
| 27 | The genlock API implements exclusive write locks and shared read locks meaning |
| 28 | that there can only be one writer at a time, but multiple readers. Processes |
| 29 | that are unable to acquire a lock can be optionally blocked until the resource |
| 30 | becomes available. |
| 31 | |
| 32 | Locks are shared between processes. Each process will have its own private |
| 33 | instance for a lock known as a handle. Handles can be shared between user |
| 34 | space and kernel space to allow a kernel driver to unlock or lock a buffer |
| 35 | on behalf of a user process. |
| 36 | |
| 37 | Kernel API |
| 38 | |
| 39 | Access to the genlock API can either be via the in-kernel API or via an |
| 40 | optional character device (/dev/genlock). The character device is primarily |
| 41 | to be used for legacy resource sharing APIs that cannot be easily changed. |
| 42 | New resource sharing APIs from this point should implement a scheme specific |
| 43 | wrapper for locking. |
| 44 | |
| 45 | To create or attach to an existing lock, a process or kernel driver must first |
| 46 | create a handle. Each handle is linked to a single lock at any time. An entityi |
| 47 | may have multiple handles, each associated with a different lock. Once a handle |
| 48 | has been created, the owner may create a new lock or attach an existing lock |
| 49 | that has been exported from a different handle. |
| 50 | |
| 51 | Once the handle has a lock attached, the owning process may attempt to lock the |
| 52 | buffer for read or write. Write locks are exclusive, meaning that only one |
| 53 | process may acquire it at any given time. Read locks are shared, meaning that |
| 54 | multiple readers can hold the lock at the same time. Attempts to acquire a read |
| 55 | lock with a writer active or a write lock with one or more readers or writers |
| 56 | active will typically cause the process to block until the lock is acquired. |
| 57 | When the lock is released, all waiting processes will be woken up. Ownership |
| 58 | of the lock is reference counted, meaning that any one owner can "lock" |
| 59 | multiple times. The lock will only be released from the owner when all the |
| 60 | references to the lock are released via unlock. |
| 61 | |
| 62 | The owner of a write lock may atomically convert the lock into a read lock |
| 63 | (which will wake up other processes waiting for a read lock) without first |
| 64 | releasing the lock. The owner would simply issue a new request for a read lock. |
| 65 | However, the owner of a read lock cannot convert it into a write lock in the |
| 66 | same manner. To switch from a read lock to a write lock, the owner must |
| 67 | release the lock and then try to reacquire it. |
| 68 | |
| 69 | These are the in-kernel API calls that drivers can use to create and |
| 70 | manipulate handles and locks. Handles can either be created and managed |
| 71 | completely inside of kernel space, or shared from user space via a file |
| 72 | descriptor. |
| 73 | |
| 74 | * struct genlock_handle *genlock_get_handle(void) |
| 75 | Create a new handle. |
| 76 | |
| 77 | * struct genlock_handle * genlock_get_handle_fd(int fd) |
| 78 | Given a valid file descriptor, return the handle associated with that |
| 79 | descriptor. |
| 80 | |
| 81 | * void genlock_put_handle(struct genlock_handle *) |
| 82 | Release a handle. |
| 83 | |
| 84 | * struct genlock * genlock_create_lock(struct genlock_handle *) |
| 85 | Create a new lock and attach it to the handle. |
| 86 | |
| 87 | * struct genlock * genlock_attach_lock(struct genlock_handle *handle, int fd) |
| 88 | Given a valid file descriptor, get the lock associated with it and attach it to |
| 89 | the handle. |
| 90 | |
| 91 | * void genlock_release_lock(struct genlock_handle *) |
| 92 | Release a lock attached to a handle. |
| 93 | |
| 94 | * int genlock_lock(struct genlock_handle *, int op, int flags, u32 timeout) |
| 95 | Lock or unlock the lock attached to the handle. A zero timeout value will |
| 96 | be treated just like if the GENOCK_NOBLOCK flag is passed; if the lock |
| 97 | can be acquired without blocking then do so otherwise return -EAGAIN. |
| 98 | Function returns -ETIMEDOUT if the timeout expired or 0 if the lock was |
| 99 | acquired. |
| 100 | |
| 101 | * int genlock_wait(struct genloc_handle *, u32 timeout) |
| 102 | Wait for a lock held by the handle to go to the unlocked state. A non-zero |
| 103 | timeout value must be passed. Returns -ETIMEDOUT if the timeout expired or |
| 104 | 0 if the lock is in an unlocked state. |
| 105 | |
| 106 | Character Device |
| 107 | |
| 108 | Opening an instance to the /dev/genlock character device will automatically |
| 109 | create a new handle. All ioctl functions with the exception of NEW and |
| 110 | RELEASE use the following parameter structure: |
| 111 | |
| 112 | struct genlock_lock { |
| 113 | int fd; /* Returned by EXPORT, used by ATTACH */ |
| 114 | int op; /* Used by LOCK */ |
| 115 | int flags; /* used by LOCK */ |
| 116 | u32 timeout; /* Used by LOCK and WAIT */ |
| 117 | } |
| 118 | |
| 119 | *GENLOCK_IOC_NEW |
| 120 | Create a new lock and attaches it to the handle. Returns -EINVAL if the handle |
| 121 | already has a lock attached (use GENLOCK_IOC_RELEASE to remove it). Returns |
| 122 | -ENOMEM if the memory for the lock can not be allocated. No data is passed |
| 123 | from the user for this ioctl. |
| 124 | |
| 125 | *GENLOCK_IOC_EXPORT |
| 126 | Export the currently attached lock to a file descriptor. The file descriptor |
| 127 | is returned in genlock_lock.fd. |
| 128 | |
| 129 | *GENLOCK_IOC_ATTACH |
| 130 | Attach an exported lock file descriptor to the current handle. Return -EINVAL |
| 131 | if the handle already has a lock attached (use GENLOCK_IOC_RELEASE to remove |
| 132 | it). Pass the file descriptor in genlock_lock.fd. |
| 133 | |
| 134 | *GENLOCK_IOC_LOCK |
| 135 | Lock or unlock the attached lock. Pass the desired operation in |
| 136 | genlock_lock.op: |
| 137 | * GENLOCK_WRLOCK - write lock |
| 138 | * GENLOCK_RDLOCK - read lock |
| 139 | * GENLOCK_UNLOCK - unlock an existing lock |
| 140 | |
| 141 | Pass flags in genlock_lock.flags: |
| 142 | * GENLOCK_NOBLOCK - Do not block if the lock is already taken |
| 143 | |
| 144 | Pass a timeout value in milliseconds in genlock_lock.timeout. |
| 145 | genlock_lock.flags and genlock_lock.timeout are not used for UNLOCK. |
| 146 | Returns -EINVAL if no lock is attached, -EAGAIN if the lock is taken and |
| 147 | NOBLOCK is specified or if the timeout value is zero, -ETIMEDOUT if the timeout |
| 148 | expires or 0 if the lock was successful. |
| 149 | |
| 150 | * GENLOCK_IOC_WAIT |
| 151 | Wait for the lock attached to the handle to be released (i.e. goes to unlock). |
| 152 | This is mainly used for a thread that needs to wait for a peer to release a |
| 153 | lock on the same shared handle. A non-zero timeout value in milliseconds is |
| 154 | passed in genlock_lock.timeout. Returns 0 when the lock has been released, |
| 155 | -EINVAL if a zero timeout is passed, or -ETIMEDOUT if the timeout expires. |
| 156 | |
| 157 | * GENLOCK_IOC_RELEASE |
| 158 | Use this to release an existing lock. This is useful if you wish to attach a |
| 159 | different lock to the same handle. You do not need to call this under normal |
| 160 | circumstances; when the handle is closed the reference to the lock is released. |
| 161 | No data is passed from the user for this ioctl. |