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 #ifdef TX_ENABLE_EVENT_TRACE
29 #include "tx_trace.h"
30 #endif
31 #include "tx_thread.h"
32 #include "tx_byte_pool.h"
33 
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _tx_byte_allocate                                   PORTABLE C      */
40 /*                                                           6.1          */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    William E. Lamie, Microsoft Corporation                             */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function allocates bytes from the specified memory byte        */
48 /*    pool.                                                               */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    pool_ptr                          Pointer to pool control block     */
53 /*    memory_ptr                        Pointer to place allocated bytes  */
54 /*                                        pointer                         */
55 /*    memory_size                       Number of bytes to allocate       */
56 /*    wait_option                       Suspension option                 */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    status                            Completion status                 */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _tx_thread_system_suspend         Suspend thread service            */
65 /*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
66 /*    _tx_byte_pool_search              Search byte pool for memory       */
67 /*                                                                        */
68 /*  CALLED BY                                                             */
69 /*                                                                        */
70 /*    Application Code                                                    */
71 /*                                                                        */
72 /*  RELEASE HISTORY                                                       */
73 /*                                                                        */
74 /*    DATE              NAME                      DESCRIPTION             */
75 /*                                                                        */
76 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
77 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
78 /*                                            resulting in version 6.1    */
79 /*                                                                        */
80 /**************************************************************************/
_tx_byte_allocate(TX_BYTE_POOL * pool_ptr,VOID ** memory_ptr,ULONG memory_size,ULONG wait_option)81 UINT  _tx_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size,  ULONG wait_option)
82 {
83 
84 TX_INTERRUPT_SAVE_AREA
85 
86 UINT                        status;
87 TX_THREAD                   *thread_ptr;
88 UCHAR                       *work_ptr;
89 UINT                        suspended_count;
90 TX_THREAD                   *next_thread;
91 TX_THREAD                   *previous_thread;
92 UINT                        finished;
93 #ifdef TX_ENABLE_EVENT_TRACE
94 TX_TRACE_BUFFER_ENTRY       *entry_ptr;
95 ULONG                       time_stamp =  ((ULONG) 0);
96 #endif
97 #ifdef TX_ENABLE_EVENT_LOGGING
98 UCHAR                       *log_entry_ptr;
99 ULONG                       upper_tbu;
100 ULONG                       lower_tbu;
101 #endif
102 
103 
104     /* Round the memory size up to the next size that is evenly divisible by
105        an ALIGN_TYPE (this is typically a 32-bit ULONG).  This guarantees proper alignment.  */
106     memory_size = (((memory_size + (sizeof(ALIGN_TYPE)))-((ALIGN_TYPE) 1))/(sizeof(ALIGN_TYPE))) * (sizeof(ALIGN_TYPE));
107 
108     /* Disable interrupts.  */
109     TX_DISABLE
110 
111     /* Pickup thread pointer.  */
112     TX_THREAD_GET_CURRENT(thread_ptr)
113 
114 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
115 
116     /* Increment the total allocations counter.  */
117     _tx_byte_pool_performance_allocate_count++;
118 
119     /* Increment the number of allocations on this pool.  */
120     pool_ptr -> tx_byte_pool_performance_allocate_count++;
121 #endif
122 
123 #ifdef TX_ENABLE_EVENT_TRACE
124 
125     /* If trace is enabled, save the current event pointer.  */
126     entry_ptr =  _tx_trace_buffer_current_ptr;
127 
128     /* If trace is enabled, insert this event into the trace buffer.  */
129     TX_TRACE_IN_LINE_INSERT(TX_TRACE_BYTE_ALLOCATE, pool_ptr, 0, memory_size, wait_option, TX_TRACE_BYTE_POOL_EVENTS)
130 
131     /* Save the time stamp for later comparison to verify that
132        the event hasn't been overwritten by the time the allocate
133        call succeeds.  */
134     if (entry_ptr != TX_NULL)
135     {
136 
137         time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
138     }
139 #endif
140 
141 #ifdef TX_ENABLE_EVENT_LOGGING
142     log_entry_ptr =  *(UCHAR **) _tx_el_current_event;
143 
144     /* Log this kernel call.  */
145     TX_EL_BYTE_ALLOCATE_INSERT
146 
147     /* Store -1 in the fourth event slot.  */
148     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) -1;
149 
150     /* Save the time stamp for later comparison to verify that
151        the event hasn't been overwritten by the time the allocate
152        call succeeds.  */
153     lower_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET));
154     upper_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET));
155 #endif
156 
157     /* Set the search finished flag to false.  */
158     finished =  TX_FALSE;
159 
160     /* Loop to handle cases where the owner of the pool changed.  */
161     do
162     {
163 
164         /* Indicate that this thread is the current owner.  */
165         pool_ptr -> tx_byte_pool_owner =  thread_ptr;
166 
167         /* Restore interrupts.  */
168         TX_RESTORE
169 
170         /* At this point, the executing thread owns the pool and can perform a search
171            for free memory.  */
172         work_ptr =  _tx_byte_pool_search(pool_ptr, memory_size);
173 
174         /* Optional processing extension.  */
175         TX_BYTE_ALLOCATE_EXTENSION
176 
177         /* Lockout interrupts.  */
178         TX_DISABLE
179 
180         /* Determine if we are finished.  */
181         if (work_ptr != TX_NULL)
182         {
183 
184             /* Yes, we have found a block the search is finished.  */
185             finished =  TX_TRUE;
186         }
187         else
188         {
189 
190             /* No block was found, does this thread still own the pool?  */
191             if (pool_ptr -> tx_byte_pool_owner == thread_ptr)
192             {
193 
194                 /* Yes, then we have looked through the entire pool and haven't found the memory.  */
195                 finished =  TX_TRUE;
196             }
197         }
198 
199     } while (finished == TX_FALSE);
200 
201     /* Copy the pointer into the return destination.  */
202     *memory_ptr =  (VOID *) work_ptr;
203 
204     /* Determine if memory was found.  */
205     if (work_ptr != TX_NULL)
206     {
207 
208 #ifdef TX_ENABLE_EVENT_TRACE
209 
210         /* Check that the event time stamp is unchanged.  A different
211            timestamp means that a later event wrote over the byte
212            allocate event.  In that case, do nothing here.  */
213         if (entry_ptr != TX_NULL)
214         {
215 
216             /* Is the timestamp the same?  */
217             if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
218             {
219 
220                 /* Timestamp is the same, update the entry with the address.  */
221 #ifdef TX_MISRA_ENABLE
222                 entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
223 #else
224                 entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
225 #endif
226             }
227         }
228 #endif
229 
230 #ifdef TX_ENABLE_EVENT_LOGGING
231         /* Check that the event time stamp is unchanged.  A different
232            timestamp means that a later event wrote over the byte
233            allocate event.  In that case, do nothing here.  */
234         if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
235             upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)))
236         {
237             /* Store the address of the allocated fragment.  */
238             *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) *memory_ptr;
239         }
240 #endif
241 
242         /* Restore interrupts.  */
243         TX_RESTORE
244 
245         /* Set the status to success.  */
246         status =  TX_SUCCESS;
247     }
248     else
249     {
250 
251         /* No memory of sufficient size was found...  */
252 
253         /* Determine if the request specifies suspension.  */
254         if (wait_option != TX_NO_WAIT)
255         {
256 
257             /* Determine if the preempt disable flag is non-zero.  */
258             if (_tx_thread_preempt_disable != ((UINT) 0))
259             {
260 
261                 /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion.  */
262                 status =  TX_NO_MEMORY;
263 
264                 /* Restore interrupts.  */
265                 TX_RESTORE
266             }
267             else
268             {
269 
270                 /* Prepare for suspension of this thread.  */
271 
272 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
273 
274                 /* Increment the total suspensions counter.  */
275                 _tx_byte_pool_performance_suspension_count++;
276 
277                 /* Increment the number of suspensions on this pool.  */
278                 pool_ptr -> tx_byte_pool_performance_suspension_count++;
279 #endif
280 
281                 /* Setup cleanup routine pointer.  */
282                 thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_byte_pool_cleanup);
283 
284                 /* Setup cleanup information, i.e. this pool control
285                    block.  */
286                 thread_ptr -> tx_thread_suspend_control_block =  (VOID *) pool_ptr;
287 
288                 /* Save the return memory pointer address as well.  */
289                 thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) memory_ptr;
290 
291                 /* Save the byte size requested.  */
292                 thread_ptr -> tx_thread_suspend_info =  memory_size;
293 
294 #ifndef TX_NOT_INTERRUPTABLE
295 
296                 /* Increment the suspension sequence number, which is used to identify
297                    this suspension event.  */
298                 thread_ptr -> tx_thread_suspension_sequence++;
299 #endif
300 
301                 /* Pickup the number of suspended threads.  */
302                 suspended_count =  pool_ptr -> tx_byte_pool_suspended_count;
303 
304                 /* Increment the suspension count.  */
305                 (pool_ptr -> tx_byte_pool_suspended_count)++;
306 
307                 /* Setup suspension list.  */
308                 if (suspended_count == TX_NO_SUSPENSIONS)
309                 {
310 
311                     /* No other threads are suspended.  Setup the head pointer and
312                        just setup this threads pointers to itself.  */
313                     pool_ptr -> tx_byte_pool_suspension_list =      thread_ptr;
314                     thread_ptr -> tx_thread_suspended_next =        thread_ptr;
315                     thread_ptr -> tx_thread_suspended_previous =    thread_ptr;
316                 }
317                 else
318                 {
319 
320                     /* This list is not NULL, add current thread to the end. */
321                     next_thread =                                   pool_ptr -> tx_byte_pool_suspension_list;
322                     thread_ptr -> tx_thread_suspended_next =        next_thread;
323                     previous_thread =                               next_thread -> tx_thread_suspended_previous;
324                     thread_ptr -> tx_thread_suspended_previous =    previous_thread;
325                     previous_thread -> tx_thread_suspended_next =   thread_ptr;
326                     next_thread -> tx_thread_suspended_previous =   thread_ptr;
327                 }
328 
329                 /* Set the state to suspended.  */
330                 thread_ptr -> tx_thread_state =       TX_BYTE_MEMORY;
331 
332 #ifdef TX_NOT_INTERRUPTABLE
333 
334                 /* Call actual non-interruptable thread suspension routine.  */
335                 _tx_thread_system_ni_suspend(thread_ptr, wait_option);
336 
337                 /* Restore interrupts.  */
338                 TX_RESTORE
339 #else
340 
341                 /* Set the suspending flag.  */
342                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
343 
344                 /* Setup the timeout period.  */
345                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
346 
347                 /* Temporarily disable preemption.  */
348                 _tx_thread_preempt_disable++;
349 
350                 /* Restore interrupts.  */
351                 TX_RESTORE
352 
353                 /* Call actual thread suspension routine.  */
354                 _tx_thread_system_suspend(thread_ptr);
355 #endif
356 
357 #ifdef TX_ENABLE_EVENT_TRACE
358 
359                 /* Check that the event time stamp is unchanged.  A different
360                    timestamp means that a later event wrote over the byte
361                    allocate event.  In that case, do nothing here.  */
362                 if (entry_ptr != TX_NULL)
363                 {
364 
365                     /* Is the timestamp the same?  */
366                     if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
367                     {
368 
369                         /* Timestamp is the same, update the entry with the address.  */
370 #ifdef TX_MISRA_ENABLE
371                         entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
372 #else
373                        entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
374 #endif
375                     }
376                 }
377 #endif
378 
379 #ifdef TX_ENABLE_EVENT_LOGGING
380                 /* Check that the event time stamp is unchanged.  A different
381                    timestamp means that a later event wrote over the byte
382                    allocate event.  In that case, do nothing here.  */
383                 if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
384                     upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)))
385                 {
386 
387                     /* Store the address of the allocated fragment.  */
388                     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) *memory_ptr;
389                 }
390 #endif
391 
392                 /* Return the completion status.  */
393                 status =  thread_ptr -> tx_thread_suspend_status;
394             }
395         }
396         else
397         {
398 
399             /* Restore interrupts.  */
400             TX_RESTORE
401 
402             /* Immediate return, return error completion.  */
403             status =  TX_NO_MEMORY;
404         }
405     }
406 
407     /* Return completion status.  */
408     return(status);
409 }
410 
411