Subject: [RFC] forkpid() syscall From: Cedric Le Goater let's the user specify a pid to fork and return EBUSY if the pid is not available. this patch includes a alloc_pid*() cleanup on the way errors are returned that could be pushed to mainline independently. usage : #include #define __NR_forkpid 324 static inline int forkpid(int pid) { return syscall(__NR_forkpid, pid); } caveats : fork oriented, should also cover clone i386 only does not cover 64 bits clone flags Signed-off-by: Cedric Le Goater --- arch/i386/kernel/process.c | 15 +++++++++++---- arch/i386/kernel/syscall_table.S | 1 + include/asm-i386/unistd.h | 3 ++- include/linux/pid.h | 2 +- include/linux/sched.h | 2 +- kernel/fork.c | 9 +++++---- kernel/pid.c | 28 +++++++++++++++------------- 7 files changed, 36 insertions(+), 24 deletions(-) Index: 2.6.22/kernel/pid.c =================================================================== --- 2.6.22.orig/kernel/pid.c +++ 2.6.22/kernel/pid.c @@ -96,12 +96,12 @@ static fastcall void free_pidmap(struct atomic_inc(&map->nr_free); } -static int alloc_pidmap(struct pid_namespace *pid_ns) +static int alloc_pidmap(struct pid_namespace *pid_ns, pid_t upid) { int i, offset, max_scan, pid, last = pid_ns->last_pid; struct pidmap *map; - pid = last + 1; + pid = upid ? upid : last + 1; if (pid >= pid_max) pid = RESERVED_PIDS; offset = pid & BITS_PER_PAGE_MASK; @@ -130,6 +130,8 @@ static int alloc_pidmap(struct pid_names pid_ns->last_pid = pid; return pid; } + if (upid) + return -EBUSY; offset = find_next_offset(map, offset); pid = mk_pid(pid_ns, map, offset); /* @@ -153,7 +155,7 @@ static int alloc_pidmap(struct pid_names } pid = mk_pid(pid_ns, map, offset); } - return -1; + return -EAGAIN; } static int next_pidmap(struct pid_namespace *pid_ns, int last) @@ -203,19 +205,24 @@ fastcall void free_pid(struct pid *pid) call_rcu(&pid->rcu, delayed_put_pid); } -struct pid *alloc_pid(void) +struct pid *alloc_pid(pid_t upid) { struct pid *pid; enum pid_type type; int nr = -1; pid = kmem_cache_alloc(pid_cachep, GFP_KERNEL); - if (!pid) + if (!pid) { + pid = ERR_PTR(-ENOMEM); goto out; + } - nr = alloc_pidmap(current->nsproxy->pid_ns); - if (nr < 0) - goto out_free; + nr = alloc_pidmap(current->nsproxy->pid_ns, upid); + if (nr < 0) { + kmem_cache_free(pid_cachep, pid); + pid = ERR_PTR(nr); + goto out; + } atomic_set(&pid->count, 1); pid->nr = nr; @@ -228,11 +235,6 @@ struct pid *alloc_pid(void) out: return pid; - -out_free: - kmem_cache_free(pid_cachep, pid); - pid = NULL; - goto out; } struct pid * fastcall find_pid(int nr) Index: 2.6.22/arch/i386/kernel/process.c =================================================================== --- 2.6.22.orig/arch/i386/kernel/process.c +++ 2.6.22/arch/i386/kernel/process.c @@ -355,7 +355,7 @@ int kernel_thread(int (*fn)(void *), voi regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2; /* Ok, create the new process.. */ - return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL, 0); } EXPORT_SYMBOL(kernel_thread); @@ -722,9 +722,16 @@ struct task_struct fastcall * __switch_t return prev_p; } +asmlinkage int sys_forkpid(struct pt_regs regs) +{ + pid_t pid = regs.ebx; + + return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL, pid); +} + asmlinkage int sys_fork(struct pt_regs regs) { - return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL); + return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL, 0); } asmlinkage int sys_clone(struct pt_regs regs) @@ -739,7 +746,7 @@ asmlinkage int sys_clone(struct pt_regs child_tidptr = (int __user *)regs.edi; if (!newsp) newsp = regs.esp; - return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr); + return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr, 0); } /* @@ -754,7 +761,7 @@ asmlinkage int sys_clone(struct pt_regs */ asmlinkage int sys_vfork(struct pt_regs regs) { - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL); + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL, 0); } /* Index: 2.6.22/arch/i386/kernel/syscall_table.S =================================================================== --- 2.6.22.orig/arch/i386/kernel/syscall_table.S +++ 2.6.22/arch/i386/kernel/syscall_table.S @@ -323,3 +323,4 @@ ENTRY(sys_call_table) .long sys_signalfd .long sys_timerfd .long sys_eventfd + .long sys_forkpid Index: 2.6.22/include/asm-i386/unistd.h =================================================================== --- 2.6.22.orig/include/asm-i386/unistd.h +++ 2.6.22/include/asm-i386/unistd.h @@ -329,10 +329,11 @@ #define __NR_signalfd 321 #define __NR_timerfd 322 #define __NR_eventfd 323 +#define __NR_forkpid 324 #ifdef __KERNEL__ -#define NR_syscalls 324 +#define NR_syscalls 325 #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR Index: 2.6.22/kernel/fork.c =================================================================== --- 2.6.22.orig/kernel/fork.c +++ 2.6.22/kernel/fork.c @@ -1358,15 +1358,16 @@ long do_fork(unsigned long clone_flags, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, - int __user *child_tidptr) + int __user *child_tidptr, + pid_t upid) { struct task_struct *p; int trace = 0; - struct pid *pid = alloc_pid(); + struct pid *pid = alloc_pid(upid); long nr; - if (!pid) - return -EAGAIN; + if (IS_ERR(pid)) + return PTR_ERR(pid); nr = pid->nr; if (unlikely(current->ptrace)) { trace = fork_traceflag (clone_flags); Index: 2.6.22/include/linux/sched.h =================================================================== --- 2.6.22.orig/include/linux/sched.h +++ 2.6.22/include/linux/sched.h @@ -1433,7 +1433,7 @@ extern int allow_signal(int); extern int disallow_signal(int); extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *); -extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); +extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *, pid_t); struct task_struct *fork_idle(int); extern void set_task_comm(struct task_struct *tsk, char *from); Index: 2.6.22/include/linux/pid.h =================================================================== --- 2.6.22.orig/include/linux/pid.h +++ 2.6.22/include/linux/pid.h @@ -95,7 +95,7 @@ extern struct pid *FASTCALL(find_pid(int extern struct pid *find_get_pid(int nr); extern struct pid *find_ge_pid(int nr); -extern struct pid *alloc_pid(void); +extern struct pid *alloc_pid(pid_t upid); extern void FASTCALL(free_pid(struct pid *pid)); static inline pid_t pid_nr(struct pid *pid)