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

Reply via email to