blob: 4914a61a3e51824acdeeb70e6b6094f1d0ea73b1 [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Test log message
*/
#include <zephyr/sys/mpsc_pbuf.h>
#include <zephyr/tc_util.h>
#include <stdbool.h>
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#include <zephyr/random/rand32.h>
#define PUT_EXT_LEN \
((sizeof(union mpsc_pbuf_generic) + sizeof(void *)) / sizeof(uint32_t))
#define LEN_BITS 9
struct test_data {
MPSC_PBUF_HDR;
uint32_t len : LEN_BITS;
uint32_t data : 32 - MPSC_PBUF_HDR_BITS - LEN_BITS;
};
struct test_data_ext {
struct test_data hdr;
void *data;
} __packed;
struct test_data_var {
struct test_data hdr;
uint32_t data[];
};
union test_item {
struct test_data data;
struct test_data_ext data_ext;
union mpsc_pbuf_generic item;
};
static uint32_t get_wlen(const union mpsc_pbuf_generic *item)
{
union test_item *t_item = (union test_item *)item;
return t_item->data.len;
}
static uint32_t drop_cnt;
static uintptr_t exp_dropped_data[10];
static uint32_t exp_dropped_len[10];
static void drop(const struct mpsc_pbuf_buffer *buffer, const union mpsc_pbuf_generic *item)
{
struct test_data_var *packet = (struct test_data_var *)item;
zassert_equal(packet->hdr.data, exp_dropped_data[drop_cnt]);
zassert_equal(packet->hdr.len, exp_dropped_len[drop_cnt]);
for (int i = 0; i < exp_dropped_len[drop_cnt] - 1; i++) {
int err = memcmp(packet->data, &exp_dropped_data[drop_cnt],
sizeof(uint32_t));
zassert_equal(err, 0);
}
drop_cnt++;
}
static uint32_t buf32[512];
static struct mpsc_pbuf_buffer_config cfg = {
.buf = buf32,
.size = ARRAY_SIZE(buf32),
.notify_drop = drop,
.get_wlen = get_wlen
};
static void init(struct mpsc_pbuf_buffer *buffer, bool overwrite, bool pow2)
{
drop_cnt = 0;
cfg.flags = overwrite ? MPSC_PBUF_MODE_OVERWRITE : 0;
cfg.size = ARRAY_SIZE(buf32) - (pow2 ? 0 : 1);
mpsc_pbuf_init(buffer, &cfg);
#if CONFIG_SOC_SERIES_NRF52X
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0;
#endif
}
static inline uint32_t get_cyc(void)
{
#if CONFIG_SOC_SERIES_NRF52X
return DWT->CYCCNT;
#else
return k_cycle_get_32();
#endif
}
void item_put_no_overwrite(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
int repeat = buffer.size*2;
union test_item test_1word = {.data = {.valid = 1, .len = 1 }};
for (int i = 0; i < repeat; i++) {
union test_item *t;
test_1word.data.data = i;
mpsc_pbuf_put_word(&buffer, test_1word.item);
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
zassert_is_null(mpsc_pbuf_claim(&buffer));
}
ZTEST(log_buffer, test_item_put_no_overwrite)
{
item_put_no_overwrite(true);
item_put_no_overwrite(false);
}
void item_put_overwrite(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, true, pow2);
union test_item test_1word = {.data = {.valid = 1, .len = 1 }};
exp_dropped_data[0] = 0;
exp_dropped_len[0] = 1;
for (int i = 0; i < buffer.size; i++) {
test_1word.data.data = i;
mpsc_pbuf_put_word(&buffer, test_1word.item);
}
zassert_equal(drop_cnt, 1,
"Unexpected number of dropped messages: %d", drop_cnt);
}
ZTEST(log_buffer, test_item_put_overwrite)
{
item_put_overwrite(true);
item_put_overwrite(false);
}
void item_put_saturate(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
int repeat = buffer.size;
union test_item test_1word = {.data = {.valid = 1, .len = 1 }};
union test_item *t;
zassert_false(mpsc_pbuf_is_pending(&buffer));
for (int i = 0; i < repeat/2; i++) {
test_1word.data.data = i;
mpsc_pbuf_put_word(&buffer, test_1word.item);
zassert_true(mpsc_pbuf_is_pending(&buffer));
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
for (int i = 0; i < repeat; i++) {
test_1word.data.data = i;
mpsc_pbuf_put_word(&buffer, test_1word.item);
}
for (int i = 0; i < (repeat-1); i++) {
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
zassert_is_null(mpsc_pbuf_claim(&buffer));
}
ZTEST(log_buffer, test_item_put_saturate)
{
item_put_saturate(true);
item_put_saturate(false);
}
void benchmark_item_put(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, true, pow2);
int repeat = buffer.size - 1;
union test_item test_1word = {.data = {.valid = 1, .len = 1 }};
uint32_t t = get_cyc();
for (int i = 0; i < repeat; i++) {
test_1word.data.data = i;
mpsc_pbuf_put_word(&buffer, test_1word.item);
}
t = get_cyc() - t;
PRINT("%s buffer\n", pow2 ? "pow2" : "non-pow2");
PRINT("single word put time: %d cycles\n", t/repeat);
t = get_cyc();
for (int i = 0; i < repeat; i++) {
union test_item *t;
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
t = get_cyc() - t;
PRINT("single word item claim,free: %d cycles\n", t/repeat);
zassert_is_null(mpsc_pbuf_claim(&buffer));
}
ZTEST(log_buffer, test_benchmark_item_put)
{
benchmark_item_put(true);
benchmark_item_put(false);
}
void item_put_ext_no_overwrite(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
int repeat = buffer.size * 2;
union test_item test_ext_item = {
.data = {
.valid = 1,
.len = PUT_EXT_LEN
}
};
void *data;
for (uintptr_t i = 0; i < repeat; i++) {
union test_item *t;
data = (void *)i;
test_ext_item.data.data = i;
mpsc_pbuf_put_word_ext(&buffer, test_ext_item.item, data);
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data_ext.hdr.data, i);
zassert_equal(t->data_ext.data, (void *)i);
mpsc_pbuf_free(&buffer, &t->item);
}
zassert_is_null(mpsc_pbuf_claim(&buffer));
}
ZTEST(log_buffer, test_item_put_ext_no_overwrite)
{
item_put_ext_no_overwrite(true);
item_put_ext_no_overwrite(false);
}
void item_put_word_ext_overwrite(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, true, pow2);
size_t w = (sizeof(uint32_t) + sizeof(void *)) / sizeof(uint32_t);
int repeat = 1 + (buffer.size - 1) / w;
union test_item test_ext_item = {
.data = {
.valid = 1,
.len = PUT_EXT_LEN
}
};
exp_dropped_data[0] = 0;
exp_dropped_len[0] = w;
exp_dropped_data[1] = 1;
exp_dropped_len[1] = w;
for (uintptr_t i = 0; i < repeat; i++) {
test_ext_item.data.data = i;
mpsc_pbuf_put_word_ext(&buffer, test_ext_item.item, (void *)i);
}
uint32_t exp_drop_cnt = (sizeof(void *) == sizeof(uint32_t)) ?
(pow2 ? 1 : 2) : 2;
zassert_equal(drop_cnt, exp_drop_cnt,
"Unexpected number of dropped messages: %d", drop_cnt);
}
ZTEST(log_buffer, test_item_put_word_ext_overwrite)
{
item_put_word_ext_overwrite(true);
item_put_word_ext_overwrite(false);
}
void item_put_ext_saturate(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
int repeat = buffer.size / PUT_EXT_LEN;
union test_item test_ext_item = {
.data = {
.valid = 1,
.len = PUT_EXT_LEN
}
};
void *data;
union test_item *t;
for (uintptr_t i = 0; i < repeat/2; i++) {
test_ext_item.data.data = i;
data = (void *)i;
mpsc_pbuf_put_word_ext(&buffer, test_ext_item.item, data);
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
for (uintptr_t i = 0; i < repeat; i++) {
test_ext_item.data.data = i;
data = (void *)i;
mpsc_pbuf_put_word_ext(&buffer, test_ext_item.item, data);
}
for (uintptr_t i = 0; i < (repeat-1); i++) {
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data_ext.data, (void *)i);
zassert_equal(t->data_ext.hdr.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
zassert_is_null(mpsc_pbuf_claim(&buffer));
}
ZTEST(log_buffer, test_item_put_ext_saturate)
{
item_put_ext_saturate(true);
item_put_ext_saturate(false);
}
void benchmark_item_put_ext(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
int repeat = (buffer.size - 1) / PUT_EXT_LEN;
union test_item test_ext_item = {
.data = {
.valid = 1,
.len = PUT_EXT_LEN
}
};
void *data = NULL;
uint32_t t = get_cyc();
for (int i = 0; i < repeat; i++) {
test_ext_item.data.data = i;
mpsc_pbuf_put_word_ext(&buffer, test_ext_item.item, data);
}
t = get_cyc() - t;
PRINT("%spow2 buffer\n", pow2 ? "" : "non-");
PRINT("put_ext time: %d cycles\n", t/repeat);
t = get_cyc();
for (int i = 0; i < repeat; i++) {
union test_item *t;
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
t = get_cyc() - t;
PRINT("ext item claim,free: %d cycles\n", t/repeat);
zassert_is_null(mpsc_pbuf_claim(&buffer));
}
ZTEST(log_buffer, test_benchmark_item_put_ext)
{
benchmark_item_put_ext(true);
benchmark_item_put_ext(false);
}
void benchmark_item_put_data(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
int repeat = (buffer.size - 1) / PUT_EXT_LEN;
union test_item test_ext_item = {
.data_ext = {
.hdr = {
.valid = 1,
.len = PUT_EXT_LEN
},
.data = NULL
}
};
uint32_t t = get_cyc();
for (uintptr_t i = 0; i < repeat; i++) {
test_ext_item.data_ext.hdr.data = i;
test_ext_item.data_ext.data = (void *)i;
mpsc_pbuf_put_data(&buffer, (uint32_t *)&test_ext_item,
PUT_EXT_LEN);
}
t = get_cyc() - t;
PRINT("%spow2 buffer\n", pow2 ? "" : "non-");
PRINT("put_ext time: %d cycles\n", t/repeat);
t = get_cyc();
for (int i = 0; i < repeat; i++) {
union test_item *t;
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data.data, i);
mpsc_pbuf_free(&buffer, &t->item);
}
t = get_cyc() - t;
PRINT("ext item claim,free: %d cycles\n", t/repeat);
zassert_is_null(mpsc_pbuf_claim(&buffer));
}
ZTEST(log_buffer, test_benchmark_item_put_data)
{
benchmark_item_put_data(true);
benchmark_item_put_data(false);
}
void item_put_data_overwrite(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, true, pow2);
size_t w = (sizeof(uint32_t) + sizeof(void *)) / sizeof(uint32_t);
int repeat = 1 + (buffer.size - 1) / w;
static const int len = sizeof(struct test_data_ext) / sizeof(uint32_t);
struct test_data_ext item = {
.hdr = {
.valid = 1,
.len = len
}
};
exp_dropped_data[0] = 0;
exp_dropped_len[0] = w;
exp_dropped_data[1] = 1;
exp_dropped_len[1] = w;
for (uintptr_t i = 0; i < repeat; i++) {
void *vitem;
item.data = (void *)i;
item.hdr.data = i;
vitem = (uint32_t *)&item;
zassert_true(IS_PTR_ALIGNED(vitem, uint32_t), "unaligned ptr");
mpsc_pbuf_put_data(&buffer, (uint32_t *)vitem, len);
}
uint32_t exp_drop_cnt = (sizeof(void *) == sizeof(uint32_t)) ?
(pow2 ? 1 : 2) : 2;
zassert_equal(drop_cnt, exp_drop_cnt,
"Unexpected number of dropped messages: %d", drop_cnt);
}
ZTEST(log_buffer, test_put_data_overwrite)
{
item_put_data_overwrite(true);
item_put_data_overwrite(false);
}
void item_alloc_commit(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
struct test_data_var *packet;
uint32_t len = 5;
int repeat = 1024;
for (int i = 0; i < repeat; i++) {
packet = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len,
K_NO_WAIT);
packet->hdr.len = len;
for (int j = 0; j < len - 1; j++) {
packet->data[j] = i + j;
}
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)packet);
packet = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(packet);
zassert_equal(packet->hdr.len, len);
for (int j = 0; j < len - 1; j++) {
zassert_equal(packet->data[j], i + j);
}
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)packet);
}
}
ZTEST(log_buffer, test_item_alloc_commit)
{
item_alloc_commit(true);
item_alloc_commit(false);
}
void item_max_alloc(bool overwrite)
{
struct mpsc_pbuf_buffer buffer;
struct test_data_var *packet;
init(&buffer, overwrite, true);
/* First try to allocate the biggest possible packet. */
for (int i = 0; i < 2; i++) {
packet = (struct test_data_var *)mpsc_pbuf_alloc(&buffer,
buffer.size - 1,
K_NO_WAIT);
zassert_true(packet != NULL);
packet->hdr.len = buffer.size - 1;
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)packet);
packet = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)packet);
}
/* Too big packet cannot be allocated. */
packet = (struct test_data_var *)mpsc_pbuf_alloc(&buffer,
buffer.size,
K_NO_WAIT);
zassert_true(packet == NULL);
}
ZTEST(log_buffer, test_item_max_alloc)
{
item_max_alloc(true);
item_max_alloc(false);
}
static uint32_t saturate_buffer_uneven(struct mpsc_pbuf_buffer *buffer,
uint32_t len)
{
struct test_data_var *packet;
uint32_t uneven = 5;
uint32_t cnt = 0;
int repeat =
uneven - 1 + ((buffer->size - (uneven * len)) / len);
/* Put some data to include wrapping */
for (int i = 0; i < uneven; i++) {
packet = (struct test_data_var *)mpsc_pbuf_alloc(buffer, len,
K_NO_WAIT);
packet->hdr.len = len;
mpsc_pbuf_commit(buffer, (union mpsc_pbuf_generic *)packet);
packet = (struct test_data_var *)mpsc_pbuf_claim(buffer);
zassert_true(packet);
mpsc_pbuf_free(buffer, (union mpsc_pbuf_generic *)packet);
}
for (int i = 0; i < repeat; i++) {
packet = (struct test_data_var *)mpsc_pbuf_alloc(buffer, len,
K_NO_WAIT);
zassert_true(packet);
packet->hdr.len = len;
packet->hdr.data = i;
for (int j = 0; j < len - 1; j++) {
packet->data[j] = i + j;
}
mpsc_pbuf_commit(buffer, (union mpsc_pbuf_generic *)packet);
cnt++;
}
return cnt;
}
void item_alloc_commit_saturate(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
saturate_buffer_uneven(&buffer, 5);
struct test_data_var *packet;
uint32_t len = 5;
packet = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len,
K_NO_WAIT);
zassert_is_null(packet);
/* Get one packet from the buffer. */
packet = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(packet);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)packet);
/* and try to allocate one more time, this time with success. */
packet = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len,
K_NO_WAIT);
zassert_true(packet);
}
ZTEST(log_buffer, test_item_alloc_commit_saturate)
{
item_alloc_commit_saturate(true);
item_alloc_commit_saturate(false);
}
void item_alloc_preemption(bool pow2)
{
struct mpsc_pbuf_buffer buffer;
init(&buffer, false, pow2);
struct test_data_var *p0;
struct test_data_var *p1;
struct test_data_var *p;
p0 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, 10, K_NO_WAIT);
zassert_true(p0);
p0->hdr.len = 10;
/* Check that no packet is yet available */
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_is_null(p);
p1 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, 20, K_NO_WAIT);
zassert_true(p1);
p1->hdr.len = 20;
/* Commit p1, p0 is still not committed, there should be no packets
* available for reading.
*/
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)p1);
/* Check that no packet is yet available */
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_is_null(p);
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)p0);
/* Validate that p0 is the first one. */
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p);
zassert_equal(p->hdr.len, 10);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
/* Validate that p1 is the next one. */
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p);
zassert_equal(p->hdr.len, 20);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
/* No more packets. */
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_is_null(p);
}
ZTEST(log_buffer, test_item_alloc_preemption)
{
item_alloc_preemption(true);
item_alloc_preemption(false);
}
void overwrite(bool pow2)
{
struct test_data_var *p;
uint32_t fill_len = 5;
uint32_t len0, len1;
struct mpsc_pbuf_buffer buffer;
init(&buffer, true, pow2);
uint32_t packet_cnt = saturate_buffer_uneven(&buffer, fill_len);
exp_dropped_data[0] = 0;
exp_dropped_len[0] = fill_len;
len0 = 6;
p = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len0, K_NO_WAIT);
p->hdr.len = len0;
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)p);
zassert_equal(drop_cnt, 1);
/* Request allocation which will require dropping 2 packets. */
len1 = 9;
exp_dropped_data[1] = 1;
exp_dropped_len[1] = fill_len;
exp_dropped_data[2] = 2;
exp_dropped_len[2] = fill_len;
p = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len1, K_NO_WAIT);
p->hdr.len = len1;
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)p);
zassert_equal(drop_cnt, 3);
for (int i = 0; i < (packet_cnt - drop_cnt); i++) {
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p);
zassert_equal(p->hdr.len, fill_len);
zassert_equal(p->hdr.data, i + drop_cnt);
for (int j = 0; j < fill_len - 1; j++) {
zassert_equal(p->data[j], p->hdr.data + j);
}
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
}
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p);
zassert_equal(p->hdr.len, len0);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p);
zassert_equal(p->hdr.len, len1);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_is_null(p);
}
ZTEST(log_buffer, test_overwrite)
{
overwrite(true);
overwrite(false);
}
void overwrite_while_claimed(bool pow2)
{
struct test_data_var *p0;
struct test_data_var *p1;
struct mpsc_pbuf_buffer buffer;
init(&buffer, true, pow2);
uint32_t fill_len = 5;
uint32_t len = 6;
uint32_t packet_cnt = saturate_buffer_uneven(&buffer, fill_len);
/* Start by claiming a packet. Buffer is now full. Allocation shall
* skip claimed packed and drop the next one.
*/
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p0);
zassert_equal(p0->hdr.len, fill_len);
exp_dropped_data[0] = p0->hdr.data + 1; /* next packet is dropped */
exp_dropped_len[0] = fill_len;
exp_dropped_data[1] = p0->hdr.data + 2; /* next packet is dropped */
exp_dropped_len[1] = fill_len;
p1 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, 6, K_NO_WAIT);
zassert_equal(drop_cnt, 2);
p1->hdr.len = len;
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)p1);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p0);
for (int i = 0; i < packet_cnt - drop_cnt - 1; i++) {
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p0);
zassert_equal(p0->hdr.len, fill_len);
zassert_equal(p0->hdr.data, i + drop_cnt + 1);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p0);
}
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p0);
zassert_equal(p0->hdr.len, len);
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_is_null(p0);
}
ZTEST(log_buffer, test_overwrite_while_claimed)
{
overwrite_while_claimed(true);
overwrite_while_claimed(false);
}
void overwrite_while_claimed2(bool pow2)
{
struct test_data_var *p0;
struct test_data_var *p1;
struct mpsc_pbuf_buffer buffer;
init(&buffer, true, pow2);
uint32_t fill_len = 1;
uint32_t len = 3;
uint32_t packet_cnt = saturate_buffer_uneven(&buffer, fill_len);
/* Start by claiming a packet. Buffer is now full. Allocation shall
* skip claimed packed and drop the next one.
*/
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p0);
zassert_equal(p0->hdr.len, fill_len);
exp_dropped_data[0] = p0->hdr.data + 1; /* next packet is dropped */
exp_dropped_len[0] = fill_len;
exp_dropped_data[1] = p0->hdr.data + 2; /* next packet is dropped */
exp_dropped_len[1] = fill_len;
exp_dropped_data[2] = p0->hdr.data + 3; /* next packet is dropped */
exp_dropped_len[2] = fill_len;
exp_dropped_data[3] = p0->hdr.data + 4; /* next packet is dropped */
exp_dropped_len[3] = fill_len;
p1 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len, K_NO_WAIT);
zassert_equal(drop_cnt, 4);
p1->hdr.len = len;
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)p1);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p0);
for (int i = 0; i < packet_cnt - drop_cnt - 1; i++) {
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p0);
zassert_equal(p0->hdr.len, fill_len);
zassert_equal(p0->hdr.data, i + drop_cnt + 1);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p0);
}
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_true(p0);
zassert_equal(p0->hdr.len, len);
p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
zassert_is_null(p0);
}
ZTEST(log_buffer, test_overwrite_while_claimed2)
{
overwrite_while_claimed2(true);
overwrite_while_claimed2(false);
}
static uintptr_t current_rd_idx;
static void validate_packet(struct test_data_var *packet)
{
zassert_equal((uintptr_t)packet->hdr.data, current_rd_idx,
"Got %d, expected: %d",
(uintptr_t)packet->hdr.data, current_rd_idx);
current_rd_idx++;
}
static void consistent_drop(const struct mpsc_pbuf_buffer *buffer,
const union mpsc_pbuf_generic *item)
{
validate_packet((struct test_data_var *)item);
}
uint32_t rand_get(uint32_t min, uint32_t max)
{
return min + (sys_rand32_get() % max);
}
ZTEST(log_buffer, test_overwrite_consistency)
{
struct mpsc_pbuf_buffer buffer;
static struct mpsc_pbuf_buffer_config cfg = {
.buf = buf32,
.size = ARRAY_SIZE(buf32),
.notify_drop = consistent_drop,
.get_wlen = get_wlen,
.flags = MPSC_PBUF_MODE_OVERWRITE
};
mpsc_pbuf_init(&buffer, &cfg);
int repeat = 50000;
int id = 0;
while (id < repeat) {
struct test_data_var *t = NULL;
bool alloc_during_claim = (rand_get(1, 5) <= 2);
/* Occasionally claim buffer to simulate that claiming is
* interrupted by allocation.
*/
if (alloc_during_claim) {
t = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
if (t) {
validate_packet(t);
}
}
uint32_t wr_cnt = rand_get(1, 200);
for (int i = 0; i < wr_cnt; i++) {
uint32_t wlen = rand_get(1, 15);
struct test_data_var *t;
t = (struct test_data_var *)mpsc_pbuf_alloc(&buffer,
wlen,
K_NO_WAIT);
t->hdr.len = wlen;
t->hdr.data = id++;
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)t);
}
/* Put back item claimed before committing new items. */
if (t) {
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)t);
}
uint32_t rd_cnt = rand_get(1, 30);
for (int i = 0; i < rd_cnt; i++) {
struct test_data_var *t;
t = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
if (!t) {
continue;
}
validate_packet(t);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)t);
}
}
}
K_THREAD_STACK_DEFINE(t1_stack, 1024);
K_THREAD_STACK_DEFINE(t2_stack, 1024);
static k_thread_stack_t *stacks[2] = {t1_stack, t2_stack};
static struct k_thread threads[2];
static k_tid_t tids[2];
void t_entry(void *p0, void *p1, void *p2)
{
struct mpsc_pbuf_buffer *buffer = p0;
uintptr_t wait_ms = (uintptr_t)p1;
struct test_data_ext *t;
void *vt;
t = (struct test_data_ext *)mpsc_pbuf_alloc(buffer,
sizeof(*t) / sizeof(uint32_t),
K_MSEC(1));
zassert_is_null(t);
t = (struct test_data_ext *)mpsc_pbuf_alloc(buffer,
sizeof(*t) / sizeof(uint32_t),
K_MSEC(wait_ms));
t->hdr.len = PUT_EXT_LEN;
t->data = k_current_get();
vt = t;
zassert_true(IS_PTR_ALIGNED(vt, union mpsc_pbuf_generic), "unaligned ptr");
mpsc_pbuf_commit(buffer, (union mpsc_pbuf_generic *)vt);
while (1) {
k_sleep(K_MSEC(10));
}
}
void start_threads(struct mpsc_pbuf_buffer *buffer)
{
int prio = 2;
uintptr_t wait_ms = 1000;
for (int i = 0; i < ARRAY_SIZE(threads); i++) {
tids[i] = k_thread_create(&threads[i], stacks[i], 1024, t_entry,
buffer, (void *)wait_ms, NULL,
prio--,
0, K_NO_WAIT);
}
k_sleep(K_MSEC(10));
for (int i = 0; i < ARRAY_SIZE(threads); i++) {
k_ticks_t t = k_thread_timeout_remaining_ticks(tids[i]);
k_ticks_t exp_wait = k_ms_to_ticks_ceil32(wait_ms);
/* Threads shall be blocked, waiting for available space. */
zassert_within(t, exp_wait, k_ms_to_ticks_ceil32(2));
}
}
/* Test creates two threads which pends on the buffer until there is a space
* available. When enough buffers is released threads are woken up and they
* allocate packets.
*/
ZTEST(log_buffer, test_pending_alloc)
{
int prio = k_thread_priority_get(k_current_get());
struct mpsc_pbuf_buffer buffer;
void *vt;
k_thread_priority_set(k_current_get(), 3);
init(&buffer, true, false);
uint32_t fill_len = 1;
uint32_t packet_cnt = saturate_buffer_uneven(&buffer, fill_len);
start_threads(&buffer);
k_sleep(K_MSEC(1));
for (int i = 0; i < packet_cnt; i++) {
union test_item *t = (union test_item *)mpsc_pbuf_claim(&buffer);
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)t);
}
for (int i = 0; i < 2; i++) {
struct test_data_ext *t =
(struct test_data_ext *)mpsc_pbuf_claim(&buffer);
zassert_true(t);
zassert_equal(t->data, tids[ARRAY_SIZE(tids) - 1 - i]);
vt = t;
zassert_true(IS_PTR_ALIGNED(vt, union mpsc_pbuf_generic), "unaligned ptr");
mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)vt);
}
zassert_equal(mpsc_pbuf_claim(&buffer), NULL, "No more packets.");
k_thread_priority_set(k_current_get(), prio);
}
static void check_usage(struct mpsc_pbuf_buffer *buffer,
uint32_t now, int exp_err, uint32_t max, uint32_t line)
{
uint32_t size;
uint32_t usage;
int err;
mpsc_pbuf_get_utilization(buffer, &size, &usage);
zassert_equal(size / sizeof(int), buffer->size - 1, "%d: got:%d, exp:%d",
line, size / sizeof(int), buffer->size - 1);
zassert_equal(usage, now, "%d: got:%d, exp:%d", line, usage, now);
err = mpsc_pbuf_get_max_utilization(buffer, &usage);
zassert_equal(err, exp_err);
if (err == 0) {
zassert_equal(usage, max, "%d: got:%d, exp:%d", line, usage, max);
}
}
#define CHECK_USAGE(buffer, now, max) \
check_usage(buffer, (now) * sizeof(int), 0, (max) * sizeof(int), __LINE__)
static void ignore_drop(const struct mpsc_pbuf_buffer *buffer,
const union mpsc_pbuf_generic *item)
{
ARG_UNUSED(buffer);
ARG_UNUSED(item);
}
ZTEST(log_buffer, test_utilization)
{
struct mpsc_pbuf_buffer buffer;
struct mpsc_pbuf_buffer_config config = {
.buf = buf32,
.size = ARRAY_SIZE(buf32),
.notify_drop = ignore_drop,
.get_wlen = get_wlen,
.flags = 0 /* Utilization not supported. */
};
mpsc_pbuf_init(&buffer, &config);
check_usage(&buffer, 0, -ENOTSUP, 0, __LINE__);
/* Initialize with max utilization support. */
config.flags = MPSC_PBUF_MAX_UTILIZATION;
mpsc_pbuf_init(&buffer, &config);
CHECK_USAGE(&buffer, 0, 0);
union test_item test_1word = {.data = {.valid = 1, .len = 1 }};
union test_item test_ext_item = {
.data = {
.valid = 1,
.len = PUT_EXT_LEN
}
};
union test_item *t;
mpsc_pbuf_put_word(&buffer, test_1word.item);
CHECK_USAGE(&buffer, 1, 1);
mpsc_pbuf_put_word_ext(&buffer, test_ext_item.item, NULL);
CHECK_USAGE(&buffer, 1 + PUT_EXT_LEN, 1 + PUT_EXT_LEN);
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t != NULL);
CHECK_USAGE(&buffer, 1 + PUT_EXT_LEN, 1 + PUT_EXT_LEN);
mpsc_pbuf_free(&buffer, &t->item);
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t != NULL);
CHECK_USAGE(&buffer, PUT_EXT_LEN, 1 + PUT_EXT_LEN);
mpsc_pbuf_free(&buffer, &t->item);
CHECK_USAGE(&buffer, 0, 1 + PUT_EXT_LEN);
union test_item test_ext_item2 = {
.data_ext = {
.hdr = {
.valid = 1,
.len = PUT_EXT_LEN
},
.data = NULL
}
};
mpsc_pbuf_put_data(&buffer, (uint32_t *)&test_ext_item2, PUT_EXT_LEN);
CHECK_USAGE(&buffer, PUT_EXT_LEN, 1 + PUT_EXT_LEN);
t = (union test_item *)mpsc_pbuf_claim(&buffer);
zassert_true(t != NULL);
mpsc_pbuf_free(&buffer, &t->item);
CHECK_USAGE(&buffer, 0, 1 + PUT_EXT_LEN);
memset(&buffer, 0, sizeof(buffer));
/* Initialize to reset indexes. */
mpsc_pbuf_init(&buffer, &config);
struct test_data_var *packet;
uint32_t len = 5;
uint32_t i;
for (i = 0; i < (buffer.size - 1) / len; i++) {
packet = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len, K_NO_WAIT);
packet->hdr.len = len;
mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)packet);
CHECK_USAGE(&buffer, len * (i + 1), len * (i + 1));
}
packet = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len, K_NO_WAIT);
zassert_true(packet == NULL);
}
/*test case main entry*/
ZTEST_SUITE(log_buffer, NULL, NULL, NULL, NULL, NULL);