Index: sys/vm/vm_page.c =================================================================== --- sys/vm/vm_page.c +++ sys/vm/vm_page.c @@ -770,51 +770,29 @@ #endif vm_cnt.v_page_count = 0; for (segind = 0; segind < vm_phys_nsegs; segind++) { - seg = &vm_phys_segs[segind]; - /* - * If lazy vm_page initialization is not enabled, simply - * initialize all of the pages in the segment. Otherwise, we - * only initialize: - * 1. Pages not covered by phys_avail[], since they might be - * freed to the allocator at some future point, e.g., by - * kmem_bootstrap_free(). - * 2. The first page of each run of free pages handed to the - * vm_phys allocator, which in turn defers initialization - * of pages until they are needed. - * This avoids blocking the boot process for long periods, which - * may be relevant for VMs (which ought to boot as quickly as - * possible) and/or systems with large amounts of physical - * memory. + * Initialize pages not covered by phys_avail[], since they + * might be freed to the allocator at some future point, e.g., + * by kmem_bootstrap_free(). */ -#ifdef VM_FREEPOOL_LAZYINIT - if (lazyinit) { - startp = seg->start; - for (i = 0; phys_avail[i + 1] != 0; i += 2) { - if (startp >= seg->end) - break; - - if (phys_avail[i + 1] < startp) - continue; - if (phys_avail[i] <= startp) { - startp = phys_avail[i + 1]; - continue; - } - - m = vm_phys_seg_paddr_to_vm_page(seg, startp); - for (endp = MIN(phys_avail[i], seg->end); - startp < endp; startp += PAGE_SIZE, m++) { - vm_page_init_page(m, startp, segind, - VM_FREEPOOL_DEFAULT); - } + seg = &vm_phys_segs[segind]; + startp = seg->start; + for (i = 0; phys_avail[i + 1] != 0; i += 2) { + if (startp >= seg->end) + break; + if (phys_avail[i + 1] < startp) + continue; + if (phys_avail[i] <= startp) { + startp = phys_avail[i + 1]; + continue; } - } else -#endif - for (m = seg->first_page, pa = seg->start; - pa < seg->end; m++, pa += PAGE_SIZE) { - vm_page_init_page(m, pa, segind, + m = vm_phys_seg_paddr_to_vm_page(seg, startp); + for (endp = MIN(phys_avail[i], seg->end); + startp < endp; startp += PAGE_SIZE, m++) { + vm_page_init_page(m, startp, segind, VM_FREEPOOL_DEFAULT); } + } /* * Add the segment's pages that are covered by one of @@ -831,16 +809,41 @@ if (pagecount == 0) continue; + /* + * If lazy vm_page initialization is not enabled, simply + * initialize all of the pages in the segment covered by + * phys_avail. Otherwise, initialize only the first + * page of each run of free pages handed to the vm_phys + * allocator, which in turn defers initialization of + * pages until they are needed. + * + * This avoids blocking the boot process for long + * periods, which may be relevant for VMs (which ought + * to boot as quickly as possible) and/or systems with + * large amounts of physical memory. + */ m = vm_phys_seg_paddr_to_vm_page(seg, startp); #ifdef VM_FREEPOOL_LAZYINIT if (lazyinit) { vm_page_init_page(m, startp, segind, VM_FREEPOOL_LAZYINIT); - } + } else #endif + for (int j = 0; j < pagecount; j++) { + vm_page_init_page(&m[j], + startp + ptoa(j), segind, + VM_NFREEPOOL); + } vmd = VM_DOMAIN(seg->domain); vm_domain_free_lock(vmd); - vm_phys_enqueue_contig(m, pagecount); +#ifdef VM_FREEPOOL_LAZYINIT + if (lazyinit) + vm_phys_enqueue_contig(m, VM_FREEPOOL_LAZYINIT, + pagecount); + else +#endif + vm_phys_enqueue_contig(m, VM_FREEPOOL_DEFAULT, + pagecount); vm_domain_free_unlock(vmd); vm_domain_freecnt_inc(vmd, pagecount); vm_cnt.v_page_count += (u_int)pagecount; Index: sys/vm/vm_phys.h =================================================================== --- sys/vm/vm_phys.h +++ sys/vm/vm_phys.h @@ -66,7 +66,7 @@ int vm_phys_alloc_npages(int domain, int pool, int npages, vm_page_t ma[]); vm_page_t vm_phys_alloc_pages(int domain, int pool, int order); int vm_phys_domain_match(int prefer, vm_paddr_t low, vm_paddr_t high); -void vm_phys_enqueue_contig(vm_page_t m, u_long npages); +void vm_phys_enqueue_contig(vm_page_t m, int pool, u_long npages); int vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end, vm_memattr_t memattr); void vm_phys_fictitious_unreg_range(vm_paddr_t start, vm_paddr_t end); Index: sys/vm/vm_phys.c =================================================================== --- sys/vm/vm_phys.c +++ sys/vm/vm_phys.c @@ -690,6 +690,7 @@ int tail) { vm_page_t m_buddy; + int pool = m->pool; while (oind > order) { oind--; @@ -697,19 +698,24 @@ KASSERT(m_buddy->order == VM_NFREEORDER, ("vm_phys_split_pages: page %p has unexpected order %d", m_buddy, m_buddy->order)); + KASSERT(m_buddy->pool == VM_NFREEPOOL, + ("vm_phys_split_pages: page %p has unexpected pool %d", + m_buddy, m_buddy->pool)); + m_buddy->pool = pool; vm_freelist_add(fl, m_buddy, oind, tail); } } static void -vm_phys_enq_chunk(struct vm_freelist *fl, vm_page_t m, int order, int tail) +vm_phys_enq_chunk(struct vm_freelist *fl, vm_page_t m, int order, int pool, + int tail) { KASSERT(order >= 0 && order < VM_NFREEORDER, ("%s: invalid order %d", __func__, order)); vm_freelist_add(fl, m, order, tail); #ifdef VM_FREEPOOL_LAZYINIT - if (__predict_false(m->pool == VM_FREEPOOL_LAZYINIT)) { + if (__predict_false(pool == VM_FREEPOOL_LAZYINIT)) { vm_page_t m_next; vm_paddr_t pa; int npages; @@ -721,8 +727,9 @@ vm_page_init_page(m_next, pa, m->segind, VM_FREEPOOL_LAZYINIT); } - } + } else #endif + m->pool = pool; } /* @@ -738,7 +745,8 @@ * The physical page m's buddy must not be free. */ static void -vm_phys_enq_beg(vm_page_t m, u_int npages, struct vm_freelist *fl, int tail) +vm_phys_enq_beg(vm_page_t m, u_int npages, struct vm_freelist *fl, int pool, + int tail) { int order; @@ -754,7 +762,7 @@ order = ilog2(npages); KASSERT(order < VM_NFREEORDER, ("%s: order %d is out of range", __func__, order)); - vm_phys_enq_chunk(fl, m, order, tail); + vm_phys_enq_chunk(fl, m, order, pool, tail); m += 1 << order; npages -= 1 << order; } @@ -774,7 +782,8 @@ * parameter m. Otherwise, the physical page m's buddy must not be free. */ static vm_page_t -vm_phys_enq_range(vm_page_t m, u_int npages, struct vm_freelist *fl, int tail) +vm_phys_enq_range(vm_page_t m, u_int npages, struct vm_freelist *fl, int pool, + int tail) { int order; @@ -788,7 +797,7 @@ ("vm_phys_enq_range: page %p has unexpected order %d", m, m->order)); order = ffs(npages) - 1; - vm_phys_enq_chunk(fl, m, order, tail); + vm_phys_enq_chunk(fl, m, order, pool, tail); m += 1 << order; npages -= 1 << order; } @@ -796,33 +805,32 @@ } /* - * Set the pool for a contiguous, power of two-sized set of physical pages. + * Complete initialization a contiguous, power of two-sized set of physical + * pages. * * If the pages currently belong to the lazy init pool, then the corresponding * page structures must be initialized. In this case it is assumed that the * first page in the run has already been initialized. */ static void -vm_phys_set_pool(int pool, vm_page_t m, int order) +vm_phys_finish_init(vm_page_t m, int order) { #ifdef VM_FREEPOOL_LAZYINIT if (__predict_false(m->pool == VM_FREEPOOL_LAZYINIT)) { vm_paddr_t pa; int segind; - m->pool = pool; + m->pool = VM_NFREEPOOL; TSENTER(); pa = m->phys_addr + PAGE_SIZE; segind = m->segind; for (vm_page_t m_tmp = m + 1; m_tmp < &m[1 << order]; m_tmp++, pa += PAGE_SIZE) - vm_page_init_page(m_tmp, pa, segind, pool); + vm_page_init_page(m_tmp, pa, segind, VM_NFREEPOOL); TSEXIT(); - } else + } #endif - for (vm_page_t m_tmp = m; m_tmp < &m[1 << order]; m_tmp++) - m_tmp->pool = pool; } /* @@ -833,7 +841,8 @@ * The returned pages may not be physically contiguous. However, in contrast * to performing multiple, back-to-back calls to vm_phys_alloc_pages(..., 0), * calling this function once to allocate the desired number of pages will - * avoid wasted time in vm_phys_split_pages(). + * avoid wasted time in vm_phys_split_pages(). Sets the pool field for + * every allocated page. * * The free page queues for the specified domain must be locked. */ @@ -862,14 +871,18 @@ vm_freelist_rem(fl, m, oind); avail = i + (1 << oind); end = imin(npages, avail); - while (i < end) + ma[i++] = m++; + while (i < end) { + m->pool = pool; ma[i++] = m++; + } if (i == npages) { /* * Return excess pages to fl. Its order * [0, oind) queues are empty. */ - vm_phys_enq_range(m, avail - i, fl, 1); + vm_phys_enq_range(m, avail - i, fl, + pool, 1); return (npages); } } @@ -881,11 +894,14 @@ while ((m = TAILQ_FIRST(&alt[oind].pl)) != NULL) { vm_freelist_rem(alt, m, oind); - vm_phys_set_pool(pool, m, oind); + vm_phys_finish_init(m, oind); avail = i + (1 << oind); end = imin(npages, avail); - while (i < end) + do { + m->pool = pool; ma[i++] = m++; + } while (i < end); + if (i == npages) { /* * Return excess pages to fl. @@ -893,7 +909,7 @@ * are empty. */ vm_phys_enq_range(m, avail - i, - fl, 1); + fl, pool, 1); return (npages); } } @@ -905,7 +921,7 @@ /* * Allocate a contiguous, power of two-sized set of physical pages - * from the free lists. + * from the free lists. Sets the pool field in the first page only. * * The free page queues must be locked. */ @@ -926,7 +942,8 @@ /* * Allocate a contiguous, power of two-sized set of physical pages from the * specified free list. The free list must be specified using one of the - * manifest constants VM_FREELIST_*. + * manifest constants VM_FREELIST_*. Sets the pool field in the first page + * only. * * The free page queues must be locked. */ @@ -959,6 +976,7 @@ m = TAILQ_FIRST(&fl[oind].pl); if (m != NULL) { vm_freelist_rem(fl, m, oind); + m->pool = pool; /* The order [order, oind) queues are empty. */ vm_phys_split_pages(m, oind, fl, order, 1); return (m); @@ -977,7 +995,8 @@ m = TAILQ_FIRST(&alt[oind].pl); if (m != NULL) { vm_freelist_rem(alt, m, oind); - vm_phys_set_pool(pool, m, oind); + vm_phys_finish_init(m, oind); + m->pool = pool; /* The order [order, oind) queues are empty. */ vm_phys_split_pages(m, oind, fl, order, 1); return (m); @@ -1197,7 +1216,8 @@ } /* - * Free a contiguous, power of two-sized set of physical pages. + * Free a contiguous, power of two-sized set of physical pages. Assumes that + * only the first page has a vaild pool field. * * The free page queues must be locked. */ @@ -1208,11 +1228,12 @@ struct vm_phys_seg *seg; vm_paddr_t pa; vm_page_t m_buddy; + int pool = m->pool; KASSERT(m->order == VM_NFREEORDER, ("vm_phys_free_pages: page %p has unexpected order %d", m, m->order)); - KASSERT(vm_phys_pool_valid(m->pool), + KASSERT(vm_phys_pool_valid(pool), ("vm_phys_free_pages: page %p has unexpected pool %d", m, m->pool)); KASSERT(order < VM_NFREEORDER, @@ -1220,6 +1241,7 @@ seg = &vm_phys_segs[m->segind]; vm_domain_free_assert_locked(VM_DOMAIN(seg->domain)); if (order < VM_NFREEORDER - 1) { + vm_page_t m_start = m; pa = VM_PAGE_TO_PHYS(m); do { pa ^= ((vm_paddr_t)1 << (PAGE_SHIFT + order)); @@ -1230,14 +1252,18 @@ break; fl = (*seg->free_queues)[m_buddy->pool]; vm_freelist_rem(fl, m_buddy, order); - if (m_buddy->pool != m->pool) - vm_phys_set_pool(m->pool, m_buddy, order); + vm_phys_finish_init(m_buddy, order); + m_buddy->pool = VM_NFREEPOOL; order++; pa &= ~(((vm_paddr_t)1 << (PAGE_SHIFT + order)) - 1); m = vm_phys_seg_paddr_to_vm_page(seg, pa); } while (order < VM_NFREEORDER - 1); + if (m != m_start) { + m_start->pool = VM_NFREEPOOL; + m->pool = pool; + } } - fl = (*seg->free_queues)[m->pool]; + fl = (*seg->free_queues)[pool]; vm_freelist_add(fl, m, order, 1); } @@ -1290,11 +1316,12 @@ VM_ALLOC_NORMAL, 1 << oind); if (unlocked) vm_domain_free_unlock(vmd); - vm_phys_set_pool(VM_FREEPOOL_DEFAULT, m, oind); + vm_phys_finish_init(m, oind); if (unlocked) { vm_domain_freecnt_inc(vmd, 1 << oind); vm_domain_free_lock(vmd); } + m->pool = VM_FREEPOOL_DEFAULT; vm_phys_free_pages(m, oind); } } @@ -1344,12 +1371,12 @@ /* * Free a contiguous, arbitrarily sized set of physical pages, without - * merging across set boundaries. + * merging across set boundaries. Assumes no pages have a valid pool field. * * The free page queues must be locked. */ void -vm_phys_enqueue_contig(vm_page_t m, u_long npages) +vm_phys_enqueue_contig(vm_page_t m, int pool, u_long npages) { struct vm_freelist *fl; struct vm_phys_seg *seg; @@ -1363,14 +1390,15 @@ */ vm_domain_free_assert_locked(vm_pagequeue_domain(m)); seg = &vm_phys_segs[m->segind]; - fl = (*seg->free_queues)[m->pool]; + fl = (*seg->free_queues)[pool]; m_end = m + npages; /* Free blocks of increasing size. */ lo = atop(VM_PAGE_TO_PHYS(m)); if (m < m_end && (diff = lo ^ (lo + npages - 1)) != 0) { order = min(ilog2(diff), VM_NFREEORDER - 1); - m = vm_phys_enq_range(m, roundup2(lo, 1 << order) - lo, fl, 1); + m = vm_phys_enq_range(m, roundup2(lo, 1 << order) - lo, fl, + pool, 1); } /* Free blocks of maximum size. */ @@ -1379,15 +1407,16 @@ KASSERT(seg == &vm_phys_segs[m->segind], ("%s: page range [%p,%p) spans multiple segments", __func__, m_end - npages, m)); - vm_phys_enq_chunk(fl, m, order, 1); + vm_phys_enq_chunk(fl, m, order, pool, 1); m += 1 << order; } /* Free blocks of diminishing size. */ - vm_phys_enq_beg(m, m_end - m, fl, 1); + vm_phys_enq_beg(m, m_end - m, fl, pool, 1); } /* * Free a contiguous, arbitrarily sized set of physical pages. + * Assumes that every page has the same, valid, pool field value. * * The free page queues must be locked. */ @@ -1397,13 +1426,20 @@ vm_paddr_t lo; vm_page_t m_start, m_end; unsigned max_order, order_start, order_end; + int pool = m->pool; + + KASSERT(vm_phys_pool_valid(pool), + ("%s: page %p has unexpected pool %d", __func__, + m, m->pool)); vm_domain_free_assert_locked(vm_pagequeue_domain(m)); lo = atop(VM_PAGE_TO_PHYS(m)); max_order = min(ilog2(lo ^ (lo + npages)), VM_NFREEORDER - 1); - - m_start = m; + m_end = m + npages; + for (m_start = m; m < m_end; m++) + m->pool = VM_NFREEPOOL; + m = m_start; order_start = ffsll(lo) - 1; if (order_start < max_order) m_start += 1 << order_start; @@ -1416,11 +1452,15 @@ * end of the range last. */ if (m_start < m_end) - vm_phys_enqueue_contig(m_start, m_end - m_start); - if (order_start < max_order) + vm_phys_enqueue_contig(m_start, pool, m_end - m_start); + if (order_start < max_order) { + m->pool = pool; vm_phys_free_pages(m, order_start); - if (order_end < max_order) + } + if (order_end < max_order) { + m_end->pool = pool; vm_phys_free_pages(m_end, order_end); + } } /* @@ -1474,7 +1514,7 @@ struct vm_phys_seg *seg; vm_paddr_t pa_half; vm_page_t m, m_set, m_tmp; - int order; + int order, pool; seg = vm_phys_paddr_to_seg(pa); vm_domain_free_assert_locked(VM_DOMAIN(seg->domain)); @@ -1515,7 +1555,8 @@ * is larger than a page, shrink "m_set" by returning the half * of "m_set" that does not contain "m" to the free lists. */ - fl = (*seg->free_queues)[m_set->pool]; + pool = m_set->pool; + fl = (*seg->free_queues)[pool]; order = m_set->order; vm_freelist_rem(fl, m_set, order); while (order > 0) { @@ -1527,8 +1568,10 @@ m_tmp = m_set; m_set = vm_phys_seg_paddr_to_vm_page(seg, pa_half); } + m_tmp->pool = pool; vm_freelist_add(fl, m_tmp, order, 0); } + m_set->pool = pool; KASSERT(m_set == m, ("vm_phys_unfree_page: fatal inconsistency")); return (true); } @@ -1668,7 +1711,8 @@ * alignment of the first physical page in the set. If the given value * "boundary" is non-zero, then the set of physical pages cannot cross * any physical address boundary that is a multiple of that value. Both - * "alignment" and "boundary" must be a power of two. + * "alignment" and "boundary" must be a power of two. Sets the pool + * field in every allocated page. */ vm_page_t vm_phys_alloc_contig(int domain, u_long npages, vm_paddr_t low, vm_paddr_t high, @@ -1727,14 +1771,17 @@ fl = (*queues)[m->pool]; oind = m->order; vm_freelist_rem(fl, m, oind); - if (m->pool != VM_FREEPOOL_DEFAULT) - vm_phys_set_pool(VM_FREEPOOL_DEFAULT, m, oind); + vm_phys_finish_init(m, oind); } /* Return excess pages to the free lists. */ fl = (*queues)[VM_FREEPOOL_DEFAULT]; - vm_phys_enq_range(&m_run[npages], m - &m_run[npages], fl, 0); + vm_phys_enq_range(&m_run[npages], m - &m_run[npages], fl, + VM_FREEPOOL_DEFAULT, 0); /* Return page verified to satisfy conditions of request. */ + for (m = m_run; m < &m_run[npages]; m++) + m->pool = VM_FREEPOOL_DEFAULT; + pa_start = VM_PAGE_TO_PHYS(m_run); KASSERT(low <= pa_start, ("memory allocated below minimum requested range")); Index: sys/vm/vm_reserv.c =================================================================== --- sys/vm/vm_reserv.c +++ sys/vm/vm_reserv.c @@ -889,30 +889,35 @@ static void vm_reserv_break(vm_reserv_t rv) { - int hi, lo, pos; + int pool, pos, pos0, pos1; vm_reserv_assert_locked(rv); CTR5(KTR_VM, "%s: rv %p object %p popcnt %d inpartpop %d", __FUNCTION__, rv, rv->object, rv->popcnt, rv->inpartpopq); vm_reserv_remove(rv); rv->pages->psind = 0; - hi = lo = -1; - pos = 0; - for (;;) { - bit_ff_at(rv->popmap, pos, VM_LEVEL_0_NPAGES, lo != hi, &pos); - if (lo == hi) { - if (pos == -1) - break; - lo = pos; - continue; - } + pool = rv->pages->pool; + rv->pages->pool = VM_NFREEPOOL; + pos0 = bit_test(rv->popmap, 0) ? -1 : 0; + pos1 = -1 - pos0; + for (pos = 0; pos < VM_LEVEL_0_NPAGES; ) { + /* Find the first different bit after pos. */ + bit_ff_at(rv->popmap, pos + 1, VM_LEVEL_0_NPAGES, + pos1 < pos0, &pos); if (pos == -1) pos = VM_LEVEL_0_NPAGES; - hi = pos; + if (pos0 <= pos1) { + /* Set pool for pages from pos1 to pos. */ + pos0 = pos1; + while (pos0 < pos) + rv->pages[pos0++].pool = pool; + continue; + } + /* Free unused pages from pos0 to pos. */ + pos1 = pos; vm_domain_free_lock(VM_DOMAIN(rv->domain)); - vm_phys_enqueue_contig(&rv->pages[lo], hi - lo); + vm_phys_enqueue_contig(&rv->pages[pos0], pool, pos1 - pos0); vm_domain_free_unlock(VM_DOMAIN(rv->domain)); - lo = hi; } bit_nclear(rv->popmap, 0, VM_LEVEL_0_NPAGES - 1); rv->popcnt = 0;