1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** ThreadX Component */
17 /** */
18 /** Byte Pool */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23 #define TX_SOURCE_CODE
24
25
26 /* Include necessary system files. */
27
28 #include "tx_api.h"
29 #include "tx_thread.h"
30 #include "tx_byte_pool.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _tx_byte_pool_search PORTABLE SMP */
38 /* 6.3.0 */
39 /* AUTHOR */
40 /* */
41 /* William E. Lamie, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function searches a byte pool for a memory block to satisfy */
46 /* the requested number of bytes. Merging of adjacent free blocks */
47 /* takes place during the search and a split of the block that */
48 /* satisfies the request may occur before this function returns. */
49 /* */
50 /* It is assumed that this function is called with interrupts enabled */
51 /* and with the tx_pool_owner field set to the thread performing the */
52 /* search. Also note that the search can occur during allocation and */
53 /* release of a memory block. */
54 /* */
55 /* INPUT */
56 /* */
57 /* pool_ptr Pointer to pool control block */
58 /* memory_size Number of bytes required */
59 /* */
60 /* OUTPUT */
61 /* */
62 /* UCHAR * Pointer to the allocated memory, */
63 /* if successful. Otherwise, a */
64 /* NULL is returned */
65 /* */
66 /* CALLS */
67 /* */
68 /* None */
69 /* */
70 /* CALLED BY */
71 /* */
72 /* _tx_byte_allocate Allocate bytes of memory */
73 /* _tx_byte_release Release bytes of memory */
74 /* */
75 /* RELEASE HISTORY */
76 /* */
77 /* DATE NAME DESCRIPTION */
78 /* */
79 /* 09-30-2020 William E. Lamie Initial Version 6.1 */
80 /* 06-02-2021 Scott Larson Improve possible free bytes */
81 /* calculation, and reduced */
82 /* number of search resets, */
83 /* resulting in version 6.1.7 */
84 /* 10-31-2023 Tiejun Zhou Fixed MISRA2012 rule 10.4_a, */
85 /* resulting in version 6.3.0 */
86 /* */
87 /**************************************************************************/
_tx_byte_pool_search(TX_BYTE_POOL * pool_ptr,ULONG memory_size)88 UCHAR *_tx_byte_pool_search(TX_BYTE_POOL *pool_ptr, ULONG memory_size)
89 {
90
91 TX_INTERRUPT_SAVE_AREA
92
93 UCHAR *current_ptr;
94 UCHAR *next_ptr;
95 UCHAR **this_block_link_ptr;
96 UCHAR **next_block_link_ptr;
97 ULONG available_bytes;
98 UINT examine_blocks;
99 UINT first_free_block_found = TX_FALSE;
100 TX_THREAD *thread_ptr;
101 ALIGN_TYPE *free_ptr;
102 UCHAR *work_ptr;
103 volatile ULONG delay_count;
104 ULONG total_theoretical_available;
105 #ifdef TX_BYTE_POOL_MULTIPLE_BLOCK_SEARCH
106 UINT blocks_searched = ((UINT) 0);
107 #endif
108
109
110 /* Disable interrupts. */
111 TX_DISABLE
112
113 /* First, determine if there are enough bytes in the pool. */
114 /* Theoretical bytes available = free bytes + ((fragments-2) * overhead of each block) */
115 total_theoretical_available = pool_ptr -> tx_byte_pool_available + ((pool_ptr -> tx_byte_pool_fragments - 2U) * ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))));
116 if (memory_size >= total_theoretical_available)
117 {
118
119 /* Restore interrupts. */
120 TX_RESTORE
121
122 /* Not enough memory, return a NULL pointer. */
123 current_ptr = TX_NULL;
124 }
125 else
126 {
127
128 /* Pickup thread pointer. */
129 TX_THREAD_GET_CURRENT(thread_ptr)
130
131 /* Setup ownership of the byte pool. */
132 pool_ptr -> tx_byte_pool_owner = thread_ptr;
133
134 /* Walk through the memory pool in search for a large enough block. */
135 current_ptr = pool_ptr -> tx_byte_pool_search;
136 examine_blocks = pool_ptr -> tx_byte_pool_fragments + ((UINT) 1);
137 available_bytes = ((ULONG) 0);
138 do
139 {
140
141
142 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
143
144 /* Increment the total fragment search counter. */
145 _tx_byte_pool_performance_search_count++;
146
147 /* Increment the number of fragments searched on this pool. */
148 pool_ptr -> tx_byte_pool_performance_search_count++;
149 #endif
150
151 /* Check to see if this block is free. */
152 work_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
153 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
154 if ((*free_ptr) == TX_BYTE_BLOCK_FREE)
155 {
156
157 /* Determine if this is the first free block. */
158 if (first_free_block_found == TX_FALSE)
159 {
160 /* This is the first free block. */
161 pool_ptr->tx_byte_pool_search = current_ptr;
162
163 /* Set the flag to indicate we have found the first free
164 block. */
165 first_free_block_found = TX_TRUE;
166 }
167
168 /* Block is free, see if it is large enough. */
169
170 /* Pickup the next block's pointer. */
171 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
172 next_ptr = *this_block_link_ptr;
173
174 /* Calculate the number of bytes available in this block. */
175 available_bytes = TX_UCHAR_POINTER_DIF(next_ptr, current_ptr);
176 available_bytes = available_bytes - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)));
177
178 /* If this is large enough, we are done because our first-fit algorithm
179 has been satisfied! */
180 if (available_bytes >= memory_size)
181 {
182 /* Get out of the search loop! */
183 break;
184 }
185 else
186 {
187
188 /* Clear the available bytes variable. */
189 available_bytes = ((ULONG) 0);
190
191 /* Not enough memory, check to see if the neighbor is
192 free and can be merged. */
193 work_ptr = TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
194 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
195 if ((*free_ptr) == TX_BYTE_BLOCK_FREE)
196 {
197
198 /* Yes, neighbor block can be merged! This is quickly accomplished
199 by updating the current block with the next blocks pointer. */
200 next_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
201 *this_block_link_ptr = *next_block_link_ptr;
202
203 /* Reduce the fragment total. We don't need to increase the bytes
204 available because all free headers are also included in the available
205 count. */
206 pool_ptr -> tx_byte_pool_fragments--;
207
208 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
209
210 /* Increment the total merge counter. */
211 _tx_byte_pool_performance_merge_count++;
212
213 /* Increment the number of blocks merged on this pool. */
214 pool_ptr -> tx_byte_pool_performance_merge_count++;
215 #endif
216
217 /* See if the search pointer is affected. */
218 if (pool_ptr -> tx_byte_pool_search == next_ptr)
219 {
220 /* Yes, update the search pointer. */
221 pool_ptr -> tx_byte_pool_search = current_ptr;
222 }
223 }
224 else
225 {
226 /* Neighbor is not free so we can skip over it! */
227 next_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
228 current_ptr = *next_block_link_ptr;
229
230 /* Decrement the examined block count to account for this one. */
231 if (examine_blocks != ((UINT) 0))
232 {
233 examine_blocks--;
234
235 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
236
237 /* Increment the total fragment search counter. */
238 _tx_byte_pool_performance_search_count++;
239
240 /* Increment the number of fragments searched on this pool. */
241 pool_ptr -> tx_byte_pool_performance_search_count++;
242 #endif
243 }
244 }
245 }
246 }
247 else
248 {
249
250 /* Block is not free, move to next block. */
251 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
252 current_ptr = *this_block_link_ptr;
253 }
254
255 /* Another block has been searched... decrement counter. */
256 if (examine_blocks != ((UINT) 0))
257 {
258
259 examine_blocks--;
260 }
261
262 #ifdef TX_BYTE_POOL_MULTIPLE_BLOCK_SEARCH
263
264 /* When this is enabled, multiple blocks are searched while holding the protection. */
265
266 /* Increment the number of blocks searched. */
267 blocks_searched = blocks_searched + ((UINT) 1);
268
269 /* Have we reached the maximum number of blocks to search while holding the protection? */
270 if (blocks_searched >= ((UINT) TX_BYTE_POOL_MULTIPLE_BLOCK_SEARCH))
271 {
272
273 /* Yes, we have exceeded the multiple block search limit. */
274
275 /* Restore interrupts temporarily. */
276 TX_RESTORE
277
278 /* Disable interrupts. */
279 TX_DISABLE
280
281 /* Reset the number of blocks searched counter. */
282 blocks_searched = ((UINT) 0);
283 }
284 #else
285 /* Restore interrupts temporarily. */
286 TX_RESTORE
287
288 /* Disable interrupts. */
289 TX_DISABLE
290 #endif
291 /* Determine if anything has changed in terms of pool ownership. */
292 if (pool_ptr -> tx_byte_pool_owner != thread_ptr)
293 {
294 /* Loop to delay changing the ownership back to avoid thrashing. */
295 delay_count = 0;
296 do
297 {
298 /* Restore interrupts temporarily. */
299 TX_RESTORE
300
301 /* Increment the delay counter. */
302 delay_count++;
303
304 /* Disable interrupts. */
305 TX_DISABLE
306 } while (delay_count < ((ULONG) TX_BYTE_POOL_DELAY_VALUE));
307 /* Pool changed ownership in the brief period interrupts were
308 enabled. Reset the search. */
309 current_ptr = pool_ptr -> tx_byte_pool_search;
310 examine_blocks = pool_ptr -> tx_byte_pool_fragments + ((UINT) 1);
311
312 /* Setup our ownership again. */
313 pool_ptr -> tx_byte_pool_owner = thread_ptr;
314 }
315 } while(examine_blocks != ((UINT) 0));
316
317 /* Determine if a block was found. If so, determine if it needs to be
318 split. */
319 if (available_bytes != ((ULONG) 0))
320 {
321
322 /* Determine if we need to split this block. */
323 if ((available_bytes - memory_size) >= ((ULONG) TX_BYTE_BLOCK_MIN))
324 {
325
326 /* Split the block. */
327 next_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (memory_size + ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
328
329 /* Setup the new free block. */
330 next_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
331 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
332 *next_block_link_ptr = *this_block_link_ptr;
333 work_ptr = TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
334 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
335 *free_ptr = TX_BYTE_BLOCK_FREE;
336
337 /* Increase the total fragment counter. */
338 pool_ptr -> tx_byte_pool_fragments++;
339
340 /* Update the current pointer to point at the newly created block. */
341 *this_block_link_ptr = next_ptr;
342
343 /* Set available equal to memory size for subsequent calculation. */
344 available_bytes = memory_size;
345
346 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
347
348 /* Increment the total split counter. */
349 _tx_byte_pool_performance_split_count++;
350
351 /* Increment the number of blocks split on this pool. */
352 pool_ptr -> tx_byte_pool_performance_split_count++;
353 #endif
354 }
355
356 /* In any case, mark the current block as allocated. */
357 work_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
358 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
359 *this_block_link_ptr = TX_BYTE_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr);
360
361 /* Reduce the number of available bytes in the pool. */
362 pool_ptr -> tx_byte_pool_available = (pool_ptr -> tx_byte_pool_available - available_bytes) - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)));
363
364 /* Determine if the search pointer needs to be updated. This is only done
365 if the search pointer matches the block to be returned. */
366 if (current_ptr == pool_ptr -> tx_byte_pool_search)
367 {
368
369 /* Yes, update the search pointer to the next block. */
370 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
371 pool_ptr -> tx_byte_pool_search = *this_block_link_ptr;
372 }
373
374 /* Restore interrupts. */
375 TX_RESTORE
376
377 /* Adjust the pointer for the application. */
378 current_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
379 }
380 else
381 {
382
383 /* Restore interrupts. */
384 TX_RESTORE
385
386 /* Set current pointer to NULL to indicate nothing was found. */
387 current_ptr = TX_NULL;
388 }
389 }
390
391 /* Return the search pointer. */
392 return(current_ptr);
393 }
394
395