1 /******************************************************************************
2 * Copyright (c) 2022 Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
3 * All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *****************************************************************************/
18 /*************************************************************************************************/
19 /*!
20 * \file my_buf.c
21 *
22 * \brief Buffer pool service.
23 *
24 * Copyright (c) 2009-2018 Arm Ltd. All Rights Reserved.
25 *
26 * Copyright (c) 2019-2020 Packetcraft, Inc.
27 *
28 * Licensed under the Apache License, Version 2.0 (the "License");
29 * you may not use this file except in compliance with the License.
30 * You may obtain a copy of the License at
31 *
32 * http://www.apache.org/licenses/LICENSE-2.0
33 *
34 * Unless required by applicable law or agreed to in writing, software
35 * distributed under the License is distributed on an "AS IS" BASIS,
36 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
37 * See the License for the specific language governing permissions and
38 * limitations under the License.
39 */
40 /*************************************************************************************************/
41
42 #include "application\print\printf.h"
43 #include "assert.h"
44 #include "common\compiler.h"
45 #include "types.h"
46 #include "utility.h"
47 #include <buf_pool0/myBuf.h>
48 #include <buf_pool0/myHeap.h>
49
50 /**************************************************************************************************
51 Global Variables
52 **************************************************************************************************/
53
54 /* ! \brief Critical section nesting level. */
55 _attribute_data_retention_ static u8 myCsNesting = 0;
56
57 /* !
58 * \brief Enter a critical section.
59 */
myCsEnter(void)60 void myCsEnter(void)
61 {
62 if (myCsNesting == 0) {
63 irq_disable();
64 }
65 myCsNesting++;
66 }
67
68 /* !
69 * \brief Exit a critical section.
70 */
myCsExit(void)71 void myCsExit(void)
72 {
73 assert(myCsNesting != 0);
74
75 myCsNesting--;
76 if (myCsNesting == 0) {
77 irq_enable();
78 }
79 }
80
81 /**************************************************************************************************
82 Macros
83 **************************************************************************************************/
84 #define DEBUG_INFO // printf // debug use
85
86 /* Magic number used to check for free buffer. */
87 #define MY_BUF_FREE_NUM 0xFAABD00D
88
89 /**************************************************************************************************
90 Data Types
91 **************************************************************************************************/
92
93 /* Unit of memory storage-- a structure containing a pointer. */
94 typedef struct myBufMem_tag {
95 struct myBufMem_tag *pNext;
96 #if MY_BUF_FREE_CHECK_ASSERT == TRUE
97 u32 free;
98 #endif
99 } myBufMem_t;
100
101 /* Internal buffer pool. */
102 typedef struct {
103 myBufPoolDesc_t desc; /* Number of buffers and length. */
104 myBufMem_t *pStart; /* Start of pool. */
105 myBufMem_t *pFree; /* First free buffer in pool. */
106 #if MY_BUF_STATS == TRUE
107 u8 numAlloc; /* Number of buffers currently allocated from pool. */
108 u8 maxAlloc; /* Maximum buffers ever allocated from pool. */
109 u16 maxReqLen; /* Maximum request length from pool. */
110 #endif
111 } myBufPool_t;
112
113 /**************************************************************************************************
114 Global Variables
115 **************************************************************************************************/
116
117 /* Number of pools. */
118 _attribute_data_retention_ u8 myBufNumPools;
119
120 /* Memory used for pools. */
121 _attribute_data_retention_ myBufMem_t *myBufMem = NULL;
122
123 /* Currently use for debugging only. */
124 _attribute_data_retention_ u32 myBufMemLen;
125
126 #if MY_BUF_STATS_HIST == TRUE
127 /* Buffer allocation counter. */
128 _attribute_data_retention_ u8 myBufAllocCount[MY_BUF_STATS_MAX_LEN];
129
130 /* Pool Overflow counter. */
131 _attribute_data_retention_ u8 myPoolOverFlowCount[MY_BUF_STATS_MAX_POOL];
132 #endif
133
134 #if MY_OS_DIAG == TRUE
135 /* MY buffer diagnostic callback function. */
136 _attribute_data_retention_ static myBufDiagCback_t myBufDiagCback = NULL;
137 #endif
138
139 /*!
140 * \brief Calculate size required by the buffer pool.
141 *
142 * \param numPools Number of buffer pools.
143 * \param pDesc Array of buffer pool descriptors, one for each pool.
144 *
145 * \return Amount of pBufMem used.
146 */
myBufCalcSize(u8 numPools,myBufPoolDesc_t * pDesc)147 u32 myBufCalcSize(u8 numPools, myBufPoolDesc_t *pDesc)
148 {
149 u32 len;
150 u32 descLen;
151 myBufPool_t *pPool;
152 myBufMem_t *pStart;
153 u8 i;
154
155 myBufMem = (myBufMem_t *)0;
156 pPool = (myBufPool_t *)myBufMem;
157
158 /* Buffer storage starts after the pool structs. */
159 pStart = (myBufMem_t *)(pPool + numPools);
160
161 /* Create each pool; see loop exit condition below. */
162 while (TRUE) {
163 /* Exit loop after verification check. */
164 if (numPools-- == 0) {
165 break;
166 }
167
168 /* Adjust pool lengths for minimum size and alignment. */
169 if (pDesc->len < sizeof(myBufMem_t)) {
170 descLen = sizeof(myBufMem_t);
171 } else if ((pDesc->len % sizeof(myBufMem_t)) != 0) {
172 descLen = pDesc->len + sizeof(myBufMem_t) - (pDesc->len % sizeof(myBufMem_t));
173 } else {
174 descLen = pDesc->len;
175 }
176
177 len = descLen / sizeof(myBufMem_t);
178 for (i = pDesc->num; i > 0; i--) {
179 /* Pointer to the next free buffer is stored in the buffer itself. */
180 pStart += len;
181 }
182 pDesc++;
183 }
184
185 return (u8 *)pStart - (u8 *)myBufMem;
186 }
187
188 /*!
189 * \brief Initialize the buffer pool service. This function should only be called once
190 * upon system initialization.
191 *
192 * \param numPools Number of buffer pools.
193 * \param pDesc Array of buffer pool descriptors, one for each pool.
194 *
195 * \return Amount of pBufMem used or 0 for failures.
196 */
myBufInit(u8 numPools,myBufPoolDesc_t * pDesc)197 u32 myBufInit(u8 numPools, myBufPoolDesc_t *pDesc)
198 {
199 myBufPool_t *pPool;
200 myBufMem_t *pStart;
201 u16 len;
202 u8 i;
203
204 myBufMem = (myBufMem_t *)myHeapGetFreeStartAddress();
205 pPool = (myBufPool_t *)myBufMem;
206
207 /* Buffer storage starts after the pool structs. */
208 pStart = (myBufMem_t *)(pPool + numPools);
209
210 myBufNumPools = numPools;
211
212 /* Create each pool; see loop exit condition below. */
213 while (TRUE) {
214 /* Verify we didn't overrun memory; if we did, abort. */
215 if (pStart > &myBufMem[myHeapCountAvailable() / sizeof(myBufMem_t)]) {
216 assert(FALSE);
217 return 0;
218 }
219
220 /* Exit loop after verification check. */
221 if (numPools-- == 0) {
222 break;
223 }
224
225 /* Adjust pool lengths for minimum size and alignment. */
226 if (pDesc->len < sizeof(myBufMem_t)) {
227 pPool->desc.len = sizeof(myBufMem_t);
228 } else if ((pDesc->len % sizeof(myBufMem_t)) != 0) {
229 pPool->desc.len = pDesc->len + sizeof(myBufMem_t) - (pDesc->len % sizeof(myBufMem_t));
230 } else {
231 pPool->desc.len = pDesc->len;
232 }
233
234 pPool->desc.num = pDesc->num;
235 pDesc++;
236
237 pPool->pStart = pStart;
238 pPool->pFree = pStart;
239 #if MY_BUF_STATS == TRUE
240 pPool->numAlloc = 0;
241 pPool->maxAlloc = 0;
242 pPool->maxReqLen = 0;
243 #endif
244
245 /* Initialize free list. */
246 len = pPool->desc.len / sizeof(myBufMem_t);
247 for (i = pPool->desc.num; i > 1; i--) {
248 /* Verify we didn't overrun memory; if we did, abort. */
249 if (pStart > &myBufMem[myHeapCountAvailable() / sizeof(myBufMem_t)]) {
250 assert(FALSE);
251 return 0;
252 }
253 /* Pointer to the next free buffer is stored in the buffer itself. */
254 pStart->pNext = pStart + len;
255 pStart += len;
256 }
257
258 /* Verify we didn't overrun memory; if we did, abort. */
259 if (pStart > &myBufMem[myHeapCountAvailable() / sizeof(myBufMem_t)]) {
260 assert(FALSE);
261 return 0;
262 }
263 /* Last one in list points to NULL. */
264 pStart->pNext = NULL;
265 pStart += len;
266
267 /* Next pool. */
268 pPool++;
269 }
270
271 myBufMemLen = (u8 *)pStart - (u8 *)myBufMem;
272
273 return myBufMemLen;
274 }
275
276 /*!
277 * \brief Allocate a buffer.
278 *
279 * \param len Length of buffer to allocate.
280 *
281 * \return Pointer to allocated buffer or NULL if allocation fails.
282 */
myBufAlloc(u16 len)283 void *myBufAlloc(u16 len)
284 {
285 myBufPool_t *pPool;
286 myBufMem_t *pBuf;
287 u8 i;
288
289 assert(len > 0);
290
291 pPool = (myBufPool_t *)myBufMem;
292
293 for (i = myBufNumPools; i > 0; i--, pPool++) {
294 /* Check if buffer is big enough. */
295 if (len <= pPool->desc.len) {
296 /* Enter critical section. */
297 myCsEnter();
298
299 /* Check if buffers are available. */
300 if (pPool->pFree != NULL) {
301 /* Allocation succeeded. */
302 pBuf = pPool->pFree;
303
304 /* Next free buffer is stored inside current free buffer. */
305 pPool->pFree = pBuf->pNext;
306
307 #if MY_BUF_FREE_CHECK_ASSERT == TRUE
308 pBuf->free = 0;
309 #endif
310 #if MY_BUF_STATS_HIST == TRUE
311 /* Increment count for buffers of this length. */
312 if (len < MY_BUF_STATS_MAX_LEN) {
313 myBufAllocCount[len]++;
314 } else {
315 myBufAllocCount[0]++;
316 }
317 #endif
318 #if MY_BUF_STATS == TRUE
319 if (++pPool->numAlloc > pPool->maxAlloc) {
320 pPool->maxAlloc = pPool->numAlloc;
321 }
322 pPool->maxReqLen = max2(pPool->maxReqLen, len);
323 #endif
324 /* Exit critical section. */
325 myCsExit();
326 ;
327
328 return pBuf;
329 } else {
330 #if MY_BUF_STATS_HIST == TRUE
331 /* Pool overflow: increment count of overflow for current pool. */
332 myPoolOverFlowCount[myBufNumPools - i]++;
333 #endif
334 }
335 /* Exit critical section. */
336 myCsExit();
337 ;
338
339 #if MY_BUF_ALLOC_BEST_FIT_FAIL_ASSERT == TRUE
340 assert(FALSE);
341 #endif
342 }
343 }
344
345 /* Allocation failed. */
346 #if MY_OS_DIAG == TRUE
347 if (myBufDiagCback != NULL) {
348 myBufDiag_t info;
349
350 info.type = MY_BUF_ALLOC_FAILED;
351 info.param.alloc.taskId = MY_OS_GET_ACTIVE_HANDLER_ID();
352 info.param.alloc.len = len;
353
354 myBufDiagCback(&info);
355 } else {
356 }
357 #else
358
359 #endif
360
361 #if MY_BUF_ALLOC_FAIL_ASSERT == TRUE
362 assert(FALSE);
363 #endif
364
365 return NULL;
366 }
367
368 /*!
369 * \brief Free a buffer.
370 *
371 * \param pBuf Buffer to free.
372 */
myBufFree(void * pBuf)373 void myBufFree(void *pBuf)
374 {
375 myBufPool_t *pPool;
376 myBufMem_t *p = pBuf;
377
378 /* Verify pointer is within range. */
379 #if MY_BUF_FREE_CHECK_ASSERT == TRUE
380 assert(p >= ((myBufPool_t *)myBufMem)->pStart);
381 assert(p < (myBufMem_t *)(((u8 *)myBufMem) + myBufMemLen));
382 #endif
383
384 /* Iterate over pools starting from last pool. */
385 pPool = (myBufPool_t *)myBufMem + (myBufNumPools - 1);
386 while (pPool >= (myBufPool_t *)myBufMem) {
387 /* Check if the buffer memory is located inside this pool. */
388 if (p >= pPool->pStart) {
389 /* Enter critical section. */
390 myCsEnter();
391
392 #if MY_BUF_FREE_CHECK_ASSERT == TRUE
393 assert(p->free != MY_BUF_FREE_NUM);
394 p->free = MY_BUF_FREE_NUM;
395 #endif
396 #if MY_BUF_STATS == TRUE
397 pPool->numAlloc--;
398 #endif
399
400 /* Pool found; put buffer back in free list. */
401 p->pNext = pPool->pFree;
402 pPool->pFree = p;
403
404 /* Exit critical section. */
405 myCsExit();
406 ;
407
408 return;
409 }
410
411 /* Next pool. */
412 pPool--;
413 }
414
415 /* Should never get here. */
416 assert(FALSE);
417
418 return;
419 }
420
421 /*!
422 * \brief Diagnostic function to get the buffer allocation statistics.
423 *
424 * \return Buffer allocation statistics array.
425 */
myBufGetAllocStats(void)426 u8 *myBufGetAllocStats(void)
427 {
428 #if MY_BUF_STATS_HIST == TRUE
429 return myBufAllocCount;
430 #else
431 return NULL;
432 #endif
433 }
434
435 /*!
436 * \brief Diagnostic function to get the number of overflow times for each pool.
437 *
438 * \return Overflow times statistics array
439 */
myBufGetPoolOverFlowStats(void)440 u8 *myBufGetPoolOverFlowStats(void)
441 {
442 #if MY_BUF_STATS_HIST == TRUE
443 return myPoolOverFlowCount;
444 #else
445 return NULL;
446 #endif
447 }
448
449 /*!
450 * \brief Get number of pools.
451 *
452 * \return Number of pools.
453 */
myBufGetNumPool(void)454 u8 myBufGetNumPool(void)
455 {
456 return myBufNumPools;
457 }
458
459 /*!
460 * \brief Get statistics for each pool.
461 *
462 * \param pBuf Buffer to store the statistics.
463 * \param poolId Pool ID.
464 */
myBufGetPoolStats(myBufPoolStat_t * pStat,u8 poolId)465 void myBufGetPoolStats(myBufPoolStat_t *pStat, u8 poolId)
466 {
467 myBufPool_t *pPool;
468
469 if (poolId >= myBufNumPools) {
470 pStat->bufSize = 0;
471 return;
472 }
473
474 myCsEnter();
475
476 pPool = (myBufPool_t *)myBufMem;
477
478 pStat->bufSize = pPool[poolId].desc.len;
479 pStat->numBuf = pPool[poolId].desc.num;
480 #if MY_BUF_STATS == TRUE
481 pStat->numAlloc = pPool[poolId].numAlloc;
482 pStat->maxAlloc = pPool[poolId].maxAlloc;
483 pStat->maxReqLen = pPool[poolId].maxReqLen;
484 #else
485 pStat->numAlloc = 0;
486 pStat->maxAlloc = 0;
487 pStat->maxReqLen = 0;
488 #endif
489
490 /* Exit critical section. */
491 myCsExit();
492 ;
493 }
494
495 /* !
496 * \brief Called to register the buffer diagnostics callback function.
497 *
498 * \param pCallback Pointer to the callback function.
499 */
myBufDiagRegister(myBufDiagCback_t callback)500 void myBufDiagRegister(myBufDiagCback_t callback)
501 {
502 #if MY_OS_DIAG == TRUE
503 myBufDiagCback = callback;
504 #else
505 /* Unused parameter */
506 (void)callback;
507 #endif
508 }