ipc: backends: Port IcMsg based backends to use pbuf

Replace spsc_pbuf with pbuf implementation dedicated to
be used by IcMsg based backends.
The pbuf is written on top of simple read/write semantics
with minimal footprint and code complexity

Signed-off-by: Emil Obalski <Emil.Obalski@nordicsemi.no>
This commit is contained in:
Emil Obalski 2023-10-24 12:24:49 +02:00 committed by Fabio Baltieri
parent 25a0489d62
commit eb4fc3f083
7 changed files with 105 additions and 304 deletions

View File

@ -21,6 +21,19 @@ properties:
required: true
type: phandle
dcache-alignment:
type: int
description: |
Data cache alignment. If any side of the communication uses cache on
rx-region/tx-region this property must be the biggest value of the
invalidation or the write-back size for both sides of the communication.
If no side of the communication uses data cache this property could be
safely omitted.
For example:
Side A: no data cache
Side B: 32 Bytes write-back size, 16 Bytes invalidation size
dcache-alignment = 32; for both
mboxes:
description: phandle to the MBOX controller (TX and RX are required)
required: true

View File

@ -12,8 +12,8 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/mbox.h>
#include <zephyr/ipc/ipc_service.h>
#include <zephyr/ipc/pbuf.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/spsc_pbuf.h>
#ifdef __cplusplus
extern "C" {
@ -33,19 +33,14 @@ enum icmsg_state {
};
struct icmsg_config_t {
uintptr_t tx_shm_addr;
uintptr_t rx_shm_addr;
size_t tx_shm_size;
size_t rx_shm_size;
struct mbox_channel mbox_tx;
struct mbox_channel mbox_rx;
};
struct icmsg_data_t {
/* Tx/Rx buffers. */
struct spsc_pbuf *tx_ib;
struct spsc_pbuf *rx_ib;
atomic_t tx_buffer_state;
struct pbuf *tx_pb;
struct pbuf *rx_pb;
#ifdef CONFIG_IPC_SERVICE_ICMSG_SHMEM_ACCESS_SYNC
struct k_mutex tx_lock;
#endif
@ -59,12 +54,6 @@ struct icmsg_data_t {
struct k_work_delayable notify_work;
struct k_work mbox_work;
atomic_t state;
/* No-copy */
#ifdef CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX
atomic_t rx_buffer_state;
const void *rx_buffer;
uint16_t rx_len;
#endif
};
/** @brief Open an icmsg instance

View File

@ -54,27 +54,33 @@ static int backend_init(const struct device *instance)
return 0;
}
#define DEFINE_BACKEND_DEVICE(i) \
static const struct icmsg_config_t backend_config_##i = { \
.tx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
.tx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
.rx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
.rx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
.mbox_tx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), tx), \
.mbox_rx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), rx), \
}; \
\
BUILD_ASSERT(DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)) > \
sizeof(struct spsc_pbuf)); \
static struct icmsg_data_t backend_data_##i; \
\
DEVICE_DT_INST_DEFINE(i, \
&backend_init, \
NULL, \
&backend_data_##i, \
&backend_config_##i, \
POST_KERNEL, \
CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY, \
#define DEFINE_BACKEND_DEVICE(i) \
static const struct icmsg_config_t backend_config_##i = { \
.mbox_tx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), tx), \
.mbox_rx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), rx), \
}; \
\
PBUF_DEFINE(tx_pb_##i, \
DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
PBUF_DEFINE(rx_pb_##i, \
DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
\
static struct icmsg_data_t backend_data_##i = { \
.tx_pb = &tx_pb_##i, \
.rx_pb = &rx_pb_##i, \
}; \
\
DEVICE_DT_INST_DEFINE(i, \
&backend_init, \
NULL, \
&backend_data_##i, \
&backend_config_##i, \
POST_KERNEL, \
CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY, \
&backend_ops);
DT_INST_FOREACH_STATUS_OKAY(DEFINE_BACKEND_DEVICE)

View File

@ -277,16 +277,28 @@ static int backend_init(const struct device *instance)
}
#define DEFINE_BACKEND_DEVICE(i) \
static const struct icmsg_config_t backend_config_##i = \
{ \
.tx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
.tx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
.rx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
.rx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
static const struct icmsg_config_t backend_config_##i = { \
.mbox_tx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), tx), \
.mbox_rx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), rx), \
}; \
static struct backend_data_t backend_data_##i; \
\
PBUF_DEFINE(tx_pb_##i, \
DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
PBUF_DEFINE(rx_pb_##i, \
DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
\
static struct backend_data_t backend_data_##i = { \
.icmsg_me_data = { \
.icmsg_data = { \
.tx_pb = &tx_pb_##i, \
.rx_pb = &rx_pb_##i, \
} \
} \
}; \
\
DEVICE_DT_INST_DEFINE(i, \
&backend_init, \

View File

@ -183,16 +183,28 @@ static int backend_init(const struct device *instance)
}
#define DEFINE_BACKEND_DEVICE(i) \
static const struct icmsg_config_t backend_config_##i = \
{ \
.tx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
.tx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
.rx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
.rx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
static const struct icmsg_config_t backend_config_##i = { \
.mbox_tx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), tx), \
.mbox_rx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), rx), \
}; \
static struct backend_data_t backend_data_##i; \
\
PBUF_DEFINE(tx_pb_##i, \
DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
PBUF_DEFINE(rx_pb_##i, \
DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
\
static struct backend_data_t backend_data_##i = { \
.icmsg_me_data = { \
.icmsg_data = { \
.tx_pb = &tx_pb_##i, \
.rx_pb = &rx_pb_##i, \
} \
} \
}; \
\
DEVICE_DT_INST_DEFINE(i, \
&backend_init, \

View File

@ -21,8 +21,7 @@ config IPC_SERVICE_STATIC_VRINGS_MEM_ALIGNMENT
menuconfig IPC_SERVICE_ICMSG
bool "icmsg IPC library"
select SPSC_PBUF
select SPSC_PBUF_USE_CACHE
select PBUF
help
Icmsg library

View File

@ -9,22 +9,12 @@
#include <string.h>
#include <zephyr/drivers/mbox.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/spsc_pbuf.h>
#include <zephyr/ipc/pbuf.h>
#include <zephyr/init.h>
#define BOND_NOTIFY_REPEAT_TO K_MSEC(CONFIG_IPC_SERVICE_ICMSG_BOND_NOTIFY_REPEAT_TO_MS)
#define SHMEM_ACCESS_TO K_MSEC(CONFIG_IPC_SERVICE_ICMSG_SHMEM_ACCESS_TO_MS)
enum rx_buffer_state {
RX_BUFFER_STATE_RELEASED,
RX_BUFFER_STATE_RELEASING,
RX_BUFFER_STATE_HELD
};
enum tx_buffer_state {
TX_BUFFER_STATE_UNUSED,
TX_BUFFER_STATE_RESERVED
};
static const uint8_t magic[] = {0x45, 0x6d, 0x31, 0x6c, 0x31, 0x4b,
0x30, 0x72, 0x6e, 0x33, 0x6c, 0x69, 0x34};
@ -82,12 +72,6 @@ static bool is_endpoint_ready(struct icmsg_data_t *dev_data)
return atomic_get(&dev_data->state) == ICMSG_STATE_READY;
}
static bool is_tx_buffer_reserved(struct icmsg_data_t *dev_data)
{
return atomic_get(&dev_data->tx_buffer_state) ==
TX_BUFFER_STATE_RESERVED;
}
static int reserve_tx_buffer_if_unused(struct icmsg_data_t *dev_data)
{
#ifdef CONFIG_IPC_SERVICE_ICMSG_SHMEM_ACCESS_SYNC
@ -97,52 +81,20 @@ static int reserve_tx_buffer_if_unused(struct icmsg_data_t *dev_data)
return ret;
}
#endif
bool was_unused = atomic_cas(&dev_data->tx_buffer_state,
TX_BUFFER_STATE_UNUSED, TX_BUFFER_STATE_RESERVED);
return was_unused ? 0 : -EALREADY;
return 0;
}
static int release_tx_buffer(struct icmsg_data_t *dev_data)
{
bool was_reserved = atomic_cas(&dev_data->tx_buffer_state,
TX_BUFFER_STATE_RESERVED, TX_BUFFER_STATE_UNUSED);
if (!was_reserved) {
return -EALREADY;
}
#ifdef CONFIG_IPC_SERVICE_ICMSG_SHMEM_ACCESS_SYNC
return k_mutex_unlock(&dev_data->tx_lock);
#else
#endif
return 0;
#endif
}
static bool is_rx_buffer_free(struct icmsg_data_t *dev_data)
static uint32_t data_available(struct icmsg_data_t *dev_data)
{
#ifdef CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX
return atomic_get(&dev_data->rx_buffer_state) == RX_BUFFER_STATE_RELEASED;
#else
return true;
#endif
}
static bool is_rx_buffer_held(struct icmsg_data_t *dev_data)
{
#ifdef CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX
return atomic_get(&dev_data->rx_buffer_state) == RX_BUFFER_STATE_HELD;
#else
return false;
#endif
}
static bool is_rx_data_available(struct icmsg_data_t *dev_data)
{
int len = spsc_pbuf_read(dev_data->rx_ib, NULL, 0);
return len > 0;
return pbuf_read(dev_data->rx_pb, NULL, 0);
}
static void submit_mbox_work(struct icmsg_data_t *dev_data)
@ -157,20 +109,13 @@ static void submit_mbox_work(struct icmsg_data_t *dev_data)
static void submit_work_if_buffer_free(struct icmsg_data_t *dev_data)
{
if (!is_rx_buffer_free(dev_data)) {
return;
}
submit_mbox_work(dev_data);
}
static void submit_work_if_buffer_free_and_data_available(
struct icmsg_data_t *dev_data)
{
if (!is_rx_buffer_free(dev_data)) {
return;
}
if (!is_rx_data_available(dev_data)) {
if (!data_available(dev_data)) {
return;
}
@ -179,47 +124,31 @@ static void submit_work_if_buffer_free_and_data_available(
static void mbox_callback_process(struct k_work *item)
{
char *rx_buffer;
struct icmsg_data_t *dev_data = CONTAINER_OF(item, struct icmsg_data_t, mbox_work);
atomic_t state = atomic_get(&dev_data->state);
uint16_t len = spsc_pbuf_claim(dev_data->rx_ib, &rx_buffer);
uint32_t len = data_available(dev_data);
if (len == 0) {
/* Unlikely, no data in buffer. */
return;
}
uint8_t rx_buffer[len];
len = pbuf_read(dev_data->rx_pb, rx_buffer, len);
if (state == ICMSG_STATE_READY) {
if (dev_data->cb->received) {
#if CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX
dev_data->rx_buffer = rx_buffer;
dev_data->rx_len = len;
#endif
dev_data->cb->received(rx_buffer, len,
dev_data->ctx);
/* Release Rx buffer here only in case when user did not request
* to hold it.
*/
if (!is_rx_buffer_held(dev_data)) {
spsc_pbuf_free(dev_data->rx_ib, len);
#if CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX
dev_data->rx_buffer = NULL;
dev_data->rx_len = 0;
#endif
}
}
} else {
__ASSERT_NO_MSG(state == ICMSG_STATE_BUSY);
bool endpoint_invalid = (len != sizeof(magic) || memcmp(magic, rx_buffer, len));
spsc_pbuf_free(dev_data->rx_ib, len);
if (endpoint_invalid) {
__ASSERT_NO_MSG(false);
return;
@ -262,8 +191,6 @@ int icmsg_open(const struct icmsg_config_t *conf,
struct icmsg_data_t *dev_data,
const struct ipc_service_cb *cb, void *ctx)
{
__ASSERT_NO_MSG(conf->tx_shm_size > sizeof(struct spsc_pbuf));
if (!atomic_cas(&dev_data->state, ICMSG_STATE_OFF, ICMSG_STATE_BUSY)) {
/* Already opened. */
return -EALREADY;
@ -277,12 +204,18 @@ int icmsg_open(const struct icmsg_config_t *conf,
k_mutex_init(&dev_data->tx_lock);
#endif
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;
int ret = pbuf_init(dev_data->tx_pb);
int ret = spsc_pbuf_write(dev_data->tx_ib, magic, sizeof(magic));
if (ret < 0) {
__ASSERT(false, "Incorrect configuration");
return ret;
}
/* Initialize local copies of rx_pb. */
dev_data->rx_pb->data.wr_idx = 0;
dev_data->rx_pb->data.rd_idx = 0;
ret = pbuf_write(dev_data->tx_pb, magic, sizeof(magic));
if (ret < 0) {
__ASSERT_NO_MSG(false);
@ -345,7 +278,7 @@ int icmsg_send(const struct icmsg_config_t *conf,
return -ENOBUFS;
}
write_ret = spsc_pbuf_write(dev_data->tx_ib, msg, len);
write_ret = pbuf_write(dev_data->tx_pb, msg, len);
release_ret = release_tx_buffer(dev_data);
__ASSERT_NO_MSG(!release_ret);
@ -366,169 +299,6 @@ int icmsg_send(const struct icmsg_config_t *conf,
return sent_bytes;
}
int icmsg_get_tx_buffer(const struct icmsg_config_t *conf,
struct icmsg_data_t *dev_data,
void **data, size_t *size)
{
int ret;
int release_ret;
uint16_t requested_size;
int allocated_len;
char *allocated_buf;
if (*size == 0) {
/* Requested allocation of maximal size.
* Try to allocate maximal buffer size from spsc,
* potentially after wrapping marker.
*/
requested_size = SPSC_PBUF_MAX_LEN - 1;
} else {
requested_size = *size;
}
ret = reserve_tx_buffer_if_unused(dev_data);
if (ret < 0) {
return -ENOBUFS;
}
ret = spsc_pbuf_alloc(dev_data->tx_ib, requested_size, &allocated_buf);
if (ret < 0) {
release_ret = release_tx_buffer(dev_data);
__ASSERT_NO_MSG(!release_ret);
return ret;
}
allocated_len = ret;
if (*size == 0) {
/* Requested allocation of maximal size.
* Pass the buffer that was allocated.
*/
*size = allocated_len;
*data = allocated_buf;
return 0;
}
if (*size == allocated_len) {
/* Allocated buffer is of requested size. */
*data = allocated_buf;
return 0;
}
/* Allocated smaller buffer than requested.
* Silently stop using the allocated buffer what is allowed by SPSC API
*/
release_tx_buffer(dev_data);
*size = allocated_len;
return -ENOMEM;
}
int icmsg_drop_tx_buffer(const struct icmsg_config_t *conf,
struct icmsg_data_t *dev_data,
const void *data)
{
/* Silently stop using the allocated buffer what is allowed by SPSC API
*/
return release_tx_buffer(dev_data);
}
int icmsg_send_nocopy(const struct icmsg_config_t *conf,
struct icmsg_data_t *dev_data,
const void *msg, size_t len)
{
int ret;
int sent_bytes;
if (!is_endpoint_ready(dev_data)) {
return -EBUSY;
}
/* Empty message is not allowed */
if (len == 0) {
return -ENODATA;
}
if (!is_tx_buffer_reserved(dev_data)) {
return -ENXIO;
}
spsc_pbuf_commit(dev_data->tx_ib, len);
sent_bytes = len;
ret = release_tx_buffer(dev_data);
__ASSERT_NO_MSG(!ret);
__ASSERT_NO_MSG(conf->mbox_tx.dev != NULL);
ret = mbox_send(&conf->mbox_tx, NULL);
if (ret) {
return ret;
}
return sent_bytes;
}
#ifdef CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX
int icmsg_hold_rx_buffer(const struct icmsg_config_t *conf,
struct icmsg_data_t *dev_data,
const void *data)
{
bool was_released;
if (!is_endpoint_ready(dev_data)) {
return -EBUSY;
}
if (data != dev_data->rx_buffer) {
return -EINVAL;
}
was_released = atomic_cas(&dev_data->rx_buffer_state,
RX_BUFFER_STATE_RELEASED, RX_BUFFER_STATE_HELD);
if (!was_released) {
return -EALREADY;
}
return 0;
}
int icmsg_release_rx_buffer(const struct icmsg_config_t *conf,
struct icmsg_data_t *dev_data,
const void *data)
{
bool was_held;
if (!is_endpoint_ready(dev_data)) {
return -EBUSY;
}
if (data != dev_data->rx_buffer) {
return -EINVAL;
}
/* Do not schedule new packet processing until buffer will be released.
* Protect buffer against being freed multiple times.
*/
was_held = atomic_cas(&dev_data->rx_buffer_state,
RX_BUFFER_STATE_HELD, RX_BUFFER_STATE_RELEASING);
if (!was_held) {
return -EALREADY;
}
spsc_pbuf_free(dev_data->rx_ib, dev_data->rx_len);
#if CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX
dev_data->rx_buffer = NULL;
dev_data->rx_len = 0;
#endif
atomic_set(&dev_data->rx_buffer_state, RX_BUFFER_STATE_RELEASED);
submit_work_if_buffer_free_and_data_available(dev_data);
return 0;
}
#endif /* CONFIG_IPC_SERVICE_ICMSG_NOCOPY_RX */
#if IS_ENABLED(CONFIG_IPC_SERVICE_BACKEND_ICMSG_WQ_ENABLE)
static int work_q_init(void)