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