Skip to content

Commit fa055f7

Browse files
nordic-krchcarlescufi
authored andcommitted
lib: os: spsc_pbuf: Extend API with zero copy model
Added functions which allow to use zero copy model for handling data within the packet buffer. Additionally, added handling of cache by adding option to keep rd_idx in different cache line than wr_idx and data. Signed-off-by: Krzysztof Chruscinski <[email protected]>
1 parent 3efce4c commit fa055f7

File tree

4 files changed

+374
-106
lines changed

4 files changed

+374
-106
lines changed

include/zephyr/sys/spsc_pbuf.h

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,47 @@ extern "C" {
2626

2727
/**@} */
2828

29+
#ifndef CONFIG_SPSC_PBUF_CACHE_LINE
30+
#define CONFIG_SPSC_PBUF_CACHE_LINE 0
31+
#endif
32+
33+
/** @brief Maximum packet length. */
34+
#define SPSC_PBUF_MAX_LEN 0xFF00
35+
36+
/** @brief First part of packet buffer control block.
37+
*
38+
* This part contains only data set during the initialization and data touched
39+
* by the reader. If packet is shared between to cores then data changed by
40+
* the reader should be on different cache line than the data changed by the
41+
* writer.
42+
*/
43+
struct spsc_pbuf_common {
44+
uint32_t len; /* Length of data[] in bytes. */
45+
uint32_t flags; /* Flags. See @ref SPSC_PBUF_FLAGS */
46+
uint32_t rd_idx; /* Index of the first valid byte in data[] */
47+
};
48+
49+
/* Padding to fill cache line. */
50+
#define Z_SPSC_PBUF_PADDING \
51+
MAX(0, CONFIG_SPSC_PBUF_CACHE_LINE - (int)sizeof(struct spsc_pbuf_common))
52+
53+
/** @brief Remaining part of a packet buffer when cache is used.
54+
*
55+
* It contains data that is only changed by the writer. A gap is added to ensure
56+
* that it is in different cache line than the data changed by the reader.
57+
*/
58+
struct spsc_pbuf_ext_cache {
59+
uint8_t reserved[Z_SPSC_PBUF_PADDING];
60+
uint32_t wr_idx; /* Index of the first free byte in data[] */
61+
uint8_t data[]; /* Buffer data. */
62+
};
63+
64+
/** @brief Remaining part of a packet buffer when cache is not used. */
65+
struct spsc_pbuf_ext_nocache {
66+
uint32_t wr_idx; /* Index of the first free byte in data[] */
67+
uint8_t data[]; /* Buffer data. */
68+
};
69+
2970
/**
3071
* @brief Single producer, single consumer packet buffer
3172
*
@@ -39,20 +80,27 @@ extern "C" {
3980
*
4081
*/
4182
struct spsc_pbuf {
42-
uint32_t len; /* Length of data[] in bytes. */
43-
uint32_t wr_idx; /* Index of the first free byte in data[] */
44-
uint32_t rd_idx; /* Index of the first valid byte in data[] */
45-
uint32_t flags; /* Flags. See @ref SPSC_PBUF_FLAGS */
46-
uint8_t data[]; /* Buffer data. */
83+
struct spsc_pbuf_common common;
84+
union {
85+
struct spsc_pbuf_ext_cache cache;
86+
struct spsc_pbuf_ext_nocache nocache;
87+
} ext;
4788
};
4889

4990
/** @brief Get buffer capacity.
5091
*
51-
* @param blen Length of the buffer dedicated for the packet buffer.
92+
* This value is the amount of data that is dedicated for storing packets. Since
93+
* each packet is prefixed with 2 byte length header, longest possible packet is
94+
* less than that.
95+
*
96+
* @param pb A buffer.
5297
*
5398
* @return Packet buffer capacity.
5499
*/
55-
#define SPSC_PBUF_CAPACITY(blen) ((blen) - offsetof(struct spsc_pbuf, data))
100+
static inline uint32_t spsc_pbuf_capacity(struct spsc_pbuf *pb)
101+
{
102+
return pb->common.len - sizeof(uint32_t);
103+
}
56104

57105
/**
58106
* @brief Initialize the packet buffer.
@@ -61,36 +109,85 @@ struct spsc_pbuf {
61109
* memory region.
62110
*
63111
* @param buf Pointer to a memory region on which buffer is
64-
* created.
112+
* created. When cache is used it must be aligned to
113+
* CONFIG_SPSC_PBUF_CACHE_LINE, otherwise it must
114+
* be 32 bit word aligned.
65115
* @param blen Length of the buffer. Must be large enough to
66116
* contain the internal structure and at least two
67117
* bytes of data (one is reserved for written
68118
* messages length).
69119
* @param flags Option flags. See @ref SPSC_PBUF_FLAGS.
70120
* @retval struct spsc_pbuf* Pointer to the created buffer. The pointer
71121
* points to the same address as buf.
122+
* @retval NULL Invalid buffer alignment.
72123
*/
73124
struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen, uint32_t flags);
74125

