summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_hibernate.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/subr_hibernate.c')
-rw-r--r--sys/kern/subr_hibernate.c305
1 files changed, 79 insertions, 226 deletions
diff --git a/sys/kern/subr_hibernate.c b/sys/kern/subr_hibernate.c
index 99323129106..d83b89b6784 100644
--- a/sys/kern/subr_hibernate.c
+++ b/sys/kern/subr_hibernate.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_hibernate.c,v 1.100 2014/09/19 20:02:25 kettenis Exp $ */
+/* $OpenBSD: subr_hibernate.c,v 1.101 2014/09/26 09:25:38 kettenis Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl>
@@ -68,6 +68,7 @@ vaddr_t hibernate_rle_page;
union hibernate_info disk_hib;
paddr_t global_pig_start;
vaddr_t global_piglet_va;
+paddr_t global_piglet_pa;
/* #define HIB_DEBUG */
#ifdef HIB_DEBUG
@@ -392,85 +393,42 @@ uvm_pmr_dirty_everything(void)
}
/*
- * Allocate the highest address that can hold sz.
- *
- * sz in bytes.
+ * Allocate an area that can hold sz bytes and doesn't overlap with
+ * the piglet at piglet_pa.
*/
int
-uvm_pmr_alloc_pig(paddr_t *addr, psize_t sz)
+uvm_pmr_alloc_pig(paddr_t *pa, psize_t sz, paddr_t piglet_pa)
{
- struct uvm_pmemrange *pmr;
- struct vm_page *pig_pg, *pg;
-
- /*
- * Convert sz to pages, since that is what pmemrange uses internally.
- */
- sz = atop(round_page(sz));
-
- uvm_lock_fpageq();
-
- TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) {
- RB_FOREACH_REVERSE(pig_pg, uvm_pmr_addr, &pmr->addr) {
- if (pig_pg->fpgsz >= sz) {
- goto found;
- }
- }
- }
-
- /*
- * Allocation failure.
- */
- uvm_unlock_fpageq();
- return ENOMEM;
-
-found:
- /* Remove page from freelist. */
- 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);
+ struct uvm_constraint_range pig_constraint;
+ struct kmem_pa_mode kp_pig = {
+ .kp_constraint = &pig_constraint,
+ .kp_maxseg = 1
+ };
+ vaddr_t va;
- /*
- * 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_PMAPMASK);
+ sz = round_page(sz);
- if (pg->pg_flags & PG_ZERO)
- uvmexp.zeropages -= sz;
- atomic_clearbits_int(&pg->pg_flags,
- PG_ZERO|PQ_FREE);
+ pig_constraint.ucr_low = piglet_pa + 4 * HIBERNATE_CHUNK_SIZE;
+ pig_constraint.ucr_high = -1;
- pg->uobject = NULL;
- pg->uanon = NULL;
- pg->pg_version++;
+ va = (vaddr_t)km_alloc(sz, &kv_any, &kp_pig, &kd_nowait);
+ if (va == 0) {
+ pig_constraint.ucr_low = 0;
+ pig_constraint.ucr_high = piglet_pa - 1;
- /*
- * Next.
- */
- pg++;
- sz--;
+ va = (vaddr_t)km_alloc(sz, &kv_any, &kp_pig, &kd_nowait);
+ if (va == 0)
+ return ENOMEM;
}
- /* Return. */
- uvm_unlock_fpageq();
+ pmap_extract(pmap_kernel(), va, pa);
return 0;
}
/*
* Allocate a piglet area.
*
- * This is as low as possible.
+ * This needs to be in DMA-safe memory.
* Piglets are aligned.
*
* sz and align in bytes.
@@ -482,18 +440,15 @@ found:
int
uvm_pmr_alloc_piglet(vaddr_t *va, paddr_t *pa, vsize_t sz, paddr_t align)
{
- paddr_t pg_addr, piglet_addr;
- struct uvm_pmemrange *pmr;
- struct vm_page *pig_pg, *pg;
- struct pglist pageq;
- int pdaemon_woken;
- vaddr_t piglet_va;
+ struct kmem_pa_mode kp_piglet = {
+ .kp_constraint = &dma_constraint,
+ .kp_align = align,
+ .kp_maxseg = 1
+ };
/* Ensure align is a power of 2 */
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
@@ -503,97 +458,11 @@ uvm_pmr_alloc_piglet(vaddr_t *va, paddr_t *pa, vsize_t sz, paddr_t align)
align = PAGE_SIZE;
sz = 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 (atop(pg_addr) + pig_pg->fpgsz >=
- atop(piglet_addr) + atop(sz))
- goto found;
- }
- }
-
- /*
- * Try to coerce 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,
- 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) + atop(sz), &pageq);
-
- *pa = piglet_addr;
- uvmexp.free -= atop(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_PMAPMASK);
-
- 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();
-
- /*
- * Now allocate a va.
- * Use direct mappings for the pages.
- */
-
- piglet_va = *va = (vaddr_t)km_alloc(sz, &kv_any, &kp_none, &kd_waitok);
- if (!piglet_va) {
- uvm_pglistfree(&pageq);
+ *va = (vaddr_t)km_alloc(sz, &kv_any, &kp_piglet, &kd_nowait);
+ if (*va == 0)
return ENOMEM;
- }
-
- /*
- * Map piglet to va.
- */
- TAILQ_FOREACH(pg, &pageq, pageq) {
- pmap_kenter_pa(piglet_va, VM_PAGE_TO_PHYS(pg), UVM_PROT_RW);
- piglet_va += PAGE_SIZE;
- }
- pmap_update(pmap_kernel());
+ pmap_extract(pmap_kernel(), *va, pa);
return 0;
}
@@ -603,35 +472,15 @@ found:
void
uvm_pmr_free_piglet(vaddr_t va, vsize_t sz)
{
- paddr_t pa;
- struct vm_page *pg;
-
/*
* Fix parameters.
*/
sz = round_page(sz);
/*
- * Find the first page in piglet.
- * Since piglets are contiguous, the first pg is all we need.
- */
- if (!pmap_extract(pmap_kernel(), va, &pa))
- panic("uvm_pmr_free_piglet: piglet 0x%lx has no pages", va);
- pg = PHYS_TO_VM_PAGE(pa);
- if (pg == NULL)
- panic("uvm_pmr_free_piglet: unmanaged page 0x%lx", pa);
-
- /*
- * Unmap.
- */
- pmap_kremove(va, sz);
- pmap_update(pmap_kernel());
-
- /*
* Free the physical and virtual memory.
*/
- uvm_pmr_freepages(pg, atop(sz));
- km_free((void *)va, sz, &kv_any, &kp_none);
+ km_free((void *)va, sz, &kv_any, &kp_dma_contig);
}
/*
@@ -725,13 +574,8 @@ get_hibernate_info(union hibernate_info *hib, int suspend)
min(strlen(version), sizeof(hib->kernel_version)-1));
if (suspend) {
- /* Allocate piglet region */
- if (uvm_pmr_alloc_piglet(&hib->piglet_va,
- &hib->piglet_pa, HIBERNATE_CHUNK_SIZE * 4,
- HIBERNATE_CHUNK_SIZE)) {
- printf("Hibernate failed to allocate the piglet\n");
- return (1);
- }
+ hib->piglet_va = global_piglet_va;
+ hib->piglet_pa = global_piglet_pa;
hib->io_page = (void *)hib->piglet_va;
/*
@@ -763,11 +607,8 @@ get_hibernate_info(union hibernate_info *hib, int suspend)
goto fail;
return (0);
-fail:
- if (suspend)
- uvm_pmr_free_piglet(hib->piglet_va,
- HIBERNATE_CHUNK_SIZE * 4);
+fail:
return (1);
}
@@ -1507,33 +1348,6 @@ hibernate_write_chunks(union hibernate_info *hib)
hib->chunk_ctr = 0;
/*
- * Allocate VA for the temp and copy page.
- *
- * These will become part of the suspended kernel and will
- * be freed in hibernate_free, upon resume.
- */
- hibernate_temp_page = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any,
- &kp_none, &kd_nowait);
- if (!hibernate_temp_page) {
- DPRINTF("out of memory allocating hibernate_temp_page\n");
- return (ENOMEM);
- }
-
- hibernate_copy_page = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any,
- &kp_none, &kd_nowait);
- if (!hibernate_copy_page) {
- DPRINTF("out of memory allocating hibernate_copy_page\n");
- return (ENOMEM);
- }
-
- hibernate_rle_page = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any,
- &kp_none, &kd_nowait);
- if (!hibernate_rle_page) {
- DPRINTF("out of memory allocating hibernate_rle_page\n");
- return (ENOMEM);
- }
-
- /*
* Map the utility VAs to the piglet. See the piglet map at the
* top of this file for piglet layout information.
*/
@@ -1816,7 +1630,7 @@ hibernate_read_image(union hibernate_info *hib)
/* Allocate the pig area */
pig_sz = compressed_size + HIBERNATE_CHUNK_SIZE;
- if (uvm_pmr_alloc_pig(&pig_start, pig_sz) == ENOMEM) {
+ if (uvm_pmr_alloc_pig(&pig_start, pig_sz, hib->piglet_pa) == ENOMEM) {
status = 1;
goto unmap;
}
@@ -2020,9 +1834,6 @@ hibernate_suspend(void)
VM_PROT_ALL);
pmap_activate(curproc);
- /* Stash the piglet VA so we can free it in the resuming kernel */
- global_piglet_va = hib.piglet_va;
-
DPRINTF("hibernate: writing chunks\n");
if (hibernate_write_chunks(&hib)) {
DPRINTF("hibernate_write_chunks failed\n");
@@ -2053,8 +1864,50 @@ hibernate_suspend(void)
return (0);
}
+int
+hibernate_alloc(void)
+{
+ KASSERT(global_piglet_va == 0);
+ KASSERT(hibernate_temp_page == 0);
+ KASSERT(hibernate_copy_page == 0);
+ KASSERT(hibernate_rle_page == 0);
+
+ if (uvm_pmr_alloc_piglet(&global_piglet_va, &global_piglet_pa,
+ HIBERNATE_CHUNK_SIZE * 4, HIBERNATE_CHUNK_SIZE))
+ return (ENOMEM);
+
+ /*
+ * Allocate VA for the temp and copy page.
+ *
+ * These will become part of the suspended kernel and will
+ * be freed in hibernate_free, upon resume.
+ */
+ hibernate_temp_page = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any,
+ &kp_none, &kd_nowait);
+ if (!hibernate_temp_page) {
+ DPRINTF("out of memory allocating hibernate_temp_page\n");
+ return (ENOMEM);
+ }
+
+ hibernate_copy_page = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any,
+ &kp_none, &kd_nowait);
+ if (!hibernate_copy_page) {
+ DPRINTF("out of memory allocating hibernate_copy_page\n");
+ return (ENOMEM);
+ }
+
+ hibernate_rle_page = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any,
+ &kp_none, &kd_nowait);
+ if (!hibernate_rle_page) {
+ DPRINTF("out of memory allocating hibernate_rle_page\n");
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
/*
- * Free items allocated by hibernate_suspend()
+ * Free items allocated by hibernate_alloc()
*/
void
hibernate_free(void)