Check if the spinlock is held before attempting to wait by taking the semaphore, as that would cause a context switch which isn't allowed and will trigger an assertion error when `CONFIG_SPIN_VALIDATE` is enabled. Logging in spinlock-held context when the log buffer is full can lead to an infinite assertion error loop, as the logging subsys attempts to allocate buffer when there's none available, it will try to wait for one and thus triggers the assertion error, the error message will be printed through the logging sybsys but there's no buffer available, so it will try to wait for one and triggers another assertion error.. This loop just goes on and on forever, and nothing gets printed to the terminal. Added a test to validate the fix. Signed-off-by: Yong Cong Sin <ycsin@meta.com> Signed-off-by: Yong Cong Sin <yongcong.sin@gmail.com> Signed-off-by: Maxim Adelman <imax@meta.com>
639 lines
16 KiB
C
639 lines
16 KiB
C
/*
|
|
* Copyright (c) 2021 Nordic Semiconductor
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <zephyr/sys/mpsc_pbuf.h>
|
|
|
|
#define MPSC_PBUF_DEBUG 0
|
|
|
|
#define MPSC_PBUF_DBG(buffer, ...) do { \
|
|
if (MPSC_PBUF_DEBUG) { \
|
|
printk(__VA_ARGS__); \
|
|
if (buffer) { \
|
|
mpsc_state_print(buffer); \
|
|
} \
|
|
} \
|
|
} while (false)
|
|
|
|
static inline void mpsc_state_print(struct mpsc_pbuf_buffer *buffer)
|
|
{
|
|
if (MPSC_PBUF_DEBUG) {
|
|
printk(", wr:%d/%d, rd:%d/%d\n",
|
|
buffer->wr_idx, buffer->tmp_wr_idx,
|
|
buffer->rd_idx, buffer->tmp_rd_idx);
|
|
}
|
|
}
|
|
|
|
void mpsc_pbuf_init(struct mpsc_pbuf_buffer *buffer,
|
|
const struct mpsc_pbuf_buffer_config *cfg)
|
|
{
|
|
memset(buffer, 0, offsetof(struct mpsc_pbuf_buffer, buf));
|
|
buffer->get_wlen = cfg->get_wlen;
|
|
buffer->notify_drop = cfg->notify_drop;
|
|
buffer->buf = cfg->buf;
|
|
buffer->size = cfg->size;
|
|
buffer->max_usage = 0;
|
|
buffer->flags = cfg->flags;
|
|
|
|
if (is_power_of_two(buffer->size)) {
|
|
buffer->flags |= MPSC_PBUF_SIZE_POW2;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_MULTITHREADING)) {
|
|
int err;
|
|
|
|
err = k_sem_init(&buffer->sem, 0, 1);
|
|
__ASSERT_NO_MSG(err == 0);
|
|
ARG_UNUSED(err);
|
|
}
|
|
}
|
|
|
|
/* Calculate free space available or till end of buffer.
|
|
*
|
|
* @param buffer Buffer.
|
|
* @param[out] res Destination where free space is written.
|
|
*
|
|
* @retval true when space was calculated until end of buffer (and there might
|
|
* be more space available after wrapping.
|
|
* @retval false When result is total free space.
|
|
*/
|
|
static inline bool free_space(struct mpsc_pbuf_buffer *buffer, uint32_t *res)
|
|
{
|
|
if (buffer->flags & MPSC_PBUF_FULL) {
|
|
*res = 0;
|
|
return false;
|
|
}
|
|
|
|
if (buffer->rd_idx > buffer->tmp_wr_idx) {
|
|
*res = buffer->rd_idx - buffer->tmp_wr_idx;
|
|
return false;
|
|
}
|
|
*res = buffer->size - buffer->tmp_wr_idx;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Get amount of valid data.
|
|
*
|
|
* @param buffer Buffer.
|
|
* @param[out] res Destination where available space is written.
|
|
*
|
|
* @retval true when space was calculated until end of buffer (and there might
|
|
* be more space available after wrapping.
|
|
* @retval false When result is total free space.
|
|
*/
|
|
static inline bool available(struct mpsc_pbuf_buffer *buffer, uint32_t *res)
|
|
{
|
|
if (buffer->flags & MPSC_PBUF_FULL || buffer->tmp_rd_idx > buffer->wr_idx) {
|
|
*res = buffer->size - buffer->tmp_rd_idx;
|
|
return true;
|
|
}
|
|
|
|
*res = (buffer->wr_idx - buffer->tmp_rd_idx);
|
|
|
|
return false;
|
|
}
|
|
|
|
static inline uint32_t get_usage(struct mpsc_pbuf_buffer *buffer)
|
|
{
|
|
uint32_t f;
|
|
|
|
if (free_space(buffer, &f)) {
|
|
f += (buffer->rd_idx - 1);
|
|
}
|
|
|
|
return buffer->size - 1 - f;
|
|
}
|
|
|
|
static inline void max_utilization_update(struct mpsc_pbuf_buffer *buffer)
|
|
{
|
|
if (!(buffer->flags & MPSC_PBUF_MAX_UTILIZATION)) {
|
|
return;
|
|
}
|
|
|
|
buffer->max_usage = MAX(buffer->max_usage, get_usage(buffer));
|
|
}
|
|
|
|
static inline bool is_valid(union mpsc_pbuf_generic *item)
|
|
{
|
|
return item->hdr.valid;
|
|
}
|
|
|
|
static inline bool is_invalid(union mpsc_pbuf_generic *item)
|
|
{
|
|
return !item->hdr.valid && !item->hdr.busy;
|
|
}
|
|
|
|
static inline uint32_t idx_inc(struct mpsc_pbuf_buffer *buffer,
|
|
uint32_t idx, int32_t val)
|
|
{
|
|
uint32_t i = idx + val;
|
|
|
|
if (buffer->flags & MPSC_PBUF_SIZE_POW2) {
|
|
return i & (buffer->size - 1);
|
|
}
|
|
|
|
return (i >= buffer->size) ? i - buffer->size : i;
|
|
}
|
|
|
|
static inline uint32_t get_skip(union mpsc_pbuf_generic *item)
|
|
{
|
|
if (item->hdr.busy && !item->hdr.valid) {
|
|
return item->skip.len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static ALWAYS_INLINE void tmp_wr_idx_inc(struct mpsc_pbuf_buffer *buffer, int32_t wlen)
|
|
{
|
|
buffer->tmp_wr_idx = idx_inc(buffer, buffer->tmp_wr_idx, wlen);
|
|
if (buffer->tmp_wr_idx == buffer->rd_idx) {
|
|
buffer->flags |= MPSC_PBUF_FULL;
|
|
}
|
|
}
|
|
|
|
static void rd_idx_inc(struct mpsc_pbuf_buffer *buffer, int32_t wlen)
|
|
{
|
|
buffer->rd_idx = idx_inc(buffer, buffer->rd_idx, wlen);
|
|
buffer->flags &= ~MPSC_PBUF_FULL;
|
|
}
|
|
|
|
static void add_skip_item(struct mpsc_pbuf_buffer *buffer, uint32_t wlen)
|
|
{
|
|
union mpsc_pbuf_generic skip = {
|
|
.skip = { .valid = 0, .busy = 1, .len = wlen }
|
|
};
|
|
|
|
buffer->buf[buffer->tmp_wr_idx] = skip.raw;
|
|
tmp_wr_idx_inc(buffer, wlen);
|
|
buffer->wr_idx = idx_inc(buffer, buffer->wr_idx, wlen);
|
|
}
|
|
|
|
static bool drop_item_locked(struct mpsc_pbuf_buffer *buffer,
|
|
uint32_t free_wlen,
|
|
union mpsc_pbuf_generic **item_to_drop,
|
|
uint32_t *tmp_wr_idx_shift)
|
|
{
|
|
union mpsc_pbuf_generic *item;
|
|
uint32_t skip_wlen;
|
|
|
|
item = (union mpsc_pbuf_generic *)&buffer->buf[buffer->rd_idx];
|
|
skip_wlen = get_skip(item);
|
|
*item_to_drop = NULL;
|
|
*tmp_wr_idx_shift = 0;
|
|
|
|
if (skip_wlen) {
|
|
/* Skip packet found, can be dropped to free some space */
|
|
MPSC_PBUF_DBG(buffer, "no space: Found skip packet %d len", skip_wlen);
|
|
|
|
rd_idx_inc(buffer, skip_wlen);
|
|
buffer->tmp_rd_idx = buffer->rd_idx;
|
|
return true;
|
|
}
|
|
|
|
/* Other options for dropping available only in overwrite mode. */
|
|
if (!(buffer->flags & MPSC_PBUF_MODE_OVERWRITE)) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t rd_wlen = buffer->get_wlen(item);
|
|
|
|
/* If packet is busy need to be ommited. */
|
|
if (!is_valid(item)) {
|
|
return false;
|
|
} else if (item->hdr.busy) {
|
|
MPSC_PBUF_DBG(buffer, "no space: Found busy packet %p (len:%d)", item, rd_wlen);
|
|
/* Add skip packet before claimed packet. */
|
|
if (free_wlen) {
|
|
add_skip_item(buffer, free_wlen);
|
|
MPSC_PBUF_DBG(buffer, "no space: Added skip packet (len:%d)", free_wlen);
|
|
}
|
|
/* Move all indexes forward, after claimed packet. */
|
|
buffer->wr_idx = idx_inc(buffer, buffer->wr_idx, rd_wlen);
|
|
|
|
/* If allocation wrapped around the buffer and found busy packet
|
|
* that was already ommited, skip it again.
|
|
*/
|
|
if (buffer->rd_idx == buffer->tmp_rd_idx) {
|
|
buffer->tmp_rd_idx = idx_inc(buffer, buffer->tmp_rd_idx, rd_wlen);
|
|
}
|
|
|
|
buffer->tmp_wr_idx = buffer->tmp_rd_idx;
|
|
buffer->rd_idx = buffer->tmp_rd_idx;
|
|
buffer->flags |= MPSC_PBUF_FULL;
|
|
} else {
|
|
/* Prepare packet dropping. */
|
|
rd_idx_inc(buffer, rd_wlen);
|
|
buffer->tmp_rd_idx = buffer->rd_idx;
|
|
/* Temporary move tmp_wr idx forward to ensure that packet
|
|
* will not be dropped twice and content will not be
|
|
* overwritten.
|
|
*/
|
|
if (free_wlen) {
|
|
/* Free location mark as invalid to prevent
|
|
* reading incomplete data.
|
|
*/
|
|
union mpsc_pbuf_generic invalid = {
|
|
.hdr = {
|
|
.valid = 0,
|
|
.busy = 0
|
|
}
|
|
};
|
|
|
|
buffer->buf[buffer->tmp_wr_idx] = invalid.raw;
|
|
}
|
|
|
|
*tmp_wr_idx_shift = rd_wlen + free_wlen;
|
|
buffer->tmp_wr_idx = idx_inc(buffer, buffer->tmp_wr_idx, *tmp_wr_idx_shift);
|
|
buffer->flags |= MPSC_PBUF_FULL;
|
|
item->hdr.valid = 0;
|
|
*item_to_drop = item;
|
|
MPSC_PBUF_DBG(buffer, "no space: dropping packet %p (len: %d)",
|
|
item, rd_wlen);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void post_drop_action(struct mpsc_pbuf_buffer *buffer,
|
|
uint32_t prev_tmp_wr_idx,
|
|
uint32_t tmp_wr_idx_shift)
|
|
{
|
|
uint32_t cmp_tmp_wr_idx = idx_inc(buffer, prev_tmp_wr_idx, tmp_wr_idx_shift);
|
|
|
|
if (cmp_tmp_wr_idx == buffer->tmp_wr_idx) {
|
|
/* Operation not interrupted by another alloc. */
|
|
buffer->tmp_wr_idx = prev_tmp_wr_idx;
|
|
buffer->flags &= ~MPSC_PBUF_FULL;
|
|
return;
|
|
}
|
|
|
|
/* Operation interrupted, mark area as to be skipped. */
|
|
union mpsc_pbuf_generic skip = {
|
|
.skip = {
|
|
.valid = 0,
|
|
.busy = 1,
|
|
.len = tmp_wr_idx_shift
|
|
}
|
|
};
|
|
|
|
buffer->buf[prev_tmp_wr_idx] = skip.raw;
|
|
buffer->wr_idx = idx_inc(buffer,
|
|
buffer->wr_idx,
|
|
tmp_wr_idx_shift);
|
|
/* full flag? */
|
|
}
|
|
|
|
void mpsc_pbuf_put_word(struct mpsc_pbuf_buffer *buffer,
|
|
const union mpsc_pbuf_generic item)
|
|
{
|
|
bool cont;
|
|
uint32_t free_wlen;
|
|
k_spinlock_key_t key;
|
|
union mpsc_pbuf_generic *dropped_item = NULL;
|
|
uint32_t tmp_wr_idx_shift = 0;
|
|
uint32_t tmp_wr_idx_val = 0;
|
|
|
|
do {
|
|
key = k_spin_lock(&buffer->lock);
|
|
|
|
if (tmp_wr_idx_shift) {
|
|
post_drop_action(buffer, tmp_wr_idx_val, tmp_wr_idx_shift);
|
|
tmp_wr_idx_shift = 0;
|
|
}
|
|
|
|
(void)free_space(buffer, &free_wlen);
|
|
|
|
MPSC_PBUF_DBG(buffer, "put_word (%d free space)", (int)free_wlen);
|
|
|
|
if (free_wlen) {
|
|
buffer->buf[buffer->tmp_wr_idx] = item.raw;
|
|
tmp_wr_idx_inc(buffer, 1);
|
|
cont = false;
|
|
buffer->wr_idx = idx_inc(buffer, buffer->wr_idx, 1);
|
|
max_utilization_update(buffer);
|
|
} else {
|
|
tmp_wr_idx_val = buffer->tmp_wr_idx;
|
|
cont = drop_item_locked(buffer, free_wlen,
|
|
&dropped_item, &tmp_wr_idx_shift);
|
|
}
|
|
|
|
k_spin_unlock(&buffer->lock, key);
|
|
|
|
if (dropped_item) {
|
|
/* Notify about item being dropped. */
|
|
if (buffer->notify_drop) {
|
|
buffer->notify_drop(buffer, dropped_item);
|
|
}
|
|
dropped_item = NULL;
|
|
}
|
|
} while (cont);
|
|
}
|
|
|
|
union mpsc_pbuf_generic *mpsc_pbuf_alloc(struct mpsc_pbuf_buffer *buffer,
|
|
size_t wlen, k_timeout_t timeout)
|
|
{
|
|
union mpsc_pbuf_generic *item = NULL;
|
|
union mpsc_pbuf_generic *dropped_item = NULL;
|
|
bool cont = true;
|
|
uint32_t free_wlen;
|
|
uint32_t tmp_wr_idx_shift = 0;
|
|
uint32_t tmp_wr_idx_val = 0;
|
|
|
|
MPSC_PBUF_DBG(buffer, "alloc %d words", (int)wlen);
|
|
|
|
if (wlen > (buffer->size)) {
|
|
MPSC_PBUF_DBG(buffer, "Failed to alloc");
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
k_spinlock_key_t key;
|
|
bool wrap;
|
|
|
|
key = k_spin_lock(&buffer->lock);
|
|
if (tmp_wr_idx_shift) {
|
|
post_drop_action(buffer, tmp_wr_idx_val, tmp_wr_idx_shift);
|
|
tmp_wr_idx_shift = 0;
|
|
}
|
|
|
|
wrap = free_space(buffer, &free_wlen);
|
|
|
|
if (free_wlen >= wlen) {
|
|
item =
|
|
(union mpsc_pbuf_generic *)&buffer->buf[buffer->tmp_wr_idx];
|
|
item->hdr.valid = 0;
|
|
item->hdr.busy = 0;
|
|
tmp_wr_idx_inc(buffer, wlen);
|
|
cont = false;
|
|
} else if (wrap) {
|
|
add_skip_item(buffer, free_wlen);
|
|
cont = true;
|
|
} else if (IS_ENABLED(CONFIG_MULTITHREADING) && !K_TIMEOUT_EQ(timeout, K_NO_WAIT) &&
|
|
!k_is_in_isr() && arch_irq_unlocked(key.key)) {
|
|
int err;
|
|
|
|
k_spin_unlock(&buffer->lock, key);
|
|
err = k_sem_take(&buffer->sem, timeout);
|
|
key = k_spin_lock(&buffer->lock);
|
|
cont = (err == 0) ? true : false;
|
|
} else if (cont) {
|
|
tmp_wr_idx_val = buffer->tmp_wr_idx;
|
|
cont = drop_item_locked(buffer, free_wlen,
|
|
&dropped_item, &tmp_wr_idx_shift);
|
|
}
|
|
k_spin_unlock(&buffer->lock, key);
|
|
|
|
if (dropped_item) {
|
|
/* Notify about item being dropped. */
|
|
if (buffer->notify_drop) {
|
|
buffer->notify_drop(buffer, dropped_item);
|
|
}
|
|
dropped_item = NULL;
|
|
}
|
|
} while (cont);
|
|
|
|
|
|
MPSC_PBUF_DBG(buffer, "allocated %p", item);
|
|
|
|
if (IS_ENABLED(CONFIG_MPSC_CLEAR_ALLOCATED) && item) {
|
|
/* During test fill with 0's to simplify message comparison */
|
|
memset(item, 0, sizeof(int) * wlen);
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
void mpsc_pbuf_commit(struct mpsc_pbuf_buffer *buffer,
|
|
union mpsc_pbuf_generic *item)
|
|
{
|
|
uint32_t wlen = buffer->get_wlen(item);
|
|
|
|
k_spinlock_key_t key = k_spin_lock(&buffer->lock);
|
|
|
|
item->hdr.valid = 1;
|
|
buffer->wr_idx = idx_inc(buffer, buffer->wr_idx, wlen);
|
|
max_utilization_update(buffer);
|
|
k_spin_unlock(&buffer->lock, key);
|
|
MPSC_PBUF_DBG(buffer, "committed %p", item);
|
|
}
|
|
|
|
void mpsc_pbuf_put_word_ext(struct mpsc_pbuf_buffer *buffer,
|
|
const union mpsc_pbuf_generic item,
|
|
const void *data)
|
|
{
|
|
static const size_t l =
|
|
(sizeof(item) + sizeof(data)) / sizeof(uint32_t);
|
|
union mpsc_pbuf_generic *dropped_item = NULL;
|
|
bool cont;
|
|
uint32_t tmp_wr_idx_shift = 0;
|
|
uint32_t tmp_wr_idx_val = 0;
|
|
|
|
do {
|
|
k_spinlock_key_t key;
|
|
uint32_t free_wlen;
|
|
bool wrap;
|
|
|
|
key = k_spin_lock(&buffer->lock);
|
|
|
|
if (tmp_wr_idx_shift) {
|
|
post_drop_action(buffer, tmp_wr_idx_val, tmp_wr_idx_shift);
|
|
tmp_wr_idx_shift = 0;
|
|
}
|
|
|
|
wrap = free_space(buffer, &free_wlen);
|
|
|
|
if (free_wlen >= l) {
|
|
buffer->buf[buffer->tmp_wr_idx] = item.raw;
|
|
void **p =
|
|
(void **)&buffer->buf[buffer->tmp_wr_idx + 1];
|
|
|
|
*p = (void *)data;
|
|
tmp_wr_idx_inc(buffer, l);
|
|
buffer->wr_idx = idx_inc(buffer, buffer->wr_idx, l);
|
|
cont = false;
|
|
max_utilization_update(buffer);
|
|
} else if (wrap) {
|
|
add_skip_item(buffer, free_wlen);
|
|
cont = true;
|
|
} else {
|
|
tmp_wr_idx_val = buffer->tmp_wr_idx;
|
|
cont = drop_item_locked(buffer, free_wlen,
|
|
&dropped_item, &tmp_wr_idx_shift);
|
|
}
|
|
|
|
k_spin_unlock(&buffer->lock, key);
|
|
|
|
if (dropped_item) {
|
|
/* Notify about item being dropped. */
|
|
if (buffer->notify_drop) {
|
|
buffer->notify_drop(buffer, dropped_item);
|
|
}
|
|
dropped_item = NULL;
|
|
}
|
|
} while (cont);
|
|
}
|
|
|
|
void mpsc_pbuf_put_data(struct mpsc_pbuf_buffer *buffer, const uint32_t *data,
|
|
size_t wlen)
|
|
{
|
|
bool cont;
|
|
union mpsc_pbuf_generic *dropped_item = NULL;
|
|
uint32_t tmp_wr_idx_shift = 0;
|
|
uint32_t tmp_wr_idx_val = 0;
|
|
|
|
do {
|
|
uint32_t free_wlen;
|
|
k_spinlock_key_t key;
|
|
bool wrap;
|
|
|
|
key = k_spin_lock(&buffer->lock);
|
|
|
|
if (tmp_wr_idx_shift) {
|
|
post_drop_action(buffer, tmp_wr_idx_val, tmp_wr_idx_shift);
|
|
tmp_wr_idx_shift = 0;
|
|
}
|
|
|
|
wrap = free_space(buffer, &free_wlen);
|
|
|
|
if (free_wlen >= wlen) {
|
|
memcpy(&buffer->buf[buffer->tmp_wr_idx], data,
|
|
wlen * sizeof(uint32_t));
|
|
buffer->wr_idx = idx_inc(buffer, buffer->wr_idx, wlen);
|
|
tmp_wr_idx_inc(buffer, wlen);
|
|
cont = false;
|
|
max_utilization_update(buffer);
|
|
} else if (wrap) {
|
|
add_skip_item(buffer, free_wlen);
|
|
cont = true;
|
|
} else {
|
|
tmp_wr_idx_val = buffer->tmp_wr_idx;
|
|
cont = drop_item_locked(buffer, free_wlen,
|
|
&dropped_item, &tmp_wr_idx_shift);
|
|
}
|
|
|
|
k_spin_unlock(&buffer->lock, key);
|
|
|
|
if (dropped_item) {
|
|
/* Notify about item being dropped. */
|
|
dropped_item->hdr.valid = 0;
|
|
if (buffer->notify_drop) {
|
|
buffer->notify_drop(buffer, dropped_item);
|
|
}
|
|
dropped_item = NULL;
|
|
}
|
|
} while (cont);
|
|
}
|
|
|
|
const union mpsc_pbuf_generic *mpsc_pbuf_claim(struct mpsc_pbuf_buffer *buffer)
|
|
{
|
|
union mpsc_pbuf_generic *item;
|
|
bool cont;
|
|
|
|
do {
|
|
uint32_t a;
|
|
k_spinlock_key_t key;
|
|
|
|
cont = false;
|
|
key = k_spin_lock(&buffer->lock);
|
|
(void)available(buffer, &a);
|
|
item = (union mpsc_pbuf_generic *)
|
|
&buffer->buf[buffer->tmp_rd_idx];
|
|
|
|
if (!a || is_invalid(item)) {
|
|
MPSC_PBUF_DBG(buffer, "invalid claim %d: %p", a, item);
|
|
item = NULL;
|
|
} else {
|
|
uint32_t skip = get_skip(item);
|
|
|
|
if (skip || !is_valid(item)) {
|
|
uint32_t inc =
|
|
skip ? skip : buffer->get_wlen(item);
|
|
|
|
buffer->tmp_rd_idx =
|
|
idx_inc(buffer, buffer->tmp_rd_idx, inc);
|
|
rd_idx_inc(buffer, inc);
|
|
cont = true;
|
|
} else {
|
|
item->hdr.busy = 1;
|
|
buffer->tmp_rd_idx =
|
|
idx_inc(buffer, buffer->tmp_rd_idx,
|
|
buffer->get_wlen(item));
|
|
}
|
|
}
|
|
|
|
if (!cont) {
|
|
MPSC_PBUF_DBG(buffer, ">>claimed %d: %p", a, item);
|
|
}
|
|
k_spin_unlock(&buffer->lock, key);
|
|
} while (cont);
|
|
|
|
return item;
|
|
}
|
|
|
|
void mpsc_pbuf_free(struct mpsc_pbuf_buffer *buffer,
|
|
const union mpsc_pbuf_generic *item)
|
|
{
|
|
uint32_t wlen = buffer->get_wlen(item);
|
|
k_spinlock_key_t key = k_spin_lock(&buffer->lock);
|
|
union mpsc_pbuf_generic *witem = (union mpsc_pbuf_generic *)item;
|
|
|
|
witem->hdr.valid = 0;
|
|
if (!(buffer->flags & MPSC_PBUF_MODE_OVERWRITE) ||
|
|
((uint32_t *)item == &buffer->buf[buffer->rd_idx])) {
|
|
witem->hdr.busy = 0;
|
|
if (buffer->rd_idx == buffer->tmp_rd_idx) {
|
|
/* There is a chance that there are so many new packets
|
|
* added between claim and free that rd_idx points again
|
|
* at claimed item. In that case tmp_rd_idx points at
|
|
* the same location. In that case increment also tmp_rd_idx
|
|
* which will mark freed buffer as the only free space in
|
|
* the buffer.
|
|
*/
|
|
buffer->tmp_rd_idx = idx_inc(buffer, buffer->tmp_rd_idx, wlen);
|
|
}
|
|
rd_idx_inc(buffer, wlen);
|
|
} else {
|
|
MPSC_PBUF_DBG(buffer, "Allocation occurred during claim");
|
|
witem->skip.len = wlen;
|
|
}
|
|
MPSC_PBUF_DBG(buffer, "<<freed: %p", item);
|
|
|
|
k_spin_unlock(&buffer->lock, key);
|
|
if (IS_ENABLED(CONFIG_MULTITHREADING)) {
|
|
k_sem_give(&buffer->sem);
|
|
}
|
|
}
|
|
|
|
bool mpsc_pbuf_is_pending(struct mpsc_pbuf_buffer *buffer)
|
|
{
|
|
uint32_t a;
|
|
k_spinlock_key_t key = k_spin_lock(&buffer->lock);
|
|
|
|
(void)available(buffer, &a);
|
|
k_spin_unlock(&buffer->lock, key);
|
|
|
|
return a ? true : false;
|
|
}
|
|
|
|
void mpsc_pbuf_get_utilization(struct mpsc_pbuf_buffer *buffer,
|
|
uint32_t *size, uint32_t *now)
|
|
{
|
|
/* One byte is left for full/empty distinction. */
|
|
*size = (buffer->size - 1) * sizeof(int);
|
|
*now = get_usage(buffer) * sizeof(int);
|
|
}
|
|
|
|
int mpsc_pbuf_get_max_utilization(struct mpsc_pbuf_buffer *buffer, uint32_t *max)
|
|
{
|
|
|
|
if (!(buffer->flags & MPSC_PBUF_MAX_UTILIZATION)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
*max = buffer->max_usage * sizeof(int);
|
|
return 0;
|
|
}
|