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