diff options
Diffstat (limited to 'firmware/kernel/thread.c')
-rw-r--r-- | firmware/kernel/thread.c | 400 |
1 files changed, 26 insertions, 374 deletions
diff --git a/firmware/kernel/thread.c b/firmware/kernel/thread.c index 9855cc3c84..5bb6eb5522 100644 --- a/firmware/kernel/thread.c +++ b/firmware/kernel/thread.c | |||
@@ -28,11 +28,7 @@ | |||
28 | #undef _FORTIFY_SOURCE | 28 | #undef _FORTIFY_SOURCE |
29 | #endif | 29 | #endif |
30 | 30 | ||
31 | #include <stdbool.h> | 31 | #include "thread-internal.h" |
32 | #include <stdio.h> | ||
33 | #include "thread.h" | ||
34 | #include "panic.h" | ||
35 | #include "system.h" | ||
36 | #include "kernel.h" | 32 | #include "kernel.h" |
37 | #include "cpu.h" | 33 | #include "cpu.h" |
38 | #include "string.h" | 34 | #include "string.h" |
@@ -40,8 +36,6 @@ | |||
40 | #include <profile.h> | 36 | #include <profile.h> |
41 | #endif | 37 | #endif |
42 | #include "core_alloc.h" | 38 | #include "core_alloc.h" |
43 | #include "gcc_extensions.h" | ||
44 | #include "corelock.h" | ||
45 | 39 | ||
46 | /**************************************************************************** | 40 | /**************************************************************************** |
47 | * ATTENTION!! * | 41 | * ATTENTION!! * |
@@ -131,7 +125,6 @@ | |||
131 | 125 | ||
132 | /* Cast to the the machine pointer size, whose size could be < 4 or > 32 | 126 | /* Cast to the the machine pointer size, whose size could be < 4 or > 32 |
133 | * (someday :). */ | 127 | * (someday :). */ |
134 | #define DEADBEEF ((uintptr_t)0xdeadbeefdeadbeefull) | ||
135 | static struct core_entry cores[NUM_CORES] IBSS_ATTR; | 128 | static struct core_entry cores[NUM_CORES] IBSS_ATTR; |
136 | struct thread_entry threads[MAXTHREADS] IBSS_ATTR; | 129 | struct thread_entry threads[MAXTHREADS] IBSS_ATTR; |
137 | 130 | ||
@@ -204,57 +197,36 @@ void switch_thread(void) | |||
204 | * End Processor-specific section | 197 | * End Processor-specific section |
205 | ***************************************************************************/ | 198 | ***************************************************************************/ |
206 | 199 | ||
207 | #if THREAD_EXTRA_CHECKS | 200 | static NO_INLINE |
208 | static void thread_panicf(const char *msg, struct thread_entry *thread) | 201 | void thread_panicf(const char *msg, struct thread_entry *thread) |
209 | { | 202 | { |
210 | IF_COP( const unsigned int core = thread->core; ) | 203 | IF_COP( const unsigned int core = thread->core; ) |
211 | static char name[32]; | 204 | static char namebuf[sizeof (((struct thread_debug_info *)0)->name)]; |
212 | thread_get_name(name, 32, thread); | 205 | const char *name = thread->name; |
206 | if (!name) | ||
207 | name = ""; | ||
208 | snprintf(namebuf, sizeof (namebuf), *name ? "%s" : "%s%08lX", | ||
209 | name, (unsigned long)thread->id); | ||
213 | panicf ("%s %s" IF_COP(" (%d)"), msg, name IF_COP(, core)); | 210 | panicf ("%s %s" IF_COP(" (%d)"), msg, name IF_COP(, core)); |
214 | } | 211 | } |
212 | |||
215 | static void thread_stkov(struct thread_entry *thread) | 213 | static void thread_stkov(struct thread_entry *thread) |
216 | { | 214 | { |
217 | thread_panicf("Stkov", thread); | 215 | thread_panicf("Stkov", thread); |
218 | } | 216 | } |
217 | |||
218 | #if THREAD_EXTRA_CHECKS | ||
219 | #define THREAD_PANICF(msg, thread) \ | 219 | #define THREAD_PANICF(msg, thread) \ |
220 | thread_panicf(msg, thread) | 220 | thread_panicf(msg, thread) |
221 | #define THREAD_ASSERT(exp, msg, thread) \ | 221 | #define THREAD_ASSERT(exp, msg, thread) \ |
222 | ({ if (!({ exp; })) thread_panicf((msg), (thread)); }) | 222 | ({ if (!({ exp; })) thread_panicf((msg), (thread)); }) |
223 | #else | 223 | #else |
224 | static void thread_stkov(struct thread_entry *thread) | 224 | #define THREAD_PANICF(msg, thread) \ |
225 | { | 225 | do {} while (0) |
226 | IF_COP( const unsigned int core = thread->core; ) | 226 | #define THREAD_ASSERT(exp, msg, thread) \ |
227 | static char name[32]; | 227 | do {} while (0) |
228 | thread_get_name(name, 32, thread); | ||
229 | panicf("Stkov %s" IF_COP(" (%d)"), name IF_COP(, core)); | ||
230 | } | ||
231 | #define THREAD_PANICF(msg, thread) | ||
232 | #define THREAD_ASSERT(exp, msg, thread) | ||
233 | #endif /* THREAD_EXTRA_CHECKS */ | 228 | #endif /* THREAD_EXTRA_CHECKS */ |
234 | 229 | ||
235 | /* Thread locking */ | ||
236 | #if NUM_CORES > 1 | ||
237 | #define LOCK_THREAD(thread) \ | ||
238 | ({ corelock_lock(&(thread)->slot_cl); }) | ||
239 | #define TRY_LOCK_THREAD(thread) \ | ||
240 | ({ corelock_try_lock(&(thread)->slot_cl); }) | ||
241 | #define UNLOCK_THREAD(thread) \ | ||
242 | ({ corelock_unlock(&(thread)->slot_cl); }) | ||
243 | #define UNLOCK_THREAD_AT_TASK_SWITCH(thread) \ | ||
244 | ({ unsigned int _core = (thread)->core; \ | ||
245 | cores[_core].blk_ops.flags |= TBOP_UNLOCK_CORELOCK; \ | ||
246 | cores[_core].blk_ops.cl_p = &(thread)->slot_cl; }) | ||
247 | #else | ||
248 | #define LOCK_THREAD(thread) \ | ||
249 | ({ (void)(thread); }) | ||
250 | #define TRY_LOCK_THREAD(thread) \ | ||
251 | ({ (void)(thread); }) | ||
252 | #define UNLOCK_THREAD(thread) \ | ||
253 | ({ (void)(thread); }) | ||
254 | #define UNLOCK_THREAD_AT_TASK_SWITCH(thread) \ | ||
255 | ({ (void)(thread); }) | ||
256 | #endif | ||
257 | |||
258 | /* RTR list */ | 230 | /* RTR list */ |
259 | #define RTR_LOCK(core) \ | 231 | #define RTR_LOCK(core) \ |
260 | ({ corelock_lock(&cores[core].rtr_cl); }) | 232 | ({ corelock_lock(&cores[core].rtr_cl); }) |
@@ -993,27 +965,6 @@ static void wakeup_thread_release(struct thread_entry *thread) | |||
993 | inherit_priority(bl, bl, blt, newblpr); | 965 | inherit_priority(bl, bl, blt, newblpr); |
994 | } | 966 | } |
995 | 967 | ||
996 | /*--------------------------------------------------------------------------- | ||
997 | * No threads must be blocked waiting for this thread except for it to exit. | ||
998 | * The alternative is more elaborate cleanup and object registration code. | ||
999 | * Check this for risk of silent data corruption when objects with | ||
1000 | * inheritable blocking are abandoned by the owner - not precise but may | ||
1001 | * catch something. | ||
1002 | *--------------------------------------------------------------------------- | ||
1003 | */ | ||
1004 | static void __attribute__((noinline)) check_for_obj_waiters( | ||
1005 | const char *function, struct thread_entry *thread) | ||
1006 | { | ||
1007 | /* Only one bit in the mask should be set with a frequency on 1 which | ||
1008 | * represents the thread's own base priority */ | ||
1009 | if (priobit_popcount(&thread->pdist.mask) != 1 || | ||
1010 | thread->pdist.hist[priobit_ffs(&thread->pdist.mask)] > 1) | ||
1011 | { | ||
1012 | unsigned char name[32]; | ||
1013 | thread_get_name(name, 32, thread); | ||
1014 | panicf("%s->%s with obj. waiters", function, name); | ||
1015 | } | ||
1016 | } | ||
1017 | #endif /* HAVE_PRIORITY_SCHEDULING */ | 968 | #endif /* HAVE_PRIORITY_SCHEDULING */ |
1018 | 969 | ||
1019 | /*--------------------------------------------------------------------------- | 970 | /*--------------------------------------------------------------------------- |
@@ -1520,31 +1471,6 @@ void block_thread(struct thread_entry *current, int timeout) | |||
1520 | } | 1471 | } |
1521 | 1472 | ||
1522 | /*--------------------------------------------------------------------------- | 1473 | /*--------------------------------------------------------------------------- |
1523 | * Wakeup an entire queue of threads - returns bitwise-or of return bitmask | ||
1524 | * from each operation or THREAD_NONE of nothing was awakened. Object owning | ||
1525 | * the queue must be locked first. | ||
1526 | * | ||
1527 | * INTERNAL: Intended for use by kernel objects and not for programs. | ||
1528 | *--------------------------------------------------------------------------- | ||
1529 | */ | ||
1530 | unsigned int thread_queue_wake(struct thread_entry **list) | ||
1531 | { | ||
1532 | unsigned result = THREAD_NONE; | ||
1533 | |||
1534 | for (;;) | ||
1535 | { | ||
1536 | unsigned int rc = wakeup_thread(list, WAKEUP_DEFAULT); | ||
1537 | |||
1538 | if (rc == THREAD_NONE) | ||
1539 | break; /* No more threads */ | ||
1540 | |||
1541 | result |= rc; | ||
1542 | } | ||
1543 | |||
1544 | return result; | ||
1545 | } | ||
1546 | |||
1547 | /*--------------------------------------------------------------------------- | ||
1548 | * Assign the thread slot a new ID. Version is 0x00000100..0xffffff00. | 1474 | * Assign the thread slot a new ID. Version is 0x00000100..0xffffff00. |
1549 | *--------------------------------------------------------------------------- | 1475 | *--------------------------------------------------------------------------- |
1550 | */ | 1476 | */ |
@@ -1580,7 +1506,7 @@ static struct thread_entry * find_empty_thread_slot(void) | |||
1580 | struct thread_entry *t = &threads[n]; | 1506 | struct thread_entry *t = &threads[n]; |
1581 | LOCK_THREAD(t); | 1507 | LOCK_THREAD(t); |
1582 | 1508 | ||
1583 | if (t->state == STATE_KILLED IF_COP( && t->name != THREAD_DESTRUCT )) | 1509 | if (t->state == STATE_KILLED) |
1584 | { | 1510 | { |
1585 | /* Slot is empty - leave it locked and caller will unlock */ | 1511 | /* Slot is empty - leave it locked and caller will unlock */ |
1586 | thread = t; | 1512 | thread = t; |
@@ -1836,21 +1762,14 @@ void thread_exit(void) | |||
1836 | corelock_lock(¤t->waiter_cl); | 1762 | corelock_lock(¤t->waiter_cl); |
1837 | LOCK_THREAD(current); | 1763 | LOCK_THREAD(current); |
1838 | 1764 | ||
1839 | #if defined (ALLOW_REMOVE_THREAD) && NUM_CORES > 1 | ||
1840 | if (current->name == THREAD_DESTRUCT) | ||
1841 | { | ||
1842 | /* Thread being killed - become a waiter */ | ||
1843 | unsigned int id = current->id; | ||
1844 | UNLOCK_THREAD(current); | ||
1845 | corelock_unlock(¤t->waiter_cl); | ||
1846 | thread_wait(id); | ||
1847 | THREAD_PANICF("thread_exit->WK:*R", current); | ||
1848 | } | ||
1849 | #endif | ||
1850 | |||
1851 | #ifdef HAVE_PRIORITY_SCHEDULING | 1765 | #ifdef HAVE_PRIORITY_SCHEDULING |
1852 | check_for_obj_waiters("thread_exit", current); | 1766 | /* Only one bit in the mask should be set with a frequency on 1 which |
1853 | #endif | 1767 | * represents the thread's own base priority otherwise threads are waiting |
1768 | * on an abandoned object */ | ||
1769 | if (priobit_popcount(¤t->pdist.mask) != 1 || | ||
1770 | current->pdist.hist[priobit_ffs(¤t->pdist.mask)] > 1) | ||
1771 | thread_panicf("abandon ship!", current); | ||
1772 | #endif /* HAVE_PRIORITY_SCHEDULING */ | ||
1854 | 1773 | ||
1855 | if (current->tmo.prev != NULL) | 1774 | if (current->tmo.prev != NULL) |
1856 | { | 1775 | { |
@@ -1872,186 +1791,6 @@ void thread_exit(void) | |||
1872 | thread_final_exit(current); | 1791 | thread_final_exit(current); |
1873 | } | 1792 | } |
1874 | 1793 | ||
1875 | #ifdef ALLOW_REMOVE_THREAD | ||
1876 | /*--------------------------------------------------------------------------- | ||
1877 | * Remove a thread from the scheduler. Not The Right Way to Do Things in | ||
1878 | * normal programs. | ||
1879 | * | ||
1880 | * Parameter is the ID as returned from create_thread(). | ||
1881 | * | ||
1882 | * Use with care on threads that are not under careful control as this may | ||
1883 | * leave various objects in an undefined state. | ||
1884 | *--------------------------------------------------------------------------- | ||
1885 | */ | ||
1886 | void remove_thread(unsigned int thread_id) | ||
1887 | { | ||
1888 | #ifdef HAVE_CORELOCK_OBJECT | ||
1889 | /* core is not constant here because of core switching */ | ||
1890 | unsigned int core = CURRENT_CORE; | ||
1891 | unsigned int old_core = NUM_CORES; | ||
1892 | struct corelock *ocl = NULL; | ||
1893 | #else | ||
1894 | const unsigned int core = CURRENT_CORE; | ||
1895 | #endif | ||
1896 | struct thread_entry *current = cores[core].running; | ||
1897 | struct thread_entry *thread = thread_id_entry(thread_id); | ||
1898 | |||
1899 | unsigned state; | ||
1900 | int oldlevel; | ||
1901 | |||
1902 | if (thread == current) | ||
1903 | thread_exit(); /* Current thread - do normal exit */ | ||
1904 | |||
1905 | oldlevel = disable_irq_save(); | ||
1906 | |||
1907 | corelock_lock(&thread->waiter_cl); | ||
1908 | LOCK_THREAD(thread); | ||
1909 | |||
1910 | state = thread->state; | ||
1911 | |||
1912 | if (thread->id != thread_id || state == STATE_KILLED) | ||
1913 | goto thread_killed; | ||
1914 | |||
1915 | #if NUM_CORES > 1 | ||
1916 | if (thread->name == THREAD_DESTRUCT) | ||
1917 | { | ||
1918 | /* Thread being killed - become a waiter */ | ||
1919 | UNLOCK_THREAD(thread); | ||
1920 | corelock_unlock(&thread->waiter_cl); | ||
1921 | restore_irq(oldlevel); | ||
1922 | thread_wait(thread_id); | ||
1923 | return; | ||
1924 | } | ||
1925 | |||
1926 | thread->name = THREAD_DESTRUCT; /* Slot can't be used for now */ | ||
1927 | |||
1928 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
1929 | check_for_obj_waiters("remove_thread", thread); | ||
1930 | #endif | ||
1931 | |||
1932 | if (thread->core != core) | ||
1933 | { | ||
1934 | /* Switch cores and safely extract the thread there */ | ||
1935 | /* Slot HAS to be unlocked or a deadlock could occur which means other | ||
1936 | * threads have to be guided into becoming thread waiters if they | ||
1937 | * attempt to remove it. */ | ||
1938 | unsigned int new_core = thread->core; | ||
1939 | |||
1940 | corelock_unlock(&thread->waiter_cl); | ||
1941 | |||
1942 | UNLOCK_THREAD(thread); | ||
1943 | restore_irq(oldlevel); | ||
1944 | |||
1945 | old_core = switch_core(new_core); | ||
1946 | |||
1947 | oldlevel = disable_irq_save(); | ||
1948 | |||
1949 | corelock_lock(&thread->waiter_cl); | ||
1950 | LOCK_THREAD(thread); | ||
1951 | |||
1952 | state = thread->state; | ||
1953 | core = new_core; | ||
1954 | /* Perform the extraction and switch ourselves back to the original | ||
1955 | processor */ | ||
1956 | } | ||
1957 | #endif /* NUM_CORES > 1 */ | ||
1958 | |||
1959 | if (thread->tmo.prev != NULL) | ||
1960 | { | ||
1961 | /* Clean thread off the timeout list if a timeout check hasn't | ||
1962 | * run yet */ | ||
1963 | remove_from_list_tmo(thread); | ||
1964 | } | ||
1965 | |||
1966 | #ifdef HAVE_SCHEDULER_BOOSTCTRL | ||
1967 | /* Cancel CPU boost if any */ | ||
1968 | boost_thread(thread, false); | ||
1969 | #endif | ||
1970 | |||
1971 | IF_COP( retry_state: ) | ||
1972 | |||
1973 | switch (state) | ||
1974 | { | ||
1975 | case STATE_RUNNING: | ||
1976 | RTR_LOCK(core); | ||
1977 | /* Remove thread from ready to run tasks */ | ||
1978 | remove_from_list_l(&cores[core].running, thread); | ||
1979 | rtr_subtract_entry(core, thread->priority); | ||
1980 | RTR_UNLOCK(core); | ||
1981 | break; | ||
1982 | case STATE_BLOCKED: | ||
1983 | case STATE_BLOCKED_W_TMO: | ||
1984 | /* Remove thread from the queue it's blocked on - including its | ||
1985 | * own if waiting there */ | ||
1986 | #if NUM_CORES > 1 | ||
1987 | if (&thread->waiter_cl != thread->obj_cl) | ||
1988 | { | ||
1989 | ocl = thread->obj_cl; | ||
1990 | |||
1991 | if (UNLIKELY(corelock_try_lock(ocl) == 0)) | ||
1992 | { | ||
1993 | UNLOCK_THREAD(thread); | ||
1994 | corelock_lock(ocl); | ||
1995 | LOCK_THREAD(thread); | ||
1996 | |||
1997 | if (UNLIKELY(thread->state != state)) | ||
1998 | { | ||
1999 | /* Something woke the thread */ | ||
2000 | state = thread->state; | ||
2001 | corelock_unlock(ocl); | ||
2002 | goto retry_state; | ||
2003 | } | ||
2004 | } | ||
2005 | } | ||
2006 | #endif | ||
2007 | #ifdef HAVE_WAKEUP_EXT_CB | ||
2008 | if (thread->wakeup_ext_cb != NULL) | ||
2009 | thread->wakeup_ext_cb(thread); | ||
2010 | #endif | ||
2011 | |||
2012 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
2013 | /* Remove thread's priority influence from its chain if needed */ | ||
2014 | if (thread->blocker != NULL) | ||
2015 | wakeup_priority_protocol_release(thread); | ||
2016 | else | ||
2017 | #endif | ||
2018 | remove_from_list_l(thread->bqp, thread); | ||
2019 | |||
2020 | #if NUM_CORES > 1 | ||
2021 | if (ocl != NULL) | ||
2022 | corelock_unlock(ocl); | ||
2023 | #endif | ||
2024 | break; | ||
2025 | /* Otherwise thread is frozen and hasn't run yet */ | ||
2026 | } | ||
2027 | |||
2028 | new_thread_id(thread_id, thread); | ||
2029 | thread->state = STATE_KILLED; | ||
2030 | |||
2031 | /* If thread was waiting on itself, it will have been removed above. | ||
2032 | * The wrong order would result in waking the thread first and deadlocking | ||
2033 | * since the slot is already locked. */ | ||
2034 | thread_queue_wake(&thread->queue); | ||
2035 | |||
2036 | thread->name = NULL; | ||
2037 | |||
2038 | thread_killed: /* Thread was already killed */ | ||
2039 | /* Removal complete - safe to unlock and reenable interrupts */ | ||
2040 | corelock_unlock(&thread->waiter_cl); | ||
2041 | UNLOCK_THREAD(thread); | ||
2042 | restore_irq(oldlevel); | ||
2043 | |||
2044 | #if NUM_CORES > 1 | ||
2045 | if (old_core < NUM_CORES) | ||
2046 | { | ||
2047 | /* Did a removal on another processor's thread - switch back to | ||
2048 | native core */ | ||
2049 | switch_core(old_core); | ||
2050 | } | ||
2051 | #endif | ||
2052 | } | ||
2053 | #endif /* ALLOW_REMOVE_THREAD */ | ||
2054 | |||
2055 | #ifdef HAVE_PRIORITY_SCHEDULING | 1794 | #ifdef HAVE_PRIORITY_SCHEDULING |
2056 | /*--------------------------------------------------------------------------- | 1795 | /*--------------------------------------------------------------------------- |
2057 | * Sets the thread's relative base priority for the core it runs on. Any | 1796 | * Sets the thread's relative base priority for the core it runs on. Any |
@@ -2205,20 +1944,9 @@ unsigned int switch_core(unsigned int new_core) | |||
2205 | return core; | 1944 | return core; |
2206 | } | 1945 | } |
2207 | 1946 | ||
2208 | int oldlevel = disable_irq_save(); | 1947 | disable_irq(); |
2209 | LOCK_THREAD(current); | 1948 | LOCK_THREAD(current); |
2210 | 1949 | ||
2211 | if (current->name == THREAD_DESTRUCT) | ||
2212 | { | ||
2213 | /* Thread being killed - deactivate and let process complete */ | ||
2214 | unsigned int id = current->id; | ||
2215 | UNLOCK_THREAD(current); | ||
2216 | restore_irq(oldlevel); | ||
2217 | thread_wait(id); | ||
2218 | /* Should never be reached */ | ||
2219 | THREAD_PANICF("switch_core->D:*R", current); | ||
2220 | } | ||
2221 | |||
2222 | /* Get us off the running list for the current core */ | 1950 | /* Get us off the running list for the current core */ |
2223 | RTR_LOCK(core); | 1951 | RTR_LOCK(core); |
2224 | remove_from_list_l(&cores[core].running, current); | 1952 | remove_from_list_l(&cores[core].running, current); |
@@ -2274,7 +2002,7 @@ unsigned int switch_core(unsigned int new_core) | |||
2274 | * are safe to perform. | 2002 | * are safe to perform. |
2275 | *--------------------------------------------------------------------------- | 2003 | *--------------------------------------------------------------------------- |
2276 | */ | 2004 | */ |
2277 | void init_threads(void) | 2005 | void INIT_ATTR init_threads(void) |
2278 | { | 2006 | { |
2279 | const unsigned int core = CURRENT_CORE; | 2007 | const unsigned int core = CURRENT_CORE; |
2280 | struct thread_entry *thread; | 2008 | struct thread_entry *thread; |
@@ -2353,82 +2081,6 @@ void init_threads(void) | |||
2353 | #endif | 2081 | #endif |
2354 | } | 2082 | } |
2355 | 2083 | ||
2356 | /* Shared stack scan helper for thread_stack_usage and idle_stack_usage */ | ||
2357 | #if NUM_CORES == 1 | ||
2358 | static inline int stack_usage(uintptr_t *stackptr, size_t stack_size) | ||
2359 | #else | ||
2360 | static int stack_usage(uintptr_t *stackptr, size_t stack_size) | ||
2361 | #endif | ||
2362 | { | ||
2363 | unsigned int stack_words = stack_size / sizeof (uintptr_t); | ||
2364 | unsigned int i; | ||
2365 | int usage = 0; | ||
2366 | |||
2367 | for (i = 0; i < stack_words; i++) | ||
2368 | { | ||
2369 | if (stackptr[i] != DEADBEEF) | ||
2370 | { | ||
2371 | usage = ((stack_words - i) * 100) / stack_words; | ||
2372 | break; | ||
2373 | } | ||
2374 | } | ||
2375 | |||
2376 | return usage; | ||
2377 | } | ||
2378 | |||
2379 | /*--------------------------------------------------------------------------- | ||
2380 | * Returns the maximum percentage of stack a thread ever used while running. | ||
2381 | * NOTE: Some large buffer allocations that don't use enough the buffer to | ||
2382 | * overwrite stackptr[0] will not be seen. | ||
2383 | *--------------------------------------------------------------------------- | ||
2384 | */ | ||
2385 | int thread_stack_usage(const struct thread_entry *thread) | ||
2386 | { | ||
2387 | if (LIKELY(thread->stack_size > 0)) | ||
2388 | return stack_usage(thread->stack, thread->stack_size); | ||
2389 | return 0; | ||
2390 | } | ||
2391 | |||
2392 | #if NUM_CORES > 1 | ||
2393 | /*--------------------------------------------------------------------------- | ||
2394 | * Returns the maximum percentage of the core's idle stack ever used during | ||
2395 | * runtime. | ||
2396 | *--------------------------------------------------------------------------- | ||
2397 | */ | ||
2398 | int idle_stack_usage(unsigned int core) | ||
2399 | { | ||
2400 | return stack_usage(idle_stacks[core], IDLE_STACK_SIZE); | ||
2401 | } | ||
2402 | #endif | ||
2403 | |||
2404 | /*--------------------------------------------------------------------------- | ||
2405 | * Fills in the buffer with the specified thread's name. If the name is NULL, | ||
2406 | * empty, or the thread is in destruct state a formatted ID is written | ||
2407 | * instead. | ||
2408 | *--------------------------------------------------------------------------- | ||
2409 | */ | ||
2410 | void thread_get_name(char *buffer, int size, | ||
2411 | struct thread_entry *thread) | ||
2412 | { | ||
2413 | if (size <= 0) | ||
2414 | return; | ||
2415 | |||
2416 | *buffer = '\0'; | ||
2417 | |||
2418 | if (thread) | ||
2419 | { | ||
2420 | /* Display thread name if one or ID if none */ | ||
2421 | const char *name = thread->name; | ||
2422 | const char *fmt = "%s"; | ||
2423 | if (name == NULL IF_COP(|| name == THREAD_DESTRUCT) || *name == '\0') | ||
2424 | { | ||
2425 | name = (const char *)(uintptr_t)thread->id; | ||
2426 | fmt = "%04lX"; | ||
2427 | } | ||
2428 | snprintf(buffer, size, fmt, name); | ||
2429 | } | ||
2430 | } | ||
2431 | |||
2432 | /* Unless otherwise defined, do nothing */ | 2084 | /* Unless otherwise defined, do nothing */ |
2433 | #ifndef YIELD_KERNEL_HOOK | 2085 | #ifndef YIELD_KERNEL_HOOK |
2434 | #define YIELD_KERNEL_HOOK() false | 2086 | #define YIELD_KERNEL_HOOK() false |