Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARC: __switch_to: asm with dwarf ops (vs. inline asm)

__switch_to() is final step of context switch, swapping kernel modes
stack (and callee regs) of outgoing task with next task.

It is also the starting point of stack unwinging of a sleeping task and
captures SP, FP, BLINK and the corresponding dwarf info. Back when
dinosaurs still roamed around, ARC gas didn't support CFI pseudo ops and
gcc was responsible for generating dwarf info. Thus it had to be written
in "C" with inline asm to do the hand crafting of stack. The function
prologue (and crucial saving of blink etc) was still gcc generated but
not visible in code. Likewise dwarf info was missing.

Now with modern tools, we can make things more obvious by writing the
code in asm and adding approproate dwarf cfi pseudo ops.

This is mostly non functional change, except for slight chnages to asm

- ARCompact doesn't support MOV_S fp, sp, so we use MOV

Signed-off-by: Vineet Gupta <vgupta@kernel.org>

+58 -152
+20 -12
arch/arc/include/asm/dwarf.h
··· 10 10 11 11 #ifdef ARC_DW2_UNWIND_AS_CFI 12 12 13 - #define CFI_STARTPROC .cfi_startproc 14 - #define CFI_ENDPROC .cfi_endproc 15 - #define CFI_DEF_CFA .cfi_def_cfa 16 - #define CFI_REGISTER .cfi_register 17 - #define CFI_REL_OFFSET .cfi_rel_offset 18 - #define CFI_UNDEFINED .cfi_undefined 13 + #define CFI_STARTPROC .cfi_startproc 14 + #define CFI_ENDPROC .cfi_endproc 15 + #define CFI_DEF_CFA .cfi_def_cfa 16 + #define CFI_DEF_CFA_OFFSET .cfi_def_cfa_offset 17 + #define CFI_DEF_CFA_REGISTER .cfi_def_cfa_register 18 + #define CFI_OFFSET .cfi_offset 19 + #define CFI_REL_OFFSET .cfi_rel_offset 20 + #define CFI_REGISTER .cfi_register 21 + #define CFI_RESTORE .cfi_restore 22 + #define CFI_UNDEFINED .cfi_undefined 19 23 20 24 #else 21 25 22 26 #define CFI_IGNORE # 23 27 24 - #define CFI_STARTPROC CFI_IGNORE 25 - #define CFI_ENDPROC CFI_IGNORE 26 - #define CFI_DEF_CFA CFI_IGNORE 27 - #define CFI_REGISTER CFI_IGNORE 28 - #define CFI_REL_OFFSET CFI_IGNORE 29 - #define CFI_UNDEFINED CFI_IGNORE 28 + #define CFI_STARTPROC CFI_IGNORE 29 + #define CFI_ENDPROC CFI_IGNORE 30 + #define CFI_DEF_CFA CFI_IGNORE 31 + #define CFI_DEF_CFA_OFFSET CFI_IGNORE 32 + #define CFI_DEF_CFA_REGISTER CFI_IGNORE 33 + #define CFI_OFFSET CFI_IGNORE 34 + #define CFI_REL_OFFSET CFI_IGNORE 35 + #define CFI_REGISTER CFI_IGNORE 36 + #define CFI_RESTORE CFI_IGNORE 37 + #define CFI_UNDEFINED CFI_IGNORE 30 38 31 39 #endif /* !ARC_DW2_UNWIND_AS_CFI */ 32 40
+2 -7
arch/arc/kernel/Makefile
··· 5 5 6 6 obj-y := head.o arcksyms.o setup.o irq.o reset.o ptrace.o process.o devtree.o 7 7 obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o 8 + obj-y += ctx_sw_asm.o 9 + 8 10 obj-$(CONFIG_ISA_ARCOMPACT) += entry-compact.o intc-compact.o 9 11 obj-$(CONFIG_ISA_ARCV2) += entry-arcv2.o intc-arcv2.o 10 12 ··· 24 22 obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o 25 23 ifdef CONFIG_ISA_ARCOMPACT 26 24 CFLAGS_fpu.o += -mdpfp 27 - endif 28 - 29 - ifdef CONFIG_ARC_DW2_UNWIND 30 - CFLAGS_ctx_sw.o += -fno-omit-frame-pointer 31 - obj-y += ctx_sw.o 32 - else 33 - obj-y += ctx_sw_asm.o 34 25 endif 35 26 36 27 extra-y := vmlinux.lds
-103
arch/arc/kernel/ctx_sw.c
··· 1 - // SPDX-License-Identifier: GPL-2.0-only 2 - /* 3 - * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) 4 - * 5 - * Vineetg: Aug 2009 6 - * -"C" version of lowest level context switch asm macro called by schedular 7 - * gcc doesn't generate the dward CFI info for hand written asm, hence can't 8 - * backtrace out of it (e.g. tasks sleeping in kernel). 9 - * So we cheat a bit by writing almost similar code in inline-asm. 10 - * -This is a hacky way of doing things, but there is no other simple way. 11 - * I don't want/intend to extend unwinding code to understand raw asm 12 - */ 13 - 14 - #include <asm/asm-offsets.h> 15 - #include <asm/switch_to.h> 16 - #include <linux/sched/debug.h> 17 - 18 - #define KSP_WORD_OFF ((TASK_THREAD + THREAD_KSP) / 4) 19 - 20 - struct task_struct *__sched 21 - __switch_to(struct task_struct *prev_task, struct task_struct *next_task) 22 - { 23 - unsigned int tmp; 24 - unsigned int prev = (unsigned int)prev_task; 25 - unsigned int next = (unsigned int)next_task; 26 - 27 - __asm__ __volatile__( 28 - /* FP/BLINK save generated by gcc (standard function prologue */ 29 - "st.a r13, [sp, -4] \n\t" 30 - "st.a r14, [sp, -4] \n\t" 31 - "st.a r15, [sp, -4] \n\t" 32 - "st.a r16, [sp, -4] \n\t" 33 - "st.a r17, [sp, -4] \n\t" 34 - "st.a r18, [sp, -4] \n\t" 35 - "st.a r19, [sp, -4] \n\t" 36 - "st.a r20, [sp, -4] \n\t" 37 - "st.a r21, [sp, -4] \n\t" 38 - "st.a r22, [sp, -4] \n\t" 39 - "st.a r23, [sp, -4] \n\t" 40 - "st.a r24, [sp, -4] \n\t" 41 - "st.a r25, [sp, -4] \n\t" 42 - 43 - /* set ksp of outgoing task in tsk->thread.ksp */ 44 - #if KSP_WORD_OFF <= 255 45 - "st.as sp, [%3, %1] \n\t" 46 - #else 47 - /* 48 - * Workaround for NR_CPUS=4k 49 - * %1 is bigger than 255 (S9 offset for st.as) 50 - */ 51 - "add2 r24, %3, %1 \n\t" 52 - "st sp, [r24] \n\t" 53 - #endif 54 - 55 - /* 56 - * setup _current_task with incoming tsk. 57 - * optionally, set caching reg to that as well 58 - * For SMP extra work to get to &_current_task[cpu] 59 - * (open coded SET_CURR_TASK_ON_CPU) 60 - */ 61 - #ifndef CONFIG_SMP 62 - "st %2, [@_current_task] \n\t" 63 - #else 64 - "lr r24, [identity] \n\t" 65 - "lsr r24, r24, 8 \n\t" 66 - "bmsk r24, r24, 7 \n\t" 67 - "add2 r24, @_current_task, r24 \n\t" 68 - "st %2, [r24] \n\t" 69 - #endif 70 - #ifdef CONFIG_ARC_CURR_IN_REG 71 - "mov gp, %2 \n\t" 72 - #endif 73 - 74 - /* get ksp of incoming task from tsk->thread.ksp */ 75 - "ld.as sp, [%2, %1] \n\t" 76 - 77 - /* start loading it's CALLEE reg file */ 78 - "ld.ab r25, [sp, 4] \n\t" 79 - "ld.ab r24, [sp, 4] \n\t" 80 - "ld.ab r23, [sp, 4] \n\t" 81 - "ld.ab r22, [sp, 4] \n\t" 82 - "ld.ab r21, [sp, 4] \n\t" 83 - "ld.ab r20, [sp, 4] \n\t" 84 - "ld.ab r19, [sp, 4] \n\t" 85 - "ld.ab r18, [sp, 4] \n\t" 86 - "ld.ab r17, [sp, 4] \n\t" 87 - "ld.ab r16, [sp, 4] \n\t" 88 - "ld.ab r15, [sp, 4] \n\t" 89 - "ld.ab r14, [sp, 4] \n\t" 90 - "ld.ab r13, [sp, 4] \n\t" 91 - 92 - /* last (ret value) = prev : although for ARC it mov r0, r0 */ 93 - "mov %0, %3 \n\t" 94 - 95 - /* FP/BLINK restore generated by gcc (standard func epilogue */ 96 - 97 - : "=r"(tmp) 98 - : "n"(KSP_WORD_OFF), "r"(next), "r"(prev) 99 - : "blink" 100 - ); 101 - 102 - return (struct task_struct *)tmp; 103 - }
+36 -30
arch/arc/kernel/ctx_sw_asm.S
··· 13 13 14 14 #define KSP_WORD_OFF ((TASK_THREAD + THREAD_KSP) / 4) 15 15 16 - ;################### Low Level Context Switch ########################## 16 + ; IN 17 + ; - r0: prev task (also current) 18 + ; - r1: next task 19 + ; OUT 20 + ; - r0: prev task (so r0 not touched) 17 21 18 22 .section .sched.text,"ax",@progbits 19 - .align 4 20 - .global __switch_to 21 - .type __switch_to, @function 22 - __switch_to: 23 - CFI_STARTPROC 23 + ENTRY_CFI(__switch_to) 24 24 25 - /* Save regs on kernel mode stack of task */ 26 - st.a blink, [sp, -4] 27 - st.a fp, [sp, -4] 25 + /* save kernel stack frame regs of @prev task */ 26 + push blink 27 + CFI_DEF_CFA_OFFSET 4 28 + CFI_OFFSET r31, -4 29 + 30 + push fp 31 + CFI_DEF_CFA_OFFSET 8 32 + CFI_OFFSET r27, -8 33 + 34 + mov fp, sp 35 + CFI_DEF_CFA_REGISTER r27 36 + 37 + /* kernel mode callee regs of @prev */ 28 38 SAVE_CALLEE_SAVED_KERNEL 29 39 30 - /* Save the now KSP in task->thread.ksp */ 40 + /* save final SP to @prev->thread.ksp */ 31 41 #if KSP_WORD_OFF <= 255 32 42 st.as sp, [r0, KSP_WORD_OFF] 33 43 #else 34 44 /* Workaround for NR_CPUS=4k as ST.as can only take s9 offset */ 35 - add2 r24, r0, KSP_WORD_OFF 36 - st sp, [r24] 45 + add2 r10, r0, KSP_WORD_OFF 46 + st sp, [r10] 37 47 #endif 38 - /* 39 - * Return last task in r0 (return reg) 40 - * On ARC, Return reg = First Arg reg = r0. 41 - * Since we already have last task in r0, 42 - * don't need to do anything special to return it 43 - */ 48 + /* update @next in _current_task[] and GP register caching it */ 49 + SET_CURR_TASK_ON_CPU r1, r10 44 50 45 - /* 46 - * switch to new task, contained in r1 47 - * Temp reg r3 is required to get the ptr to store val 48 - */ 49 - SET_CURR_TASK_ON_CPU r1, r3 51 + /* load SP from @next->thread.ksp */ 52 + ld.as sp, [r1, KSP_WORD_OFF] 50 53 51 - /* reload SP with kernel mode stack pointer in task->thread.ksp */ 52 - ld.as sp, [r1, KSP_WORD_OFF] 53 - 54 - /* restore the registers */ 54 + /* restore callee regs, stack frame regs of @next */ 55 55 RESTORE_CALLEE_SAVED_KERNEL 56 - ld.ab fp, [sp, 4] 57 - ld.ab blink, [sp, 4] 58 - j [blink] 59 56 57 + pop fp 58 + CFI_RESTORE r27 59 + CFI_DEF_CFA r28, 4 60 + 61 + pop blink 62 + CFI_RESTORE r31 63 + CFI_DEF_CFA_OFFSET 0 64 + 65 + j [blink] 60 66 END_CFI(__switch_to)