[PATCH 2/3] sparc: Add architecture support for clone3

Ludwig Rydberg posted 3 patches 3 weeks, 3 days ago
There is a newer version of this series
[PATCH 2/3] sparc: Add architecture support for clone3
Posted by Ludwig Rydberg 3 weeks, 3 days ago
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 call conventions for clone on SPARC are kept for clone3:
  Parent -->  %o0 == child's  pid, %o1 == 0
  Child  -->  %o0 == parent's pid, %o1 == 1

Closes: https://github.com/sparclinux/issues/issues/10
Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.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            | 25 +++++++++++++++++++++++++
 arch/sparc/kernel/process_32.c         |  2 +-
 arch/sparc/kernel/process_64.c         |  2 +-
 arch/sparc/kernel/syscalls.S           |  6 ++++++
 arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
 9 files changed, 51 insertions(+), 5 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..b8e23295db69 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,27 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
 
 	return ret;
 }
+
+asmlinkage long sparc_clone3(struct pt_regs *regs)
+{
+	unsigned long sz;
+	long ret;
+	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];
+
+	ret = sys_clone3(cl_args, sz);
+
+	/* If we get an error and potentially restart the system
+	 * call, we're screwed because copy_thread() clobbered
+	 * the parent's %o1.  So detect that case and restore it
+	 * here.
+	 */
+	if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK)
+		regs->u_regs[UREG_I1] = sz;
+
+	return ret;
+}
diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
index 5a28c0e91bf1..216c07971c81 100644
--- a/arch/sparc/kernel/process_32.c
+++ b/arch/sparc/kernel/process_32.c
@@ -261,11 +261,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) {
diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
index 25781923788a..885d617ba29d 100644
--- a/arch/sparc/kernel/process_64.c
+++ b/arch/sparc/kernel/process_64.c
@@ -568,13 +568,13 @@ void fault_in_user_windows(struct pt_regs *regs)
 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);
diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
index 0e8ab0602c36..c8d374a37f98 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. */
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
-- 
2.35.3
Re: [PATCH 2/3] sparc: Add architecture support for clone3
Posted by John Paul Adrian Glaubitz 3 weeks, 3 days ago
Hi Ludwig,

On Fri, 2026-01-16 at 16:30 +0100, 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 call conventions for clone on SPARC are kept for clone3:
>   Parent -->  %o0 == child's  pid, %o1 == 0
>   Child  -->  %o0 == parent's pid, %o1 == 1
> 
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.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            | 25 +++++++++++++++++++++++++
>  arch/sparc/kernel/process_32.c         |  2 +-
>  arch/sparc/kernel/process_64.c         |  2 +-
>  arch/sparc/kernel/syscalls.S           |  6 ++++++
>  arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
>  9 files changed, 51 insertions(+), 5 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..b8e23295db69 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,27 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
>  
>  	return ret;
>  }
> +
> +asmlinkage long sparc_clone3(struct pt_regs *regs)
> +{
> +	unsigned long sz;
> +	long ret;
> +	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];
> +
> +	ret = sys_clone3(cl_args, sz);
> +
> +	/* If we get an error and potentially restart the system
> +	 * call, we're screwed because copy_thread() clobbered
> +	 * the parent's %o1.  So detect that case and restore it
> +	 * here.
> +	 */
> +	if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK)
> +		regs->u_regs[UREG_I1] = sz;
> +
> +	return ret;
> +}
> diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
> index 5a28c0e91bf1..216c07971c81 100644
> --- a/arch/sparc/kernel/process_32.c
> +++ b/arch/sparc/kernel/process_32.c
> @@ -261,11 +261,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) {
> diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
> index 25781923788a..885d617ba29d 100644
> --- a/arch/sparc/kernel/process_64.c
> +++ b/arch/sparc/kernel/process_64.c
> @@ -568,13 +568,13 @@ void fault_in_user_windows(struct pt_regs *regs)
>  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);
> diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
> index 0e8ab0602c36..c8d374a37f98 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. */
> 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

Applied on top of 6.19-rc5, tested on a Sun Netra 240 (UltraSPARC IIIi).

Running the kernel selftest for clone3 works fine:

root@raverin:/usr/src/linux/tools/testing/selftests/clone3# uname -a
Linux raverin 6.19.0-rc5+ #18 Fri Jan 16 16:02:10 UTC 2026 sparc64 GNU/Linux
root@raverin:/usr/src/linux/tools/testing/selftests/clone3# make
  CC       clone3
  CC       clone3_clear_sighand
  CC       clone3_set_tid
  CC       clone3_cap_checkpoint_restore
