Skip to content

Commit 36c1c12

Browse files
committed
lib: os: Add mpsc (multiple producer, single consumer) packet buffer
Added module for storing variable length packets in a ring buffer. Implementation assumes multiple producing contexts and single consumer. API provides zero copy functionality with alloc, commit, claim, free scheme. Additionally, there are functions optimized for storing single word packets and packets consisting of a word and a pointer. Buffer can work in two modes: saturation or overwriting the oldest packets when buffer has no space to allocate for a new buffer. Signed-off-by: Krzysztof Chruscinski <[email protected]>
1 parent e7efb10 commit 36c1c12

File tree

5 files changed

+722
-0
lines changed

5 files changed

+722
-0
lines changed

include/sys/mpsc_packet.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2021 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef ZEPHYR_INCLUDE_SYS_MPSC_PACKET_H_
7+
#define ZEPHYR_INCLUDE_SYS_MPSC_PACKET_H_
8+
9+
#include <string.h>
10+
#include <stdint.h>
11+
#include <stdbool.h>
12+
13+
#ifdef __cplusplus
14+
extern "C" {
15+
#endif
16+
17+
/**
18+
* @brief Multi producer, single consumer packet header
19+
* @defgroup mpsc_packet MPSC (Multi producer, single consumer) packet header
20+
* @ingroup mpsc_buf
21+
* @{
22+
*/
23+
24+
/** @brief Number of bits in the first word which are used by the buffer. */
25+
#define MPSC_PBUF_HDR_BITS 2
26+
27+
/** @brief Header that must be added to the first word in each packet.
28+
*
29+
* This fields are controlled by the packet buffer and unless specified must
30+
* not be used. Fields must be added at the top of the packet header structure.
31+
*/
32+
#define MPSC_PBUF_HDR \
33+
uint32_t valid: 1; \
34+
uint32_t busy: 1
35+
36+
/** @brief Generic packet header. */
37+
struct mpsc_pbuf_hdr {
38+
MPSC_PBUF_HDR;
39+
uint32_t data: 32 - MPSC_PBUF_HDR_BITS;
40+
};
41+
42+
/** @brief Skip packet used internally by the packet buffer. */
43+
struct mpsc_pbuf_skip {
44+
MPSC_PBUF_HDR;
45+
uint32_t len: 32 - MPSC_PBUF_HDR_BITS;
46+
};
47+
48+
/** @brief Generic packet header. */
49+
union mpsc_pbuf_generic {
50+
struct mpsc_pbuf_hdr hdr;
51+
struct mpsc_pbuf_skip skip;
52+
uint32_t raw;
53+
};
54+
55+
/**
56+
* @}
57+
*/
58+
59+
#ifdef __cplusplus
60+
}
61+
#endif
62+
63+
#endif /* ZEPHYR_INCLUDE_SYS_MPSC_PACKET_H_ */

