[PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()

Tiezhu Yang posted 4 patches 2 weeks, 6 days ago
[PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Tiezhu Yang 2 weeks, 6 days ago
After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
ORC unwinder"), the system can not boot normally under some configs,
there are many error messages "cannot find unwind pc at".

The kernel boots normally with the defconfig, so no problem found out
at the first time. Here is one way to reproduce:

  cd linux
  make mrproper defconfig -j"$(nproc)"
  scripts/config -e KASAN
  make olddefconfig all -j"$(nproc)"
  sudo make modules_install
  sudo make install
  sudo reboot

The address that can not unwind is not a valid kernel address which is
between "pcpu_handlers[cpu]" and "pcpu_handlers[cpu] + vec_sz" that is
a very big address.

The root cause is that the code of eentry was copied to the new area of
pcpu_handlers[cpu] in setup_tlb_handler(), handle this special case to
get the real address in bt_address() to unwind normally.

Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
---
 arch/loongarch/include/asm/setup.h |  2 ++
 arch/loongarch/kernel/unwind_orc.c | 12 ++++++++++++
 2 files changed, 14 insertions(+)

diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h
index 3c2fb16b11b6..79eb63e749f9 100644
--- a/arch/loongarch/include/asm/setup.h
+++ b/arch/loongarch/include/asm/setup.h
@@ -14,6 +14,8 @@
 
 extern unsigned long eentry;
 extern unsigned long tlbrentry;
+extern unsigned long pcpu_handlers[NR_CPUS];
+extern long exception_handlers[VECSIZE * 128 / sizeof(long)];
 extern char init_command_line[COMMAND_LINE_SIZE];
 extern void tlb_init(int cpu);
 extern void cpu_cache_init(void);
diff --git a/arch/loongarch/kernel/unwind_orc.c b/arch/loongarch/kernel/unwind_orc.c
index d6b3688a1ce9..40f4d6c12cf6 100644
--- a/arch/loongarch/kernel/unwind_orc.c
+++ b/arch/loongarch/kernel/unwind_orc.c
@@ -352,6 +352,18 @@ static inline unsigned long bt_address(unsigned long ra)
 {
 	extern unsigned long eentry;
 
+#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
+	for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
+		int vec_sz = sizeof(exception_handlers);
+
+		if (ra >= pcpu_handlers[cpu] &&
+		    ra < pcpu_handlers[cpu] + vec_sz) {
+			ra = eentry + (ra - pcpu_handlers[cpu]);
+			break;
+		}
+	}
+#endif
+
 	if (ra >= eentry && ra < eentry +  EXCCODE_INT_END * VECSIZE) {
 		unsigned long func;
 		unsigned long type = (ra - eentry) / VECSIZE;
-- 
2.42.0
Re: [PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Huacai Chen 2 weeks, 6 days ago
Hi, Tiezhu,

On Tue, Jan 20, 2026 at 5:23 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
> ORC unwinder"), the system can not boot normally under some configs,
> there are many error messages "cannot find unwind pc at".
>
> The kernel boots normally with the defconfig, so no problem found out
> at the first time. Here is one way to reproduce:
>
>   cd linux
>   make mrproper defconfig -j"$(nproc)"
>   scripts/config -e KASAN
>   make olddefconfig all -j"$(nproc)"
>   sudo make modules_install
>   sudo make install
>   sudo reboot
>
> The address that can not unwind is not a valid kernel address which is
> between "pcpu_handlers[cpu]" and "pcpu_handlers[cpu] + vec_sz" that is
> a very big address.
>
> The root cause is that the code of eentry was copied to the new area of
> pcpu_handlers[cpu] in setup_tlb_handler(), handle this special case to
> get the real address in bt_address() to unwind normally.
>
> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
> ---
>  arch/loongarch/include/asm/setup.h |  2 ++
>  arch/loongarch/kernel/unwind_orc.c | 12 ++++++++++++
>  2 files changed, 14 insertions(+)
>
> diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h
> index 3c2fb16b11b6..79eb63e749f9 100644
> --- a/arch/loongarch/include/asm/setup.h
> +++ b/arch/loongarch/include/asm/setup.h
> @@ -14,6 +14,8 @@
>
>  extern unsigned long eentry;
>  extern unsigned long tlbrentry;
> +extern unsigned long pcpu_handlers[NR_CPUS];
> +extern long exception_handlers[VECSIZE * 128 / sizeof(long)];
>  extern char init_command_line[COMMAND_LINE_SIZE];
>  extern void tlb_init(int cpu);
>  extern void cpu_cache_init(void);
> diff --git a/arch/loongarch/kernel/unwind_orc.c b/arch/loongarch/kernel/unwind_orc.c
> index d6b3688a1ce9..40f4d6c12cf6 100644
> --- a/arch/loongarch/kernel/unwind_orc.c
> +++ b/arch/loongarch/kernel/unwind_orc.c
> @@ -352,6 +352,18 @@ static inline unsigned long bt_address(unsigned long ra)
>  {
>         extern unsigned long eentry;
>
> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
> +       for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
Use nr_cpu_ids instead of num_possible_cpus() can improve performance a little.

> +               int vec_sz = sizeof(exception_handlers);
> +
It is better to add "if (!pcpu_handlers[cpu]) continue" here.


Huacai

> +               if (ra >= pcpu_handlers[cpu] &&
> +                   ra < pcpu_handlers[cpu] + vec_sz) {
> +                       ra = eentry + (ra - pcpu_handlers[cpu]);
> +                       break;
> +               }
> +       }
> +#endif
> +
>         if (ra >= eentry && ra < eentry +  EXCCODE_INT_END * VECSIZE) {
>                 unsigned long func;
>                 unsigned long type = (ra - eentry) / VECSIZE;
> --
> 2.42.0
>
Re: [PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Tiezhu Yang 2 weeks, 6 days ago
On 2026/1/20 下午8:04, Huacai Chen wrote:
> Hi, Tiezhu,
> 
> On Tue, Jan 20, 2026 at 5:23 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>>
>> After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
>> ORC unwinder"), the system can not boot normally under some configs,
>> there are many error messages "cannot find unwind pc at".

...

>> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
>> +       for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
> Use nr_cpu_ids instead of num_possible_cpus() can improve performance a little.
> 
>> +               int vec_sz = sizeof(exception_handlers);
>> +
> It is better to add "if (!pcpu_handlers[cpu]) continue" here.

If so, it can use for_each_possible_cpu(cpu) directly?

Thanks,
Tiezhu

Re: [PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Huacai Chen 2 weeks, 6 days ago
On Tue, Jan 20, 2026 at 8:57 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> On 2026/1/20 下午8:04, Huacai Chen wrote:
> > Hi, Tiezhu,
> >
> > On Tue, Jan 20, 2026 at 5:23 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
> >>
> >> After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
> >> ORC unwinder"), the system can not boot normally under some configs,
> >> there are many error messages "cannot find unwind pc at".
>
> ...
>
> >> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
> >> +       for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
> > Use nr_cpu_ids instead of num_possible_cpus() can improve performance a little.
> >
> >> +               int vec_sz = sizeof(exception_handlers);
> >> +
> > It is better to add "if (!pcpu_handlers[cpu]) continue" here.
>
> If so, it can use for_each_possible_cpu(cpu) directly?
It can, but I remember you want to skip CPU 0?

Huacai

>
> Thanks,
> Tiezhu
>
Re: [PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Tiezhu Yang 2 weeks, 6 days ago
On 2026/1/20 下午9:01, Huacai Chen wrote:
> On Tue, Jan 20, 2026 at 8:57 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>>
>> On 2026/1/20 下午8:04, Huacai Chen wrote:
>>> Hi, Tiezhu,
>>>
>>> On Tue, Jan 20, 2026 at 5:23 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>>>>
>>>> After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
>>>> ORC unwinder"), the system can not boot normally under some configs,
>>>> there are many error messages "cannot find unwind pc at".
>>
>> ...
>>
>>>> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
>>>> +       for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
>>> Use nr_cpu_ids instead of num_possible_cpus() can improve performance a little.
>>>
>>>> +               int vec_sz = sizeof(exception_handlers);
>>>> +
>>> It is better to add "if (!pcpu_handlers[cpu]) continue" here.
>>
>> If so, it can use for_each_possible_cpu(cpu) directly?
> It can, but I remember you want to skip CPU 0?

Yes, that is the intention to use the for loop, but if checking
!pcpu_handlers[cpu], I think no need to use for loop to skip cpu
0, just use for_each_possible_cpu(cpu) is more simple and direct.

The code looks like:

----->8-----
diff --git a/arch/loongarch/kernel/unwind_orc.c 
b/arch/loongarch/kernel/unwind_orc.c
index d6b3688a1ce9..a0ba4e416f99 100644
--- a/arch/loongarch/kernel/unwind_orc.c
+++ b/arch/loongarch/kernel/unwind_orc.c
@@ -352,6 +352,24 @@ static inline unsigned long bt_address(unsigned 
long ra)
  {
         extern unsigned long eentry;

+#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
+       int cpu;
+       int vec_sz __maybe_unused;
+
+       for_each_possible_cpu(cpu) {
+               if (!pcpu_handlers[cpu])
+                       continue;
+
+               vec_sz = sizeof(exception_handlers);
+
+               if (ra >= pcpu_handlers[cpu] &&
+                   ra < pcpu_handlers[cpu] + vec_sz) {
+                       ra = eentry + (ra - pcpu_handlers[cpu]);
+                       break;
+               }
+       }
+#endif
+
         if (ra >= eentry && ra < eentry +  EXCCODE_INT_END * VECSIZE) {
                 unsigned long func;
                 unsigned long type = (ra - eentry) / VECSIZE;

Please let me know what is the better way.

Thanks,
Tiezhu

Re: [PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Huacai Chen 2 weeks, 5 days ago
On Tue, Jan 20, 2026 at 9:09 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> On 2026/1/20 下午9:01, Huacai Chen wrote:
> > On Tue, Jan 20, 2026 at 8:57 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
> >>
> >> On 2026/1/20 下午8:04, Huacai Chen wrote:
> >>> Hi, Tiezhu,
> >>>
> >>> On Tue, Jan 20, 2026 at 5:23 PM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
> >>>>
> >>>> After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
> >>>> ORC unwinder"), the system can not boot normally under some configs,
> >>>> there are many error messages "cannot find unwind pc at".
> >>
> >> ...
> >>
> >>>> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
> >>>> +       for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
> >>> Use nr_cpu_ids instead of num_possible_cpus() can improve performance a little.
> >>>
> >>>> +               int vec_sz = sizeof(exception_handlers);
> >>>> +
> >>> It is better to add "if (!pcpu_handlers[cpu]) continue" here.
> >>
> >> If so, it can use for_each_possible_cpu(cpu) directly?
> > It can, but I remember you want to skip CPU 0?
>
> Yes, that is the intention to use the for loop, but if checking
> !pcpu_handlers[cpu], I think no need to use for loop to skip cpu
> 0, just use for_each_possible_cpu(cpu) is more simple and direct.
My original purpose, checking  !pcpu_handlers[cpu] is to handle the
memory allocation failure case, but not for skipping CPU 0.

But yes, it will skip CPU 0 indeed, so you can choose the original for
loop or for_each_possible_cpu(cpu), both OK for me.

>
> The code looks like:
>
> ----->8-----
> diff --git a/arch/loongarch/kernel/unwind_orc.c
> b/arch/loongarch/kernel/unwind_orc.c
> index d6b3688a1ce9..a0ba4e416f99 100644
> --- a/arch/loongarch/kernel/unwind_orc.c
> +++ b/arch/loongarch/kernel/unwind_orc.c
> @@ -352,6 +352,24 @@ static inline unsigned long bt_address(unsigned
> long ra)
>   {
>          extern unsigned long eentry;
>
> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
> +       int cpu;
> +       int vec_sz __maybe_unused;
 __maybe_unused is not needed because it is already in #ifdefs.

> +
> +       for_each_possible_cpu(cpu) {
> +               if (!pcpu_handlers[cpu])
> +                       continue;
> +
> +               vec_sz = sizeof(exception_handlers);
Calculate it when defining it.


Huacai

> +
> +               if (ra >= pcpu_handlers[cpu] &&
> +                   ra < pcpu_handlers[cpu] + vec_sz) {
> +                       ra = eentry + (ra - pcpu_handlers[cpu]);
> +                       break;
> +               }
> +       }
> +#endif
> +
>          if (ra >= eentry && ra < eentry +  EXCCODE_INT_END * VECSIZE) {
>                  unsigned long func;
>                  unsigned long type = (ra - eentry) / VECSIZE;
>
> Please let me know what is the better way.
>
> Thanks,
> Tiezhu
>
>
Re: [PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Tiezhu Yang 2 weeks, 5 days ago
On 2026/1/20 下午9:08, Tiezhu Yang wrote:
> On 2026/1/20 下午9:01, Huacai Chen wrote:
>> On Tue, Jan 20, 2026 at 8:57 PM Tiezhu Yang <yangtiezhu@loongson.cn> 
>> wrote:
>>>
>>> On 2026/1/20 下午8:04, Huacai Chen wrote:
>>>> Hi, Tiezhu,
>>>>
>>>> On Tue, Jan 20, 2026 at 5:23 PM Tiezhu Yang <yangtiezhu@loongson.cn> 
>>>> wrote:
>>>>>
>>>>> After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
>>>>> ORC unwinder"), the system can not boot normally under some configs,
>>>>> there are many error messages "cannot find unwind pc at".
>>>
>>> ...
>>>
>>>>> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
>>>>> +       for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
>>>> Use nr_cpu_ids instead of num_possible_cpus() can improve 
>>>> performance a little.
>>>>
>>>>> +               int vec_sz = sizeof(exception_handlers);
>>>>> +
>>>> It is better to add "if (!pcpu_handlers[cpu]) continue" here.
>>>
>>> If so, it can use for_each_possible_cpu(cpu) directly?
>> It can, but I remember you want to skip CPU 0?
> 
> Yes, that is the intention to use the for loop, but if checking
> !pcpu_handlers[cpu], I think no need to use for loop to skip cpu
> 0, just use for_each_possible_cpu(cpu) is more simple and direct.

Handle this special case only if bt_address() returns 0 to avoid
affecting performance for the normal address.

Here is the draft change:

----->8-----
diff --git a/arch/loongarch/kernel/unwind_orc.c 
b/arch/loongarch/kernel/unwind_orc.c
index d6b3688a1ce9..1ce10701f3c3 100644
--- a/arch/loongarch/kernel/unwind_orc.c
+++ b/arch/loongarch/kernel/unwind_orc.c
@@ -378,6 +378,28 @@ static inline unsigned long bt_address(unsigned 
long ra)
         return 0;
  }

+static inline unsigned long handle_pcpu_handlers(unsigned long ra)
+{
+#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
+       int cpu;
+       int vec_sz __maybe_unused;
+
+       for_each_possible_cpu(cpu) {
+               if (!pcpu_handlers[cpu])
+                       continue;
+
+               vec_sz = sizeof(exception_handlers);
+
+               if (ra >= pcpu_handlers[cpu] &&
+                   ra < pcpu_handlers[cpu] + vec_sz) {
+                       ra = eentry + (ra - pcpu_handlers[cpu]);
+                       break;
+               }
+       }
+#endif
+       return ra;
+}
+
  bool unwind_next_frame(struct unwind_state *state)
  {
         unsigned long *p, pc;
@@ -494,8 +516,11 @@ bool unwind_next_frame(struct unwind_state *state)

         state->pc = bt_address(pc);
         if (!state->pc) {
-               pr_err("cannot find unwind pc at %px\n", (void *)pc);
-               goto err;
+               state->pc = bt_address(handle_pcpu_handlers(pc));
+               if (!state->pc) {
+                       pr_err("cannot find unwind pc at %px\n", (void 
*)pc);
+                       goto err;
+               }
         }

         return true;

I will test it, if it works well I will wait for more comments and
submit v2 later.

Thanks,
Tiezhu

Re: [PATCH v1 2/4] LoongArch: Handle percpu handler address in bt_address()
Posted by Huacai Chen 2 weeks, 5 days ago
On Wed, Jan 21, 2026 at 9:47 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> On 2026/1/20 下午9:08, Tiezhu Yang wrote:
> > On 2026/1/20 下午9:01, Huacai Chen wrote:
> >> On Tue, Jan 20, 2026 at 8:57 PM Tiezhu Yang <yangtiezhu@loongson.cn>
> >> wrote:
> >>>
> >>> On 2026/1/20 下午8:04, Huacai Chen wrote:
> >>>> Hi, Tiezhu,
> >>>>
> >>>> On Tue, Jan 20, 2026 at 5:23 PM Tiezhu Yang <yangtiezhu@loongson.cn>
> >>>> wrote:
> >>>>>
> >>>>> After commit 4cd641a79e69 ("LoongArch: Remove unnecessary checks for
> >>>>> ORC unwinder"), the system can not boot normally under some configs,
> >>>>> there are many error messages "cannot find unwind pc at".
> >>>
> >>> ...
> >>>
> >>>>> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
> >>>>> +       for (int cpu = 1; cpu < num_possible_cpus(); cpu++) {
> >>>> Use nr_cpu_ids instead of num_possible_cpus() can improve
> >>>> performance a little.
> >>>>
> >>>>> +               int vec_sz = sizeof(exception_handlers);
> >>>>> +
> >>>> It is better to add "if (!pcpu_handlers[cpu]) continue" here.
> >>>
> >>> If so, it can use for_each_possible_cpu(cpu) directly?
> >> It can, but I remember you want to skip CPU 0?
> >
> > Yes, that is the intention to use the for loop, but if checking
> > !pcpu_handlers[cpu], I think no need to use for loop to skip cpu
> > 0, just use for_each_possible_cpu(cpu) is more simple and direct.
>
> Handle this special case only if bt_address() returns 0 to avoid
> affecting performance for the normal address.
It is a little more complex than V1, since unwinder is not
performance-critical, I prefer the V1 logic.

Huacai

>
> Here is the draft change:
>
> ----->8-----
> diff --git a/arch/loongarch/kernel/unwind_orc.c
> b/arch/loongarch/kernel/unwind_orc.c
> index d6b3688a1ce9..1ce10701f3c3 100644
> --- a/arch/loongarch/kernel/unwind_orc.c
> +++ b/arch/loongarch/kernel/unwind_orc.c
> @@ -378,6 +378,28 @@ static inline unsigned long bt_address(unsigned
> long ra)
>          return 0;
>   }
>
> +static inline unsigned long handle_pcpu_handlers(unsigned long ra)
> +{
> +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
> +       int cpu;
> +       int vec_sz __maybe_unused;
> +
> +       for_each_possible_cpu(cpu) {
> +               if (!pcpu_handlers[cpu])
> +                       continue;
> +
> +               vec_sz = sizeof(exception_handlers);
> +
> +               if (ra >= pcpu_handlers[cpu] &&
> +                   ra < pcpu_handlers[cpu] + vec_sz) {
> +                       ra = eentry + (ra - pcpu_handlers[cpu]);
> +                       break;
> +               }
> +       }
> +#endif
> +       return ra;
> +}
> +
>   bool unwind_next_frame(struct unwind_state *state)
>   {
>          unsigned long *p, pc;
> @@ -494,8 +516,11 @@ bool unwind_next_frame(struct unwind_state *state)
>
>          state->pc = bt_address(pc);
>          if (!state->pc) {
> -               pr_err("cannot find unwind pc at %px\n", (void *)pc);
> -               goto err;
> +               state->pc = bt_address(handle_pcpu_handlers(pc));
> +               if (!state->pc) {
> +                       pr_err("cannot find unwind pc at %px\n", (void
> *)pc);
> +                       goto err;
> +               }
>          }
>
>          return true;
>
> I will test it, if it works well I will wait for more comments and
> submit v2 later.
>
> Thanks,
> Tiezhu
>