diff options
author | 2011-07-08 18:31:16 +0000 | |
---|---|---|
committer | 2011-07-08 18:31:16 +0000 | |
commit | 0f0ce22aeec69fd01eb6e6f433e67477e2fb596e (patch) | |
tree | a90ced1dcd99bf8474c2b25de7c9101e3cc1ee91 /sys/kern/subr_hibernate.c | |
parent | Include PIPEX in kernel by default. And add new sysctl variable (diff) | |
download | wireguard-openbsd-0f0ce22aeec69fd01eb6e6f433e67477e2fb596e.tar.xz wireguard-openbsd-0f0ce22aeec69fd01eb6e6f433e67477e2fb596e.zip |
Change pig allocator to a highest-address selection.
Add piglet allocator, which does a lowest-address selection.
No callers yet, needed by mlarkin@ for hibernate voodoo.
Diffstat (limited to 'sys/kern/subr_hibernate.c')
-rw-r--r-- | sys/kern/subr_hibernate.c | 199 |
1 files changed, 172 insertions, 27 deletions
diff --git a/sys/kern/subr_hibernate.c b/sys/kern/subr_hibernate.c index b071538a289..8c8c4ccfd46 100644 --- a/sys/kern/subr_hibernate.c +++ b/sys/kern/subr_hibernate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: subr_hibernate.c,v 1.3 2011/07/08 18:25:56 ariane Exp $ */ +/* $OpenBSD: subr_hibernate.c,v 1.4 2011/07/08 18:31:16 ariane Exp $ */ /* * Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl> @@ -264,45 +264,190 @@ uvm_pmr_zero_everything(void) } /* - * Allocate the biggest contig chunk of memory. + * Allocate the highest address that can hold sz. + * + * sz in bytes. */ int -uvm_pmr_alloc_pig(paddr_t *addr, psize_t *sz) +uvm_pmr_alloc_pig(paddr_t *addr, psize_t sz) { - struct uvm_pmemrange *pig_pmr, *pmr; + struct uvm_pmemrange *pmr; struct vm_page *pig_pg, *pg; - int memtype; + + /* + * Convert sz to pages, since that is what pmemrange uses internally. + */ + sz = atop(round_page(sz)); uvm_lock_fpageq(); - pig_pg = NULL; + TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) { - for (memtype = 0; memtype < UVM_PMR_MEMTYPE_MAX; memtype++) { - /* Find biggest page in this memtype pmr. */ - pg = RB_MAX(uvm_pmr_size, &pmr->size[memtype]); - if (pg == NULL) - pg = TAILQ_FIRST(&pmr->single[memtype]); - else - pg--; - - if (pig_pg == NULL || (pg != NULL && pig_pg != NULL && - pig_pg->fpgsz < pg->fpgsz)) { - pig_pmr = pmr; - pig_pg = pg; + RB_FOREACH_REVERSE(pig_pg, uvm_pmr_addr, &pmr->addr) { + if (pig_pg->fpgsz >= sz) { + goto found; } } } + /* + * Allocation failure. + */ + uvm_unlock_pageq(); + return ENOMEM; + +found: /* Remove page from freelist. */ - if (pig_pg != NULL) { - uvm_pmr_remove(pig_pmr, pig_pg); - uvmexp.free -= pig_pg->fpgsz; - if (pig_pg->pg_flags & PG_ZERO) - uvmexp.zeropages -= pig_pg->fpgsz; - *addr = VM_PAGE_TO_PHYS(pig_pg); - *sz = pig_pg->fpgsz; + uvm_pmr_remove_size(pmr, pig_pg); + pig_pg->fpgsz -= sz; + pg = pig_pg + pig_pg->fpgsz; + if (pig_pg->fpgsz == 0) + uvm_pmr_remove_addr(pmr, pig_pg); + else + uvm_pmr_insert_size(pmr, pig_pg); + + uvmexp.free -= sz; + *addr = VM_PAGE_TO_PHYS(pg); + + /* + * Update pg flags. + * + * Note that we trash the sz argument now. + */ + while (sz > 0) { + KASSERT(pg->pg_flags & PQ_FREE); + + atomic_clearbits_int(&pg->pg_flags, + PG_PMAP0|PG_PMAP1|PG_PMAP2|PG_PMAP3); + + if (pg->pg_flags & PG_ZERO) + uvmexp.zeropages -= sz; + atomic_clearbits_int(&pg->pg_flags, + PG_ZERO|PQ_FREE); + + pg->uobject = NULL; + pg->uanon = NULL; + pg->pg_version++; + + /* + * Next. + */ + pg++; + sz--; } - uvm_unlock_fpageq(); /* Return. */ - return (pig_pg != NULL ? 0 : ENOMEM); + uvm_unlock_fpageq(); + return 0; +} + +/* + * Allocate a piglet area. + * + * This is as low as possible. + * Piglets are aligned. + * + * sz and align in bytes. + * + * The call will sleep for the pagedaemon to attempt to free memory. + * The pagedaemon may decide its not possible to free enough memory, causing + * the allocation to fail. + */ +int +uvm_pmr_alloc_piglet(paddr_t *addr, psize_t sz, paddr_t align) +{ + vaddr_t pg_addr, piglet_addr; + struct uvm_pmemrange *pmr; + struct vm_page *pig_pg, *pg; + struct pglist pageq; + int pdaemon_woken; + + KASSERT((align & (align - 1)) == 0); + pdaemon_woken = 0; /* Didn't wake the pagedaemon. */ + + /* + * Fixup arguments: align must be at least PAGE_SIZE, + * sz will be converted to pagecount, since that is what + * pmemrange uses internally. + */ + if (align < PAGE_SIZE) + align = PAGE_SIZE; + sz = atop(round_page(sz)); + + uvm_lock_fpageq(); + + TAILQ_FOREACH_REVERSE(pmr, &uvm.pmr_control.use, uvm_pmemrange_use, + pmr_use) { +retry: + /* + * Search for a range with enough space. + * Use the address tree, to ensure the range is as low as + * possible. + */ + RB_FOREACH(pig_pg, uvm_pmr_addr, &pmr->addr) { + pg_addr = VM_PAGE_TO_PHYS(pig_pg); + piglet_addr = (pg_addr + (align - 1)) & ~(align - 1); + + if (pig_pg->fpgsz >= sz) { + goto found; + } + + if (atop(pg_addr) + pig_pg->fpgsz > + atop(piglet_addr) + sz) { + goto found; + } + } + + /* + * Try to coerse the pagedaemon into freeing memory + * for the piglet. + * + * pdaemon_woken is set to prevent the code from + * falling into an endless loop. + */ + if (!pdaemon_woken) { + pdaemon_woken = 1; + if (uvm_wait_pla(ptoa(pmr->low), ptoa(pmr->high) - 1, + ptoa(sz), UVM_PLA_FAILOK) == 0) + goto retry; + } + } + + /* Return failure. */ + uvm_unlock_fpageq(); + return ENOMEM; + +found: + /* + * Extract piglet from pigpen. + */ + TAILQ_INIT(&pageq); + uvm_pmr_extract_range(pmr, pig_pg, + atop(piglet_addr), atop(piglet_addr) + sz, &pageq); + + *addr = piglet_addr; + uvmexp.free -= sz; + + /* + * Update pg flags. + * + * Note that we trash the sz argument now. + */ + TAILQ_FOREACH(pg, &pageq, pageq) { + KASSERT(pg->pg_flags & PQ_FREE); + + atomic_clearbits_int(&pg->pg_flags, + PG_PMAP0|PG_PMAP1|PG_PMAP2|PG_PMAP3); + + if (pg->pg_flags & PG_ZERO) + uvmexp.zeropages--; + atomic_clearbits_int(&pg->pg_flags, + PG_ZERO|PQ_FREE); + + pg->uobject = NULL; + pg->uanon = NULL; + pg->pg_version++; + } + + uvm_unlock_fpageq(); + return 0; } |