• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }