Date: Mon, 29 Mar 2021 22:12:03 GMT From: John Baldwin <jhb@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: 60ae7f745da0 - stable/13 - x86: Always use clean FPU and segment base state for new kthreads. Message-ID: <202103292212.12TMC3oX064670@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=60ae7f745da0ec03f9a08b92f4db2fc9101b7123 commit 60ae7f745da0ec03f9a08b92f4db2fc9101b7123 Author: John Baldwin <jhb@FreeBSD.org> AuthorDate: 2021-03-12 17:48:36 +0000 Commit: John Baldwin <jhb@FreeBSD.org> CommitDate: 2021-03-29 18:10:59 +0000 x86: Always use clean FPU and segment base state for new kthreads. Sponsored by: Netflix (cherry picked from commit c7b021352332a2f79907d68f971849f74b73e1c6) --- sys/amd64/amd64/vm_machdep.c | 46 +++++++++++++++++++++++++--------- sys/i386/i386/sys_machdep.c | 6 +++-- sys/i386/i386/vm_machdep.c | 59 +++++++++++++++++++++++++++++++------------- 3 files changed, 81 insertions(+), 30 deletions(-) diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c index f10d0339a65a..6e60f2b3faff 100644 --- a/sys/amd64/amd64/vm_machdep.c +++ b/sys/amd64/amd64/vm_machdep.c @@ -164,10 +164,12 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) return; } - /* Ensure that td1's pcb is up to date. */ - fpuexit(td1); - if (td1 == curthread) + /* 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); @@ -178,8 +180,18 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) /* Properly initialize pcb_save */ pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); - bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2), - cpu_max_ext_state_size); + + /* 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 */ mdp2 = &p2->p_md; @@ -564,10 +576,12 @@ cpu_copy_thread(struct thread *td, struct thread *td0) pcb2 = td->td_pcb; - /* Ensure that td0's pcb is up to date. */ - fpuexit(td0); - if (td0 == curthread) + /* 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. @@ -575,12 +589,22 @@ cpu_copy_thread(struct thread *td, struct thread *td0) * values here. */ bcopy(td0->td_pcb, pcb2, sizeof(*pcb2)); - clear_pcb_flags(pcb2, PCB_KERNFPU); pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); - bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save, - cpu_max_ext_state_size); + + /* 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); + /* * Create a new fresh stack for the new thread. */ diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c index 3f650b65e160..a0a1c273f467 100644 --- a/sys/i386/i386/sys_machdep.c +++ b/sys/i386/i386/sys_machdep.c @@ -108,7 +108,8 @@ set_fsbase(struct thread *td, uint32_t base) fill_based_sd(&sd, base); critical_enter(); td->td_pcb->pcb_fsd = sd; - PCPU_GET(fsgs_gdt)[0] = sd; + if (td == curthread) + PCPU_GET(fsgs_gdt)[0] = sd; critical_exit(); } @@ -120,7 +121,8 @@ set_gsbase(struct thread *td, uint32_t base) fill_based_sd(&sd, base); critical_enter(); td->td_pcb->pcb_gsd = sd; - PCPU_GET(fsgs_gdt)[1] = sd; + if (td == curthread) + PCPU_GET(fsgs_gdt)[1] = sd; critical_exit(); } diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index 502de6e7f38f..471128e1713d 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -167,13 +167,15 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) return; } - /* Ensure that td1's pcb is up to date. */ - if (td1 == curthread) + /* 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(); + 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); @@ -184,8 +186,19 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) /* Properly initialize pcb_save */ pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); - bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2), - cpu_max_ext_state_size); + + /* 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); + } /* Point mdproc and then copy over td1's contents */ mdp2 = &p2->p_md; @@ -428,13 +441,15 @@ cpu_copy_thread(struct thread *td, struct thread *td0) /* Point the pcb to the top of the stack. */ pcb2 = td->td_pcb; - /* Ensure that td0's pcb is up to date. */ - if (td0 == curthread) + /* 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(); + critical_enter(); + if (PCPU_GET(fpcurthread) == td0) + npxsave(td0->td_pcb->pcb_save); + critical_exit(); + } /* * Copy the upcall pcb. This loads kernel regs. @@ -442,10 +457,20 @@ cpu_copy_thread(struct thread *td, struct thread *td0) * values here. */ bcopy(td0->td_pcb, pcb2, sizeof(*pcb2)); - pcb2->pcb_flags &= ~PCB_KERNNPX; pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); - bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save, - cpu_max_ext_state_size); + + /* 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); + } /* * Create a new fresh stack for the new thread.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202103292212.12TMC3oX064670>