diff options
Diffstat (limited to 'src/lib/ppc405lib/progress.c')
-rw-r--r-- | src/lib/ppc405lib/progress.c | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/src/lib/ppc405lib/progress.c b/src/lib/ppc405lib/progress.c new file mode 100644 index 0000000..8d2cd30 --- /dev/null +++ b/src/lib/ppc405lib/progress.c @@ -0,0 +1,743 @@ +/* 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]); + } +} + + + + + + + + |