Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/x86/x86 LDT handling fixes:



details:   https://anonhg.NetBSD.org/src/rev/6c41d9aa1b3d
branches:  trunk
changeset: 347946:6c41d9aa1b3d
user:      dholland <dholland%NetBSD.org@localhost>
date:      Sat Sep 24 21:13:44 2016 +0000

description:
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.

diffstat:

 sys/arch/x86/x86/pmap.c        |  33 ++++++++++++++++++++++++++++-----
 sys/arch/x86/x86/sys_machdep.c |  15 ++++++++++-----
 2 files changed, 38 insertions(+), 10 deletions(-)

diffs (149 lines):

diff -r 273556412f4d -r 6c41d9aa1b3d sys/arch/x86/x86/pmap.c
--- a/sys/arch/x86/x86/pmap.c   Sat Sep 24 21:00:54 2016 +0000
+++ b/sys/arch/x86/x86/pmap.c   Sat Sep 24 21:13:44 2016 +0000
@@ -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 @@
                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 @@
                        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 @@
        }
 
        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 */
diff -r 273556412f4d -r 6c41d9aa1b3d sys/arch/x86/x86/sys_machdep.c
--- a/sys/arch/x86/x86/sys_machdep.c    Sat Sep 24 21:00:54 2016 +0000
+++ b/sys/arch/x86/x86/sys_machdep.c    Sat Sep 24 21:13:44 2016 +0000
@@ -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 @@
                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 @@
        }
 
        /* 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 @@
        /* 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



Home | Main Index | Thread Index | Old Index