*
* @(#)kern_fork.c 8.6 (Berkeley) 4/8/94
* $FreeBSD: src/sys/kern/kern_fork.c,v 1.72.2.14 2003/06/26 04:15:10 silby Exp $
- * $DragonFly: src/sys/kern/kern_fork.c,v 1.64 2007/02/25 23:17:12 corecode Exp $
+ * $DragonFly: src/sys/kern/kern_fork.c,v 1.65 2007/02/26 21:41:08 corecode Exp $
*/
#include "opt_ktrace.h"
TAILQ_HEAD(forklist_head, forklist);
static struct forklist_head fork_list = TAILQ_HEAD_INITIALIZER(fork_list);
+static struct lwp *lwp_fork(struct lwp *, struct proc *, int flags);
+
int forksleep; /* Place for fork1() to sleep on. */
/* ARGSUSED */
struct proc *p1 = lp1->lwp_proc;
struct proc *p2, *pptr;
struct pgrp *pgrp;
- struct lwp *lp2;
uid_t uid;
int ok, error;
static int curfail = 0;
*/
if ((flags & RFPROC) == 0) {
- vm_fork(lp1, 0, flags);
+ /*
+ * This kind of stunt does not work anymore if
+ * there are native threads (lwps) running
+ */
+ if (p1->p_nthreads != 1)
+ return (EINVAL);
+
+ vm_fork(p1, 0, flags);
/*
* Close all file descriptors.
/* Allocate new proc. */
p2 = zalloc(proc_zone);
- lp2 = zalloc(lwp_zone);
/*
* Setup linkage for kernel based threading XXX lwp
p2->p_emuldata = NULL;
LIST_INIT(&p2->p_lwps);
- /* XXX lwp */
- lp2->lwp_proc = p2;
- lp2->lwp_tid = 0;
- LIST_INSERT_HEAD(&p2->p_lwps, lp2, lwp_list);
- p2->p_nthreads = 1;
+ p2->p_nthreads = 0;
p2->p_nstopped = 0;
p2->p_lasttid = 0;
* process once it starts getting hooked into the rest of the system.
*/
p2->p_stat = SIDL;
- lp2->lwp_stat = LSRUN; /* XXX use other state? start_forked_proc() handles this*/
proc_add_allproc(p2);
/*
*/
bzero(&p2->p_startzero,
(unsigned) ((caddr_t)&p2->p_endzero - (caddr_t)&p2->p_startzero));
- bzero(&lp2->lwp_startzero,
- (unsigned) ((caddr_t)&lp2->lwp_endzero -
- (caddr_t)&lp2->lwp_startzero));
bcopy(&p1->p_startcopy, &p2->p_startcopy,
(unsigned) ((caddr_t)&p2->p_endcopy - (caddr_t)&p2->p_startcopy));
- bcopy(&lp1->lwp_startcopy, &lp2->lwp_startcopy,
- (unsigned) ((caddr_t)&lp2->lwp_endcopy -
- (caddr_t)&lp2->lwp_startcopy));
p2->p_aioinfo = NULL;
*/
p2->p_flag = 0;
p2->p_lock = 0;
- lp2->lwp_lock = 0;
+
if (p1->p_flag & P_PROFIL)
startprofclock(p2);
p2->p_ucred = crhold(p1->p_ucred);
if (p2->p_args)
p2->p_args->ar_ref++;
+ p2->p_usched = p1->p_usched;
+
if (flags & RFSIGSHARE) {
p2->p_sigacts = p1->p_sigacts;
p2->p_sigacts->ps_refcnt++;
* been preserved.
*/
p2->p_flag |= p1->p_flag & P_SUGID;
- lp2->lwp_flag |= lp1->lwp_flag & LWP_ALTSTACK;
if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT)
p2->p_flag |= P_CONTROLT;
if (flags & RFPPWAIT)
#endif
/*
- * Inherit the scheduler and initialize scheduler-related fields.
- * Set cpbase to the last timeout that occured (not the upcoming
- * timeout).
- *
- * A critical section is required since a timer IPI can update
- * scheduler specific data.
- */
- crit_enter();
- p2->p_usched = p1->p_usched;
- lp2->lwp_cpbase = mycpu->gd_schedclock.time -
- mycpu->gd_schedclock.periodic;
- p2->p_usched->heuristic_forking(lp1, lp2);
- crit_exit();
-
- /*
* This begins the section where we must prevent the parent
* from being swapped.
+ *
+ * Gets PRELE'd in the caller in start_forked_proc().
*/
PHOLD(p1);
+ vm_fork(p1, p2, flags);
+
/*
- * Finish creating the child process. It will return via a different
- * execution path later. (ie: directly into user mode)
+ * Create the first lwp associated with the new proc.
+ * It will return via a different execution path later, directly
+ * into userland, after it was put on the runq by
+ * start_forked_proc().
*/
- vm_fork(lp1, p2, flags);
- caps_fork(lp1->lwp_thread, lp2->lwp_thread, flags);
+ lwp_fork(lp1, p2, flags);
if (flags == (RFFDG | RFPROC)) {
mycpu->gd_cnt.v_forks++;
return (error);
}
+static struct lwp *
+lwp_fork(struct lwp *origlp, struct proc *destproc, int flags)
+{
+ struct lwp *lp;
+ struct thread *td;
+ lwpid_t tid;
+
+ /*
+ * We need to prevent wrap-around collisions.
+ * Until we have a nice tid allocator, we need to
+ * start searching for free tids once we wrap around.
+ *
+ * XXX give me a nicer allocator
+ */
+ if (destproc->p_lasttid + 1 <= 0) {
+ tid = 0;
+restart:
+ FOREACH_LWP_IN_PROC(lp, destproc) {
+ if (lp->lwp_tid != tid)
+ continue;
+ /* tids match, search next. */
+ tid++;
+ /*
+ * Wait -- the whole tid space is depleted?
+ * Impossible.
+ */
+ if (tid <= 0)
+ panic("lwp_fork: All tids depleted?!");
+ goto restart;
+ }
+ /* When we come here, the tid is not occupied */
+ } else {
+ tid = destproc->p_lasttid++;
+ }
+
+ lp = zalloc(lwp_zone);
+ lp->lwp_proc = destproc;
+ lp->lwp_tid = tid;
+ LIST_INSERT_HEAD(&destproc->p_lwps, lp, lwp_list);
+ destproc->p_nthreads++;
+ lp->lwp_stat = LSRUN;
+ bzero(&lp->lwp_startzero,
+ (unsigned) ((caddr_t)&lp->lwp_endzero -
+ (caddr_t)&lp->lwp_startzero));
+ bcopy(&origlp->lwp_startcopy, &lp->lwp_startcopy,
+ (unsigned) ((caddr_t)&lp->lwp_endcopy -
+ (caddr_t)&lp->lwp_startcopy));
+ lp->lwp_lock = 0;
+ lp->lwp_flag |= origlp->lwp_flag & LWP_ALTSTACK;
+ /*
+ * Set cpbase to the last timeout that occured (not the upcoming
+ * timeout).
+ *
+ * A critical section is required since a timer IPI can update
+ * scheduler specific data.
+ */
+ crit_enter();
+ lp->lwp_cpbase = mycpu->gd_schedclock.time -
+ mycpu->gd_schedclock.periodic;
+ destproc->p_usched->heuristic_forking(origlp, lp);
+ crit_exit();
+
+ td = lwkt_alloc_thread(NULL, LWKT_THREAD_STACK, -1, 0);
+ lp->lwp_thread = td;
+ td->td_proc = destproc;
+ td->td_lwp = lp;
+ td->td_switch = cpu_heavy_switch;
+#ifdef SMP
+ KKASSERT(td->td_mpcount == 1);
+#endif
+ lwkt_setpri(td, TDPRI_KERN_USER);
+ lwkt_set_comm(td, "%s", destproc->p_comm);
+
+ /*
+ * cpu_fork will copy and update the pcb, set up the kernel stack,
+ * and make the child ready to run.
+ */
+ cpu_fork(origlp, lp, flags);
+ caps_fork(origlp->lwp_thread, lp->lwp_thread);
+
+ return (lp);
+}
+
/*
* The next two functionms are general routines to handle adding/deleting
* items on the fork callout list.
* rights to redistribute these changes.
*
* $FreeBSD: src/sys/vm/vm_glue.c,v 1.94.2.4 2003/01/13 22:51:17 dillon Exp $
- * $DragonFly: src/sys/vm/vm_glue.c,v 1.52 2007/02/25 23:17:13 corecode Exp $
+ * $DragonFly: src/sys/vm/vm_glue.c,v 1.53 2007/02/26 21:41:08 corecode Exp $
*/
#include "opt_vm.h"
* to user mode to avoid stack copying and relocation problems.
*/
void
-vm_fork(struct lwp *lp1, struct proc *p2, int flags)
+vm_fork(struct proc *p1, struct proc *p2, int flags)
{
- struct proc *p1 = lp1->lwp_proc;
- struct thread *td2;
-
if ((flags & RFPROC) == 0) {
/*
* Divorce the memory, if it is shared, essentially
vmspace_unshare(p1);
}
}
- cpu_fork(lp1, NULL, flags);
+ cpu_fork(ONLY_LWP_IN_PROC(p1), NULL, flags);
return;
}
shmfork(p1, p2);
}
- td2 = lwkt_alloc_thread(NULL, LWKT_THREAD_STACK, -1, 0);
- pmap_init_proc(p2, td2);
- lwkt_setpri(td2, TDPRI_KERN_USER);
- lwkt_set_comm(td2, "%s", p1->p_comm);
-
- /*
- * cpu_fork will copy and update the pcb, set up the kernel stack,
- * and make the child ready to run.
- */
- cpu_fork(lp1, td2->td_lwp, flags);
+ pmap_init_proc(p2);
}
/*