root@raverin:/usr/src/linux/tools/testing/selftests/clone3# ./clone3
TAP version 13
1..19
# clone3() syscall supported
# Running test 'simple clone3()'
# [1385] Trying clone3() with flags 0 (size 0)
# I am the parent (1385). My child's pid is 1386
# I am the child, my PID is 1386
# [1385] clone3() with flags says: 0 expected 0
ok 1 simple clone3()
# Running test 'clone3() in a new PID_NS'
# [1385] Trying clone3() with flags 0x20000000 (size 0)
# I am the child, my PID is 1
# I am the parent (1385). My child's pid is 1387
# [1385] clone3() with flags says: 0 expected 0
ok 2 clone3() in a new PID_NS
# Running test 'CLONE_ARGS_SIZE_VER0'
# [1385] Trying clone3() with flags 0 (size 64)
# I am the parent (1385). My child's pid is 1388
# I am the child, my PID is 1388
# [1385] clone3() with flags says: 0 expected 0
ok 3 CLONE_ARGS_SIZE_VER0
# Running test 'CLONE_ARGS_SIZE_VER0 - 8'
# [1385] Trying clone3() with flags 0 (size 56)
# Invalid argument - Failed to create new process
# [1385] clone3() with flags says: -22 expected -22
ok 4 CLONE_ARGS_SIZE_VER0 - 8
# Running test 'sizeof(struct clone_args) + 8'
# [1385] Trying clone3() with flags 0 (size 96)
# I am the parent (1385). My child's pid is 1389
# I am the child, my PID is 1389
# [1385] clone3() with flags says: 0 expected 0
ok 5 sizeof(struct clone_args) + 8
# Running test 'exit_signal with highest 32 bits non-zero'
# [1385] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1385] clone3() with flags says: -22 expected -22
ok 6 exit_signal with highest 32 bits non-zero
# Running test 'negative 32-bit exit_signal'
# [1385] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1385] clone3() with flags says: -22 expected -22
ok 7 negative 32-bit exit_signal
# Running test 'exit_signal not fitting into CSIGNAL mask'
# [1385] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1385] clone3() with flags says: -22 expected -22
ok 8 exit_signal not fitting into CSIGNAL mask
# Running test 'NSIG < exit_signal < CSIG'
# [1385] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1385] clone3() with flags says: -22 expected -22
ok 9 NSIG < exit_signal < CSIG
# Running test 'Arguments sizeof(struct clone_args) + 8'
# [1385] Trying clone3() with flags 0 (size 96)
# I am the parent (1385). My child's pid is 1390
# I am the child, my PID is 1390
# [1385] clone3() with flags says: 0 expected 0
ok 10 Arguments sizeof(struct clone_args) + 8
# Running test 'Arguments sizeof(struct clone_args) + 16'
# [1385] Trying clone3() with flags 0 (size 104)
# Argument list too long - Failed to create new process
# [1385] clone3() with flags says: -7 expected -7
ok 11 Arguments sizeof(struct clone_args) + 16
# Running test 'Arguments sizeof(struct clone_arg) * 2'
# [1385] Trying clone3() with flags 0 (size 104)
# Argument list too long - Failed to create new process
# [1385] clone3() with flags says: -7 expected -7
ok 12 Arguments sizeof(struct clone_arg) * 2
# Running test 'Arguments > page size'
# [1385] Trying clone3() with flags 0 (size 8200)
# Argument list too long - Failed to create new process
# [1385] clone3() with flags says: -7 expected -7
ok 13 Arguments > page size
# Running test 'CLONE_ARGS_SIZE_VER0 in a new PID NS'
# [1385] Trying clone3() with flags 0x20000000 (size 64)
# I am the parent (1385). My child's pid is 1391
# I am the child, my PID is 1
# [1385] clone3() with flags says: 0 expected 0
ok 14 CLONE_ARGS_SIZE_VER0 in a new PID NS
# Running test 'CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS'
# [1385] Trying clone3() with flags 0x20000000 (size 56)
# Invalid argument - Failed to create new process
# [1385] clone3() with flags says: -22 expected -22
ok 15 CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS
# Running test 'sizeof(struct clone_args) + 8 in a new PID NS'
# [1385] Trying clone3() with flags 0x20000000 (size 96)
# I am the parent (1385). My child's pid is 1392
# I am the child, my PID is 1
# [1385] clone3() with flags says: 0 expected 0
ok 16 sizeof(struct clone_args) + 8 in a new PID NS
# Running test 'Arguments > page size in a new PID NS'
# [1385] Trying clone3() with flags 0x20000000 (size 8200)
# Argument list too long - Failed to create new process
# [1385] clone3() with flags says: -7 expected -7
ok 17 Arguments > page size in a new PID NS
# Time namespaces are not supported
ok 18 # SKIP New time NS
# Running test 'exit signal (SIGCHLD) in flags'
# [1385] Trying clone3() with flags 0x14 (size 0)
# Invalid argument - Failed to create new process
# [1385] clone3() with flags says: -22 expected -22
ok 19 exit signal (SIGCHLD) in flags
# 1 skipped test(s) detected. Consider enabling relevant config options to improve coverage.
# Totals: pass:18 fail:0 xfail:0 xpass:0 skip:1 error:0
root@raverin:/usr/src/linux/tools/testing/selftests/clone3#