75126
/**
76127
* @brief Write specified amount of data to the packet buffer.
77128
*
129+
* It combines @ref spsc_pbuf_alloc and @ref spsc_pbuf_commit into a single call.
130+
*
78131
* @param pb A buffer to which to write.
79132
* @param buf Pointer to the data to be written to the buffer.
80-
* @param len Number of bytes to be written to the buffer.
133+
* @param len Number of bytes to be written to the buffer. Must be positive
134+
* but less than @ref SPSC_PBUF_MAX_LEN.
81135
* @retval int Number of bytes written, negative error code on fail.
82136
* -EINVAL, if len == 0.
83137
* -ENOMEM, if len is bigger than the buffer can fit.
84138
*/
85139
int spsc_pbuf_write(struct spsc_pbuf *pb, const char *buf, uint16_t len);
86140

141+
/**
142+
* @brief Allocate space in the packet buffer.
143+
*
144+
* This function attempts to allocate @p len bytes of continuous memory within
145+
* the packet buffer. An internal padding is added at the end of the buffer, if
146+
* wrapping occurred during allocation. Apart from padding, allocation does not
147+
* change the state of the buffer so if after allocation packet is not needed
148+
* a commit is not needed.
149+
*
150+
* Allocated buffer must be committed (@ref spsc_pbuf_commit) to make the packet
151+
* available for reading.
152+
*
153+
* Packet buffer ensures that allocated buffers are 32 bit word aligned.
154+
*
155+
* @note If data cache is used, it is the user responsibility to write back the
156+
* new data.
157+
*
158+
* @param[in] pb A buffer to which to write.
159+
* @param[in] len Allocation length. Must be positive. If less than @ref SPSC_PBUF_MAX_LEN
160+
* then if requested length cannot be allocated, an attempt to allocate
161+
* largest possible is performed (which may include adding wrap padding).
162+
* If @ref SPSC_PBUF_MAX_LEN is used then an attempt to allocate largest
163+
* buffer without applying wrap padding is performed.
164+
* @param[out] buf Location where buffer address is written on successful allocation.
165+
*
166+
* @retval non-negative Amount of space that got allocated. Can be equal or smaller than %p len.
167+
* @retval -EINVAL if @p len is forbidden.
168+
*/
169+
int spsc_pbuf_alloc(struct spsc_pbuf *pb, uint16_t len, char **buf);
170+
171+
/**
172+
* @brief Commit packet to the buffer.
173+
*
174+
* Commit a packet which was previously allocated (@ref spsc_pbuf_alloc).
175+
* If cache is used, cache writeback is perfromed on the written data.
176+
*
177+
* @param pb A buffer to which to write.
178+
* @param len Packet length. Must be equal or less than the length used for allocation.
179+
*/
180+
void spsc_pbuf_commit(struct spsc_pbuf *pb, uint16_t len);
181+
87182
/**
88183
* @brief Read specified amount of data from the packet buffer.
89184
*
90185
* Single read allows to read the message send by the single write.
91-
* The provided buf must be big enough to store the whole message.
186+
* The provided %p buf must be big enough to store the whole message.
92187
*
93-
* @param pb A buffer from which data is to be read.
188+
* It combines @ref spsc_pbuf_claim and @ref spsc_pbuf_free into a single call.
189+
*
190+
* @param pb A buffer from which data will be read.
94191
* @param buf Data pointer to which read data will be written.
95192
* If NULL, len of stored message is returned.
96193
* @param len Number of bytes to be read from the buffer.
@@ -101,6 +198,30 @@ int spsc_pbuf_write(struct spsc_pbuf *pb, const char *buf, uint16_t len);
101198
*/
102199
int spsc_pbuf_read(struct spsc_pbuf *pb, char *buf, uint16_t len);
103200

201+
/**
202+
* @brief Claim packet from the buffer.
203+
*
204+
* Claimed packet must be freed using @ref spsc_pbuf_free.
205+
*
206+
* @note If data cache is used, cache is invalidate on the packet.
207+
*
208+
* @param[in] pb A buffer from which packet will be claimed.
209+
* @param[in,out] buf A location where claimed packet address is written.
210+
*
211+
* @retval 0 No packets in the buffer.
212+
* @retval positive packet length.
213+
*/
214+
uint16_t spsc_pbuf_claim(struct spsc_pbuf *pb, char **buf);
215+
216+
/**
217+
* @brief Free the packet to the buffer.
218+
*
219+
* Packet must be claimed (@ref spsc_pbuf_claim) before it can be freed.
220+
*
221+
* @param pb A packet buffer from which packet was claimed.
222+
* @param len Claimed packet length.
223+
*/
224+
void spsc_pbuf_free(struct spsc_pbuf *pb, uint16_t len);
104225

105226
/**
106227
* @}

lib/os/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ config SPSC_PBUF_USE_CACHE
7878
config SPSC_PBUF_NO_CACHE
7979
bool
8080

81+
if SPSC_PBUF_CACHE_FLAG || SPSC_PBUF_CACHE_ALWAYS
82+
83+
config SPSC_PBUF_CACHE_LINE
84+
int "Maximum cache line"
85+
default 32
86+
help
87+
If buffer is used by 2 cores then value should be set to the length of
88+
of the longer cache line.
89+
90+
endif # SPSC_PBUF_CACHE_FLAG || SPSC_PBUF_CACHE_ALWAYS
91+
8192
endif # SPSC_PBUF
8293

8394
config SHARED_MULTI_HEAP

0 commit comments

Comments
 (0)