ipc: icmsg multi endpoint with nocopy in receive path
Add nocopy feature to icmsg-me initiator and follower roles in their receive path. This feature is optional, configured with Kconfig. Signed-off-by: Hubert Miś <hubert.mis@gmail.com>
This commit is contained in:
parent
6d3d2309ce
commit
0b5ac4023f
3 changed files with 166 additions and 62 deletions
|
@ -37,4 +37,14 @@ config IPC_SERVICE_BACKEND_ICMSG_ME_SHMEM_RESET
|
|||
is going to be incorrectly interpreted by spsc and icmsg instances.
|
||||
This option adds performance overhead in the initialization process.
|
||||
|
||||
config IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX
|
||||
bool "Nocopy feature for receive path"
|
||||
select IPC_SERVICE_ICMSG_NOCOPY_RX
|
||||
help
|
||||
Enable nocopy feature for receive path of multiendpoint icmsg
|
||||
ipc_service backend. This features enables functions to hold and
|
||||
release rx buffer by the ipc_service API user. It also creates
|
||||
performance and memory overhead so it is recommended to disable
|
||||
this feature if unused by the API user.
|
||||
|
||||
endif # IPC_SERVICE_BACKEND_ICMSG_ME_INITIATOR || IPC_SERVICE_BACKEND_ICMSG_ME_FOLLOWER
|
||||
|
|
|
@ -107,6 +107,26 @@ static int cache_ept_rmt(struct backend_data_t *data, const char *name,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void *icmsg_buffer_to_user_buffer(const void *icmsg_buffer)
|
||||
{
|
||||
return (void *)(((char *)icmsg_buffer) + sizeof(ept_id_t));
|
||||
}
|
||||
|
||||
static void *user_buffer_to_icmsg_buffer(const void *user_buffer)
|
||||
{
|
||||
return (void *)(((char *)user_buffer) - sizeof(ept_id_t));
|
||||
}
|
||||
|
||||
static size_t icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len)
|
||||
{
|
||||
return icmsg_buffer_len - sizeof(ept_id_t);
|
||||
}
|
||||
|
||||
static size_t user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len)
|
||||
{
|
||||
return user_buffer_len + sizeof(ept_id_t);
|
||||
}
|
||||
|
||||
static void set_ept_id_in_send_buffer(uint8_t *send_buffer, ept_id_t ept_id)
|
||||
{
|
||||
send_buffer[0] = ept_id;
|
||||
|
@ -201,9 +221,10 @@ static void received(const void *data, size_t len, void *priv)
|
|||
return;
|
||||
}
|
||||
if (dev_data->epts[i]->cb.received) {
|
||||
dev_data->epts[i]->cb.received(id + 1,
|
||||
len - sizeof(ept_id_t),
|
||||
dev_data->epts[i]->priv);
|
||||
dev_data->epts[i]->cb.received(
|
||||
icmsg_buffer_to_user_buffer(data),
|
||||
icmsg_buffer_len_to_user_buffer_len(len),
|
||||
dev_data->epts[i]->priv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +296,7 @@ exit:
|
|||
}
|
||||
|
||||
static int send(const struct device *instance, void *token,
|
||||
const void *msg, size_t len)
|
||||
const void *msg, size_t user_len)
|
||||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
|
@ -287,7 +308,7 @@ static int send(const struct device *instance, void *token,
|
|||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
if (len >= SEND_BUF_SIZE - sizeof(ept_id_t)) {
|
||||
if (user_len >= SEND_BUF_SIZE - sizeof(ept_id_t)) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
|
@ -296,10 +317,11 @@ static int send(const struct device *instance, void *token,
|
|||
/* TODO: Optimization: How to avoid this copying? */
|
||||
/* Scatter list supported by icmsg? */
|
||||
set_ept_id_in_send_buffer(dev_data->send_buffer, *id);
|
||||
memcpy(dev_data->send_buffer + sizeof(ept_id_t), msg, len);
|
||||
memcpy(icmsg_buffer_to_user_buffer(dev_data->send_buffer), msg,
|
||||
user_len);
|
||||
|
||||
r = icmsg_send(conf, &dev_data->icmsg_data, dev_data->send_buffer,
|
||||
len + sizeof(ept_id_t));
|
||||
user_buffer_len_to_icmsg_buffer_len(user_len));
|
||||
|
||||
if (r > 0) {
|
||||
sent_bytes = r - 1;
|
||||
|
@ -317,34 +339,36 @@ static int send(const struct device *instance, void *token,
|
|||
static size_t get_buffer_length_to_pass(size_t allocated_buffer_length)
|
||||
{
|
||||
if (allocated_buffer_length >= sizeof(ept_id_t)) {
|
||||
return allocated_buffer_length - sizeof(ept_id_t);
|
||||
return icmsg_buffer_len_to_user_buffer_len(
|
||||
allocated_buffer_length);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_tx_buffer(const struct device *instance, void *token,
|
||||
void **data, uint32_t *len, k_timeout_t wait)
|
||||
void **data, uint32_t *user_len, k_timeout_t wait)
|
||||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
char *allocated_buffer;
|
||||
void *icmsg_buffer;
|
||||
int r;
|
||||
size_t len_to_allocate = *len;
|
||||
size_t icmsg_len;
|
||||
|
||||
if (!K_TIMEOUT_EQ(wait, K_NO_WAIT)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (len_to_allocate) {
|
||||
len_to_allocate += sizeof(ept_id_t);
|
||||
if (*user_len) {
|
||||
icmsg_len = user_buffer_len_to_icmsg_buffer_len(*user_len);
|
||||
} else {
|
||||
icmsg_len = 0;
|
||||
}
|
||||
|
||||
r = icmsg_get_tx_buffer(conf, &dev_data->icmsg_data,
|
||||
(void **)&allocated_buffer,
|
||||
&len_to_allocate);
|
||||
r = icmsg_get_tx_buffer(conf, &dev_data->icmsg_data, &icmsg_buffer,
|
||||
&icmsg_len);
|
||||
if (r == -ENOMEM) {
|
||||
*len = get_buffer_length_to_pass(len_to_allocate);
|
||||
*user_len = get_buffer_length_to_pass(icmsg_len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -352,19 +376,19 @@ static int get_tx_buffer(const struct device *instance, void *token,
|
|||
return r;
|
||||
}
|
||||
|
||||
*len = get_buffer_length_to_pass(len_to_allocate);
|
||||
/* If requested max buffer length (*len == 0) allocated buffer might be
|
||||
* shorter than sizeof(ept_id_t). In such circumstances drop the buffer
|
||||
* and return error.
|
||||
*user_len = get_buffer_length_to_pass(icmsg_len);
|
||||
/* If requested max buffer length (*user_len == 0) allocated buffer
|
||||
* might be shorter than sizeof(ept_id_t). In such circumstances drop
|
||||
* the buffer and return error.
|
||||
*/
|
||||
|
||||
if (*len) {
|
||||
*data = allocated_buffer + sizeof(ept_id_t);
|
||||
if (*user_len) {
|
||||
*data = icmsg_buffer_to_user_buffer(icmsg_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = icmsg_drop_tx_buffer(conf, &dev_data->icmsg_data,
|
||||
allocated_buffer);
|
||||
icmsg_buffer);
|
||||
__ASSERT_NO_MSG(!r);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
@ -374,9 +398,7 @@ static int drop_tx_buffer(const struct device *instance, void *token,
|
|||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
const char *buffer_to_drop = data;
|
||||
|
||||
buffer_to_drop -= sizeof(ept_id_t);
|
||||
const void *buffer_to_drop = user_buffer_to_icmsg_buffer(data);
|
||||
|
||||
return icmsg_drop_tx_buffer(conf, &dev_data->icmsg_data, buffer_to_drop);
|
||||
}
|
||||
|
@ -387,28 +409,49 @@ static int send_nocopy(const struct device *instance, void *token,
|
|||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
ept_id_t *id = token;
|
||||
char *buffer_to_send = (void *)data;
|
||||
size_t len_to_send = len;
|
||||
void *buffer_to_send;
|
||||
size_t len_to_send;
|
||||
int r;
|
||||
|
||||
if (*id == INVALID_EPT_ID) {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
buffer_to_send -= sizeof(ept_id_t);
|
||||
len_to_send += sizeof(ept_id_t);
|
||||
buffer_to_send = user_buffer_to_icmsg_buffer(data);
|
||||
len_to_send = user_buffer_len_to_icmsg_buffer_len(len);
|
||||
|
||||
set_ept_id_in_send_buffer(buffer_to_send, *id);
|
||||
|
||||
r = icmsg_send_nocopy(conf, &dev_data->icmsg_data, buffer_to_send,
|
||||
len_to_send);
|
||||
if (r > 0) {
|
||||
return r - sizeof(ept_id_t);
|
||||
return icmsg_buffer_len_to_user_buffer_len(r);
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX
|
||||
int hold_rx_buffer(const struct device *instance, void *token, void *data)
|
||||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
void *icmsg_buffer = user_buffer_to_icmsg_buffer(data);
|
||||
|
||||
return icmsg_hold_rx_buffer(conf, &dev_data->icmsg_data, icmsg_buffer);
|
||||
}
|
||||
|
||||
int release_rx_buffer(const struct device *instance, void *token, void *data)
|
||||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
void *icmsg_buffer = user_buffer_to_icmsg_buffer(data);
|
||||
|
||||
return icmsg_release_rx_buffer(conf, &dev_data->icmsg_data,
|
||||
icmsg_buffer);
|
||||
}
|
||||
#endif /* CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX */
|
||||
|
||||
const static struct ipc_service_backend backend_ops = {
|
||||
.open_instance = open,
|
||||
.register_endpoint = register_ept,
|
||||
|
@ -417,6 +460,11 @@ const static struct ipc_service_backend backend_ops = {
|
|||
.get_tx_buffer = get_tx_buffer,
|
||||
.drop_tx_buffer = drop_tx_buffer,
|
||||
.send_nocopy = send_nocopy,
|
||||
|
||||
#ifdef CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX
|
||||
.hold_rx_buffer = hold_rx_buffer,
|
||||
.release_rx_buffer = release_rx_buffer,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int backend_init(const struct device *instance)
|
||||
|
|
|
@ -45,6 +45,26 @@ static void bound(void *priv)
|
|||
k_event_post(&dev_data->event, EVENT_BOUND);
|
||||
}
|
||||
|
||||
static void *icmsg_buffer_to_user_buffer(const void *icmsg_buffer)
|
||||
{
|
||||
return (void *)(((char *)icmsg_buffer) + sizeof(ept_id_t));
|
||||
}
|
||||
|
||||
static void *user_buffer_to_icmsg_buffer(const void *user_buffer)
|
||||
{
|
||||
return (void *)(((char *)user_buffer) - sizeof(ept_id_t));
|
||||
}
|
||||
|
||||
static size_t icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len)
|
||||
{
|
||||
return icmsg_buffer_len - sizeof(ept_id_t);
|
||||
}
|
||||
|
||||
static size_t user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len)
|
||||
{
|
||||
return user_buffer_len + sizeof(ept_id_t);
|
||||
}
|
||||
|
||||
static void set_ept_id_in_send_buffer(uint8_t *send_buffer, ept_id_t ept_id)
|
||||
{
|
||||
send_buffer[0] = ept_id;
|
||||
|
@ -86,9 +106,10 @@ static void received(const void *data, size_t len, void *priv)
|
|||
return;
|
||||
}
|
||||
if (dev_data->epts[i]->cb.received) {
|
||||
dev_data->epts[i]->cb.received(id + 1,
|
||||
len - sizeof(ept_id_t),
|
||||
dev_data->epts[i]->priv);
|
||||
dev_data->epts[i]->cb.received(
|
||||
icmsg_buffer_to_user_buffer(data),
|
||||
icmsg_buffer_len_to_user_buffer_len(len),
|
||||
dev_data->epts[i]->priv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,12 +207,12 @@ static int send(const struct device *instance, void *token,
|
|||
* scatter list also for SPSC buffer implementation.
|
||||
*/
|
||||
set_ept_id_in_send_buffer(dev_data->send_buffer, *id);
|
||||
memcpy(dev_data->send_buffer + sizeof(ept_id_t), msg, len);
|
||||
memcpy(icmsg_buffer_to_user_buffer(dev_data->send_buffer), msg, len);
|
||||
|
||||
r = icmsg_send(conf, &dev_data->icmsg_data, dev_data->send_buffer,
|
||||
len + sizeof(ept_id_t));
|
||||
user_buffer_len_to_icmsg_buffer_len(len));
|
||||
if (r > 0) {
|
||||
sent_bytes = r - sizeof(ept_id_t);;
|
||||
sent_bytes = icmsg_buffer_len_to_user_buffer_len(r);
|
||||
}
|
||||
|
||||
k_mutex_unlock(&dev_data->send_mutex);
|
||||
|
@ -203,37 +224,38 @@ static int send(const struct device *instance, void *token,
|
|||
}
|
||||
}
|
||||
|
||||
static size_t get_buffer_length_to_pass(size_t allocated_buffer_length)
|
||||
static size_t get_buffer_length_to_pass(size_t icmsg_buffer_len)
|
||||
{
|
||||
if (allocated_buffer_length >= sizeof(ept_id_t)) {
|
||||
return allocated_buffer_length - sizeof(ept_id_t);
|
||||
if (icmsg_buffer_len >= sizeof(ept_id_t)) {
|
||||
return icmsg_buffer_len_to_user_buffer_len(icmsg_buffer_len);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_tx_buffer(const struct device *instance, void *token,
|
||||
void **data, uint32_t *len, k_timeout_t wait)
|
||||
void **data, uint32_t *user_len, k_timeout_t wait)
|
||||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
char *allocated_buffer;
|
||||
void *icmsg_buffer;
|
||||
int r;
|
||||
size_t len_to_allocate = *len;
|
||||
size_t icmsg_len;
|
||||
|
||||
if (!K_TIMEOUT_EQ(wait, K_NO_WAIT)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (len_to_allocate) {
|
||||
len_to_allocate += sizeof(ept_id_t);
|
||||
if (*user_len) {
|
||||
icmsg_len = user_buffer_len_to_icmsg_buffer_len(*user_len);
|
||||
} else {
|
||||
icmsg_len = 0;
|
||||
}
|
||||
|
||||
r = icmsg_get_tx_buffer(conf, &dev_data->icmsg_data,
|
||||
(void **)&allocated_buffer,
|
||||
&len_to_allocate);
|
||||
r = icmsg_get_tx_buffer(conf, &dev_data->icmsg_data, &icmsg_buffer,
|
||||
&icmsg_len);
|
||||
if (r == -ENOMEM) {
|
||||
*len = get_buffer_length_to_pass(len_to_allocate);
|
||||
*user_len = get_buffer_length_to_pass(icmsg_len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -241,19 +263,19 @@ static int get_tx_buffer(const struct device *instance, void *token,
|
|||
return r;
|
||||
}
|
||||
|
||||
*len = get_buffer_length_to_pass(len_to_allocate);
|
||||
*user_len = get_buffer_length_to_pass(icmsg_len);
|
||||
/* If requested max buffer length (*len == 0) allocated buffer might be
|
||||
* shorter than sizeof(ept_id_t). In such circumstances drop the buffer
|
||||
* and return error.
|
||||
*/
|
||||
|
||||
if (*len) {
|
||||
*data = allocated_buffer + sizeof(ept_id_t);
|
||||
if (*user_len) {
|
||||
*data = icmsg_buffer_to_user_buffer(icmsg_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = icmsg_drop_tx_buffer(conf, &dev_data->icmsg_data,
|
||||
allocated_buffer);
|
||||
icmsg_buffer);
|
||||
__ASSERT_NO_MSG(!r);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
@ -263,9 +285,7 @@ static int drop_tx_buffer(const struct device *instance, void *token,
|
|||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
const char *buffer_to_drop = data;
|
||||
|
||||
buffer_to_drop -= sizeof(ept_id_t);
|
||||
const void *buffer_to_drop = user_buffer_to_icmsg_buffer(data);
|
||||
|
||||
return icmsg_drop_tx_buffer(conf, &dev_data->icmsg_data, buffer_to_drop);
|
||||
}
|
||||
|
@ -276,24 +296,45 @@ static int send_nocopy(const struct device *instance, void *token,
|
|||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
ept_id_t *id = token;
|
||||
char *buffer_to_send = (void *)data;
|
||||
size_t len_to_send = len;
|
||||
void *buffer_to_send;
|
||||
size_t len_to_send;
|
||||
int r;
|
||||
|
||||
buffer_to_send -= sizeof(ept_id_t);
|
||||
len_to_send += sizeof(ept_id_t);
|
||||
buffer_to_send = user_buffer_to_icmsg_buffer(data);
|
||||
len_to_send = user_buffer_len_to_icmsg_buffer_len(len);
|
||||
|
||||
set_ept_id_in_send_buffer(buffer_to_send, *id);
|
||||
|
||||
r = icmsg_send_nocopy(conf, &dev_data->icmsg_data, buffer_to_send,
|
||||
len_to_send);
|
||||
if (r > 0) {
|
||||
return r - sizeof(ept_id_t);
|
||||
return icmsg_buffer_len_to_user_buffer_len(r);
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX
|
||||
int hold_rx_buffer(const struct device *instance, void *token, void *data)
|
||||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
void *icmsg_buffer = user_buffer_to_icmsg_buffer(data);
|
||||
|
||||
return icmsg_hold_rx_buffer(conf, &dev_data->icmsg_data, icmsg_buffer);
|
||||
}
|
||||
|
||||
int release_rx_buffer(const struct device *instance, void *token, void *data)
|
||||
{
|
||||
const struct icmsg_config_t *conf = instance->config;
|
||||
struct backend_data_t *dev_data = instance->data;
|
||||
void *icmsg_buffer = user_buffer_to_icmsg_buffer(data);
|
||||
|
||||
return icmsg_release_rx_buffer(conf, &dev_data->icmsg_data,
|
||||
icmsg_buffer);
|
||||
}
|
||||
#endif /* CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX */
|
||||
|
||||
const static struct ipc_service_backend backend_ops = {
|
||||
.open_instance = open,
|
||||
.register_endpoint = register_ept,
|
||||
|
@ -302,6 +343,11 @@ const static struct ipc_service_backend backend_ops = {
|
|||
.get_tx_buffer = get_tx_buffer,
|
||||
.drop_tx_buffer = drop_tx_buffer,
|
||||
.send_nocopy = send_nocopy,
|
||||
|
||||
#ifdef CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX
|
||||
.hold_rx_buffer = hold_rx_buffer,
|
||||
.release_rx_buffer = release_rx_buffer,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int backend_init(const struct device *instance)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue