diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/common/libcommonfiles.mk | 4 | ||||
-rw-r--r-- | src/lib/common/rand.h | 124 | ||||
-rw-r--r-- | src/lib/common/rand32.c | 384 | ||||
-rw-r--r-- | src/lib/common/sync.c | 294 | ||||
-rw-r--r-- | src/lib/common/sync.h | 167 | ||||
-rw-r--r-- | src/lib/ppc405lib/byte_pool.c | 1442 | ||||
-rw-r--r-- | src/lib/ppc405lib/byte_pool.h | 166 | ||||
-rw-r--r-- | src/lib/ppc405lib/initcall.c | 70 | ||||
-rw-r--r-- | src/lib/ppc405lib/initcall.h | 116 | ||||
-rw-r--r-- | src/lib/ppc405lib/lfsr.c | 50 | ||||
-rw-r--r-- | src/lib/ppc405lib/lfsr.h | 46 | ||||
-rw-r--r-- | src/lib/ppc405lib/libppc405files.mk | 10 | ||||
-rw-r--r-- | src/lib/ppc405lib/mutex.c | 129 | ||||
-rw-r--r-- | src/lib/ppc405lib/mutex.h | 164 | ||||
-rw-r--r-- | src/lib/ppc405lib/periodic_semaphore.c | 114 | ||||
-rw-r--r-- | src/lib/ppc405lib/periodic_semaphore.h | 152 | ||||
-rw-r--r-- | src/lib/ppc405lib/progress.c | 743 | ||||
-rw-r--r-- | src/lib/ppc405lib/progress.h | 177 | ||||
-rw-r--r-- | src/lib/ppc405lib/rtx_stdio.c | 149 | ||||
-rw-r--r-- | src/lib/ppc405lib/rtx_stdio.h | 74 | ||||
-rw-r--r-- | src/lib/ppc405lib/rtx_stdio_addresses.h | 55 | ||||
-rw-r--r-- | src/lib/ppc405lib/sxlock.c | 494 | ||||
-rw-r--r-- | src/lib/ppc405lib/sxlock.h | 108 |
23 files changed, 2 insertions, 5230 deletions
diff --git a/src/lib/common/libcommonfiles.mk b/src/lib/common/libcommonfiles.mk index 9fe7592..4a05fac 100644 --- a/src/lib/common/libcommonfiles.mk +++ b/src/lib/common/libcommonfiles.mk @@ -5,7 +5,7 @@ # # OpenPOWER OnChipController Project # -# Contributors Listed Below - COPYRIGHT 2015 +# Contributors Listed Below - COPYRIGHT 2015,2016 # [+] International Business Machines Corp. # # @@ -44,9 +44,7 @@ C-SOURCES = \ memcpy.c \ memset.c \ - rand32.c \ string.c \ - sync.c S-SOURCES = diff --git a/src/lib/common/rand.h b/src/lib/common/rand.h deleted file mode 100644 index 369b8fd..0000000 --- a/src/lib/common/rand.h +++ /dev/null @@ -1,124 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/common/rand.h $ */ -/* */ -/* 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 */ -#ifndef __RAND_H__ -#define __RAND_H__ - -// $Id$ - -/// \file rand.h -/// \brief Random number generation - -#include <stdint.h> - -// Error/Panic codes - -#define RAND64_UNIMPLEMENTED 0x00726301 - - -/// RAND32_ALL is used as the \a limit argument to rand32() and _rand32() to -/// request the return of a full 32-bit random unsigned integer. - -#define RAND32_ALL 0 - -extern uint32_t _seed32; - -uint32_t -_rand32(uint32_t *seed, uint32_t limit); - -uint32_t -rand32(uint32_t limit); - -void -srand32(uint32_t seed); - - -/// RAND64_ALL is used as the \a limit argument to rand64() and _rand64() to -/// request the return of a full 64-bit random unsigned integer. - -#define RAND64_ALL 0 - -extern uint64_t _seed64; - -//void -//davidmult64to128(uint64_t u, uint64_t v, uint64_t &h, uint64_t &l); - -uint64_t -_rand64(uint64_t *seed, uint64_t limit); - -uint64_t -rand64(uint64_t limit); - -void -srand64(uint64_t seed); - - -/// A random weighting map for integer selections -/// -/// See rand_map_int() for details. - -typedef struct { - - /// The relative weight of this selection - /// - /// The final weight of the map array \e must be 0 to terminate the map. - unsigned weight; - - /// The selection - int selection; - -} RandMapInt; - - -/// A random weighting map for pointer selections -/// -/// See rand_map_ptr() for details. - -typedef struct { - - /// The relative weight of this selection - /// - /// The final weight of the map array \e must be 0 to terminate the map. - unsigned weight; - - /// The selection - void *selection; - -} RandMapPtr; - - -int -_rand_map_int(uint32_t *seed, RandMapInt *map, int *index); - -int -rand_map_int(RandMapInt *map); - -void * -_rand_map_ptr(uint32_t *seed, RandMapPtr *map, int *index); - -void * -rand_map_ptr(RandMapPtr *map); - - -#endif /* __RAND_H__ */ diff --git a/src/lib/common/rand32.c b/src/lib/common/rand32.c deleted file mode 100644 index c75fc76..0000000 --- a/src/lib/common/rand32.c +++ /dev/null @@ -1,384 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/common/rand32.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 rand32.c -/// \brief 32-bit unsigned pseudo-random number generation - -#include "rand.h" - -/// The default seed for rand32() - -uint32_t _seed32 = 405405405; - - -/// Generate a random 32-bit unsigned integer from an explicit seed -/// -/// \param seed A pointer to the random seed (updated by this routine). -/// -/// \param limit The (exclusive) upper bound of the range of generated random -/// integers. The (inclusive) lower bound is always 0. -/// -/// \retval A pseudo-random unsigned 32-bit integer uniformly selected from -/// the range 0 to \a limit - 1 (inclusive). However if the \a limit parameter -/// is \c RAND32_ALL (0), then the return value is a full 32-bit random bit -/// vector. -/// -/// This is a 32-bit linear congruential generator, taken from the 'ranqd1' -/// generator from "Numerical Recipes in C". The authors' only praise for -/// this generator is that it is "\e very fast"; the quality of random numbers -/// is deemed "entirely adequate for many uses". -/// -/// The initial 32 pseudo-random result is treated as a 32-bit binary fraction -/// that is multipled by the limit to yield the final random 32-bit -/// integer. If the limit is 0, then the full 32-bit result is returned. As -/// with all LCG, do not count on the low-order bits to be particularly -/// random. - -uint32_t -_rand32(uint32_t *seed, uint32_t limit) -{ - uint64_t x; - - *seed = (*seed * 1664525) + 1013904223; - if (limit == RAND32_ALL) { - return *seed; - } else { - x = (uint64_t)(*seed) * limit; - return x >> 32; - } -} - - -/// Generate a random 32-bit unsigned integer from a system-wide seed -/// -/// \param limit The (exclusive) upper bound of the range of generated random -/// integers. The (inclusive) lower bound is always 0. -/// -/// \retval A pseudo-random unsigned 32-bit integer uniformly selected from -/// the range 0 to \a limit - 1 (inclusive). However if the \a limit parameter -/// is \c RAND32_ALL (0), then the return value is a full 32-bit random bit -/// vector. -/// -/// rand32() is not thread safe. There is a small possibility that multiple -/// threads may observe the same random numbers, and it is also possible that -/// the random sequence may appear to repeat due to thread interactions. If -/// these are concerns then the application should either call rand32() from -/// within a critical section, or provide a unique seed to each thread or -/// process and use the underlying _rand32() API explicitly. - -uint32_t -rand32(uint32_t limit) -{ - return _rand32(&_seed32, limit); -} - - -/// Set the global random seed for rand32() - -void -srand32(uint32_t seed) -{ - _seed32 = seed; -} - - -/// Select an integer from a weighted distribution using a specific seed -/// -/// \param seed A 32-bit unsigned random seed (accumulator) -/// -/// \param map An array of RandMapInt structures, the final element of which -/// must have the \a weight field = 0. This array will typically be allocated -/// statically. -/// -/// \param index An optional pointer to an integer which will recieve the -/// index of the item selected. NULL \a index are ignored. This is provided -/// for appplications that require statistics on selections. -/// -/// \retval One of the \a selection from the array with a non-0 \a weight. If -/// the weight array is NULL (= {{0, \<dont care\>}}), then by convention the -/// return value is 0, and the return index is -1. -/// -/// This routine selects items from the \a map randomly, given the weighting -/// implied by (map[i].weight / SUM(i = 0,...,N, map[i].weight)). For -/// example, the following two maps are equivalent in that they select 'a' and -/// 'c' with 25% probability, and 'b' with 50% probability: -/// -/// RandMapInt map0[] = {{1, 'a'}, {2, 'b'}, {1, 'c'}, {0, 0}}; -/// -/// RandMapInt map1[] = {{25, 'a'}, {50, 'b'}, {25, 'c'}, {0, 0}}; -/// -/// Note that several errors including negative weights, or the overflow of -/// the sum of weights as an \a unsigned number are neither detected nor -/// reported. -/// -/// \todo We could probably merge the code for the integer and pointer versions -/// somewhat. This is a great example of where C++ would be nice, as we could -/// easily cache the sum of weights when the map was constructed. - -int -_rand_map_int(uint32_t *seed, RandMapInt *map, int *index) -{ - unsigned weight, sum; - RandMapInt *p; - uint32_t rand; - int i = -1; - int selection = 0; - - sum = 0; - p = map; - while (p->weight != 0) { - sum += p->weight; - p++; - } - - if (sum != 0) { - - rand = _rand32(seed, sum); - - weight = 0; - p = map; - i = 0; - while (p->weight != 0) { - weight += p->weight; - if (rand < weight) { - selection = p->selection; - break; - } - p++; - i++; - } - } - - if (index != 0) { - *index = i; - } - - return selection; -} - - -/// Select an integer from a weighted distribution using the system-side seed -/// \a _seed32 -/// -/// See _rand_map_int() for documentation - -int -rand_map_int(RandMapInt *map) -{ - return _rand_map_int(&_seed32, map, 0); -} - - -/// Select a pointer from a weighted distribution using a specific seed -/// -/// \param seed A 32-bit unsigned random seed (accumulator) -/// -/// \param map An array of RandMapPtr structures, the final element of which -/// must have the \a weight field = 0. This array will typically be allocated -/// statically. -/// -/// \param index An optional pointer to an integer which will recieve the -/// index of the item selected. NULL \a index are ignored. This is provided -/// for appplications that require statistics on selections. -/// -/// \retval One of the \a selection from the array with a non-0 \a weight. If -/// the weight array is NULL (= {{0, \<dont care\>}}), then by convention the -/// return value is 0, and the return index is -1; -/// -/// This routine selects items from the \a map randomly, given the weighting -/// implied by (map[i].weight / SUM(i = 0,...,N, map[i].weight)). For -/// example, the following two maps are equivalent in that they select &a and -/// &c with 25% probability, and &b with 50% probability: -/// -/// RandMapPtr map0[] = {{1, &a}, {2, &b}, {1, &c}, {0, 0}}; -/// -/// RandMapPtr map1[] = {{25, &a}, {50, &b}, {25, &c}, {0, 0}}; -/// -/// Note that several errors including negative weights, or the overflow of -/// the sum of weights as an \a unsigned number are neither detected nor -/// reported. - - -void * -_rand_map_ptr(uint32_t *seed, RandMapPtr *map, int *index) -{ - unsigned weight, sum; - RandMapPtr *p; - uint32_t rand; - int i = -1; - void *selection = 0; - - sum = 0; - p = map; - while (p->weight != 0) { - sum += p->weight; - p++; - } - - if (sum != 0) { - - rand = _rand32(seed, sum); - - weight = 0; - p = map; - i = 0; - while (p->weight != 0) { - weight += p->weight; - if (rand < weight) { - selection = p->selection; - break; - } - p++; - i++; - } - } - - if (index != 0) { - *index = i; - } - - return selection; -} - - -/// Select a pointer from a weighted distribution using the system-side seed -/// \a _seed32 -/// -/// See _rand_map_ptr() for documentation - -void * -rand_map_ptr(RandMapPtr *map) -{ - return _rand_map_ptr(&_seed32, map, 0); -} - - -//////////////////////////////////////////////////////////////////////////// - -#ifdef __TEST_RAND_C__ - -#include <stdio.h> -#include <stdlib.h> - -#ifdef RANDOM_MAP - -// Weighted distribution testing - -int a, b, c; -int aa, bb, cc; - -int x[3]; - -RandMapPtr map0[] = {{1, &a}, {2, &b}, {1, &c}, {0, 0}}; -RandMapPtr map1[] = {{25, &aa}, {50, &bb}, {25, &cc}, {0, 0}}; - -RandMapInt map2[] = {{25, 0}, {50, 1}, {25, 2}, {0, 0}}; - -int -main() -{ - int i, j; - int *p; - - for (i = 0; i < 1000000; i++) { - p = (int *)(rand_map_ptr(map0)); - *p = *p + 1; - p = (int *)(rand_map_ptr(map1)); - *p = *p + 1; - j = rand_map_int(map2); - x[j]++; - } - - printf("%d %d %d\n", a, b, c); - printf("%d %d %d\n", aa, bb, cc); - printf("%d %d %d\n", x[0], x[1], x[2]); - - return 0; -} - -#endif /* RANDOM_MAP */ - - -#ifdef BASIC_TEST - -// Simple self-checking uniform distrubution tests for rand32. - -void -test(int *a, int size, int count, double max_error) -{ - int i; - double error; - - for (i = 0; i < size; i++) { - a[i] = 0; - } - - for (i = 0; i < size * count; i++) { - a[rand32(size)]++; - } - - for (i = 0; i < size; i++) { - error = (a[i] / (double)count) - 1.0; - printf("a[%4d] : %10d %.5f\n", i, a[i], error); - if (abs(error) > max_error) { - printf("Too much error\n"); - exit(1); - } - } -} - -#define MAX_SIZE 128 - -int -main() -{ - int a[MAX_SIZE]; - int count = 1000000; - double max_error = .005; - int i; - - for (i = 2; i <= MAX_SIZE; i *= 2) { - printf("\nTest %d\n\n", i); - test(a, i, count, max_error); - } - - srand32(0); - for (i = 2; i <= MAX_SIZE; i *= 2) { - printf("\nTest %d\n\n", i); - test(a, i, count, max_error); - } - - srand32(0xdeadbeef); - for (i = 2; i <= MAX_SIZE; i *= 2) { - printf("\nTest %d\n\n", i); - test(a, i, count, max_error); - } -} - -#endif /* BASIC_TEST */ - -#endif /* __TEST_RAND_C__ */ - diff --git a/src/lib/common/sync.c b/src/lib/common/sync.c deleted file mode 100644 index a489be8..0000000 --- a/src/lib/common/sync.c +++ /dev/null @@ -1,294 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/common/sync.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 sync.c -/// \brief A library of higher-level synchronization primitives based on -/// low-level kernel services. -/// -/// The APIs provided here are currently based on kernel services, but the -/// specifications should allow applications to be ported to other -/// environments if required. Note that like kernel services, data -/// structures manipulated by this code are protected in KERN_NONCRITICAL -/// critical sections. - -#include "kernel.h" -#include "sync.h" - -/// Create a Barrier -/// -/// \param barrier A pointer to an uninitialized or currently unused Barrier -/// -/// \param count The number of threads required to pend at the barrier before -/// all threads are released again for execution. Note that \a count values -/// of 0 and 1 are treated as equivalent - threads will not pend at all in -/// these cases. Also note that if a watchdog thread is being used, the -/// watchdog thread should not be included in the count. -/// -/// \retval 0 Success -/// -/// \retval -SYNC_INVALID_OBJECT The \a barrier is NULL (0) - -int -barrier_create(Barrier *barrier, KERN_SEMAPHORE_COUNT count) -{ - if (KERN_ERROR_CHECK_API) { - KERN_ERROR_IF(barrier == 0, SYNC_INVALID_OBJECT); - } - - barrier->entry_sem = &(barrier->sem[0]); - barrier->exit_sem = &(barrier->sem[1]); - - KERN_SEMAPHORE_CREATE((KERN_SEMAPHORE *)(barrier->entry_sem), 0, 0); - KERN_SEMAPHORE_CREATE((KERN_SEMAPHORE *)(barrier->exit_sem), 0, 0); - - barrier->entry_count = 0; - barrier->exit_count = 0; - barrier->target_count = count; - - barrier->watchdog_pending = 0; - barrier->watchdog_entries = 0; - - barrier->callback = 0; - barrier->arg = 0; - barrier->run_callback = 0; - - return 0; -} - - -/// Install a barrier callback -/// -/// \param barrier A pointer to an initialized Barrier object -/// -/// \param callback A function taking a single (void *) argument, to be -/// executed in the context of the first thread to exit the barrier when the -/// barrier condition is met. -/// -/// \param arg The argument of the \a callback. -/// -/// The Barrier object supports an optional callback function. The callback -/// (with the customary single (void *) parameter) is made when the barrier -/// condition is met, in the thread context of the first (highest priority) -/// thread to exit the barrier. The callback is made inside the barrier_pend() -/// call, but outside of a critical section. The specification of the callback -/// is not part of the barrier_create() call, but is provided later by this -/// API. Setting a NULL (0) callback disables the callback mechanism. -/// -/// \retval 0 Success -/// -/// \retval -SYNC_INVALID_OBJECT The \a barrier is NULL (0) - -int -barrier_callback_set(Barrier *barrier, - BarrierCallback callback, - void *arg) -{ - KERN_MACHINE_CONTEXT ctx; - - if (KERN_ERROR_CHECK_API) { - KERN_ERROR_IF(barrier == 0, SYNC_INVALID_OBJECT); - } - - KERN_CRITICAL_SECTION_ENTER(KERN_NONCRITICAL, &ctx); - - barrier->callback = callback; - barrier->arg = arg; - - KERN_CRITICAL_SECTION_EXIT(&ctx); - - return 0; -} - - -static int -_barrier_pend(Barrier *barrier, int watchdog) -{ - KERN_MACHINE_CONTEXT ctx; - int rc = 0; - KERN_SEMAPHORE *temp_sem; - BarrierCallback callback = 0; /* Make GCC Happy */ - void *arg = 0; /* Make GCC Happy */ - int run_callback; - - if (KERN_ERROR_CHECK_API) { - KERN_ERROR_IF(barrier == 0, SYNC_INVALID_OBJECT); - } - - KERN_CRITICAL_SECTION_ENTER(KERN_NONCRITICAL, &ctx); - - // A normal thread will pend at the entry unless 1) the thread satisfies - // the barrier condition, or 2) a watchdog thread is pending here. A - // watchdog thread only pends if no other threads are pending. - - if (watchdog) { - barrier->watchdog_entries++; - } - - barrier->entry_count++; - if (!barrier->watchdog_pending && - ((watchdog && (barrier->entry_count == 1)) || - (!watchdog && (barrier->entry_count < barrier->target_count)))) { - - if (watchdog) { - barrier->watchdog_pending = 1; - } - - // The thread must pend here - - rc = KERN_SEMAPHORE_PEND((KERN_SEMAPHORE *)(barrier->entry_sem), - KERN_WAIT_FOREVER); - if (rc) { - goto exit_critical; - } - - } else { - - // The barrier condition is met - or the watchdog thread is blocked - // here. The entry and exit semaphores and counts are swapped. The - // callback is marked to be called. - - // If the barrier is used incorrectly, or threads are deleted without - // adjusting the barrier target count then the following condition - // could become true, which could lead to bad behavior. - - if (barrier->exit_count != 0) { - KERN_PANIC(SYNC_BARRIER_INVARIANT); - } - - barrier->entry_count--; // Undo preincrement above - barrier->watchdog_pending = 0; - - temp_sem = (KERN_SEMAPHORE *)(barrier->exit_sem); - barrier->exit_sem = barrier->entry_sem; - barrier->entry_sem = temp_sem; - - barrier->exit_count = barrier->entry_count; - barrier->entry_count = 0; - - barrier->run_callback = 1; - } - - // This thread either continues to run or just woke up after having - // blocked at the barrier. The current thread makes the next thread (if - // any) runnable as well. Normally the current thread will be of a higher - // priority than any blocked threads, so no context switch will occur. The - // thread that satisfies the barrier condition \e will cause a context - // switch here, unless it just happens to be the highest priority thread - // in the barrier group. - - if (barrier->exit_count != 0) { - barrier->exit_count--; - rc = KERN_SEMAPHORE_POST((KERN_SEMAPHORE *)(barrier->exit_sem)); - } - -exit_critical: - - if (rc) { - KERN_CRITICAL_SECTION_EXIT(&ctx); - return rc; - } - - // In the case of a satisfied barrier condition, the first thread to exit - // the critical section will be the higest priority thread blocked at the - // barrier. This thread is tasked with executing the callback, outside of - // the critical section. - - run_callback = barrier->run_callback; - barrier->run_callback = 0; - if (run_callback) { - callback = barrier->callback; - arg = (void *)(barrier->arg); /* Cast away 'volatile' */ - } - - KERN_CRITICAL_SECTION_EXIT(&ctx); - - if (run_callback && callback) { - callback(arg); - } - - return 0; -} - - -/// Pend at a Barrier -/// -/// \param barrier An initialized barrier object -/// -/// A thread will pend at a barrier until \a count number of threads (supplied -/// in the call of barrier_create()) are pending. If \a count is 0 or 1, the -/// API always returns immediately. -/// -/// If barrier watchdog thread is being used (correctly), then the watchdog -/// will cause thread pending on the barrier to be released whenever all -/// threads in the group are blocked, regardless of whether some of the -/// group's threads are blocked elesewhere. -/// -/// \retval 0 Success -/// -/// \retval -SYNC_INVALID_OBJECT The \a barrier is NULL (0) -/// -/// Other errors may be returned by the embedded call of ssx_semephore_pend(). -/// In particular this API will fail if called outside of a thread context -/// since it requires blocking indefinitely on a semaphore. -/// -/// \bug The semaphore should be able to provide the number of pending threads -/// - which we should really be using here instead of the barrier counts. The -/// current implementation can produce some strange behavior if threads are -/// deleted. - -int -barrier_pend(Barrier *barrier) -{ - return _barrier_pend(barrier, 0); -} - - -/// Barrier watchdog thread -/// -/// \param arg A pointer to the Barrier object this thread should manage. -/// -/// A barrier_watchdog() thread is attached to a Barrier object, and forces -/// the barrier condition to be met whenever it runs. This thread is designed -/// to be mapped at a priority immediately below the priorities of a group of -/// threads that pend on the barrier. -/// -/// In this way, should every thread in the group become blocked, the watchdog -/// will allow any threads that are blocked on the barrier to run. If all the -/// threads are blocked elsewhere, then the watchdog blocks on the barrier, -/// and as soon as any thread pends again at the barrier the thread remains -/// runnable and the watchdog becomes runnable. -/// -/// The barrier_watchdog() thread is not required. Without the watchdog, -/// otherwise runnable threads in the barrier group will remain blocked on the -/// barrier as long as any of their cohorts remain blocked elsewhere. - -void -barrier_watchdog(void *arg) -{ - do { - _barrier_pend((Barrier *)arg, 1); - } while (1); -} diff --git a/src/lib/common/sync.h b/src/lib/common/sync.h deleted file mode 100644 index 5657c78..0000000 --- a/src/lib/common/sync.h +++ /dev/null @@ -1,167 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/common/sync.h $ */ -/* */ -/* 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 */ -#ifndef __SYNC_H__ -#define __SYNC_H__ - -// $Id$ - -/// \file sync.h -/// \brief A library of higher-level synchronization primitives based on -/// low-level kernel services. -/// -/// The APIs provided here are currently based on SSX services, but the -/// specifications should allow applications to be fairly easily ported to -/// other environments if required. -/// -/// \todo Consider implementing a subset of the POSIX pthreads standards -/// instead of these non-standard synchronization primitives. Ideally all -/// synchronization primitives would be part of the SSX kernel so that they -/// would correctly handle thread suspension and deletion while pending. - -#include "kernel.h" - -// Error/panic codes - -#define SYNC_INVALID_OBJECT 0x00896201 -#define SYNC_INVALID_ARGUMENT 0x00896202 -#define SYNC_BARRIER_PEND_TIMED_OUT 0x00896203 -#define SYNC_BARRIER_OVERFLOW 0x00896204 -#define SYNC_BARRIER_UNDERFLOW 0x00896205 -#define SYNC_BARRIER_INVARIANT 0x00896206 -#define SYNC_SHARED_UNDERFLOW 0x00896207 - -//////////////////////////////////////////////////////////////////////////// -// Barrier -//////////////////////////////////////////////////////////////////////////// - -typedef void (*BarrierCallback)(void *); - -/// A thread barrier object -/// -/// A Barrier allows multiple threads to pend until a group of threads are all -/// pending at the barrier. Once all threads are pending at the barrier, all -/// threads are released again for execution. The barrier guarantees that -/// once released, lower-priority threads will have the chance to execute -/// before the barrier condition is satisfied again. Thus the Barrier can be -/// used as a form of fair scheduling for a group of threads that execute in a -/// loop of doing work followed by pending at the barrier. -/// -/// Although the barrier guarantees fairness, it can not by itself guarantee -/// progress. If several threads in a barrier group are -/// pending at the barrier while other threads in the group are blocked away -/// from the barrier, the threads at the barrier should be running (if allowed -/// by the priority mappings etc.). The only way to guarantee constant -/// progress by any unblocked thread in a group is to created another, -/// 'watchdog' thread that has lower priority than the other threads in the -/// group. The watchdog thread only executes when all threads in the group -/// are blocked, and ensures progress. This implementation provides a -/// watchdog thread routine as barrier_watchdog(). -/// -/// Normally the threads in a barrier group will be assigned consecutive -/// priorities - otherwise various forms of priority inversion can arise. The -/// watchdog thread, if any, will normally be assigned the priority -/// immediately lower than the lowest priority thread in the group. -/// -/// The Barrier object supports an optional callback function. The callback -/// (with the customary single (void *) parameter) is made when the barrier -/// condition is met, in the thread context of the first (highest priority) -/// thread to exit the barrier. The callback is made inside the barrier_pend() -/// call, but outside of a critical section. The specification of the callback -/// is not part of the barrier_create() call, but is provided later by the -/// barrier_callback_set() call. A NULL (0) callback (the default) is ignored. -/// -/// NB: All barrier APIs (other than barrier_create()) must be made from -/// thread mode - they will fail if called from interrupt handlers or before -/// threads have started. - -typedef struct Barrier { - - /// Semaphore array; see \a entry_sem and \a exit_sem; - KERN_SEMAPHORE sem[2]; - - /// The entry semaphore. - /// - /// Threads pending at the barrier initially block here. Once all of the - /// threads in the group are pending here, the entry and exit semaphores - /// are swapped and threads are released (in priority order) from the new - /// exit semaphore. - volatile KERN_SEMAPHORE *entry_sem; - - /// The exit semaphore. - volatile KERN_SEMAPHORE *exit_sem; - - /// The current count of threads pending at \a entry_sem. - volatile KERN_SEMAPHORE_COUNT entry_count; - - /// The current count of threads pending at \a exit_sem. - volatile KERN_SEMAPHORE_COUNT exit_count; - - /// The target number of threads required to release the barrier - volatile KERN_SEMAPHORE_COUNT target_count; - - /// A flag - Is the watchdog thread pending at the barrier? - volatile int watchdog_pending; - - /// Statistics - The number of times the watchdog has entered the barrier. - volatile uint32_t watchdog_entries; - - /// The barrier condition callback function - volatile BarrierCallback callback; - - /// The argument of the callback function - volatile void *arg; - - /// This flag is set to tell the first thread to exit the barrier to - /// execute the callback (if any). - volatile int run_callback; - -} Barrier; - -int -barrier_create(Barrier *barrier, KERN_SEMAPHORE_COUNT count); - -int -barrier_callback_set(Barrier *barrier, - BarrierCallback callback, - void *arg); - -int -barrier_pend(Barrier *barrier); - -void -barrier_watchdog(void *arg); - - -#endif // __SYNC_H__ - - - - - - - - - - diff --git a/src/lib/ppc405lib/byte_pool.c b/src/lib/ppc405lib/byte_pool.c deleted file mode 100644 index d382abc..0000000 --- a/src/lib/ppc405lib/byte_pool.c +++ /dev/null @@ -1,1442 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/byte_pool.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 byte_pool.c -/// \brief An implementation of a constant-time malloc() algorithm. -/// -/// The 'byte-pool' API defined in this file is similar to ThreadX' byte-pool -/// operations, with the major difference that here there is no concept of a -/// thread blocking on memory allocation. This is a concept that is difficult -/// to implement correctly and efficiently, and several semantic options -/// exist for how allocation and freeing memory should work in the presence of -/// blocked threads. The application is always free to implement a blocking -/// API using these APIs and SSX synchronization primitives. -/// -/// For convenience this implementation also provides malloc() and calloc() -/// calls. These APIs depend on the application creating a byte pool and -/// assigning it to the library variable \a _malloc_byte_pool. -/// -/// \todo Consider separating the idea of creating a byte pool with a specific -/// maximum-sized block, from the idea of adding memory to a byte pool. The -/// idea is to allow pieces of memory required during initialization of the -/// application to be added back to the pool at run time. We could also -/// simply add an API to allow memory to be added to a previously created -/// pool, with the special case that if the block were 'too big' that it would -/// be split into smaller chunks. -/// -/// \todo Consider -/// adding an option to store a pointer to the originating byte pool in the -/// block header. This would allow deallocation of any block with free() -/// regardless of which pool it was allocated from, at a cost of 1 pointer per -/// block. This would simplify some of our validation test cases. We could -/// also accomplish this by having each pool register ranges of addresses that -/// it allocates from, but that would require a search every time we freed a -/// block. - -#include "ssx.h" -#include "byte_pool.h" - -// This is an implementation of the TLSF (Two-Level Segregate Fit) algorithm -// described by M. Masmano, I. Ripoll and A Crespol, -// http:/rtportal.upv.es/rtmalloc. A couple of their papers and presentations -// are archived in /lib/doc/tlsf. This is a 'clean-room' implementation of -// their published ideas based solely on their papers and presentations. No -// part of their GPL implementation was used to create the byte-pool facility -// implemented here. The algorithm as implemented here should port without -// problem to either 32-bit or 64-bit implementations. -// -// TLSF has the nice property that it is a constant-time algorithm, both for -// allocation and freeing allocated blocks. This property is guaranteed by -// always trading space for speed in the algorithm. This means that we can -// (and do) run the allocation and freeing in a critical section. With all -// error checking and statistics, a worst-case byte_bool_alloc() was -// timed at 318 PowerPC instructions in the PgP OCC (PPC405) Simics -// simulation. A worst-case byte_pool_free() was timed at 368 PowerPC -// instructions. These times are expected to equate to ~3us in OCC, a -// reasonable value for a critical section. [The above times include all -// error checking]. -// -// An allocation unit requires two pointers of overhead - a pointer to the -// previous block and a pointer to the next block (both in terms of linear -// addresses). The minimum block size also includes the requirement for two -// extra pointers used to link free blocks into their free lists. The final -// size of the block (including user data) is simply computed as (next - self) -// in terms of linear addresses. -// -// An allocated block is marked by setting the low-order bit of the 'previous' -// pointer, which bit is otherwise guaranteed to be 0 due to alignment -// restrictions. Whenever a block is freed it is immediately merged with the -// previous and next blocks, if possible. Several places in the block -// splitting and merging code take advantage of this invariant, and assume -// that if a block is not already merged with its 'next' partner, then the -// 'next' partner must be allocated. Sentinel blocks are allocated at either -// end of the managed area to avoid special checks for the first and last -// memory blocks during merging. -// -// The 'Two-Level' in TLSF refers to the fact that there are multiple free -// lists arranged in a 2-dimensional array. Each free lists contains blocks -// that fall into a particular size range. The free list pointers and other -// data structures described below are carved out of the initial free area -// when a byte pool is initialized. -// -// The first dimension of the free list array is simply the floor(log2(size)) -// of the block. For the second dimension, a tuning parameter selects how many -// columns each row in the table will contain. The number of columns must be -// an even power-of-2. Each column represents a fixed power-of-2 size -// increment of block sizes. Given a block size, it is easy to compute the -// row and column indices of a free list containing blocks of that size with -// shifting and masking operation. -// -// It is assumed that the C type 'unsigned long' is the same size as a -// pointer. Therefore the number of rows in the table is less than or equal -// to the number of bits in an unsigned long. The number of columns is also -// restricted to being in the range (1, 2, 4, ... number of bits in unsigned -// long). -// -// The above restrictions make it very fast (and constant time) to find a free -// list that contains a block that will satisfy the allocation request. The -// byte pool structure maintains a 'row status' word that indicates whether -// there are any blocks free in any of the free lists of the row. Each row -// also has an associated 'column status' word that indicates which free lists -// have blocks free. A row status bit is set if and only if at least one bit -// in the column status for that row is set. -// -// Note that although the 32-bit PowerPC implementation conceptually contains -// a 32x32 array of free list pointers, only the free list pointers actually -// required to hold the representable blocks are allocated. -// -// The algorithm uses the GCC __builtin_clzl() function to count leading zeros -// in the status words to find rows/columns that contain free blocks. This -// generates the 'cntlzw' instruction on 32-bit PowerPC and a similar -// instruction on X86. So the algorithm is also portable across machines - -// which simplifies testing. -// -// A couple of final facts: When the application requests memory, the block -// header overhead is added to the request and we look for a free list -// guaranteed to contain blocks of the requested size. That means that the -// request size must be rounded up to the next free list size, to avoid having -// to search a list that might not contain a block of the proper size. This -// leads to cases where allocation will fail, even though the requested memory -// is actually available. That's just the price we have to pay for a -// constant-time guarantee. -// -// This memory allocator will never be used in mission-mode for hard-real-time -// applications, so the statistics are always kept up-to-date. This adds some -// overhead, but does not effect the constant-time behavior. -// -// Given the above description, hopefuly the brief comments with the -// implementation will make sense. - - -/// The byte pool for use by malloc() and calloc(). -/// -/// The application must define a byte pool and assign it to _malloc_byte_pool -/// in order for malloc() and calloc() to work. - -BytePool *_malloc_byte_pool = 0; - - -// The byte pool memory block header. -// -// Each memory block requires 2 pointers of overhead - the pointers to the -// previous and next (in terms of linear addresses) blocks. The low-order bit -// of the \a previous pointer is used as the \e allocated flag, and is set -// when a block is allocated. The size of a block is computed simply as the -// \a next - \a self. This header layout makes it very simple to merge blocks -// when they are deallocated. - -typedef struct ByteBlock { - - // Pointer to the previous (in terms of linear address) block. - // - // The low-order bit of the pointer is set to indicate a block that has - // been allocated. - struct ByteBlock *previous; - - // Pointer to the next (in terms of linear address) block. - // - // The size of the block is computed simply as \a next - \a self. - struct ByteBlock *next; - -} ByteBlock; - - -// A free byte-pool memory block -// -// Blocks stored in free lists require an additional 2 pointers of -// overhead. The blocks are doubly-linked in the free lists to make deletion -// a constant-time operation. Note that the previous pointer is a pointer to -// a pointer to a ByteBlock - it may be pointing to the free list -// header. Since all blocks must be freeable, this structure defines the -// minimum block size. - -typedef struct FreeByteBlock { - - // The base object - ByteBlock block; - - // Pointer to the next block in the free list - struct FreeByteBlock *next; - - // Pointer to the \a next pointer of the previous element in the free - // list, or a pointer to the free list header. - struct FreeByteBlock **previous; - -} FreeByteBlock; - - -// All blocks will be aligned to this size, so this size also defines the -// minimum quantum of memory allocation. The coice of 8 should give -// good results for both 32-bit and 64-bit implementations. -// -// NB : This implmentation assumes that the ByteBlock and FreeByteBLock are -// aligned to this alignment - if this constant is ever changed from 8 then -// the ByteBlock and FreeByteBlock may need to be padded to meet the alignment -// assumptions, and the \a minimum_block_size may need to be adjusted. - -#define ALIGNMENT 8 - - -// An unsigned long, big-endian bit mask - -#define UL_BE_MASK(i) \ - ((unsigned long)1 << (BITS_PER_UNSIGNED_LONG - (i) - 1)) - - -// Align a value to the alignment. The direction is either positive or -// negative to indicate alignment up or down. - -static inline unsigned long -align(unsigned long x, int direction) -{ - if (x % ALIGNMENT) { - if (direction > 0) { - return x + (ALIGNMENT - (x % ALIGNMENT)); - } else { - return x - (x % ALIGNMENT); - } - } else { - return x; - } -} - - -// Compute the floor(log2(x)) of x. This is used to compute the row indices -// of blocks based on the block size. - -static inline int -floor_log2(unsigned long x) -{ - return BITS_PER_UNSIGNED_LONG - 1 - __builtin_clzl(x); -} - - -// In theory the tuning parameters might vary based on the amount of memory -// being managed, but for now we simply use constants. -// -// The minimum block size includes both the size of the header, as well as the -// requirement that the number of columns be <= the mimumum block size to make -// the addressing uniform. For example, on PPC405 the minimum block size is 16 -// bytes (4 pointers) -- unless the number of columns is 32, in which case it -// has to grow to 32 bytes. -// -// Note that no matter what, we may allocate free list pointers in the -// lower-numbered rows that will never be populated due to alignment -// constraints. - -#ifndef BYTE_POOL_TLSF_COLUMNS -#define BYTE_POOL_TLSF_COLUMNS 8 /* 1,2,4, ... BITS_PER_UNSIGNED_LONG */ -#endif - -static void -compute_tuning(BytePool *pool, size_t size, int columns) -{ - int log2_min_size; - - pool->columns = columns; - pool->log2_columns = floor_log2(pool->columns); - pool->column_mask = (1 << pool->log2_columns) - 1; - - log2_min_size = MAX(pool->log2_columns, floor_log2(sizeof(FreeByteBlock))); - pool->minimum_block_size = align(1 << log2_min_size, 1); -} - - -// Compute the size of a block - -static inline size_t -block_size(ByteBlock *block) -{ - return (unsigned long)(block->next) - (unsigned long)block; -} - - -/// Return implementation information for a block -/// -/// \param memory The memory block to query. This pointer must have been -/// returned by one of the byte_pool functions or derivitives, or may also be -/// 0. -/// -/// \param actual_address : Returned as the address of the block header. -/// -/// \param actual_size Returned as the size of the complete block, including -/// the header. -/// -/// \param useful_size : Returned as the actual amount of space available from -/// \a memory to the end of the block. The \a useful_size may be useful to -/// applications that allocate big blocks then carve them up into smaller -/// structures. -/// -/// Note that any of \a actual_address, \a actual_size and \a useful_size may -/// be passed in as 0 if the caller does not require the information. - -void -byte_pool_block_info(void* memory, - void** actual_address, size_t* actual_size, - size_t* useful_size) -{ - ByteBlock* block; - - if (memory == 0) { - - if (actual_address) *actual_address = 0; - if (actual_size) *actual_size = 0; - if (useful_size) *useful_size = 0; - - } else { - - // This implementation uses the convention that if the \a next pointer - // of the putative ByteBlock == 1, then this is actually an aligned - // allocation and the actual ByteBlock is located at the address - // contained in the \a previous field of the dummy header. - - block = (ByteBlock *)(((unsigned long)memory) - sizeof(ByteBlock)); - if ((int)(block->next) == 1) { - block = block->previous; - } - - if (actual_address) *actual_address = block; - if (actual_size) *actual_size = block_size(block); - if (useful_size) - *useful_size = - (unsigned long)(block->next) - (unsigned long)memory; - } -} - - -// Mark a block as allocated by setting the low-order bit of the \a previous -// pointer. - -static inline ByteBlock * -allocated(ByteBlock *p) -{ - return (ByteBlock *)((unsigned long)p | 1ul); -} - - -static void -mark_allocated(BytePool *pool, ByteBlock *block) -{ - size_t bytes = block_size(block); - - pool->bytes_allocated += bytes; - pool->bytes_free -= bytes; - pool->blocks_allocated += 1; - pool->blocks_free -= 1; - - block->previous = allocated(block->previous); -} - - -// Mark a block as free by clearing the low-order bit of the \a previous -// pointer. - -static inline ByteBlock * -deallocated(ByteBlock *p) -{ - return (ByteBlock *)((unsigned long)p & ~1ul); -} - - -static void -mark_free(BytePool *pool, ByteBlock *block) -{ - size_t bytes = block_size(block); - - pool->bytes_allocated -= bytes; - pool->bytes_free += bytes; - pool->blocks_allocated -= 1; - pool->blocks_free += 1; - - block->previous = deallocated(block->previous); -} - - -// Check for a block being free - -static inline int -block_is_free(ByteBlock *block) -{ - return (((unsigned long)(block->previous)) & 1ul) == 0; -} - - -// Normalize a 'previous' pointer - -static inline ByteBlock * -normalize_previous(ByteBlock *previous) -{ - return (ByteBlock *)((unsigned long)previous & ~1ul); -} - - -// Check for correct linkage. This is such a critical check for application -// memory corruption that it is always done. - -static int -check_linkage(ByteBlock *block) -{ - if (normalize_previous(block->next->previous) != block) { - printk("byte_pool: Forward linkage error\n" - " block : %p\n" - " block->next : %p\n" - " block->next->previous : %p\n", - block, - block->next, - block->next->previous); - SSX_ERROR(BYTE_POOL_REVERSE_LINKAGE); - } else if (normalize_previous(block->previous)->next != block) { - printk("byte_pool: linkage error\n" - " block->previous : %p\n" - " block->pevious->next : %p\n" - " block : %p\n", - block->previous, - block->previous->next, - block); - SSX_ERROR(BYTE_POOL_FORWARD_LINKAGE); - } - return 0; -} - - -// Mark a free list as empty - -static inline void -mark_empty(BytePool *pool, int row, int column) -{ - pool->column_status[row] &= ~UL_BE_MASK(column); - if (pool->column_status[row] == 0) { - pool->row_status &= ~UL_BE_MASK(row); - } -} - - -// Mark a free list as non-empty - -static inline void -mark_non_empty(BytePool *pool, int row, int column) -{ - pool->column_status[row] |= UL_BE_MASK(column); - pool->row_status |= UL_BE_MASK(row); -} - - -// Convert a size into row and column indices - -static inline void -size2rc(BytePool *pool, size_t size, int *row, int *column) -{ - *row = floor_log2(size); - *column = (size >> (*row - pool->log2_columns)) & pool->column_mask; -} - - -// Given a block size, find the free list that contains blocks of that size -// (or greater, up to the next free list). When called during block freeing, -// the block size is known to be valid. When called during allocation, the -// block size may be invalid (too big), in which case 0 is returned. - -static FreeByteBlock ** -find_free_list(BytePool *pool, size_t size, int *row, int *column) -{ - size2rc(pool, size, row, column); - if (*row > pool->last_row) { - return 0; - } - return &((pool->free[*row])[*column]); -} - - -// Remove an arbitrary block from its free list due to a merging operation. - -static void -unlink_free_block(BytePool *pool, ByteBlock *block) -{ - FreeByteBlock **free_list; - FreeByteBlock *free_block; - int row, column; - - free_list = find_free_list(pool, block_size(block), &row, &column); - - if (SSX_ERROR_CHECK_KERNEL) { - if (free_list == 0) { - SSX_PANIC(BYTE_POOL_NULL_FREE_LIST); - } - } - - // Unlink the block from the free list - - free_block = (FreeByteBlock *)block; - *(free_block->previous) = free_block->next; - if (free_block->next) { - free_block->next->previous = free_block->previous; - } - - // If the free list is now 0, mark the free list as empty. - - if (*free_list == 0) { - mark_empty(pool, row, column); - } -} - - -// Link a block into the head of its free list due to freeing memory - -static void -link_free_block(BytePool *pool, ByteBlock *block) -{ - FreeByteBlock **free_list; - FreeByteBlock *free_block; - int row, column; - - free_list = find_free_list(pool, block_size(block), &row, &column); - - if (SSX_ERROR_CHECK_KERNEL) { - if (free_list == 0) { - SSX_PANIC(BYTE_POOL_NULL_FREE_LIST); - } - } - - // Link the block into the free list, and mark the free list as - // non-empty. - - free_block = (FreeByteBlock *)block; - - free_block->next = *free_list; - if (*free_list) { - (*free_list)->previous = &(free_block->next); - } - *free_list = free_block; - free_block->previous = free_list; - - mark_non_empty(pool, row, column); -} - - -// Round up the block size (if required) to the next column. Note that the -// block_size input here is aligned, and remains aligned even after rounding. - -static size_t -round_up_size(BytePool *pool, size_t block_size) -{ - size_t residue, column_span, column_mask; - int row = floor_log2(block_size); - - column_span = 1 << (row - pool->log2_columns); - column_mask = column_span - 1; - residue = block_size & column_mask; - - if (residue == 0) { - return block_size; - } else { - return block_size + (column_span - residue); - } -} - - -// The implemenation of freeing a block of memory. When freed, a block is -// immediately merged with its neighbors if possible, and the final merged -// block is inserted into the proper free list. -// -// The linkage check is done here so that it can also protect internal uses of -// this API (but only if SSX errors lead to panics, the expected default). - -static int -byte_pool_free_block(BytePool *pool, ByteBlock *block) -{ - int rc; - SsxMachineContext ctx; - - rc = check_linkage(block); - if (rc) return rc; - - ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); - - pool->free_calls++; - - mark_free(pool, block); - - if (block_is_free(block->next)) { - - // Merge next block into current block - - unlink_free_block(pool, block->next); - - block->next = (ByteBlock *)((unsigned long)(block->next) + - block_size(block->next)); - block->next->previous = allocated(block); - - pool->blocks_free--; - } - - if (block_is_free(block->previous)) { - - // Merge current block into previous block - - unlink_free_block(pool, block->previous); - - block->previous->next = - (ByteBlock *)((unsigned long)(block->previous->next) + - block_size(block)); - block = block->previous; - block->next->previous = allocated(block); - - pool->blocks_free--; - } - - // Finally, insert the block into the proper free list. - - link_free_block(pool, block); - - ssx_critical_section_exit(&ctx); - - return 0; -} - - -/// Free a block of memory back to a byte pool -/// -/// \param pool A pointer to the BytePool structure that allocated the memory. -/// -/// \param memory A pointer to memory returned by byte_pool_alloc() or -/// byte_pool_alloc_aligned() for the pool. This pointer may be NULL (0), in -/// which case the byte_pool_free() request succeeds immediately. -/// -/// The part of this API that manipulates the \a pool runs as an -/// SSX_NONCRITICAL critical section. byte_pool_free() uses a constant-time -/// algorithm. -/// -/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors -/// -/// \retval 0 Success -/// -/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). -/// -/// \retval -BYTE_POOL_INVALID_ARGUMENT The block is not marked as being -/// allocated, or does not appear to have been allocated from this byte_pool. -/// -/// \retval -BYTE_POOL_LINKAGE_ERROR The block being freed is not linked -/// correctly with the other blocks managed by the pool, most likely -/// indicating that the memory being freed was not allocated by -/// byte_pool_alloc(), or that memory corruption has occured. - -// This implementation uses the convention that if the \a next pointer of the -// putative ByteBlock == 1, then this is actually an aligned allocation and -// the actual ByteBlock is located at the address contained in the \a previous -// field of the dummy header. - -int -byte_pool_free(BytePool *pool, void *memory) -{ - ByteBlock *block; - - if (memory == 0) { - return 0; - } - - block = (ByteBlock *)(((unsigned long)memory) - sizeof(ByteBlock)); - if ((int)(block->next) == 1) { - if (0) { - printk("byte_pool_free(%p, %p) [%p] : Aligned\n", - pool, memory, block); - } - block = block->previous; - } - - if (0) { - printk("byte_pool_free(%p, %p) [%p] : %d %d %d\n", - pool, memory, block, - block_is_free(block), - block < pool->first_block, - block > pool->last_block); - } - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); - SSX_ERROR_IF(block_is_free(block) || - (block < pool->first_block) || - (block > pool->last_block), - BYTE_POOL_INVALID_ARGUMENT); - } - - return byte_pool_free_block(pool, block); -} - - -/// Create a BytePool with explicit specification of tuning parameters -/// -/// This routine is the real body of byte_pool_create(), however this -/// underlying interface is provided for testing and experimentation and allows -/// the specification of non-default tuning parameters. -/// -/// There is actually only one tuning parameter for TLSF - the number of -/// columns. The number of columns must be an even power of two no larger -/// than the number of bits in an unsigned long. -/// -/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors -/// -/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool pointer was NULL (0). -/// -/// \retval -BYTE_POOL_INVALID_ARGUMENT Either the \a memory pointer was NULL -/// (0), the amount of memory was insufficient for the management overhead, or -/// the parameterization was invalid. - -int -byte_pool_create_tuned(BytePool *pool, void *memory, size_t size, - int columns) -{ - size_t overhead, free_list_overhead; - unsigned long memory_ul, aligned_memory; - int i; - FreeByteBlock **free; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); - SSX_ERROR_IF((memory == 0) || - (columns < 1) || - ((columns & (columns - 1)) != 0) || - (floor_log2(columns) > floor_log2(BITS_PER_UNSIGNED_LONG)), - BYTE_POOL_INVALID_ARGUMENT); - } - - // Compute tuning parameters - - compute_tuning(pool, size, columns); - - // Clear free list vector pointers and column status - - for (i = 0; i < BITS_PER_UNSIGNED_LONG; i++) { - pool->free[i] = 0; - pool->column_status[i] = 0; - } - - // Determine the first and last allocated rows. - - pool->first_row = floor_log2(pool->minimum_block_size); - pool->last_row = floor_log2(size); - - // The dynamic overhead consists of aligment overhead, 2 sentinel nodes, - // the vectors of pointers to free lists, plus 2 alignments. There must - // also be enough room for at least 1 block to allocate. - - memory_ul = (unsigned long)memory; - aligned_memory = align(memory_ul, 1); - - free_list_overhead = - (((pool->last_row - pool->first_row + 1) * pool->columns) * - sizeof(FreeByteBlock *)); - - overhead = - (aligned_memory - memory_ul) + - (2 * sizeof(ByteBlock)) + - free_list_overhead + - (2 * ALIGNMENT) + - pool->minimum_block_size; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(overhead >= size, BYTE_POOL_INVALID_ARGUMENT); - } - - // Allocate the overhead items. The free list vectors and column status - // arrays are carved out and zeroed. For good measure we re-align after - // each of these operations. The sentinel blocks are carved off of either - // end of the remaining free space and marked allocated. The remaining - // initial "big block" is also initialized (as if it were allocated). - - size = size - (aligned_memory - memory_ul); - size = align(size, -1); - - pool->row_status = 0; - - free = (FreeByteBlock **)aligned_memory; - memset((void *)free, 0, free_list_overhead); - - aligned_memory += free_list_overhead; - size -= free_list_overhead; - aligned_memory = align(aligned_memory, 1); - size = align(size, -1); - - for (i = pool->first_row; i <= pool->last_row; i++) { - pool->free[i] = free; - free += pool->columns; - } - - pool->first_block = (ByteBlock *)aligned_memory; - aligned_memory += sizeof(ByteBlock); - size -= sizeof(ByteBlock); - - pool->big_block = (ByteBlock *)aligned_memory; - - pool->last_block = - (ByteBlock *)(aligned_memory + size - sizeof(ByteBlock)); - size -= sizeof(ByteBlock); - - pool->first_block->next = pool->big_block; - pool->first_block->previous = 0; - mark_allocated(pool, pool->first_block); - - pool->last_block->next = 0; - pool->last_block->previous = pool->big_block; - mark_allocated(pool, pool->last_block); - - pool->big_block->previous = pool->first_block; - pool->big_block->next = pool->last_block; - - // Initialize statistics - - pool->bytes_allocated = 0; - pool->bytes_free = block_size(pool->big_block); - pool->initial_allocation = pool->bytes_free; - pool->blocks_allocated = 0; - pool->blocks_free = 1; - pool->alloc_calls = 0; - pool->free_calls = 0; - - // Free the big block and we're ready to go. - - mark_allocated(pool, pool->big_block); - byte_pool_free_block(pool, pool->big_block); - - return 0; -} - - -/// Create a BytePool -/// -/// \param pool A pointer to an uninitialized BytePool structure -/// -/// \param memory A pointer to the memory to be managed by the BytePool -/// -/// \param size The size of the managed area in bytes -/// -/// byte_pool_create() sets up the \a memory area to be used as a memory pool -/// for malloc()-style allocation using byte_pool_alloc() and -/// byte_pool_free(). Note that the actual memory area available for -/// allocation will be smaller than \a size due to alignment, and reservation -/// of a portion of the area for management overhead. -/// -/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors -/// -/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool pointer was NULL (0). -/// -/// \retval -BYTE_POOL_INVALID_ARGUMENT Either the \a memory pointer was NULL -/// (0), the amount of memory was insufficient for the management overhead, or -/// the parameterization was invalid. - -int -byte_pool_create(BytePool *pool, void *memory, size_t size) -{ - return byte_pool_create_tuned(pool, memory, size, BYTE_POOL_TLSF_COLUMNS); -} - - -/// Allocate memory from a byte pool -/// -/// \param pool A pointer to an initialized BytePool -/// -/// \param memory An address to recieve a pointer to the allocated memory. -/// This address will be set to NULL (0) if the allocation request can not be -/// satisfied (or the \a size is 0). -/// -/// \param size The number of bytes to allocate. -/// -/// The part of this API that manipulates the \a pool runs as an -/// SSX_NONCRITICAL critical section. byte_pool_alloc() uses a constant-time -/// algorithm. -/// -/// Return values other than 0 are not necessarily errors; see \ref -/// ssx_errors. -/// -/// The following return codes are not considered errors: -/// -/// \retval 0 Success -/// -/// \retval -BYTE_POOL_NO_MEMORY The allocation request could not be -/// satisfied. The memory pointer will also be NULL (0) in this case. -/// -/// The following return codes are considered errors: -/// -/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). -/// -/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memory argument is NULL (0). - -int -byte_pool_alloc(BytePool *pool, void **memory, size_t size) -{ - SsxMachineContext ctx; - size_t request_size, actual_size; - int found, row, column; - unsigned long row_status, column_status; - FreeByteBlock **free_list; - FreeByteBlock *free_block; - ByteBlock *block; - ByteBlock *residue_block; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); - SSX_ERROR_IF(memory == 0, BYTE_POOL_INVALID_ARGUMENT); - } - - // Quickly dispense with NULL requests - - if (size == 0) { - *memory = 0; - return 0; - } - - // Compute the requested block size (which includes the header). If the - // size went down we overflowed due to a huge request (which can't be - // filled). Otherwise if the request is small it is boosted up to the - // (aligned) minimum size. To guarantee fast search, the requested size - // must then be rounded up to a size that is represented in the 2-D array - // of free list pointers. - - request_size = align(size + sizeof(ByteBlock), 1); - if (request_size < size) { - *memory = 0; - return -BYTE_POOL_NO_MEMORY; - } - - if (request_size < pool->minimum_block_size) { - request_size = pool->minimum_block_size; - } - - request_size = round_up_size(pool, request_size); - - // Up to this point, all accesses of the memory pool object have been to - // read only constants. Now we get serious. - - ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); - - pool->alloc_calls++; - - // See if a block of the correct or larger size exists in the row. The - // search is first via a single bit in the row_status. If that hits then - // we check for columns >= the target column. - - found = 0; - size2rc(pool, request_size, &row, &column); - - if (pool->row_status & UL_BE_MASK(row)) { - - column_status = pool->column_status[row] & - ((UL_BE_MASK(column) << 1) - 1); - - if (column_status != 0) { - column = __builtin_clzl(column_status); - found = 1; - } - } - - // If the block was not found in the 'optimum' row, look in all rows of - // larger size and take the first block that fits. - - if (!found) { - - row_status = pool->row_status & (UL_BE_MASK(row) - 1); - - if (row_status != 0) { - row = __builtin_clzl(row_status); - column = __builtin_clzl(pool->column_status[row]); - found = 1; - } - } - - // Another out of memory case. - - if (!found) { - ssx_critical_section_exit(&ctx); - *memory = 0; - return -BYTE_POOL_NO_MEMORY; - } - - // Now we can get the pointer to the free list and take the block. - - free_list = &((pool->free[row])[column]); - - if (SSX_ERROR_CHECK_KERNEL) { - if ((free_list == 0) || (*free_list == 0)) { - SSX_PANIC(BYTE_POOL_INVALID_FREE_LIST); - } - } - - free_block = *free_list; - *free_list = free_block->next; - if (free_block->next) { - free_block->next->previous = free_list; - } else { - mark_empty(pool, row, column); - } - - // Mark the block as allocated - - block = (ByteBlock *)free_block; - mark_allocated(pool, block); - - // If there is enough residue, split the excess memory off of the end of - // the block. This is a kind of dummy transaction for our statistical - // purposes. - - actual_size = block_size(block); - if ((actual_size - request_size) >= pool->minimum_block_size) { - - residue_block = (ByteBlock *)((unsigned long)block + request_size); - - residue_block->next = block->next; - residue_block->previous = block; - residue_block->previous->next = residue_block; - residue_block->next->previous = allocated(residue_block); - - pool->blocks_allocated++; - byte_pool_free_block(pool, residue_block); - pool->free_calls--; - } - - // Set the memory pointer to the area to be used by the application and - // return. - - *memory = (void *)((unsigned long)block + sizeof(ByteBlock)); - - ssx_critical_section_exit(&ctx); - - if (0) { - ByteBlock* block = - (ByteBlock*)((unsigned long)*memory - sizeof(ByteBlock)); - - printk("byte_pool_alloc(%p, -> %p, %zu)\n" - " request_size = %u, Previous = %p, Next = %p\n", - pool, *memory, size, - request_size, block->previous, block->next); - } - - return 0; -} - - -/// Allocate memory from a byte pool and clear -/// -/// byte_pool_calloc() allocates memory using byte_pool_alloc() then clears -/// the memory area using memset(). The arguments conform to the POSIX -/// standard for calloc(). See byte_pool_alloc() for return codes and usage -/// notes. - -int -byte_pool_calloc(BytePool *pool, void **memory, size_t nmemb, size_t size) -{ - int rc; - - rc = byte_pool_alloc(pool, memory, nmemb * size); - if (rc || (*memory == 0)) { - return rc; - } - - memset(*memory, 0, nmemb * size); - - return 0; -} - - -/// Allocate an aligned memory area -/// -/// \param pool A pointer to an initialized BytePool -/// -/// \param memory An address to recieve a pointer to the allocated memory. -/// This address will be set to NULL (0) if the allocation request can not be -/// satisfied (or the \a size is 0). -/// -/// \param size The size of the memory area required (in bytes). This can be -/// any size - it \e does \e not have to be a multiple of the aligned size (as -/// is required by other common aligned memory allocators). -/// -/// \param alignment The alignment constraint, specified as the base 2 -/// logarithm of the alignment. For example, to align on a 128-byte boundary -/// the \a alignment would be specified as 7. -/// -/// byte_pool_alloc_aligned() is a convenience interface for allocating memory -/// with a guaranteed alignment. The BytePool APIs do not normally do aligned -/// allocation. byte_pool_alloc_aligned() first uses byte_pool_alloc() to -/// allocate a block of memory large enough to satisfy the request and -/// guarantee that a subset of the memory allocation will satisfy the -/// alignment constraint plus the overhead of a dummy block header. Note that -/// it is space-inefficient to allocate many small aligned areas. If possble -/// it would be better to allocate a single aligned area and then have the -/// application partition the memory as required. -/// -/// Memory areas allocated by byte_pool_alloc_aligned() can be freed with -/// byte_pool_free(), just like any other dynamic memory allocation. -/// -/// The part of this API that manipulates the \a pool runs as an -/// SSX_NONCRITICAL critical section. The underlying call of byte_pool_alloc() -/// uses a constant-time algorithm. -/// -/// Return values other than 0 are not necessarily errors; see \ref -/// ssx_errors. -/// -/// The following return codes are not considered errors: -/// -/// \retval 0 Success -/// -/// \retval -BYTE_POOL_NO_MEMORY The allocation request could not be -/// satisfied. The memory pointer will also be NULL (0) in this case. -/// -/// The following return codes are considered errors: -/// -/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). -/// -/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memory argument is NULL (0), or -/// the \a alignment argument is invalid. - -// The allocation must be big enough for the size requested + the alignment -// amount (to guarantee alignment) + room for a dummy ByteBlock. The dummy -// ByteBlock is marked by setting the \a next pointer to 1 to indicate that -// this is an aligned allocation. In this case the \a previous pointer of the -// dummy ByteBlock points to the ByteBlock of the original allocation. - -int -byte_pool_alloc_aligned(BytePool *pool, void **memory, size_t size, - int alignment) -{ - int rc; - unsigned long pow2_alignment, mask, aligned; - void *unaligned_memory; - ByteBlock *dummy_block, *unaligned_block; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF((alignment < 1) || (alignment >= BITS_PER_UNSIGNED_LONG), - BYTE_POOL_INVALID_ARGUMENT); - } - - pow2_alignment = (unsigned long)1 << (unsigned long)alignment; - mask = pow2_alignment - 1; - - rc = byte_pool_alloc(pool, &unaligned_memory, - size + pow2_alignment + sizeof(ByteBlock)); - - if (rc || (unaligned_memory == 0)) { - *memory = 0; - return rc; - } - unaligned_block = (ByteBlock *)(((unsigned long)unaligned_memory) - - sizeof(ByteBlock)); - - aligned = (unsigned long)unaligned_memory + sizeof(ByteBlock); - if (aligned & mask) { - aligned += (pow2_alignment - (aligned & mask)); - } - *memory = (void *)aligned; - - dummy_block = (ByteBlock *)(aligned - sizeof(ByteBlock)); - dummy_block->previous = unaligned_block; - dummy_block->next = (ByteBlock*)1; - - if (0) { - printk("byte_pool_alloc_aligned(%p, -> %p, %zu, %d)\n", - pool, *memory, size, alignment); - } - - return 0; -} - - -/// Allocate aligned memory from a byte pool and clear -/// -/// byte_pool_calloc_alligned() allocates memory using -/// byte_pool_alloc_aligned() then clears the memory area using memset(). The -/// arguments conform to the POSIX standard for calloc(). See -/// byte_pool_alloc_aligned() for return codes and usage notes. In particular -/// note that this memory must be freed with byte_pool_free_aligned(). - -int -byte_pool_calloc_aligned(BytePool *pool, void **memory, - size_t nmemb, size_t size, int alignment) -{ - int rc; - - rc = byte_pool_alloc_aligned(pool, memory, nmemb * size, alignment); - if (rc || (*memory == 0)) return rc; - - memset(*memory, 0, nmemb * size); - - return 0; -} - - -/// malloc() allocates \a size bytes and returns a pointer to the allocated -/// memory. The memory is not cleared. The value returned is a pointer to the -/// allocated memory, which is suitably aligned for any kind of variable, or -/// NULL if the requested \a size is 0 or the request fails. -/// -/// NB: The aplication must create and assign a BytePool object to the -/// library variable _malloc_byte_pool in order for malloc() to work. - -void * -malloc(size_t size) -{ - void *memory; - - if (byte_pool_alloc(_malloc_byte_pool, &memory, size)) { - memory = 0; - } - return memory; -} - - -/// calloc() allocates memory for an array of \a nmemb elements of \a size -/// bytes each and returns a pointer to the allocated memory. The memory is -/// set to zero. The value returned is a pointer to the allocated and cleared -/// memory, which is suitably aligned for any kind of variable, or NULL if the -/// requested \a size is 0 or the request fails. -/// -/// NB: The aplication must create and assign a BytePool object to the -/// library variable _malloc_byte_pool in order for calloc() to work. - -void * -calloc(size_t nmemb, size_t size) -{ - void *memory; - - if (byte_pool_calloc(_malloc_byte_pool, &memory, nmemb, size)) { - return 0; - } - return memory; -} - - -/// free() frees the memory space pointed to by \a ptr, which must have been -/// returned by a previous call to malloc(), posix_memalign, calloc() or -/// realloc(). Otherwise, or if free(ptr) has already been called before, -/// undefined behavior occurs. If \a ptr is NULL, no operation is performed. -/// -/// NB: The aplication must create and assign a BytePool object to the -/// library variable _malloc_byte_pool in order for free() to work. - -void -free(void *ptr) -{ - byte_pool_free(_malloc_byte_pool, ptr); -} - - -/// realloc() changes the size of the memory block pointed to by \a ptr to \a -/// size bytes. The contents will be unchanged to the minimum of the old and -/// new sizes; newly allocated memory will be uninitialized. If \a ptr is -/// NULL, the call is equivalent to malloc(size); if \a size is equal to zero, -/// the call is equivalent to free(ptr). Unless \a ptr is NULL, it must have -/// been returned by an earlier call to malloc(), calloc() or realloc(). If -/// the area pointed to was moved, a free(ptr) is done. -/// -/// realloc() returns a pointer to the newly allocated memory, which is -/// suitably aligned for any kind of variable and may be different from \a -/// ptr, or NULL if the request fails. If \a size was equal to 0, either NULL -/// or a pointer suitable to be passed to free() is returned. If realloc() -/// fails the original block is left untouched; it is not freed or moved. - -void* -realloc(void *ptr, size_t size) -{ - void *memory; - size_t useful_size; - - // Handle simple case - - if (ptr == 0) { - - memory = malloc(size); - - } else if (size == 0) { - - free(ptr); - memory = 0; - - } else { - - // Find out the useful size of the block. If we need more than this we - // need to allocate a new block and memcpy() the old data to the new - // block and free the old block. If we need less then this we also - // need to allocate a new block and move the head of the current - // data. If the new size is the same as the current size we do nothing. - - byte_pool_block_info(ptr, 0, 0, &useful_size); - - if (size == useful_size) { - - memory = ptr; - - } else { - - memory = malloc(size); - if (memory != 0) { - memcpy(memory, ptr, (size > useful_size) ? useful_size : size); - free(ptr); - } - } - } - return memory; -} - - -/// The posix_memalign() function allocates \a size bytes aligned on a -/// boundary specified by \a alignment, and returns a pointer to the allocated -/// memory in \a memptr. The value of \a alignment shall be a multiple of -/// sizeof(void*), that is also a power of two. Upon successful completion, -/// the value pointed to by \a memptr will be a multiple of alignment. -/// -/// Note that memory allocated with posix_memalign() can be freed with -/// free(). -/// -/// In the event of errors, the contents of \a memptr will be returned as 0. -/// -/// The following return codes are mandated by POSIX, and are always returned -/// in the event of the specified condition. -/// -/// \retval 0 Success -/// -/// \retval -EINVAL The value of the \a alignment parameter is not a power of -/// two multiple of sizeof(void*). -/// -/// \retval -ENOMEM There is insufficient memory available with the requested -/// alignment. -/// -/// The following return codes are implementation-specific and may be -/// configured to cause a kernel panic. -/// -/// \retval -BYTE_POOL_INVALID_OBJECT The \a _malloc_byte_pool is NULL (0). -/// -/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memptr argument is NULL (0). - -int -posix_memalign(void** memptr, size_t alignment, size_t size) -{ - int rc; - - if (((alignment & (alignment - 1)) != 0) || - (alignment < sizeof(void*))) { - rc = -EINVAL; - } else { - rc = byte_pool_alloc_aligned(_malloc_byte_pool, memptr, size, - floor_log2(alignment)); - if (!rc && (*memptr == 0)) { - rc = -ENOMEM; - } - } - if (rc && memptr) { - *memptr = 0; - } - return rc; -} - - -/// Print a dump of a byte pool, including the header and allocation report -/// -/// \param stream The stream to receive the dump -/// -/// \param pool The BytePool object to dump -/// -/// \bug This routine is not thread safe. - -void -byte_pool_report(FILE* stream, BytePool* pool) -{ - ByteBlock* block; - uint8_t* p8; - uint32_t* p32; - int i; - - fprintf(stream, ">>> Byte Pool Report for Pool %p <<<\n", pool); - - fprintf(stream, ">>> BytePool Object Dump <<<\n"); - -#define DUMPIT(x, fmt) \ - fprintf(stream, "%20s : " #fmt "\n", #x, pool->x) - - DUMPIT(first_row, %d); - DUMPIT(last_row, %d); - DUMPIT(columns, %d); - DUMPIT(log2_columns, %d); - DUMPIT(column_mask, 0x%08x); - DUMPIT(minimum_block_size, %d); - DUMPIT(free, %p); - DUMPIT(column_status, %p); - DUMPIT(row_status, %lu); - DUMPIT(first_block, %p); - DUMPIT(big_block, %p); - DUMPIT(last_block, %p); - DUMPIT(initial_allocation, %d); - DUMPIT(bytes_allocated, %d); - DUMPIT(bytes_free, %d); - DUMPIT(blocks_allocated, %d); - DUMPIT(blocks_free, %d); - DUMPIT(alloc_calls, %d); - DUMPIT(free_calls, %d); - - fprintf(stream, ">>> Byte Pool Allocation Report <<<\n"); - fprintf(stream, - ">>> status : address : size : binary[0:7] : ASCII[0:7] <<<\n"); - - for (block = pool->first_block->next; - block != pool->last_block; - block = block->next) { - - fprintf(stream, " %c : %p : %6zu : ", - (block_is_free(block) ? 'F' : 'A'), - block, block_size(block)); - - p8 = (uint8_t*)((unsigned long)block + sizeof(ByteBlock)); - p32 = (uint32_t*)p8; - - fprintf(stream, "0x%08x 0x%08x : ", p32[0], p32[1]); - for (i = 0; i < 8; i++) { - if (isprint(p8[i])) { - fputc(p8[i], stream); - } else { - fputc('.', stream); - } - } - fputc('\n', stream); - } - - fprintf(stream, ">>> End Report <<<\n"); -} diff --git a/src/lib/ppc405lib/byte_pool.h b/src/lib/ppc405lib/byte_pool.h deleted file mode 100644 index 8766745..0000000 --- a/src/lib/ppc405lib/byte_pool.h +++ /dev/null @@ -1,166 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/byte_pool.h $ */ -/* */ -/* 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 */ -#ifndef __BYTE_POOL_H__ -#define __BYTE_POOL_H__ - -// $Id$ - -#ifndef __ASSEMBLER__ - -#include "ssx_io.h" - -struct ByteBlock; -struct FreeByteBlock; - -// A handy constant - -#define BITS_PER_UNSIGNED_LONG (8 * sizeof(unsigned long)) - - -/// A control structure for a byte pool. The application will never modify the -/// structure fields directly, but some applications may be interested in -/// reading the statistics. - -typedef struct { - - // The index of the first allocated row of free list pointers - int first_row; - - // The index of the last allocated row of free list pointers - int last_row; - - // The number of columns in each row of free list pointers - int columns; - - // The log2 of the number of columns in each row of free list pointers - int log2_columns; - - // The shifted block size is ANDed with this mask to extract the column - // number. - size_t column_mask; - - // The minimum block size. - int minimum_block_size; - - // The vectors of free list pointers - struct FreeByteBlock **free[BITS_PER_UNSIGNED_LONG]; - - // The array of column status bit masks - unsigned long column_status[BITS_PER_UNSIGNED_LONG]; - - // The row status bit mask - unsigned long row_status; - - // A sentinel node - the first allocated block. Kept for error checking - // purposes. - struct ByteBlock *first_block; - - // The initial memory allocation. Kept here only for debugging purposes. - struct ByteBlock *big_block; - - // A sentinel node - the last allocated block. Kept here for error - // checking purposes. - struct ByteBlock *last_block; - - // The initial allocation. Kept here for debugging and statistics. - size_t initial_allocation; - - // The total number of bytes currently allocated (excludes overhead) - size_t bytes_allocated; - - // The total number of bytes currently free in the pool - size_t bytes_free; - - // The total number of blocks allocated from the pool - size_t blocks_allocated; - - // The total number of blocks free in the pool - size_t blocks_free; - - // The number of calls to allocate memory - size_t alloc_calls; - - // The number of calls to free memory - size_t free_calls; - -} BytePool; - -extern BytePool *_malloc_byte_pool; - -int -byte_pool_create(BytePool *pool, void *memory, size_t size); - -int -byte_pool_create_tuned(BytePool *pool, void *memory, size_t size, - int columns); - -int -byte_pool_alloc(BytePool *pool, void **memory, size_t size); - -int -byte_pool_calloc(BytePool *pool, void **memory, size_t nmemb, size_t size); - -int -byte_pool_free(BytePool *pool, void *memory); - -void -byte_pool_block_info(void* memory, - void** actual_address, size_t* actual_size, - size_t* useful_size); - -int -byte_pool_alloc_aligned(BytePool *pool, void **memory, size_t size, - int alignment); - -void * -malloc(size_t size); - -void * -calloc(size_t nmemb, size_t size); - -void -free(void *ptr); - -int -posix_memalign(void** memptr, size_t alignment, size_t size); - -void -byte_pool_report(FILE* stream, BytePool* pool); - -#endif /* __ASSEMBLER__ */ - - -// Error/panic codes - -#define BYTE_POOL_INVALID_OBJECT 0x00b98e01 -#define BYTE_POOL_INVALID_ARGUMENT 0x00b98e02 -#define BYTE_POOL_REVERSE_LINKAGE 0x00b98e03 -#define BYTE_POOL_FORWARD_LINKAGE 0x00b98e04 -#define BYTE_POOL_NO_MEMORY 0x00b98e05 - -#define BYTE_POOL_NULL_FREE_LIST 0x00b98e10 -#define BYTE_POOL_INVALID_FREE_LIST 0x00b98e11 - -#endif /* __BYTE_POOL_H__ */ diff --git a/src/lib/ppc405lib/initcall.c b/src/lib/ppc405lib/initcall.c deleted file mode 100644 index 664fb6e..0000000 --- a/src/lib/ppc405lib/initcall.c +++ /dev/null @@ -1,70 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/initcall.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 initcall.c -/// \brief An anonymous early initialization facility for SSX applications - -#include "ssx.h" -#include "initcall.h" - -// These linker symbols must be defined if the initcall facility is used. The -// special ELF section .data.initcall contains an array of Initcall structures -// for all declared initcalls. - -extern InitCall _INITCALL_SECTION_BASE[]; -extern SsxLinkerSymbol _INITCALL_SECTION_SIZE; - -void -_initcall_run(InitCall* initcall) -{ - void (*f)(void* arg); - - f = initcall->initcall; - if (f) { - initcall->initcall = 0; - f(initcall->arg); - } -} - - -void -initcall_run_all() -{ - InitCall* initcall; - size_t nCalls; - - initcall = _INITCALL_SECTION_BASE; - nCalls = (size_t)(&_INITCALL_SECTION_SIZE) / sizeof(InitCall); - - for (; nCalls--; initcall++) { - _initcall_run(initcall); - } -} - - - - - diff --git a/src/lib/ppc405lib/initcall.h b/src/lib/ppc405lib/initcall.h deleted file mode 100644 index af8b53c..0000000 --- a/src/lib/ppc405lib/initcall.h +++ /dev/null @@ -1,116 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/initcall.h $ */ -/* */ -/* 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 */ -#ifndef __INITCALL_H__ -#define __INITCALL_H__ - -// $Id$ - -/// \file initcall.h -/// \brief An early initialization facility for SSX applications -/// -/// The C language standard does not define a generic load-time initialization -/// method, unlike C++ which supports load-time initialization of static -/// objects. The \e initcall facility implements a simple method for SSX -/// applications to declare early initialization functions that are executed -/// prior to or during the invocation of main(). -/// -/// An \e initcall can be any function with the prototype -/// -/// \code -/// -/// void (*initcall)(void* arg) -/// -/// \endcode -/// -/// Initcalls are declared with the INITCALL() macro. An initcall is -/// represented by a named structure, and typically an initcall will be -/// declared static to the compilation unit that implements the initcall: -/// -/// \code -/// -/// void (*init_fn)(void* arg); -/// void* init_data = ...; -/// static INITCALL(init_var, init_fn, init_data); -/// -/// \endcode -/// -/// All INITCALLS loaded in the executable image are executed by the -/// initcall_run_all() API. An SSX application will typically call -/// initcall_run_all() in the function declared as the \a ssx_main_hook, or in -/// the main() routine itself. -/// -/// Initcalls are run in an arbitrary order. However if initcall \a b is -/// dependent on initcall \a a, then initcall \a b can execute -/// initcall_run(&a) to guarantee that initcall \a a runs before \a b. -/// Regardless, every initcall is run exectly once by the initcall facility, -/// even if initcall_run() or initcall_run_all() were to be used multiple -/// times. -/// -/// Behind the scenes, initcalls are implemented by a special ELF section, -/// .data.initcall, that records all declared initcalls. The -/// initcall_run_all() API simply runs all initcalls declared in -/// .data.initcall. - -/// The structure representing an initcall - -typedef struct { - - /// The initialization function - /// - /// Prior to running the initcall, this field is zeroed. This guarantess - /// that each initcall is run at most 1 time. - void (*initcall)(void* arg); - - /// The argument to the initialization function - void* arg; - -} InitCall; - - -/// Declare an initcall -/// -/// This macro generates C code and global data so must be placed at file -/// scope in a C file, not in a header file or inside a C function -/// body. Unless the initcall needs to be referenced by another initcall (to -/// guarantee ordering), this declaration will normally be prepended with -/// 'static'. -#define INITCALL(_var, _initcall, _arg) \ - InitCall _var __attribute__ ((used, section (".data.initcall"))) = \ - {.initcall = _initcall, .arg = _arg}; - - -/// Run the initcall represented by an InitCall structure, assuming it has not -/// already run. -/// -/// \param[in] i_initcall The address of the initcall structure to run -void -initcall_run(InitCall* i_initcall); - - -/// Run all initcalls -void -initcall_run_all(); - -#endif // __INITCALL_H__ diff --git a/src/lib/ppc405lib/lfsr.c b/src/lib/ppc405lib/lfsr.c deleted file mode 100644 index 21ddb93..0000000 --- a/src/lib/ppc405lib/lfsr.c +++ /dev/null @@ -1,50 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/lfsr.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 lfsr.c -/// \brief - -#include <stdint.h> -#include "lfsr.h" - -// Parity for 4-bit numbers -static uint8_t S_parity4[16] = { - 0, 1, 1, 0, - 1, 0, 0, 1, - 1, 0, 0, 1, - 0, 1, 1, 0 -}; - -// 64, 63, 61, 60 LFSR. The routine is coded with the uint8_t casting to help -// the compiler generate more efficient code. - -void -_lfsr64(uint64_t* io_seed) -{ - *io_seed = (*io_seed << 1) | - S_parity4[(uint8_t)((*io_seed >> 59) & 0x3) | - (uint8_t)((*io_seed >> 60) & 0xc)]; -} diff --git a/src/lib/ppc405lib/lfsr.h b/src/lib/ppc405lib/lfsr.h deleted file mode 100644 index 951ec45..0000000 --- a/src/lib/ppc405lib/lfsr.h +++ /dev/null @@ -1,46 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/lfsr.h $ */ -/* */ -/* 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 */ -#ifndef __LFSR_H__ -#define __LFSR_H__ - -/// \file lfsr.h -/// \brief Linear-Feedback Shift Register Implementations -/// -/// The 32- and 64-bit pseudo-random number generators in this library are of -/// the linear-conguential type. These maximal-length LFSR pseudo-random -/// sequence generators are also provided. - -/// 64-bit LFSR -/// -/// \param[in,out] io_seed The input seed is converted in one step to the -/// output seed. -/// -/// This 64-bit LFSR uses taps 64, 63, 61, and 60. In big-endian numbering -/// these are bits 0, 1, 3 and 4. This LFSR is also implemented for the PORE -/// engines in the file pore_rand.pS. -void -_lfsr64(uint64_t* io_seed); - -#endif // __LFSR_H__ diff --git a/src/lib/ppc405lib/libppc405files.mk b/src/lib/ppc405lib/libppc405files.mk index 59aa7a0..b042616 100644 --- a/src/lib/ppc405lib/libppc405files.mk +++ b/src/lib/ppc405lib/libppc405files.mk @@ -5,7 +5,7 @@ # # OpenPOWER OnChipController Project # -# Contributors Listed Below - COPYRIGHT 2015 +# Contributors Listed Below - COPYRIGHT 2015,2016 # [+] International Business Machines Corp. # # @@ -43,19 +43,12 @@ C-SOURCES = \ assert.c \ - byte_pool.c \ ctype.c \ ctype_table.c \ fgetc.c \ - initcall.c \ - lfsr.c \ - mutex.c \ - periodic_semaphore.c \ polling.c \ printf.c \ - progress.c \ puts.c \ - rtx_stdio.c \ simics_stdio.c \ sprintf.c \ ssx_dump.c \ @@ -66,7 +59,6 @@ C-SOURCES = \ string.c \ string_stream.c \ strtox.c \ - sxlock.c \ time.c S-SOURCES = diff --git a/src/lib/ppc405lib/mutex.c b/src/lib/ppc405lib/mutex.c deleted file mode 100644 index db57f54..0000000 --- a/src/lib/ppc405lib/mutex.c +++ /dev/null @@ -1,129 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/mutex.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 mutex.c -/// \brief A ThreadX-style mutual exclusion object - -#include "mutex.h" - -int -mutex_create(Mutex* i_mutex) -{ - int rc; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); - } - - rc = ssx_semaphore_create(&(i_mutex->sem), 1, 1); - i_mutex->thread = 0; - i_mutex->count = 0; - - return rc; -} - - -// If the current thread owns the Mutex we simply increment the count, -// otherwise pend for the semaphore. -// -// Note: It's possible this doesn't need to be done in a critical section. The -// fact that ssx_semaphore_pend() is atomic may be sufficient since it locks -// the Mutex. - -int -mutex_pend(Mutex* i_mutex, SsxInterval i_timeout) -{ - int rc; - SsxMachineContext ctx; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); - SSX_ERROR_UNLESS_THREAD_CONTEXT(); - } - - ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); - - if (i_mutex->thread == ssx_current()) { - - i_mutex->count++; - if (i_mutex->count == 0) { - rc = MUTEX_OVERFLOW; - } else { - rc = 0; - } - - } else { - - rc = ssx_semaphore_pend(&(i_mutex->sem), i_timeout); - if (rc == 0) { - i_mutex->thread = ssx_current(); - i_mutex->count = 1; - } - } - - ssx_critical_section_exit(&ctx); - - return rc; -} - - -// If the current thread owns the Mutex we decrement the count and free the -// object when the count goes to 0. -// -// Note: It's possible this doesn't need to be done in a critical section. The -// fact that ssx_semaphore_pend() is atomic may be sufficient since it locks -// the Mutex. - -int -mutex_post(Mutex* i_mutex) -{ - int rc; - SsxMachineContext ctx; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); - SSX_ERROR_UNLESS_THREAD_CONTEXT(); - } - - ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); - - SSX_ERROR_IF(i_mutex->thread != ssx_current(), MUTEX_NOT_OWNED); - - if (--i_mutex->count == 0) { - i_mutex->thread = 0; - rc = ssx_semaphore_post(&(i_mutex->sem)); - } else { - rc = 0; - } - - ssx_critical_section_exit(&ctx); - - return rc; -} - - - - diff --git a/src/lib/ppc405lib/mutex.h b/src/lib/ppc405lib/mutex.h deleted file mode 100644 index 6d3a352..0000000 --- a/src/lib/ppc405lib/mutex.h +++ /dev/null @@ -1,164 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/mutex.h $ */ -/* */ -/* 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 */ -#ifndef __MUTEX_H__ -#define __MUTEX_H__ - -// $Id$ - -/// \file mutex.h -/// \brief A ThreadX-style mutual exclusion object -/// -/// A Mutex is a binary semaphore with the concept of thread ownership. A -/// thread first obtains the Mutex using the mutex_pend() API, which may block -/// if the Mutex is currently owned by another thread. Once a thread owns a -/// Mutex, subsequent calls of mutex_pend() by the same thread simply -/// increment an internal counter, but do not block. Once a thread has -/// executed a matching mutex_post() call for every mutex_pend() call, the -/// Mutex is free for another thread. -/// -/// This type of mutual exclusion object is useful for example to control -/// access to data structures that are manipulated by APIs with several common -/// entry points. Each call of an API in the chain will 'lock' the data -/// structure using mutex_pend()/mutex_post(). The Mutex semantics allows -/// multiple "locks" by the same thread, but requires a corresponding "unlock" -/// for every "lock". -/// -/// The Mutex usage counter is a 32-bit unsigned integer. If a thread makes -/// 2^32 calls to mutex_pend() without an intervening call of mutex_post(), an -/// overflow is signalled. This error should be considered unrecoverable to -/// the application. -/// -/// Like the SSX semaphore, no record is kept in the thread of which Mutex -/// objects are currently owned by the thread. If a thread terminates or is -/// deleted while holding a Mutex it is likely that the application will -/// hang. Unlike the SSX semaphore, it is absolutely illegal to call -/// mutex_pend() and mutex_post() from interrupt contexts. It is also illegal -/// for a thread to call mutex_post() for a mutex it does not own. -/// -/// Mutex objects are easily created with the static initialization macro -/// MUTEX_INITIALIZATION as in the following example. -/// -/// \code -/// -/// Mutex G_mutex = MUTEX_INITIALIZATION; -/// -/// \endcode -/// -/// The API mutex_create() is also provided for run-time initialization. - -#include "ssx.h" - - -// Mutex error/panic codes - -#define MUTEX_OVERFLOW 0x00688901 -#define MUTEX_NOT_OWNED 0x00688902 - - -#ifndef __ASSEMBLER__ - -/// Static initialization of a Mutex -/// -/// For a full description of the Mutex please see the documentation fof the -/// file mutex.h. -#define MUTEX_INITIALIZATION {SSX_SEMAPHORE_INITIALIZATION(1, 1), 0, 0} - - -/// The Mutex object - -typedef struct { - - /// The binary semaphore - SsxSemaphore sem; - - /// A pointer to the owning thread, or NULL (0) - SsxThread* thread; - - /// The count of unmatched mutex_pend() calls made by the owning thread. - uint32_t count; - -} Mutex; - - -/// Create (initialize) a Mutex -/// -/// \param[in] i_mutex A pointer to the Mutex object to initialize. -/// -/// For a full description of the Mutex please see the documentation for the -/// file mutex.h. -/// -/// \retval 0 Success -/// -/// \retval 0 -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). -int -mutex_create(Mutex* i_mutex); - - -/// Pend on a Mutex with optional timeout -/// -/// \param[in] i_mutex A pointer to the Mutex -/// -/// \param[in] i_timeout Either the constant SSX_WAIT_FOREVER, or a timeout -/// interval specification. -/// -/// For a full description of the Mutex please see the documentation for the -/// file mutex.h. -/// -/// \retval 0 Success -/// -/// \retval -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). -/// -/// \retval -SSX_ILLEGAL_CONTEXT The call was not made from a thread context. -/// -/// \retval -SSX_SEMEPHORE_PEND_TIMED_OUT The thread was not able to obtain -/// the Mutex before the timeout. -/// -/// \retval -MUTEX_OVERFLOW The owning thread has made 2^32 unmatched calls of -/// mutex_pend(). -int -mutex_pend(Mutex* i_mutex, SsxInterval i_timeout); - - -/// Post to a Mutex -/// -/// \param[in] i_mutex A pointer to the Mutex -/// -/// For a full description of the Mutex please see the documentation for the -/// file mutex.h. -/// -/// \retval 0 Success -/// -/// \retval -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). -/// -/// \retval -SSX_ILLEGAL_CONTEXT The call was not made from a thread context. -/// -/// \retval -MUTEX_NOT_OWNED The thread calling mutex_post() does not own the -/// Mutex. -int -mutex_post(Mutex* i_mutex); - -#endif // __ASSEMBLER__ - -#endif // __MUTEX_H__ diff --git a/src/lib/ppc405lib/periodic_semaphore.c b/src/lib/ppc405lib/periodic_semaphore.c deleted file mode 100644 index dec9ea2..0000000 --- a/src/lib/ppc405lib/periodic_semaphore.c +++ /dev/null @@ -1,114 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/periodic_semaphore.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 perodic_semaphore.h -/// \brief Periodic semphores - -#include "ssx.h" -#include "periodic_semaphore.h" - -// The timer callback is created nonpreemptible, so noncritical interrupts are -// disabled at entry. - -static void -_periodic_semaphore_timeout(void* arg) -{ - PeriodicSemaphore* ps; - - ps = (PeriodicSemaphore*)arg; - - if (ps->sem.count != 1) { - ssx_semaphore_post(&(ps->sem)); - } -} - - -int -periodic_semaphore_create(PeriodicSemaphore* sem, SsxInterval period) -{ - int rc; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); - } - - do { - rc = ssx_semaphore_create(&(sem->sem), 0, 1); - if (rc) break; - - rc = ssx_timer_create_nonpreemptible(&(sem->timer), - _periodic_semaphore_timeout, - sem); - if (rc) break; - - rc = ssx_timer_schedule(&(sem->timer), - period, - period); - if (rc) break; - - } while (0); - - return rc; -} - - -int -periodic_semaphore_pend(PeriodicSemaphore* sem) -{ - int rc; - SsxMachineContext ctx; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); - } - - ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); - - if (sem->sem.count == 0) { - - rc = ssx_semaphore_pend(&(sem->sem), SSX_WAIT_FOREVER); - - } else { - - sem->sem.count = 0; - rc = -PERIODIC_SEMAPHORE_OVERRUN; - } - - ssx_critical_section_exit(&ctx); - - return rc; -} - - -int -periodic_semaphore_cancel(PeriodicSemaphore* sem) -{ - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); - } - - return ssx_timer_cancel(&(sem->timer)); -} diff --git a/src/lib/ppc405lib/periodic_semaphore.h b/src/lib/ppc405lib/periodic_semaphore.h deleted file mode 100644 index c3c2e53..0000000 --- a/src/lib/ppc405lib/periodic_semaphore.h +++ /dev/null @@ -1,152 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/periodic_semaphore.h $ */ -/* */ -/* 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 */ -#ifndef __PERIODIC_SEMAPHORE_H__ -#define __PERIODIC_SEMAPHORE_H__ - -// $Id$ - -/// \file perodic_semaphore.h -/// \brief Periodic semphores -/// -/// The PeriodicSemphore is a simple abstraction introduced to simplify coding -/// peridic threads. A periodic thread creates the PeriodicSemaphore after -/// thread initialization, but prior to the entry to the periodic infinite -/// loop. This creates the periodicSemaphore with a count of 0. Once thread -/// processing is finished, the thread pends on the PeriodicSemaphore. A -/// periodic timer posts to the PeriodicSemaphore on a fixed, absolute period -/// to reschedule the thread. -/// -/// If the thread pends on the PeriodicSemaphore and the timer has already -/// posted to the semaphore, the call of periodic_semaphore_pend() clears the -/// semaphore and terminates immediately with the return code -/// -PERIODIC_SEMAPHORE_OVERRUN, indicating that the thread has overrun its -/// period. The thread can choose the appropriate action upon obtaining this -/// return code. -/// -/// The PeriodicSemaphore can also be cancelled, which simply cancels the -/// periodic timer posting to the semaphore. If the thread needs to -/// re-initialize the PeriodicSemaphore for any reason (e.g, to resynchronize -/// after an overrun) it must be cancelled first. - -// Error/Panic codes - -#define PERIODIC_SEMAPHORE_OVERRUN 0x0077e601 - - -/// A periodic semaphore - -typedef struct { - - /// The semaphore - SsxSemaphore sem; - - /// The timer - SsxTimer timer; - -} PeriodicSemaphore; - - -/// Create (initialize) a PeriodicSemaphore -/// -/// \param sem A pointer to an uninitialized or inactive -/// PeriodicSemaphore. -/// -/// \param period The semaphore period -/// -/// This API creates the embedded semaphore as a binary semaphore with an -/// initial value of 0, and schedules a periodic timer to post to the -/// semaphore. -/// -/// \retval 0 Success -/// -/// \retval -SSX_INVALID_OBJECT The \a sem was NULL (0) or otherwise invalid. -/// -/// Other return codes are possible from the embedded calls of SSX APIs. -int -periodic_semaphore_create(PeriodicSemaphore* sem, SsxInterval period); - - -/// Pend on a PeriodicSemaphore -/// -/// \param sem A pointer to an initialized PeriodicSemaphore -/// -/// Pend on a PeriodicSemaphore. It is considered a non-fatal error if the -/// semaphore has a non-0 count as this may indicate that a periodic thread -/// has missed a deadline. -/// -/// 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 Success. In particular, the semaphore count was 0 at entry. -/// -/// \retval -PERIODIC_SEMAPHORE_OVERRUN This return code indicates that the -/// semaphore count was 1 at entry. This code is always returned (never causes -/// a panic). -/// -/// The following return codes are error codes: -/// -/// \retval -SSX_IVALID_OBJECT The \a sem was NULL () or otherwise invalid at -/// entry. -/// -/// Other error return codes are possible from embedded calls of SSX APIs. -int -periodic_semaphore_pend(PeriodicSemaphore* sem); - - -/// Cancel a periodic semaphore -/// -/// \param sem A pointer to an initialized PeriodicSemaphore -/// -/// Cancel the PeriodicSemaphore timeout. This is a required step if the -/// PeriodicSemaphore is to be reinitialized. This is also required if the -/// PeriodicSemaphore is created on the thread stack and the thread -/// terminates. PeriodicSemaphore can be canceled at any time. It is never -/// an error to call periodic_semaphore_cancel() on a PeriodicSemaphore object -/// after it is created. -/// -/// 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 -SSX_TIMER_NOT_ACTIVE The embedded timer is not currently -/// scheduled, meaning that the PeriodicSemaphore was previosly -/// cancelled. -/// -/// The following return codes are error codes: -/// -/// \retval -SSX_IVALID_OBJECT The \a sem was NULL () or otherwise invalid at -/// entry. -/// -/// Other error return codes are possible from embedded calls of SSX APIs. -int -periodic_semaphore_cancel(PeriodicSemaphore* sem); - - -#endif // __PERIODIC_SEMAPHORE_H__ diff --git a/src/lib/ppc405lib/progress.c b/src/lib/ppc405lib/progress.c deleted file mode 100644 index 8d2cd30..0000000 --- a/src/lib/ppc405lib/progress.c +++ /dev/null @@ -1,743 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/progress.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 progress.c -/// \brief Programmable progress (hang) checking -/// -/// This is a simple implementation of a progress (hang) checking -/// facility. The application provides an array of \e pass \e counts that are -/// expected to update/count over time. For simplicity and generality all -/// pass counts are defined to be \c uint64_t types. The -/// progress_checker_create() API initializes the checker, and as a -/// convenience clears the pass counts. -/// -/// The checker can be created with an optional \a callback function, which -/// has the prototype -/// -/// \code -/// typedef int (*ProgressCheckerCallback)(struct ProgressChecker *checker, -/// void* arg, -/// size_t failed) -/// \endcode -/// -/// The checker callback is called \e every time a check is made by -/// progress_checker_check(). In addition to a private void* argument, the -/// parameter list of the callback includes a count of the number of counters -/// that failed to make progress - this count will be 0 if the check was -/// successful. The return value of the callback is passed back as the return -/// value of progress_checker_check(). If the callback is specified as NULL -/// (0), then a successful check returns 0, and any failure causes a return -/// code of -PROGRESS_CHECKER_FAILED. -/// -/// The application can dynamically mark counters as either \e exempt or \e -/// required. By default all counts are required to have increased -/// each time a check is made for progress. Counters marked \e exempt when a -/// progress check is made are not checked for progress. -/// -/// The application can also use the progress_checker_schedule() API to -/// schedule either one-shot or periodic checks. The -/// progress_checker_cancel() API can be used to cancel any scheduled -/// checks. It is never an error to call this API, even if no checks are -/// currently scheduled. Note that each call of progress_checker_schedule() -/// also cancels any outstanding scheduled requests before (re-) scheduling -/// the checker. If using the built-in timer mechanism, any calls of -/// progress_checker_check that return a non-0 value will cause a kernel panic. -/// -/// If failures are detected and caught, the ProgressChecker provides a -/// primitive iteration facility for the callback or the applicaton to -/// determine which counters have failed to update. Calling -/// progress_checker_next_failure() returns either the index of the next -/// failing counter, or -1 to indicate no more failures. This iteration -/// facility is reset every time a check is made by progress_checker_check() -/// (including those made implcitly by the timer-based mechanism). There is no -/// API to reset the iteration. -/// -/// The implemetation provides 2 standard callback functions: -/// progess_checker_printk() and progress_checker_printk_dump(). The former -/// callback uses printk() to print a simple report of failed counters, and if -/// there were any failures it then returns its argument as a return code. If -/// the return code is non-zero then the lack of progress will cause a kernel -/// panic (test failure). The later callback first calls -/// progress_checker_printk(). If progress_checker_printk() returns a non-0 -/// value then progress_checker_printk_dump() enters an SSX_CRITICAL crictal -/// section and prints a full kernel state dump that may be useful to help -/// diagnose the hang. -/// -/// \note We do not make the kernel dump the default or only behavior because -/// it could take 1ms or more to produce the large quantity of formatted -/// output required, which could be a significant amount of wall time in a -/// logic simulation environment. -/// -/// The progress_checker_create() API could be used in a couple of ways as -/// illustrated below: -/// -/// \code -/// -/// ProgressChecker progress; -/// uint64_t counter; -/// -/// progress_checker_create(&progress, "progress", counter, 1, -/// progress_checker_printk, -/// (void*)-PROGRESS_CHECKER_FAILED); -/// -/// OR -/// -/// progress_checker_create(&progress, "progress", counter, 1, -/// progress_checker_printk, 0); -/// -/// \endcode -/// -/// The first usage prints a report and panics the test if lack of progress is -/// detected. The second form simply prints a report in the event of a lack -/// or progress. The second form may be useful to report on counters that -/// only have a statistical probability of making progress, however be aware -/// that the report is generated in an interrupt context and all thread -/// activity will be blocked until the formatted I/O is complete. -/// -/// Notes: -/// -/// This implementation requires the \c byte_pool facility and malloc() to be -/// set up as the ProgressChecker allocates dynamic storage during -/// initialization to store the previous pass counts. -/// -/// It is probably not a good idea to use a single ProgressChecker for both -/// manual and timer-based checking, since there is no protection in the -/// implementation for mutiple accesses to the ProgressChecker. - -#include "progress.h" -#include "ssx_dump.h" - -// The built-in timer callback - -static void -progress_callback(void *arg) -{ - ProgressChecker *checker = (ProgressChecker *)arg; - - if (progress_checker_check(checker)){ - if (0) { - progress_checker_dump(checker); - } - SSX_PANIC(PROGRESS_CHECKER_FAILED); - } -} - - -// Bit-vector operations manage the array of bits using little-endian -// protocols - -static inline void -bit_vector_set(uint8_t *vector, size_t bit) -{ - vector[bit / 8] |= (1 << (bit % 8)); -} - - -static inline void -bit_vector_clear(uint8_t *vector, size_t bit) -{ - vector[bit / 8] &= ~(1 << (bit % 8)); -} - - -static inline int -bit_vector_is_set(uint8_t *vector, size_t bit) -{ - return ((vector[bit / 8] & (1 << (bit % 8))) != 0); -} - - -// NB: We don't have a bit-vector object with a size included. For this -// application we can only call this API if we know that there is at least 1 -// bit set in the vector. - -static size_t -bit_vector_find_first_set(uint8_t *vector) -{ - size_t byte = 0; - - while (vector[byte] == 0) { - byte++; - } - - return (8 * byte) + __builtin_ffs(vector[byte]) - 1; -} - - -/// Create a progress checker -/// -/// \param checker A pointer to an uninitialized or idle ProgressChecker -/// -/// \param name An optional character string associated with the checker -/// -/// \param pass_counts An array of pass counters - the array will be cleared -/// by this API. -/// -/// \param counters The number of counters in the array -/// -/// \param callback This function is called \e every time a check is -/// completed. -/// -/// \param arg The private argument of the callback function -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c -/// -/// Return values other than 0 are errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. -/// -/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT A null (0) pointer was provided -/// as the \a pass_counts argument, or the number of \a counters can not be -/// represented as a signed integer. -/// -/// \retval -PROGRESS_CHECKER_ALLOCATION_FAILED Memory allocation of dynamic -/// memory failed. This is treated as a fatal error here. -/// -/// This API may also return or signal other errors from its implementation -/// APIs. - -int -progress_checker_create(ProgressChecker *checker, - const char* name, - uint64_t *pass_counts, - size_t counters, - ProgressCheckerCallback callback, - void *arg) -{ - int rc, bytes; - void *memory; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF((checker == 0), - PROGRESS_CHECKER_INVALID_OBJECT); - SSX_ERROR_IF((pass_counts == 0) || - (counters != (int)counters), - PROGRESS_CHECKER_INVALID_ARGUMENT); - } - - // Install and clear the counters - - checker->pass_counts = pass_counts; - memset((void *)pass_counts, 0, counters * sizeof(uint64_t)); - checker->counters = counters; - - // Allocate and clear dynamic memory - - memory = calloc(counters, sizeof(uint64_t)); - checker->saved_counts = (uint64_t *)memory; - - bytes = (counters / 8) + (counters % 8 ? 1 : 0); - checker->bit_vector_bytes = bytes; - - memory = calloc(bytes, 1); - checker->exempt = (uint8_t *)memory; - - memory = calloc(bytes, 1); - checker->failure = (uint8_t *)memory; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF((counters != 0) && - ((checker->saved_counts == 0) || - (checker->exempt == 0) || - (checker->failure == 0)), - PROGRESS_CHECKER_ALLOCATION_FAILED); - } - - // Initialize other fields - - checker->name = name; - checker->callback = callback; - checker->arg = arg; - checker->failed = 0; - checker->checks = 0; - - // Initialize the timer structure. - - rc = ssx_timer_create(&(checker->timer), - progress_callback, - (void *)checker); - if (rc) return rc; - - return 0; -} - - -/// Exempt a pass count from progress checking -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// \param counter The index of the counter to exempt -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c -/// -/// Return values other than 0 are errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. -/// -/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not -/// valid. - -int -progress_checker_exempt(ProgressChecker *checker, - size_t counter) -{ - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(checker == 0, - PROGRESS_CHECKER_INVALID_OBJECT); - SSX_ERROR_IF(counter >= checker->counters, - PROGRESS_CHECKER_INVALID_ARGUMENT); - } - - bit_vector_set(checker->exempt, counter); - - return 0; -} - - -/// Exempt all pass counts from progress checking -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c -/// -/// This API is provided to support applications where pass-count-updating -/// processes are added dynamically. This API coule typically be called -/// immediately after progress_checker_create(). Them, as each process was -/// created it would call progress_checker_require() for the pass count. -/// -/// Return values other than 0 are errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. -int -progress_checker_exempt_all(ProgressChecker *checker) -{ - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(checker == 0, - PROGRESS_CHECKER_INVALID_OBJECT); - } - - memset(checker->exempt, -1, checker->bit_vector_bytes); - - return 0; -} - - -/// Require a pass count to update for progress checking -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// \param counter The index of the counter to require -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c -/// -/// Return values other than 0 are errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. -/// -/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not -/// valid. - -int -progress_checker_require(ProgressChecker *checker, - size_t counter) -{ - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(checker == 0, - PROGRESS_CHECKER_INVALID_OBJECT); - SSX_ERROR_IF(counter >= checker->counters, - PROGRESS_CHECKER_INVALID_ARGUMENT); - } - - bit_vector_clear(checker->exempt, counter); - - return 0; -} - - -/// Require a pass count to update for progress checking avoiding races -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// \param counter The index of the counter to require -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c -/// -/// If a pass counter is marked "exempt" but then later marked "required", -/// there is a potential race between the update of the pass counter and the -/// next check, particularly when the checker is scheduled periodically. This -/// form of the progress_checker_require() marks the progress checker such -/// that \e all checks are deferred on the next call of -/// progress_checker_check() targeting the object in order to avoid the race. -/// -/// Return values other than 0 are errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. -/// -/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not -/// valid. -int -progress_checker_require_defer(ProgressChecker *checker, - size_t counter) -{ - int rc; - SsxMachineContext ctx; - - ssx_critical_section_enter(SSX_CRITICAL, &ctx); - - rc = progress_checker_require(checker, counter); - checker->defer = 1; - - ssx_critical_section_exit(&ctx); - - return rc; -} - - -/// Check for progress in every required pass counter. -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c -/// -/// Return values other than 0 are not necessarily errors; see \ref -/// ssx_errors -/// -/// \retval various Except for the error listed below, -/// progress_checker_check() returns the code returned by the callback -/// function. If no callback was provided when the checker was created, then 0 -/// is returned for success and -PROGRESS_CHECKER_FAILED is returned in the -/// event of a lack of progress. -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. - -int -progress_checker_check(ProgressChecker *checker) -{ - size_t i; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(checker == 0, - PROGRESS_CHECKER_INVALID_OBJECT); - } - - // Avoid doing this step unless necessary - - if (checker->failed != 0) { - checker->failed = 0; - memset((void *)checker->failure, 0, checker->bit_vector_bytes); - } - - // Check, unless checking has been deferred for 1 time by - // progress_checker_require_defer(). - - if (checker->defer) { - - checker->defer = 0; - - } else { - - SSX_ATOMIC(SSX_CRITICAL, checker->start_check = ssx_timebase_get()); - - for (i = 0; i < checker->counters; i++) { - - if ((checker->pass_counts[i] <= checker->saved_counts[i]) && - !(bit_vector_is_set(checker->exempt, i))) { - - checker->failed++; - bit_vector_set(checker->failure, i); - } - checker->saved_counts[i] = checker->pass_counts[i]; - } - - SSX_ATOMIC(SSX_CRITICAL, checker->end_check = ssx_timebase_get()); - } - - checker->checks++; - - if (checker->callback) { - return checker->callback(checker, checker->arg, checker->failed); - } else if (checker->failed != 0) { - return -PROGRESS_CHECKER_FAILED; - } else { - return 0; - } -} - - -/// Schedule progress checks (periodically) in the future. -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// \param interval The relative time of the (first) check -/// -/// \param period If non-zero, checks will be made periodically with this -/// period. -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c. See the SSX documentation for a discussion of -/// timer scheduling in SSX. -/// -/// Return values other than 0 errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. -/// -/// This API may also return or signal other errors from its implementation -/// APIs. - - -int -progress_checker_schedule(ProgressChecker *checker, - SsxInterval interval, - SsxInterval period) -{ - int rc; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(checker == 0, - PROGRESS_CHECKER_INVALID_OBJECT); - } - - rc = ssx_timer_cancel(&(checker->timer)); - if (rc != -SSX_TIMER_NOT_ACTIVE) return rc; - - rc = ssx_timer_schedule(&(checker->timer), interval, period); - if (rc) return rc; - - return 0; -} - - -/// Cancel all future (periodic) progress checks -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c. See the SSX documentation for a discussion of -/// timer scheduling in SSX. -/// -/// Return values other than 0 errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. -/// -/// This API may also return or signal other errors from its implementation -/// APIs. - -int -progress_checker_cancel(ProgressChecker *checker) -{ - int rc; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(checker == 0, - PROGRESS_CHECKER_INVALID_OBJECT); - } - - rc = ssx_timer_cancel(&(checker->timer)); - if (rc) return rc; - - return 0; -} - - -/// Iterate over progress check failures -/// -/// \param checker A pointer to an initialized ProgressChecker -/// -/// \param counter Will return the index of the next failing counter, or -1 to -/// indicate no more failing counters. -/// -/// For an overview of the ProgressChecker and its APIs, see the documentation -/// for the file progress.c. -/// -/// Return values other than 0 errors; see \ref ssx_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided -/// as the \a checker argument. - -int -progress_checker_next_failure(ProgressChecker *checker, int *counter) -{ - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(checker == 0, - PROGRESS_CHECKER_INVALID_OBJECT); - } - - if (checker->failed == 0) { - *counter = -1; - return 0; - } - - *counter = bit_vector_find_first_set(checker->failure); - bit_vector_clear(checker->failure, *counter); - checker->failed--; - - return 0; -} - - -/// A standard way to print the results of a progress check failure, suitable -/// for use as a ProgressChecker callback. -/// -/// \param checker The checker - which may or may not have failed. If the -/// checker did fail, then a failure report is printed using printk(). -/// -/// \param arg The value to return in case of failure. In case of -/// success, 0 is returned. -/// -/// \param failed - The number of failed checks - -int -progress_checker_printk(ProgressChecker *checker, - void *arg, - size_t failed) -{ - int counter; - - if (!failed) { - return 0; - } - - printk("---------------------------------------------------------------\n"); - printk("-- Progress check failed for \"%s\" (%p).\n", - checker->name, checker); - printk("-- Check %zu over interval 0x%016llx - 0x%016llx\n", - checker->checks, checker->start_check, checker->end_check); - printk("-- %zu failed counter%s listed below\n", - failed, failed > 1 ? "s are" : " is"); - printk("---------------------------------------------------------------\n"); - - do { - progress_checker_next_failure(checker, &counter); - if (counter < 0) { - break; - } - printk("%4d. 0x%016llx\n", counter, checker->pass_counts[counter]); - } while (1); - - printk("---------------------------------------------------------------\n"); - - return (int)arg; -} - - -/// Call progress_checker_printk(), then create a kernel dump on failure -/// -/// \param checker The checker - which may or may not have failed. If the -/// checker did fail, then a failure report is printed using -/// progress_checker_printk(). -/// -/// \param arg The value to return in case of failure. In case of -/// success, 0 is returned. -/// -/// \param failed - The number of failed checks -/// -/// If progress_checker_printk() fails with a non-0 return code then this API -/// prints a full SSX kernel dump after the progress_checker_printk() report. -int -progress_checker_printk_dump(ProgressChecker *checker, - void *arg, - size_t failed) -{ - int rc; - SsxMachineContext ctx; - - rc = progress_checker_printk(checker, arg, failed); - if (rc != 0) { - ssx_critical_section_enter(SSX_CRITICAL, &ctx); - ssx_dump(ssxout, 0); - ssx_critical_section_exit(&ctx); - } - - return rc; -} - - -/// Dump a progress checker structure using printk() - -void -progress_checker_dump(ProgressChecker *checker) -{ - size_t i; - - printk("Dump of progress checker \"%s\" (%p)\n" - " Counters = %zu\n" - " Checks = %zu\n" - " Failed = %zu\n" - " Callback = %p(%p)\n", - checker->name, checker, checker->counters, checker->checks, - checker->failed, checker->callback, checker->arg); - - printk(" Pass Counts (%p) :\n", checker->pass_counts); - for (i = 0; i < checker->counters; i++) { - printk(" %9d%c 0x%016llx\n", - i, - bit_vector_is_set(checker->exempt, i) ? '*' : ' ', - checker->pass_counts[i]); - } - printk(" Saved Counts (%p) :\n", checker->saved_counts); - for (i = 0; i < checker->counters; i++) { - printk(" %9d 0x%016llx\n", - i, checker->saved_counts[i]); - } -} - - - - - - - - diff --git a/src/lib/ppc405lib/progress.h b/src/lib/ppc405lib/progress.h deleted file mode 100644 index 3454a0f..0000000 --- a/src/lib/ppc405lib/progress.h +++ /dev/null @@ -1,177 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/progress.h $ */ -/* */ -/* 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 */ -#ifndef __PROGRESS_H__ -#define __PROGRESS_H__ - -// $Id$ - -/// \file progress.h -/// \brief Programmable progress (hang) checking - -#include <time.h> -#include "ssx.h" -#include "byte_pool.h" -#include "ssx_io.h" - -#ifndef __ASSEMBLER__ - -struct ProgressChecker; - -/// ProgressChecker callback type -/// -/// \param checker The checker that has just been checked -/// -/// \param arg The private argument provided when the checker was created -/// -/// \param failed The number of failed pass counts - 0 indicates no failures - -typedef int (*ProgressCheckerCallback)(struct ProgressChecker* checker, - void* arg, - size_t failed); - - -/// A simple progress (hang) checker. For API details see file progress.c - -typedef struct ProgressChecker { - - /// The application provided pass-count array - uint64_t *pass_counts; - - /// The number of pass-count counters in the array. - size_t counters; - - /// The (optional) name of the checker for reporting purposes. - const char *name; - - /// The (optional) checker callback. - ProgressCheckerCallback callback; - - /// The checker callback private argument - void *arg; - - /// The dynamically-allocated saved pass counts. - uint64_t *saved_counts; - - /// The dynamically-allocated exemption bit-vector - /// - /// \todo Get or implement a generic unlimited-precision bit vector - uint8_t *exempt; - - /// The dynamically-allocated failure bit-vector - uint8_t *failure; - - /// The number of bytes in the bit vector - size_t bit_vector_bytes; - - /// Defer all checking the next time progress_checker_check() is called. - /// - /// See progress_checker_require_defer() - int defer; - - /// The number of failures present in the *failure vector - size_t failed; - - /// A timer object to support time-based checking. - SsxTimer timer; - - /// The number of times progress_checker_check() has been called on the - /// object. - size_t checks; - - /// The time the last check started - SsxTimebase start_check; - - /// The time the last check ended - SsxTimebase end_check; - -} ProgressChecker; - - -int -progress_checker_create(ProgressChecker *checker, - const char *name, - uint64_t *pass_counts, - size_t counters, - ProgressCheckerCallback callback, - void *arg); - -int -progress_checker_exempt(ProgressChecker *checker, - size_t counter); - -int -progress_checker_exempt_all(ProgressChecker *checker); - -int -progress_checker_require(ProgressChecker *checker, - size_t counter); - -int -progress_checker_require_defer(ProgressChecker *checker, - size_t counter); - -int -progress_checker_check(ProgressChecker *checker); - -int -progress_checker_schedule(ProgressChecker *checker, - SsxInterval interval, - SsxInterval period); - -int -progress_checker_cancel(ProgressChecker *checker); - -int -progress_checker_next_failure(ProgressChecker *checker, int *counter); - -int -progress_checker_delete(ProgressChecker *checker); - -int -progress_checker_printk(ProgressChecker *checker, - void *arg, - size_t failed); - -int -progress_checker_printk_dump(ProgressChecker *checker, - void *arg, - size_t failed); - -void -progress_checker_dump(ProgressChecker *checker); - - -#endif /* __ASSEMBLER__ */ - -// Error/Panic codes - -#define PROGRESS_CHECKER_INVALID_OBJECT 0x00776001 -#define PROGRESS_CHECKER_INVALID_ARGUMENT 0x00776002 -#define PROGRESS_CHECKER_FAILED 0x00776003 -#define PROGRESS_CHECKER_CALLBACK_PANIC 0x00776004 -#define PROGRESS_CHECKER_INVARIANT 0x00776005 -#define PROGRESS_CHECKER_ALLOCATION_FAILED 0x00776006 - -#endif /* __PROGRESS_H__ */ diff --git a/src/lib/ppc405lib/rtx_stdio.c b/src/lib/ppc405lib/rtx_stdio.c deleted file mode 100644 index 4bb1a18..0000000 --- a/src/lib/ppc405lib/rtx_stdio.c +++ /dev/null @@ -1,149 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/rtx_stdio.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 rtx_stdio.c -/// \brief SSX I/O drivers for RTX stdio streams -/// -/// The RTX \a stdout and \a stderr components accept 1, 2 and 4-byte -/// transactions on a 32-bit OCI address and write the data to the RTX -/// job's \a stdout or \a stderr respectively. The \a stdin device is not yet -/// implemented. - -#include "ssx.h" -#include "rtx_stdio.h" - -RtxStdio rtx_stdin; -RtxStdio rtx_stdout; -RtxStdio rtx_stderr; - -int -rtx_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read) -{ - SSX_PANIC(ENXIO); - return -ENXIO; -} - - -int -rtx_stdio_swrite(FILE *stream, const void *buf, - size_t count, size_t *written) -{ - RtxStdio *rtx = (RtxStdio *)stream; - size_t n; - - n = count; - while (n) { - if (n >= 4) { - out32(rtx->address, *((uint32_t *)buf)); - buf += 4; - n -= 4; - } else if (n >= 2) { - out16(rtx->address, *((uint16_t *)buf)); - buf += 2; - n -= 2; - } else { - out8(rtx->address, *((uint8_t *)buf)); - buf++; - n--; - } - } - - if (written != 0) { - *written = count; - } - - return 0; -} - - -ssize_t -rtx_stdio_fflush(FILE *stream) -{ - RtxStdio *rtx = (RtxStdio *)stream; - - out8(rtx->flush_address, 0); - return 0; -} - - -int -rtx_stdin_create(RtxStdio *stream) -{ - FILE *base = (FILE *)stream; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(stream == 0, EBADF); - } - - memset((void *)stream, 0, sizeof(RtxStdio)); - base->sread = rtx_stdio_sread; - - stream->address = RTX_STDIN; - - return 0; -} - - -int -rtx_stdout_create(RtxStdio *stream) -{ - FILE *base = (FILE *)stream; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(stream == 0, EBADF); - } - - memset((void *)stream, 0, sizeof(RtxStdio)); - base->swrite = rtx_stdio_swrite; - base->fflush = rtx_stdio_fflush; - - stream->address = RTX_STDOUT; - stream->flush_address = RTX_STDOUT_FLUSH; - - return 0; -} - - -int -rtx_stderr_create(RtxStdio *stream) -{ - FILE *base = (FILE *)stream; - - if (SSX_ERROR_CHECK_API) { - SSX_ERROR_IF(stream == 0, ENXIO); - } - - memset((void *)stream, 0, sizeof(RtxStdio)); - base->swrite = rtx_stdio_swrite; - base->fflush = rtx_stdio_fflush; - - stream->address = RTX_STDERR; - stream->flush_address = RTX_STDERR_FLUSH; - - return 0; -} - - diff --git a/src/lib/ppc405lib/rtx_stdio.h b/src/lib/ppc405lib/rtx_stdio.h deleted file mode 100644 index cd2a439..0000000 --- a/src/lib/ppc405lib/rtx_stdio.h +++ /dev/null @@ -1,74 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/rtx_stdio.h $ */ -/* */ -/* 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 */ -#ifndef __RTX_STDIO_H__ -#define __RTX_STDIO_H__ - -// $Id$ - -/// \file rtx_stdio.h -/// \brief SSX I/O implementations for RTX stdio streams - -#include "ssx_io.h" -#include "rtx_stdio_addresses.h" - -/// A FILE structure for a RTX fake stdio stream - -typedef struct { - - /// The base class - FILE stream; - - /// The MMIO address of the RTX device for the stream - SsxAddress address; - - /// The MMIO address of the RTX device for flushing the stream; - SsxAddress flush_address; - -} RtxStdio; - -extern RtxStdio rtx_stdin; -extern RtxStdio rtx_stdout; -extern RtxStdio rtx_stderr; - -int -rtx_stdin_create(RtxStdio *stream); - -int -rtx_stdout_create(RtxStdio *stream); - -int -rtx_stderr_create(RtxStdio *stream); - -int -rtx_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read); - -int -rtx_stdio_swrite(FILE *stream, const void *buf, - size_t count, size_t *written); - -int -rtx_stdio_sflush(FILE *stream); - -#endif /* __RTX_STDIO_H__ */ diff --git a/src/lib/ppc405lib/rtx_stdio_addresses.h b/src/lib/ppc405lib/rtx_stdio_addresses.h deleted file mode 100644 index 5359f7f..0000000 --- a/src/lib/ppc405lib/rtx_stdio_addresses.h +++ /dev/null @@ -1,55 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/rtx_stdio_addresses.h $ */ -/* */ -/* 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 */ -#ifndef __RTX_STDIO_ADDRESSES_H__ -#define __RTX_STDIO_ADDRESSES_H__ - -// $Id$ - -/// \file rtx_stdio_addresses.h -/// \brief MMIO addresses and offsets of the rtx fake stdio model -/// -/// The RTX stdio module appears as OCI device #7? Reading 1 byte from the -/// stdin offset returns that byte from stdin. Writing 1, 2 or 4 bytes to the -/// stdout or stderr offsets causes output on that stream. Writing any single -/// byte to the 'flush' offsets flush the stdout or stderr streams. -/// -/// -*- This header is maintained as part of the PMX RTX model. -*- -/// -*- Do not edit in the SSX library as your edits will be lost. -*- - -#define RTX_STDIO_BASE 0x40060000 - -#define RTX_STDIN_OFFSET 0x00 -#define RTX_STDOUT_OFFSET 0x04 -#define RTX_STDOUT_FLUSH_OFFSET 0x08 -#define RTX_STDERR_OFFSET 0x0c -#define RTX_STDERR_FLUSH_OFFSET 0x10 - -#define RTX_STDIN (RTX_STDIO_BASE + RTX_STDIN_OFFSET) -#define RTX_STDOUT (RTX_STDIO_BASE + RTX_STDOUT_OFFSET) -#define RTX_STDOUT_FLUSH (RTX_STDIO_BASE + RTX_STDOUT_FLUSH_OFFSET) -#define RTX_STDERR (RTX_STDIO_BASE + RTX_STDERR_OFFSET) -#define RTX_STDERR_FLUSH (RTX_STDIO_BASE + RTX_STDERR_FLUSH_OFFSET) - -#endif /* __RTX_STDIO_ADDRESSES_H__ */ diff --git a/src/lib/ppc405lib/sxlock.c b/src/lib/ppc405lib/sxlock.c deleted file mode 100644 index 35f9698..0000000 --- a/src/lib/ppc405lib/sxlock.c +++ /dev/null @@ -1,494 +0,0 @@ -/* 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; -} diff --git a/src/lib/ppc405lib/sxlock.h b/src/lib/ppc405lib/sxlock.h deleted file mode 100644 index e0fa196..0000000 --- a/src/lib/ppc405lib/sxlock.h +++ /dev/null @@ -1,108 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/lib/ppc405lib/sxlock.h $ */ -/* */ -/* 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 */ -#ifndef __SXLOCK_H__ -#define __SXLOCK_H__ - -// $Id$ - -/// \file sxlock.h -/// \brief The implementation of a SharedExclusiveLock -/// -/// The SharedExclusiveLock is documented in the comments for the file -/// sxlock.c - -// Error/panic codes - -#define SXLOCK_INVALID_OBJECT 0x00795501 -#define SXLOCK_INVALID_ARGUMENT 0x00795502 -#define SXLOCK_TIMED_OUT 0x00795503 -#define SXLOCK_SHARED_UNDERFLOW 0x00795504 -#define SXLOCK_EXCLUSIVE_UNDERFLOW 0x00795505 -#define SXLOCK_SHARED_EXCLUSIVE_INVARIANT 0x00795506 - -/// A shared-exclusive lock object (also called a readers-write lock) -/// -/// This facility is documented in the file sxlock.c - -typedef struct { - - /// A semaphore for threads requesting shared access - SsxSemaphore shared_sem; - - /// A semaphore for threads requesting exclusive access - SsxSemaphore exclusive_sem; - - /// The number of threads running shared - SsxSemaphoreCount running_shared; - - /// The number of threads running exclusive - SsxSemaphoreCount running_exclusive; - -} SharedExclusiveLock; - - -/// Static initialization of a shared-exclusive lock object -/// -/// \param[in] shared The number of threads running shared at static -/// initialization -/// -/// \param[in] exclusive The number of threads running exclusive at static -/// initialization. -/// -/// Note that it is an error to specify both \a shared and \a exclusive as -/// non-0. - -#define SXLOCK_INITIALIZATION(shared, exclusive) \ - { \ - SSX_SEMAPHORE_INITIALIZATION(0, 0), \ - SSX_SEMAPHORE_INITIALIZATION(0, 0), \ - (shared), (exclusive) \ - } - -/// Declare and initialize a shared-exclusive lock - -#define SXLOCK(sxlock, shared, exclusive) \ - SharedExclusiveLock sxlock = SXLOCK_INITIALIZATION(shared, exclusive) - - - -int -sxlock_create(SharedExclusiveLock* sxlock, - SsxSemaphoreCount shared, - SsxSemaphoreCount exclusive); - -int -sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout); - -int -sxlock_unlock_shared(SharedExclusiveLock* sxlock); - -int -sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout); - -int -sxlock_unlock_exclusive(SharedExclusiveLock* sxlock); - -#endif // __SXLOCK_H__ |