ring_buffer: the great simplification
This code is rather hairy. When I look at it I don't like the way it stares back at me. First, the rewind business looks fishy. It has to die. And we don't have to rely on modulus either. Not even for non-power-of-2 buffers. Let's kill that distinction too and make all sizes always "high performance". The code is now entirely relying only on simple ALU operations (add, sub and compare). The key assumption: 32-bit values do wrap around after max range has been reached. No saturation. All architectures supported by Zephyr do that. Some stats: lib/os/ring_buffer.c: 62 insertions(+), 124 deletions(-) ring_buffer.c.obj before after diff ---------------------------------------------- frdm_k64f 1224 1136 -88 m2gl025_miv 2485 2079 -406 mps2_an385 1228 1132 -96 mps2_an521 1228 1132 -96 native_posix 1546 1496 -50 native_posix_64 1598 1595 -3 nsim_hs_mpuv6 1252 1192 -60 nsim_hs_smp 1252 1192 -60 nsim_sem 1252 1192 -60 qemu_arc_em 1324 1192 -132 qemu_arc_hs6x 1824 1620 -204 qemu_arc_hs 1252 1192 -60 qemu_cortex_a53_smp 2154 1888 -266 qemu_cortex_a53 2154 1888 -266 qemu_cortex_a9 1938 1792 -146 Before (qemu_cortex_a53): START - test_ringbuffer_performance 1 byte put-get, avg cycles: 52 4 byte put-get, avg cycles: 47 1 byte put claim-finish, avg cycles: 39 5 byte put claim-finish, avg cycles: 41 5 byte get claim-finish, avg cycles: 52 PASS - test_ringbuffer_performance in 0.8 seconds After (qemu_cortex_a53): START - test_ringbuffer_performance 1 byte put-get, avg cycles: 34 4 byte put-get, avg cycles: 41 1 byte put claim-finish, avg cycles: 27 5 byte put claim-finish, avg cycles: 29 5 byte get claim-finish, avg cycles: 29 PASS - test_ringbuffer_performance in 0.4 seconds Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
This commit is contained in:
parent
c2543320d8
commit
099850e916
|
@ -60,13 +60,8 @@ buffer will be used later. Macros for combining these steps in a
|
|||
single static declaration exist for convenience.
|
||||
:c:macro:`RING_BUF_DECLARE` will declare and statically initialize a ring
|
||||
buffer with a specified byte count, where
|
||||
:c:macro:`RING_BUF_ITEM_DECLARE_SIZE` will declare and statically
|
||||
:c:macro:`RING_BUF_ITEM_DECLARE` will declare and statically
|
||||
initialize a buffer with a given count of 32 bit words.
|
||||
:c:macro:`RING_BUF_ITEM_DECLARE_POW2` can be used to initialize an
|
||||
items-mode buffer with a memory region guaranteed to be a power of
|
||||
two, which enables various optimizations internal to the
|
||||
implementation. No power-of-two initialization is available for
|
||||
bytes-mode ring buffers.
|
||||
|
||||
"Bytes" data may be copied into the ring buffer using
|
||||
:c:func:`ring_buf_put`, passing a data pointer and byte count. These
|
||||
|
@ -104,7 +99,7 @@ does not fit in its entirety.
|
|||
|
||||
The user can manage the capacity of a ring buffer without modifying it
|
||||
using either :c:func:`ring_buf_space_get` or :c:func:`ring_buf_item_space_get`
|
||||
which returns the number of free bytes or free items respectively,
|
||||
which returns the number of free bytes or free 32-bit item words respectively,
|
||||
or by testing the :c:func:`ring_buf_is_empty` predicate.
|
||||
|
||||
Finally, a :c:func:`ring_buf_reset` call exists to immediately empty a
|
||||
|
@ -117,8 +112,7 @@ Data item mode
|
|||
==============
|
||||
|
||||
A **data item mode** ring buffer instance is declared using
|
||||
:c:macro:`RING_BUF_ITEM_DECLARE_POW2()` or
|
||||
:c:macro:`RING_BUF_ITEM_DECLARE_SIZE()` and accessed using
|
||||
:c:macro:`RING_BUF_ITEM_DECLARE()` and accessed using
|
||||
:c:func:`ring_buf_item_put` and :c:func:`ring_buf_item_get`.
|
||||
|
||||
A ring buffer **data item** is an array of 32-bit words from 0 to 1020 bytes
|
||||
|
@ -174,37 +168,26 @@ mutexes and/or use semaphores to notify consumers that there is data to
|
|||
read.
|
||||
|
||||
For the trivial case of one producer and one consumer, concurrency
|
||||
shouldn't be needed.
|
||||
control shouldn't be needed.
|
||||
|
||||
Internal Operation
|
||||
==================
|
||||
|
||||
If the size of the data buffer is a power of two, the ring buffer
|
||||
uses efficient masking operations instead of expensive modulo operations
|
||||
when enqueuing and dequeuing data items. This option is applicable only for
|
||||
data item mode.
|
||||
|
||||
Data streamed through a ring buffer is always written to the next byte
|
||||
within the buffer, wrapping around to the first element after reaching
|
||||
the end, thus the "ring" structure. Internally, the ``struct
|
||||
ring_buf`` contains its own buffer pointer and its size, and also a
|
||||
"head" and "tail" index representing where the next read and write
|
||||
set of "head" and "tail" indices representing where the next read and write
|
||||
operations may occur.
|
||||
|
||||
This boundary is invisible to the user using the normal put/get APIs,
|
||||
but becomes a barrier to the "claim" API, because obviously no
|
||||
contiguous region can be returned that crosses the end of the buffer.
|
||||
This can be surprising to application code, and produce performance
|
||||
artifacts when transfers need to alias closely to the size of the
|
||||
buffer, as the number of calls to claim/finish need to double for such
|
||||
artifacts when transfers need to happen close to the end of the
|
||||
buffer, as the number of calls to claim/finish needs to double for such
|
||||
transfers.
|
||||
|
||||
When running in items mode (only), the ring buffer contains two
|
||||
implementations for the modular arithmetic required to compute "next
|
||||
element" offsets. One is used for arbitrary sized buffers, but the
|
||||
other is optimized for power of two sizes and can replace the compare
|
||||
and subtract steps with a simple bitmask in several places, at the
|
||||
cost of testing the "mask" value for each call.
|
||||
|
||||
|
||||
Implementation
|
||||
**************
|
||||
|
@ -240,26 +223,14 @@ Alternatively, a ring buffer can be defined and initialized at compile time
|
|||
using one of two macros at file scope. Each macro defines both the ring
|
||||
buffer itself and its data buffer.
|
||||
|
||||
The following code defines and initializes an empty **data item mode**
|
||||
ring with a power-of-two sized data buffer, which can be accessed using
|
||||
efficient masking operations.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Buffer with 2^8 (or 256) words */
|
||||
RING_BUF_ITEM_DECLARE_POW2(my_ring_buf, 8);
|
||||
|
||||
The following code defines a **data item mode** ring with an arbitrary-sized
|
||||
data buffer:
|
||||
The following code defines a **data item mode** ring buffer:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MY_RING_BUF_WORDS 93
|
||||
RING_BUF_ITEM_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_WORDS);
|
||||
RING_BUF_ITEM_DECLARE(my_ring_buf, MY_RING_BUF_WORDS);
|
||||
|
||||
The following code defines a ring buffer with an arbitrary-sized data buffer,
|
||||
which can be accessed using less efficient modulo operations. Ring buffer is
|
||||
intended to be used for raw bytes.
|
||||
The following code defines a ring buffer intended to be used for raw bytes:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <kernel.h>
|
||||
#include <sys/util.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -27,31 +26,20 @@ extern "C" {
|
|||
#define RING_BUFFER_MAX_SIZE 0x80000000U
|
||||
|
||||
#define RING_BUFFER_SIZE_ASSERT_MSG \
|
||||
"Size too big, if it is the ring buffer test check custom max size"
|
||||
"Size too big"
|
||||
|
||||
/**
|
||||
* @brief A structure to represent a ring buffer
|
||||
*/
|
||||
struct ring_buf {
|
||||
uint32_t head; /**< Index in buf for the head element */
|
||||
uint32_t tail; /**< Index in buf for the tail element */
|
||||
union ring_buf_misc {
|
||||
struct ring_buf_misc_item_mode {
|
||||
uint32_t dropped_put_count; /**< Running tally of the
|
||||
* number of failed put
|
||||
* attempts.
|
||||
*/
|
||||
} item_mode;
|
||||
struct ring_buf_misc_byte_mode {
|
||||
uint32_t tmp_tail;
|
||||
uint32_t tmp_head;
|
||||
} byte_mode;
|
||||
} misc;
|
||||
uint32_t size;
|
||||
uint8_t *buffer;
|
||||
uint32_t mask; /**< Modulo mask if size is a power of 2 */
|
||||
|
||||
struct k_spinlock lock;
|
||||
int32_t put_head;
|
||||
int32_t put_tail;
|
||||
int32_t put_base;
|
||||
int32_t get_head;
|
||||
int32_t get_tail;
|
||||
int32_t get_base;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -61,39 +49,24 @@ struct ring_buf {
|
|||
*/
|
||||
|
||||
/**
|
||||
* @brief Define and initialize an "item based" high performance ring buffer.
|
||||
* @brief Define and initialize an "item based" ring buffer with a power of 2.
|
||||
*
|
||||
* This macro establishes a ring buffer whose size must be a power of 2;
|
||||
* that is, the ring buffer contains 2^pow 32-bit words, where @a pow is
|
||||
* the specified ring buffer size exponent. A high performance ring buffer
|
||||
* doesn't require the use of modulo arithmetic operations to maintain itself.
|
||||
*
|
||||
* Each data item is an array of 32-bit words (from zero to 1020 bytes in
|
||||
* length), coupled with a 16-bit type identifier and an 8-bit integer value.
|
||||
*
|
||||
* The ring buffer can be accessed outside the module where it is defined
|
||||
* using:
|
||||
*
|
||||
* @code extern struct ring_buf <name>; @endcode
|
||||
* This macro establishes an "item based" ring buffer by specifying its
|
||||
* size using a power of 2. This exists mainly for backward compatibility
|
||||
* reasons. @ref RING_BUF_ITEM_DECLARE should be used instead.
|
||||
*
|
||||
* @param name Name of the ring buffer.
|
||||
* @param pow Ring buffer size exponent.
|
||||
*/
|
||||
#define RING_BUF_ITEM_DECLARE_POW2(name, pow) \
|
||||
BUILD_ASSERT(BIT(pow) < RING_BUFFER_MAX_SIZE / 4,\
|
||||
RING_BUFFER_SIZE_ASSERT_MSG); \
|
||||
static uint32_t __noinit _ring_buffer_data_##name[BIT(pow)]; \
|
||||
struct ring_buf name = { \
|
||||
.size = 4 * BIT(pow), \
|
||||
.mask = (4 * BIT(pow)) - 1, \
|
||||
.buffer = (uint8_t *) _ring_buffer_data_##name \
|
||||
}
|
||||
RING_BUF_ITEM_DECLARE(name, BIT(pow))
|
||||
|
||||
/**
|
||||
* @brief Define and initialize a standard ring buffer.
|
||||
* @brief Define and initialize an "item based" ring buffer.
|
||||
*
|
||||
* This macro establishes a ring buffer of an arbitrary size. A standard
|
||||
* ring buffer uses modulo arithmetic operations to maintain itself.
|
||||
* This macro establishes an "item based" ring buffer. Each data item is
|
||||
* an array of 32-bit words (from zero to 1020 bytes in length), coupled
|
||||
* with a 16-bit type identifier and an 8-bit integer value.
|
||||
*
|
||||
* The ring buffer can be accessed outside the module where it is defined
|
||||
* using:
|
||||
|
@ -103,7 +76,7 @@ struct ring_buf {
|
|||
* @param name Name of the ring buffer.
|
||||
* @param size32 Size of ring buffer (in 32-bit words).
|
||||
*/
|
||||
#define RING_BUF_ITEM_DECLARE_SIZE(name, size32) \
|
||||
#define RING_BUF_ITEM_DECLARE(name, size32) \
|
||||
BUILD_ASSERT((size32) < RING_BUFFER_MAX_SIZE / 4,\
|
||||
RING_BUFFER_SIZE_ASSERT_MSG); \
|
||||
static uint32_t __noinit _ring_buffer_data_##name[size32]; \
|
||||
|
@ -112,6 +85,18 @@ struct ring_buf {
|
|||
.buffer = (uint8_t *) _ring_buffer_data_##name \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Define and initialize an "item based" ring buffer.
|
||||
*
|
||||
* This exists for backward compatibility reasons. @ref RING_BUF_ITEM_DECLARE
|
||||
* should be used instead.
|
||||
*
|
||||
* @param name Name of the ring buffer.
|
||||
* @param size32 Size of ring buffer (in 32-bit words).
|
||||
*/
|
||||
#define RING_BUF_ITEM_DECLARE_SIZE(name, size32) \
|
||||
RING_BUF_ITEM_DECLARE(name, size32)
|
||||
|
||||
/**
|
||||
* @brief Define and initialize a ring buffer for byte data.
|
||||
*
|
||||
|
@ -141,10 +126,6 @@ struct ring_buf {
|
|||
* This routine initializes a ring buffer, prior to its first use. It is only
|
||||
* used for ring buffers not defined using RING_BUF_DECLARE.
|
||||
*
|
||||
* Setting @a size to a power of 2 establishes a high performance ring buffer
|
||||
* that doesn't require the use of modulo arithmetic operations to maintain
|
||||
* itself.
|
||||
*
|
||||
* @param buf Address of ring buffer.
|
||||
* @param size Ring buffer size (in bytes).
|
||||
* @param data Ring buffer data area (uint8_t data[size]).
|
||||
|
@ -155,26 +136,20 @@ static inline void ring_buf_init(struct ring_buf *buf,
|
|||
{
|
||||
__ASSERT(size < RING_BUFFER_MAX_SIZE, RING_BUFFER_SIZE_ASSERT_MSG);
|
||||
|
||||
memset(buf, 0, sizeof(struct ring_buf));
|
||||
buf->size = size;
|
||||
buf->buffer = data;
|
||||
if (is_power_of_two(size)) {
|
||||
buf->mask = size - 1U;
|
||||
} else {
|
||||
buf->mask = 0U;
|
||||
}
|
||||
buf->put_head = buf->put_tail = buf->put_base = 0;
|
||||
buf->get_head = buf->get_tail = buf->get_base = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an "item based" ring buffer.
|
||||
*
|
||||
* This routine initializes a ring buffer, prior to its first use. It is only
|
||||
* used for ring buffers not defined using RING_BUF_ITEM_DECLARE_POW2 or
|
||||
* RING_BUF_ITEM_DECLARE_SIZE.
|
||||
* used for ring buffers not defined using RING_BUF_ITEM_DECLARE.
|
||||
*
|
||||
* Setting @a size to a power of 2 establishes a high performance ring buffer
|
||||
* that doesn't require the use of modulo arithmetic operations to maintain
|
||||
* itself.
|
||||
* Each data item is an array of 32-bit words (from zero to 1020 bytes in
|
||||
* length), coupled with a 16-bit type identifier and an 8-bit integer value.
|
||||
*
|
||||
* Each data item is an array of 32-bit words (from zero to 1020 bytes in
|
||||
* length), coupled with a 16-bit type identifier and an 8-bit integer value.
|
||||
|
@ -196,9 +171,12 @@ static inline void ring_buf_item_init(struct ring_buf *buf,
|
|||
*
|
||||
* @param buf Address of ring buffer.
|
||||
*
|
||||
* @return 1 if the ring buffer is empty, or 0 if not.
|
||||
* @return true if the ring buffer is empty, or false if not.
|
||||
*/
|
||||
int ring_buf_is_empty(struct ring_buf *buf);
|
||||
static inline bool ring_buf_is_empty(struct ring_buf *buf)
|
||||
{
|
||||
return buf->get_head == buf->put_tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset ring buffer state.
|
||||
|
@ -207,9 +185,8 @@ int ring_buf_is_empty(struct ring_buf *buf);
|
|||
*/
|
||||
static inline void ring_buf_reset(struct ring_buf *buf)
|
||||
{
|
||||
buf->head = 0;
|
||||
buf->tail = 0;
|
||||
memset(&buf->misc, 0, sizeof(buf->misc));
|
||||
buf->put_head = buf->put_tail = buf->put_base = 0;
|
||||
buf->get_head = buf->get_tail = buf->get_base = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -219,7 +196,10 @@ static inline void ring_buf_reset(struct ring_buf *buf)
|
|||
*
|
||||
* @return Ring buffer free space (in bytes).
|
||||
*/
|
||||
uint32_t ring_buf_space_get(struct ring_buf *buf);
|
||||
static inline uint32_t ring_buf_space_get(struct ring_buf *buf)
|
||||
{
|
||||
return buf->size - (buf->put_head - buf->get_tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine free space in an "item based" ring buffer.
|
||||
|
@ -252,7 +232,10 @@ static inline uint32_t ring_buf_capacity_get(struct ring_buf *buf)
|
|||
*
|
||||
* @return Ring buffer space used (in bytes).
|
||||
*/
|
||||
uint32_t ring_buf_size_get(struct ring_buf *buf);
|
||||
static inline uint32_t ring_buf_size_get(struct ring_buf *buf)
|
||||
{
|
||||
return buf->put_tail - buf->get_head;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a data item to a ring buffer.
|
||||
|
|
|
@ -9,16 +9,6 @@
|
|||
#include <sys/ring_buffer.h>
|
||||
#include <string.h>
|
||||
|
||||
/* LCOV_EXCL_START */
|
||||
/* The weak function used to allow overwriting it in the test and trigger
|
||||
* rewinding earlier.
|
||||
*/
|
||||
uint32_t __weak ring_buf_get_rewind_threshold(void)
|
||||
{
|
||||
return RING_BUFFER_MAX_SIZE;
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
||||
/**
|
||||
* Internal data structure for a buffer header.
|
||||
*
|
||||
|
@ -31,52 +21,6 @@ struct ring_element {
|
|||
uint32_t value :8; /**< Room for small integral values */
|
||||
};
|
||||
|
||||
static uint32_t mod(struct ring_buf *buf, uint32_t val)
|
||||
{
|
||||
return likely(buf->mask) ? val & buf->mask : val % buf->size;
|
||||
}
|
||||
|
||||
static uint32_t get_rewind_value(uint32_t buf_size, uint32_t threshold)
|
||||
{
|
||||
/* Rewind value is rounded to buffer size and decreased by buffer_size.
|
||||
* This is done to ensure that there will be no negative numbers after
|
||||
* subtraction. That could happen because tail is rewinded first and
|
||||
* head (which follows tail) is rewinded on next getting.
|
||||
*/
|
||||
return buf_size * (threshold / buf_size - 1);
|
||||
}
|
||||
|
||||
int ring_buf_is_empty(struct ring_buf *buf)
|
||||
{
|
||||
uint32_t tail = buf->tail;
|
||||
uint32_t head = buf->head;
|
||||
|
||||
if (tail < head) {
|
||||
tail += get_rewind_value(buf->size,
|
||||
ring_buf_get_rewind_threshold());
|
||||
}
|
||||
|
||||
return (head == tail);
|
||||
}
|
||||
|
||||
uint32_t ring_buf_size_get(struct ring_buf *buf)
|
||||
{
|
||||
uint32_t tail = buf->tail;
|
||||
uint32_t head = buf->head;
|
||||
|
||||
if (tail < head) {
|
||||
tail += get_rewind_value(buf->size,
|
||||
ring_buf_get_rewind_threshold());
|
||||
}
|
||||
|
||||
return tail - head;
|
||||
}
|
||||
|
||||
uint32_t ring_buf_space_get(struct ring_buf *buf)
|
||||
{
|
||||
return buf->size - ring_buf_size_get(buf);
|
||||
}
|
||||
|
||||
int ring_buf_item_put(struct ring_buf *buf, uint16_t type, uint8_t value,
|
||||
uint32_t *data32, uint8_t size32)
|
||||
{
|
||||
|
@ -163,52 +107,50 @@ int ring_buf_item_get(struct ring_buf *buf, uint16_t *type, uint8_t *value,
|
|||
|
||||
uint32_t ring_buf_put_claim(struct ring_buf *buf, uint8_t **data, uint32_t size)
|
||||
{
|
||||
uint32_t space, trail_size, allocated, tmp_trail_mod;
|
||||
uint32_t head = buf->head;
|
||||
uint32_t tmp_tail = buf->misc.byte_mode.tmp_tail;
|
||||
uint32_t free_space, wrap_size;
|
||||
int32_t base;
|
||||
|
||||
if (buf->misc.byte_mode.tmp_tail < head) {
|
||||
/* Head is already rewinded but tail is not */
|
||||
tmp_tail += get_rewind_value(buf->size, ring_buf_get_rewind_threshold());
|
||||
base = buf->put_base;
|
||||
wrap_size = buf->put_head - base;
|
||||
if (unlikely(wrap_size >= buf->size)) {
|
||||
/* put_base is not yet adjusted */
|
||||
wrap_size -= buf->size;
|
||||
base += buf->size;
|
||||
}
|
||||
wrap_size = buf->size - wrap_size;
|
||||
|
||||
tmp_trail_mod = mod(buf, buf->misc.byte_mode.tmp_tail);
|
||||
space = (head + buf->size) - tmp_tail;
|
||||
trail_size = buf->size - tmp_trail_mod;
|
||||
free_space = ring_buf_space_get(buf);
|
||||
size = MIN(size, free_space);
|
||||
size = MIN(size, wrap_size);
|
||||
|
||||
/* Limit requested size to available size. */
|
||||
size = MIN(size, space);
|
||||
*data = &buf->buffer[buf->put_head - base];
|
||||
buf->put_head += size;
|
||||
|
||||
trail_size = buf->size - (tmp_trail_mod);
|
||||
|
||||
/* Limit allocated size to trail size. */
|
||||
allocated = MIN(trail_size, size);
|
||||
*data = &buf->buffer[tmp_trail_mod];
|
||||
|
||||
buf->misc.byte_mode.tmp_tail =
|
||||
buf->misc.byte_mode.tmp_tail + allocated;
|
||||
|
||||
return allocated;
|
||||
return size;
|
||||
}
|
||||
|
||||
int ring_buf_put_finish(struct ring_buf *buf, uint32_t size)
|
||||
{
|
||||
uint32_t rew;
|
||||
uint32_t threshold = ring_buf_get_rewind_threshold();
|
||||
uint32_t finish_space, wrap_size;
|
||||
|
||||
if ((buf->tail + size) > (buf->head + buf->size)) {
|
||||
if (unlikely(size == 0)) {
|
||||
/* claim is cancelled */
|
||||
buf->put_head = buf->put_tail;
|
||||
return 0;
|
||||
}
|
||||
|
||||
finish_space = buf->put_head - buf->put_tail;
|
||||
if (unlikely(size > finish_space)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if indexes shall be rewind. */
|
||||
if (buf->tail > threshold) {
|
||||
rew = get_rewind_value(buf->size, threshold);
|
||||
} else {
|
||||
rew = 0;
|
||||
}
|
||||
buf->put_tail += size;
|
||||
|
||||
buf->tail += (size - rew);
|
||||
buf->misc.byte_mode.tmp_tail = buf->tail;
|
||||
wrap_size = buf->put_tail - buf->put_base;
|
||||
if (unlikely(wrap_size >= buf->size)) {
|
||||
/* we wrapped: adjust put_base */
|
||||
buf->put_base += buf->size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -236,54 +178,50 @@ uint32_t ring_buf_put(struct ring_buf *buf, const uint8_t *data, uint32_t size)
|
|||
|
||||
uint32_t ring_buf_get_claim(struct ring_buf *buf, uint8_t **data, uint32_t size)
|
||||
{
|
||||
uint32_t space, granted_size, trail_size, tmp_head_mod;
|
||||
uint32_t tail = buf->tail;
|
||||
uint32_t available_size, wrap_size;
|
||||
int32_t base;
|
||||
|
||||
/* Tail is always ahead, if it is not, it's only because it got rewinded. */
|
||||
if (tail < buf->misc.byte_mode.tmp_head) {
|
||||
/* Locally, increment it to pre-rewind value */
|
||||
tail += get_rewind_value(buf->size,
|
||||
ring_buf_get_rewind_threshold());
|
||||
base = buf->get_base;
|
||||
wrap_size = buf->get_head - base;
|
||||
if (unlikely(wrap_size >= buf->size)) {
|
||||
/* get_base is not yet adjusted */
|
||||
wrap_size -= buf->size;
|
||||
base += buf->size;
|
||||
}
|
||||
wrap_size = buf->size - wrap_size;
|
||||
|
||||
tmp_head_mod = mod(buf, buf->misc.byte_mode.tmp_head);
|
||||
space = tail - buf->misc.byte_mode.tmp_head;
|
||||
trail_size = buf->size - tmp_head_mod;
|
||||
available_size = ring_buf_size_get(buf);
|
||||
size = MIN(size, available_size);
|
||||
size = MIN(size, wrap_size);
|
||||
|
||||
/* Limit requested size to available size. */
|
||||
granted_size = MIN(size, space);
|
||||
*data = &buf->buffer[buf->get_head - base];
|
||||
buf->get_head += size;
|
||||
|
||||
/* Limit allocated size to trail size. */
|
||||
granted_size = MIN(trail_size, granted_size);
|
||||
|
||||
*data = &buf->buffer[tmp_head_mod];
|
||||
buf->misc.byte_mode.tmp_head += granted_size;
|
||||
|
||||
return granted_size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int ring_buf_get_finish(struct ring_buf *buf, uint32_t size)
|
||||
{
|
||||
uint32_t tail = buf->tail;
|
||||
uint32_t rew;
|
||||
uint32_t finish_space, wrap_size;
|
||||
|
||||
/* Tail is always ahead, if it is not, it's only because it got rewinded. */
|
||||
if (tail < buf->misc.byte_mode.tmp_head) {
|
||||
/* tail was rewinded. Locally, increment it to pre-rewind value */
|
||||
rew = get_rewind_value(buf->size,
|
||||
ring_buf_get_rewind_threshold());
|
||||
tail += rew;
|
||||
} else {
|
||||
rew = 0;
|
||||
if (unlikely(size == 0)) {
|
||||
/* claim is cancelled */
|
||||
buf->get_head = buf->get_tail;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((buf->head + size) > tail) {
|
||||
finish_space = buf->get_head - buf->get_tail;
|
||||
if (unlikely(size > finish_space)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Include potential rewinding. */
|
||||
buf->head += (size - rew);
|
||||
buf->misc.byte_mode.tmp_head = buf->head;
|
||||
buf->get_tail += size;
|
||||
|
||||
wrap_size = buf->get_tail - buf->get_base;
|
||||
if (unlikely(wrap_size >= buf->size)) {
|
||||
/* we wrapped: adjust get_base */
|
||||
buf->get_base += buf->size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#define TYPE 0xc
|
||||
|
||||
static ZTEST_BMEM SYS_MUTEX_DEFINE(mutex);
|
||||
RING_BUF_ITEM_DECLARE_SIZE(ringbuf, RINGBUFFER);
|
||||
RING_BUF_ITEM_DECLARE(ringbuf, RINGBUFFER);
|
||||
static uint32_t output[LENGTH];
|
||||
static uint32_t databuffer1[LENGTH];
|
||||
static uint32_t databuffer2[LENGTH];
|
||||
|
@ -252,18 +252,14 @@ static bool consume(void *user_data, uint32_t iter_cnt, bool last, int prio)
|
|||
return true;
|
||||
}
|
||||
|
||||
extern uint32_t test_rewind_threshold;
|
||||
|
||||
static void test_ztress(ztress_handler high_handler,
|
||||
ztress_handler low_handler,
|
||||
bool item_mode)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
uint32_t buf32[32];
|
||||
uint32_t old_rewind_threshold = test_rewind_threshold;
|
||||
k_timeout_t timeout;
|
||||
|
||||
test_rewind_threshold = 256;
|
||||
if (item_mode) {
|
||||
ring_buf_item_init(&ringbuf, ARRAY_SIZE(buf32), buf32);
|
||||
} else {
|
||||
|
@ -276,8 +272,6 @@ static void test_ztress(ztress_handler high_handler,
|
|||
ztress_set_timeout(timeout);
|
||||
ZTRESS_EXECUTE(ZTRESS_THREAD(high_handler, NULL, 0, 0, Z_TIMEOUT_TICKS(20)),
|
||||
ZTRESS_THREAD(low_handler, NULL, 0, 2000, Z_TIMEOUT_TICKS(20)));
|
||||
test_rewind_threshold = old_rewind_threshold;
|
||||
|
||||
}
|
||||
|
||||
void test_ringbuffer_stress(ztress_handler produce_handler,
|
||||
|
|
|
@ -21,20 +21,6 @@
|
|||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(test);
|
||||
|
||||
/* Max size is used internally in the algorithm. Value is decreased in the test
|
||||
* to trigger rewind algorithm.
|
||||
*/
|
||||
#undef RING_BUFFER_MAX_SIZE
|
||||
#define RING_BUFFER_MAX_SIZE 0x00000800
|
||||
|
||||
/* Use global variable that can be modified by the test. */
|
||||
uint32_t test_rewind_threshold = RING_BUFFER_MAX_SIZE;
|
||||
|
||||
uint32_t ring_buf_get_rewind_threshold(void)
|
||||
{
|
||||
return test_rewind_threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup lib_ringbuffer_tests Ringbuffer
|
||||
* @ingroup all_tests
|
||||
|
@ -163,13 +149,13 @@ void test_ring_buffer_main(void)
|
|||
/**TESTPOINT: init via RING_BUF_ITEM_DECLARE_POW2*/
|
||||
RING_BUF_ITEM_DECLARE_POW2(ringbuf_pow2, POW);
|
||||
|
||||
/**TESTPOINT: init via RING_BUF_ITEM_DECLARE_SIZE*/
|
||||
/**TESTPOINT: init via RING_BUF_ITEM_DECLARE*/
|
||||
/**
|
||||
* @brief define a ring buffer with arbitrary size
|
||||
*
|
||||
* @see RING_BUF_ITEM_DECLARE_SIZE(),RING_BUF_DECLARE()
|
||||
* @see RING_BUF_ITEM_DECLARE(), RING_BUF_DECLARE()
|
||||
*/
|
||||
RING_BUF_ITEM_DECLARE_SIZE(ringbuf_size, RINGBUFFER_SIZE);
|
||||
RING_BUF_ITEM_DECLARE(ringbuf_size, RINGBUFFER_SIZE);
|
||||
|
||||
RING_BUF_DECLARE(ringbuf_raw, RINGBUFFER_SIZE);
|
||||
|
||||
|
@ -402,7 +388,7 @@ void test_ringbuffer_pow2_put_get_thread_isr(void)
|
|||
*
|
||||
* @details
|
||||
* Test Objective:
|
||||
* - define and initialize a ring buffer by macro RING_BUF_ITEM_DECLARE_SIZE,
|
||||
* - define and initialize a ring buffer by macro RING_BUF_ITEM_DECLARE,
|
||||
* then passing data by thread and isr to verify the ringbuffer
|
||||
* if it works to be placed in any user-controlled memory.
|
||||
*
|
||||
|
@ -412,7 +398,7 @@ void test_ringbuffer_pow2_put_get_thread_isr(void)
|
|||
* - Structural test coverage(entry points,statements,branches)
|
||||
*
|
||||
* Prerequisite Conditions:
|
||||
* - Define and initialize a ringbuffer by RING_BUF_ITEM_DECLARE_SIZE
|
||||
* - Define and initialize a ringbuffer by RING_BUF_ITEM_DECLARE
|
||||
* - Define a pointer of ring buffer type.
|
||||
*
|
||||
* Input Specifications:
|
||||
|
|
Loading…
Reference in a new issue