Added env_user_mem_walk
authorAndrew Waterman <waterman@parcad.millennium.berkeley.edu>
Mon, 1 Feb 2010 02:18:23 +0000 (18:18 -0800)
committerAndrew Waterman <waterman@parcad.millennium.berkeley.edu>
Mon, 1 Feb 2010 02:18:23 +0000 (18:18 -0800)
..which calls a user-specified function on each resident
page in a specified range.  can be used to free memory
(env_user_mem_free was rewritten to use it), or other
neat things.

kern/arch/i386/env.c
kern/arch/sparc/env.c
kern/arch/sparc/mmu.h
kern/arch/sparc/ros/mmu.h
kern/include/env.h
kern/include/ros/procdata.h
kern/src/env.c
kern/src/process.c

index 72eb99f..d24ea87 100644 (file)
@@ -85,14 +85,19 @@ void env_pop_tf(trapframe_t *tf)
 
 // Flush all mapped pages in the user portion of the address space
 void
-env_user_mem_free(env_t* e)
+env_user_mem_walk(env_t* e, void* start, size_t len,
+                  mem_walk_callback_t callback, void* arg)
 {
        pte_t *pt;
        uint32_t pdeno, pteno;
        physaddr_t pa;
 
-       static_assert(UVPT % PTSIZE == 0);
-       for (pdeno = 0; pdeno < PDX(UVPT); pdeno++) {
+       assert((intptr_t)start % PGSIZE == 0 && len % PGSIZE == 0);
+       void* end = (char*)start+len;
+       uint32_t pdeno_start = PDX(start);
+       uint32_t pdeno_end = PDX(ROUNDUP(end,PTSIZE));
+
+       for (pdeno = pdeno_start; pdeno < pdeno_end; pdeno++) {
 
                // only look at mapped page tables
                if (!(e->env_pgdir[pdeno] & PTE_P))
@@ -103,13 +108,38 @@ env_user_mem_free(env_t* e)
                pt = (pte_t*COUNT(NPTENTRIES)) KADDR(pa);
 
                // unmap all PTEs in this page table 
-               for (pteno = 0; pteno <= PTX(~0); pteno++) {
+               uint32_t pteno_start = pdeno == pdeno_start ? PTX(start) : 0;
+               uint32_t pteno_end = pdeno == pdeno_end-1 && PTX(end) != 0 ?
+                                    PTX(end) : NPTENTRIES;
+               for (pteno = pteno_start; pteno < pteno_end; pteno++) {
                        if (pt[pteno] & PTE_P)
-                               page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0));
+                               callback(e, &pt[pteno], PGADDR(pdeno, pteno, 0), arg);
                }
+       }
+}
+
+void
+env_pagetable_free(env_t* e)
+{
+       static_assert(UVPT % PTSIZE == 0);
+       for(uint32_t pdeno = 0; pdeno < PDX(UVPT); pdeno++)
+       {
+               // only look at mapped page tables
+               if (!(e->env_pgdir[pdeno] & PTE_P))
+                       continue;
+
+               // find the pa and va of the page table
+               physaddr_t pa = PTE_ADDR(e->env_pgdir[pdeno]);
 
                // free the page table itself
                e->env_pgdir[pdeno] = 0;
                page_decref(pa2page(pa));
        }
+
+       // free the page directory
+       physaddr_t pa = e->env_cr3;
+       e->env_cr3 = 0;
+       page_decref(pa2page(pa));
+       tlbflush();
 }
+
index 5f5ed39..bb7aa60 100644 (file)
@@ -89,73 +89,77 @@ restore_fp_state(ancillary_state_t* silly)
        write_psr(read_psr() & ~PSR_EF);
 }
 
-
 // Flush all mapped pages in the user portion of the address space
 // TODO: only supports L3 user pages
-
-typedef void (*env_user_mem_walk_t)(env_t* e, void* va, pte_t* pte, void* arg);
-
 void
