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 /**   Block Pool                                                          */
19 /**                                                                       */
20 /**************************************************************************/
21 /**************************************************************************/
22 
23 #define TX_SOURCE_CODE
24 
25 
26 /* Include necessary system files.  */
27 
28 #include "tx_api.h"
29 #ifdef TX_ENABLE_EVENT_TRACE
30 #include "tx_trace.h"
31 #endif
32 #include "tx_thread.h"
33 #include "tx_block_pool.h"
34 
35 
36 /**************************************************************************/
37 /*                                                                        */
38 /*  FUNCTION                                               RELEASE        */
39 /*                                                                        */
40 /*    _tx_block_allocate                                  PORTABLE C      */
41 /*                                                           6.1          */
42 /*  AUTHOR                                                                */
43 /*                                                                        */
44 /*    William E. Lamie, Microsoft Corporation                             */
45 /*                                                                        */
46 /*  DESCRIPTION                                                           */
47 /*                                                                        */
48 /*    This function allocates a block from the specified memory block     */
49 /*    pool.                                                               */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    pool_ptr                          Pointer to pool control block     */
54 /*    block_ptr                         Pointer to place allocated block  */
55 /*                                        pointer                         */
56 /*    wait_option                       Suspension option                 */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    status                            Completion status                 */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _tx_thread_system_suspend         Suspend thread                    */
65 /*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
66 /*                                                                        */
67 /*  CALLED BY                                                             */
68 /*                                                                        */
69 /*    Application Code                                                    */
70 /*                                                                        */
71 /*  RELEASE HISTORY                                                       */
72 /*                                                                        */
73 /*    DATE              NAME                      DESCRIPTION             */
74 /*                                                                        */
75 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
76 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
77 /*                                            resulting in version 6.1    */
78 /*                                                                        */
79 /**************************************************************************/
_tx_block_allocate(TX_BLOCK_POOL * pool_ptr,VOID ** block_ptr,ULONG wait_option)80 UINT  _tx_block_allocate(TX_BLOCK_POOL *pool_ptr, VOID **block_ptr, ULONG wait_option)
81 {
82 
83 TX_INTERRUPT_SAVE_AREA
84 
85 UINT                        status;
86 TX_THREAD                   *thread_ptr;
87 UCHAR                       *work_ptr;
88 UCHAR                       *temp_ptr;
89 UCHAR                       **next_block_ptr;
90 UCHAR                       **return_ptr;
91 UINT                        suspended_count;
92 TX_THREAD                   *next_thread;
93 TX_THREAD                   *previous_thread;
94 #ifdef TX_ENABLE_EVENT_TRACE
95 TX_TRACE_BUFFER_ENTRY       *entry_ptr;
96 ULONG                       time_stamp =  ((ULONG) 0);
97 #endif
98 #ifdef TX_ENABLE_EVENT_LOGGING
99 UCHAR                       *log_entry_ptr;
100 ULONG                       upper_tbu;
101 ULONG                       lower_tbu;
102 #endif
103 
104 
105     /* Disable interrupts to get a block from the pool.  */
106     TX_DISABLE
107 
108 #ifdef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
109 
110     /* Increment the total allocations counter.  */
111     _tx_block_pool_performance_allocate_count++;
112 
113     /* Increment the number of allocations on this pool.  */
114     pool_ptr -> tx_block_pool_performance_allocate_count++;
115 #endif
116 
117 #ifdef TX_ENABLE_EVENT_TRACE
118 
119     /* If trace is enabled, save the current event pointer.  */
120     entry_ptr =  _tx_trace_buffer_current_ptr;
121 
122     /* If trace is enabled, insert this event into the trace buffer.  */
123     TX_TRACE_IN_LINE_INSERT(TX_TRACE_BLOCK_ALLOCATE, pool_ptr, 0, wait_option, pool_ptr -> tx_block_pool_available, TX_TRACE_BLOCK_POOL_EVENTS)
124 
125     /* Save the time stamp for later comparison to verify that
126        the event hasn't been overwritten by the time the allocate
127        call succeeds.  */
128     if (entry_ptr != TX_NULL)
129     {
130 
131         time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
132     }
133 #endif
134 
135 #ifdef TX_ENABLE_EVENT_LOGGING
136     log_entry_ptr =  *(UCHAR **) _tx_el_current_event;
137 
138     /* Log this kernel call.  */
139     TX_EL_BLOCK_ALLOCATE_INSERT
140 
141     /* Store -1 in the third event slot.  */
142     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =  (ULONG) -1;
143 
144     /* Save the time stamp for later comparison to verify that
145        the event hasn't been overwritten by the time the allocate
146        call succeeds.  */
147     lower_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET));
148     upper_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET));
149 #endif
150 
151     /* Determine if there is an available block.  */
152     if (pool_ptr -> tx_block_pool_available != ((UINT) 0))
153     {
154 
155         /* Yes, a block is available.  Decrement the available count.  */
156         pool_ptr -> tx_block_pool_available--;
157 
158         /* Pickup the current block pointer.  */
159         work_ptr =  pool_ptr -> tx_block_pool_available_list;
160 
161         /* Return the first available block to the caller.  */
162         temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
163         return_ptr =  TX_INDIRECT_VOID_TO_UCHAR_POINTER_CONVERT(block_ptr);
164         *return_ptr =  temp_ptr;
165 
166         /* Modify the available list to point at the next block in the pool. */
167         next_block_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
168         pool_ptr -> tx_block_pool_available_list =  *next_block_ptr;
169 
170         /* Save the pool's address in the block for when it is released!  */
171         temp_ptr =  TX_BLOCK_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr);
172         *next_block_ptr =  temp_ptr;
173 
174 #ifdef TX_ENABLE_EVENT_TRACE
175 
176         /* Check that the event time stamp is unchanged.  A different
177            timestamp means that a later event wrote over the byte
178            allocate event.  In that case, do nothing here.  */
179         if (entry_ptr != TX_NULL)
180         {
181 
182             /* Is the time stamp the same?  */
183             if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
184             {
185 
186                 /* Timestamp is the same, update the entry with the address.  */
187 #ifdef TX_MISRA_ENABLE
188                 entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
189 #else
190                 entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
191 #endif
192             }
193         }
194 #endif
195 
196 #ifdef TX_ENABLE_EVENT_LOGGING
197         /* Store the address of the allocated block.  */
198         *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =  (ULONG) *block_ptr;
199 #endif
200 
201         /* Set status to success.  */
202         status =  TX_SUCCESS;
203 
204         /* Restore interrupts.  */
205         TX_RESTORE
206     }
207     else
208     {
209 
210         /* Default the return pointer to NULL.  */
211         return_ptr =   TX_INDIRECT_VOID_TO_UCHAR_POINTER_CONVERT(block_ptr);
212         *return_ptr =  TX_NULL;
213 
214         /* Determine if the request specifies suspension.  */
215         if (wait_option != TX_NO_WAIT)
216         {
217 
218             /* Determine if the preempt disable flag is non-zero.  */
219             if (_tx_thread_preempt_disable != ((UINT) 0))
220             {
221 
222                 /* Suspension is not allowed if the preempt disable flag is non-zero at this point, return error completion.  */
223                 status =  TX_NO_MEMORY;
224 
225                 /* Restore interrupts.  */
226                 TX_RESTORE
227             }
228             else
229             {
230 
231                 /* Prepare for suspension of this thread.  */
232 
233 #ifdef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
234 
235                 /* Increment the total suspensions counter.  */
236                 _tx_block_pool_performance_suspension_count++;
237 
238                 /* Increment the number of suspensions on this pool.  */
239                 pool_ptr -> tx_block_pool_performance_suspension_count++;
240 #endif
241 
242                 /* Pickup thread pointer.  */
243                 TX_THREAD_GET_CURRENT(thread_ptr)
244 
245                 /* Setup cleanup routine pointer.  */
246                 thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_block_pool_cleanup);
247 
248                 /* Setup cleanup information, i.e. this pool control
249                    block.  */
250                 thread_ptr -> tx_thread_suspend_control_block =  (VOID *) pool_ptr;
251 
252                 /* Save the return block pointer address as well.  */
253                 thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) block_ptr;
254 
255 #ifndef TX_NOT_INTERRUPTABLE
256 
257                 /* Increment the suspension sequence number, which is used to identify
258                    this suspension event.  */
259                 thread_ptr -> tx_thread_suspension_sequence++;
260 #endif
261 
262                 /* Pickup the number of suspended threads.  */
263                 suspended_count =  (pool_ptr -> tx_block_pool_suspended_count);
264 
265                 /* Increment the number of suspended threads.  */
266                 (pool_ptr -> tx_block_pool_suspended_count)++;
267 
268                 /* Setup suspension list.  */
269                 if (suspended_count == TX_NO_SUSPENSIONS)
270                 {
271 
272                     /* No other threads are suspended.  Setup the head pointer and
273                        just setup this threads pointers to itself.  */
274                     pool_ptr -> tx_block_pool_suspension_list =     thread_ptr;
275                     thread_ptr -> tx_thread_suspended_next =        thread_ptr;
276                     thread_ptr -> tx_thread_suspended_previous =    thread_ptr;
277                 }
278                 else
279                 {
280 
281                     /* This list is not NULL, add current thread to the end. */
282                     next_thread =                                   pool_ptr -> tx_block_pool_suspension_list;
283                     thread_ptr -> tx_thread_suspended_next =        next_thread;
284                     previous_thread =                               next_thread -> tx_thread_suspended_previous;
285                     thread_ptr -> tx_thread_suspended_previous =    previous_thread;
286                     previous_thread -> tx_thread_suspended_next =   thread_ptr;
287                     next_thread -> tx_thread_suspended_previous =   thread_ptr;
288                 }
289 
290                 /* Set the state to suspended.  */
291                 thread_ptr -> tx_thread_state =       TX_BLOCK_MEMORY;
292 
293 #ifdef TX_NOT_INTERRUPTABLE
294 
295                 /* Call actual non-interruptable thread suspension routine.  */
296                 _tx_thread_system_ni_suspend(thread_ptr, wait_option);
297 
298                 /* Restore interrupts.  */
299                 TX_RESTORE
300 #else
301 
302                 /* Set the suspending flag.  */
303                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
304 
305                 /* Setup the timeout period.  */
306                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
307 
308                 /* Temporarily disable preemption.  */
309                 _tx_thread_preempt_disable++;
310 
311                 /* Restore interrupts.  */
312                 TX_RESTORE
313 
314                 /* Call actual thread suspension routine.  */
315                 _tx_thread_system_suspend(thread_ptr);
316 #endif
317 
318 #ifdef TX_ENABLE_EVENT_TRACE
319 
320                 /* Check that the event time stamp is unchanged.  A different
321                    timestamp means that a later event wrote over the byte
322                    allocate event.  In that case, do nothing here.  */
323                 if (entry_ptr != TX_NULL)
324                 {
325 
326                     /* Is the time-stamp the same?  */
327                     if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
328                     {
329 
330                         /* Timestamp is the same, update the entry with the address.  */
331 #ifdef TX_MISRA_ENABLE
332                         entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
333 #else
334                         entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
335 #endif
336                     }
337                 }
338 #endif
339 
340 #ifdef TX_ENABLE_EVENT_LOGGING
341                 /* Check that the event time stamp is unchanged and the call is about
342                    to return success.  A different timestamp means that a later event
343                    wrote over the block allocate event.  A return value other than
344                    TX_SUCCESS indicates that no block was available. In those cases,
345                    do nothing here.  */
346                 if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
347                     upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) &&
348                     ((thread_ptr -> tx_thread_suspend_status) == TX_SUCCESS))
349                 {
350 
351                     /* Store the address of the allocated block.  */
352                     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =  (ULONG) *block_ptr;
353                 }
354 #endif
355 
356                 /* Return the completion status.  */
357                 status =  thread_ptr -> tx_thread_suspend_status;
358             }
359         }
360         else
361         {
362 
363             /* Immediate return, return error completion.  */
364             status =  TX_NO_MEMORY;
365 
366             /* Restore interrupts.  */
367             TX_RESTORE
368         }
369     }
370 
371     /* Return completion status.  */
372     return(status);
373 }
374 
375