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 Memory                                                         */
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_trace.h"
29 #include "tx_thread.h"
30 #include "tx_byte_pool.h"
31 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _tx_byte_release                                    PORTABLE C      */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    William E. Lamie, Microsoft Corporation                             */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function returns previously allocated memory to its            */
46 /*    associated memory byte pool.                                        */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    memory_ptr                        Pointer to allocated memory       */
51 /*                                                                        */
52 /*  OUTPUT                                                                */
53 /*                                                                        */
54 /*    [TX_PTR_ERROR | TX_SUCCESS]       Completion status                 */
55 /*                                                                        */
56 /*  CALLS                                                                 */
57 /*                                                                        */
58 /*    _tx_thread_system_preempt_check   Check for preemption              */
59 /*    _tx_thread_system_resume          Resume thread service             */
60 /*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
61 /*    _tx_byte_pool_search              Search the byte pool for memory   */
62 /*                                                                        */
63 /*  CALLED BY                                                             */
64 /*                                                                        */
65 /*    Application Code                                                    */
66 /*                                                                        */
67 /*  RELEASE HISTORY                                                       */
68 /*                                                                        */
69 /*    DATE              NAME                      DESCRIPTION             */
70 /*                                                                        */
71 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
72 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
73 /*                                            resulting in version 6.1    */
74 /*                                                                        */
75 /**************************************************************************/
_tx_byte_release(VOID * memory_ptr)76 UINT  _tx_byte_release(VOID *memory_ptr)
77 {
78 
79 TX_INTERRUPT_SAVE_AREA
80 
81 UINT                status;
82 TX_BYTE_POOL        *pool_ptr;
83 TX_THREAD           *thread_ptr;
84 UCHAR               *work_ptr;
85 UCHAR               *temp_ptr;
86 UCHAR               *next_block_ptr;
87 TX_THREAD           *susp_thread_ptr;
88 UINT                suspended_count;
89 TX_THREAD           *next_thread;
90 TX_THREAD           *previous_thread;
91 ULONG               memory_size;
92 ALIGN_TYPE          *free_ptr;
93 TX_BYTE_POOL        **byte_pool_ptr;
94 UCHAR               **block_link_ptr;
95 UCHAR               **suspend_info_ptr;
96 
97 
98     /* Default to successful status.  */
99     status =  TX_SUCCESS;
100 
101     /* Set the pool pointer to NULL.  */
102     pool_ptr =  TX_NULL;
103 
104     /* Lockout interrupts.  */
105     TX_DISABLE
106 
107     /* Determine if the memory pointer is valid.  */
108     work_ptr =  TX_VOID_TO_UCHAR_POINTER_CONVERT(memory_ptr);
109     if (work_ptr != TX_NULL)
110     {
111 
112         /* Back off the memory pointer to pickup its header.  */
113         work_ptr =  TX_UCHAR_POINTER_SUB(work_ptr, ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))));
114 
115         /* There is a pointer, pickup the pool pointer address.  */
116         temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
117         free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
118         if ((*free_ptr) != TX_BYTE_BLOCK_FREE)
119         {
120 
121             /* Pickup the pool pointer.  */
122             temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
123             byte_pool_ptr =  TX_UCHAR_TO_INDIRECT_BYTE_POOL_POINTER(temp_ptr);
124             pool_ptr =  *byte_pool_ptr;
125 
126             /* See if we have a valid pool pointer.  */
127             if (pool_ptr == TX_NULL)
128             {
129 
130                 /* Return pointer error.  */
131                 status =  TX_PTR_ERROR;
132             }
133             else
134             {
135 
136                 /* See if we have a valid pool.  */
137                 if (pool_ptr -> tx_byte_pool_id != TX_BYTE_POOL_ID)
138                 {
139 
140                     /* Return pointer error.  */
141                     status =  TX_PTR_ERROR;
142 
143                     /* Reset the pool pointer is NULL.  */
144                     pool_ptr =  TX_NULL;
145                 }
146             }
147         }
148         else
149         {
150 
151             /* Return pointer error.  */
152             status =  TX_PTR_ERROR;
153         }
154     }
155     else
156     {
157 
158         /* Return pointer error.  */
159         status =  TX_PTR_ERROR;
160     }
161 
162     /* Determine if the pointer is valid.  */
163     if (pool_ptr == TX_NULL)
164     {
165 
166         /* Restore interrupts.  */
167         TX_RESTORE
168     }
169     else
170     {
171 
172         /* At this point, we know that the pointer is valid.  */
173 
174         /* Pickup thread pointer.  */
175         TX_THREAD_GET_CURRENT(thread_ptr)
176 
177         /* Indicate that this thread is the current owner.  */
178         pool_ptr -> tx_byte_pool_owner =  thread_ptr;
179 
180 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
181 
182         /* Increment the total release counter.  */
183         _tx_byte_pool_performance_release_count++;
184 
185         /* Increment the number of releases on this pool.  */
186         pool_ptr -> tx_byte_pool_performance_release_count++;
187 #endif
188 
189         /* If trace is enabled, insert this event into the trace buffer.  */
190         TX_TRACE_IN_LINE_INSERT(TX_TRACE_BYTE_RELEASE, pool_ptr, TX_POINTER_TO_ULONG_CONVERT(memory_ptr), pool_ptr -> tx_byte_pool_suspended_count, pool_ptr -> tx_byte_pool_available, TX_TRACE_BYTE_POOL_EVENTS)
191 
192         /* Log this kernel call.  */
193         TX_EL_BYTE_RELEASE_INSERT
194 
195         /* Release the memory.  */
196         temp_ptr =   TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
197         free_ptr =   TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
198         *free_ptr =  TX_BYTE_BLOCK_FREE;
199 
200         /* Update the number of available bytes in the pool.  */
201         block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
202         next_block_ptr =  *block_link_ptr;
203         pool_ptr -> tx_byte_pool_available =
204             pool_ptr -> tx_byte_pool_available + TX_UCHAR_POINTER_DIF(next_block_ptr, work_ptr);
205 
206         /* Determine if the free block is prior to current search pointer.  */
207         if (work_ptr < (pool_ptr -> tx_byte_pool_search))
208         {
209 
210             /* Yes, update the search pointer to the released block.  */
211             pool_ptr -> tx_byte_pool_search =  work_ptr;
212         }
213 
214         /* Determine if there are threads suspended on this byte pool.  */
215         if (pool_ptr -> tx_byte_pool_suspended_count != TX_NO_SUSPENSIONS)
216         {
217 
218             /* Now examine the suspension list to find threads waiting for
219                memory.  Maybe it is now available!  */
220             while (pool_ptr -> tx_byte_pool_suspended_count != TX_NO_SUSPENSIONS)
221             {
222 
223                 /* Pickup the first suspended thread pointer.  */
224                 susp_thread_ptr =  pool_ptr -> tx_byte_pool_suspension_list;
225 
226                 /* Pickup the size of the memory the thread is requesting.  */
227                 memory_size =  susp_thread_ptr -> tx_thread_suspend_info;
228 
229                 /* Restore interrupts.  */
230                 TX_RESTORE
231 
232                 /* See if the request can be satisfied.  */
233                 work_ptr =  _tx_byte_pool_search(pool_ptr, memory_size);
234 
235                 /* Optional processing extension.  */
236                 TX_BYTE_RELEASE_EXTENSION
237 
238                 /* Disable interrupts.  */
239                 TX_DISABLE
240 
241                 /* Indicate that this thread is the current owner.  */
242                 pool_ptr -> tx_byte_pool_owner =  thread_ptr;
243 
244                 /* If there is not enough memory, break this loop!  */
245                 if (work_ptr == TX_NULL)
246                 {
247 
248                   /* Break out of the loop.  */
249                     break;
250                 }
251 
252                 /* Check to make sure the thread is still suspended.  */
253                 if (susp_thread_ptr ==  pool_ptr -> tx_byte_pool_suspension_list)
254                 {
255 
256                     /* Also, makes sure the memory size is the same.  */
257                     if (susp_thread_ptr -> tx_thread_suspend_info == memory_size)
258                     {
259 
260                         /* Remove the suspended thread from the list.  */
261 
262                         /* Decrement the number of threads suspended.  */
263                         pool_ptr -> tx_byte_pool_suspended_count--;
264 
265                         /* Pickup the suspended count.  */
266                         suspended_count =  pool_ptr -> tx_byte_pool_suspended_count;
267 
268                         /* See if this is the only suspended thread on the list.  */
269                         if (suspended_count == TX_NO_SUSPENSIONS)
270                         {
271 
272                             /* Yes, the only suspended thread.  */
273 
274                             /* Update the head pointer.  */
275                             pool_ptr -> tx_byte_pool_suspension_list =  TX_NULL;
276                         }
277                         else
278                         {
279 
280                             /* At least one more thread is on the same expiration list.  */
281 
282                             /* Update the list head pointer.  */
283                             next_thread =                                susp_thread_ptr -> tx_thread_suspended_next;
284                             pool_ptr -> tx_byte_pool_suspension_list =   next_thread;
285 
286                             /* Update the links of the adjacent threads.  */
287                             previous_thread =                              susp_thread_ptr -> tx_thread_suspended_previous;
288                             next_thread -> tx_thread_suspended_previous =  previous_thread;
289                             previous_thread -> tx_thread_suspended_next =  next_thread;
290                         }
291 
292                         /* Prepare for resumption of the thread.  */
293 
294                         /* Clear cleanup routine to avoid timeout.  */
295                         susp_thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
296 
297                         /* Return this block pointer to the suspended thread waiting for
298                            a block.  */
299                         suspend_info_ptr =   TX_VOID_TO_INDIRECT_UCHAR_POINTER_CONVERT(susp_thread_ptr -> tx_thread_additional_suspend_info);
300                         *suspend_info_ptr =  work_ptr;
301 
302                         /* Clear the memory pointer to indicate that it was given to the suspended thread.  */
303                         work_ptr =  TX_NULL;
304 
305                         /* Put return status into the thread control block.  */
306                         susp_thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
307 
308 #ifdef TX_NOT_INTERRUPTABLE
309 
310                         /* Resume the thread!  */
311                         _tx_thread_system_ni_resume(susp_thread_ptr);
312 
313                         /* Restore interrupts.  */
314                         TX_RESTORE
315 #else
316                         /* Temporarily disable preemption.  */
317                         _tx_thread_preempt_disable++;
318 
319                         /* Restore interrupts.  */
320                         TX_RESTORE
321 
322                         /* Resume thread.  */
323                         _tx_thread_system_resume(susp_thread_ptr);
324 #endif
325 
326                         /* Lockout interrupts.  */
327                         TX_DISABLE
328                     }
329                 }
330 
331                 /* Determine if the memory was given to the suspended thread.  */
332                 if (work_ptr != TX_NULL)
333                 {
334 
335                     /* No, it wasn't given to the suspended thread.  */
336 
337                     /* Put the memory back on the available list since this thread is no longer
338                        suspended.  */
339                     work_ptr =  TX_UCHAR_POINTER_SUB(work_ptr, (((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
340                     temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
341                     free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
342                     *free_ptr =  TX_BYTE_BLOCK_FREE;
343 
344                     /* Update the number of available bytes in the pool.  */
345                     block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
346                     next_block_ptr =  *block_link_ptr;
347                     pool_ptr -> tx_byte_pool_available =
348                         pool_ptr -> tx_byte_pool_available + TX_UCHAR_POINTER_DIF(next_block_ptr, work_ptr);
349 
350                     /* Determine if the current pointer is before the search pointer.  */
351                     if (work_ptr < (pool_ptr -> tx_byte_pool_search))
352                     {
353 
354                         /* Yes, update the search pointer.  */
355                         pool_ptr -> tx_byte_pool_search =  work_ptr;
356                     }
357                 }
358             }
359 
360             /* Restore interrupts.  */
361             TX_RESTORE
362 
363             /* Check for preemption.  */
364             _tx_thread_system_preempt_check();
365         }
366         else
367         {
368 
369             /* No, threads suspended, restore interrupts.  */
370             TX_RESTORE
371         }
372     }
373 
374     /* Return completion status.  */
375     return(status);
376 }
377 
378