-env_user_mem_walk(env_t* e, env_user_mem_walk_t callback, void* arg)
+env_user_mem_walk(env_t* e, void* start, size_t len,
+                  mem_walk_callback_t callback, void* arg)
 {
-       pte_t *l1pt = e->env_pgdir, *l2pt, *l3pt;
-       uint32_t l1x,l2x,l3x;
-       physaddr_t l2ptpa,l3ptpa,page_pa;
-       uint32_t l2_tables_per_page,l3_tables_per_page;
+       pte_t *l1pt = e->env_pgdir;
 
-       l2_tables_per_page = PGSIZE/(sizeof(pte_t)*NL2ENTRIES);
-       l3_tables_per_page = PGSIZE/(sizeof(pte_t)*NL3ENTRIES);
+       assert((intptr_t)start % PGSIZE == 0 && len % PGSIZE == 0);
+       void* end = (char*)start+len;
 
-       static_assert(L2X(KERNBASE) == 0 && L3X(KERNBASE) == 0);
-       for(l1x = 0; l1x < L1X(KERNBASE); l1x++)
+       int l1x_start = L1X(start);
+       int l1x_end = L1X(ROUNDUP(end,L1PGSIZE));
+       for(int l1x = l1x_start; l1x < l1x_end; l1x++)
        {
                if(!(l1pt[l1x] & PTE_PTD))
                        continue;
 
-               l2ptpa = PTD_ADDR(l1pt[l1x]);
-               l2pt = (pte_t*COUNT(NL2ENTRIES)) KADDR(l2ptpa);
+               physaddr_t l2ptpa = PTD_ADDR(l1pt[l1x]);
+               pte_t* l2pt = (pte_t*)KADDR(l2ptpa);
 
-               for(l2x = 0; l2x < NL2ENTRIES; l2x++)
+               int l2x_start = l1x == l1x_start ? L2X(start) : 0;
+               int l2x_end = l1x == l1x_end-1 && L2X(ROUNDUP(end,L2PGSIZE)) ?
+                             L2X(ROUNDUP(end,L2PGSIZE)) : NL2ENTRIES;
+               for(int l2x = l2x_start; l2x < l2x_end; l2x++)
                {
                        if(!(l2pt[l2x] & PTE_PTD))
                                continue;
 
-                       l3ptpa = PTD_ADDR(l2pt[l2x]);
-                       l3pt = (pte_t*COUNT(NL3ENTRIES)) KADDR(l3ptpa);
+                       physaddr_t l3ptpa = PTD_ADDR(l2pt[l2x]);
+                       pte_t* l3pt = (pte_t*)KADDR(l3ptpa);
 
-                       for(l3x = 0; l3x < NL3ENTRIES; l3x++)
-                       {
+                       int l3x_start = l1x == l1x_start && l2x == l2x_start ?
+                                       L3X(start) : 0;
+                       int l3x_end = l1x == l1x_end-1 && l2x == l2x_end-1 && L3X(end) ?
+                                     L3X(end) : NL3ENTRIES;
+                       for(int l3x = l3x_start; l3x < l3x_end; l3x++)
                                if(l3pt[l3x] & PTE_PTE)
-                               {
-                                       callback(e,PGADDR(l1x,l2x,l3x,0),
-                                                &l3pt[l3x],arg);
-                               }
-                       }
+                                       callback(e,&l3pt[l3x],PGADDR(l1x,l2x,l3x,0),arg);
+               }
+       }
+}
 
-                       l2pt[l2x] = 0;
+void
+env_pagetable_free(env_t* e)
+{
+       static_assert(L2X(KERNBASE) == 0 && L3X(KERNBASE) == 0);
+       pte_t *l1pt = e->env_pgdir;
+
+       for(int l1x = 0; l1x < L1X(KERNBASE); l1x++)
+       {
+               if(!(l1pt[l1x] & PTE_PTD))
+                       continue;
 
-                       // free the L3 PT itself
+               physaddr_t l2ptpa = PTD_ADDR(l1pt[l1x]);
+               pte_t* l2pt = (pte_t*)KADDR(l2ptpa);
+
+               for(int l2x = 0; l2x < NL2ENTRIES; l2x++)
+               {
+                       if(!(l2pt[l2x] & PTE_PTD))
+                               continue;
+
+                       physaddr_t l3ptpa = PTD_ADDR(l2pt[l2x]);
+                       l2pt[l2x] = 0;
                        page_decref(pa2page(l3ptpa));
                }
 
                l1pt[l1x] = 0;
-
-               // free the L2 PT itself
                page_decref(pa2page(l2ptpa));
        }
 
+       page_decref(pa2page(e->env_cr3));
        tlbflush();
 }
-
-void
-env_user_mem_free(env_t* e)
-{
-       void mem_free_callback(env_t* e, void* va, pte_t* pte, void* arg)
-       {
-               page_t* page = ppn2page(PTE2PPN(*pte));
-               page_decref(page);
-               *pte = 0;
-       }
-       env_user_mem_walk(e,&mem_free_callback,NULL);
-}
-
index f56826a..7d79b4b 100644 (file)
 #define NL2ENTRIES     64              // # entries in an L2 page table
 #define NL1ENTRIES     256             // # entries in an L1 page table
 
-#define L3PGSIZE       4096            // bytes mapped by an L3 page
-#define L3PGSHIFT      12              // log2(L3PGSIZE)
-
-#define L2PGSIZE       (4096*64)       // bytes mapped by an L2 page
-#define L2PGSHIFT      (12+6)          // log2(L2PGSIZE)
-
-#define L1PGSIZE       (4096*64*64)    // bytes mapped by an L1 page
-#define L1PGSHIFT      (12+6+6)        // log2(L1PGSIZE)
-
 // Page table/directory entry flags.
 #define PTE_PTD                0x001   // Entry is a Page Table Descriptor
 #define PTE_PTE                0x002   // Entry is a Page Table Entry
