tests: lib: Add test for mpsc_pbuf
Added suite for multiple producer, single consumer packet buffer.
Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
diff --git a/include/sys/mpsc_pbuf.h b/include/sys/mpsc_pbuf.h
index 11d9b0e..931dc7a 100644
--- a/include/sys/mpsc_pbuf.h
+++ b/include/sys/mpsc_pbuf.h
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
@@ -28,8 +27,8 @@
* Multi producer, single consumer packet buffer allows to allocate variable
* length consecutive space for storing a packet. When space is allocated
* it can be filled by the user (except for the first 2 bits) and when packet
- * is ready it is commited. It is allowed to allocate another packet before
- * commiting the previous one.
+ * is ready it is committed. It is allowed to allocate another packet before
+ * committing the previous one.
*
* If buffer is full and packet cannot be allocated then null is returned unless
* overwrite mode is selected. In that mode, oldest entry are dropped (user is
@@ -43,7 +42,8 @@
*/
/**@defgroup MPSC_PBUF_FLAGS MPSC packet buffer flags
- * @{ */
+ * @{
+ */
/** @brief Flag indicating that buffer size is power of 2.
*
diff --git a/lib/os/mpsc_pbuf.c b/lib/os/mpsc_pbuf.c
index 4673aa8..0d23aac 100644
--- a/lib/os/mpsc_pbuf.c
+++ b/lib/os/mpsc_pbuf.c
@@ -434,4 +434,3 @@
return a ? true : false;
}
-
diff --git a/tests/lib/mpsc_pbuf/CMakeLists.txt b/tests/lib/mpsc_pbuf/CMakeLists.txt
new file mode 100644
index 0000000..2c217cb
--- /dev/null
+++ b/tests/lib/mpsc_pbuf/CMakeLists.txt
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.13.1)
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(mpsc_pbuf)
+
+FILE(GLOB app_sources src/*.c)
+target_sources(app PRIVATE ${app_sources})
diff --git a/tests/lib/mpsc_pbuf/prj.conf b/tests/lib/mpsc_pbuf/prj.conf
new file mode 100644
index 0000000..0c4c1dc
--- /dev/null
+++ b/tests/lib/mpsc_pbuf/prj.conf
@@ -0,0 +1,3 @@
+CONFIG_ZTEST=y
+CONFIG_MPSC_PBUF=y
+CONFIG_TEST_RANDOM_GENERATOR=y
diff --git a/tests/lib/mpsc_pbuf/src/main.c b/tests/lib/mpsc_pbuf/src/main.c
new file mode 100644
index 0000000..9fc68be
--- /dev/null
+++ b/tests/lib/mpsc_pbuf/src/main.c
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (c) 2021 Nordic Semiconductor
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief Test log message
+ */
+
+#include <sys/mpsc_pbuf.h>
+
+#include <tc_util.h>
+#include <stdbool.h>
+#include <zephyr.h>
+#include <ztest.h>
+#include <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(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(struct mpsc_pbuf_buffer *buffer, 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], NULL);
+ zassert_equal(packet->hdr.len, exp_dropped_len[drop_cnt], NULL);
+ 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, NULL);
+ }
+
+ 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, NULL);
+ zassert_equal(t->data.data, i, NULL);
+ mpsc_pbuf_free(&buffer, &t->item);
+
+ }
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, NULL);
+}
+
+void test_item_put_no_overwrite(void)
+{
+ 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);
+}
+
+void test_item_put_overwrite(void)
+{
+ 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), NULL);
+
+ 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), NULL);
+
+ t = (union test_item *)mpsc_pbuf_claim(&buffer);
+ zassert_true(t, NULL);
+ zassert_equal(t->data.data, i, NULL);
+ 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, NULL);
+ zassert_equal(t->data.data, i, NULL);
+ mpsc_pbuf_free(&buffer, &t->item);
+ }
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, NULL);
+}
+
+void test_item_put_saturate(void)
+{
+ 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, NULL);
+ zassert_equal(t->data.data, i, NULL);
+ mpsc_pbuf_free(&buffer, &t->item);
+ }
+
+ t = get_cyc() - t;
+ PRINT("single word item claim,free: %d cycles\n", t/repeat);
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, NULL);
+}
+
+void test_benchmark_item_put(void)
+{
+ 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, NULL);
+ zassert_equal(t->data_ext.hdr.data, i, NULL);
+ zassert_equal(t->data_ext.data, (void *)i, NULL);
+ mpsc_pbuf_free(&buffer, &t->item);
+ }
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, NULL);
+}
+
+void test_item_put_ext_no_overwrite(void)
+{
+ 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);
+}
+
+void test_item_put_word_ext_overwrite(void)
+{
+ 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, NULL);
+ zassert_equal(t->data.data, i, NULL);
+ 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, NULL);
+ zassert_equal(t->data_ext.data, (void *)i, NULL);
+ zassert_equal(t->data_ext.hdr.data, i, NULL);
+ mpsc_pbuf_free(&buffer, &t->item);
+ }
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, NULL);
+}
+
+void test_item_put_ext_saturate(void)
+{
+ 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, NULL);
+ zassert_equal(t->data.data, i, NULL);
+ mpsc_pbuf_free(&buffer, &t->item);
+ }
+
+ t = get_cyc() - t;
+ PRINT("ext item claim,free: %d cycles\n", t/repeat);
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, NULL);
+}
+
+void test_benchmark_item_put_ext(void)
+{
+ 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, NULL);
+ zassert_equal(t->data.data, i, NULL);
+ mpsc_pbuf_free(&buffer, &t->item);
+ }
+
+ t = get_cyc() - t;
+ PRINT("ext item claim,free: %d cycles\n", t/repeat);
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, NULL);
+}
+
+void test_benchmark_item_put_data(void)
+{
+ 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++) {
+ item.data = (void *)i;
+ item.hdr.data = i;
+ mpsc_pbuf_put_data(&buffer, (uint32_t *)&item, 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);
+}
+
+void test_put_data_overwrite(void)
+{
+ 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, NULL);
+ zassert_equal(packet->hdr.len, len, NULL);
+
+ for (int j = 0; j < len - 1; j++) {
+ zassert_equal(packet->data[j], i + j, NULL);
+ }
+
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)packet);
+ }
+}
+
+void test_item_alloc_commit(void)
+{
+ item_alloc_commit(true);
+ item_alloc_commit(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, NULL);
+ 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, NULL);
+ 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_equal(packet, NULL, NULL);
+
+ /* Get one packet from the buffer. */
+ packet = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_true(packet, NULL);
+ 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, NULL);
+}
+
+void test_item_alloc_commit_saturate(void)
+{
+ 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, NULL);
+ p0->hdr.len = 10;
+
+ /* Check that no packet is yet available */
+ p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_equal(p, NULL, NULL);
+
+ p1 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, 20, K_NO_WAIT);
+ zassert_true(p1, NULL);
+ 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_equal(p, NULL, NULL);
+
+ 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, NULL);
+ zassert_equal(p->hdr.len, 10, NULL);
+ 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, NULL);
+ zassert_equal(p->hdr.len, 20, NULL);
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
+
+ /* No more packets. */
+ p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_equal(p, NULL, NULL);
+}
+
+void test_item_alloc_preemption(void)
+{
+ 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, NULL);
+
+ /* 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, NULL);
+
+ for (int i = 0; i < (packet_cnt - drop_cnt); i++) {
+ p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_true(p, NULL);
+ zassert_equal(p->hdr.len, fill_len, NULL);
+ zassert_equal(p->hdr.data, i + drop_cnt, NULL);
+ for (int j = 0; j < fill_len - 1; j++) {
+ zassert_equal(p->data[j], p->hdr.data + j, NULL);
+ }
+
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
+ }
+
+ p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_true(p, NULL);
+ zassert_equal(p->hdr.len, len0, NULL);
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
+
+ p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_true(p, NULL);
+ zassert_equal(p->hdr.len, len1, NULL);
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p);
+
+ p = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_equal(p, NULL, NULL);
+}
+
+void test_overwrite(void)
+{
+ 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, NULL);
+ zassert_equal(p0->hdr.len, fill_len, NULL);
+
+ 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, NULL);
+ 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, NULL);
+ zassert_equal(p0->hdr.len, fill_len, NULL);
+ zassert_equal(p0->hdr.data, i + drop_cnt + 1, NULL);
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p0);
+ }
+
+ p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_true(p0, NULL);
+ zassert_equal(p0->hdr.len, len, NULL);
+
+ p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_equal(p0, NULL, NULL);
+}
+
+void test_overwrite_while_claimed(void)
+{
+ 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, NULL);
+ zassert_equal(p0->hdr.len, fill_len, NULL);
+
+ 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, NULL);
+ 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, NULL);
+ zassert_equal(p0->hdr.len, fill_len, NULL);
+ zassert_equal(p0->hdr.data, i + drop_cnt + 1, NULL);
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)p0);
+ }
+
+ p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_true(p0, NULL);
+ zassert_equal(p0->hdr.len, len, NULL);
+
+ p0 = (struct test_data_var *)mpsc_pbuf_claim(&buffer);
+ zassert_equal(p0, NULL, NULL);
+}
+
+void test_overwrite_while_claimed2(void)
+{
+ 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(struct mpsc_pbuf_buffer *buffer,
+ 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);
+}
+
+void test_overwrite_consistency(void)
+{
+ 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;
+
+ t = (struct test_data_ext *)mpsc_pbuf_alloc(buffer,
+ sizeof(*t) / sizeof(uint32_t),
+ K_MSEC(1));
+ zassert_equal(t, NULL, NULL);
+
+ 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();
+
+ mpsc_pbuf_commit(buffer, (union mpsc_pbuf_generic *)t);
+ 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), NULL);
+ }
+}
+
+/* Test creates two threads which pends on the buffer until there is a space
+ * available. When engough buffers is released threads are waken up and they
+ * allocate packets.
+ */
+void test_pending_alloc(void)
+{
+ int prio = k_thread_priority_get(k_current_get());
+ struct mpsc_pbuf_buffer buffer;
+
+ 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, NULL);
+ zassert_equal(t->data, tids[ARRAY_SIZE(tids) - 1 - i], NULL);
+ mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)t);
+ }
+
+ zassert_equal(mpsc_pbuf_claim(&buffer), NULL, "No more packets.");
+ k_thread_priority_set(k_current_get(), prio);
+}
+
+/*test case main entry*/
+void test_main(void)
+{
+ ztest_test_suite(test_log_buffer,
+ ztest_unit_test(test_benchmark_item_put),
+ ztest_unit_test(test_item_put_saturate),
+ ztest_unit_test(test_item_put_no_overwrite),
+ ztest_unit_test(test_item_put_overwrite),
+ ztest_unit_test(test_item_put_ext_no_overwrite),
+ ztest_unit_test(test_item_put_word_ext_overwrite),
+ ztest_unit_test(test_item_put_ext_saturate),
+ ztest_unit_test(test_put_data_overwrite),
+ ztest_unit_test(test_benchmark_item_put_ext),
+ ztest_unit_test(test_benchmark_item_put_data),
+ ztest_unit_test(test_item_alloc_commit),
+ ztest_unit_test(test_item_alloc_commit_saturate),
+ ztest_unit_test(test_item_alloc_preemption),
+ ztest_unit_test(test_overwrite),
+ ztest_unit_test(test_overwrite_while_claimed),
+ ztest_unit_test(test_overwrite_while_claimed2),
+ ztest_unit_test(test_overwrite_consistency),
+ ztest_unit_test(test_pending_alloc)
+ );
+ ztest_run_test_suite(test_log_buffer);
+}
diff --git a/tests/lib/mpsc_pbuf/testcase.yaml b/tests/lib/mpsc_pbuf/testcase.yaml
new file mode 100644
index 0000000..07bccad
--- /dev/null
+++ b/tests/lib/mpsc_pbuf/testcase.yaml
@@ -0,0 +1,7 @@
+tests:
+ lib.mpsc_pbuf:
+ tags: mpsc_pbuf
+ platform_allow: >
+ qemu_arc_em qemu_arc_hs qemu_cortex_a53 qemu_cortex_m0 qemu_cortex_m3
+ qemu_cortex_r5 qemu_leon3 qemu_nios2 qemu_riscv32 qemu_riscv64 qemu_x86
+ qemu_x86_64 qemu_xtensa