Skip to content

Commit d26c712

Browse files
ycsinnashif
authored andcommitted
arch: add new interfaces to set/get the current thread of current CPU
Add the following arch-specific APIs: - arch_curr_thread() - arch_set_curr_thread() which allow SMP architectures to implement a faster "get current thread pointer" than the default provided by the kernel. The 'set' function is required for the 'get' to work, more on that later. When `CONFIG_ARCH_HAS_CUSTOM_CURRENT_IMPL` is selected, calls to `_current` & `k_sched_current_thread_query()` will be redirected to `arch_curr_thread()`, which ideally should translate into a single instruction read, avoiding the current "lock > read CPU > read current thread > unlock" path in SMP architectures and thus greatly improves the read performance. However, since the kernel relies on a copy of the "current thread"s on every CPU for certain operations (i.e. to compare the priority of the currently scheduled thread on another CPU to determine if IPI should be sent), we can't eliminate the copy of "current thread" (`current`) from the `struct _cpu` and therefore the kernel now has to invoke `arch_set_curr_thread()` in addition to what it has been doing. This means that it will take slightly longer (most likely one instruction write) to change the current thread pointer on the current CPU. Signed-off-by: Yong Cong Sin <[email protected]> Signed-off-by: Yong Cong Sin <[email protected]>
1 parent 9f2a7a7 commit d26c712

File tree

9 files changed

+70
-22
lines changed

9 files changed

+70
-22
lines changed

arch/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,3 +1148,9 @@ config ARCH_HAS_CUSTOM_BUSY_WAIT
11481148
It's possible that an architecture port cannot or does not want to use
11491149
the provided k_busy_wait(), but instead must do something custom. It must
11501150
enable this option in that case.
1151+
1152+
config ARCH_HAS_CUSTOM_CURRENT_IMPL
1153+
bool
1154+
help
1155+
Select when architecture implements arch_current_thread() &
1156+
arch_current_thread_set().

doc/releases/release-notes-4.1.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ Deprecated in this release
3636
Architectures
3737
*************
3838

39+
* Common
40+
41+
* Introduced :kconfig:option:`CONFIG_ARCH_HAS_CUSTOM_CURRENT_IMPL`, which can be selected when
42+
an architecture implemented and enabled its own :c:func:`arch_current_thread` and
43+
:c:func:`arch_current_thread_set` functions for faster retrieval of the current CPU's thread
44+
pointer. When enabled, ``_current`` variable will be routed to the
45+
:c:func:`arch_current_thread` (:github:`80716`).
46+
3947
* ARC
4048

4149
* ARM

include/zephyr/arch/arch_inlines.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@
3434
#include <zephyr/arch/sparc/arch_inlines.h>
3535
#endif
3636

37+
#include <zephyr/arch/common/arch_inlines.h>
38+
3739
#endif /* ZEPHYR_INCLUDE_ARCH_INLINES_H_ */
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2024 Meta Platforms.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_ZEPHYR_ARCH_COMMON_ARCH_INLINES_H_
8+
#define ZEPHYR_INCLUDE_ZEPHYR_ARCH_COMMON_ARCH_INLINES_H_
9+
10+
#ifndef ZEPHYR_INCLUDE_ARCH_INLINES_H_
11+
#error "This header shouldn't be included directly"
12+
#endif /* ZEPHYR_INCLUDE_ARCH_INLINES_H_ */
13+
14+
#ifndef _ASMLANGUAGE
15+
16+
#include <zephyr/kernel_structs.h>
17+
18+
#ifndef CONFIG_ARCH_HAS_CUSTOM_CURRENT_IMPL
19+
static ALWAYS_INLINE struct k_thread *arch_current_thread(void)
20+
{
21+
#ifdef CONFIG_SMP
22+
/* In SMP, _current is a field read from _current_cpu, which
23+
* can race with preemption before it is read. We must lock
24+
* local interrupts when reading it.
25+
*/
26+
unsigned int k = arch_irq_lock();
27+
28+
struct k_thread *ret = _current_cpu->current;
29+
30+
arch_irq_unlock(k);
31+
#else
32+
struct k_thread *ret = _kernel.cpus[0].current;
33+
#endif /* CONFIG_SMP */
34+
return ret;
35+
}
36+
37+
static ALWAYS_INLINE void arch_current_thread_set(struct k_thread *thread)
38+
{
39+
_current_cpu->current = thread;
40+
}
41+
#endif /* CONFIG_ARCH_HAS_CUSTOM_CURRENT_IMPL */
42+
43+
#endif /* _ASMLANGUAGE */
44+
45+
#endif /* ZEPHYR_INCLUDE_ZEPHYR_ARCH_COMMON_ARCH_INLINES_H_ */

