Skip to content

Commit

Permalink
riscv/syscall: Optimize user service call performance
Browse files Browse the repository at this point in the history
This patch changes how user service calls are executed:
Instead of using the common interrupt logic, execute the user service
call directly.

Why? When a user makes a service call request, all of the service call
parameters are already loaded into the correct registers, thus it makes
no sense to first clobber them and then reload them, which is what the
old logic does. It is much more effective to run the system call directly.

During a user system call the interrupts must be re-enabled, which the
new logic does as soon as we know the exception is a user service call
request.

This patch does NOT change the behavior of reserved system calls (like
switch_context), only the user service call request is affected.
  • Loading branch information
pussuw authored and acassis committed Jun 1, 2024
1 parent a5574d9 commit e6973c7
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 219 deletions.
30 changes: 0 additions & 30 deletions arch/risc-v/include/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,6 @@

/* Configuration ************************************************************/

/* If this is a kernel build, how many nested system calls should we
* support?
*/

#ifndef CONFIG_SYS_NNEST
# define CONFIG_SYS_NNEST 2
#endif

/* Processor PC */

#define REG_EPC_NDX 0
Expand Down Expand Up @@ -539,18 +531,6 @@

#ifndef __ASSEMBLY__

/* This structure represents the return state from a system call */

#ifdef CONFIG_LIB_SYSCALL
struct xcpt_syscall_s
{
uintptr_t sysreturn; /* The return PC */
#ifndef CONFIG_BUILD_FLAT
uintptr_t int_ctx; /* Interrupt context (i.e. m-/sstatus) */
#endif
};
#endif

/* The following structure is included in the TCB and defines the complete
* state of the thread.
*/
Expand Down Expand Up @@ -582,16 +562,6 @@ struct xcptcontext
uintptr_t sigreturn;
#endif

#ifdef CONFIG_LIB_SYSCALL
/* The following array holds information needed to return from each nested
* system call.
*/

uint8_t nsyscalls;
struct xcpt_syscall_s syscall[CONFIG_SYS_NNEST];

#endif

#ifdef CONFIG_ARCH_ADDRENV
#ifdef CONFIG_ARCH_KERNEL_STACK
/* In this configuration, all syscalls execute from an internal kernel
Expand Down
9 changes: 0 additions & 9 deletions arch/risc-v/include/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,6 @@

#define SYS_switch_context (2)

#ifdef CONFIG_LIB_SYSCALL
/* SYS call 3:
*
* void riscv_syscall_return(void);
*/

#define SYS_syscall_return (3)
#endif /* CONFIG_LIB_SYSCALL */

#ifndef CONFIG_BUILD_FLAT
/* SYS call 4:
*
Expand Down
78 changes: 68 additions & 10 deletions arch/risc-v/src/common/riscv_exception_common.S
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@

#include <sys/types.h>

#ifdef CONFIG_LIB_SYSCALL
# include <syscall.h>
#endif

#include "chip.h"

#include "riscv_percpu.h"
Expand Down Expand Up @@ -73,6 +77,7 @@
.section EXCEPTION_SECTION
.global exception_common
.global return_from_exception
.global return_from_syscall
.align 8

exception_common:
Expand All @@ -99,24 +104,69 @@ exception_common:
addi sp, sp, -XCPTCONTEXT_SIZE
save_ctx sp

csrr s0, CSR_STATUS
REGSTORE s0, REG_INT_CTX(sp) /* status */
csrr s0, CSR_STATUS /* s0=status */
csrr s1, CSR_EPC /* s1=exception PC */
csrr s2, CSR_CAUSE /* s2=cause */

#ifdef CONFIG_ARCH_KERNEL_STACK
csrr s0, CSR_SCRATCH
REGLOAD s0, RISCV_PERCPU_USP(s0)
csrr s3, CSR_SCRATCH
REGLOAD s3, RISCV_PERCPU_USP(s3)
#else
addi s0, sp, XCPTCONTEXT_SIZE
addi s3, sp, XCPTCONTEXT_SIZE
#endif

REGSTORE s0, REG_X2(sp) /* original SP */
REGSTORE s0, REG_INT_CTX(sp)
REGSTORE s1, REG_EPC(sp)
REGSTORE s3, REG_SP(sp)

