Messages in this thread Patch in this message | | | From | Tim Hockin <> | Subject | PATCH: CLONE_PARENT (was CLONE_PPID) | Date | Tue, 24 Aug 1999 03:32:57 -0500 (CDT) |
| |
Here is the latest rev of the CLONE_PPID patch. It has been renamed to CLONE_PARENT, because that was already in the source tree for 2.3.x.
This removes CLONE_WAIT, but adds CLONE_CHILDREN by David Wragg - much nicer.
Please have a look, point out stupidness, etc. With all the waitqueue changes betwixt 2.2 and 2.3, please keep an eye for those.
Thanks!
Tim
p.s. - If all looks good I'll try again to get this against 2.3.latest, and send to Linus.
Tim
diff -ruN clean-linux-2.3.13/arch/alpha/kernel/process.c clone-linux-2.3.13/arch/alpha/kernel/process.c --- clean-linux-2.3.13/arch/alpha/kernel/process.c Tue Aug 3 08:20:45 1999 +++ clone-linux-2.3.13/arch/alpha/kernel/process.c Mon Aug 23 03:39:30 1999 @@ -57,6 +57,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); union task_union init_task_union __attribute__((section("init_task"))) diff -ruN clean-linux-2.3.13/arch/arm/kernel/init_task.c clone-linux-2.3.13/arch/arm/kernel/init_task.c --- clean-linux-2.3.13/arch/arm/kernel/init_task.c Mon Jul 26 22:41:09 1999 +++ clone-linux-2.3.13/arch/arm/kernel/init_task.c Mon Aug 23 03:39:30 1999 @@ -8,6 +8,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); /* diff -ruN clean-linux-2.3.13/arch/i386/kernel/init_task.c clone-linux-2.3.13/arch/i386/kernel/init_task.c --- clean-linux-2.3.13/arch/i386/kernel/init_task.c Tue Aug 3 10:32:57 1999 +++ clone-linux-2.3.13/arch/i386/kernel/init_task.c Mon Aug 23 03:39:30 1999 @@ -10,6 +10,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); /* diff -ruN clean-linux-2.3.13/arch/m68k/kernel/process.c clone-linux-2.3.13/arch/m68k/kernel/process.c --- clean-linux-2.3.13/arch/m68k/kernel/process.c Mon Aug 9 12:27:30 1999 +++ clone-linux-2.3.13/arch/m68k/kernel/process.c Mon Aug 23 03:39:30 1999 @@ -42,6 +42,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); union task_union init_task_union diff -ruN clean-linux-2.3.13/arch/mips/kernel/init_task.c clone-linux-2.3.13/arch/mips/kernel/init_task.c --- clean-linux-2.3.13/arch/mips/kernel/init_task.c Mon Jul 26 22:41:09 1999 +++ clone-linux-2.3.13/arch/mips/kernel/init_task.c Mon Aug 23 03:39:30 1999 @@ -8,6 +8,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); /* diff -ruN clean-linux-2.3.13/arch/mips/kernel/irixsig.c clone-linux-2.3.13/arch/mips/kernel/irixsig.c --- clean-linux-2.3.13/arch/mips/kernel/irixsig.c Thu Jul 22 09:47:55 1999 +++ clone-linux-2.3.13/arch/mips/kernel/irixsig.c Mon Aug 23 03:39:30 1999 @@ -686,10 +686,10 @@ retval = -EINVAL; goto out; } - add_wait_queue(¤t->wait_chldexit, &wait); + add_wait_queue(¤t->children->wait_chldexit, &wait); repeat: flag = 0; - for(p = current->p_cptr; p; p = p->p_osptr) { + for(p = current->children->youngest; p; p = p->p_osptr) { if((type == P_PID) && p->pid != pid) continue; if((type == P_PGID) && p->pgrp != pid) @@ -756,7 +756,7 @@ } retval = -ECHILD; end_waitsys: - remove_wait_queue(¤t->wait_chldexit, &wait); + remove_wait_queue(¤t->children->wait_chldexit, &wait); out: unlock_kernel(); diff -ruN clean-linux-2.3.13/arch/ppc/kernel/process.c clone-linux-2.3.13/arch/ppc/kernel/process.c --- clean-linux-2.3.13/arch/ppc/kernel/process.c Mon Jul 26 22:41:09 1999 +++ clone-linux-2.3.13/arch/ppc/kernel/process.c Mon Aug 23 03:39:30 1999 @@ -49,6 +49,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); union task_union init_task_union = { INIT_TASK(init_task_union.task) }; /* only used to get secondary processor up */ diff -ruN clean-linux-2.3.13/arch/sparc/kernel/init_task.c clone-linux-2.3.13/arch/sparc/kernel/init_task.c --- clean-linux-2.3.13/arch/sparc/kernel/init_task.c Mon Jul 26 22:41:09 1999 +++ clone-linux-2.3.13/arch/sparc/kernel/init_task.c Mon Aug 23 03:39:30 1999 @@ -8,6 +8,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); /* .text section in head.S is aligned at 8k boundry and this gets linked diff -ruN clean-linux-2.3.13/arch/sparc64/kernel/init_task.c clone-linux-2.3.13/arch/sparc64/kernel/init_task.c --- clean-linux-2.3.13/arch/sparc64/kernel/init_task.c Mon Jul 26 22:41:09 1999 +++ clone-linux-2.3.13/arch/sparc64/kernel/init_task.c Mon Aug 23 03:39:30 1999 @@ -8,6 +8,7 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; +static struct children_struct init_children = INIT_CHILDREN(init_children); struct mm_struct init_mm = INIT_MM(init_mm); /* .text section in head.S is aligned at 2 page boundry and this gets linked diff -ruN clean-linux-2.3.13/fs/exec.c clone-linux-2.3.13/fs/exec.c --- clean-linux-2.3.13/fs/exec.c Fri Aug 6 11:11:03 1999 +++ clone-linux-2.3.13/fs/exec.c Mon Aug 23 11:20:51 1999 @@ -402,6 +402,73 @@ } /* + * When a process with a shared child list execs, we have to give + * it a new unshared child list. Allocate the new list. + */ + +static inline int alloc_private_children(void) +{ + struct children_struct * newkids; + + if (atomic_read(¤t->children->count) <= 1) + return 0; + + newkids = kmalloc(sizeof(*newkids), GFP_KERNEL); + if (newkids == NULL) + return -ENOMEM; + + current->children = newkids; + return 0; +} + +/* + * Unshare the child list. Any children that have current as + * their parent go into its new private child list. + */ + +static inline void make_private_children(struct children_struct *oldkids) +{ + struct task_struct * p, * next; + struct task_struct * oldprev, ** oldlink; + struct task_struct * newprev, ** newlink; + + if (current->children == oldkids) + return; + + atomic_dec(&oldkids->count); + atomic_set(¤t->children->count, 1); + init_waitqueue_head(¤t->children->wait_chldexit); + + newlink = ¤t->children->youngest; + newprev = NULL; + + oldlink = &oldkids->youngest; + oldprev = NULL; + + for (p = oldkids->youngest; p != NULL; p = next) { + next = p->p_osptr; + + if (p->p_pptr == current) { + *newlink = p; + newlink = &p->p_osptr; + + p->p_ysptr = newprev; + newprev = p; + } + else { + *oldlink = p; + oldlink = &p->p_osptr; + + p->p_ysptr = oldprev; + oldprev = p; + } + } + + *oldlink = NULL; + *newlink = NULL; +} + +/* * These functions flushes out all traces of the currently running executable * so that a new one can be started */ @@ -431,6 +498,7 @@ char * name; int i, ch, retval; struct signal_struct * oldsig; + struct children_struct * oldkids; /* * Make sure we have a private signal table @@ -439,6 +507,13 @@ retval = make_private_signals(); if (retval) goto flush_failed; + /* + * Allocate the private child list + */ + oldkids = current->children; + retval = alloc_private_children(); + if (retval) goto children_failed; + /* * Release all of the old mmap stuff */ @@ -447,6 +522,7 @@ /* This is the point of no return */ release_old_signals(oldsig); + make_private_children(oldkids); if (current->euid == current->uid && current->egid == current->gid) current->dumpable = 1; @@ -472,10 +548,16 @@ return 0; mmap_failed: - if (current->sig != oldsig) + if (current->children != oldkids) { + kfree(current->children); + current->children = oldkids; + } +children_failed: + if (current->sig != oldsig) { kfree(current->sig); + current->sig = oldsig; + } flush_failed: - current->sig = oldsig; return retval; } @@ -781,9 +863,12 @@ goto out; retval = search_binary_handler(&bprm,regs); - if (retval >= 0) + if (retval >= 0) { + /* clear any thread-special bits - JIC */ + current->flags &= ~PF_PARENTOK; /* execve success */ return retval; + } out: /* Something went wrong, return the inode and free the argument pages*/ diff -ruN clean-linux-2.3.13/include/linux/sched.h clone-linux-2.3.13/include/linux/sched.h --- clean-linux-2.3.13/include/linux/sched.h Mon Aug 9 12:42:10 1999 +++ clone-linux-2.3.13/include/linux/sched.h Mon Aug 23 03:46:19 1999 @@ -35,7 +35,10 @@ #define CLONE_PID 0x00001000 /* set if pid shared */ #define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */ #define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ -#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */ +#define CLONE_PARENTOK 0x00008000 /* set if the child should be able to create siblings */ +#define CLONE_PARENT 0x00010000 /* set if we want to create a sibling, not a child */ +#define CLONE_CHILDREN 0x00020000 /* set if child list shared */ +#define CLONE_SUSPENDED 0x00040000 /* create a process in the stopped state */ /* * These are the constant used to fake the fixed-point load-average @@ -233,6 +236,15 @@ { {{0,}}, }, \ SPIN_LOCK_UNLOCKED } +struct children_struct { + atomic_t count; + struct task_struct *youngest; + wait_queue_head_t wait_chldexit; /* for wait4() */ +}; + +#define INIT_CHILDREN(name) { ATOMIC_INIT(1), NULL, \ + __WAIT_QUEUE_HEAD_INITIALIZER(name.wait_chldexit) } + /* * Some day this will be a full-fledged user tracking system.. * Right now it is only used to track how many processes a @@ -279,17 +291,18 @@ /* boolean value for session group leader */ int leader; /* - * pointers to (original) parent process, youngest child, younger sibling, + * pointers to (original) parent process, child list, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->p_pptr->pid) */ - struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr; + struct task_struct *p_opptr, *p_pptr; + struct children_struct *children; + struct task_struct *p_ysptr, *p_osptr; /* PID hash table linkage. */ struct task_struct *pidhash_next; struct task_struct **pidhash_pprev; - wait_queue_head_t wait_chldexit; /* for wait4() */ struct semaphore *vfork_sem; /* for vfork() */ unsigned long policy, rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; @@ -355,6 +368,7 @@ #define PF_USEDFPU 0x00100000 /* task used FPU this quantum (SMP) */ #define PF_DTRACE 0x00200000 /* delayed trace (used on m68k, i386) */ +#define PF_PARENTOK 0x00400000 /* task is allowed to use CLONE_PARENT */ /* * Limit the stack by to some sane default: root can always @@ -376,9 +390,9 @@ /* binfmt */ NULL, \ /* ec,brk... */ 0,0,0,0,0,0, \ /* pid etc.. */ 0,0,0,0,0, \ -/* proc links*/ &init_task,&init_task,NULL,NULL,NULL, \ +/* proc links*/ &init_task,&init_task,&init_children,NULL,NULL, \ /* pidhash */ NULL, NULL, \ -/* chld wait */ __WAIT_QUEUE_HEAD_INITIALIZER(name.wait_chldexit), NULL, \ +/* vfork_sem */ NULL, \ /* timeout */ SCHED_OTHER,0,0,0,0,0,0,0, \ /* timer */ { NULL, NULL, 0, 0, it_real_fn }, \ /* utime */ {0,0,0,0},0, \ @@ -676,6 +690,7 @@ extern void exit_fs(struct task_struct *); extern void exit_files(struct task_struct *); extern void exit_sighand(struct task_struct *); +extern void exit_children(struct task_struct *); extern int do_execve(char *, char **, char **, struct pt_regs *); extern int do_fork(unsigned long, unsigned long, struct pt_regs *); @@ -768,7 +783,7 @@ if ((p)->p_ysptr) \ (p)->p_ysptr->p_osptr = (p)->p_osptr; \ else \ - (p)->p_pptr->p_cptr = (p)->p_osptr; \ + (p)->p_pptr->children->youngest = (p)->p_osptr; \ } while (0) #define SET_LINKS(p) do { \ @@ -777,9 +792,9 @@ init_task.prev_task->next_task = (p); \ init_task.prev_task = (p); \ (p)->p_ysptr = NULL; \ - if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \ + if (((p)->p_osptr = (p)->p_pptr->children->youngest) != NULL) \ (p)->p_osptr->p_ysptr = p; \ - (p)->p_pptr->p_cptr = p; \ + (p)->p_pptr->children->youngest = p; \ } while (0) #define for_each_task(p) \ diff -ruN clean-linux-2.3.13/kernel/exit.c clone-linux-2.3.13/kernel/exit.c --- clean-linux-2.3.13/kernel/exit.c Thu Jul 29 13:40:13 1999 +++ clone-linux-2.3.13/kernel/exit.c Mon Aug 23 03:39:30 1999 @@ -135,6 +135,7 @@ read_lock(&tasklist_lock); for_each_task(p) { if (p->p_opptr == father) { + p->flags &= ~PF_PARENTOK; p->exit_signal = SIGCHLD; p->p_opptr = child_reaper; /* init */ if (p->pdeath_signal) send_sig(p->pdeath_signal, p, 0); @@ -260,6 +261,30 @@ mmdrop(active_mm); } +static inline void __exit_children(struct task_struct *tsk) +{ + struct children_struct * children = tsk->children; + + if (children) { + tsk->children = NULL; + if (atomic_dec_and_test(&children->count)) { + /* + * We will already have dealt with orphans, so + * children->youngest == NULL + */ + if (children->youngest) + panic("stray children"); + + kfree(children); + } + } +} + +void exit_children(struct task_struct *tsk) +{ + __exit_children(tsk); +} + /* * Turn us into a lazy TLB process if we * aren't already.. @@ -288,7 +313,7 @@ */ static void exit_notify(void) { - struct task_struct * p; + struct task_struct * p, * next, * prev, ** link; forget_original_parent(current); /* @@ -315,23 +340,35 @@ * This loop does two things: * * A. Make init inherit all the child processes + * with parent == current. * B. Check to see if any process groups have become orphaned * as a result of our exiting, and if they have any stopped * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) */ write_lock_irq(&tasklist_lock); - while (current->p_cptr != NULL) { - p = current->p_cptr; - current->p_cptr = p->p_osptr; + link = ¤t->children->youngest; + prev = NULL; + for (p = current->children->youngest; p != NULL; p = next) { + next = p->p_osptr; + + if (p->p_pptr != current) { + *link = p; + link = &p->p_osptr; + + p->p_ysptr = prev; + prev = p; + + continue; + } p->p_ysptr = NULL; p->flags &= ~(PF_PTRACED|PF_TRACESYS); p->p_pptr = p->p_opptr; - p->p_osptr = p->p_pptr->p_cptr; + p->p_osptr = p->p_pptr->children->youngest; if (p->p_osptr) p->p_osptr->p_ysptr = p; - p->p_pptr->p_cptr = p; + p->p_pptr->children->youngest = p; if (p->state == TASK_ZOMBIE) notify_parent(p, p->exit_signal); /* @@ -352,6 +389,7 @@ write_lock_irq(&tasklist_lock); } } + *link = NULL; write_unlock_irq(&tasklist_lock); if (current->leader) @@ -388,6 +426,7 @@ tsk->state = TASK_ZOMBIE; tsk->exit_code = code; exit_notify(); + __exit_children(tsk); #ifdef DEBUG_PROC_TREE audit_ptree(); #endif @@ -426,11 +465,14 @@ if (options & ~(WNOHANG|WUNTRACED|__WCLONE)) return -EINVAL; - add_wait_queue(¤t->wait_chldexit,&wait); + if (pid == current->pid) + return -ECHILD; + + add_wait_queue(¤t->children->wait_chldexit,&wait); repeat: flag = 0; read_lock(&tasklist_lock); - for (p = current->p_cptr ; p ; p = p->p_osptr) { + for (p = current->children->youngest ; p ; p = p->p_osptr) { if (pid>0) { if (p->pid != pid) continue; @@ -501,7 +543,7 @@ } retval = -ECHILD; end_wait4: - remove_wait_queue(¤t->wait_chldexit,&wait); + remove_wait_queue(¤t->children->wait_chldexit,&wait); return retval; } diff -ruN clean-linux-2.3.13/kernel/fork.c clone-linux-2.3.13/kernel/fork.c --- clean-linux-2.3.13/kernel/fork.c Mon Aug 2 22:07:16 1999 +++ clone-linux-2.3.13/kernel/fork.c Mon Aug 23 03:39:30 1999 @@ -568,16 +568,36 @@ return 0; } +static int copy_children(unsigned long clone_flags, struct task_struct * tsk) +{ + if (clone_flags & CLONE_CHILDREN) { + atomic_inc(¤t->children->count); + return 0; + } + + tsk->children = kmalloc(sizeof(*tsk->children), GFP_KERNEL); + if (!tsk->children) + return -1; + atomic_set(&tsk->children->count, 1); + tsk->children->youngest = NULL; + init_waitqueue_head(&tsk->children->wait_chldexit); + + return 0; +} + static inline void copy_flags(unsigned long clone_flags, struct task_struct *p) { unsigned long new_flags = p->flags; - new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU | PF_VFORK); + new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU | PF_VFORK | PF_PARENTOK); new_flags |= PF_FORKNOEXEC; if (!(clone_flags & CLONE_PTRACE)) new_flags &= ~(PF_PTRACED|PF_TRACESYS); if (clone_flags & CLONE_VFORK) new_flags |= PF_VFORK; + if (clone_flags & CLONE_PARENTOK) + new_flags |= PF_PARENTOK; + p->flags = new_flags; } @@ -638,10 +658,17 @@ p->run_list.next = NULL; p->run_list.prev = NULL; - if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) + if (!clone_flags & CLONE_PARENT) { + /* didn't want CLONE_PARENT */ p->p_pptr = p->p_opptr = current; - p->p_cptr = NULL; - init_waitqueue_head(&p->wait_chldexit); + } else if (current->flags & PF_PARENTOK) { + /* did want it, and it is OK */ + p->p_pptr = p->p_opptr = current->p_opptr; + } else { + /* did want it, and it is not OK */ + retval = -EPERM; + goto bad_fork_cleanup; + } p->vfork_sem = NULL; p->sigpending = 0; @@ -680,8 +707,10 @@ goto bad_fork_cleanup_files; if (copy_sighand(clone_flags, p)) goto bad_fork_cleanup_fs; - if (copy_mm(clone_flags, p)) + if (copy_children(clone_flags, p)) goto bad_fork_cleanup_sighand; + if (copy_mm(clone_flags, p)) + goto bad_fork_cleanup_children; retval = copy_thread(0, clone_flags, usp, p, regs); if (retval) goto bad_fork_cleanup_sighand; @@ -714,7 +743,12 @@ write_unlock_irq(&tasklist_lock); nr_threads++; - wake_up_process(p); /* do this last */ + + if (!(clone_flags & CLONE_SUSPENDED)) + wake_up_process(p); /* do this last */ + else + p->state = TASK_STOPPED; + ++total_forks; bad_fork: @@ -724,6 +758,8 @@ down(&sem); return retval; +bad_fork_cleanup_children: + exit_children(p); bad_fork_cleanup_sighand: exit_sighand(p); bad_fork_cleanup_fs: diff -ruN clean-linux-2.3.13/kernel/ksyms.c clone-linux-2.3.13/kernel/ksyms.c --- clean-linux-2.3.13/kernel/ksyms.c Mon Aug 9 11:43:49 1999 +++ clone-linux-2.3.13/kernel/ksyms.c Mon Aug 23 03:39:30 1999 @@ -88,6 +88,7 @@ EXPORT_SYMBOL(exit_files); EXPORT_SYMBOL(exit_fs); EXPORT_SYMBOL(exit_sighand); +EXPORT_SYMBOL(exit_children); /* internal kernel memory management */ EXPORT_SYMBOL(__get_free_pages); diff -ruN clean-linux-2.3.13/kernel/sched.c clone-linux-2.3.13/kernel/sched.c --- clean-linux-2.3.13/kernel/sched.c Tue Aug 3 08:01:29 1999 +++ clone-linux-2.3.13/kernel/sched.c Mon Aug 23 03:39:30 1999 @@ -1945,8 +1945,8 @@ free = (unsigned long) n - (unsigned long)(p+1); } printk("%5lu %5d %6d ", free, p->pid, p->p_pptr->pid); - if (p->p_cptr) - printk("%5d ", p->p_cptr->pid); + if (p->children->youngest) + printk("%5d ", p->children->youngest->pid); else printk(" "); if (!p->mm) diff -ruN clean-linux-2.3.13/kernel/signal.c clone-linux-2.3.13/kernel/signal.c --- clean-linux-2.3.13/kernel/signal.c Wed Jul 28 10:30:45 1999 +++ clone-linux-2.3.13/kernel/signal.c Mon Aug 23 03:39:30 1999 @@ -611,7 +611,7 @@ info.si_code = why; send_sig_info(sig, &info, tsk->p_pptr); - wake_up_interruptible(&tsk->p_pptr->wait_chldexit); + wake_up_interruptible(&tsk->p_pptr->children->wait_chldexit); } EXPORT_SYMBOL(dequeue_signal); - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |