unified/doc: Update ring buffers section of Kernel Primer
Ring buffer section now resides under "other" topic, since the ring buffer type is a general purpose type (like the singly and doubly linked list types), rather than a kernel-specific type. Enhances ring buffer section to improve content and improve consistency with the form used elsewhere in the Kernel Primer. Also corrects a minor error in the ring buffer API documentation. Change-Id: Icaa8661524f80e31f173adee859844cadb38967f Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
This commit is contained in:
parent
b9a4bd906c
commit
11a1bb4125
@ -13,6 +13,5 @@ between different threads, or between an ISR and a thread.
|
||||
lifos.rst
|
||||
stacks.rst
|
||||
message_queues.rst
|
||||
ring_buffers.rst
|
||||
mailboxes.rst
|
||||
pipes.rst
|
||||
|
||||
@ -1,143 +0,0 @@
|
||||
.. _ring_buffers_v2:
|
||||
|
||||
Ring Buffers [TBD]
|
||||
##################
|
||||
|
||||
Definition
|
||||
**********
|
||||
|
||||
The ring buffer is defined in :file:`include/misc/ring_buffer.h` and
|
||||
:file:`kernel/nanokernel/ring_buffer.c`. This is an array-based
|
||||
circular buffer, stored in first-in-first-out order. The APIs allow
|
||||
for enqueueing and retrieval of chunks of data up to 1024 bytes in size,
|
||||
along with two metadata values (type ID and an app-specific integer).
|
||||
|
||||
Unlike nanokernel FIFOs, storage of enqueued items and their metadata
|
||||
is managed in a fixed buffer and there are no preconditions on the data
|
||||
enqueued (other than the size limit). Since the size annotation is only
|
||||
an 8-bit value, sizes are expressed in terms of 32-bit chunks.
|
||||
|
||||
Internally, the ring buffer always maintains an empty 32-bit block in the
|
||||
buffer to distinguish between empty and full buffers. Any given entry
|
||||
in the buffer will use a 32-bit block for metadata plus any data attached.
|
||||
If the size of the buffer array is a power of two, the ring buffer will
|
||||
use more efficient masking instead of expensive modulo operations to
|
||||
maintain itself.
|
||||
|
||||
Concurrency
|
||||
***********
|
||||
|
||||
Concurrency control of ring buffers is not implemented at this level.
|
||||
Depending on usage (particularly with respect to number of concurrent
|
||||
readers/writers) applications may need to protect the ring buffer with
|
||||
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.
|
||||
|
||||
Example: Initializing a Ring Buffer
|
||||
===================================
|
||||
|
||||
There are three ways to initialize a ring buffer. The first two are through use
|
||||
of macros which defines one (and an associated private buffer) in file scope.
|
||||
You can declare a fast ring buffer that uses mask operations by declaring
|
||||
a power-of-two sized buffer:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Buffer with 2^8 or 256 elements */
|
||||
SYS_RING_BUF_DECLARE_POW2(my_ring_buf, 8);
|
||||
|
||||
Arbitrary-sized buffers may also be declared with a different macro, but
|
||||
these will always be slower due to use of modulo operations:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MY_RING_BUF_SIZE 93
|
||||
SYS_RING_BUF_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_SIZE);
|
||||
|
||||
Alternatively, a ring buffer may be initialized manually. Whether the buffer
|
||||
will use modulo or mask operations will be detected automatically:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MY_RING_BUF_SIZE 64
|
||||
|
||||
struct my_struct {
|
||||
struct ring_buffer rb;
|
||||
uint32_t buffer[MY_RING_BUF_SIZE];
|
||||
...
|
||||
};
|
||||
struct my_struct ms;
|
||||
|
||||
void init_my_struct {
|
||||
sys_ring_buf_init(&ms.rb, sizeof(ms.buffer), ms.buffer);
|
||||
...
|
||||
}
|
||||
|
||||
Example: Enqueuing data
|
||||
=======================
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int ret;
|
||||
|
||||
ret = sys_ring_buf_put(&ring_buf, TYPE_FOO, 0, &my_foo, SIZE32_OF(my_foo));
|
||||
if (ret == -EMSGSIZE) {
|
||||
... not enough room for the message ..
|
||||
}
|
||||
|
||||
If the type or value fields are sufficient, the data pointer and size may be 0.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int ret;
|
||||
|
||||
ret = sys_ring_buf_put(&ring_buf, TYPE_BAR, 17, NULL, 0);
|
||||
if (ret == -EMSGSIZE) {
|
||||
... not enough room for the message ..
|
||||
}
|
||||
|
||||
Example: Retrieving data
|
||||
========================
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int ret;
|
||||
uint32_t data[6];
|
||||
|
||||
size = SIZE32_OF(data);
|
||||
ret = sys_ring_buf_get(&ring_buf, &type, &value, data, &size);
|
||||
if (ret == -EMSGSIZE) {
|
||||
printk("Buffer is too small, need %d uint32_t\n", size);
|
||||
} else if (ret == -EAGAIN) {
|
||||
printk("Ring buffer is empty\n");
|
||||
} else {
|
||||
printk("got item of type %u value &u of size %u dwords\n",
|
||||
type, value, size);
|
||||
...
|
||||
}
|
||||
|
||||
APIs
|
||||
****
|
||||
|
||||
The following APIs for ring buffers are provided by :file:`ring_buffer.h`:
|
||||
|
||||
:cpp:func:`sys_ring_buf_init()`
|
||||
Initializes a ring buffer.
|
||||
|
||||
:c:func:`SYS_RING_BUF_DECLARE_POW2()`, :c:func:`SYS_RING_BUF_DECLARE_SIZE()`
|
||||
Declare and init a file-scope ring buffer.
|
||||
|
||||
:cpp:func:`sys_ring_buf_space_get()`
|
||||
Returns the amount of free buffer storage space in 32-bit dwords.
|
||||
|
||||
:cpp:func:`sys_ring_buf_is_empty()`
|
||||
Indicates whether a buffer is empty.
|
||||
|
||||
:cpp:func:`sys_ring_buf_put()`
|
||||
Enqueues an item.
|
||||
|
||||
:cpp:func:`sys_ring_buf_get()`
|
||||
De-queues an item.
|
||||
@ -10,6 +10,7 @@ This section describes other services provided by the kernel.
|
||||
|
||||
atomic.rst
|
||||
float.rst
|
||||
ring_buffers.rst
|
||||
event_logger.rst
|
||||
c_library.rst
|
||||
cxx_support.rst
|
||||
|
||||
184
doc/kernel_v2/other/ring_buffers.rst
Normal file
184
doc/kernel_v2/other/ring_buffers.rst
Normal file
@ -0,0 +1,184 @@
|
||||
.. _ring_buffers_v2:
|
||||
|
||||
Ring Buffers
|
||||
############
|
||||
|
||||
A :dfn:`ring buffer` is a circular buffer of 32-bit words, whose contents
|
||||
are stored in first-in-first-out order. Data items can be enqueued and dequeued
|
||||
from a ring buffer in chunks of up to 1020 bytes. Each data item also has
|
||||
two associated metadata values: a type identifier and a 16-bit integer value,
|
||||
both of which are application-specific.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Concepts
|
||||
********
|
||||
|
||||
Any number of ring buffers can be defined. Each ring buffer is referenced
|
||||
by its memory address.
|
||||
|
||||
A ring buffer has the following key properties:
|
||||
|
||||
* A **data buffer** of 32-bit words. The data buffer contains the data items
|
||||
that have been added to the ring buffer but not yet removed.
|
||||
|
||||
* A **data buffer size**, measured in 32-bit words. This governs the maximum
|
||||
amount of data (including metadata values) the ring buffer can hold.
|
||||
|
||||
A ring buffer must be initialized before it can be used. This sets its
|
||||
data buffer to empty.
|
||||
|
||||
A ring buffer **data item** is an array of 32-bit words from 0 to 1020 bytes
|
||||
in length. When a data item is **enqueued** its contents are copied
|
||||
to the data buffer, along with its associated metadata values (which occupy
|
||||
one additional 32-bit word).
|
||||
If the ring buffer has insufficient space to hold the new data item
|
||||
the enqueue operation fails.
|
||||
|
||||
A data items is **dequeued** from a ring buffer by removing the oldest
|
||||
enqueued item. The contents of the dequeued data item, as well as its
|
||||
two metadata values, are copied to areas supplied by the retriever.
|
||||
If the ring buffer is empty, or if the data array supplied by the retriever
|
||||
is not large enough to hold the data item's data, the dequeue operation fails.
|
||||
|
||||
Concurrency
|
||||
===========
|
||||
|
||||
The ring buffer APIs do not provide any concurrency control.
|
||||
Depending on usage (particularly with respect to number of concurrent
|
||||
readers/writers) applications may need to protect the ring buffer with
|
||||
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.
|
||||
|
||||
Internal Operation
|
||||
==================
|
||||
|
||||
The ring buffer always maintains an empty 32-bit word in its data buffer
|
||||
to allow it to distinguish between empty and full states.
|
||||
|
||||
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.
|
||||
|
||||
Implementation
|
||||
**************
|
||||
|
||||
Defining a Ring Buffer
|
||||
======================
|
||||
|
||||
A ring buffer is defined using a variable of type :c:type:`struct ring_buf`.
|
||||
It must then be initialized by calling :cpp:func:`sys_ring_buf_init()`.
|
||||
|
||||
The following code defines and initializes an empty ring buffer
|
||||
(which is part of a larger data structure). The ring buffer's data buffer
|
||||
is capable of holding 64 words of data and metadata information.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MY_RING_BUF_SIZE 64
|
||||
|
||||
struct my_struct {
|
||||
struct ring_buffer rb;
|
||||
uint32_t buffer[MY_RING_BUF_SIZE];
|
||||
...
|
||||
};
|
||||
struct my_struct ms;
|
||||
|
||||
void init_my_struct {
|
||||
sys_ring_buf_init(&ms.rb, sizeof(ms.buffer), ms.buffer);
|
||||
...
|
||||
}
|
||||
|
||||
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 a ring buffer 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 */
|
||||
SYS_RING_BUF_DECLARE_POW2(my_ring_buf, 8);
|
||||
|
||||
The following code defines a ring buffer with an arbitraty-sized data buffer,
|
||||
which can be accessed using less efficient modulo operations.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MY_RING_BUF_WORDS 93
|
||||
SYS_RING_BUF_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_WORDS);
|
||||
|
||||
Enqueuing Data
|
||||
==============
|
||||
|
||||
A data item is added to a ring buffer by calling :cpp:func:`sys_ring_buf_put()`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
uint32_t my_data[MY_DATA_WORDS];
|
||||
int ret;
|
||||
|
||||
ret = sys_ring_buf_put(&ring_buf, TYPE_FOO, 0, my_data, SIZE32_OF(my_data));
|
||||
if (ret == -EMSGSIZE) {
|
||||
/* not enough room for the data item */
|
||||
...
|
||||
}
|
||||
|
||||
If the data item requires only the type or application-specific integer value
|
||||
(i.e. it has no data array), a size of 0 and data pointer of :c:macro:`NULL`
|
||||
can be specified.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int ret;
|
||||
|
||||
ret = sys_ring_buf_put(&ring_buf, TYPE_BAR, 17, NULL, 0);
|
||||
if (ret == -EMSGSIZE) {
|
||||
/* not enough room for the data item */
|
||||
...
|
||||
}
|
||||
|
||||
Retrieving Data
|
||||
===============
|
||||
|
||||
A data item is removed from a ring buffer by calling
|
||||
:cpp:func:`sys_ring_buf_get()`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
uint32_t my_data[MY_DATA_WORDS];
|
||||
uint16_t my_type;
|
||||
uint8_t my_value;
|
||||
uint8_t my_size;
|
||||
int ret;
|
||||
|
||||
my_size = SIZE32_OF(my_data);
|
||||
ret = sys_ring_buf_get(&ring_buf, &my_type, &my_value, my_data, &my_size);
|
||||
if (ret == -EMSGSIZE) {
|
||||
printk("Buffer is too small, need %d uint32_t\n", my_size);
|
||||
} else if (ret == -EAGAIN) {
|
||||
printk("Ring buffer is empty\n");
|
||||
} else {
|
||||
printk("Got item of type %u value &u of size %u dwords\n",
|
||||
my_type, my_value, my_size);
|
||||
...
|
||||
}
|
||||
|
||||
APIs
|
||||
****
|
||||
|
||||
The following ring buffer APIs are provided by :file:`misc/ring_buffer.h`:
|
||||
|
||||
* :c:func:`SYS_RING_BUF_DECLARE_POW2()`
|
||||
* :c:func:`SYS_RING_BUF_DECLARE_SIZE()`
|
||||
* :cpp:func:`sys_ring_buf_init()`
|
||||
* :cpp:func:`sys_ring_buf_is_empty()`
|
||||
* :cpp:func:`sys_ring_buf_space_get()`
|
||||
* :cpp:func:`sys_ring_buf_put()`
|
||||
* :cpp:func:`sys_ring_buf_get()`
|
||||
@ -161,7 +161,7 @@ static inline int sys_ring_buf_space_get(struct ring_buf *buf)
|
||||
* @param value Integral data to include, application specific
|
||||
* @param data Pointer to a buffer containing data to enqueue
|
||||
* @param size32 Size of data buffer, in 32-bit chunks (not bytes)
|
||||
* @return 0 on success, -ENOSPC if there isn't sufficient space
|
||||
* @return 0 on success, -EMSGSIZE if there isn't sufficient space
|
||||
*/
|
||||
int sys_ring_buf_put(struct ring_buf *buf, uint16_t type, uint8_t value,
|
||||
uint32_t *data, uint8_t size32);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user