#ifdef CONFIG_LIB_SYSCALL
/* Check whether it is an exception or interrupt */

blt s2, x0, handle_irq /* If cause < 0 it is interrupt */

/* Is it a system call ? */

li s3, RISCV_IRQ_ECALLU /* Is it a system call ? */
bne s2, s3, handle_irq

/* Is it one of the reserved system calls ? */

li s3, CONFIG_SYS_RESERVED
blt a0, s3, handle_irq /* If a0 < CONFIG_SYS_RESERVED */

/* It is a system call, re-enable interrupts if they were enabled */

andi s3, s0, STATUS_PIE
beqz s3, 1f
csrs CSR_STATUS, STATUS_IE

1:
addi s1, s1, 0x4 /* Must move EPC forward by +4 */
REGSTORE s1, REG_EPC(sp) /* Updated EPC to user context */

csrr tp, CSR_SCRATCH /* Load kernel TP */
REGLOAD tp, RISCV_PERCPU_TCB(tp)

call x1, dispatch_syscall /* Dispatch the system call */

return_from_syscall:

/* System call is done, disable interrupts */

csrc CSR_STATUS, STATUS_IE

csrr s0, CSR_EPC
REGSTORE s0, REG_EPC(sp) /* exception PC */
/* Clean up after system call */

REGSTORE a0, REG_A0(sp) /* Syscall return value to user context */
mv a0, sp /* Return to same context */
tail return_from_exception

handle_irq:
#endif

/* Setup arg0(exception cause), arg1(context) */

csrr a0, CSR_CAUSE /* exception cause */
mv a0, s2 /* exception cause */
mv a1, sp /* context = sp */

#if CONFIG_ARCH_INTERRUPTSTACK > 15
Expand Down Expand Up @@ -152,9 +202,17 @@ return_from_exception:
REGLOAD s0, REG_EPC(sp) /* restore sepc */
csrw CSR_EPC, s0

REGLOAD s0, REG_INT_CTX(sp) /* restore sstatus */
REGLOAD s0, REG_INT_CTX(sp) /* restore status */
csrw CSR_STATUS, s0

#ifdef CONFIG_LIB_SYSCALL
/* Store tcb to scratch register */

call x1, nxsched_self
csrr s1, CSR_SCRATCH
REGSTORE a0, RISCV_PERCPU_TCB(s1)
#endif

#ifdef CONFIG_ARCH_KERNEL_STACK
/* Returning to userspace ? */

Expand Down
23 changes: 0 additions & 23 deletions arch/risc-v/src/common/riscv_fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,29 +246,6 @@ pid_t riscv_fork(const struct fork_s *context)
fregs[REG_FS11] = context->fs11; /* Saved register fs11 */
#endif

#ifdef CONFIG_LIB_SYSCALL
/* If we got here via a syscall, then we are going to have to setup some
* syscall return information as well.
*/

if (parent->xcp.nsyscalls > 0)
{
int index;
for (index = 0; index < parent->xcp.nsyscalls; index++)
{
child->cmn.xcp.syscall[index].sysreturn =
parent->xcp.syscall[index].sysreturn;

#ifndef CONFIG_BUILD_FLAT
child->cmn.xcp.syscall[index].int_ctx =
parent->xcp.syscall[index].int_ctx;
#endif
}

child->cmn.xcp.nsyscalls = parent->xcp.nsyscalls;
}
#endif /* CONFIG_LIB_SYSCALL */

/* And, finally, start the child task. On a failure, nxtask_start_fork()
* will discard the TCB by calling nxtask_abort_fork().
*/
Expand Down
10 changes: 0 additions & 10 deletions arch/risc-v/src/common/riscv_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,16 +438,6 @@ void *riscv_perform_syscall(uintptr_t *regs);
#define riscv_switchcontext(prev, next) \
sys_call2(SYS_switch_context, (uintptr_t)prev, (uintptr_t)next)

#ifdef CONFIG_BUILD_KERNEL
/* SYS call 3:
*
* void riscv_syscall_return(void);
*/

#define riscv_syscall_return() sys_call0(SYS_syscall_return)

#endif /* CONFIG_BUILD_KERNEL */

#undef EXTERN
#ifdef __cplusplus
}
Expand Down
Loading

0 comments on commit e6973c7

Please sign in to comment.