| /* |
| * 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 uint32_t exp_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_true(drop_cnt < exp_drop_cnt); |
| zassert_equal(packet->hdr.len, exp_dropped_len[drop_cnt], |
| "(%d) Got:%08x, Expected: %08x", |
| drop_cnt, packet->hdr.len, exp_dropped_len[drop_cnt]); |
| zassert_equal(packet->hdr.data, exp_dropped_data[drop_cnt], |
| "(%d) Got:%08x, Expected: %08x", |
| drop_cnt, packet->hdr.data, exp_dropped_data[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, uint32_t wlen, bool overwrite) |
| { |
| drop_cnt = 0; |
| exp_drop_cnt = 0; |
| cfg.flags = overwrite ? MPSC_PBUF_MODE_OVERWRITE : 0; |
| cfg.size = wlen; |
| 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, 4 - !pow2, false); |
| |
| 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, 4 - !pow2, true); |
| |
| union test_item test_1word = {.data = {.valid = 1, .len = 1 }}; |
| |
| exp_dropped_data[0] = 0; |
| exp_dropped_len[0] = 1; |
| exp_drop_cnt = 1; |
| |
| for (int i = 0; i < buffer.size + 1; i++) { |
| test_1word.data.data = i; |
| mpsc_pbuf_put_word(&buffer, test_1word.item); |
| } |
| |
| zassert_equal(drop_cnt, exp_drop_cnt, |
| "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, 4 - !pow2, false); |
| |
| 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 + 1; i++) { |
| test_1word.data.data = i; |
| mpsc_pbuf_put_word(&buffer, test_1word.item); |
| } |
| |
| for (int i = 0; i < repeat; 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, ARRAY_SIZE(buf32) - !pow2, true); |
| |
| 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, 8 - !pow2, false); |
| |
| 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, 8 - !pow2, true); |
| |
| size_t w = (sizeof(uint32_t) + sizeof(void *)) / sizeof(uint32_t); |
| int repeat = 1 + buffer.size / 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_drop_cnt = 1; |
| |
| 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); |
| } |
| |
| zassert_equal(drop_cnt, exp_drop_cnt, |
| "Unexpected number of dropped messages: %d (exp: %d)", |
| drop_cnt, exp_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, 8 - !pow2, false); |
| |
| 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; 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, ARRAY_SIZE(buf32) - !pow2, false); |
| |
| 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, ARRAY_SIZE(buf32) - !pow2, false); |
| |
| 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, 8 - !pow2, true); |
| |
| size_t w = (sizeof(uint32_t) + sizeof(void *)) / sizeof(uint32_t); |
| int repeat = 1 + buffer.size / 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_drop_cnt = 1; |
| |
| 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); |
| } |
| |
| 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, 16 - !pow2, false); |
| |
| 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, 8, overwrite); |
| |
| /* 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, |
| K_NO_WAIT); |
| zassert_true(packet != NULL); |
| packet->hdr.len = buffer.size; |
| 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 + 1, |
| 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 = 3; |
| uint32_t cnt = 0; |
| int repeat = |
| uneven + ((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, 32 - !pow2, false); |
| |
| 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, ARRAY_SIZE(buf32) - !pow2, false); |
| |
| 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, 32 - !pow2, true); |
| uint32_t packet_cnt = saturate_buffer_uneven(&buffer, fill_len); |
| |
| zassert_equal(drop_cnt, exp_drop_cnt, NULL); |
| |
| exp_dropped_data[0] = 0; |
| exp_dropped_len[0] = fill_len; |
| exp_drop_cnt++; |
| exp_dropped_data[1] = 1; |
| exp_dropped_len[1] = fill_len; |
| exp_drop_cnt++; |
| |
| 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, exp_drop_cnt); |
| |
| /* 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; |
| exp_drop_cnt = 3; |
| |
| 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, exp_drop_cnt); |
| |
| 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, 32 - !pow2, true); |
| |
| 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; |
| exp_drop_cnt = 2; |
| p1 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, 6, K_NO_WAIT); |
| zassert_equal(drop_cnt, exp_drop_cnt); |
| 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, 32 - !pow2, true); |
| |
| 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_drop_cnt = 3; |
| p1 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, len, K_NO_WAIT); |
| |
| zassert_equal(drop_cnt, exp_drop_cnt); |
| 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, 15); |
| |
| 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(20)); |
| } |
| } |
| |
| /* 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, ARRAY_SIZE(buf32) - 1, true); |
| |
| 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_packet(struct mpsc_pbuf_buffer *buffer, char exp_c) |
| { |
| union test_item claimed_item; |
| const union mpsc_pbuf_generic *claimed; |
| |
| claimed = mpsc_pbuf_claim(buffer); |
| zassert_true(claimed, NULL); |
| claimed_item.item = *claimed; |
| zassert_equal(claimed_item.data.data, exp_c, NULL); |
| |
| mpsc_pbuf_free(buffer, claimed); |
| } |
| |
| ZTEST(log_buffer, test_put_while_claim) |
| { |
| struct mpsc_pbuf_buffer buffer; |
| uint32_t buffer_storage[4]; |
| const union mpsc_pbuf_generic *claimed; |
| struct mpsc_pbuf_buffer_config buffer_config = { |
| .buf = buffer_storage, |
| .size = 4, |
| .notify_drop = drop, |
| .get_wlen = get_wlen, |
| .flags = MPSC_PBUF_SIZE_POW2 | MPSC_PBUF_MODE_OVERWRITE |
| }; |
| union test_item claimed_item; |
| union test_item item = { |
| .data = { |
| .valid = 1, |
| .busy = 0, |
| .len = 1, |
| .data = (uint32_t)'a' |
| } |
| }; |
| |
| exp_drop_cnt = 0; |
| drop_cnt = 0; |
| mpsc_pbuf_init(&buffer, &buffer_config); |
| /* Expect buffer = {} */ |
| |
| for (int i = 0; i < buffer.size; ++i) { |
| mpsc_pbuf_put_word(&buffer, item.item); |
| item.data.data++; |
| } |
| |
| /* Expect buffer = {a, b, c, d}. Adding new word will drop 'a'. */ |
| exp_dropped_data[exp_drop_cnt] = (uint32_t)'a'; |
| exp_dropped_len[exp_drop_cnt] = 1; |
| exp_drop_cnt++; |
| |
| item.data.data = 'e'; |
| mpsc_pbuf_put_word(&buffer, item.item); |
| zassert_equal(drop_cnt, exp_drop_cnt, NULL); |
| /* Expect buffer = {e, b, c, d} */ |
| |
| claimed = mpsc_pbuf_claim(&buffer); |
| zassert_true(claimed, NULL); |
| claimed_item.item = *claimed; |
| zassert_equal(claimed_item.data.data, (uint32_t)'b', NULL); |
| |
| /* Expect buffer = {e, B, c, d}. Adding new will drop 'c'. */ |
| exp_dropped_data[exp_drop_cnt] = (int)'c'; |
| exp_dropped_len[exp_drop_cnt] = 1; |
| exp_drop_cnt++; |
| |
| item.data.data = 'f'; |
| mpsc_pbuf_put_word(&buffer, item.item); |
| zassert_equal(drop_cnt, exp_drop_cnt, NULL); |
| /* Expect buffer = {e, B, f, d}, Adding new will drop 'd'. */ |
| |
| exp_dropped_data[exp_drop_cnt] = (int)'d'; |
| exp_dropped_len[exp_drop_cnt] = 1; |
| exp_drop_cnt++; |
| item.data.data = 'g'; |
| mpsc_pbuf_put_word(&buffer, item.item); |
| zassert_equal(drop_cnt, exp_drop_cnt, NULL); |
| /* Expect buffer = {e, B, f, g} */ |
| |
| mpsc_pbuf_free(&buffer, claimed); |
| /* Expect buffer = {e -, f, g} */ |
| |
| check_packet(&buffer, 'e'); |
| /* Expect buffer = {-, -, f, g} */ |
| |
| check_packet(&buffer, 'f'); |
| /* Expect buffer = {-, -, -, g} */ |
| |
| check_packet(&buffer, 'g'); |
| /* Expect buffer = {-, -, -, -} */ |
| |
| claimed = mpsc_pbuf_claim(&buffer); |
| zassert_equal(claimed, NULL, NULL); |
| } |
| |
| 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); |