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.1.7 */
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 /* */
85 /**************************************************************************/
_tx_byte_pool_search(TX_BYTE_POOL * pool_ptr,ULONG memory_size)86 UCHAR *_tx_byte_pool_search(TX_BYTE_POOL *pool_ptr, ULONG memory_size)
87 {
88
89 TX_INTERRUPT_SAVE_AREA
90
91 UCHAR *current_ptr;
92 UCHAR *next_ptr;
93 UCHAR **this_block_link_ptr;
94 UCHAR **next_block_link_ptr;
95 ULONG available_bytes;
96 UINT examine_blocks;
97 UINT first_free_block_found = TX_FALSE;
98 TX_THREAD *thread_ptr;
99 ALIGN_TYPE *free_ptr;
100 UCHAR *work_ptr;
101 volatile ULONG delay_count;
102 ULONG total_theoretical_available;
103 #ifdef TX_BYTE_POOL_MULTIPLE_BLOCK_SEARCH
104 UINT blocks_searched = ((UINT) 0);
105 #endif
106
107
108 /* Disable interrupts. */
109 TX_DISABLE
110
111 /* First, determine if there are enough bytes in the pool. */
112 /* Theoretical bytes available = free bytes + ((fragments-2) * overhead of each block) */
113 total_theoretical_available = pool_ptr -> tx_byte_pool_available + ((pool_ptr -> tx_byte_pool_fragments - 2) * ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))));
114 if (memory_size >= total_theoretical_available)
115 {
116
117 /* Restore interrupts. */
118 TX_RESTORE
119
120 /* Not enough memory, return a NULL pointer. */
121 current_ptr = TX_NULL;
122 }
123 else
124 {
125
126 /* Pickup thread pointer. */
127 TX_THREAD_GET_CURRENT(thread_ptr)
128
129 /* Setup ownership of the byte pool. */
130 pool_ptr -> tx_byte_pool_owner = thread_ptr;
131
132 /* Walk through the memory pool in search for a large enough block. */
133 current_ptr = pool_ptr -> tx_byte_pool_search;
134 examine_blocks = pool_ptr -> tx_byte_pool_fragments + ((UINT) 1);
135 available_bytes = ((ULONG) 0);
136 do
137 {
138
139
140 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
141
142 /* Increment the total fragment search counter. */
143 _tx_byte_pool_performance_search_count++;
144
145 /* Increment the number of fragments searched on this pool. */
146 pool_ptr -> tx_byte_pool_performance_search_count++;
147 #endif
148
149 /* Check to see if this block is free. */
150 work_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
151 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
152 if ((*free_ptr) == TX_BYTE_BLOCK_FREE)
153 {
154
155 /* Determine if this is the first free block. */
156 if (first_free_block_found == TX_FALSE)
157 {
158 /* This is the first free block. */
159 pool_ptr->tx_byte_pool_search = current_ptr;
160
161 /* Set the flag to indicate we have found the first free
162 block. */
163 first_free_block_found = TX_TRUE;
164 }
165
166 /* Block is free, see if it is large enough. */
167
168 /* Pickup the next block's pointer. */
169 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
170 next_ptr = *this_block_link_ptr;
171
172 /* Calculate the number of bytes available in this block. */
173 available_bytes = TX_UCHAR_POINTER_DIF(next_ptr, current_ptr);
174 available_bytes = available_bytes - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)));
175
176 /* If this is large enough, we are done because our first-fit algorithm
177 has been satisfied! */
178 if (available_bytes >= memory_size)
179 {
180 /* Get out of the search loop! */
181 break;
182 }
183 else
184 {
185
186 /* Clear the available bytes variable. */
187 available_bytes = ((ULONG) 0);
188
189 /* Not enough memory, check to see if the neighbor is
190 free and can be merged. */
191 work_ptr = TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
192 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
193 if ((*free_ptr) == TX_BYTE_BLOCK_FREE)
194 {
195
196 /* Yes, neighbor block can be merged! This is quickly accomplished
197 by updating the current block with the next blocks pointer. */
198 next_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
199 *this_block_link_ptr = *next_block_link_ptr;
200
201 /* Reduce the fragment total. We don't need to increase the bytes
202 available because all free headers are also included in the available
203 count. */
204 pool_ptr -> tx_byte_pool_fragments--;
205
206 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
207
208 /* Increment the total merge counter. */
209 _tx_byte_pool_performance_merge_count++;
210
211 /* Increment the number of blocks merged on this pool. */
212 pool_ptr -> tx_byte_pool_performance_merge_count++;
213 #endif
214
215 /* See if the search pointer is affected. */
216 if (pool_ptr -> tx_byte_pool_search == next_ptr)
217 {
218 /* Yes, update the search pointer. */
219 pool_ptr -> tx_byte_pool_search = current_ptr;
220 }
221 }
222 else
223 {
224 /* Neighbor is not free so we can skip over it! */
225 next_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
226 current_ptr = *next_block_link_ptr;
227
228 /* Decrement the examined block count to account for this one. */
229 if (examine_blocks != ((UINT) 0))
230 {
231 examine_blocks--;
232
233 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
234
235 /* Increment the total fragment search counter. */
236 _tx_byte_pool_performance_search_count++;
237
238 /* Increment the number of fragments searched on this pool. */
239 pool_ptr -> tx_byte_pool_performance_search_count++;
240 #endif
241 }
242 }
243 }
244 }
245 else
246 {
247
248 /* Block is not free, move to next block. */
249 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
250 current_ptr = *this_block_link_ptr;
251 }
252
253 /* Another block has been searched... decrement counter. */
254 if (examine_blocks != ((UINT) 0))
255 {
256
257 examine_blocks--;
258 }
259
260 #ifdef TX_BYTE_POOL_MULTIPLE_BLOCK_SEARCH
261
262 /* When this is enabled, multiple blocks are searched while holding the protection. */
263
264 /* Increment the number of blocks searched. */
265 blocks_searched = blocks_searched + ((UINT) 1);
266
267 /* Have we reached the maximum number of blocks to search while holding the protection? */
268 if (blocks_searched >= ((UINT) TX_BYTE_POOL_MULTIPLE_BLOCK_SEARCH))
269 {
270
271 /* Yes, we have exceeded the multiple block search limit. */
272
273 /* Restore interrupts temporarily. */
274 TX_RESTORE
275
276 /* Disable interrupts. */
277 TX_DISABLE
278
279 /* Reset the number of blocks searched counter. */
280 blocks_searched = ((UINT) 0);
281 }
282 #else
283 /* Restore interrupts temporarily. */
284 TX_RESTORE
285
286 /* Disable interrupts. */
287 TX_DISABLE
288 #endif
289 /* Determine if anything has changed in terms of pool ownership. */
290 if (pool_ptr -> tx_byte_pool_owner != thread_ptr)
291 {
292 /* Loop to delay changing the ownership back to avoid thrashing. */
293 delay_count = 0;
294 do
295 {
296 /* Restore interrupts temporarily. */
297 TX_RESTORE
298
299 /* Increment the delay counter. */
300 delay_count++;
301
302 /* Disable interrupts. */
303 TX_DISABLE
304 } while (delay_count < ((ULONG) TX_BYTE_POOL_DELAY_VALUE));
305 /* Pool changed ownership in the brief period interrupts were
306 enabled. Reset the search. */
307 current_ptr = pool_ptr -> tx_byte_pool_search;
308 examine_blocks = pool_ptr -> tx_byte_pool_fragments + ((UINT) 1);
309
310 /* Setup our ownership again. */
311 pool_ptr -> tx_byte_pool_owner = thread_ptr;
312 }
313 } while(examine_blocks != ((UINT) 0));
314
315 /* Determine if a block was found. If so, determine if it needs to be
316 split. */
317 if (available_bytes != ((ULONG) 0))
318 {
319
320 /* Determine if we need to split this block. */
321 if ((available_bytes - memory_size) >= ((ULONG) TX_BYTE_BLOCK_MIN))
322 {
323
324 /* Split the block. */
325 next_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (memory_size + ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
326
327 /* Setup the new free block. */
328 next_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
329 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
330 *next_block_link_ptr = *this_block_link_ptr;
331 work_ptr = TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *)));
332 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
333 *free_ptr = TX_BYTE_BLOCK_FREE;
334
335 /* Increase the total fragment counter. */
336 pool_ptr -> tx_byte_pool_fragments++;
337
338 /* Update the current pointer to point at the newly created block. */
339 *this_block_link_ptr = next_ptr;
340
341 /* Set available equal to memory size for subsequent calculation. */
342 available_bytes = memory_size;
343
344 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
345
346 /* Increment the total split counter. */
347 _tx_byte_pool_performance_split_count++;
348
349 /* Increment the number of blocks split on this pool. */
350 pool_ptr -> tx_byte_pool_performance_split_count++;
351 #endif
352 }
353
354 /* In any case, mark the current block as allocated. */
355 work_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
356 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
357 *this_block_link_ptr = TX_BYTE_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr);
358
359 /* Reduce the number of available bytes in the pool. */
360 pool_ptr -> tx_byte_pool_available = (pool_ptr -> tx_byte_pool_available - available_bytes) - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)));
361
362 /* Determine if the search pointer needs to be updated. This is only done
363 if the search pointer matches the block to be returned. */
364 if (current_ptr == pool_ptr -> tx_byte_pool_search)
365 {
366
367 /* Yes, update the search pointer to the next block. */
368 this_block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
369 pool_ptr -> tx_byte_pool_search = *this_block_link_ptr;
370 }
371
372 /* Restore interrupts. */
373 TX_RESTORE
374
375 /* Adjust the pointer for the application. */
376 current_ptr = TX_UCHAR_POINTER_ADD(current_ptr, (((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
377 }
378 else
379 {
380
381 /* Restore interrupts. */
382 TX_RESTORE
383
384 /* Set current pointer to NULL to indicate nothing was found. */
385 current_ptr = TX_NULL;
386 }
387 }
388
389 /* Return the search pointer. */
390 return(current_ptr);
391 }
392
393