Tested-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>

Thanks,
Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913
Re: [PATCH 2/3] sparc: Add architecture support for clone3
Posted by Jessica Clarke 3 weeks, 3 days ago
On Fri, Jan 16, 2026 at 04:30:50PM +0100, 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 call conventions for clone on SPARC are kept for clone3:
>   Parent -->  %o0 == child's  pid, %o1 == 0
>   Child  -->  %o0 == parent's pid, %o1 == 1

One of the benefits of having a new clone3 is that the interface can be
made the same across all architectures*, unlike clone, which both passes
the arguments in different orders for different architectures and, in
the case of SPARC, has this weird return convention inherited from the
SunOS syscall interface. Is there a good reason to deviate for clone3
too and keep this annoying oddity going, that requires special-casing
SPARC when other architectures can just syscall(__NR_clone3, ...)?

Jessica

* Even Itanium's clone2 could have been subsumed by it, as clone3 passes
  the stack base and size rather than the desired stack pointer
Re: [PATCH 2/3] sparc: Add architecture support for clone3
Posted by Ludwig Rydberg 3 weeks ago
Hi Jessica,

> One of the benefits of having a new clone3 is that the interface can be
> made the same across all architectures*, unlike clone, which both passes
> the arguments in different orders for different architectures and, in
> the case of SPARC, has this weird return convention inherited from the
> SunOS syscall interface. Is there a good reason to deviate for clone3
> too and keep this annoying oddity going, that requires special-casing
> SPARC when other architectures can just syscall(__NR_clone3, ...)?
> 

Thanks for commenting on this. No, you're right (there is no good reason).
The original implementation just followed how things were but I have a v2
coming up which will follow the regular kernel return value conventions.
This will ensure that syscall(__NR_clone3, ...) works on SPARC exactly
as it does on other architectures (i.e I'll drop the third patch in the
series).

Best regards,
// Ludwig
Re: [PATCH 2/3] sparc: Add architecture support for clone3
Posted by John Paul Adrian Glaubitz 3 weeks, 2 days ago
Hi,

On Fri, 2026-01-16 at 16:47 +0000, Jessica Clarke wrote:
> On Fri, Jan 16, 2026 at 04:30:50PM +0100, 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 call conventions for clone on SPARC are kept for clone3:
> >   Parent -->  %o0 == child's  pid, %o1 == 0
> >   Child  -->  %o0 == parent's pid, %o1 == 1
> 
> One of the benefits of having a new clone3 is that the interface can be
> made the same across all architectures*, unlike clone, which both passes
> the arguments in different orders for different architectures and, in
> the case of SPARC, has this weird return convention inherited from the
> SunOS syscall interface. Is there a good reason to deviate for clone3
> too and keep this annoying oddity going, that requires special-casing
> SPARC when other architectures can just syscall(__NR_clone3, ...)?

Very good point. Since clone3() is a new syscall, I think it would make more
sense to use the same interface as all the other architectures.

The weird syscall interface was already the reason why we had to write custom
code for systemd on SPARC in order to use the raw clone() syscall.

I think the proposed implementation of clone3() for SPARC would actually break
the libcamera build which calls clone3() using the syscall handler [1]:

FAILED: [code=1] src/libcamera/libcamera.so.0.6.0.p/process.cpp.o 
c++ -Isrc/libcamera/libcamera.so.0.6.0.p -Isrc/libcamera -I../src/libcamera -Iinclude -I../include -Iinclude/libcamera -Iinclude/libcamera/ipa -Iinclude/libcamera/internal -Isrc/libcamera/proxy -
I/usr/include/p11-kit-1 -I/usr/include/sparc64-linux-gnu -fdiagnostics-color=always -D_GLIBCXX_ASSERTIONS=1 -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wextra -Werror -std=c++17 -Wnon-virtual-dtor -
Wno-redundant-move -Wmissing-declarations -Wshadow -include /build/reproducible-path/libcamera-0.6.0/obj-sparc64-linux-gnu/config.h -g -O2 -ffile-prefix-map=/build/reproducible-path/libcamera-0.6.0=.
-fstack-protector-strong -Wformat -Werror=format-security -Wno-error -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -DLIBCAMERA_BASE_PRIVATE -MD -MQ src/libcamera/libcamera.so.0.6.0.p/process.cpp.o -MF
src/libcamera/libcamera.so.0.6.0.p/process.cpp.o.d -o src/libcamera/libcamera.so.0.6.0.p/process.cpp.o -c ../src/libcamera/process.cpp
../src/libcamera/process.cpp: In member function ‘int libcamera::Process::start(const std::string&, libcamera::Span<const std::__cxx11::basic_string<char> >, libcamera::Span<const int>)’:
../src/libcamera/process.cpp:160:33: error: ‘SYS_clone3’ was not declared in this scope; did you mean ‘SYS_clone’?
  160 |         long childPid = syscall(SYS_clone3, &cargs, sizeof(cargs));
      |                                 ^~~~~~~~~~
      |                                 SYS_clone

