The STAge eXclusion lock (aka stax
) serializes in-band vs
out-of-band thread activities for accessing an arbitrary
resource. Such lock can be shared so that multiple threads which run
on the same execution stage may ‘own’ the stax guarding the resource,
excluding any access from the converse stage until the last owner
drops the innermost lock. In other words, at any point in time, the
resource guarded by a stax is either owned by out-of-band threads
exclusively, or by in-band threads exclusively, or by no thread at
all.
A stax is useful for implementing “phased sharing” of a resource between threads which belong to different execution stages, which normally access it during distinct phases of the overall process. For instance, a device (re)configuration phase may involve in-band operations such as DMA channel preparation, virtual memory allocation and page pinning, whilst a device operational phase may involve out-of-band operations exclusively to convey traffic in and out. Both phases should never overlap, but there has to be a way to guarantee this, otherwise things might end with extreme prejudice if they do.
A stax is such a sanity mechanism which guarantees that threads running on different stages can never access the guarded resource concurrently. By design, a stax cannot guarantee bounded low latency to out-of-band threads though: if the application allows threads from both stages to compete for the stax, the out-of-band threads may be delayed until no in-band activity runs in the guarded section.
Sleeping locks like the EVL kernel mutexes cannot be used for synchronizing in-band threads, and conversely regular kernel mutexes cannot be used for synchronizing out-of-band threads. However, since we may assume that there will be no stage concurrency when accessing the resource guarded by a stax, as a consequence we may rely on stage-specific serializers to enforce mutual exclusion among them while they own the stax, without having to care further about threads running on the converse stage. For instance, if the current kernel thread is running out-of-band within a section of code guarded by a stax, it knows beforehand that it will never compete with in-band threads while there, therefore only concurrency with other out-of-band threads remains to be addressed. This solves a tricky issue about sharing the implementation of a common driver between in-band and out-of-band users in a safe way.
Initialize an EVL stax.
A stax descriptor is constructed by evl_init_stax(), which contains ancillary information other calls will need.
Delete an EVL stax. Any thread sleeping on the out-of-band stage for the stax to become available is woken up by this call, receiving a ‘resource removed’ status (-EIDRM).
The descriptor of the stax to be destroyed.
Lock the stax for the current stage. The first thread which is granted access to the stax enables all threads running on the same stage to enter the section concurrently, locking out any thread which runs on the converse stage.
The stax descriptor.
Returns zero on success, otherwise:
if the caller runs in-band on entry:
if the caller runs out-of-band on entry:
-EINTR if the caller was forcibly unblocked while sleeping (e.g. by a call to evl_unblock_thread()).
-EIDRM if the stax was deleted while the caller was sleeping on it. When this status is returned, the stax must be considered stale and should not be accessed anymore.
Attempt to lock the stax. This is a variant of evl_lock_stax()) which does not block the caller if access cannot be immediately granted on entry.
Returns zero on success, otherwise -EAGAIN if the stax was already locked for the converse stage on entry.
Unlock the stax, dropping a lock previously acquired by a successful call to evl_lock_stax()) or evl_trylock_stax()). Once all locks currently held by threads which belong to the same execution stage (i.e. in-band or out-of-band) have been released, the stax becomes available anew for the converse stage to acquire it.
The stax descriptor.
Like most calls in this API, unlocking a stax is sensitive to the current execution stage of the caller (in-band or out-of-band). Do NOT switch stage while holding a stax lock, ever.