Add the Regulator Interface and test

Updates #4924.

The Regulator is an application support class which is used to
deal with the scenario where there is a bursty input source
which needs to be metered out to a destination sink. The maximum
size of bursts needs to be known and the delivery method must
be configured to deliver messages at a rate that allows the
traffic to not overflow.
This commit is contained in:
Joel Sherrill
2023-07-05 15:28:54 -05:00
parent 0a766a88d7
commit fd693085ea
10 changed files with 2795 additions and 0 deletions

View File

@@ -0,0 +1,502 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RegulatorAPI
*
* @brief This header file defines the Regulator API.
*
*/
/*
* Copyright (C) 2023 On-Line Applications Research Corporation (OAR)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @defgroup RegulatorAPI Regulator API
*
* @brief Regulator APIs
*
* The Regulator provides a set of APIs to manage input sources which
* produces bursts of message traffic.
*
* The regulator is designed to sit logically between two entities -- a
* source and a destination, where it limits the traffic sent to the
* destination to prevent it from being flooded with messages from the
* source. This can be used to accommodate bursts of input from a source
* and meter it out to a destination. The maximum number of messages
* which can be buffered in the regulator is specified by the
* @a maximum_messages field in the @a rtems_regulator_attributes
* structure passed as an argument to @a rtems_regulator_create().
*
* The regulator library accepts an input stream of messages from a
* source and delivers them to a destination. The regulator assumes that the
* input stream from the source contains sporadic bursts of data which can
* exceed the acceptable rate of the destination. By limiting the message rate,
* the regulator prevents an overflow of messages.
*
* The regulator can be configured for the input buffering required to manage
* the maximum burst and for the metering rate for the output. The output rate
* is in messages per second. If the sender produces data too fast, the
* regulator will buffer the configured number of messages.
*
* A configuration capability is provided to allow for adaptation to different
* message streams. The regulator can also support running multiple instances,
* which could be used on independent message streams.
*
* The regulator provides a simple interface to the application for avoiding
* bursts of input from a fast source overflowing a slower destination.
*
* It is assumed that the application has a design limit on the number of
* messages which may be buffered. All messages accepted by the regulator,
* assuming no overflow on input, will eventually be output by the Delivery
* thread.
*
* A regulator instance is used as follows from the producer/source side:
*
* @code
* while (1)
* use rtems_regulator_obtain_buffer to obtain a buffer
* input operation to fetch data into the buffer
* rtems_regulator_send(buffer, size of message)
* @endcode
*
* The delivery of message buffers to the Destination and subsequent
* release is performed in the context of the delivery thread by either
* the delivery function or delivery thread. Details are below.
*
* The sequence diagram below shows the interaction between a message Source,
* a Regulator instance, and RTEMS, given the usage described in the above
* paragraphs.
*
* \startuml "Regulator Application Input Source Usage"
* Source -> Regulator : rtems_regulator_obtain_buffer(regulator, buffer)
* Regulator -> RTEMS : rtems_partition_get_buffer(id, buffer)
* RTEMS --> Regulator : rtems_status_code
* Regulator --> Source : rtems_status_code
* Source -> Regulator : rtems_regulator_send(regulator, message, length)
* Regulator -> RTEMS : rtems_message_queue_send(id, message, size)
* RTEMS --> Regulator : rtems_status_code
* Regulator --> Source : rtems_status_code
* \enduml
*
* As illustrated in the sequence diagram, the Source usually corresponds
* to application software reading a system input. The Source obtains a
* buffer from the Regulator instance and fills it with incoming data.
* The application explicitly obtaining a buffer and filling it in allows
* for zero copy operations on the Source side.
*
* The Source then sends the buffer to the Regulator instance. The Regulator
* the sends the buffer via a message queue which to the Delivery thread.
* The Delivery thread executes periodically at a rate specified at
* Regulation creation. At each period, the Delivery thread attempts to
* receive up to a configured number of buffers and invoke the Delivery
* function to deliver them to the Destination.
*
* The Delivery function is provided by the application for this
* specific Regulator instance. Depending on the Destination, it may use
* a function which copies the buffer contents (e.g., write()) or which
* operates directly on the buffer contents (e.g. DMA from buffer). In
* the case of a Destination which copies the buffer contents, the buffer
* can be released via @a rtems_regulator_release_buffer() as soon as the
* function or copying completes. In the case where the delivery uses the
* buffer and returns, the call to @a rtems_regulator_release_buffer()
* will occur when the use of the buffer is complete (e.g. completion
* of DMA transfer). This explicit and deliberate exposure of buffering
* provides the application with the ability to avoid copying the contents.
*
* After the Source has sent the message to the Regulator instance,
* the Source is free to process another input and the Regulator
* instance will ensure that the buffer is delivered to the Delivery
* function and Destination.
*
* The Regulator implementation uses the RTEMS Classic API Partition Manager
* to manage the buffer pool and the RTEMS Classic API Message Queue
* Manager to send the buffer to the Delivery thread.
*/
#ifndef REGULATOR_H
#define REGULATOR_H
#include <stdlib.h>
#include <rtems.h>
/**
* @ingroup RegulatorAPI
*
* @brief Regulator Delivery Function Type
*
* The user provides a function which is invoked to deliver a message
* to the output. It is invoked by the Delivery thread created as part
* of @a rtems_regulator_create(). The priority and stack size of the
* Delivery thread are specified in the regulator attribute set.
*
* It takes three parameters:
*
* @param[in] context is an untyped pointer to a user context
* @param[in] message points to the message
* @param[in] length is the message size
*
* The following is an example deliverer function. It assumes that the
* application has defined the my_context_t structure and it has at least
* the socket field. The @a message passed in originated with an
* application source which obtained the @a message buffer using
* @a rtems_regulator_obtain_buffer(), filled it in with source data,
* and used @a rtems_regulator_send() to hand to the regulator instance
* for later delivery.
*
* @code
* bool my_deliverer(
* void *context,
* void *message,
* size_t length
* )
* {
* my_context_t *my_context;
*
* my_context = (my_context_t *)context;
*
* write(my_context->socket, message, length);
* rtems_regulator_release_buffer(message);
* // return false to indicate we released the buffer
* return false;
* }
* @endcode
*
* The delivery function returns true to indicate that the delivery thread
* should release the buffer or false to indicate that it released the
* buffer. If the delivery function invokes a function like @a write()
* to deliver the message to the destination, then the buffer can be
* released immediately after the call. If the delivery function does
* something like setting up a DMA transfer of the buffer, it cannot be
* released until after the DMA is complete.
*
* The following sequence diagram shows the behavior of the Delivery thread
* body and its interaction with the user-supplied deliverer() function.
*
* \startuml "Regulator Delivery Thread Body"
* loop while (1)
* "Delivery Thread" -> RTEMS : rtems_rate_monotonic_period(id, delivery_thread_period)
* loop for 0 : maximum_to_dequeue_per_period
* "Delivery Thread" -> RTEMS : rtems_message_queue_receive(id, message, size, wait, 0)
* RTEMS --> "Delivery Thread" : rtems_status_code
* group if [rtems_status_code != RTEMS_SUCCESSFUL]
* RTEMS -> "Delivery Thread" : break
* end
* "Delivery Thread" -> Application : deliverer(context, buffer, length)
* "Delivery Thread" -> RTEMS : rtems_partition_return_buffer(id, buffer)
* RTEMS --> "Delivery Thread" : rtems_status_code
* end
* end
* \enduml
*
* In the above sequence diagram, the key points are:
*
* -# The Delivery Thread Body is periodically executed.
* -# During each period, up to the instance configuration parameter
* @a maximum_to_dequeue_per_period may be dequeued and
* passed the application's delivery function for processing.
*
* Note that the application explicitly obtains buffers from the
* regulator instance but that the release may be done by Delivery
* Thread, the Delivery function, or later when the buffer contents
* are transferred.
*/
typedef bool (*rtems_regulator_deliverer)(
void *context,
void *message,
size_t length
);
/**
* @ingroup RegulatorAPI
*
* @brief Attributes for Regulator Instance
*
* An instance of this structure must be populated by the application
* before creating an instance of the regulator. These settings tailor
* the behavior of the regulator instance.
*/
typedef struct {
/** Application function to invoke to output a message to the destination*/
rtems_regulator_deliverer deliverer;
/** Context pointer to pass to deliver function */
void *deliverer_context;
/** Maximum size message to process */
size_t maximum_message_size;
/** Maximum number of messages to be able to buffer */
size_t maximum_messages;
/** Priority of Delivery thread */
rtems_task_priority delivery_thread_priority;
/** Stack size of Delivery thread */
size_t delivery_thread_stack_size;
/** Period (in ticks) of Delivery thread */
rtems_interval delivery_thread_period;
/** Maximum messages to dequeue per period */
size_t maximum_to_dequeue_per_period;
} rtems_regulator_attributes;
/**
* @ingroup RegulatorAPI
*
* @brief Statistics for Regulator Instance
*
* An instance of this structure is provided to the directive
* @a rtems_regulator_get_statistics and is filled in by that service.
*/
typedef struct {
/** Number of successfully obtained buffers. */
size_t obtained;
/** Number of successfully released buffers. */
size_t released;
/** Number of successfully delivered buffers. */
size_t delivered;
/** Rate Monotonic Period statistics for Delivery Thread */
rtems_rate_monotonic_period_statistics period_statistics;
} rtems_regulator_statistics;
/**
* @ingroup RegulatorAPI
*
* @brief Regulator Internal Structure
*/
struct _Regulator_Control;
/**
* @ingroup RegulatorAPI
*
* @brief Regulator Instance
*
* This is used by the application as the handle to a Regulator instance.
*/
typedef struct _Regulator_Control *rtems_regulator_instance;
/**
* @ingroup RegulatorAPI
*
* @brief Create a regulator
*
* This function creates an instance of a regulator. It uses the provided
* @a attributes to create the instance return in @a regulator. This instance
* will allocate the buffers associated with the regulator instance as well
* as the Delivery thread.
*
* The @a attributes structure defines the priority and stack size of
* the Delivery thread dedicated to this regulator instance. It also
* defines the period of the Delivery thread and the maximum number of
* messages that may be delivered per period via invocation of the
* delivery function.
*
* For each regulator instance, the following resources are allocated:
*
* - A memory area for the regulator control block using @a malloc().
* - A RTEMS Classic API Message Queue is constructed with message
* buffer memory allocated using @a malloc(). Each message consists
* of a pointer and a length.
* - A RTEMS Classic API Partition.
* - A RTEMS Classic API Rate Monotonic Period.
*
* @param[in] attributes specify the regulator instance attributes
* @param[inout] regulator will point to the regulator instance
*
* @return an RTEMS status code indicating success or failure.
*
* @note This function allocates memory for the buffers holding messages,
* an Delivery thread and an RTEMS partition. When it executes, the
* Delivery thread will create an RTEMS rate monotonic period.
*/
rtems_status_code rtems_regulator_create(
rtems_regulator_attributes *attributes,
rtems_regulator_instance **regulator
);
/**
* @ingroup RegulatorAPI
*
* @brief Delete a regulator
*
* This function is used to delete the specified @a regulator instance.
*
* It is the responsibility of the user to ensure that any resources
* such as sockets or open file descriptors used by the delivery
* function are also deleted. It is likely safer to delete those
* delivery resources after deleting the regulator instance rather than
* before.
*
* @param[in] regulator is the instance to delete
* @param[in] ticks is the maximum number of ticks to wait for
* the delivery thread to shutdown.
*
* @return an RTEMS status code indicating success or failure.
*
* @note This function deallocates the resources allocated during
* @a rtems_regulator_create().
*/
rtems_status_code rtems_regulator_delete(
rtems_regulator_instance *regulator,
rtems_interval ticks
);
/**
* @ingroup RegulatorAPI
*
* @brief Obtain Buffer from Regulator
*
* This function is used to obtain a buffer from the regulator's pool. The
* @a buffer returned is assumed to be filled in with contents and used
* in a subsequent call to @a rtems_regulator_send(). When the @a buffer is
* delivered, it is expected to be released. If the @a buffer is not
* successfully accepted by this function, then it should be returned
* using @a rtems_regulator_release_buffer() or used to send another message.
*
* The @a buffer is of the maximum_message_size specified in the attributes
* passed in to @a rtems_regulator_create().
*
* @param[in] regulator is the regulator instance to operate upon
* @param[out] buffer will point to the allocated buffer
*
* @return an RTEMS status code indicating success or failure.
*
* @note This function does not perform dynamic allocation. It obtains a
* buffer from the pool allocated during @a rtems_regulator_create().
*
* @note Any attempt to write outside the buffer area is undefined.
*/
rtems_status_code rtems_regulator_obtain_buffer(
rtems_regulator_instance *regulator,
void **buffer
);
/**
* @ingroup RegulatorAPI
*
* @brief Release Previously Obtained Regulator Buffer
*
* This function is used to release a buffer to the regulator's pool. It is
* assumed that the @a buffer returned will not be used by the application
* anymore. The @a buffer must have previously been allocated by
* @a rtems_regulator_obtain_buffer() and NOT passed to
* @a rtems_regulator_send().
*
* If a subsequent @a rtems_regulator_send() using this @a buffer is
* successful, the @a buffer will eventually be processed by the delivery
* thread and released.
*
* @param[in] regulator is the regulator instance to operate upon
* @param[out] buffer will point to the buffer to release
*
* @return an RTEMS status code indicating success or failure.
*
* @note This function does not perform dynamic deallocation. It releases a
* buffer to the pool allocated during @a rtems_regulator_create().
*/
rtems_status_code rtems_regulator_release_buffer(
rtems_regulator_instance *regulator,
void *buffer
);
/**
* @ingroup RegulatorAPI
*
* @brief Send to regulator instance
*
* This function is used by the producer to send a @a message to the
* @a regulator for later delivery by the Delivery thread. The message is
* contained in the memory pointed to by @a message and is @a length
* bytes in length.
*
* It is required that the @a message buffer was obtained via
* @a rtems_regulator_obtain_buffer().
*
* It is assumed that the @a message buffer has been filled in with
* application content to deliver.
*
* If the @a rtems_regulator_send() is successful, the buffer is enqueued
* inside the regulator instance for subsequent delivery. After the
* @a message is delivered, it may be released by either delivery
* function or the application code depending on the implementation.
*
* The status @a RTEMS_TOO_MANY is returned if the regulator's
* internal queue is full. This indicates that the configured
* maximum number of messages was insufficient. It is the
* responsibility of the caller to decide whether to hold messages,
* drop them, or print a message that the maximum number of messages
* should be increased.
*
* If @a rtems_regulator_send() is unsuccessful, it is the application's
* responsibility to release the buffer. If it is successfully sent,
* then it becomes the responsibility of the delivery function to
* release it.
*
* @param[in] regulator is the regulator instance to operate upon
* @param[out] message points to the message to deliver
* @param[out] length is the size of the message in bytes
*
* @return an RTEMS status code indicating success or failure.
*
*/
rtems_status_code rtems_regulator_send(
rtems_regulator_instance *regulator,
void *message,
size_t length
);
/**
* @ingroup RegulatorAPI
*
* @brief Obtain statistics for regulator instance
*
* This function is used by the application to obtain statistics
* information about the regulator instance.
*
* If the @a obtained and @a released fields in the returned
* @a statistics structure are equal, then there are no buffers
* outstanding from this regulator instance.
*
* @param[in] regulator is the regulator instance to operate upon
* @param[inout] statistics points to the statistics structure to fill in
*
* @return an RTEMS status code indicating success or failure.
*
*/
rtems_status_code rtems_regulator_get_statistics(
rtems_regulator_instance *regulator,
rtems_regulator_statistics *statistics
);
#endif /* REGULATOR_H */