I'll verify that and report back.

Adrian

> [1] https://buildd.debian.org/status/fetch.php?pkg=libcamera&arch=sparc64&ver=0.6.0-2&stamp=1766489678&raw=0

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913
Re: [PATCH 2/3] sparc: Add architecture support for clone3
Posted by John Paul Adrian Glaubitz 3 weeks, 1 day ago
Hi,

On Sat, 2026-01-17 at 22:23 +0100, John Paul Adrian Glaubitz wrote:
> Hi,
> 
> On Fri, 2026-01-16 at 16:47 +0000, Jessica Clarke wrote:
> > On Fri, Jan 16, 2026 at 04:30:50PM +0100, 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 call conventions for clone on SPARC are kept for clone3:
> > >   Parent -->  %o0 == child's  pid, %o1 == 0
> > >   Child  -->  %o0 == parent's pid, %o1 == 1
> > 
> > One of the benefits of having a new clone3 is that the interface can be
> > made the same across all architectures*, unlike clone, which both passes
> > the arguments in different orders for different architectures and, in
> > the case of SPARC, has this weird return convention inherited from the
> > SunOS syscall interface. Is there a good reason to deviate for clone3
> > too and keep this annoying oddity going, that requires special-casing
> > SPARC when other architectures can just syscall(__NR_clone3, ...)?
> 
> Very good point. Since clone3() is a new syscall, I think it would make more
> sense to use the same interface as all the other architectures.
> 
> The weird syscall interface was already the reason why we had to write custom
> code for systemd on SPARC in order to use the raw clone() syscall.
> 
> I think the proposed implementation of clone3() for SPARC would actually break
> the libcamera build which calls clone3() using the syscall handler [1]:
> 
> FAILED: [code=1] src/libcamera/libcamera.so.0.6.0.p/process.cpp.o 
> c++ -Isrc/libcamera/libcamera.so.0.6.0.p -Isrc/libcamera -I../src/libcamera -Iinclude -I../include -Iinclude/libcamera -Iinclude/libcamera/ipa -Iinclude/libcamera/internal -Isrc/libcamera/proxy -
> I/usr/include/p11-kit-1 -I/usr/include/sparc64-linux-gnu -fdiagnostics-color=always -D_GLIBCXX_ASSERTIONS=1 -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wextra -Werror -std=c++17 -Wnon-virtual-dtor -
> Wno-redundant-move -Wmissing-declarations -Wshadow -include /build/reproducible-path/libcamera-0.6.0/obj-sparc64-linux-gnu/config.h -g -O2 -ffile-prefix-map=/build/reproducible-path/libcamera-0.6.0=.
> -fstack-protector-strong -Wformat -Werror=format-security -Wno-error -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -DLIBCAMERA_BASE_PRIVATE -MD -MQ src/libcamera/libcamera.so.0.6.0.p/process.cpp.o -MF
> src/libcamera/libcamera.so.0.6.0.p/process.cpp.o.d -o src/libcamera/libcamera.so.0.6.0.p/process.cpp.o -c ../src/libcamera/process.cpp
> ../src/libcamera/process.cpp: In member function ‘int libcamera::Process::start(const std::string&, libcamera::Span<const std::__cxx11::basic_string<char> >, libcamera::Span<const int>)’:
> ../src/libcamera/process.cpp:160:33: error: ‘SYS_clone3’ was not declared in this scope; did you mean ‘SYS_clone’?
>   160 |         long childPid = syscall(SYS_clone3, &cargs, sizeof(cargs));
>       |                                 ^~~~~~~~~~
>       |                                 SYS_clone
> 
> I'll verify that and report back.

I can confirm that libcamera builds fine and passes its testsuite with a patched kernel:

Ok:                38
Expected Fail:     1
Fail:              0
Skipped:           31

Full log written to /home/glaubitz/libcamera/libcamera-0.6.0/obj-sparc64-linux-gnu/meson-logs/testlog.txt
make[1]: Leaving directory '/home/glaubitz/libcamera/libcamera-0.6.0'

Whether the currently chosen interface is the right one, is another question though.

Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913