diff options
Diffstat (limited to 'src/lib/ppc405lib/sxlock.c')
-rw-r--r-- | src/lib/ppc405lib/sxlock.c | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/src/lib/ppc405lib/sxlock.c b/src/lib/ppc405lib/sxlock.c new file mode 100644 index 0000000..35f9698 --- /dev/null +++ b/src/lib/ppc405lib/sxlock.c @@ -0,0 +1,494 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/sxlock.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file sxlock.c +/// \brief API for the SharedExclusiveLock +/// +/// The APIs in this file implement a shared-exclusive lock for SSX +/// applications. This type of lock is also called a readers-writer lock. The +/// lock is implemented in terms of SSX semaphores, so its use is limited to +/// threads willing to block for access to a resource. +/// +/// The SharedExclusiveLock allows multiple threads shared access to a +/// resource, while limiting exclusive access to a single thread. There are +/// several ways that this type of lock might be specified. The specification +/// implemented here is an "exclusive-biasing" lock. As long as the lock is +/// held or requested in exclusive mode, all new shared-mode requests will +/// block, and only exclusive accesses will be allowed. If multiple threads +/// are blocked exclusive the requests are honored in priority order (as +/// the underlying implementation is an SSX semaphore). Once the exclusive +/// lock is cleared, any/all threads blocked for shared access are released +/// simultaneously (using an SSX semaphore as a thread barrier). +/// +/// The lock is created (initialized) by sxlock_create(), which allows +/// specification of an initial value of the number of shared or exclusive +/// accesses outstanding. The lock/unlock APIs are as follows: +/// +/// - sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout) +/// - sxlock_unlock_shared(SharedExclusiveLock* sxlock) +/// - sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout) +/// - sxlock_unlock_exclusive(SharedExclusiveLock* sxlock) +/// +/// Threads must always issue *_lock() and *_unlock() requests in matched +/// pairs in order to avoid errors and deadlock. The *_lock() APIs allow +/// specification of a timeout, which may be SSX_WAIT_FOREVER to indicate no +/// timeout. The *_lock() APIs will return the code -SXLOCK_TIMED_OUT if the +/// timeout occurs before the thread acquires the resource. If called from an +/// interrupt context then the only legal timeout specification is +/// SSX_NO_WAIT (0). +/// +/// If a *_lock() request times out then the thread \e has \e not acquired the +/// resource and \e must \e not call *_unlock(). As with semaphores there is +/// no record that a thread holds a lock, so if a thread completes or is +/// deleted while holding a lock it is likely that the application will +/// deadlock. + +#include "ssx.h" +#include "sxlock.h" + +/// Create (initialize) a SharedExclusiveLock +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to +/// initialize +/// +/// \param shared The initial numbers of shared accesses +/// +/// \param exclusive The initial numbers of exclusive accesses +/// +/// Create (initialize) a SharedExclusiveLock and optionally specify an +/// initial state. The initial number of shared or exclusive accesses can be +/// specified, however at most one of \a nshared and \a exclusive can be +/// non-0. If \a shared or \a exclusive are non-0 then eventually a +/// thread(s) will need to issue unmatched *_unlock() call(s) to allow +/// progress for other threads requiring the resource. +/// +/// \retval 0 Success +/// +/// \retval SXLOCK_INVALID_OBJECT The \a sxlock parameter is NULL (0) or +/// otherwise invalid. +/// +/// \retval SXLOCK_INVALID_ARGUMENT Both of the \a shared and \a exclusive +/// parameters are non-0. +/// +/// \retval others sxlock_create() may also return codes from +/// ssx_semaphore_create(), which would indicate a serious bug. + +int +sxlock_create(SharedExclusiveLock* sxlock, + SsxSemaphoreCount shared, + SsxSemaphoreCount exclusive) +{ + int rc; + + rc = 0; + do { + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, + SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF((shared != 0) && (exclusive != 0), + SXLOCK_INVALID_ARGUMENT); + } + + rc = ssx_semaphore_create(&(sxlock->shared_sem), 0, 0); + if (rc) break; + + rc = ssx_semaphore_create(&(sxlock->exclusive_sem), 0, 0); + if (rc) break; + + sxlock->running_shared = shared; + sxlock->running_exclusive = exclusive; + + } while (0); + + return rc; +} + + +/// Lock a SharedExclusiveLock for shared access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to lock +/// +/// \param timeout The maximum amount of time to wait for access, or the +/// constant SSX_WAIT_FOREVER to wait forever. +/// +/// Acquire a SharedExclusiveLock for shared access, potentially blocking +/// forever or until a specified timeout if access is not immediately +/// granted. Access will be blocked as long as one or more threads request or +/// control exclusive acesss to the resource. Once the access is granted, the +/// thread maintains shared access to the resource until a subsequent call of +/// sxlock_unlock_shared(). +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SXLOCK_TIMED_OUT Shared access was not acquired before the +/// timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), shared access was +/// not immediately available and a non-zero timeout was specified. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + + +int +sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout) +{ + SsxMachineContext ctx; + int rc, pending_exclusive; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + rc = 0; + do { + + // NB: This is the only way to correctly compute the number of threads + // pending exclusive, given that threads could be removed from the + // exclusive_sem by timeout. + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if ((sxlock->running_exclusive == 0) && (pending_exclusive == 0)) { + + // If no other thread has or is requesting exclusive access, the + // current thread gets immediate access. + + sxlock->running_shared++; + + } else { + + // If threads are running or pending exclusive, this thread must + // pend shared. The thread will be unblocked by an exclusive + // unlock, which is responsible for adjusting + // sxlock->running_shared in this case. + + rc = ssx_semaphore_pend(&(sxlock->shared_sem), timeout); + if (rc == -SSX_SEMAPHORE_PEND_TIMED_OUT) { + rc = -SXLOCK_TIMED_OUT; + } + } + + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Relase a SharedExclusiveLock from shared access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to unlock +/// +/// Release a SharedExclusiveLock from shared access, signalling that the +/// thread no longer requires or expects shared access to the resource. It is +/// an error for a thread to use this API if it has not previously locked +/// shared access by a call of sxlock_pend_shared() (or the thread is +/// unlocking a lock initialized in the shared-locked state). +/// +/// Return values other than SSX_OK (0) are errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SXLOCK_SHARED_UNDERFLOW There was apparently no matched call of +/// sxlock_lock_shared() prior to this call. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + +int +sxlock_unlock_shared(SharedExclusiveLock* sxlock) +{ + SsxMachineContext ctx; + int rc, pending_exclusive; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF(sxlock->running_shared == 0, SXLOCK_SHARED_UNDERFLOW); + } + + if (SSX_ERROR_CHECK_KERNEL) { + SSX_PANIC_IF(sxlock->running_exclusive != 0, + SXLOCK_SHARED_EXCLUSIVE_INVARIANT); + } + + rc = 0; + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + do { + + // If this is the last shared thread running, and a thread wants + // exclusive access, grant it. Note that there may be shared requests + // pending on the shared_sem but we always give preference to + // exclusive requests. + + sxlock->running_shared--; + if (sxlock->running_shared == 0) { + + // Wake any single thread pending exclusive + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive != 0) { + + sxlock->running_exclusive = 1; + rc = ssx_semaphore_post(&(sxlock->exclusive_sem)); + if (rc) break; + } + } + + } while(0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Lock a SharedExclusiveLock for exclusive access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to lock +/// +/// \param timeout The maximum amount of time to wait for access, or the +/// constant SSX_WAIT_FOREVER to wait forever. +/// +/// Acquire a SharedExclusiveLock for exclusive access, potentially blocking +/// forever or until a specified timeout if access is not immediately +/// granted. Access will be blocked as long as one or more threads control +/// shared acesss to the resource, however once the thread requests exclusive +/// access all new shared access requests will block. Once the access is +/// granted, the thread maintains exclusive access to the resource until a +/// subsequent call of sxlock_unlock_exclusive(). +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SXLOCK_TIMED_OUT Exclusive access was not acquired before the +/// timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), exclusive access +/// was not immediately available and a non-zero timeout was specified. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which shoudl be considered as non-recoverable errors. + + +int +sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout) +{ + SsxMachineContext ctx; + int rc, pending_exclusive, pending_shared; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + rc = 0; + do { + + if ((sxlock->running_shared == 0) && + (sxlock->running_exclusive == 0)) { + + // If no other thread has acquired the lock, this thread gets + // immediate access. + + sxlock->running_exclusive = 1; + + } else { + + // Some other thread has acquired the lock. This thread must pend + // exclusive. In this case the sxlock->running_exclusive must be + // set by the *_unlock() operation that unblocks the thread. + + rc = ssx_semaphore_pend(&(sxlock->exclusive_sem), timeout); + if (rc == -SSX_SEMAPHORE_PEND_TIMED_OUT) { + + // This exclusive request timed out. Since the request may + // have blocked shared requests, then if this is the only + // exclusive request or thread we need to unblock any pending + // shared requests. + + if (sxlock->running_exclusive == 0) { + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive == 0) { + + rc = ssx_semaphore_info_get(&(sxlock->shared_sem), + 0, &pending_shared); + if (rc) break; + + if (pending_shared != 0) { + + sxlock->running_shared += pending_shared; + rc = ssx_semaphore_release_all(&(sxlock->shared_sem)); + if (rc) break; + } + } + } + + rc = -SXLOCK_TIMED_OUT; + } + } + + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Release a SharedExclusiveLock from exclusive access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to unlock +/// +/// Release a SharedExclusiveLock from exclusive access, signalling that the +/// thread no longer requires or expects exclusive access to the resource. It +/// is an error for a thread to use this API if it has not previously locked +/// exclusive access by a call of sxlock_lock_exclusive() (or the thread is +/// unlocking a lock initialized in the exclusive-locked state). +/// +/// Return values other than SSX_OK (0) are errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SXLOCK_EXCLUSIVE_UNDERFLOW There was apparently no matched call of +/// sxlock_lock_exclusive() prior to this call. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + +int +sxlock_unlock_exclusive(SharedExclusiveLock* sxlock) +{ + SsxMachineContext ctx; + int rc, pending_exclusive, pending_shared; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF(sxlock->running_exclusive != 1, SXLOCK_SHARED_UNDERFLOW); + } + + if (SSX_ERROR_CHECK_KERNEL) { + SSX_PANIC_IF(sxlock->running_shared != 0, + SXLOCK_SHARED_EXCLUSIVE_INVARIANT); + } + + rc = 0; + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + do { + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive != 0) { + + // If there are other threads pending exclusive, make the + // highest-priority one of them + // runnable. sxlock->running_exclusive remains equal to 1. + + rc = ssx_semaphore_post(&(sxlock->exclusive_sem)); + if (rc) break; + + } else { + + // Otherwise unblock any/all threads pending shared + + sxlock->running_exclusive = 0; + + rc = ssx_semaphore_info_get(&(sxlock->shared_sem), + 0, &pending_shared); + if (rc) break; + + if (pending_shared != 0) { + + sxlock->running_shared = pending_shared; + rc = ssx_semaphore_release_all(&(sxlock->shared_sem)); + if (rc) break; + } + } + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} |