index 6608868..394fe78 100644 (file)
@@ -7,8 +7,18 @@
 // Use this if needed in annotations
 #define IVY_KERNBASE (0x8000U << 16)
 
-#define PGSHIFT 12
+#define L3PGSHIFT   12
+#define L3PGSIZE    (1<<L3PGSHIFT)
+
+#define L2PGSHIFT   (12+6)
+#define L2PGSIZE    (1<<L2PGSHIFT)
+
+#define L1PGSHIFT   (12+6+6)
+#define L1PGSIZE    (1<<L1PGSHIFT)
+
+
+#define PGSHIFT L3PGSHIFT
 #define PGSIZE (1 << PGSHIFT)
-#define PTSIZE PGSIZE
+#define PTSIZE L1PGSIZE
 
 #endif
index 49f67b1..920a49b 100644 (file)
@@ -77,11 +77,15 @@ extern atomic_t num_envs;           // Number of envs
 int            env_setup_vm(env_t *e);
 void   env_push_ancillary_state(env_t* e);
 void   env_pop_ancillary_state(env_t* e);
-void   env_user_mem_free(env_t* e);
+void   env_user_mem_free(env_t* e, void* start, size_t len);
+void   env_pagetable_free(env_t* e);
 void   env_segment_alloc(env_t *e, void *SNT va, size_t len);
 void   env_segment_free(env_t *e, void *SNT va, size_t len);
 void   env_load_icode(env_t* e, env_t* binary_env, uint8_t *COUNT(size) binary, size_t size);
 
+typedef        void (*mem_walk_callback_t)(env_t* e, pte_t* pte, void* va, void* arg);
+void   env_user_mem_walk(env_t* e, void* start, size_t len, mem_walk_callback_t callback, void* arg);
+
 // The following three functions do not return
 void   env_pop_tf(trapframe_t *tf) __attribute__((noreturn));
 
index 220d3e9..6d0913c 100644 (file)
@@ -8,6 +8,7 @@
 #include <ros/ring_syscall.h>
 #include <ros/common.h>
 #include <ros/procinfo.h>
+#include <arch/mmu.h>
 
 typedef struct procdata {
        // The actual ring buffers for communicating with user space
index e60d595..39796ac 100644 (file)
@@ -122,8 +122,8 @@ env_setup_vm_error_d:
        free_cont_pages(e->env_procinfo, LOG2_UP(PROCINFO_NUM_PAGES));
 env_setup_vm_error_i:
        page_free(shared_page);
-       env_user_mem_free(e);
-       page_free(pgdir);
+       env_user_mem_free(e,0,KERNBASE);
+       env_pagetable_free(e);
        return -ENOMEM;
 }
 
@@ -184,12 +184,7 @@ env_segment_free(env_t *e, void *SNT va, size_t len)
        assert(e->env_cr3 == rcr3());
        num_pages = LA2PPN(end - start);
 
-       for (int i = 0; i < num_pages; i++, start += PGSIZE) {
-               // skip if a page is already unmapped. 
-               pte = pgdir_walk(e->env_pgdir, start, 0);
-               if (pte && *pte & PTE_P)
-                       page_remove(e->env_pgdir,start);
-       }
+       env_user_mem_free(e,start,(char*)end-(char*)start);
 }
 
 // this helper function handles all cases of copying to/from user/kernel
@@ -346,3 +341,18 @@ void run_env_handler(trapframe_t *tf, void * data)
        if (enqueue_work(workqueue, &job))
                panic("Failed to enqueue work!");
 }
+
+void
+env_user_mem_free(env_t* e, void* start, size_t len)
+{
+       void user_page_free(env_t* e, pte_t* pte, void* va, void* arg)
+       {
+               page_t* page = ppn2page(PTE2PPN(*pte));
+               *pte = 0;
+               page_decref(page);
+       }
+
+       env_user_mem_walk(e,start,len,&user_page_free,NULL);
+       tlbflush();
+}
+
index 9d9624c..2795edd 100644 (file)
@@ -316,16 +316,14 @@ static void __proc_free(struct proc *p)
        }
 
        // Flush all mapped pages in the user portion of the address space
-       env_user_mem_free(p);
+       env_user_mem_free(p,0,KERNBASE);
        /* These need to be free again, since they were allocated with a refcnt. */
        free_cont_pages(p->env_procinfo, LOG2_UP(PROCINFO_NUM_PAGES));
        free_cont_pages(p->env_procdata, LOG2_UP(PROCDATA_NUM_PAGES));
 
-       // free the page directory
-       pa = p->env_cr3;
+       env_pagetable_free(p);
        p->env_pgdir = 0;
        p->env_cr3 = 0;
-       page_decref(pa2page(pa));
 
        /* Remove self from the pid hash, return PID.  Note the reversed order. */
        spin_lock(&pid_hash_lock);