1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/ztress.h>
9 #include <zephyr/sys/spsc_pbuf.h>
10 #include <zephyr/random/random.h>
11
12 #define HDR_LEN sizeof(uint32_t)
13 #define TLEN(len) ROUND_UP(HDR_LEN + len, sizeof(uint32_t))
14 #define STRESS_TIMEOUT_MS ((CONFIG_SYS_CLOCK_TICKS_PER_SEC < 10000) ? 1000 : 15000)
15
16 /* The buffer size itself would be 199 bytes.
17 * 212 - sizeof(struct spsc_pbuf) - 1 = 199.
18 * -1 because internal rd/wr_idx is reserved to mean the buffer is empty.
19 */
use_cache(uint32_t flags)20 static bool use_cache(uint32_t flags)
21 {
22 return IS_ENABLED(CONFIG_SPSC_PBUF_CACHE_ALWAYS) ||
23 (IS_ENABLED(CONFIG_SPSC_PBUF_CACHE_FLAG) && (flags & SPSC_PBUF_CACHE));
24 }
25
test_spsc_pbuf_flags(uint32_t flags)26 static void test_spsc_pbuf_flags(uint32_t flags)
27 {
28 static uint8_t memory_area[216] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
29 static uint8_t rbuf[198];
30 static uint8_t message[20] = {'a'};
31 struct spsc_pbuf *ib;
32 int rlen;
33 int wlen;
34 size_t capacity = (use_cache(flags) ?
35 (sizeof(memory_area) - offsetof(struct spsc_pbuf, ext.cache.data)) :
36 (sizeof(memory_area) - offsetof(struct spsc_pbuf, ext.nocache.data))) -
37 sizeof(uint32_t);
38
39 memset(memory_area, 0, sizeof(memory_area));
40 ib = spsc_pbuf_init(memory_area, sizeof(memory_area), flags);
41 zassert_equal_ptr(ib, memory_area, NULL);
42 zassert_equal(spsc_pbuf_capacity(ib), capacity);
43
44 /* Try writing invalid value. */
45 rlen = spsc_pbuf_write(ib, rbuf, 0);
46 zassert_equal(rlen, -EINVAL);
47 rlen = spsc_pbuf_write(ib, rbuf, SPSC_PBUF_MAX_LEN);
48 zassert_equal(rlen, -EINVAL);
49
50 /* Try to write more than buffer can store. */
51 rlen = spsc_pbuf_write(ib, rbuf, sizeof(rbuf));
52 zassert_equal(rlen, -ENOMEM);
53
54 /* Read empty buffer. */
55 rlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
56 zassert_equal(rlen, 0);
57
58 /* Single write and read. */
59 wlen = spsc_pbuf_write(ib, message, sizeof(message));
60 zassert_equal(wlen, sizeof(message));
61
62 rlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
63 zassert_equal(rlen, sizeof(message));
64
65 ib = spsc_pbuf_init(memory_area, sizeof(memory_area), flags);
66 zassert_equal_ptr(ib, memory_area, NULL);
67
68 int repeat = capacity / (sizeof(message) + sizeof(uint32_t));
69
70 for (int i = 0; i < repeat; i++) {
71 wlen = spsc_pbuf_write(ib, message, sizeof(message));
72 zassert_equal(wlen, sizeof(message));
73 }
74
75 wlen = spsc_pbuf_write(ib, message, sizeof(message));
76 zassert_equal(wlen, -ENOMEM);
77
78 /* Test reading with buf == NULL, should return len of the next message to read. */
79 rlen = spsc_pbuf_read(ib, NULL, 0);
80 zassert_equal(rlen, sizeof(message));
81
82 /* Read with len == 0 and correct buf pointer. */
83 rlen = spsc_pbuf_read(ib, rbuf, 0);
84 zassert_equal(rlen, -ENOMEM);
85
86 /* Read whole data from the buffer. */
87 for (size_t i = 0; i < repeat; i++) {
88 wlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
89 zassert_equal(wlen, sizeof(message));
90 }
91
92 /* Buffer is empty */
93 rlen = spsc_pbuf_read(ib, NULL, 0);
94 zassert_equal(rlen, 0);
95
96 /* Write message that would be wrapped around. */
97 wlen = spsc_pbuf_write(ib, message, sizeof(message));
98 zassert_equal(wlen, sizeof(message));
99
100 /* Read wrapped message. */
101 rlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
102 zassert_equal(rlen, sizeof(message));
103 zassert_equal(message[0], 'a');
104 }
105
ZTEST(test_spsc_pbuf,test_spsc_pbuf_ut)106 ZTEST(test_spsc_pbuf, test_spsc_pbuf_ut)
107 {
108 test_spsc_pbuf_flags(0);
109 }
110
ZTEST(test_spsc_pbuf,test_spsc_pbuf_ut_cache)111 ZTEST(test_spsc_pbuf, test_spsc_pbuf_ut_cache)
112 {
113 test_spsc_pbuf_flags(SPSC_PBUF_CACHE);
114 }
115
check_buffer(char * buf,uint16_t len,char exp)116 static int check_buffer(char *buf, uint16_t len, char exp)
117 {
118 for (uint16_t i = 0; i < len; i++) {
119 if (buf[i] != exp) {
120 return -EINVAL;
121 }
122 }
123
124 return 0;
125 }
126
packet_write(struct spsc_pbuf * pb,uint16_t len,uint16_t outlen,uint8_t id,int exp_rv,int line)127 static void packet_write(struct spsc_pbuf *pb,
128 uint16_t len,
129 uint16_t outlen,
130 uint8_t id,
131 int exp_rv,
132 int line)
133 {
134 int rv;
135 char *buf;
136
137 rv = spsc_pbuf_alloc(pb, len, &buf);
138 zassert_equal(rv, exp_rv, "%d: Unexpected rv:%d (exp:%d)", line, rv, exp_rv);
139 if (rv < 0) {
140 return;
141 }
142 zassert_equal((uintptr_t)buf % sizeof(uint32_t), 0, "%d: Expected aligned buffer", line);
143 zassert_true(rv >= outlen, "%d: Unexpected rv (bigger than %d)", line, rv, outlen);
144
145 for (uint16_t i = 0; i < outlen; i++) {
146 buf[i] = id + i;
147 }
148
149 if (outlen > 0) {
150 spsc_pbuf_commit(pb, outlen);
151 }
152 }
153 #define PACKET_WRITE(_pb, _len, _outlen, _id, _exp_rv) \
154 packet_write(_pb, _len, _outlen, _id, _exp_rv, __LINE__)
155
packet_consume(struct spsc_pbuf * pb,uint16_t exp_rv,uint8_t exp_id,int line)156 static void packet_consume(struct spsc_pbuf *pb,
157 uint16_t exp_rv,
158 uint8_t exp_id,
159 int line)
160 {
161 uint16_t rv;
162 char *buf;
163
164 rv = spsc_pbuf_claim(pb, &buf);
165 zassert_equal(rv, exp_rv, "%d: Unexpected rv:%d (exp:%d)", line, rv, exp_rv);
166 if (rv == 0) {
167 return;
168 }
169
170 for (int i = 0; i < rv; i++) {
171 zassert_equal(buf[i], exp_id + i, "%d: Unexpected value %d (exp:%d) at %d",
172 line, buf[i], exp_id + i, i);
173 }
174
175 spsc_pbuf_free(pb, rv);
176 }
177
178 #define PACKET_CONSUME(_pb, _exp_rv, _exp_id) packet_consume(_pb, _exp_rv, _exp_id, __LINE__)
179
ZTEST(test_spsc_pbuf,test_0cpy)180 ZTEST(test_spsc_pbuf, test_0cpy)
181 {
182 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
183 struct spsc_pbuf *pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
184 uint32_t capacity = spsc_pbuf_capacity(pb);
185 uint16_t len1;
186 uint16_t len2;
187
188 /* Writing 0 length returns error. */
189 PACKET_WRITE(pb, 0, 0, 0, -EINVAL);
190 spsc_pbuf_commit(pb, 0);
191
192 PACKET_WRITE(pb, SPSC_PBUF_MAX_LEN, 0, 0, capacity - sizeof(uint32_t));
193
194 len1 = capacity - 8 - 2 * sizeof(uint32_t);
195 PACKET_WRITE(pb, len1, len1, 0, len1);
196
197 /* Remaining space. */
198 len2 = capacity - TLEN(len1) - HDR_LEN;
199 /* Request exceeding capacity*/
200 PACKET_WRITE(pb, len2 + 1, 0, 1, len2);
201
202 PACKET_WRITE(pb, len2, len2, 1, len2);
203
204 /* Consume packets. */
205 PACKET_CONSUME(pb, len1, 0);
206 PACKET_CONSUME(pb, len2, 1);
207
208 /* No more packets. */
209 PACKET_CONSUME(pb, 0, 0);
210 }
211
ZTEST(test_spsc_pbuf,test_0cpy_smaller)212 ZTEST(test_spsc_pbuf, test_0cpy_smaller)
213 {
214 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
215 struct spsc_pbuf *pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
216 uint32_t capacity = spsc_pbuf_capacity(pb);
217 uint16_t len1;
218 uint16_t len2;
219
220 len1 = capacity - 10 - sizeof(uint16_t);
221 PACKET_WRITE(pb, len1, len1 - 5, 0, len1);
222
223 len2 = 10 - sizeof(uint16_t) - 1;
224 PACKET_WRITE(pb, len2, len2, 1, len2);
225
226 /* Consume packets. */
227 PACKET_CONSUME(pb, len1 - 5, 0);
228 PACKET_CONSUME(pb, len2, 1);
229 PACKET_CONSUME(pb, 0, 0);
230 }
231
ZTEST(test_spsc_pbuf,test_0cpy_discard)232 ZTEST(test_spsc_pbuf, test_0cpy_discard)
233 {
234 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
235 struct spsc_pbuf *pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
236 uint32_t capacity = spsc_pbuf_capacity(pb);
237 int len1, len2;
238
239 len1 = 14;
240 PACKET_WRITE(pb, len1, len1, 0, len1);
241
242 len2 = capacity - TLEN(len1) - 10;
243 PACKET_WRITE(pb, len2, len2, 1, len2);
244
245 /* Consume first packet */
246 PACKET_CONSUME(pb, len1, 0);
247
248 /* Consume next packet. At this point buffer shall be completely empty. */
249 PACKET_CONSUME(pb, len2, 1);
250
251 /* Allocate but then discard by committing 0 length. Alloc will add padding. */
252 PACKET_WRITE(pb, len1, 0, 0, len1);
253
254 /* No packet in the buffer. */
255 PACKET_CONSUME(pb, 0, 0);
256
257 /* Buffer is empty except for the padding added by alloc. */
258 len2 = len1 + len2 - sizeof(uint16_t);
259 PACKET_WRITE(pb, len2, 0, 0, len2);
260 }
261
ZTEST(test_spsc_pbuf,test_0cpy_corner1)262 ZTEST(test_spsc_pbuf, test_0cpy_corner1)
263 {
264 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
265 struct spsc_pbuf *pb;
266 uint32_t capacity;
267 char *buf;
268 uint16_t len;
269 uint16_t len1;
270 uint16_t len2;
271
272 pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
273 capacity = spsc_pbuf_capacity(pb);
274
275 /* Commit 5 byte packet. */
276 len1 = 5;
277 PACKET_WRITE(pb, len1, len1, 0, len1);
278
279 /* Attempt to allocate packet till the end of the buffer. */
280 len2 = capacity;
281 len2 = spsc_pbuf_alloc(pb, len2, &buf);
282
283 uint16_t exp_len2 = capacity - TLEN(len1) - HDR_LEN;
284
285 zassert_equal(len2, exp_len2, "got %d, exp: %d", len2, exp_len2);
286
287 len = spsc_pbuf_claim(pb, &buf);
288 zassert_equal(len1, len);
289 spsc_pbuf_free(pb, len);
290
291 spsc_pbuf_commit(pb, len2);
292
293 len = spsc_pbuf_claim(pb, &buf);
294 zassert_equal(len2, len);
295 spsc_pbuf_free(pb, len);
296 }
297
ZTEST(test_spsc_pbuf,test_0cpy_corner2)298 ZTEST(test_spsc_pbuf, test_0cpy_corner2)
299 {
300 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
301 struct spsc_pbuf *pb;
302 uint32_t capacity;
303 uint16_t len1;
304 uint16_t len2;
305
306 pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
307 capacity = spsc_pbuf_capacity(pb);
308
309 /* Commit 16 byte packet. */
310 len1 = 16;
311 PACKET_WRITE(pb, len1, len1, 0, len1);
312
313 /* Attempt to allocate packet that will leave 5 bytes at the end. */
314 len2 = capacity - TLEN(len1) - HDR_LEN - 5;
315 PACKET_WRITE(pb, len2, len2, 1, len2);
316
317 /* Free first packet. */
318 PACKET_CONSUME(pb, len1, 0);
319
320 /* Allocate something that does not fit at the end. */
321 len1 = 8;
322 PACKET_WRITE(pb, len1, len1, 2, len1);
323
324 /* There should be no place in the buffer now, only length field would fill.*/
325 PACKET_WRITE(pb, 1, 0, 2, 0);
326
327 /* Free second packet. */
328 PACKET_CONSUME(pb, len2, 1);
329
330 /* Get longest available. As now there is only one packet at the beginning
331 * that should be remaining space decremented by length fields.
332 */
333 uint16_t exp_len = capacity - TLEN(len1) - HDR_LEN;
334
335 PACKET_WRITE(pb, capacity, 0, 2, exp_len);
336 }
337
ZTEST(test_spsc_pbuf,test_largest_alloc)338 ZTEST(test_spsc_pbuf, test_largest_alloc)
339 {
340 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
341 struct spsc_pbuf *pb;
342 uint32_t capacity;
343 uint16_t len1;
344 uint16_t len2;
345
346 pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
347 capacity = spsc_pbuf_capacity(pb);
348
349 len1 = 15;
350 PACKET_WRITE(pb, len1, len1, 0, len1);
351 PACKET_CONSUME(pb, len1, 0);
352
353 len2 = capacity - TLEN(len1) - TLEN(10);
354 PACKET_WRITE(pb, len2, len2, 1, len2);
355
356 PACKET_WRITE(pb, SPSC_PBUF_MAX_LEN, 0, 1, 12);
357
358 PACKET_WRITE(pb, SPSC_PBUF_MAX_LEN - 1, 0, 1, 12);
359
360 pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
361 capacity = spsc_pbuf_capacity(pb);
362
363 len1 = 15;
364 PACKET_WRITE(pb, len1, len1, 0, len1);
365 PACKET_CONSUME(pb, len1, 0);
366
367 len2 = capacity - TLEN(len1) - TLEN(12);
368 PACKET_WRITE(pb, len2, len2, 1, len2);
369
370 PACKET_WRITE(pb, SPSC_PBUF_MAX_LEN - 1, 0, 1, 12);
371 }
372
ZTEST(test_spsc_pbuf,test_utilization)373 ZTEST(test_spsc_pbuf, test_utilization)
374 {
375 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
376 struct spsc_pbuf *pb;
377 uint32_t capacity;
378 uint16_t len1, len2, len3;
379 int u;
380
381 pb = spsc_pbuf_init(buffer, sizeof(buffer), 0);
382
383 if (!IS_ENABLED(CONFIG_SPSC_PBUF_UTILIZATION)) {
384 zassert_equal(spsc_pbuf_get_utilization(pb), -ENOTSUP);
385 return;
386 }
387 capacity = spsc_pbuf_capacity(pb);
388
389 len1 = 10;
390 PACKET_WRITE(pb, len1, len1, 0, len1);
391 u = spsc_pbuf_get_utilization(pb);
392 zassert_equal(u, 0);
393
394 PACKET_CONSUME(pb, len1, 0);
395 u = spsc_pbuf_get_utilization(pb);
396 zassert_equal(u, TLEN(len1));
397
398 len2 = 11;
399 PACKET_WRITE(pb, len2, len2, 1, len2);
400 PACKET_CONSUME(pb, len2, 1);
401 u = spsc_pbuf_get_utilization(pb);
402 zassert_equal(u, TLEN(len2));
403
404 len3 = capacity - TLEN(len1) - TLEN(len2);
405 PACKET_WRITE(pb, SPSC_PBUF_MAX_LEN, len3, 2, len3);
406 PACKET_CONSUME(pb, len3, 2);
407
408 u = spsc_pbuf_get_utilization(pb);
409 int exp_u = TLEN(len3);
410
411 zassert_equal(u, exp_u);
412 }
413
414 struct stress_data {
415 struct spsc_pbuf *pbuf;
416 uint32_t capacity;
417 uint32_t write_cnt;
418 uint32_t read_cnt;
419 uint32_t wr_err;
420 };
421
stress_read(void * user_data,uint32_t cnt,bool last,int prio)422 bool stress_read(void *user_data, uint32_t cnt, bool last, int prio)
423 {
424 struct stress_data *ctx = (struct stress_data *)user_data;
425 char buf[128];
426 int len;
427 int rpt = (sys_rand8_get() & 3) + 1;
428
429 for (int i = 0; i < rpt; i++) {
430 len = spsc_pbuf_read(ctx->pbuf, buf, (uint16_t)sizeof(buf));
431 if (len == 0) {
432 return true;
433 }
434
435 if (len < 0) {
436 zassert_true(false, "Unexpected error: %d, cnt:%d", len, ctx->read_cnt);
437 }
438
439 zassert_ok(check_buffer(buf, len, ctx->read_cnt));
440 ctx->read_cnt++;
441 }
442
443 return true;
444 }
445
stress_write(void * user_data,uint32_t cnt,bool last,int prio)446 bool stress_write(void *user_data, uint32_t cnt, bool last, int prio)
447 {
448 struct stress_data *ctx = (struct stress_data *)user_data;
449 char buf[128];
450 uint16_t len = 1 + (sys_rand16_get() % (ctx->capacity / 4));
451 int rpt = (sys_rand8_get() & 1) + 1;
452
453 zassert_true(len < sizeof(buf), "len:%d %d", len, ctx->capacity);
454
455 for (int i = 0; i < rpt; i++) {
456 memset(buf, (uint8_t)ctx->write_cnt, len);
457 if (spsc_pbuf_write(ctx->pbuf, buf, len) == len) {
458 ctx->write_cnt++;
459 } else {
460 ctx->wr_err++;
461 }
462 }
463
464 return true;
465 }
466
ZTEST(test_spsc_pbuf,test_stress)467 ZTEST(test_spsc_pbuf, test_stress)
468 {
469 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
470 static struct stress_data ctx = {};
471 uint32_t repeat = 0;
472
473 ctx.pbuf = spsc_pbuf_init(buffer, sizeof(buffer), 0);
474 ctx.capacity = spsc_pbuf_capacity(ctx.pbuf);
475
476 ztress_set_timeout(K_MSEC(STRESS_TIMEOUT_MS));
477 TC_PRINT("Reading from an interrupt, writing from a thread\n");
478 ZTRESS_EXECUTE(ZTRESS_TIMER(stress_read, &ctx, repeat, Z_TIMEOUT_TICKS(4)),
479 ZTRESS_THREAD(stress_write, &ctx, repeat, 2000, Z_TIMEOUT_TICKS(4)));
480 TC_PRINT("Writes:%d failures: %d\n", ctx.write_cnt, ctx.wr_err);
481
482 TC_PRINT("Writing from an interrupt, reading from a thread\n");
483 ZTRESS_EXECUTE(ZTRESS_TIMER(stress_write, &ctx, repeat, Z_TIMEOUT_TICKS(4)),
484 ZTRESS_THREAD(stress_read, &ctx, repeat, 1000, Z_TIMEOUT_TICKS(4)));
485 TC_PRINT("Writes:%d failures: %d\n", ctx.write_cnt, ctx.wr_err);
486 }
487
stress_claim_free(void * user_data,uint32_t cnt,bool last,int prio)488 bool stress_claim_free(void *user_data, uint32_t cnt, bool last, int prio)
489 {
490 struct stress_data *ctx = (struct stress_data *)user_data;
491 char *buf;
492 uint16_t len;
493 int rpt = sys_rand8_get() % 0x3;
494
495 for (int i = 0; i < rpt; i++) {
496 len = spsc_pbuf_claim(ctx->pbuf, &buf);
497
498 if (len == 0) {
499 return true;
500 }
501
502 zassert_ok(check_buffer(buf, len, ctx->read_cnt));
503
504 spsc_pbuf_free(ctx->pbuf, len);
505
506 ctx->read_cnt++;
507 }
508
509 return true;
510 }
511
stress_alloc_commit(void * user_data,uint32_t cnt,bool last,int prio)512 bool stress_alloc_commit(void *user_data, uint32_t cnt, bool last, int prio)
513 {
514 struct stress_data *ctx = (struct stress_data *)user_data;
515 uint16_t rnd = sys_rand16_get();
516 uint16_t len = 1 + (rnd % (ctx->capacity / 4));
517 int rpt = rnd % 0x3;
518 char *buf;
519 int err;
520
521 for (int i = 0; i < rpt; i++) {
522 err = spsc_pbuf_alloc(ctx->pbuf, len, &buf);
523 zassert_true(err >= 0);
524 if (err != len) {
525 return true;
526 }
527
528 memset(buf, (uint8_t)ctx->write_cnt, len);
529
530 spsc_pbuf_commit(ctx->pbuf, len);
531 ctx->write_cnt++;
532 }
533
534 return true;
535 }
536
ZTEST(test_spsc_pbuf,test_stress_0cpy)537 ZTEST(test_spsc_pbuf, test_stress_0cpy)
538 {
539 static uint8_t buffer[128] __aligned(MAX(Z_SPSC_PBUF_DCACHE_LINE, 4));
540 static struct stress_data ctx;
541 uint32_t repeat = 0;
542
543 ctx.write_cnt = 0;
544 ctx.read_cnt = 0;
545 ctx.pbuf = spsc_pbuf_init(buffer, sizeof(buffer), 0);
546 ctx.capacity = spsc_pbuf_capacity(ctx.pbuf);
547
548 ztress_set_timeout(K_MSEC(STRESS_TIMEOUT_MS));
549 ZTRESS_EXECUTE(ZTRESS_THREAD(stress_claim_free, &ctx, repeat, 0, Z_TIMEOUT_TICKS(4)),
550 ZTRESS_THREAD(stress_alloc_commit, &ctx, repeat, 1000, Z_TIMEOUT_TICKS(4)));
551
552 ZTRESS_EXECUTE(ZTRESS_THREAD(stress_alloc_commit, &ctx, repeat, 0, Z_TIMEOUT_TICKS(4)),
553 ZTRESS_THREAD(stress_claim_free, &ctx, repeat, 1000, Z_TIMEOUT_TICKS(4)));
554 }
555
556 ZTEST_SUITE(test_spsc_pbuf, NULL, NULL, NULL, NULL, NULL);
557