x86: Reduce code duplication in cpu_fork() and cpu_copy_thread().
Add copy_thread() to hold shared code. Reviewed by: kib MFC after: 1 week Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D29228
This commit is contained in:
parent
c39dda8192
commit
a7883464fc
@ -136,6 +136,65 @@ alloc_fpusave(int flags)
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Common code shared between cpu_fork() and cpu_copy_thread() for
|
||||
* initializing a thread.
|
||||
*/
|
||||
static void
|
||||
copy_thread(struct thread *td1, struct thread *td2)
|
||||
{
|
||||
struct pcb *pcb2;
|
||||
|
||||
pcb2 = td2->td_pcb;
|
||||
|
||||
/* Ensure that td1's pcb is up to date for user threads. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) == 0) {
|
||||
MPASS(td1 == curthread);
|
||||
fpuexit(td1);
|
||||
update_pcb_bases(td1->td_pcb);
|
||||
}
|
||||
|
||||
/* Copy td1's pcb */
|
||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
|
||||
|
||||
/* Properly initialize pcb_save */
|
||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2);
|
||||
|
||||
/* Kernel threads start with clean FPU and segment bases. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) {
|
||||
pcb2->pcb_fsbase = 0;
|
||||
pcb2->pcb_gsbase = 0;
|
||||
clear_pcb_flags(pcb2, PCB_FPUINITDONE | PCB_USERFPUINITDONE |
|
||||
PCB_KERNFPU | PCB_KERNFPU_THR);
|
||||
} else {
|
||||
MPASS((pcb2->pcb_flags & (PCB_KERNFPU | PCB_KERNFPU_THR)) == 0);
|
||||
bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2),
|
||||
cpu_max_ext_state_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set registers for trampoline to user mode. Leave space for the
|
||||
* return address on stack. These are the kernel mode register values.
|
||||
*/
|
||||
pcb2->pcb_r12 = (register_t)fork_return; /* fork_trampoline argument */
|
||||
pcb2->pcb_rbp = 0;
|
||||
pcb2->pcb_rsp = (register_t)td2->td_frame - sizeof(void *);
|
||||
pcb2->pcb_rbx = (register_t)td2; /* fork_trampoline argument */
|
||||
pcb2->pcb_rip = (register_t)fork_trampoline;
|
||||
/*-
|
||||
* pcb2->pcb_dr*: cloned above.
|
||||
* pcb2->pcb_savefpu: cloned above.
|
||||
* pcb2->pcb_flags: cloned above.
|
||||
* pcb2->pcb_onfault: cloned above (always NULL here?).
|
||||
* pcb2->pcb_[fg]sbase: cloned above
|
||||
*/
|
||||
|
||||
/* Setup to release spin count in fork_exit(). */
|
||||
td2->td_md.md_spinlock_count = 1;
|
||||
td2->td_md.md_saved_flags = PSL_KERNEL | PSL_I;
|
||||
pmap_thread_init_invl_gen(td2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish a fork operation, with process p2 nearly set up.
|
||||
* Copy and update the pcb, set up the stack so that the child
|
||||
@ -164,36 +223,13 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure that td1's pcb is up to date for user processes. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) == 0) {
|
||||
MPASS(td1 == curthread);
|
||||
fpuexit(td1);
|
||||
update_pcb_bases(td1->td_pcb);
|
||||
}
|
||||
|
||||
/* Point the stack and pcb to the actual location */
|
||||
set_top_of_stack_td(td2);
|
||||
td2->td_pcb = pcb2 = get_pcb_td(td2);
|
||||
|
||||
/* Copy td1's pcb */
|
||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
|
||||
copy_thread(td1, td2);
|
||||
|
||||
/* Properly initialize pcb_save */
|
||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2);
|
||||
|
||||
/* Kernel processes start with clean FPU and segment bases. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) {
|
||||
pcb2->pcb_fsbase = 0;
|
||||
pcb2->pcb_gsbase = 0;
|
||||
clear_pcb_flags(pcb2, PCB_FPUINITDONE | PCB_USERFPUINITDONE |
|
||||
PCB_KERNFPU | PCB_KERNFPU_THR);
|
||||
} else {
|
||||
MPASS((pcb2->pcb_flags & (PCB_KERNFPU | PCB_KERNFPU_THR)) == 0);
|
||||
bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2),
|
||||
cpu_max_ext_state_size);
|
||||
}
|
||||
|
||||
/* Point mdproc and then copy over td1's contents */
|
||||
/* Point mdproc and then copy over p1's contents */
|
||||
mdp2 = &p2->p_md;
|
||||
bcopy(&p1->p_md, mdp2, sizeof(*mdp2));
|
||||
|
||||
@ -215,29 +251,7 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
|
||||
*/
|
||||
td2->td_frame->tf_rflags &= ~PSL_T;
|
||||
|
||||
/*
|
||||
* Set registers for trampoline to user mode. Leave space for the
|
||||
* return address on stack. These are the kernel mode register values.
|
||||
*/
|
||||
pcb2->pcb_r12 = (register_t)fork_return; /* fork_trampoline argument */
|
||||
pcb2->pcb_rbp = 0;
|
||||
pcb2->pcb_rsp = (register_t)td2->td_frame - sizeof(void *);
|
||||
pcb2->pcb_rbx = (register_t)td2; /* fork_trampoline argument */
|
||||
pcb2->pcb_rip = (register_t)fork_trampoline;
|
||||
/*-
|
||||
* pcb2->pcb_dr*: cloned above.
|
||||
* pcb2->pcb_savefpu: cloned above.
|
||||
* pcb2->pcb_flags: cloned above.
|
||||
* pcb2->pcb_onfault: cloned above (always NULL here?).
|
||||
* pcb2->pcb_[fg]sbase: cloned above
|
||||
*/
|
||||
|
||||
/* Setup to release spin count in fork_exit(). */
|
||||
td2->td_md.md_spinlock_count = 1;
|
||||
td2->td_md.md_saved_flags = PSL_KERNEL | PSL_I;
|
||||
pmap_thread_init_invl_gen(td2);
|
||||
|
||||
/* As an i386, do not copy io permission bitmap. */
|
||||
/* As on i386, do not copy io permission bitmap. */
|
||||
pcb2->pcb_tssp = NULL;
|
||||
|
||||
/* New segment registers. */
|
||||
@ -275,7 +289,7 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
|
||||
* pcb_rsp is loaded pointing to the cpu_switch() stack frame
|
||||
* containing the return address when exiting cpu_switch.
|
||||
* This will normally be to fork_trampoline(), which will have
|
||||
* %ebx loaded with the new proc's pointer. fork_trampoline()
|
||||
* %rbx loaded with the new proc's pointer. fork_trampoline()
|
||||
* will set up a stack to call fork_return(p, frame); to complete
|
||||
* the return to user-mode.
|
||||
*/
|
||||
@ -571,38 +585,7 @@ cpu_set_syscall_retval(struct thread *td, int error)
|
||||
void
|
||||
cpu_copy_thread(struct thread *td, struct thread *td0)
|
||||
{
|
||||
struct pcb *pcb2;
|
||||
|
||||
pcb2 = td->td_pcb;
|
||||
|
||||
/* Ensure that td0's pcb is up to date for user threads. */
|
||||
if ((td->td_pflags & TDP_KTHREAD) == 0) {
|
||||
MPASS(td0 == curthread);
|
||||
fpuexit(td0);
|
||||
update_pcb_bases(td0->td_pcb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the upcall pcb. This loads kernel regs.
|
||||
* Those not loaded individually below get their default
|
||||
* values here.
|
||||
*/
|
||||
bcopy(td0->td_pcb, pcb2, sizeof(*pcb2));
|
||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2);
|
||||
|
||||
/* Kernel threads start with clean FPU and segment bases. */
|
||||
if ((td->td_pflags & TDP_KTHREAD) != 0) {
|
||||
pcb2->pcb_fsbase = 0;
|
||||
pcb2->pcb_gsbase = 0;
|
||||
clear_pcb_flags(pcb2, PCB_FPUINITDONE | PCB_USERFPUINITDONE |
|
||||
PCB_KERNFPU | PCB_KERNFPU_THR);
|
||||
} else {
|
||||
MPASS((pcb2->pcb_flags & (PCB_KERNFPU | PCB_KERNFPU_THR)) == 0);
|
||||
bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save,
|
||||
cpu_max_ext_state_size);
|
||||
}
|
||||
set_pcb_flags_raw(pcb2, PCB_FULL_IRET);
|
||||
|
||||
copy_thread(td0, td);
|
||||
|
||||
/*
|
||||
* Copy user general-purpose registers.
|
||||
@ -620,27 +603,7 @@ cpu_copy_thread(struct thread *td, struct thread *td0)
|
||||
*/
|
||||
td->td_frame->tf_rflags &= ~PSL_T;
|
||||
|
||||
/*
|
||||
* Set registers for trampoline to user mode. Leave space for the
|
||||
* return address on stack. These are the kernel mode register values.
|
||||
*/
|
||||
pcb2->pcb_r12 = (register_t)fork_return; /* trampoline arg */
|
||||
pcb2->pcb_rbp = 0;
|
||||
pcb2->pcb_rsp = (register_t)td->td_frame - sizeof(void *); /* trampoline arg */
|
||||
pcb2->pcb_rbx = (register_t)td; /* trampoline arg */
|
||||
pcb2->pcb_rip = (register_t)fork_trampoline;
|
||||
/*
|
||||
* If we didn't copy the pcb, we'd need to do the following registers:
|
||||
* pcb2->pcb_dr*: cloned above.
|
||||
* pcb2->pcb_savefpu: cloned above.
|
||||
* pcb2->pcb_onfault: cloned above (always NULL here?).
|
||||
* pcb2->pcb_[fg]sbase: cloned above
|
||||
*/
|
||||
|
||||
/* Setup to release spin count in fork_exit(). */
|
||||
td->td_md.md_spinlock_count = 1;
|
||||
td->td_md.md_saved_flags = PSL_KERNEL | PSL_I;
|
||||
pmap_thread_init_invl_gen(td);
|
||||
set_pcb_flags_raw(td->td_pcb, PCB_FULL_IRET);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -133,6 +133,74 @@ alloc_fpusave(int flags)
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Common code shared between cpu_fork() and cpu_copy_thread() for
|
||||
* initializing a thread.
|
||||
*/
|
||||
static void
|
||||
copy_thread(struct thread *td1, struct thread *td2)
|
||||
{
|
||||
struct pcb *pcb2;
|
||||
|
||||
pcb2 = td2->td_pcb;
|
||||
|
||||
/* Ensure that td1's pcb is up to date for user threads. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) == 0) {
|
||||
MPASS(td1 == curthread);
|
||||
td1->td_pcb->pcb_gs = rgs();
|
||||
critical_enter();
|
||||
if (PCPU_GET(fpcurthread) == td1)
|
||||
npxsave(td1->td_pcb->pcb_save);
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
/* Copy td1's pcb */
|
||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
|
||||
|
||||
/* Properly initialize pcb_save */
|
||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2);
|
||||
|
||||
/* Kernel threads start with clean NPX and segment bases. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) {
|
||||
pcb2->pcb_gs = _udatasel;
|
||||
set_fsbase(td2, 0);
|
||||
set_gsbase(td2, 0);
|
||||
pcb2->pcb_flags &= ~(PCB_NPXINITDONE | PCB_NPXUSERINITDONE |
|
||||
PCB_KERNNPX | PCB_KERNNPX_THR);
|
||||
} else {
|
||||
MPASS((pcb2->pcb_flags & (PCB_KERNNPX | PCB_KERNNPX_THR)) == 0);
|
||||
bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2),
|
||||
cpu_max_ext_state_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set registers for trampoline to user mode. Leave space for the
|
||||
* return address on stack. These are the kernel mode register values.
|
||||
*/
|
||||
pcb2->pcb_edi = 0;
|
||||
pcb2->pcb_esi = (int)fork_return; /* trampoline arg */
|
||||
pcb2->pcb_ebp = 0;
|
||||
pcb2->pcb_esp = (int)td2->td_frame - sizeof(void *); /* trampoline arg */
|
||||
pcb2->pcb_ebx = (int)td2; /* trampoline arg */
|
||||
pcb2->pcb_eip = (int)fork_trampoline + setidt_disp;
|
||||
/*
|
||||
* If we didn't copy the pcb, we'd need to do the following registers:
|
||||
* pcb2->pcb_cr3: cloned above.
|
||||
* pcb2->pcb_dr*: cloned above.
|
||||
* pcb2->pcb_savefpu: cloned above.
|
||||
* pcb2->pcb_flags: cloned above.
|
||||
* pcb2->pcb_onfault: cloned above (always NULL here?).
|
||||
* pcb2->pcb_gs: cloned above.
|
||||
* pcb2->pcb_ext: cleared below.
|
||||
*/
|
||||
pcb2->pcb_ext = NULL;
|
||||
|
||||
/* Setup to release spin count in fork_exit(). */
|
||||
td2->td_md.md_spinlock_count = 1;
|
||||
td2->td_md.md_saved_flags = PSL_KERNEL | PSL_I;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish a fork operation, with process p2 nearly set up.
|
||||
* Copy and update the pcb, set up the stack so that the child
|
||||
@ -167,38 +235,11 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure that td1's pcb is up to date for user processes. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) == 0) {
|
||||
MPASS(td1 == curthread);
|
||||
td1->td_pcb->pcb_gs = rgs();
|
||||
critical_enter();
|
||||
if (PCPU_GET(fpcurthread) == td1)
|
||||
npxsave(td1->td_pcb->pcb_save);
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
/* Point the pcb to the top of the stack */
|
||||
pcb2 = get_pcb_td(td2);
|
||||
td2->td_pcb = pcb2;
|
||||
|
||||
/* Copy td1's pcb */
|
||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
|
||||
|
||||
/* Properly initialize pcb_save */
|
||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2);
|
||||
|
||||
/* Kernel processes start with clean NPX and segment bases. */
|
||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) {
|
||||
pcb2->pcb_gs = _udatasel;
|
||||
set_fsbase(td2, 0);
|
||||
set_gsbase(td2, 0);
|
||||
pcb2->pcb_flags &= ~(PCB_NPXINITDONE | PCB_NPXUSERINITDONE |
|
||||
PCB_KERNNPX | PCB_KERNNPX_THR);
|
||||
} else {
|
||||
MPASS((pcb2->pcb_flags & (PCB_KERNNPX | PCB_KERNNPX_THR)) == 0);
|
||||
bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2),
|
||||
cpu_max_ext_state_size);
|
||||
}
|
||||
copy_thread(td1, td2);
|
||||
|
||||
/* Point mdproc and then copy over td1's contents */
|
||||
mdp2 = &p2->p_md;
|
||||
@ -225,30 +266,13 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
|
||||
*/
|
||||
td2->td_frame->tf_eflags &= ~PSL_T;
|
||||
|
||||
/*
|
||||
* Set registers for trampoline to user mode. Leave space for the
|
||||
* return address on stack. These are the kernel mode register values.
|
||||
*/
|
||||
/* Set cr3 for the new process. */
|
||||
pcb2->pcb_cr3 = pmap_get_cr3(vmspace_pmap(p2->p_vmspace));
|
||||
pcb2->pcb_edi = 0;
|
||||
pcb2->pcb_esi = (int)fork_return; /* fork_trampoline argument */
|
||||
pcb2->pcb_ebp = 0;
|
||||
pcb2->pcb_esp = (int)td2->td_frame - sizeof(void *);
|
||||
pcb2->pcb_ebx = (int)td2; /* fork_trampoline argument */
|
||||
pcb2->pcb_eip = (int)fork_trampoline + setidt_disp;
|
||||
/*-
|
||||
* pcb2->pcb_dr*: cloned above.
|
||||
* pcb2->pcb_savefpu: cloned above.
|
||||
* pcb2->pcb_flags: cloned above.
|
||||
* pcb2->pcb_onfault: cloned above (always NULL here?).
|
||||
* pcb2->pcb_gs: cloned above.
|
||||
* pcb2->pcb_ext: cleared below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* XXX don't copy the i/o pages. this should probably be fixed.
|
||||
*/
|
||||
pcb2->pcb_ext = 0;
|
||||
pcb2->pcb_ext = NULL;
|
||||
|
||||
/* Copy the LDT, if necessary. */
|
||||
mtx_lock_spin(&dt_lock);
|
||||
@ -264,10 +288,6 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
|
||||
}
|
||||
mtx_unlock_spin(&dt_lock);
|
||||
|
||||
/* Setup to release spin count in fork_exit(). */
|
||||
td2->td_md.md_spinlock_count = 1;
|
||||
td2->td_md.md_saved_flags = PSL_KERNEL | PSL_I;
|
||||
|
||||
/*
|
||||
* Now, cpu_switch() can schedule the new process.
|
||||
* pcb_esp is loaded pointing to the cpu_switch() stack frame
|
||||
@ -435,41 +455,7 @@ cpu_set_syscall_retval(struct thread *td, int error)
|
||||
void
|
||||
cpu_copy_thread(struct thread *td, struct thread *td0)
|
||||
{
|
||||
struct pcb *pcb2;
|
||||
|
||||
/* Point the pcb to the top of the stack. */
|
||||
pcb2 = td->td_pcb;
|
||||
|
||||
/* Ensure that td0's pcb is up to date for user threads. */
|
||||
if ((td->td_pflags & TDP_KTHREAD) == 0) {
|
||||
MPASS(td0 == curthread);
|
||||
td0->td_pcb->pcb_gs = rgs();
|
||||
critical_enter();
|
||||
if (PCPU_GET(fpcurthread) == td0)
|
||||
npxsave(td0->td_pcb->pcb_save);
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the upcall pcb. This loads kernel regs.
|
||||
* Those not loaded individually below get their default
|
||||
* values here.
|
||||
*/
|
||||
bcopy(td0->td_pcb, pcb2, sizeof(*pcb2));
|
||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2);
|
||||
|
||||
/* Kernel threads start with clean NPX and segment bases. */
|
||||
if ((td->td_pflags & TDP_KTHREAD) != 0) {
|
||||
pcb2->pcb_gs = _udatasel;
|
||||
set_fsbase(td, 0);
|
||||
set_gsbase(td, 0);
|
||||
pcb2->pcb_flags &= ~(PCB_NPXINITDONE | PCB_NPXUSERINITDONE |
|
||||
PCB_KERNNPX | PCB_KERNNPX_THR);
|
||||
} else {
|
||||
MPASS((pcb2->pcb_flags & (PCB_KERNNPX | PCB_KERNNPX_THR)) == 0);
|
||||
bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save,
|
||||
cpu_max_ext_state_size);
|
||||
}
|
||||
copy_thread(td0, td);
|
||||
|
||||
/*
|
||||
* Copy user general-purpose registers.
|
||||
@ -486,32 +472,6 @@ cpu_copy_thread(struct thread *td, struct thread *td0)
|
||||
* instruction after returning to userland.
|
||||
*/
|
||||
td->td_frame->tf_eflags &= ~PSL_T;
|
||||
|
||||
/*
|
||||
* Set registers for trampoline to user mode. Leave space for the
|
||||
* return address on stack. These are the kernel mode register values.
|
||||
*/
|
||||
pcb2->pcb_edi = 0;
|
||||
pcb2->pcb_esi = (int)fork_return; /* trampoline arg */
|
||||
pcb2->pcb_ebp = 0;
|
||||
pcb2->pcb_esp = (int)td->td_frame - sizeof(void *); /* trampoline arg */
|
||||
pcb2->pcb_ebx = (int)td; /* trampoline arg */
|
||||
pcb2->pcb_eip = (int)fork_trampoline + setidt_disp;
|
||||
/*
|
||||
* If we didn't copy the pcb, we'd need to do the following registers:
|
||||
* pcb2->pcb_cr3: cloned above.
|
||||
* pcb2->pcb_dr*: cloned above.
|
||||
* pcb2->pcb_savefpu: cloned above.
|
||||
* pcb2->pcb_flags: cloned above.
|
||||
* pcb2->pcb_onfault: cloned above (always NULL here?).
|
||||
* pcb2->pcb_gs: cloned above.
|
||||
* pcb2->pcb_ext: cleared below.
|
||||
*/
|
||||
pcb2->pcb_ext = NULL;
|
||||
|
||||
/* Setup to release spin count in fork_exit(). */
|
||||
td->td_md.md_spinlock_count = 1;
|
||||
td->td_md.md_saved_flags = PSL_KERNEL | PSL_I;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user