include/sys/mpsc_pbuf.h

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
2+
/*
3+
* Copyright (c) 2021 Nordic Semiconductor ASA
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
#ifndef ZEPHYR_INCLUDE_SYS_MPSC_PBUF_H_
8+
#define ZEPHYR_INCLUDE_SYS_MPSC_PBUF_H_
9+
10+
#include <kernel.h>
11+
#include <sys/mpsc_packet.h>
12+
#include <string.h>
13+
#include <stdint.h>
14+
#include <stdbool.h>
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
20+
/**
21+
* @brief Multi producer, single consumer packet buffer API
22+
* @defgroup mpsc_buf MPSC (Multi producer, single consumer) packet buffer API
23+
* @ingroup kernel_apis
24+
* @{
25+
*/
26+
27+
/*
28+
* Multi producer, single consumer packet buffer allows to allocate variable
29+
* length consecutive space for storing a packet. When space is allocated
30+
* it can be filled by the user (except for the first 2 bits) and when packet
31+
* is ready it is commited. It is allowed to allocate another packet before
32+
* commiting the previous one.
33+
*
34+
* If buffer is full and packet cannot be allocated then null is returned unless
35+
* overwrite mode is selected. In that mode, oldest entry are dropped (user is
36+
* notified) until allocation succeeds. It can happen that candidate for
37+
* dropping is currently being claimed. In that case, it is ommited and next
38+
* packet is dropped and claimed packet is marked as invalid when freeing.
39+
*
40+
* Reading packets is performed in two steps. First packet is claimed. Claiming
41+
* returns pointer to the packet within the buffer. Packet is freed when no
42+
* longer in use.
43+
*/
44+
45+
/**@defgroup MPSC_PBUF_FLAGS MPSC packet buffer flags
46+
* @{ */
47+
48+
/** @brief Flag indicating that buffer size is power of 2.
49+
*
50+
* When buffer size is power of 2 then optimizations are applied.
51+
*/
52+
#define MPSC_PBUF_SIZE_POW2 BIT(0)
53+
54+
/** @brief Flag indicating buffer full policy.
55+
*
56+
* If flag is set then when allocating from a full buffer oldest packets are
57+
* dropped. When flag is not set then allocation returns null.
58+
*/
59+
#define MPSC_PBUF_MODE_OVERWRITE BIT(1)
60+
61+
/**@} */
62+
63+
/* Forward declaration */
64+
struct mpsc_pbuf_buffer;
65+
66+
/** @brief Callback prototype for getting length of a packet.
67+
*
68+
* @param packet User packet.
69+
*
70+
* @return Size of the packet in 32 bit words.
71+
*/
72+
typedef uint32_t (*mpsc_pbuf_get_wlen)(union mpsc_pbuf_generic *packet);
73+
74+
/** @brief Callback called when packet is dropped.
75+
*
76+
* @param buffer Packet buffer.
77+
*
78+
* @param packet Packet that is being dropped.
79+
*/
80+
typedef void (*mpsc_pbuf_notify_drop)(struct mpsc_pbuf_buffer *buffer,
81+
union mpsc_pbuf_generic *packet);
82+
83+
/** @brief MPSC packet buffer structure. */
84+
struct mpsc_pbuf_buffer {
85+
/** Temporary write index. */
86+
uint32_t tmp_wr_idx;
87+
88+
/** Write index. */
89+
uint32_t wr_idx;
90+
91+
/** Temporary read index. */
92+
uint32_t tmp_rd_idx;
93+
94+
/** Read index. */
95+
uint32_t rd_idx;
96+
97+
/** Flags. */
98+
uint32_t flags;
99+
100+
/** Lock. */
101+
struct k_spinlock lock;
102+
103+
/** User callback called whenever packet is dropped. */
104+
mpsc_pbuf_notify_drop notify_drop;
105+
106+
/** Callback for getting packet length. */
107+
mpsc_pbuf_get_wlen get_wlen;
108+
109+
/* Buffer. */
110+
uint32_t *buf;
111+
112+
/* Buffer size in 32 bit words. */
113+
uint32_t size;
114+
115+
struct k_sem sem;
116+
};
117+
118+
/** @brief MPSC packet buffer configuration. */
119+
struct mpsc_pbuf_buffer_config {
120+
/* Pointer to a memory used for storing packets. */
121+
uint32_t *buf;
122+
123+
/* Buffer size in 32 bit words. */
124+
uint32_t size;
125+
126+
/* Callbacks. */
127+
mpsc_pbuf_notify_drop notify_drop;
128+
mpsc_pbuf_get_wlen get_wlen;
129+
130+
/* Configuration flags. */
131+
uint32_t flags;
132+
};
133+
134+
/** @brief Initnialize a packet buffer.
135+
*
136+
* @param buffer Buffer.
137+
*
138+
* @param config Configuration.
139+
*/
140+
void mpsc_pbuf_init(struct mpsc_pbuf_buffer *buffer,
141+
const struct mpsc_pbuf_buffer_config *config);
142+
143+
/** @brief Allocate a packet.
144+
*
145+
* If a buffer is configured to overwrite mode then if there is no space to
146+
* allocated a new buffer, oldest packets are dropped. Otherwise allocation
147+
* fails and null pointer is returned.
148+
*
149+
* @param buffer Buffer.
150+
*
151+
* @param wlen Number of words to allocate.
152+
*
153+
* @param timeout Timeout. If called from thread context it will pend for given
154+
* timeout if packet cannot be allocated before dropping the oldest or
155+
* returning null.
156+
*
157+
* @return Pointer to the allocated space or null if it cannot be allocated.
158+
*/
159+
union mpsc_pbuf_generic *mpsc_pbuf_alloc(struct mpsc_pbuf_buffer *buffer,
160+
size_t wlen, k_timeout_t timeout);
161+
162+
/** @brief Commit a packet.
163+
*
164+
* @param buffer Buffer.
165+
*
166+
* @param packet Pointer to a packet allocated by @ref mpsc_pbuf_alloc.
167+
*/
168+
void mpsc_pbuf_commit(struct mpsc_pbuf_buffer *buffer,
169+
union mpsc_pbuf_generic *packet);
170+
171+
/** @brief Put single word packet into a buffer.
172+
*
173+
* Function is optimized for storing a packet which fit into a single word.
174+
* Note that 2 bits of that word is used by the buffer.
175+
*
176+
* @param buffer Buffer.
177+
*
178+
* @param word Packet content consisting of MPSC_PBUF_HDR with valid bit set
179+
* and data on remaining bits.
180+
*/
181+
void mpsc_pbuf_put_word(struct mpsc_pbuf_buffer *buffer,
182+
union mpsc_pbuf_generic word);
183+
184+
/** @brief Put a packet consisting of a word and a pointer.
185+
* *
186+
* Function is optimized for storing packet consisting of a word and a pointer.
187+
* Note that 2 bits of a first word is used by the buffer.
188+
*
189+
* @param buffer Buffer.
190+
*
191+
* @param word First word of a packet consisting of MPSC_PBUF_HDR with valid
192+
* bit set and data on remaining bits.
193+
*
194+
* @param data User data.
195+
*/
196+
void mpsc_pbuf_put_word_ext(struct mpsc_pbuf_buffer *buffer,
197+
union mpsc_pbuf_generic word, void *data);
198+
199+
/** @brief Put a packet into a buffer.
200+
*
201+
* Copy data into a buffer.
202+
* Note that 2 bits of a first word is used by the buffer.
203+
*
204+
* @param buffer Buffer.
205+
*
206+
* @param data First word of data must contain MPSC_PBUF_HDR with valid set.
207+
*
208+
* @param wlen Packet size in words.
209+
*/
210+
void mpsc_pbuf_put_data(struct mpsc_pbuf_buffer *buffer,
211+
uint32_t *data, size_t wlen);
212+
213+
/** @brief Claim the first pending packet.
214+
*
215+
* @param buffer Buffer.
216+
*/
217+
union mpsc_pbuf_generic *mpsc_pbuf_claim(struct mpsc_pbuf_buffer *buffer);
218+
219+
/** @brief Free a packet.
220+
*
221+
* @param buffer Buffer.
222+
*
223+
* @param packet Packet.
224+
*/
225+
void mpsc_pbuf_free(struct mpsc_pbuf_buffer *buffer,
226+
union mpsc_pbuf_generic *packet);
227+
228+
/** @brief Check if there are any message pending.
229+
*
230+
* @param buffer Buffer.
231+
*
232+
* @retval true if pending.
233+
* @retval false if no message is pending.
234+
*/
235+
bool mpsc_pbuf_is_pending(struct mpsc_pbuf_buffer *buffer);
236+
237+
/**
238+
* @}
239+
*/
240+
241+
#ifdef __cplusplus
242+
}
243+
#endif
244+
245+
#endif /* ZEPHYR_INCLUDE_SYS_MPSC_PBUF_H_ */

lib/os/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ zephyr_sources_ifdef(CONFIG_ASSERT assert.c)
3535

3636
zephyr_sources_ifdef(CONFIG_USERSPACE mutex.c user_work.c)
3737

38+
zephyr_sources_ifdef(CONFIG_MPSC_PBUF mpsc_pbuf.c)
39+
3840
zephyr_sources_ifdef(CONFIG_SCHED_DEADLINE p4wq.c)
3941

4042
zephyr_library_include_directories(

lib/os/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ config PRINTK_SYNC
6060
interleaving with concurrent usage from another CPU or an
6161
preempting interrupt.
6262

63+
config MPSC_PBUF
64+
bool "Multi producer, single consumer packet buffer"
65+
select TIMEOUT_64BIT
66+
help
67+
Enable usage of mpsc packet buffer. Packet buffer is capable of
68+
storing variable length packets in a circular way and operate directly
69+
on the buffer memory.
70+
6371
rsource "Kconfig.cbprintf"
6472

6573
endmenu

0 commit comments

Comments
 (0)