View File

@@ -0,0 +1,135 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RegulatorInternalAPI
*
* @brief Regulator Library Implementation Support
*/
/*
* Copyright (C) 2023 On-Line Applications Research Corporation (OAR)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @defgroup RegulatorInternalAPI Regulator API Internals
*
* @brief Regulator Internal Information
*
* This concerns implementation information about the Regulator.
*/
#ifndef RTEMS_REGULATORIMPL_H
#define RTEMS_REGULATORIMPL_H
#include <stdatomic.h>
#include <rtems/chain.h>
/**
* @ingroup RegulatorInternalAPI
*
* This constant is used to indicate the regulator instance is initialized.
*/
#define REGULATOR_INITIALIZED 0xDeadF00d
/**
* @ingroup RegulatorInternalAPI
*
* @brief Regulator Message Instance Management Structure
*/
typedef struct {
/** This points to the message contents. */
void *buffer;
/** This is the length of the message. */
size_t length;
} _Regulator_Message_t;
/**
* @ingroup RegulatorInternalAPI
*
* @brief Regulator Statistics Private Structure
*
* An instance of this structure is allocated per regulator instance.
*/
typedef struct {
/** Number of successfully obtained buffers. */
atomic_size_t obtained;
/** Number of successfully released buffers. */
atomic_size_t released;
/** Number of successfully delivered buffers. */
atomic_size_t delivered;
} _Regulator_Statistics;
/**
* @ingroup RegulatorInternalAPI
*
* @brief Regulator Instance Private Structure
*
* An instance of this structure is allocated per regulator instance.
*/
typedef struct {
/** Has magic value when instance is usable */
uint32_t initialized;
/** Attributes for this instance -- copied from user */
rtems_regulator_attributes Attributes;
/** Pointer to allocated message memory */
void *message_memory;
/** Pointer to allocated memory for RTEMS Message Queue for pending buffers*/
void *message_queue_storage;
/** RTEMS Message Queue of pending outgoing messages */
rtems_id queue_id;
/** RTEMS Partition for pool of unused messages */
rtems_id messages_partition_id;
/** RTEMS Task for performing output */
rtems_id delivery_thread_id;
/** Id of period used by output thread */
rtems_id delivery_thread_period_id;
/** Indicates Delivery thread is running */
bool delivery_thread_is_running;
/** Indicates Delivery thread has been requested to exit */
bool delivery_thread_request_exit;
/** Indicates Delivery thread has exited */
bool delivery_thread_has_exited;
/** Internal Statistics */
_Regulator_Statistics Statistics;
} _Regulator_Control;
#endif /* RTEMS_REGULATORIMPL_H */

