* [RFC][v4][PATCH 0/7] clone_extended() syscall
@ 2009-08-06 6:10 Sukadev Bhattiprolu
[not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Sukadev Bhattiprolu @ 2009-08-06 6:10 UTC (permalink / raw)
To: Oren Laadan; +Cc: Containers, Alexey Dobriyan
Define clone_extended() system call (previously known as clone_with_pids()).
See patch 7/7 for details. (Other patches are same as before).
Changelog [v4]:
- Rename system call to clone_extended() and support 64bit clone-flags
- Rename 'struct target_pid_set' to 'struct pid_set'
^ permalink raw reply [flat|nested] 18+ messages in thread[parent not found: <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>]
* [RFC][v4][PATCH 1/7] Factor out code to allocate pidmap page [not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-06 6:22 ` Sukadev Bhattiprolu 2009-08-06 6:23 ` [RFC][v4][PATCH 2/7]: Have alloc_pidmap() return actual error code Sukadev Bhattiprolu ` (5 subsequent siblings) 6 siblings, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 6:22 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Alexey Dobriyan Subject: [RFC][v4][PATCH 1/7] Factor out code to allocate pidmap page To implement support for clone_with_pids() system call we would need to allocate pidmap page in more than one place. Move this code to a new function alloc_pidmap_page(). Changelog[v2]: - (Matt Helsley, Dave Hansen) Have alloc_pidmap_page() return -ENOMEM on error instead of -1. Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> Reviewed-by: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org> --- kernel/pid.c | 46 ++++++++++++++++++++++++++++++---------------- 1 files changed, 30 insertions(+), 16 deletions(-) Index: linux-2.6/kernel/pid.c =================================================================== --- linux-2.6.orig/kernel/pid.c 2009-08-05 17:00:22.000000000 -0700 +++ linux-2.6/kernel/pid.c 2009-08-05 17:02:40.000000000 -0700 @@ -122,9 +122,34 @@ atomic_inc(&map->nr_free); } +static int alloc_pidmap_page(struct pidmap *map) +{ + void *page; + + if (likely(map->page)) + return 0; + + page = kzalloc(PAGE_SIZE, GFP_KERNEL); + + /* + * Free the page if someone raced with us installing it: + */ + spin_lock_irq(&pidmap_lock); + if (map->page) + kfree(page); + else + map->page = page; + spin_unlock_irq(&pidmap_lock); + + if (unlikely(!map->page)) + return -ENOMEM; + + return 0; +} + static int alloc_pidmap(struct pid_namespace *pid_ns) { - int i, offset, max_scan, pid, last = pid_ns->last_pid; + int i, rc, offset, max_scan, pid, last = pid_ns->last_pid; struct pidmap *map; pid = last + 1; @@ -134,21 +159,10 @@ map = &pid_ns->pidmap[pid/BITS_PER_PAGE]; max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset; for (i = 0; i <= max_scan; ++i) { - if (unlikely(!map->page)) { - void *page = kzalloc(PAGE_SIZE, GFP_KERNEL); - /* - * Free the page if someone raced with us - * installing it: - */ - spin_lock_irq(&pidmap_lock); - if (map->page) - kfree(page); - else - map->page = page; - spin_unlock_irq(&pidmap_lock); - if (unlikely(!map->page)) - break; - } + rc = alloc_pidmap_page(map); + if (rc) + break; + if (likely(atomic_read(&map->nr_free))) { do { if (!test_and_set_bit(offset, map->page)) { ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 2/7]: Have alloc_pidmap() return actual error code [not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 2009-08-06 6:22 ` [RFC][v4][PATCH 1/7] Factor out code to allocate pidmap page Sukadev Bhattiprolu @ 2009-08-06 6:23 ` Sukadev Bhattiprolu 2009-08-06 6:23 ` [RFC][v4][PATCH 3/7]: Add target_pid parameter to alloc_pidmap() Sukadev Bhattiprolu ` (4 subsequent siblings) 6 siblings, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 6:23 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Alexey Dobriyan Subject: [RFC][v4][PATCH 2/7]: Have alloc_pidmap() return actual error code alloc_pidmap() can fail either because all pid numbers are in use or because memory allocation failed. With support for setting a specific pid number, alloc_pidmap() would also fail if either the given pid number is invalid or in use. Rather than have callers assume -ENOMEM, have alloc_pidmap() return the actual error. Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> Reviewed-by: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org> --- kernel/fork.c | 5 +++-- kernel/pid.c | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) Index: linux-2.6/kernel/fork.c =================================================================== --- linux-2.6.orig/kernel/fork.c 2009-08-05 17:00:22.000000000 -0700 +++ linux-2.6/kernel/fork.c 2009-08-05 17:02:45.000000000 -0700 @@ -1124,10 +1124,11 @@ goto bad_fork_cleanup_io; if (pid != &init_struct_pid) { - retval = -ENOMEM; pid = alloc_pid(p->nsproxy->pid_ns); - if (!pid) + if (IS_ERR(pid)) { + retval = PTR_ERR(pid); goto bad_fork_cleanup_io; + } if (clone_flags & CLONE_NEWPID) { retval = pid_ns_prepare_proc(p->nsproxy->pid_ns); Index: linux-2.6/kernel/pid.c =================================================================== --- linux-2.6.orig/kernel/pid.c 2009-08-05 17:02:40.000000000 -0700 +++ linux-2.6/kernel/pid.c 2009-08-05 17:02:45.000000000 -0700 @@ -158,6 +158,7 @@ offset = pid & BITS_PER_PAGE_MASK; map = &pid_ns->pidmap[pid/BITS_PER_PAGE]; max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset; + rc = -EAGAIN; for (i = 0; i <= max_scan; ++i) { rc = alloc_pidmap_page(map); if (rc) @@ -188,12 +189,14 @@ } else { map = &pid_ns->pidmap[0]; offset = RESERVED_PIDS; - if (unlikely(last == offset)) + if (unlikely(last == offset)) { + rc = -EAGAIN; break; + } } pid = mk_pid(pid_ns, map, offset); } - return -1; + return rc; } int next_pidmap(struct pid_namespace *pid_ns, int last) @@ -298,7 +301,7 @@ free_pidmap(pid->numbers + i); kmem_cache_free(ns->pid_cachep, pid); - pid = NULL; + pid = ERR_PTR(nr); goto out; } ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 3/7]: Add target_pid parameter to alloc_pidmap() [not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 2009-08-06 6:22 ` [RFC][v4][PATCH 1/7] Factor out code to allocate pidmap page Sukadev Bhattiprolu 2009-08-06 6:23 ` [RFC][v4][PATCH 2/7]: Have alloc_pidmap() return actual error code Sukadev Bhattiprolu @ 2009-08-06 6:23 ` Sukadev Bhattiprolu 2009-08-06 6:24 ` [RFC][v4][PATCH 4/7]: Add target_pids parameter to alloc_pid() Sukadev Bhattiprolu ` (3 subsequent siblings) 6 siblings, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 6:23 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Alexey Dobriyan Subject: [RFC][v4][PATCH 3/7]: Add target_pid parameter to alloc_pidmap() With support for setting a specific pid number for a process, alloc_pidmap() will need a paramter a 'target_pid' parameter. Changelog[v2]: - (Serge Hallyn) Check for 'pid < 0' in set_pidmap().(Code actually checks for 'pid <= 0' for completeness). Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> Reviewed-by: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org> --- kernel/pid.c | 28 ++++++++++++++++++++++++++-- 1 files changed, 26 insertions(+), 2 deletions(-) Index: linux-2.6/kernel/pid.c =================================================================== --- linux-2.6.orig/kernel/pid.c 2009-08-05 17:02:45.000000000 -0700 +++ linux-2.6/kernel/pid.c 2009-08-05 19:34:37.000000000 -0700 @@ -147,11 +147,35 @@ return 0; } -static int alloc_pidmap(struct pid_namespace *pid_ns) +static int set_pidmap(struct pid_namespace *pid_ns, int pid) +{ + int offset; + struct pidmap *map; + + if (pid <= 0 || pid >= pid_max) + return -EINVAL; + + offset = pid & BITS_PER_PAGE_MASK; + map = &pid_ns->pidmap[pid/BITS_PER_PAGE]; + + if (alloc_pidmap_page(map)) + return -ENOMEM; + + if (test_and_set_bit(offset, map->page)) + return -EBUSY; + + atomic_dec(&map->nr_free); + return pid; +} + +static int alloc_pidmap(struct pid_namespace *pid_ns, int target_pid) { int i, rc, offset, max_scan, pid, last = pid_ns->last_pid; struct pidmap *map; + if (target_pid) + return set_pidmap(pid_ns, target_pid); + pid = last + 1; if (pid >= pid_max) pid = RESERVED_PIDS; @@ -270,7 +294,7 @@ tmp = ns; for (i = ns->level; i >= 0; i--) { - nr = alloc_pidmap(tmp); + nr = alloc_pidmap(tmp, 0); if (nr < 0) goto out_free; ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 4/7]: Add target_pids parameter to alloc_pid() [not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> ` (2 preceding siblings ...) 2009-08-06 6:23 ` [RFC][v4][PATCH 3/7]: Add target_pid parameter to alloc_pidmap() Sukadev Bhattiprolu @ 2009-08-06 6:24 ` Sukadev Bhattiprolu 2009-08-06 6:24 ` [RFC][v4][PATCH 5/7]: Add target_pids parameter to copy_process() Sukadev Bhattiprolu ` (2 subsequent siblings) 6 siblings, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 6:24 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Alexey Dobriyan Subject: [RFC][v4][PATCH 4/7]: Add target_pids parameter to alloc_pid() This parameter is currently NULL, but will be used in a follow-on patch. Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> Reviewed-by: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org> --- include/linux/pid.h | 2 +- kernel/fork.c | 3 ++- kernel/pid.c | 9 +++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) Index: linux-2.6/include/linux/pid.h =================================================================== --- linux-2.6.orig/include/linux/pid.h 2009-08-05 19:34:37.000000000 -0700 +++ linux-2.6/include/linux/pid.h 2009-08-05 19:35:08.000000000 -0700 @@ -119,7 +119,7 @@ extern struct pid *find_ge_pid(int nr, struct pid_namespace *); int next_pidmap(struct pid_namespace *pid_ns, int last); -extern struct pid *alloc_pid(struct pid_namespace *ns); +extern struct pid *alloc_pid(struct pid_namespace *ns, pid_t *target_pids); extern void free_pid(struct pid *pid); /* Index: linux-2.6/kernel/fork.c =================================================================== --- linux-2.6.orig/kernel/fork.c 2009-08-05 19:34:37.000000000 -0700 +++ linux-2.6/kernel/fork.c 2009-08-05 19:35:08.000000000 -0700 @@ -954,6 +954,7 @@ int retval; struct task_struct *p; int cgroup_callbacks_done = 0; + pid_t *target_pids = NULL; if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) return ERR_PTR(-EINVAL); @@ -1124,7 +1125,7 @@ goto bad_fork_cleanup_io; if (pid != &init_struct_pid) { - pid = alloc_pid(p->nsproxy->pid_ns); + pid = alloc_pid(p->nsproxy->pid_ns, target_pids); if (IS_ERR(pid)) { retval = PTR_ERR(pid); goto bad_fork_cleanup_io; Index: linux-2.6/kernel/pid.c =================================================================== --- linux-2.6.orig/kernel/pid.c 2009-08-05 19:34:37.000000000 -0700 +++ linux-2.6/kernel/pid.c 2009-08-05 19:35:08.000000000 -0700 @@ -280,13 +280,14 @@ call_rcu(&pid->rcu, delayed_put_pid); } -struct pid *alloc_pid(struct pid_namespace *ns) +struct pid *alloc_pid(struct pid_namespace *ns, pid_t *target_pids) { struct pid *pid; enum pid_type type; int i, nr; struct pid_namespace *tmp; struct upid *upid; + int tpid; pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL); if (!pid) @@ -294,7 +295,11 @@ tmp = ns; for (i = ns->level; i >= 0; i--) { - nr = alloc_pidmap(tmp, 0); + tpid = 0; + if (target_pids) + tpid = target_pids[i]; + + nr = alloc_pidmap(tmp, tpid); if (nr < 0) goto out_free; ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 5/7]: Add target_pids parameter to copy_process() [not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> ` (3 preceding siblings ...) 2009-08-06 6:24 ` [RFC][v4][PATCH 4/7]: Add target_pids parameter to alloc_pid() Sukadev Bhattiprolu @ 2009-08-06 6:24 ` Sukadev Bhattiprolu 2009-08-06 6:24 ` [RFC][v4][PATCH 6/7]: Define do_fork_with_pids() Sukadev Bhattiprolu 2009-08-06 6:25 ` [RFC][v4][PATCH 7/7]: Define clone_extended() syscall Sukadev Bhattiprolu 6 siblings, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 6:24 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Alexey Dobriyan Subject: [RFC][v4][PATCH 5/7]: Add target_pids parameter to copy_process() The new parameter will be used in a follow-on patch when clone_with_pids() is implemented. Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> Reviewed-by: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org> --- kernel/fork.c | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) Index: linux-2.6/kernel/fork.c =================================================================== --- linux-2.6.orig/kernel/fork.c 2009-08-05 19:35:08.000000000 -0700 +++ linux-2.6/kernel/fork.c 2009-08-05 19:35:34.000000000 -0700 @@ -949,12 +949,12 @@ unsigned long stack_size, int __user *child_tidptr, struct pid *pid, + pid_t *target_pids, int trace) { int retval; struct task_struct *p; int cgroup_callbacks_done = 0; - pid_t *target_pids = NULL; if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) return ERR_PTR(-EINVAL); @@ -1330,7 +1330,7 @@ struct pt_regs regs; task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, - &init_struct_pid, 0); + &init_struct_pid, NULL, 0); if (!IS_ERR(task)) init_idle(task, cpu); @@ -1353,6 +1353,7 @@ struct task_struct *p; int trace = 0; long nr; + pid_t *target_pids = NULL; /* * Do some preliminary argument and permissions checking before we @@ -1393,7 +1394,7 @@ trace = tracehook_prepare_clone(clone_flags); p = copy_process(clone_flags, stack_start, regs, stack_size, - child_tidptr, NULL, trace); + child_tidptr, NULL, target_pids, trace); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 6/7]: Define do_fork_with_pids() [not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> ` (4 preceding siblings ...) 2009-08-06 6:24 ` [RFC][v4][PATCH 5/7]: Add target_pids parameter to copy_process() Sukadev Bhattiprolu @ 2009-08-06 6:24 ` Sukadev Bhattiprolu 2009-08-06 6:25 ` [RFC][v4][PATCH 7/7]: Define clone_extended() syscall Sukadev Bhattiprolu 6 siblings, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 6:24 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Alexey Dobriyan Subject: [RFC][v4][PATCH 6/7]: Define do_fork_with_pids() do_fork_with_pids() is same as do_fork(), except that it takes an additional, 'pid_set', parameter. This parameter, currently unused, specifies the set of target pids of the process in each of its pid namespaces. Changelog[v3]: - Fix "long-line" warning from checkpatch.pl Changelog[v2]: - To facilitate moving architecture-inpdendent code to kernel/fork.c pass in 'struct target_pid_set __user *' to do_fork_with_pids() rather than 'pid_t *' (next patch moves the arch-independent code to kernel/fork.c) Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> Reviewed-by: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org> --- include/linux/sched.h | 3 +++ include/linux/types.h | 5 +++++ kernel/fork.c | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) Index: linux-2.6/include/linux/sched.h =================================================================== --- linux-2.6.orig/include/linux/sched.h 2009-08-05 19:34:36.000000000 -0700 +++ linux-2.6/include/linux/sched.h 2009-08-05 19:36:59.000000000 -0700 @@ -2053,6 +2053,9 @@ 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_with_pids(unsigned long, unsigned long, struct pt_regs *, + unsigned long, int __user *, int __user *, + struct pid_set __user *pid_set); struct task_struct *fork_idle(int); extern void set_task_comm(struct task_struct *tsk, char *from); Index: linux-2.6/include/linux/types.h =================================================================== --- linux-2.6.orig/include/linux/types.h 2009-08-05 19:34:36.000000000 -0700 +++ linux-2.6/include/linux/types.h 2009-08-05 19:55:42.000000000 -0700 @@ -204,6 +204,11 @@ char f_fpack[6]; }; +struct pid_set { + int num_pids; + pid_t *pids; +}; + #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ #endif /* _LINUX_TYPES_H */ Index: linux-2.6/kernel/fork.c =================================================================== --- linux-2.6.orig/kernel/fork.c 2009-08-05 19:35:34.000000000 -0700 +++ linux-2.6/kernel/fork.c 2009-08-05 19:55:42.000000000 -0700 @@ -1343,12 +1343,13 @@ * It copies the process, and if successful kick-starts * it and waits for it to finish using the VM if required. */ -long do_fork(unsigned long clone_flags, +long do_fork_with_pids(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, - int __user *child_tidptr) + int __user *child_tidptr, + struct pid_set __user *pid_setp) { struct task_struct *p; int trace = 0; @@ -1451,6 +1452,17 @@ return nr; } +long do_fork(unsigned long clone_flags, + unsigned long stack_start, + struct pt_regs *regs, + unsigned long stack_size, + int __user *parent_tidptr, + int __user *child_tidptr) +{ + return do_fork_with_pids(clone_flags, stack_start, regs, stack_size, + parent_tidptr, child_tidptr, NULL); +} + #ifndef ARCH_MIN_MMSTRUCT_ALIGN #define ARCH_MIN_MMSTRUCT_ALIGN 0 #endif ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> ` (5 preceding siblings ...) 2009-08-06 6:24 ` [RFC][v4][PATCH 6/7]: Define do_fork_with_pids() Sukadev Bhattiprolu @ 2009-08-06 6:25 ` Sukadev Bhattiprolu [not found] ` <20090806062505.GG5619-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 6 siblings, 1 reply; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 6:25 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Alexey Dobriyan Subject: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall Container restart requires that a task have the same pid it had when it was checkpointed. When containers are nested the tasks within the containers exist in multiple pid namespaces and hence have multiple pids to specify during restart. This patch defines, a new system call, clone_extended() which is like clone(), but takes a new 'pid_set' parameter. This parameter lets caller choose specific pid numbers for the child process, in the process's active and ancestor pid namespaces. (Descendant pid namespaces in general don't matter since processes don't have pids in them anyway, but see comments in copy_target_pids() regarding CLONE_NEWPID). Unlike clone(), however, clone_extended() needs CAP_SYS_ADMIN, at least for now, to prevent unprivileged processes from misusing this interface. While the main motivation for this interface is the need to let a process choose its 'pid numbers', the clone_extended() interface uses 64-bit clone flags. The 'higher' portion of the clone flags are unused and are only included to preclude yet another version of clone when a new clone flag is needed. ===== Interface: Compared to clone(), clone_extended() needs to pass in three more pieces of information: - additional 32-bit of clone_flags - number of pids in the set - user buffer containing the list of pids. But since clone() already takes 5 parameters and some (all ?) architectures are restricted to 6 parameters to a system-call, additional data-structures (and copy_from_user()) are needed. The proposed interface for clone_extended() is: struct clone_tid_info { void *parent_tid; /* parent_tid_ptr parameter */ void *child_tid; /* child_tid_ptr parameter */ }; struct pid_set { int num_pids; pid_t *pids; }; int clone_extended(int flags_low, int flags_high, void *child_stack, void *unused, struct clone_tid_info *tid_ptrs, struct pid_set *pid_setp); where: - flags_low corresponds to the 'flags' parameter in normal clone(). - flags_high is currently unused and meant to be used if/when a new clone flag is needed. - 'tid_ptrs' is rather an aribtrary grouping of parameters to keep total parameter count to 6 - pid_setp specifies the pid-numbers for the process in the different pid namespaces (discussed further below). We expect 'struct pid_set' to be useful in other contexts. - if pid_setp is NULL and flags_high is 0, clone_extended() behaves like normal clone(), except for an extra copy_from_user() when 'tid_ptrs' is non-NULL. ===== Usage: int flags_low = (CLONE_FS|CLONE_VM|SIGCHLD); int flags_high = 0; // unused pid_t pids[] = { 0, 77, 99 }; struct pid_set pid_set; int parent_tid; int child_tid; struct clone_tid_info tid_ptrs = {&parent_tid, &child_tid}; pid_set.num_pids = sizeof(pids) / sizeof(int); pid_set.pids = &pids; syscall(__NR_clone_extended, flags_low, flags_high, stack, NULL, &tid_ptrs, &pid_set); ===== Notes on 'struct pid_set' parameter When pid_set is specified and a target-pid (i.e pid_set.pids[i]) is 0, the kernel continues to assign a pid for the process in that namespace. In the above example, pids[0] is 0, meaning the kernel will assign next available pid to the process in init_pid_ns. But kernel will assign pid 77 in the child pid namespace 1 and pid 99 in pid namespace 2. If either 77 or 99 are taken, the system call fails with -EBUSY. If 'pid_set.num_pids' exceeds the current nesting level of pid namespaces, the system call fails with -EINVAL. TODO: - Its mostly an exploratory patch seeking feedback on the interface. - Gently tested. - May need additional sanity checks in do_fork_with_pids(). Changelog[v4]: (address following comments from Serge Hallyn, Oren Laadan). - Rename interface from clone_with_pids() to clone_extended() - Add support for clone_flags (define/use 'struct clone_tid_info') - Rename 'target_pid_set' to simply 'pid_set' since pid_set may be useful in other contexts. Changelog[v3]: - (Oren Laadan) Allow CLONE_NEWPID flag (by allocating an extra pid in the target_pids[] list and setting it 0. See copy_target_pids()). - (Oren Laadan) Specified target pids should apply only to youngest pid-namespaces (see copy_target_pids()) - (Matt Helsley) Update patch description. Changelog[v2]: - Remove unnecessary printk and add a note to callers of copy_target_pids() to free target_pids. - (Serge Hallyn) Mention CAP_SYS_ADMIN restriction in patch description. - (Oren Laadan) Add checks for 'num_pids < 0' (return -EINVAL) and 'num_pids == 0' (fall back to normal clone()). - Move arch-independent code (sanity checks and copy-in of target-pids) into kernel/fork.c and simplify sys_clone_with_pids() Changelog[v1]: - Fixed some compile errors (had fixed these errors earlier in my git tree but had not refreshed patches before emailing them) Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> --- arch/x86/include/asm/syscalls.h | 2 + arch/x86/include/asm/unistd_32.h | 1 + arch/x86/kernel/entry_32.S | 1 + arch/x86/kernel/process_32.c | 21 +++++++ arch/x86/kernel/syscall_table_32.S | 1 + kernel/fork.c | 108 +++++++++++++++++++++++++++++++++++- 6 files changed, 133 insertions(+), 1 deletions(-) Index: linux-2.6/arch/x86/include/asm/syscalls.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/syscalls.h 2009-08-05 19:55:41.000000000 -0700 +++ linux-2.6/arch/x86/include/asm/syscalls.h 2009-08-05 19:56:15.000000000 -0700 @@ -40,6 +40,8 @@ /* kernel/process_32.c */ int sys_clone(struct pt_regs *); +int sys_clone_extended(struct pt_regs *); +int sys_vfork(struct pt_regs *); int sys_execve(struct pt_regs *); /* kernel/signal.c */ Index: linux-2.6/arch/x86/include/asm/unistd_32.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/unistd_32.h 2009-08-05 19:55:42.000000000 -0700 +++ linux-2.6/arch/x86/include/asm/unistd_32.h 2009-08-05 19:56:15.000000000 -0700 @@ -342,6 +342,7 @@ #define __NR_pwritev 334 #define __NR_rt_tgsigqueueinfo 335 #define __NR_perf_counter_open 336 +#define __NR_clone_extended 337 #ifdef __KERNEL__ Index: linux-2.6/arch/x86/kernel/entry_32.S =================================================================== --- linux-2.6.orig/arch/x86/kernel/entry_32.S 2009-08-05 19:55:42.000000000 -0700 +++ linux-2.6/arch/x86/kernel/entry_32.S 2009-08-05 19:56:15.000000000 -0700 @@ -718,6 +718,7 @@ PTREGSCALL(iopl) PTREGSCALL(fork) PTREGSCALL(clone) +PTREGSCALL(clone_extended) PTREGSCALL(vfork) PTREGSCALL(execve) PTREGSCALL(sigaltstack) Index: linux-2.6/arch/x86/kernel/process_32.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/process_32.c 2009-08-05 19:55:42.000000000 -0700 +++ linux-2.6/arch/x86/kernel/process_32.c 2009-08-05 19:56:15.000000000 -0700 @@ -443,6 +443,44 @@ return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); } +int sys_clone_extended(struct pt_regs *regs) +{ + unsigned long clone_flags_low; + unsigned long clone_flags_high; + int __user *utids; + struct clone_tid_info ktids; + unsigned long newsp; + int __user *parent_tidptr; + int __user *child_tidptr; + void __user *upid_setp; + + clone_flags_low = regs->bx; + /* + * clone_flags_high unused for now. If additional clone flags + * are defined, this could be used + */ + clone_flags_high = regs->cx; + newsp = regs->dx; + utids = (int __user *)regs->di; + upid_setp = (void __user *)regs->bp; + + if (!newsp) + newsp = regs->sp; + + parent_tidptr = NULL; + child_tidptr = NULL; + if (utids) { + int n = sizeof(struct clone_tid_info); + if (copy_from_user(&ktids, utids, n)) + return -EFAULT; + parent_tidptr = ktids.parent_tid; + child_tidptr = ktids.child_tid; + } + + return do_fork_with_pids(clone_flags_low, newsp, regs, 0, parent_tidptr, + child_tidptr, upid_setp); +} + /* * sys_execve() executes a new program. */ Index: linux-2.6/arch/x86/kernel/syscall_table_32.S =================================================================== --- linux-2.6.orig/arch/x86/kernel/syscall_table_32.S 2009-08-05 19:55:42.000000000 -0700 +++ linux-2.6/arch/x86/kernel/syscall_table_32.S 2009-08-05 19:56:15.000000000 -0700 @@ -336,3 +336,4 @@ .long sys_pwritev .long sys_rt_tgsigqueueinfo /* 335 */ .long sys_perf_counter_open + .long ptregs_clone_extended Index: linux-2.6/kernel/fork.c =================================================================== --- linux-2.6.orig/kernel/fork.c 2009-08-05 19:55:42.000000000 -0700 +++ linux-2.6/kernel/fork.c 2009-08-05 19:57:10.000000000 -0700 @@ -1338,6 +1338,97 @@ } /* + * If user specified any 'target-pids' in @upid_setp, copy them from + * user and return a pointer to a local copy of the list of pids. The + * caller must free the list, when they are done using it. + * + * If user did not specify any target pids, return NULL (caller should + * treat this like normal clone). + * + * On any errors, return the error code + */ +static pid_t *copy_target_pids(void __user *upid_setp) +{ + int j; + int rc; + int size; + int unum_pids; /* # of pids specified by user */ + int knum_pids; /* # of pids needed in kernel */ + pid_t *target_pids; + struct pid_set pid_set; + + if (!upid_setp) + return NULL; + + rc = copy_from_user(&pid_set, upid_setp, sizeof(pid_set)); + if (rc) + return ERR_PTR(-EFAULT); + + unum_pids = pid_set.num_pids; + knum_pids = task_pid(current)->level + 1; + + if (!unum_pids) + return NULL; + + if (unum_pids < 0 || unum_pids > knum_pids) + return ERR_PTR(-EINVAL); + + /* + * To keep alloc_pid() simple, allocate an extra pid_t in target_pids[] + * and set it to 0. This last entry in target_pids[] corresponds to the + * (yet-to-be-created) descendant pid-namespace if CLONE_NEWPID was + * specified. If CLONE_NEWPID was not specified, this last entry will + * simply be ignored. + */ + target_pids = kzalloc((knum_pids + 1) * sizeof(pid_t), GFP_KERNEL); + if (!target_pids) + return ERR_PTR(-ENOMEM); + + /* + * A process running in a level 2 pid namespace has three pid namespaces + * and hence three pid numbers. If this process is checkpointed, + * information about these three namespaces are saved. We refer to these + * namespaces as 'known namespaces'. + * + * If this checkpointed process is however restarted in a level 3 pid + * namespace, the restarted process has an extra ancestor pid namespace + * (i.e 'unknown namespace') and 'knum_pids' exceeds 'unum_pids'. + * + * During restart, the process requests specific pids for its 'known + * namespaces' and lets kernel assign pids to its 'unknown namespaces'. + * + * Since the requested-pids correspond to 'known namespaces' and since + * 'known-namespaces' are younger than (i.e descendants of) 'unknown- + * namespaces', copy requested pids to the back-end of target_pids[] + * (i.e before the last entry for CLONE_NEWPID mentioned above). + * Any entries in target_pids[] not corresponding to a requested pid + * will be set to zero and kernel assigns a pid in those namespaces. + * + * NOTE: The order of pids in target_pids[] is oldest pid namespace to + * youngest (target_pids[0] corresponds to init_pid_ns). i.e. + * the order is: + * + * - pids for 'unknown-namespaces' (if any) + * - pids for 'known-namespaces' (requested pids) + * - 0 in the last entry (for CLONE_NEWPID). + */ + j = knum_pids - unum_pids; + size = unum_pids * sizeof(pid_t); + + rc = copy_from_user(&target_pids[j], pid_set.pids, size); + if (rc) { + rc = -EFAULT; + goto out_free; + } + + return target_pids; + +out_free: + kfree(target_pids); + return ERR_PTR(rc); +} + +/* * Ok, this is the main fork-routine. * * It copies the process, and if successful kick-starts @@ -1354,7 +1445,7 @@ struct task_struct *p; int trace = 0; long nr; - pid_t *target_pids = NULL; + pid_t *target_pids; /* * Do some preliminary argument and permissions checking before we @@ -1388,6 +1479,17 @@ } } + target_pids = copy_target_pids(pid_setp); + + if (target_pids) { + if (IS_ERR(target_pids)) + return PTR_ERR(target_pids); + + nr = -EPERM; + if (!capable(CAP_SYS_ADMIN)) + goto out_free; + } + /* * When called from kernel_thread, don't do user tracing stuff. */ @@ -1449,6 +1551,10 @@ } else { nr = PTR_ERR(p); } + +out_free: + kfree(target_pids); + return nr; } Index: linux-2.6/include/linux/types.h =================================================================== --- linux-2.6.orig/include/linux/types.h 2009-08-05 19:55:42.000000000 -0700 +++ linux-2.6/include/linux/types.h 2009-08-05 19:56:15.000000000 -0700 @@ -209,6 +209,11 @@ pid_t *pids; }; +struct clone_tid_info { + void *parent_tid; + void *child_tid; +}; + #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ #endif /* _LINUX_TYPES_H */ ^ permalink raw reply [flat|nested] 18+ messages in thread
[parent not found: <20090806062505.GG5619-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>]
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <20090806062505.GG5619-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-06 13:38 ` Serge E. Hallyn [not found] ` <20090806133847.GA28392-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 2009-08-06 20:38 ` Matt Helsley 1 sibling, 1 reply; 18+ messages in thread From: Serge E. Hallyn @ 2009-08-06 13:38 UTC (permalink / raw) To: Sukadev Bhattiprolu; +Cc: Containers, Alexey Dobriyan Quoting Sukadev Bhattiprolu (sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org): > > Subject: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall > > Container restart requires that a task have the same pid it had when it was > checkpointed. When containers are nested the tasks within the containers > exist in multiple pid namespaces and hence have multiple pids to specify > during restart. > > This patch defines, a new system call, clone_extended() which is like clone(), > but takes a new 'pid_set' parameter. This parameter lets caller choose > specific pid numbers for the child process, in the process's active and > ancestor pid namespaces. (Descendant pid namespaces in general don't matter > since processes don't have pids in them anyway, but see comments in > copy_target_pids() regarding CLONE_NEWPID). > > Unlike clone(), however, clone_extended() needs CAP_SYS_ADMIN, at least for > now, to prevent unprivileged processes from misusing this interface. It only needs that when specifying pids. > While the main motivation for this interface is the need to let a process > choose its 'pid numbers', the clone_extended() interface uses 64-bit clone > flags. The 'higher' portion of the clone flags are unused and are only > included to preclude yet another version of clone when a new clone flag is > needed. > > ===== Interface: > > Compared to clone(), clone_extended() needs to pass in three more pieces > of information: > > - additional 32-bit of clone_flags > - number of pids in the set > - user buffer containing the list of pids. > > But since clone() already takes 5 parameters and some (all ?) architectures > are restricted to 6 parameters to a system-call, additional data-structures > (and copy_from_user()) are needed. > > The proposed interface for clone_extended() is: > > struct clone_tid_info { > void *parent_tid; /* parent_tid_ptr parameter */ > void *child_tid; /* child_tid_ptr parameter */ > }; > > struct pid_set { > int num_pids; > pid_t *pids; > }; > > int clone_extended(int flags_low, int flags_high, void *child_stack, > void *unused, struct clone_tid_info *tid_ptrs, > struct pid_set *pid_setp); I was thinking additional flags would be passed in the (renamed) struct pid_set. -serge ^ permalink raw reply [flat|nested] 18+ messages in thread
[parent not found: <20090806133847.GA28392-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>]
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <20090806133847.GA28392-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-06 15:37 ` Oren Laadan [not found] ` <4A7AF8AD.4070805-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org> 0 siblings, 1 reply; 18+ messages in thread From: Oren Laadan @ 2009-08-06 15:37 UTC (permalink / raw) To: Serge E. Hallyn; +Cc: Containers, Sukadev Bhattiprolu, Alexey Dobriyan Serge E. Hallyn wrote: > Quoting Sukadev Bhattiprolu (sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org): >> Subject: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall >> >> Container restart requires that a task have the same pid it had when it was >> checkpointed. When containers are nested the tasks within the containers >> exist in multiple pid namespaces and hence have multiple pids to specify >> during restart. >> >> This patch defines, a new system call, clone_extended() which is like clone(), >> but takes a new 'pid_set' parameter. This parameter lets caller choose >> specific pid numbers for the child process, in the process's active and >> ancestor pid namespaces. (Descendant pid namespaces in general don't matter >> since processes don't have pids in them anyway, but see comments in >> copy_target_pids() regarding CLONE_NEWPID). >> >> Unlike clone(), however, clone_extended() needs CAP_SYS_ADMIN, at least for >> now, to prevent unprivileged processes from misusing this interface. > > It only needs that when specifying pids. > >> While the main motivation for this interface is the need to let a process >> choose its 'pid numbers', the clone_extended() interface uses 64-bit clone >> flags. The 'higher' portion of the clone flags are unused and are only >> included to preclude yet another version of clone when a new clone flag is >> needed. >> >> ===== Interface: >> >> Compared to clone(), clone_extended() needs to pass in three more pieces >> of information: >> >> - additional 32-bit of clone_flags >> - number of pids in the set >> - user buffer containing the list of pids. >> >> But since clone() already takes 5 parameters and some (all ?) architectures >> are restricted to 6 parameters to a system-call, additional data-structures >> (and copy_from_user()) are needed. >> >> The proposed interface for clone_extended() is: >> >> struct clone_tid_info { >> void *parent_tid; /* parent_tid_ptr parameter */ >> void *child_tid; /* child_tid_ptr parameter */ >> }; >> >> struct pid_set { >> int num_pids; >> pid_t *pids; >> }; >> >> int clone_extended(int flags_low, int flags_high, void *child_stack, >> void *unused, struct clone_tid_info *tid_ptrs, >> struct pid_set *pid_setp); > > I was thinking additional flags would be passed in the (renamed) > struct pid_set. Yes. But maybe in (renamed) 'struct clone_info' instead of 'struct pid_set' ? I vaguely recall a strong preference to not require copy-from-user during a fast-path clone, because it may hurt performance. *If* this is the case, then maybe place extra flags among the "base" args, or at least a CLONE_EXTRA would indicate that more arguments need to be pulled from user-space ? Do you intend to get feedback from LKML too ? Oren. ^ permalink raw reply [flat|nested] 18+ messages in thread
[parent not found: <4A7AF8AD.4070805-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>]
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <4A7AF8AD.4070805-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org> @ 2009-08-06 15:55 ` Serge E. Hallyn [not found] ` <20090806155520.GA904-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 0 siblings, 1 reply; 18+ messages in thread From: Serge E. Hallyn @ 2009-08-06 15:55 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Sukadev Bhattiprolu, Alexey Dobriyan Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org): > > > Serge E. Hallyn wrote: > > Quoting Sukadev Bhattiprolu (sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org): > >> Subject: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall > >> > >> Container restart requires that a task have the same pid it had when it was > >> checkpointed. When containers are nested the tasks within the containers > >> exist in multiple pid namespaces and hence have multiple pids to specify > >> during restart. > >> > >> This patch defines, a new system call, clone_extended() which is like clone(), > >> but takes a new 'pid_set' parameter. This parameter lets caller choose > >> specific pid numbers for the child process, in the process's active and > >> ancestor pid namespaces. (Descendant pid namespaces in general don't matter > >> since processes don't have pids in them anyway, but see comments in > >> copy_target_pids() regarding CLONE_NEWPID). > >> > >> Unlike clone(), however, clone_extended() needs CAP_SYS_ADMIN, at least for > >> now, to prevent unprivileged processes from misusing this interface. > > > > It only needs that when specifying pids. > > > >> While the main motivation for this interface is the need to let a process > >> choose its 'pid numbers', the clone_extended() interface uses 64-bit clone > >> flags. The 'higher' portion of the clone flags are unused and are only > >> included to preclude yet another version of clone when a new clone flag is > >> needed. > >> > >> ===== Interface: > >> > >> Compared to clone(), clone_extended() needs to pass in three more pieces > >> of information: > >> > >> - additional 32-bit of clone_flags > >> - number of pids in the set > >> - user buffer containing the list of pids. > >> > >> But since clone() already takes 5 parameters and some (all ?) architectures > >> are restricted to 6 parameters to a system-call, additional data-structures > >> (and copy_from_user()) are needed. > >> > >> The proposed interface for clone_extended() is: > >> > >> struct clone_tid_info { > >> void *parent_tid; /* parent_tid_ptr parameter */ > >> void *child_tid; /* child_tid_ptr parameter */ > >> }; > >> > >> struct pid_set { > >> int num_pids; > >> pid_t *pids; > >> }; > >> > >> int clone_extended(int flags_low, int flags_high, void *child_stack, > >> void *unused, struct clone_tid_info *tid_ptrs, > >> struct pid_set *pid_setp); > > > > I was thinking additional flags would be passed in the (renamed) > > struct pid_set. > > Yes. > > But maybe in (renamed) 'struct clone_info' instead of 'struct pid_set' ? > > I vaguely recall a strong preference to not require copy-from-user > during a fast-path clone, because it may hurt performance. > > *If* this is the case, then maybe place extra flags among the > "base" args, or at least a CLONE_EXTRA would indicate that more > arguments need to be pulled from user-space ? Wouldn't passing NULL for struct clone_info suffice? > Do you intend to get feedback from LKML too ? > > Oren. ^ permalink raw reply [flat|nested] 18+ messages in thread
[parent not found: <20090806155520.GA904-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>]
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <20090806155520.GA904-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-06 16:05 ` Oren Laadan [not found] ` <4A7AFF61.8050802-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org> 0 siblings, 1 reply; 18+ messages in thread From: Oren Laadan @ 2009-08-06 16:05 UTC (permalink / raw) To: Serge E. Hallyn; +Cc: Containers, Sukadev Bhattiprolu, Alexey Dobriyan Serge E. Hallyn wrote: > Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org): >> >> Serge E. Hallyn wrote: >>> Quoting Sukadev Bhattiprolu (sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org): >>>> Subject: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall >>>> >>>> Container restart requires that a task have the same pid it had when it was >>>> checkpointed. When containers are nested the tasks within the containers >>>> exist in multiple pid namespaces and hence have multiple pids to specify >>>> during restart. >>>> >>>> This patch defines, a new system call, clone_extended() which is like clone(), >>>> but takes a new 'pid_set' parameter. This parameter lets caller choose >>>> specific pid numbers for the child process, in the process's active and >>>> ancestor pid namespaces. (Descendant pid namespaces in general don't matter >>>> since processes don't have pids in them anyway, but see comments in >>>> copy_target_pids() regarding CLONE_NEWPID). >>>> >>>> Unlike clone(), however, clone_extended() needs CAP_SYS_ADMIN, at least for >>>> now, to prevent unprivileged processes from misusing this interface. >>> It only needs that when specifying pids. >>> >>>> While the main motivation for this interface is the need to let a process >>>> choose its 'pid numbers', the clone_extended() interface uses 64-bit clone >>>> flags. The 'higher' portion of the clone flags are unused and are only >>>> included to preclude yet another version of clone when a new clone flag is >>>> needed. >>>> >>>> ===== Interface: >>>> >>>> Compared to clone(), clone_extended() needs to pass in three more pieces >>>> of information: >>>> >>>> - additional 32-bit of clone_flags >>>> - number of pids in the set >>>> - user buffer containing the list of pids. >>>> >>>> But since clone() already takes 5 parameters and some (all ?) architectures >>>> are restricted to 6 parameters to a system-call, additional data-structures >>>> (and copy_from_user()) are needed. >>>> >>>> The proposed interface for clone_extended() is: >>>> >>>> struct clone_tid_info { >>>> void *parent_tid; /* parent_tid_ptr parameter */ >>>> void *child_tid; /* child_tid_ptr parameter */ >>>> }; >>>> >>>> struct pid_set { >>>> int num_pids; >>>> pid_t *pids; >>>> }; >>>> >>>> int clone_extended(int flags_low, int flags_high, void *child_stack, >>>> void *unused, struct clone_tid_info *tid_ptrs, >>>> struct pid_set *pid_setp); >>> I was thinking additional flags would be passed in the (renamed) >>> struct pid_set. >> Yes. >> >> But maybe in (renamed) 'struct clone_info' instead of 'struct pid_set' ? >> >> I vaguely recall a strong preference to not require copy-from-user >> during a fast-path clone, because it may hurt performance. >> >> *If* this is the case, then maybe place extra flags among the >> "base" args, or at least a CLONE_EXTRA would indicate that more >> arguments need to be pulled from user-space ? > > Wouldn't passing NULL for struct clone_info suffice? :o Actually, I misread the original prototype, and I prefer Suka's current suggestion. Oren. > >> Do you intend to get feedback from LKML too ? >> >> Oren. ^ permalink raw reply [flat|nested] 18+ messages in thread
[parent not found: <4A7AFF61.8050802-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>]
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <4A7AFF61.8050802-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org> @ 2009-08-06 16:16 ` Serge E. Hallyn [not found] ` <20090806161616.GA1472-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 0 siblings, 1 reply; 18+ messages in thread From: Serge E. Hallyn @ 2009-08-06 16:16 UTC (permalink / raw) To: Oren Laadan; +Cc: Containers, Sukadev Bhattiprolu, Alexey Dobriyan Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org): > >>>> The proposed interface for clone_extended() is: > >>>> > >>>> struct clone_tid_info { > >>>> void *parent_tid; /* parent_tid_ptr parameter */ > >>>> void *child_tid; /* child_tid_ptr parameter */ > >>>> }; > >>>> > >>>> struct pid_set { > >>>> int num_pids; > >>>> pid_t *pids; > >>>> }; > >>>> > >>>> int clone_extended(int flags_low, int flags_high, void *child_stack, > >>>> void *unused, struct clone_tid_info *tid_ptrs, > >>>> struct pid_set *pid_setp); > >>> I was thinking additional flags would be passed in the (renamed) > >>> struct pid_set. > >> Yes. > >> > >> But maybe in (renamed) 'struct clone_info' instead of 'struct pid_set' ? > >> > >> I vaguely recall a strong preference to not require copy-from-user > >> during a fast-path clone, because it may hurt performance. > >> > >> *If* this is the case, then maybe place extra flags among the > >> "base" args, or at least a CLONE_EXTRA would indicate that more > >> arguments need to be pulled from user-space ? > > > > Wouldn't passing NULL for struct clone_info suffice? > > :o > > Actually, I misread the original prototype, and I prefer Suka's > current suggestion. I think Suka's suggestion is again inherently limited (in # of clone flags) and will force even uglier syscalls for each arch than the previous one already does. -serge ^ permalink raw reply [flat|nested] 18+ messages in thread
[parent not found: <20090806161616.GA1472-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>]
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <20090806161616.GA1472-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-06 18:23 ` Sukadev Bhattiprolu [not found] ` <20090806182340.GA2579-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 0 siblings, 1 reply; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-06 18:23 UTC (permalink / raw) To: Serge E. Hallyn; +Cc: Containers, Alexey Dobriyan | I think Suka's suggestion is again inherently limited (in # of | clone flags) and will force even uglier syscalls for each arch than the | previous one already does. | Yes other architectures are forced to ignore the flags_high and copy-in the tid pointers. But if we want more than 64 bit flags, we may need follow the sigset_t model ? Also, I am listing three approaches below. Do you prefer #2 below ? 1. ===== struct clone_tid_info { void *parent_tid; /* parent_tid_ptr parameter */ void *child_tid; /* child_tid_ptr parameter */ }; struct pid_set { int num_pids; pid_t *pids; }; int clone_extended(int flags_low, int flags_high, void *child_stack, void *unused, struct clone_tid_info *tid_ptrs, struct pid_set *pid_setp); 2. ====== struct clone_info { int flags_high; struct pid_set pid_set; } int clone_extended(int flags_low, void *child_stack, void *unused, int *parent_tid, int *child_tid, struct clone_info *clone_info); Pros: - copy_from_user() needed only for new flags and pid_set Cons: - splitting the high and low clone-flags is awkward ? 3. ===== typedef struct { unsigned long flags[N_CLONE_FLAGS]; } clone_flags_t; int clone_extended(clone_flags_t *flags, void *child_stack, int *unused, int *parent_tid, int *child_tid, struct pid_set *pid_set); Pros: - extendible clone_flags (like sigset_t) - no copy_from_user() when we have 32 clone-flags - no copy_from_user for tids Cons: - copy_from_user() needed on 32-bit architectures for all flags when they exceed 32. Sukadev ^ permalink raw reply [flat|nested] 18+ messages in thread
[parent not found: <20090806182340.GA2579-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>]
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <20090806182340.GA2579-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-06 18:35 ` Serge E. Hallyn 0 siblings, 0 replies; 18+ messages in thread From: Serge E. Hallyn @ 2009-08-06 18:35 UTC (permalink / raw) To: Sukadev Bhattiprolu; +Cc: Containers, Alexey Dobriyan Quoting Sukadev Bhattiprolu (sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org): > | I think Suka's suggestion is again inherently limited (in # of > | clone flags) and will force even uglier syscalls for each arch than the > | previous one already does. > | > > Yes other architectures are forced to ignore the flags_high and copy-in the > tid pointers. > > But if we want more than 64 bit flags, we may need follow the sigset_t > model ? > > Also, I am listing three approaches below. Do you prefer #2 below ? I prefer #2 with 'struct pid_set' renamed to clone_ext_data or something, and either a version # or int num_clone_words so we can add clone flags later. I know, adding more then 32 more clone flags seems unlikely, but... > 1. ===== > > struct clone_tid_info { > void *parent_tid; /* parent_tid_ptr parameter */ > void *child_tid; /* child_tid_ptr parameter */ > }; > > struct pid_set { > int num_pids; > pid_t *pids; > }; > > int clone_extended(int flags_low, int flags_high, void *child_stack, > void *unused, struct clone_tid_info *tid_ptrs, > struct pid_set *pid_setp); > > 2. ====== > > struct clone_info { > int flags_high; > struct pid_set pid_set; > } > > int clone_extended(int flags_low, void *child_stack, void *unused, > int *parent_tid, int *child_tid, struct clone_info *clone_info); > > > Pros: > - copy_from_user() needed only for new flags and pid_set > > Cons: > - splitting the high and low clone-flags is awkward ? > > 3. ===== > > typedef struct { > unsigned long flags[N_CLONE_FLAGS]; > } clone_flags_t; > > int clone_extended(clone_flags_t *flags, void *child_stack, int *unused, > int *parent_tid, int *child_tid, struct pid_set *pid_set); > > > Pros: > - extendible clone_flags (like sigset_t) > - no copy_from_user() when we have 32 clone-flags > - no copy_from_user for tids > > Cons: > - copy_from_user() needed on 32-bit architectures for all flags > when they exceed 32. > > > Sukadev ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall [not found] ` <20090806062505.GG5619-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> 2009-08-06 13:38 ` Serge E. Hallyn @ 2009-08-06 20:38 ` Matt Helsley 1 sibling, 0 replies; 18+ messages in thread From: Matt Helsley @ 2009-08-06 20:38 UTC (permalink / raw) To: Sukadev Bhattiprolu; +Cc: Containers, Alexey Dobriyan On Wed, Aug 05, 2009 at 11:25:05PM -0700, Sukadev Bhattiprolu wrote: > > Subject: [RFC][v4][PATCH 7/7]: Define clone_extended() syscall > > Container restart requires that a task have the same pid it had when it was > checkpointed. When containers are nested the tasks within the containers > exist in multiple pid namespaces and hence have multiple pids to specify > during restart. > > This patch defines, a new system call, clone_extended() which is like clone(), > but takes a new 'pid_set' parameter. This parameter lets caller choose > specific pid numbers for the child process, in the process's active and > ancestor pid namespaces. (Descendant pid namespaces in general don't matter > since processes don't have pids in them anyway, but see comments in > copy_target_pids() regarding CLONE_NEWPID). > > Unlike clone(), however, clone_extended() needs CAP_SYS_ADMIN, at least for > now, to prevent unprivileged processes from misusing this interface. It might be good to describe how, without CAP_SYS_ADMIN, the interface could be misused (I believe this was Linus' point): In the status quo, a malicious task must fork rapidly in order to obtain by trial-and-error the same pid as found in a stale /var/run/foo.pid file. Without CAP_SYS_ADMIN clone_extended() would remove the trial-and-error element that loosely protects the system from such malicious attacks. Cheers, -Matt ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 0/7] clone_with_pids() system call
@ 2009-08-07 6:11 Sukadev Bhattiprolu
[not found] ` <20090807061103.GA19343-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-07 6:12 ` Sukadev Bhattiprolu
0 siblings, 2 replies; 18+ messages in thread
From: Sukadev Bhattiprolu @ 2009-08-07 6:11 UTC (permalink / raw)
To: linux-kernel
Cc: Oren Laadan, Eric W. Biederman, serue, Alexey Dobriyan,
Pavel Emelyanov, Andrew Morton, torvalds, mikew, mingo, hpa,
Containers, sukadev
=== NEW CLONE() SYSTEM CALL:
To support application checkpoint/restart, a task must have the same pid it
had when it was checkpointed. When containers are nested, the tasks within
the containers exist in multiple pid namespaces and hence have multiple pids
to specify during restart.
This patchset implements a new system call, clone_with_pids() that lets a
process specify the pids of the child process.
Patches 1 through 5 are helpers and we believe they are needed for application
restart, regardless of the kernel implementation of application restart.
Patch 7/7 defines a prototype of the new system call.
=== IMPORTANT TODO:
clone() system call has another limitation - all available bits in clone-flags
are in use and any new clone-flag will need a variant of the clone() system
call.
It appears to make sense to try and extend this new system call to address
this limitation as well. The basic requirements of a new clone system call
could then be summarized as:
- do everything clone() does today, and
- give application an ability to choose pids for the child process
in all ancestor pid namespaces, and
- allow more clone_flags
Contstraints:
- system-calls are restricted to 6 parameters and clone() already
takes 5 parameters, any extension to clone() interface would require
one or more copy_from_user().
- does copy_from_user() of a few words have a significant impact on
the total cost of clone() ?
Based on these requirements and constraints, we have been exploring a couple
of system call interfaces and appreciate any iput.
1. =====
#if 64bit
#define CLONE_FLAGS_WORDS 1
#else
#define CLONE_FLAGS_WORDS 2
#endif
struct pid_set {
int num_pids;
pid_t *pids;
};
typedef struct {
unsigned long flags[CLONE_FLAGS_WORDS];
} clone_flags_t;
int clone_extended(clone_flags_t *flags, void *child_stack, int *unused,
int *parent_tid, int *child_tid, struct pid_set *pid_set);
Pros:
- extendible clone_flags (like sigset_t)
Cons:
- copy_from_user() needed on all architectures (we maybe able
to play some tricks with 'clone_flags_t' to avoid the copy
on 64-bit archtitectures till N_CLONE_FLAGS exceeds 64).
- Both applications and kernel must use interfaces equivalent
to sigsetops(3) to test/set/clear clone flags.
2. ======
struct clone_info {
int num_clone_high_words;
int *flags_high;
struct pid_set pid_set;
}
int clone_extended(int flags_low, void *child_stack, void *unused,
int *parent_tid, int *child_tid, struct clone_info *clone_info);
Pros:
- copy_from_user() needed only for new flags and pid_set
Cons:
- splitting the high and low clone-flags is awkward ?
Signed-off-by: Sukadev Bhattiprolu <sukadev@us.ibm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread[parent not found: <20090807061103.GA19343-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>]
* [RFC][v4][PATCH 1/7]: Factor out code to allocate pidmap page [not found] ` <20090807061103.GA19343-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-07 6:12 ` Sukadev Bhattiprolu 0 siblings, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-07 6:12 UTC (permalink / raw) To: linux-kernel-u79uwXL29TY76Z2rM5mHXA Cc: Containers, Eric W. Biederman, hpa-YMNOUZJC4hwAvxtiuMwx3w, mingo-X9Un+BFzKDI, torvalds-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b, Alexey Dobriyan, Pavel Emelyanov Subject: [RFC][v4][PATCH 1/7]: Factor out code to allocate pidmap page To implement support for clone_with_pids() system call we would need to allocate pidmap page in more than one place. Move this code to a new function alloc_pidmap_page(). Changelog[v2]: - (Matt Helsley, Dave Hansen) Have alloc_pidmap_page() return -ENOMEM on error instead of -1. Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> Reviewed-by: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org> --- kernel/pid.c | 46 ++++++++++++++++++++++++++++++---------------- 1 files changed, 30 insertions(+), 16 deletions(-) Index: linux-2.6/kernel/pid.c =================================================================== --- linux-2.6.orig/kernel/pid.c 2009-08-05 17:00:22.000000000 -0700 +++ linux-2.6/kernel/pid.c 2009-08-05 17:02:40.000000000 -0700 @@ -122,9 +122,34 @@ atomic_inc(&map->nr_free); } +static int alloc_pidmap_page(struct pidmap *map) +{ + void *page; + + if (likely(map->page)) + return 0; + + page = kzalloc(PAGE_SIZE, GFP_KERNEL); + + /* + * Free the page if someone raced with us installing it: + */ + spin_lock_irq(&pidmap_lock); + if (map->page) + kfree(page); + else + map->page = page; + spin_unlock_irq(&pidmap_lock); + + if (unlikely(!map->page)) + return -ENOMEM; + + return 0; +} + static int alloc_pidmap(struct pid_namespace *pid_ns) { - int i, offset, max_scan, pid, last = pid_ns->last_pid; + int i, rc, offset, max_scan, pid, last = pid_ns->last_pid; struct pidmap *map; pid = last + 1; @@ -134,21 +159,10 @@ map = &pid_ns->pidmap[pid/BITS_PER_PAGE]; max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset; for (i = 0; i <= max_scan; ++i) { - if (unlikely(!map->page)) { - void *page = kzalloc(PAGE_SIZE, GFP_KERNEL); - /* - * Free the page if someone raced with us - * installing it: - */ - spin_lock_irq(&pidmap_lock); - if (map->page) - kfree(page); - else - map->page = page; - spin_unlock_irq(&pidmap_lock); - if (unlikely(!map->page)) - break; - } + rc = alloc_pidmap_page(map); + if (rc) + break; + if (likely(atomic_read(&map->nr_free))) { do { if (!test_and_set_bit(offset, map->page)) { ^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC][v4][PATCH 1/7]: Factor out code to allocate pidmap page 2009-08-07 6:11 [RFC][v4][PATCH 0/7] clone_with_pids() system call Sukadev Bhattiprolu [not found] ` <20090807061103.GA19343-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> @ 2009-08-07 6:12 ` Sukadev Bhattiprolu 1 sibling, 0 replies; 18+ messages in thread From: Sukadev Bhattiprolu @ 2009-08-07 6:12 UTC (permalink / raw) To: linux-kernel Cc: Oren Laadan, Eric W. Biederman, serue, Alexey Dobriyan, Pavel Emelyanov, Andrew Morton, torvalds, mikew, mingo, hpa, Containers, sukadev Subject: [RFC][v4][PATCH 1/7]: Factor out code to allocate pidmap page To implement support for clone_with_pids() system call we would need to allocate pidmap page in more than one place. Move this code to a new function alloc_pidmap_page(). Changelog[v2]: - (Matt Helsley, Dave Hansen) Have alloc_pidmap_page() return -ENOMEM on error instead of -1. Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> Acked-by: Serge Hallyn <serue@us.ibm.com> Reviewed-by: Oren Laadan <orenl@cs.columbia.edu> --- kernel/pid.c | 46 ++++++++++++++++++++++++++++++---------------- 1 files changed, 30 insertions(+), 16 deletions(-) Index: linux-2.6/kernel/pid.c =================================================================== --- linux-2.6.orig/kernel/pid.c 2009-08-05 17:00:22.000000000 -0700 +++ linux-2.6/kernel/pid.c 2009-08-05 17:02:40.000000000 -0700 @@ -122,9 +122,34 @@ atomic_inc(&map->nr_free); } +static int alloc_pidmap_page(struct pidmap *map) +{ + void *page; + + if (likely(map->page)) + return 0; + + page = kzalloc(PAGE_SIZE, GFP_KERNEL); + + /* + * Free the page if someone raced with us installing it: + */ + spin_lock_irq(&pidmap_lock); + if (map->page) + kfree(page); + else + map->page = page; + spin_unlock_irq(&pidmap_lock); + + if (unlikely(!map->page)) + return -ENOMEM; + + return 0; +} + static int alloc_pidmap(struct pid_namespace *pid_ns) { - int i, offset, max_scan, pid, last = pid_ns->last_pid; + int i, rc, offset, max_scan, pid, last = pid_ns->last_pid; struct pidmap *map; pid = last + 1; @@ -134,21 +159,10 @@ map = &pid_ns->pidmap[pid/BITS_PER_PAGE]; max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset; for (i = 0; i <= max_scan; ++i) { - if (unlikely(!map->page)) { - void *page = kzalloc(PAGE_SIZE, GFP_KERNEL); - /* - * Free the page if someone raced with us - * installing it: - */ - spin_lock_irq(&pidmap_lock); - if (map->page) - kfree(page); - else - map->page = page; - spin_unlock_irq(&pidmap_lock); - if (unlikely(!map->page)) - break; - } + rc = alloc_pidmap_page(map); + if (rc) + break; + if (likely(atomic_read(&map->nr_free))) { do { if (!test_and_set_bit(offset, map->page)) { ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2009-08-07 6:12 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-08-06 6:10 [RFC][v4][PATCH 0/7] clone_extended() syscall Sukadev Bhattiprolu
[not found] ` <20090806061056.GA1044-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-06 6:22 ` [RFC][v4][PATCH 1/7] Factor out code to allocate pidmap page Sukadev Bhattiprolu
2009-08-06 6:23 ` [RFC][v4][PATCH 2/7]: Have alloc_pidmap() return actual error code Sukadev Bhattiprolu
2009-08-06 6:23 ` [RFC][v4][PATCH 3/7]: Add target_pid parameter to alloc_pidmap() Sukadev Bhattiprolu
2009-08-06 6:24 ` [RFC][v4][PATCH 4/7]: Add target_pids parameter to alloc_pid() Sukadev Bhattiprolu
2009-08-06 6:24 ` [RFC][v4][PATCH 5/7]: Add target_pids parameter to copy_process() Sukadev Bhattiprolu
2009-08-06 6:24 ` [RFC][v4][PATCH 6/7]: Define do_fork_with_pids() Sukadev Bhattiprolu
2009-08-06 6:25 ` [RFC][v4][PATCH 7/7]: Define clone_extended() syscall Sukadev Bhattiprolu
[not found] ` <20090806062505.GG5619-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-06 13:38 ` Serge E. Hallyn
[not found] ` <20090806133847.GA28392-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-06 15:37 ` Oren Laadan
[not found] ` <4A7AF8AD.4070805-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
2009-08-06 15:55 ` Serge E. Hallyn
[not found] ` <20090806155520.GA904-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-06 16:05 ` Oren Laadan
[not found] ` <4A7AFF61.8050802-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
2009-08-06 16:16 ` Serge E. Hallyn
[not found] ` <20090806161616.GA1472-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-06 18:23 ` Sukadev Bhattiprolu
[not found] ` <20090806182340.GA2579-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-06 18:35 ` Serge E. Hallyn
2009-08-06 20:38 ` Matt Helsley
-- strict thread matches above, loose matches on Subject: below --
2009-08-07 6:11 [RFC][v4][PATCH 0/7] clone_with_pids() system call Sukadev Bhattiprolu
[not found] ` <20090807061103.GA19343-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-08-07 6:12 ` [RFC][v4][PATCH 1/7]: Factor out code to allocate pidmap page Sukadev Bhattiprolu
2009-08-07 6:12 ` Sukadev Bhattiprolu
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.