[PATCH bpf-next v1 1/2] selftests/bpf: Support get_preempt_count() for LoongArch

From: Tiezhu Yang

Date: Thu May 21 2026 - 09:08:52 EST


There is no LoongArch support for get_preempt_count() currently and its
fallback path always returns 0, just add it so that bpf_in_interrupt(),
bpf_in_nmi(), bpf_in_hardirq(), bpf_in_serving_softirq(), bpf_in_task()
work for LoongArch as well.

Since LoongArch is transitioning to CONFIG_THREAD_INFO_IN_TASK, use
bpf_core_field_exists() to provide compatibility for both legacy and
future kernels. A struct flavor "task_struct___local" is introduced
to bypass static compiler checks when thread_info is missing from the
base task_struct.

For the newer kernels that select CONFIG_THREAD_INFO_IN_TASK, it reads
preempt_count from the thread_info embedded within task_struct. For the
older kernels without selecting CONFIG_THREAD_INFO_IN_TASK, it retrieves
the thread_info pointer from the bottom of the kernel stack (task->stack)
and then reads the preempt_count. This ensures the BPF selftests remain
functional across various LoongArch kernel versions.

With this patch, "./test_progs -t exe_ctx" passes on LoongArch regardless
of whether CONFIG_THREAD_INFO_IN_TASK is set.

Signed-off-by: Tiezhu Yang <yangtiezhu@xxxxxxxxxxx>
---
.../testing/selftests/bpf/bpf_experimental.h | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)

diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h
index d1db355e872b..1d0b29a9acb8 100644
--- a/tools/testing/selftests/bpf/bpf_experimental.h
+++ b/tools/testing/selftests/bpf/bpf_experimental.h
@@ -401,6 +401,12 @@ struct task_struct___preempt_rt {
extern struct lowcore *bpf_get_lowcore(void) __weak __ksym;
#endif

+#ifdef bpf_target_loongarch
+struct task_struct___local {
+ struct thread_info thread_info;
+} __attribute__((preserve_access_index));
+#endif
+
static inline int get_preempt_count(void)
{
#if defined(bpf_target_x86)
@@ -423,6 +429,18 @@ static inline int get_preempt_count(void)
return bpf_get_current_task_btf()->thread_info.preempt_count;
#elif defined(bpf_target_s390)
return bpf_get_lowcore()->preempt_count;
+#elif defined(bpf_target_loongarch)
+ struct task_struct *task = bpf_get_current_task_btf();
+ struct task_struct___local *task_alt = (void *)task;
+
+ if (bpf_core_field_exists(task_alt->thread_info)) {
+ return BPF_CORE_READ(task_alt, thread_info.preempt_count);
+ } else {
+ void *stack = BPF_CORE_READ(task, stack);
+ struct thread_info *ti = (void *)stack;
+
+ return BPF_CORE_READ(ti, preempt_count);
+ }
#endif
return 0;
}
@@ -433,6 +451,7 @@ static inline int get_preempt_count(void)
* * arm64
* * powerpc64
* * s390x
+ * * loongarch
*/
static inline int bpf_in_interrupt(void)
{
@@ -454,6 +473,7 @@ static inline int bpf_in_interrupt(void)
* * arm64
* * powerpc64
* * s390x
+ * * loongarch
*/
static inline int bpf_in_nmi(void)
{
@@ -466,6 +486,7 @@ static inline int bpf_in_nmi(void)
* * arm64
* * powerpc64
* * s390x
+ * * loongarch
*/
static inline int bpf_in_hardirq(void)
{
@@ -478,6 +499,7 @@ static inline int bpf_in_hardirq(void)
* * arm64
* * powerpc64
* * s390x
+ * * loongarch
*/
static inline int bpf_in_serving_softirq(void)
{
@@ -498,6 +520,7 @@ static inline int bpf_in_serving_softirq(void)
* * arm64
* * powerpc64
* * s390x
+ * * loongarch
*/
static inline int bpf_in_task(void)
{
--
2.42.0