View File

@@ -0,0 +1,679 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @brief Regulator Library Implementation
*/
/*
* Copyright (C) 2022 On-Line Applications Research Corporation (OAR)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <rtems.h>
#include <rtems/regulator.h>
#include <rtems/regulatorimpl.h>
/**
* @ingroup RegulatorInternalAPI
*
* This method is the body for the task which delivers the output for
* this regulator instance at the configured rate.
*
* @param[in] arg points to the regulator instance this thread
* is associated with
*
* @note The argument passed in cannot be NULL if the
* rtems_regulator_create worked.
*/
static rtems_task _Regulator_Output_task_body(
rtems_task_argument arg
)
{
_Regulator_Control *the_regulator = (_Regulator_Control *)arg;
rtems_status_code sc;
size_t to_dequeue;
_Regulator_Message_t regulator_message;
size_t regulator_message_size;
bool release_it;
the_regulator->delivery_thread_is_running = true;
/**
* This thread uses a rate monotonic period object instance. A rate
* monotonic period object must be created by the thread using it.
* It can be deleted by any thread which simplifies clean up.
*
* The rate_monotonic_create() call can fail if the application
* is incorrectly configured. This thread has no way to report the
* failure. If it continues with an invalid id, then the thread will
* not block on the period and spin continuously consuming CPU. The only
* alternatives are to invoke rtems_fatal_error_occurred() or silently
* exit the thread.
*/
sc = rtems_rate_monotonic_create(
rtems_build_name('P', 'E', 'R', 'D'),
&the_regulator->delivery_thread_period_id
);
if (sc != RTEMS_SUCCESSFUL) {
goto exit_delivery_thread;
}
/**
* Loop on the rate_monotonic_period() based on the specified period.
*/
while (1) {
sc = rtems_rate_monotonic_period(
the_regulator->delivery_thread_period_id,
the_regulator->Attributes.delivery_thread_period
);
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
/**
* If the delivery thread has been requested to exit, then
* quit processing messages, break out of this loop, and exit
* this thread.
*/
if (the_regulator->delivery_thread_request_exit) {
break;
}
/**
* Loop for the configured number of messages to deliver per period.
* If we reach the point, there are no more messages, block for the
* rest of this period. If there are messages, deliver them.
*/
for (to_dequeue = 0;
to_dequeue < the_regulator->Attributes.maximum_to_dequeue_per_period;
to_dequeue++) {
regulator_message_size = sizeof(_Regulator_Message_t);
sc = rtems_message_queue_receive(
the_regulator->queue_id,
&regulator_message,
&regulator_message_size,
RTEMS_NO_WAIT,
0
);
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
if (sc != RTEMS_SUCCESSFUL) {
break;
}
release_it = the_regulator->Attributes.deliverer(
the_regulator->Attributes.deliverer_context,
regulator_message.buffer,
regulator_message.length
);
the_regulator->Statistics.delivered++;
/**
* The message was successfully delivered. If the delivery function
* wants the buffer returned, do it now. The delivery to the Destination
* may involve handing the buffer off to something like DMA
* and need to wait for it to complete before releasing the buffer.
*
* Note that this is the underlying RTEMS service
* used by @a rtems_regulator_obtain_buffer() and @a
* rtems_regulator_release_buffer().
*/
if (release_it == true) {
the_regulator->Statistics.released++;
sc = rtems_partition_return_buffer(
the_regulator->messages_partition_id,
regulator_message.buffer
);
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
}
}
}
/**
* This thread was requested to exit. Do so.
*/
exit_delivery_thread:
the_regulator->delivery_thread_is_running = false;
the_regulator->delivery_thread_has_exited = true;
(void) rtems_rate_monotonic_delete(the_regulator->delivery_thread_period_id);
rtems_task_exit();
}
/**
* @ingroup RegulatorInternalAPI
*
* This method frees the resources associated with a regulator instance.
* The resources are freed in the opposite of the order in which they are
* allocated. This is used on error cases in @a rtems_regulator_create() and in
* @a rtems_regulator_delete().
*
* @param[in] the_regulator is the instance to operate upon
* @param[in] ticks is the length of time to wait for the delivery thread
* to exit
*
* @return This method returns true is successful and false on timeout.
*/
static bool _Regulator_Free_helper(
_Regulator_Control *the_regulator,
rtems_interval ticks
)
{
rtems_status_code sc;
/*
* If the output thread has not started running, then we can just delete it.
*/
if (ticks == 0 || the_regulator->delivery_thread_is_running == false) {
sc = rtems_task_delete(the_regulator->delivery_thread_id);
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
} else {
rtems_interval remaining = ticks;
the_regulator->delivery_thread_request_exit = true;
while (1) {
if (the_regulator->delivery_thread_has_exited) {
break;
}
if (remaining == 0) {
return false;
}
(void) rtems_task_wake_after(1);
remaining--;
}
}
/*
* The output thread deletes the rate monotonic period that it created.
*/
/*
* The regulator's message_queue_storage is implicitly freed by this call.
*/
sc = rtems_message_queue_delete(the_regulator->queue_id);
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
sc = rtems_partition_delete(the_regulator->messages_partition_id);
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
if (the_regulator->message_memory) {
free(the_regulator->message_memory);
}
the_regulator->initialized = 0;
free(the_regulator);
return true;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_create(
rtems_regulator_attributes *attributes,
rtems_regulator_instance **regulator
)
{
_Regulator_Control *the_regulator;
rtems_status_code sc;
size_t alloc_size;
/**
* Perform basic validation of parameters
*/
if (attributes == NULL) {
return RTEMS_INVALID_ADDRESS;
}
if (regulator == NULL) {
return RTEMS_INVALID_ADDRESS;
}
/**
* Verify attributes are OK. Some are checked by calls to object create
* methods. Specifically the following are not checked:
*
* - delivery_thread_priority by rtems_task_create()
* - delivery_thread_stack_size can be any value
*/
if (attributes->deliverer == NULL) {
return RTEMS_INVALID_ADDRESS;
}
if (attributes->maximum_messages == 0) {
return RTEMS_INVALID_NUMBER;
}
if (attributes->maximum_message_size == 0) {
return RTEMS_INVALID_SIZE;
}
if (attributes->maximum_to_dequeue_per_period == 0) {
return RTEMS_INVALID_NUMBER;
}
if (attributes->delivery_thread_period == 0) {
return RTEMS_INVALID_NUMBER;
}
/**
* Allocate memory for regulator instance
*/
the_regulator = (_Regulator_Control *) calloc(sizeof(_Regulator_Control), 1);
if (the_regulator == NULL) {
return RTEMS_NO_MEMORY;
}
/**
* We do NOT want the delivery_thread_id field to be initialized to 0. If the
* @a rtems_task_create() fails, then the field will not be overwritten.
* This results in an attempt to rtems_task_delete(0) during clean
* up. The thread ID of 0 is self which results in the calling thread
* accidentally deleting itself.
*/
the_regulator->delivery_thread_id = (rtems_id) -1;
/**
* Copy the attributes to an internal area for later use
*/
the_regulator->Attributes = *attributes;
/**
* Allocate memory for the messages. There is no need to zero out the
* message memory because the user should fill that in.
*/
alloc_size = attributes->maximum_message_size * attributes->maximum_messages;
the_regulator->message_memory = calloc(alloc_size, 1);
if (the_regulator->message_memory == NULL) {
_Regulator_Free_helper(the_regulator, 0);
return RTEMS_NO_MEMORY;
}
/**
* Associate message memory with a partition so allocations are atomic
*/
sc = rtems_partition_create(
rtems_build_name('P', 'O', 'O', 'L'),
the_regulator->message_memory,
alloc_size,
attributes->maximum_message_size,
RTEMS_DEFAULT_ATTRIBUTES,
&the_regulator->messages_partition_id
);
if (sc != RTEMS_SUCCESSFUL) {
_Regulator_Free_helper(the_regulator, 0);
return sc;
}
/**
* Create the message queue between the sender and output thread
*/
RTEMS_MESSAGE_QUEUE_BUFFER(sizeof(_Regulator_Message_t)) regulator_message_t;
size_t storage_size = sizeof(regulator_message_t) * attributes->maximum_messages;
the_regulator->message_queue_storage = malloc(storage_size);
if (the_regulator->message_queue_storage == NULL) {
_Regulator_Free_helper(the_regulator, 0);
return RTEMS_NO_MEMORY;
}
rtems_message_queue_config mq_config = {
.name = rtems_build_name('S', 'N', 'D', 'Q'),
.maximum_pending_messages = attributes->maximum_messages,
.maximum_message_size = sizeof(_Regulator_Message_t),
.storage_area = the_regulator->message_queue_storage,
.storage_size = storage_size,
.storage_free = free,
.attributes = RTEMS_DEFAULT_ATTRIBUTES
};
sc = rtems_message_queue_construct(
&mq_config,
&the_regulator->queue_id
);
if (sc != RTEMS_SUCCESSFUL) {
_Regulator_Free_helper(the_regulator, 0);
return sc;
}
/**
* @note A rate monotonic period object must be created by the thread
* using it. Thus that specific create operation is not included
* in this method. All other resources are allocated here.
*/
/**
* Create the output thread Using the priority and stack size attributes
* specified by the user.
*/
sc = rtems_task_create(
rtems_build_name('R', 'E', 'G', 'U'),
attributes->delivery_thread_priority,
attributes->delivery_thread_stack_size,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&the_regulator->delivery_thread_id
);
if (sc != RTEMS_SUCCESSFUL) {
_Regulator_Free_helper(the_regulator, 0);
return sc;
}
/**
* Start the output thread.
*
* @note There should be no way this call can fail. The task id is valid,
* the regulator output thread entry point is valid, and the argument
* is valid.
*/
the_regulator->delivery_thread_is_running = true;
the_regulator->delivery_thread_request_exit = false;
the_regulator->delivery_thread_has_exited = false;
sc = rtems_task_start(
the_regulator->delivery_thread_id,
_Regulator_Output_task_body,
(rtems_task_argument) the_regulator
);
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
/**
* The regulator is successfully initialized. Set the initialized field
* to reflect this and return the instance pointer.
*/
the_regulator->initialized = REGULATOR_INITIALIZED;
*regulator = (void *)the_regulator;
return RTEMS_SUCCESSFUL;
}
/**
* @brief Validate the regulator instance provided by the user
*
* Validate the regulator instance provided by the user
*
* @param[in] regulator is the instance provided by the user
* @param[inout] status will contain the RTEMS status for this check
*
* @return This method returns a @a _Regulator_Control instance pointer
* which is NULL if invalid or points to the internal regulator
* control structure if valid.
*/
static inline _Regulator_Control *_Regulator_Get(
rtems_regulator_instance *regulator,
rtems_status_code *status
)
{
_Regulator_Control *the_regulator = (_Regulator_Control *) regulator;
if (the_regulator == NULL) {
*status = RTEMS_INVALID_ADDRESS;
return NULL;
}
if (the_regulator->initialized != REGULATOR_INITIALIZED) {
*status = RTEMS_INCORRECT_STATE;
return NULL;
}
status = RTEMS_SUCCESSFUL;
return the_regulator;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_delete(
rtems_regulator_instance *regulator,
rtems_interval ticks
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get(regulator, &status);
if (the_regulator == NULL) {
return status;
}
/**
* There can be no buffers outstanding
*/
_Regulator_Statistics *stats = &the_regulator->Statistics;
if (stats->obtained != stats->released ) {
return RTEMS_RESOURCE_IN_USE;
}
/**
* Free the resources associated with this regulator instance.
*/
bool bc;
bc = _Regulator_Free_helper(the_regulator, ticks);
if (bc == false) {
return RTEMS_TIMEOUT;
}
return RTEMS_SUCCESSFUL;
}
/**
* @ingroup RegulatorInternalAPI
*
* Allocate a buffer for the caller using the internal partition.
*/
rtems_status_code rtems_regulator_obtain_buffer(
rtems_regulator_instance *regulator,
void **buffer
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get(regulator, &status);
if (the_regulator == NULL) {
return status;
}
/**
* Allocate a buffer for the user application from the buffer pool managed
* by an Classic API partition.
*/
status = rtems_partition_get_buffer(
the_regulator->messages_partition_id,
buffer
);
if (status == RTEMS_SUCCESSFUL) {
the_regulator->Statistics.obtained++;
}
return status;
}
/**
* @ingroup RegulatorInternalAPI
*
* Allocate a buffer for the caller using the internal partition.
*/
rtems_status_code rtems_regulator_release_buffer(
rtems_regulator_instance *regulator,
void *buffer
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get(regulator, &status);
if (the_regulator == NULL) {
return status;
}
/**
* Deallocate the buffer to the buffer pool managed by a Classic
* API partition.
*/
status = rtems_partition_return_buffer(
the_regulator->messages_partition_id,
buffer
);
if (status == RTEMS_SUCCESSFUL) {
the_regulator->Statistics.released++;
}
return status;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_send(
rtems_regulator_instance *regulator,
void *message,
size_t length
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
_Regulator_Message_t regulator_message;
the_regulator = (_Regulator_Control *) regulator;
/**
* Validate the arguments and ensure the regulator was successfully
* initialized.
*/
if (message == NULL) {
return RTEMS_INVALID_ADDRESS;
}
if (length == 0) {
return RTEMS_INVALID_NUMBER;
}
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get(regulator, &status);
if (the_regulator == NULL) {
return status;
}
/**
* Place the message pointer and length into a temporary structure. This
* lets the implementation internally send the message by reference and
* have a zero-copy implementation.
*/
regulator_message.buffer = message;
regulator_message.length = length;
/**
* Send the application message to the output thread for delivery using
* a Classic API message queue.
*/
status = rtems_message_queue_send(
the_regulator->queue_id,
&regulator_message,
sizeof(_Regulator_Message_t)
);
if (status != RTEMS_SUCCESSFUL) {
return status;
}
return status;
}
/**
* @ingroup RegulatorInternalAPI
*/
rtems_status_code rtems_regulator_get_statistics(
rtems_regulator_instance *regulator,
rtems_regulator_statistics *statistics
)
{
_Regulator_Control *the_regulator;
rtems_status_code status;
/**
* Validate the arguments and ensure the regulator was successfully
* initialized.
*/
if (statistics == NULL) {
return RTEMS_INVALID_ADDRESS;
}
/**
* Convert external handle to internal instance pointer
*/
the_regulator = _Regulator_Get(regulator, &status);
if (the_regulator == NULL) {
return status;
}
/**
* Zero out the statistics structure in case the get period statistics
* fails below.
*/
memset(statistics, 0, sizeof(rtems_regulator_statistics));
/**
* Fill in the caller's statistics structure from information
* maintained by the regulator instance about buffers processed.
*/
statistics->obtained = the_regulator->Statistics.obtained;
statistics->released = the_regulator->Statistics.released;
statistics->delivered = the_regulator->Statistics.delivered;
/**
* Attempt to retrieve the delivery thread's period's statistics.
*
* NOTE; If the Delivery Thread has not run yet, the period will not
* exist yet. We should not fail for this reason but it is why
* we zeroed out the entire structure above.
*/
(void) rtems_rate_monotonic_get_statistics(
the_regulator->delivery_thread_period_id,
&statistics->period_statistics
);
return RTEMS_SUCCESSFUL;
}

View File

@@ -510,6 +510,8 @@ links:
uid: objmpci
- role: build-dependency
uid: objpci
- role: build-dependency
uid: objregulator
- role: build-dependency
uid: objpsxsgnl
- role: build-dependency

View File

@@ -0,0 +1,18 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: objects
cflags: []
copyrights:
- Copyright (C) 2023 OAR Corporatoin
cppflags: []
cxxflags: []
enabled-by: true
includes: []
install:
- destination: ${BSP_INCLUDEDIR}/rtems
source:
- cpukit/include/rtems/regulator.h
- cpukit/include/rtems/regulatorimpl.h
links: []
source:
- cpukit/libmisc/regulator/regulator.c
type: build

View File

@@ -228,6 +228,8 @@ links:
uid: record01
- role: build-dependency
uid: record02
- role: build-dependency
uid: regulator01
- role: build-dependency
uid: rtmonuse
- role: build-dependency

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2023 OAR Corporation
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes: []
ldflags:
- -Wl,--wrap=malloc
links: []
source:
- testsuites/libtests/regulator01/regulator01.c
- testsuites/libtests/regulator01/rtems_config.c
stlib: []
target: testsuites/libtests/regulator01.exe
type: build
use-after: []
use-before: []

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
# SPDX-License-Identifier: BSD-2-Clause
# Copyright (c) 2023 OAR Corporation
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
This file describes the directives and concepts tested by this test set.
test set name: regulator01
directives:
+ rtems_regulator_create
+ rtems_regulator_delete
+ rtems_regulator_obtain_buffer
+ rtems_regulator_release_buffer
+ rtems_regulator_send
concepts
+ Verify rtems_regulator_create() maximum_to_dequeue_per_period
+ Verify rtems_regulator_create() NULL attributes error
+ Verify rtems_regulator_create NULL regulator error
+ Verify rtems_regulator_create deliverer is NULL
+ Verify rtems_regulator_create maximum_messages is 0 error
+ Verify rtems_regulator_create maximum_message_size is 0 error
+ Verify rtems_regulator_create maximum_to_dequeue_per_period is 0 error
+ Verify rtems_regulator_create returns error on failure to allocate regulator
+ Verify rtems_regulator_create returns error on failure to allocate buffers
+ Verify rtems_regulator_create and delete work
+ Verify rtems_regulator_create rtems_partition_create error
+ Verify rtems_regulator_create rtems_message_queue_create error
+ Verify rtems_regulator_create rtems_task_create error
+ Verify Regulator Output Thread Handles Error on Period Create
+ Verify rtems_regulator_delete NULL regulator error
+ Verify rtems_regulator_delete uninitialized regulator error
+ Verify rtems_regulator_delete successful case
+ Verify rtems_regulator_obtain_buffer NULL regulator error
+ Verify rtems_regulator_obtain_buffer uninitialized regulator error
+ Verify rtems_regulator_obtain_buffer successful case
+ Verify rtems_regulator_release_buffer NULL regulator error
+ Verify rtems_regulator_release_buffer uninitialized regulator error
+ Verify rtems_regulator_release_buffer successful case
+ Verify rtems_regulator_send NULL regulator error
+ Verify rtems_regulator_send NULL message error
+ Verify rtems_regulator_send zero length message error
+ Verify rtems_regulator_send uninitialized regulator error
+ Verify rtems_regulator_send and output thread delivers message
+ Verify rtems_regulator_send and cannot delete with outstanding messages

View File

@@ -0,0 +1,59 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @brief RTEMS Configuration for regulator tests
*/
/*
* COPYRIGHT (c) 2022. * On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <rtems.h>
rtems_task test_regulator(rtems_task_argument);
#include <bsp.h> /* for device driver prototypes */
/* NOTICE: the clock driver is explicitly disabled */
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT_TASK_ENTRY_POINT test_regulator
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
/* Use hard limits to make it easier to trip object creation errors */
#define CONFIGURE_MAXIMUM_TASKS 2
#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES 1
#define CONFIGURE_MAXIMUM_PARTITIONS 1
#define CONFIGURE_MAXIMUM_PERIODS 1
#define CONFIGURE_UNIFIED_WORK_AREAS
#define CONFIGURE_MINIMUM_TASK_STACK_SIZE (8 * 1024)
#define CONFIGURE_INIT
#include <rtems/confdefs.h>