/* * Copyright (c) 2021 Nordic Semiconductor * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Test log message */ #include #include #include #include #include #include #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 mpsc_buf_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; mpsc_buf_cfg.flags = overwrite ? MPSC_PBUF_MODE_OVERWRITE : 0; mpsc_buf_cfg.size = wlen; mpsc_pbuf_init(buffer, &mpsc_buf_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 *ti; ti = (union test_item *)mpsc_pbuf_claim(&buffer); zassert_true(ti); zassert_equal(ti->data.data, i); mpsc_pbuf_free(&buffer, &ti->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 *ti; ti = (union test_item *)mpsc_pbuf_claim(&buffer); zassert_true(ti); zassert_equal(ti->data.data, i); mpsc_pbuf_free(&buffer, &ti->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 cyc = 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); } cyc = get_cyc() - cyc; PRINT("%spow2 buffer\n", pow2 ? "" : "non-"); PRINT("put_ext time: %d cycles\n", cyc/repeat); cyc = get_cyc(); for (int i = 0; i < repeat; i++) { union test_item *ti; ti = (union test_item *)mpsc_pbuf_claim(&buffer); zassert_true(ti); zassert_equal(ti->data.data, i); mpsc_pbuf_free(&buffer, &ti->item); } cyc = get_cyc() - cyc; PRINT("ext item claim,free: %d cycles\n", cyc/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 *tdv = 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) { tdv = (struct test_data_var *)mpsc_pbuf_claim(&buffer); if (tdv) { validate_packet(tdv); } } 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 *tdv2; tdv2 = (struct test_data_var *)mpsc_pbuf_alloc(&buffer, wlen, K_NO_WAIT); tdv2->hdr.len = wlen; tdv2->hdr.data = id++; mpsc_pbuf_commit(&buffer, (union mpsc_pbuf_generic *)tdv2); } /* Put back item claimed before committing new items. */ if (tdv) { mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)tdv); } uint32_t rd_cnt = rand_get(1, 30); for (int i = 0; i < rd_cnt; i++) { struct test_data_var *tdv2; tdv2 = (struct test_data_var *)mpsc_pbuf_claim(&buffer); if (!tdv2) { continue; } validate_packet(tdv2); mpsc_pbuf_free(&buffer, (union mpsc_pbuf_generic *)tdv2); } } } 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);