Module Name: src Committed By: dholland Date: Sat Sep 24 21:13:44 UTC 2016
Modified Files: src/sys/arch/x86/x86: pmap.c sys_machdep.c Log Message: LDT handling fixes: - add missing membar_store_store ("membar_producer") when setting a new ldt; - use UVM_KMF_WAITVA when allocating space for a new ldt instead of crashing if uvm_km_alloc fails; - if uvm_km_alloc fails in pmap_fork, bail instead of crashing; - clarify what else is going on in pmap_fork; - don't uvm_km_free while holding a mutex. To generate a diff of this commit: cvs rdiff -u -r1.221 -r1.222 src/sys/arch/x86/x86/pmap.c cvs rdiff -u -r1.29 -r1.30 src/sys/arch/x86/x86/sys_machdep.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/x86/x86/pmap.c diff -u src/sys/arch/x86/x86/pmap.c:1.221 src/sys/arch/x86/x86/pmap.c:1.222 --- src/sys/arch/x86/x86/pmap.c:1.221 Sat Aug 27 16:07:26 2016 +++ src/sys/arch/x86/x86/pmap.c Sat Sep 24 21:13:44 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.c,v 1.221 2016/08/27 16:07:26 maxv Exp $ */ +/* $NetBSD: pmap.c,v 1.222 2016/09/24 21:13:44 dholland Exp $ */ /*- * Copyright (c) 2008, 2010, 2016 The NetBSD Foundation, Inc. @@ -171,7 +171,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.221 2016/08/27 16:07:26 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.222 2016/09/24 21:13:44 dholland Exp $"); #include "opt_user_ldt.h" #include "opt_lockdebug.h" @@ -2443,30 +2443,49 @@ pmap_fork(struct pmap *pmap1, struct pma return; } + /* + * Copy the LDT into the new process. + * + * Read pmap1's ldt pointer and length unlocked; if it changes + * behind our back we'll retry. This will starve if there's a + * stream of LDT changes in another thread but that should not + * happen. + */ + retry: if (pmap1->pm_ldt != NULL) { len = pmap1->pm_ldt_len; + /* Allocate space for the new process's LDT */ new_ldt = (union descriptor *)uvm_km_alloc(kernel_map, len, 0, UVM_KMF_WIRED); + if (new_ldt == NULL) { + printf("WARNING: pmap_fork: " + "unable to allocate LDT space\n"); + return; + } mutex_enter(&cpu_lock); + /* Get a GDT slot for it */ sel = ldt_alloc(new_ldt, len); if (sel == -1) { mutex_exit(&cpu_lock); uvm_km_free(kernel_map, (vaddr_t)new_ldt, len, UVM_KMF_WIRED); - printf("WARNING: pmap_fork: unable to allocate LDT\n"); + printf("WARNING: pmap_fork: " + "unable to allocate LDT selector\n"); return; } } else { + /* Wasn't anything there after all. */ len = -1; new_ldt = NULL; sel = -1; mutex_enter(&cpu_lock); } - /* Copy the LDT, if necessary. */ + /* If there's still something there now that we have cpu_lock... */ if (pmap1->pm_ldt != NULL) { if (len != pmap1->pm_ldt_len) { + /* Oops, it changed. Drop what we did and try again */ if (len != -1) { ldt_free(sel); uvm_km_free(kernel_map, (vaddr_t)new_ldt, @@ -2476,6 +2495,7 @@ pmap_fork(struct pmap *pmap1, struct pma goto retry; } + /* Copy the LDT data and install it in pmap2 */ memcpy(new_ldt, pmap1->pm_ldt, len); pmap2->pm_ldt = new_ldt; pmap2->pm_ldt_len = pmap1->pm_ldt_len; @@ -2484,11 +2504,14 @@ pmap_fork(struct pmap *pmap1, struct pma } if (len != -1) { + /* There wasn't still something there, so mop up */ ldt_free(sel); + mutex_exit(&cpu_lock); uvm_km_free(kernel_map, (vaddr_t)new_ldt, len, UVM_KMF_WIRED); + } else { + mutex_exit(&cpu_lock); } - mutex_exit(&cpu_lock); #endif /* USER_LDT */ } #endif /* PMAP_FORK */ Index: src/sys/arch/x86/x86/sys_machdep.c diff -u src/sys/arch/x86/x86/sys_machdep.c:1.29 src/sys/arch/x86/x86/sys_machdep.c:1.30 --- src/sys/arch/x86/x86/sys_machdep.c:1.29 Fri Oct 23 18:53:26 2015 +++ src/sys/arch/x86/x86/sys_machdep.c Sat Sep 24 21:13:44 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: sys_machdep.c,v 1.29 2015/10/23 18:53:26 christos Exp $ */ +/* $NetBSD: sys_machdep.c,v 1.30 2016/09/24 21:13:44 dholland Exp $ */ /*- * Copyright (c) 1998, 2007, 2009 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sys_machdep.c,v 1.29 2015/10/23 18:53:26 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_machdep.c,v 1.30 2016/09/24 21:13:44 dholland Exp $"); #include "opt_mtrr.h" #include "opt_perfctrs.h" @@ -327,7 +327,7 @@ x86_set_ldt1(struct lwp *l, struct x86_s new_len = max(new_len, NLDT * sizeof(union descriptor)); new_len = round_page(new_len); new_ldt = (union descriptor *)uvm_km_alloc(kernel_map, - new_len, 0, UVM_KMF_WIRED | UVM_KMF_ZERO); + new_len, 0, UVM_KMF_WIRED | UVM_KMF_ZERO | UVM_KMF_WAITVA); mutex_enter(&cpu_lock); if (pmap->pm_ldt_len <= new_len) { break; @@ -365,9 +365,11 @@ x86_set_ldt1(struct lwp *l, struct x86_s } /* All changes are now globally visible. Swap in the new LDT. */ - pmap->pm_ldt = new_ldt; pmap->pm_ldt_len = new_len; pmap->pm_ldt_sel = new_sel; + /* membar_store_store for pmap_fork() to read these unlocked safely */ + membar_producer(); + pmap->pm_ldt = new_ldt; /* Switch existing users onto new LDT. */ pmap_ldt_sync(pmap); @@ -375,10 +377,13 @@ x86_set_ldt1(struct lwp *l, struct x86_s /* Free existing LDT (if any). */ if (old_ldt != NULL) { ldt_free(old_sel); + /* exit the mutex before free */ + mutex_exit(&cpu_lock); uvm_km_free(kernel_map, (vaddr_t)old_ldt, old_len, UVM_KMF_WIRED); + } else { + mutex_exit(&cpu_lock); } - mutex_exit(&cpu_lock); return error; #endif