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