include/zephyr/kernel_structs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ bool z_smp_cpu_mobile(void);
260260

261261
#define _current_cpu ({ __ASSERT_NO_MSG(!z_smp_cpu_mobile()); \
262262
arch_curr_cpu(); })
263-
#define _current k_sched_current_thread_query()
263+
#define _current arch_current_thread()
264264

265265
#else
266266
#define _current_cpu (&_kernel.cpus[0])

kernel/include/kswap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key,
134134
#endif /* CONFIG_SMP */
135135
z_thread_mark_switched_out();
136136
z_sched_switch_spin(new_thread);
137-
_current_cpu->current = new_thread;
137+
arch_current_thread_set(new_thread);
138138

139139
#ifdef CONFIG_TIMESLICING
140140
z_reset_time_slice(new_thread);
@@ -260,6 +260,6 @@ static inline void z_dummy_thread_init(struct k_thread *dummy_thread)
260260
dummy_thread->base.slice_ticks = 0;
261261
#endif /* CONFIG_TIMESLICE_PER_THREAD */
262262

263-
_current_cpu->current = dummy_thread;
263+
arch_current_thread_set(dummy_thread);
264264
}
265265
#endif /* ZEPHYR_KERNEL_INCLUDE_KSWAP_H_ */

kernel/mmu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@ static bool do_page_fault(void *addr, bool pin)
16741674
#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
16751675

16761676
key = k_spin_lock(&z_mm_lock);
1677-
faulting_thread = _current_cpu->current;
1677+
faulting_thread = _current;
16781678

16791679
status = arch_page_location_get(addr, &page_in_location);
16801680
if (status == ARCH_PAGE_LOCATION_BAD) {

kernel/sched.c

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -838,11 +838,11 @@ struct k_thread *z_swap_next_thread(void)
838838
}
839839

840840
#ifdef CONFIG_USE_SWITCH
841-
/* Just a wrapper around _current = xxx with tracing */
841+
/* Just a wrapper around arch_current_thread_set(xxx) with tracing */
842842
static inline void set_current(struct k_thread *new_thread)
843843
{
844844
z_thread_mark_switched_out();
845-
_current_cpu->current = new_thread;
845+
arch_current_thread_set(new_thread);
846846
}
847847

848848
/**
@@ -1230,20 +1230,7 @@ static inline void z_vrfy_k_wakeup(k_tid_t thread)
12301230

12311231
k_tid_t z_impl_k_sched_current_thread_query(void)
12321232
{
1233-
#ifdef CONFIG_SMP
1234-
/* In SMP, _current is a field read from _current_cpu, which
1235-
* can race with preemption before it is read. We must lock
1236-
* local interrupts when reading it.
1237-
*/
1238-
unsigned int k = arch_irq_lock();
1239-
#endif /* CONFIG_SMP */
1240-
1241-
k_tid_t ret = _current_cpu->current;
1242-
1243-
#ifdef CONFIG_SMP
1244-
arch_irq_unlock(k);
1245-
#endif /* CONFIG_SMP */
1246-
return ret;
1233+
return arch_current_thread();
12471234
}
12481235

12491236
#ifdef CONFIG_USERSPACE

kernel/thread.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -946,8 +946,8 @@ void z_thread_mark_switched_out(void)
946946
#ifdef CONFIG_TRACING
947947
#ifdef CONFIG_THREAD_LOCAL_STORAGE
948948
/* Dummy thread won't have TLS set up to run arbitrary code */
949-
if (!_current_cpu->current ||
950-
(_current_cpu->current->base.thread_state & _THREAD_DUMMY) != 0)
949+
if (!_current ||
950+
(_current->base.thread_state & _THREAD_DUMMY) != 0)
951951
return;
952952
#endif /* CONFIG_THREAD_LOCAL_STORAGE */
953953
SYS_PORT_TRACING_FUNC(k_thread, switched_out);

0 commit comments

Comments
 (0)