[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <a270f924-22ca-450f-b3f6-826e3d2676d2@gaisler.com>
Date: Mon, 26 Jan 2026 18:03:34 +0100
From: Andreas Larsson <andreas@...sler.com>
To: Ludwig Rydberg <ludwig.rydberg@...sler.com>, davem@...emloft.net,
brauner@...nel.org, shuah@...nel.org
Cc: sparclinux@...r.kernel.org, linux-kselftest@...r.kernel.org,
linux-kernel@...r.kernel.org, arnd@...db.de, glaubitz@...sik.fu-berlin.de,
geert@...ux-m68k.org, schuster.simon@...mens-energy.com, jrtc27@...c27.com
Subject: Re: [PATCH v2 2/2] sparc: Add architecture support for clone3
On 2026-01-19 15:47, Ludwig Rydberg wrote:
> Add support for the clone3 system call to the SPARC architectures.
>
> The implementation follows the pattern of the original clone syscall.
> However, instead of explicitly calling kernel_clone, the clone3
> handler calls the generic sys_clone3 handler in kernel/fork.
> In case no stack is provided, the parents stack is reused.
>
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
>
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@...sler.com>
> ---
> arch/sparc/include/asm/syscalls.h | 1 +
> arch/sparc/include/asm/unistd.h | 2 --
> arch/sparc/kernel/entry.S | 15 ++++++++++++++
> arch/sparc/kernel/kernel.h | 1 +
> arch/sparc/kernel/process.c | 14 +++++++++++++
> arch/sparc/kernel/process_32.c | 23 ++++++++++++++++------
> arch/sparc/kernel/process_64.c | 27 ++++++++++++++++++++------
> arch/sparc/kernel/syscalls.S | 8 ++++++++
> arch/sparc/kernel/syscalls/syscall.tbl | 2 +-
> 9 files changed, 78 insertions(+), 15 deletions(-)
>
> diff --git a/arch/sparc/include/asm/syscalls.h b/arch/sparc/include/asm/syscalls.h
> index 35575fbfb9dc..282e62b66518 100644
> --- a/arch/sparc/include/asm/syscalls.h
> +++ b/arch/sparc/include/asm/syscalls.h
> @@ -7,5 +7,6 @@ struct pt_regs;
> asmlinkage long sparc_fork(struct pt_regs *regs);
> asmlinkage long sparc_vfork(struct pt_regs *regs);
> asmlinkage long sparc_clone(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>
> #endif /* _SPARC64_SYSCALLS_H */
> diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
> index 3380411a4537..d6bc76706a7a 100644
> --- a/arch/sparc/include/asm/unistd.h
> +++ b/arch/sparc/include/asm/unistd.h
> @@ -49,8 +49,6 @@
> #define __ARCH_WANT_COMPAT_STAT
> #endif
>
> -#define __ARCH_BROKEN_SYS_CLONE3
> -
> #ifdef __32bit_syscall_numbers__
> /* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
> * it never had the plain ones and there is no value to adding those
> diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
> index a3fdee4cd6fa..ea51ef52c952 100644
> --- a/arch/sparc/kernel/entry.S
> +++ b/arch/sparc/kernel/entry.S
> @@ -907,6 +907,21 @@ flush_patch_four:
> jmpl %l1 + %lo(sparc_vfork), %g0
> add %sp, STACKFRAME_SZ, %o0
>
> + .globl __sys_clone3, flush_patch_five
> +__sys_clone3:
> + mov %o7, %l5
> +flush_patch_five:
> + FLUSH_ALL_KERNEL_WINDOWS;
> + ld [%curptr + TI_TASK], %o4
> + rd %psr, %g4
> + WRITE_PAUSE
> + rd %wim, %g5
> + WRITE_PAUSE
> + std %g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
> + add %sp, STACKFRAME_SZ, %o0
> + call sparc_clone3
> + mov %l5, %o7
> +
> .align 4
> linux_sparc_ni_syscall:
> sethi %hi(sys_ni_syscall), %l7
> diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
> index 8328a3b78a44..4ee85051521a 100644
> --- a/arch/sparc/kernel/kernel.h
> +++ b/arch/sparc/kernel/kernel.h
> @@ -18,6 +18,7 @@ extern int ncpus_probed;
> asmlinkage long sparc_clone(struct pt_regs *regs);
> asmlinkage long sparc_fork(struct pt_regs *regs);
> asmlinkage long sparc_vfork(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>
> #ifdef CONFIG_SPARC64
> /* setup_64.c */
> diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
> index 7d69877511fa..d72fa0665943 100644
> --- a/arch/sparc/kernel/process.c
> +++ b/arch/sparc/kernel/process.c
> @@ -12,6 +12,7 @@
> #include <linux/sched/task.h>
> #include <linux/sched/task_stack.h>
> #include <linux/signal.h>
> +#include <linux/syscalls.h>
>
> #include "kernel.h"
>
> @@ -118,3 +119,16 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
>
> return ret;
> }
> +
> +asmlinkage long sparc_clone3(struct pt_regs *regs)
> +{
> + unsigned long sz;
> + struct clone_args __user *cl_args;
> +
> + synchronize_user_stack();
> +
> + cl_args = (struct clone_args __user *)regs->u_regs[UREG_I0];
> + sz = regs->u_regs[UREG_I1];
> +
> + return sys_clone3(cl_args, sz);
> +}
> diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
> index 5a28c0e91bf1..dd8c6c02b0f1 100644
> --- a/arch/sparc/kernel/process_32.c
> +++ b/arch/sparc/kernel/process_32.c
> @@ -247,6 +247,8 @@ clone_stackframe(struct sparc_stackf __user *dst,
> * Parent --> %o0 == childs pid, %o1 == 0
> * Child --> %o0 == parents pid, %o1 == 1
> *
> + * clone3() - Uses regular kernel return value conventions
> + *
> * NOTE: We have a separate fork kpsr/kwim because
> * the parent could change these values between
> * sys_fork invocation and when we reach here
> @@ -261,11 +263,11 @@ extern void ret_from_kernel_thread(void);
> int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
> {
> u64 clone_flags = args->flags;
> - unsigned long sp = args->stack;
> unsigned long tls = args->tls;
> struct thread_info *ti = task_thread_info(p);
> struct pt_regs *childregs, *regs = current_pt_regs();
> char *new_stack;
> + unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>
> #ifndef CONFIG_SMP
> if(last_task_used_math == current) {
> @@ -350,13 +352,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
> childregs->psr &= ~PSR_EF;
> clear_tsk_thread_flag(p, TIF_USEDFPU);
> #endif
> + /* Handle return value conventions */
> + if (regs->u_regs[UREG_G1] == __NR_clone3) {
> + /* clone3() - use regular kernel return value convention */
> +
> + /* Set the return value for the child. */
> + childregs->u_regs[UREG_I0] = 0;
> + } else {
> + /* clone()/fork() - use SunOS return value convention */
>
> - /* Set the return value for the child. */
> - childregs->u_regs[UREG_I0] = current->pid;
> - childregs->u_regs[UREG_I1] = 1;
> + /* Set the return value for the child. */
> + childregs->u_regs[UREG_I0] = current->pid;
> + childregs->u_regs[UREG_I1] = 1;
>
> - /* Set the return value for the parent. */
> - regs->u_regs[UREG_I1] = 0;
> + /* Set the return value for the parent. */
> + regs->u_regs[UREG_I1] = 0;
> + }
>
> if (clone_flags & CLONE_SETTLS)
> childregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
> index 25781923788a..e889da8e4835 100644
> --- a/arch/sparc/kernel/process_64.c
> +++ b/arch/sparc/kernel/process_64.c
> @@ -564,17 +564,19 @@ void fault_in_user_windows(struct pt_regs *regs)
> * under SunOS are nothing short of bletcherous:
> * Parent --> %o0 == childs pid, %o1 == 0
> * Child --> %o0 == parents pid, %o1 == 1
> + *
> + * clone3() - Uses regular kernel return value conventions
> */
> int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
> {
> u64 clone_flags = args->flags;
> - unsigned long sp = args->stack;
> unsigned long tls = args->tls;
> struct thread_info *t = task_thread_info(p);
> struct pt_regs *regs = current_pt_regs();
> struct sparc_stackf *parent_sf;
> unsigned long child_stack_sz;
> char *child_trap_frame;
> + unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>
> /* Calculate offset to stack_frame & pt_regs */
> child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
> @@ -616,12 +618,25 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
> if (t->utraps)
> t->utraps[0]++;
>
> - /* Set the return value for the child. */
> - t->kregs->u_regs[UREG_I0] = current->pid;
> - t->kregs->u_regs[UREG_I1] = 1;
> + /* Handle return value conventions */
> + if (regs->u_regs[UREG_G1] == __NR_clone3) {
> + /* clone3() - use regular kernel return value convention */
> +
> + /* Set the return value for the child. */
> + t->kregs->u_regs[UREG_I0] = 0;
> +
> + /* Clear g1 to indicate user thread */
> + t->kregs->u_regs[UREG_G1] = 0;
> + } else {
> + /* clone()/fork() - use SunOS return value convention */
> +
> + /* Set the return value for the child. */
> + t->kregs->u_regs[UREG_I0] = current->pid;
> + t->kregs->u_regs[UREG_I1] = 1;
>
> - /* Set the second return value for the parent. */
> - regs->u_regs[UREG_I1] = 0;
> + /* Set the second return value for the parent. */
> + regs->u_regs[UREG_I1] = 0;
> + }
>
> if (clone_flags & CLONE_SETTLS)
> t->kregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
> index 0e8ab0602c36..96fe8763d70c 100644
> --- a/arch/sparc/kernel/syscalls.S
> +++ b/arch/sparc/kernel/syscalls.S
> @@ -103,6 +103,12 @@ sys_clone:
> ba,pt %xcc, sparc_clone
> add %sp, PTREGS_OFF, %o0
>
> + .align 32
> +__sys_clone3:
> + flushw
> + ba,pt %xcc, sparc_clone3
> + add %sp, PTREGS_OFF, %o0
> +
> .globl ret_from_fork
> ret_from_fork:
> /* Clear current_thread_info()->new_child. */
> @@ -113,6 +119,8 @@ ret_from_fork:
> brnz,pt %o0, ret_sys_call
> ldx [%g6 + TI_FLAGS], %l0
> ldx [%sp + PTREGS_OFF + PT_V9_G1], %l1
> + brz,pt %l1, ret_sys_call
> + nop
> call %l1
> ldx [%sp + PTREGS_OFF + PT_V9_G2], %o0
> ba,pt %xcc, ret_sys_call
> diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
> index 39aa26b6a50b..c0307bb09892 100644
> --- a/arch/sparc/kernel/syscalls/syscall.tbl
> +++ b/arch/sparc/kernel/syscalls/syscall.tbl
> @@ -480,7 +480,7 @@
> 432 common fsmount sys_fsmount
> 433 common fspick sys_fspick
> 434 common pidfd_open sys_pidfd_open
> -# 435 reserved for clone3
> +435 common clone3 __sys_clone3
> 436 common close_range sys_close_range
> 437 common openat2 sys_openat2
> 438 common pidfd_getfd sys_pidfd_getfd
Reviewed-by: Andreas Larsson <andreas@...sler.com>
Tested-by: Andreas Larsson <andreas@...sler.com>
Thanks,
Andreas
Powered by blists - more mailing lists