vmap: Use {map,unmap}_segment() helpers
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 28 Nov 2016 03:24:47 +0000 (22:24 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 29 Nov 2016 16:27:40 +0000 (11:27 -0500)
map_vmap_segment() is basically a wrapper with a lock around the x86
helper map_segment().  This commit exposes that helper and uses it
directly, instead of having another version of a PTE walker.  The x86
helpers are a little more capable than the one-off functions I had in
mm.c.

map_segment() and unmap_segment() are a little more efficient for larger
sizes, since they just walk once and then operate on every PTE in a PT
instead of doing a fresh pgdir walk for each page.  map() will try to
use jumbo pages; pgdir_walk() assumes you want a PML1.  unmap() also
frees intermediate PTs (not for kernel mappings, of course), which will
help if we ever reuse this vmap code for userspace mappings.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/riscv/pmap.c
kern/arch/x86/pmap64.c
kern/include/pmap.h
kern/src/mm.c

index 48bd962..f44f92b 100644 (file)
@@ -140,3 +140,14 @@ void arch_add_intermediate_pts(pgdir_t pgdir, uintptr_t va, size_t len)
 {
        #error "Implement me"
 }
+
+void map_segment(pgdir_t pgdir, uintptr_t va, size_t size, physaddr_t pa,
+                 int perm, int pml_shift)
+{
+       #error "Implement me"
+}
+
+int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size)
+{
+       #error "Implement me"
+}
index 6531e7c..e8d36b5 100644 (file)
@@ -42,10 +42,6 @@ pseudodesc_t gdt_pd;
 #define PG_WALK_CREATE                         0x0100
 
 kpte_t *pml_walk(kpte_t *pml, uintptr_t va, int flags);
-void map_segment(pgdir_t pgdir, uintptr_t va, size_t size, physaddr_t pa,
-                 int perm, int pml_shift);
-int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size);
-
 typedef int (*kpte_cb_t)(kpte_t *kpte, uintptr_t kva, int pml_shift,
                         bool visited_subs, void *arg);
 int pml_for_each(kpte_t *pml, uintptr_t start, size_t len, kpte_cb_t callback,
index 6504d02..92500e1 100644 (file)
@@ -96,6 +96,9 @@ bool regions_collide_unsafe(uintptr_t start1, uintptr_t end1,
                             uintptr_t start2, uintptr_t end2);
 
 /* Arch specific implementations for these */
+void map_segment(pgdir_t pgdir, uintptr_t va, size_t size, physaddr_t pa,
+                 int perm, int pml_shift);
+int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size);
 pte_t pgdir_walk(pgdir_t pgdir, const void *va, int create);
 int get_va_perms(pgdir_t pgdir, const void *va);
 int arch_pgdir_setup(pgdir_t boot_copy, pgdir_t *new_pd);
index cb896e2..f0d8797 100644 (file)
@@ -1172,20 +1172,6 @@ static size_t vmap_nr_to_free;
 /* This value tunes the ratio of global TLB shootdowns to __vmap_free()s. */
 #define VMAP_MAX_TO_FREE 1000
 
-/* Unmaps / 0's the PTEs of a chunk of vaddr space.  Caller needs to send a
- * global TLB shootdown before freeing the vaddr. */
-static int __unmap_vmap_segment(uintptr_t vaddr, unsigned long num_pages)
-{
-       pte_t pte;
-
-       for (int i = 0; i < num_pages; i++) {
-               pte = pgdir_walk(boot_pgdir, (void*)(vaddr + i * PGSIZE), 1);
-               if (pte_walk_okay(pte))
-                       pte_clear(pte);
-       }
-       return 0;
-}
-
 /* We don't immediately return the addrs to their source (vmap_addr_arena).
  * Instead, we hold on to them until we have a suitable amount, then free them
  * in a batch.  This amoritizes the cost of the TLB global shootdown.  We can
@@ -1193,15 +1179,13 @@ static int __unmap_vmap_segment(uintptr_t vaddr, unsigned long num_pages)
  * vmap_to_free array). */
 static void __vmap_free(struct arena *source, void *obj, size_t size)
 {
-       unsigned long nr_pages;
        struct vmap_free_tracker *vft;
 
-       nr_pages = ROUNDUP(size, PGSIZE) >> PGSHIFT;
        spin_lock(&vmap_lock);
        /* All objs get *unmapped* immediately, but we'll shootdown later.  Note
         * that it is OK (but slightly dangerous) for the kernel to reuse the paddrs
         * pointed to by the vaddrs before a TLB shootdown. */
-       __unmap_vmap_segment((uintptr_t)obj, nr_pages);
+       unmap_segment(boot_pgdir, (uintptr_t)obj, size);
        if (vmap_nr_to_free < VMAP_MAX_TO_FREE) {
                vft = &vmap_to_free[vmap_nr_to_free++];
                vft->addr = obj;
@@ -1263,25 +1247,12 @@ void put_vmap_segment(uintptr_t vaddr, size_t nr_bytes)
 int map_vmap_segment(uintptr_t vaddr, uintptr_t paddr, unsigned long num_pages,
                      int perm)
 {
-       /* TODO: (MM) you should lock on boot pgdir modifications.  A vm region lock
-        * isn't enough, since there might be a race on outer levels of page tables.
-        * For now, we'll just use the vmap_lock (which technically works). */
-       spin_lock(&vmap_lock);
-       pte_t pte;
 #ifdef CONFIG_X86
        perm |= PTE_G;
 #endif
-       for (int i = 0; i < num_pages; i++) {
-               pte = pgdir_walk(boot_pgdir, (void*)(vaddr + i * PGSIZE), 1);
-               if (!pte_walk_okay(pte)) {
-                       spin_unlock(&vmap_lock);
-                       return -ENOMEM;
-               }
-               /* You probably should have unmapped first */
-               if (pte_is_mapped(pte))
-                       warn("Existing PTE value %p\n", pte_print(pte));
-               pte_write(pte, paddr + i * PGSIZE, perm);
-       }
+       spin_lock(&vmap_lock);
+       map_segment(boot_pgdir, vaddr, num_pages * PGSIZE, paddr, perm,
+                   arch_max_jumbo_page_shift());
        spin_unlock(&vmap_lock);
        return 0;
 }