lib: os: spsc_pbuf: Add option to use cache
Add flags option to init call and a flag to use cache. Add Kconfig choice to pick how to approach cache. Cache can be enforced in all spsc_pbuf instances, disable in all, or runtime selected based on configuration flag. Option is added to allow memory footprint savings. Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
parent
03e36855d3
commit
0829610bbc
6 changed files with 103 additions and 27 deletions
|
@ -17,25 +17,43 @@ extern "C" {
|
|||
* @{
|
||||
*/
|
||||
|
||||
/**@defgroup SPSC_PBUF_FLAGS MPSC packet buffer flags
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief Flag indicating that cache shall be handled. */
|
||||
#define SPSC_PBUF_CACHE BIT(0)
|
||||
|
||||
/**@} */
|
||||
|
||||
/**
|
||||
* @brief Inter core messaging buffer
|
||||
* @brief Single producer, single consumer packet buffer
|
||||
*
|
||||
* The inter core messaging buffer implements lightweight unidirectional
|
||||
* messaging buffer with read/write semantics on top of a memory region shared
|
||||
* by the reader and writer. It embeds cache and memory barier management to
|
||||
* ensure correct data access.
|
||||
* The SPSC packet buffer implements lightweight unidirectional packet buffer
|
||||
* with read/write semantics on top of a memory region shared
|
||||
* by the reader and writer. It optionally embeds cache and memory barier
|
||||
* management to ensure correct data access.
|
||||
*
|
||||
* This structure supports single writter and reader. Data stored in the buffer
|
||||
* is encapsulated to a message.
|
||||
* This structure supports single writer and reader. Data stored in the buffer
|
||||
* is encapsulated to a message (with length header).
|
||||
*
|
||||
*/
|
||||
struct spsc_pbuf {
|
||||
uint32_t len; /* Length of data[] in bytes. */
|
||||
uint32_t wr_idx; /* Index of the first free byte in data[] */
|
||||
uint32_t rd_idx; /* Index of the first valid byte in data[] */
|
||||
uint32_t flags; /* Flags. See @ref SPSC_PBUF_FLAGS */
|
||||
uint8_t data[]; /* Buffer data. */
|
||||
};
|
||||
|
||||
/** @brief Get buffer capacity.
|
||||
*
|
||||
* @param blen Length of the buffer dedicated for the packet buffer.
|
||||
*
|
||||
* @return Packet buffer capacity.
|
||||
*/
|
||||
#define SPSC_PBUF_CAPACITY(blen) ((blen) - offsetof(struct spsc_pbuf, data))
|
||||
|
||||
/**
|
||||
* @brief Initialize the packet buffer.
|
||||
*
|
||||
|
@ -48,10 +66,11 @@ struct spsc_pbuf {
|
|||
* contain the internal structure and at least two
|
||||
* bytes of data (one is reserved for written
|
||||
* messages length).
|
||||
* @param flags Option flags. See @ref SPSC_PBUF_FLAGS.
|
||||
* @retval struct spsc_pbuf* Pointer to the created buffer. The pointer
|
||||
* points to the same address as buf.
|
||||
*/
|
||||
struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen);
|
||||
struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen, uint32_t flags);
|
||||
|
||||
/**
|
||||
* @brief Write specified amount of data to the packet buffer.
|
||||
|
|
|
@ -45,6 +45,41 @@ config SPSC_PBUF
|
|||
storing variable length packets in a circular way and operate directly
|
||||
on the buffer memory.
|
||||
|
||||
if SPSC_PBUF
|
||||
|
||||
choice SPSC_PBUF_CACHE_HANDLING
|
||||
prompt "Cache handling"
|
||||
default SPSC_PBUF_CACHE_ALWAYS if SPSC_PBUF_USE_CACHE && !SPSC_PBUF_NO_CACHE
|
||||
default SPSC_PBUF_CACHE_NEVER if !SPSC_PBUF_USE_CACHE && SPSC_PBUF_NO_CACHE
|
||||
default SPSC_PBUF_CACHE_FLAG
|
||||
|
||||
config SPSC_PBUF_CACHE_FLAG
|
||||
bool "Use cache flag"
|
||||
help
|
||||
Use instance specific configuration flag for cache handling.
|
||||
|
||||
config SPSC_PBUF_CACHE_ALWAYS
|
||||
bool "Always handle cache"
|
||||
help
|
||||
Handle cache writeback and invalidation for all instances. Option used
|
||||
to avoid runtime check and thus reduce memory footprint.
|
||||
|
||||
config SPSC_PBUF_CACHE_NEVER
|
||||
bool "Never handle cache"
|
||||
help
|
||||
Discar cache handling for all instances. Option used to avoid runtime
|
||||
check and thus reduce memory footprint.
|
||||
|
||||
endchoice
|
||||
|
||||
config SPSC_PBUF_USE_CACHE
|
||||
bool
|
||||
|
||||
config SPSC_PBUF_NO_CACHE
|
||||
bool
|
||||
|
||||
endif # SPSC_PBUF
|
||||
|
||||
config SHARED_MULTI_HEAP
|
||||
bool "Shared multi-heap manager"
|
||||
help
|
||||
|
|
|
@ -24,7 +24,23 @@ static uint32_t idx_cut(uint32_t len, uint32_t idx)
|
|||
return (idx >= len) ? (idx - len) : (idx);
|
||||
}
|
||||
|
||||
struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen)
|
||||
static inline void cache_wb(void *data, size_t len, uint32_t flags)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_SPSC_PBUF_CACHE_ALWAYS) ||
|
||||
(IS_ENABLED(CONFIG_SPSC_PBUF_CACHE_FLAG) && (flags & SPSC_PBUF_CACHE))) {
|
||||
sys_cache_data_range(data, len, K_CACHE_WB);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cache_inv(void *data, size_t len, uint32_t flags)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_SPSC_PBUF_CACHE_ALWAYS) ||
|
||||
(IS_ENABLED(CONFIG_SPSC_PBUF_CACHE_FLAG) && (flags & SPSC_PBUF_CACHE))) {
|
||||
sys_cache_data_range(data, len, K_CACHE_INVD);
|
||||
}
|
||||
}
|
||||
|
||||
struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen, uint32_t flags)
|
||||
{
|
||||
/* blen must be big enough to contain spsc_pbuf struct, byte of data
|
||||
* and message len (2 bytes).
|
||||
|
@ -36,9 +52,10 @@ struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen)
|
|||
pb->len = blen - sizeof(*pb);
|
||||
pb->wr_idx = 0;
|
||||
pb->rd_idx = 0;
|
||||
pb->flags = flags;
|
||||
|
||||
__sync_synchronize();
|
||||
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_WB);
|
||||
cache_wb(pb, sizeof(*pb), pb->flags);
|
||||
|
||||
return pb;
|
||||
}
|
||||
|
@ -55,7 +72,7 @@ int spsc_pbuf_write(struct spsc_pbuf *pb, const char *buf, uint16_t len)
|
|||
*/
|
||||
const uint32_t max_len = pblen - 1;
|
||||
|
||||
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_INVD);
|
||||
cache_inv(pb, sizeof(*pb), pb->flags);
|
||||
__sync_synchronize();
|
||||
|
||||
uint32_t wr_idx = pb->wr_idx;
|
||||
|
@ -76,23 +93,23 @@ int spsc_pbuf_write(struct spsc_pbuf *pb, const char *buf, uint16_t len)
|
|||
|
||||
/* Store info about the message length. */
|
||||
pb->data[wr_idx] = (uint8_t)len;
|
||||
sys_cache_data_range(&pb->data[wr_idx], sizeof(pb->data[wr_idx]), K_CACHE_WB);
|
||||
cache_wb(&pb->data[wr_idx], sizeof(pb->data[wr_idx]), pb->flags);
|
||||
wr_idx = idx_cut(pblen, wr_idx + sizeof(pb->data[wr_idx]));
|
||||
|
||||
pb->data[wr_idx] = (uint8_t)(len >> 8);
|
||||
sys_cache_data_range(&pb->data[wr_idx], sizeof(pb->data[wr_idx]), K_CACHE_WB);
|
||||
cache_wb(&pb->data[wr_idx], sizeof(pb->data[wr_idx]), pb->flags);
|
||||
wr_idx = idx_cut(pblen, wr_idx + sizeof(pb->data[wr_idx]));
|
||||
|
||||
/* Write until the end of the buffer. */
|
||||
uint32_t sz = MIN(len, pblen - wr_idx);
|
||||
|
||||
memcpy(&pb->data[wr_idx], buf, sz);
|
||||
sys_cache_data_range(&pb->data[wr_idx], sz, K_CACHE_WB);
|
||||
cache_wb(&pb->data[wr_idx], sz, pb->flags);
|
||||
|
||||
if (len > sz) {
|
||||
/* Write remaining data at the buffer head. */
|
||||
memcpy(&pb->data[0], buf + sz, len - sz);
|
||||
sys_cache_data_range(&pb->data[0], len - sz, K_CACHE_WB);
|
||||
cache_wb(&pb->data[0], len - sz, pb->flags);
|
||||
}
|
||||
|
||||
/* Update write index - make other side aware data was written. */
|
||||
|
@ -100,7 +117,7 @@ int spsc_pbuf_write(struct spsc_pbuf *pb, const char *buf, uint16_t len)
|
|||
wr_idx = idx_cut(pblen, wr_idx + len);
|
||||
pb->wr_idx = wr_idx;
|
||||
|
||||
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_WB);
|
||||
cache_wb(pb, sizeof(*pb), pb->flags);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
@ -110,7 +127,7 @@ int spsc_pbuf_read(struct spsc_pbuf *pb, char *buf, uint16_t len)
|
|||
/* The length of buffer is immutable - avoid reloading. */
|
||||
const uint32_t pblen = pb->len;
|
||||
|
||||
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_INVD);
|
||||
cache_inv(pb, sizeof(*pb), pb->flags);
|
||||
__sync_synchronize();
|
||||
|
||||
uint32_t rd_idx = pb->rd_idx;
|
||||
|
@ -124,12 +141,12 @@ int spsc_pbuf_read(struct spsc_pbuf *pb, char *buf, uint16_t len)
|
|||
uint32_t bytes_stored = idx_occupied(pblen, wr_idx, rd_idx);
|
||||
|
||||
/* Read message len. */
|
||||
sys_cache_data_range(&pb->data[rd_idx], sizeof(pb->data[rd_idx]), K_CACHE_INVD);
|
||||
cache_inv(&pb->data[rd_idx], sizeof(pb->data[rd_idx]), pb->flags);
|
||||
uint16_t mlen = pb->data[rd_idx];
|
||||
|
||||
rd_idx = idx_cut(pblen, rd_idx + sizeof(pb->data[rd_idx]));
|
||||
|
||||
sys_cache_data_range(&pb->data[rd_idx], sizeof(pb->data[rd_idx]), K_CACHE_INVD);
|
||||
cache_inv(&pb->data[rd_idx], sizeof(pb->data[rd_idx]), pb->flags);
|
||||
mlen |= (pb->data[rd_idx] << 8);
|
||||
rd_idx = idx_cut(pblen, rd_idx + sizeof(pb->data[rd_idx]));
|
||||
|
||||
|
@ -152,11 +169,11 @@ int spsc_pbuf_read(struct spsc_pbuf *pb, char *buf, uint16_t len)
|
|||
/* Read up to the end of the buffer. */
|
||||
uint32_t sz = MIN(len, pblen - rd_idx);
|
||||
|
||||
sys_cache_data_range(&pb->data[rd_idx], sz, K_CACHE_INVD);
|
||||
cache_inv(&pb->data[rd_idx], sz, pb->flags);
|
||||
memcpy(buf, &pb->data[rd_idx], sz);
|
||||
if (len > sz) {
|
||||
/* Read remaining bytes starting from the buffer head. */
|
||||
sys_cache_data_range(&pb->data[0], len - sz, K_CACHE_INVD);
|
||||
cache_inv(&pb->data[0], len - sz, pb->flags);
|
||||
memcpy(&buf[sz], &pb->data[0], len - sz);
|
||||
}
|
||||
|
||||
|
@ -165,7 +182,7 @@ int spsc_pbuf_read(struct spsc_pbuf *pb, char *buf, uint16_t len)
|
|||
rd_idx = idx_cut(pblen, rd_idx + len);
|
||||
pb->rd_idx = rd_idx;
|
||||
|
||||
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_WB);
|
||||
cache_wb(pb, sizeof(*pb), pb->flags);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ config IPC_SERVICE_BACKEND_ICMSG
|
|||
depends on MBOX
|
||||
default $(dt_compat_enabled,$(DT_COMPAT_ZEPHYR_IPC_ICMSG))
|
||||
select SPSC_PBUF
|
||||
select SPSC_PBUF_USE_CACHE
|
||||
help
|
||||
Chosing this backend results in single endpoint implementation based
|
||||
on circular packet buffer.
|
||||
|
|
|
@ -192,7 +192,9 @@ static int backend_init(const struct device *instance)
|
|||
|
||||
__ASSERT_NO_MSG(conf->tx_shm_size > sizeof(struct spsc_pbuf));
|
||||
|
||||
dev_data->tx_ib = spsc_pbuf_init((void *)conf->tx_shm_addr, conf->tx_shm_size);
|
||||
dev_data->tx_ib = spsc_pbuf_init((void *)conf->tx_shm_addr,
|
||||
conf->tx_shm_size,
|
||||
SPSC_PBUF_CACHE);
|
||||
dev_data->rx_ib = (void *)conf->rx_shm_addr;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
* 212 - sizeof(struct spsc_pbuf) - 1 = 199.
|
||||
* -1 because internal rd/wr_idx is reserved to mean the buffer is empty.
|
||||
*/
|
||||
static uint8_t memory_area[212] __aligned(4);
|
||||
static uint8_t memory_area[216] __aligned(4);
|
||||
|
||||
static void test_spsc_pbuf_ut(void)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ static void test_spsc_pbuf_ut(void)
|
|||
int rlen;
|
||||
int wlen;
|
||||
|
||||
ib = spsc_pbuf_init(memory_area, sizeof(memory_area));
|
||||
ib = spsc_pbuf_init(memory_area, sizeof(memory_area), 0);
|
||||
zassert_equal_ptr(ib, memory_area, NULL);
|
||||
zassert_equal(ib->len, (sizeof(memory_area) - sizeof(*ib)), NULL);
|
||||
zassert_equal(ib->wr_idx, 0, NULL);
|
||||
|
@ -48,14 +48,16 @@ static void test_spsc_pbuf_ut(void)
|
|||
zassert_equal(ib->wr_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
||||
zassert_equal(ib->rd_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
||||
|
||||
/* Buffer size is 212 - 12 = 200 Bytes for len, wr_idx, rd_idx.
|
||||
/* Buffer size is 216 - 16 = 200 Bytes for len, wr_idx, rd_idx and flags.
|
||||
* When writing message of 20 Bytes, actually 22 Bytes are stored,
|
||||
* (2 Bytes reserved for message len). Test if after 9 writes, 10th write
|
||||
* would return -ENOMEM. 200 - (9 * 22) = 2 bytes left.
|
||||
*
|
||||
* Reset the buffer first.
|
||||
*/
|
||||
ib = spsc_pbuf_init(memory_area, sizeof(memory_area));
|
||||
zassert_equal(SPSC_PBUF_CAPACITY(sizeof(memory_area)), 200, NULL);
|
||||
|
||||
ib = spsc_pbuf_init(memory_area, sizeof(memory_area), 0);
|
||||
zassert_equal_ptr(ib, memory_area, NULL);
|
||||
zassert_equal(ib->len, (sizeof(memory_area) - sizeof(*ib)), NULL);
|
||||
zassert_equal(ib->wr_idx, 0, NULL);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue