xref: /netbsd-src/sys/uvm/uvm_physseg.c (revision ecaf2f40636d722d955675482a7dee48814b8405)
1*ecaf2f40Stnn /* $NetBSD: uvm_physseg.c,v 1.20 2024/01/13 09:44:42 tnn Exp $ */
207acf3c0Scherry 
307acf3c0Scherry /*
407acf3c0Scherry  * Copyright (c) 1997 Charles D. Cranor and Washington University.
507acf3c0Scherry  * Copyright (c) 1991, 1993, The Regents of the University of California.
607acf3c0Scherry  *
707acf3c0Scherry  * All rights reserved.
807acf3c0Scherry  *
907acf3c0Scherry  * This code is derived from software contributed to Berkeley by
1007acf3c0Scherry  * The Mach Operating System project at Carnegie-Mellon University.
1107acf3c0Scherry  *
1207acf3c0Scherry  * Redistribution and use in source and binary forms, with or without
1307acf3c0Scherry  * modification, are permitted provided that the following conditions
1407acf3c0Scherry  * are met:
1507acf3c0Scherry  * 1. Redistributions of source code must retain the above copyright
1607acf3c0Scherry  *    notice, this list of conditions and the following disclaimer.
1707acf3c0Scherry  * 2. Redistributions in binary form must reproduce the above copyright
1807acf3c0Scherry  *    notice, this list of conditions and the following disclaimer in the
1907acf3c0Scherry  *    documentation and/or other materials provided with the distribution.
2007acf3c0Scherry  * 3. Neither the name of the University nor the names of its contributors
2107acf3c0Scherry  *    may be used to endorse or promote products derived from this software
2207acf3c0Scherry  *    without specific prior written permission.
2307acf3c0Scherry  *
2407acf3c0Scherry  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2507acf3c0Scherry  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2607acf3c0Scherry  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2707acf3c0Scherry  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2807acf3c0Scherry  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2907acf3c0Scherry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3007acf3c0Scherry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3107acf3c0Scherry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3207acf3c0Scherry  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3307acf3c0Scherry  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3407acf3c0Scherry  * SUCH DAMAGE.
3507acf3c0Scherry  *
3607acf3c0Scherry  *	@(#)vm_page.h   7.3 (Berkeley) 4/21/91
3707acf3c0Scherry  * from: Id: uvm_page.h,v 1.1.2.6 1998/02/04 02:31:42 chuck Exp
3807acf3c0Scherry  *
3907acf3c0Scherry  *
4007acf3c0Scherry  * Copyright (c) 1987, 1990 Carnegie-Mellon University.
4107acf3c0Scherry  * All rights reserved.
4207acf3c0Scherry  *
4307acf3c0Scherry  * Permission to use, copy, modify and distribute this software and
4407acf3c0Scherry  * its documentation is hereby granted, provided that both the copyright
4507acf3c0Scherry  * notice and this permission notice appear in all copies of the
4607acf3c0Scherry  * software, derivative works or modified versions, and any portions
4707acf3c0Scherry  * thereof, and that both notices appear in supporting documentation.
4807acf3c0Scherry  *
4907acf3c0Scherry  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
5007acf3c0Scherry  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
5107acf3c0Scherry  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
5207acf3c0Scherry  *
5307acf3c0Scherry  * Carnegie Mellon requests users of this software to return to
5407acf3c0Scherry  *
5507acf3c0Scherry  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
5607acf3c0Scherry  *  School of Computer Science
5707acf3c0Scherry  *  Carnegie Mellon University
5807acf3c0Scherry  *  Pittsburgh PA 15213-3890
5907acf3c0Scherry  *
6007acf3c0Scherry  * any improvements or extensions that they make and grant Carnegie the
6107acf3c0Scherry  * rights to redistribute these changes.
6207acf3c0Scherry  */
6307acf3c0Scherry 
6407acf3c0Scherry /*
6507acf3c0Scherry  * Consolidated API from uvm_page.c and others.
6607acf3c0Scherry  * Consolidated and designed by Cherry G. Mathew <cherry@zyx.in>
6707acf3c0Scherry  * rbtree(3) backing implementation by:
6807acf3c0Scherry  * Santhosh N. Raju <santhosh.raju@gmail.com>
6907acf3c0Scherry  */
7007acf3c0Scherry 
7107acf3c0Scherry #ifdef _KERNEL_OPT
7207acf3c0Scherry #include "opt_uvm.h"
7307acf3c0Scherry #endif
7407acf3c0Scherry 
7507acf3c0Scherry #include <sys/param.h>
7607acf3c0Scherry #include <sys/types.h>
7707acf3c0Scherry #include <sys/extent.h>
7807acf3c0Scherry #include <sys/kmem.h>
7907acf3c0Scherry 
8007acf3c0Scherry #include <uvm/uvm.h>
8107acf3c0Scherry #include <uvm/uvm_page.h>
8207acf3c0Scherry #include <uvm/uvm_param.h>
8307acf3c0Scherry #include <uvm/uvm_pdpolicy.h>
8407acf3c0Scherry #include <uvm/uvm_physseg.h>
8507acf3c0Scherry 
8607acf3c0Scherry /*
8707acf3c0Scherry  * uvm_physseg: describes one segment of physical memory
8807acf3c0Scherry  */
8907acf3c0Scherry struct uvm_physseg {
909887ecbeSad 	/* used during RB tree lookup for PHYS_TO_VM_PAGE(). */
91128aefc0Sad #if defined(UVM_HOTPLUG)
9207acf3c0Scherry 	struct  rb_node rb_node;	/* tree information */
93128aefc0Sad #endif
9407acf3c0Scherry 	paddr_t	start;			/* PF# of first page in segment */
9507acf3c0Scherry 	paddr_t	end;			/* (PF# of last page in segment) + 1 */
969887ecbeSad 	struct	vm_page *pgs;		/* vm_page structures (from start) */
979887ecbeSad 
989887ecbeSad 	/* less performance sensitive fields. */
9907acf3c0Scherry 	paddr_t	avail_start;		/* PF# of first free page in segment */
10007acf3c0Scherry 	paddr_t	avail_end;		/* (PF# of last free page in segment) +1  */
10107acf3c0Scherry 	struct  extent *ext;		/* extent(9) structure to manage pgs[] */
10207acf3c0Scherry 	int	free_list;		/* which free list they belong on */
103*ecaf2f40Stnn 	u_long	start_hint;		/* start looking for free pages here */
10407acf3c0Scherry #ifdef __HAVE_PMAP_PHYSSEG
10507acf3c0Scherry 	struct	pmap_physseg pmseg;	/* pmap specific (MD) data */
10607acf3c0Scherry #endif
10707acf3c0Scherry };
10807acf3c0Scherry 
10907acf3c0Scherry /*
11007acf3c0Scherry  * These functions are reserved for uvm(9) internal use and are not
11107acf3c0Scherry  * exported in the header file uvm_physseg.h
11207acf3c0Scherry  *
11307acf3c0Scherry  * Thus they are redefined here.
11407acf3c0Scherry  */
11507acf3c0Scherry void uvm_physseg_init_seg(uvm_physseg_t, struct vm_page *);
11607acf3c0Scherry void uvm_physseg_seg_chomp_slab(uvm_physseg_t, struct vm_page *, size_t);
11707acf3c0Scherry 
11807acf3c0Scherry /* returns a pgs array */
11907acf3c0Scherry struct vm_page *uvm_physseg_seg_alloc_from_slab(uvm_physseg_t, size_t);
12007acf3c0Scherry 
12107acf3c0Scherry #if defined(UVM_HOTPLUG) /* rbtree impementation */
12207acf3c0Scherry 
12307acf3c0Scherry #define		HANDLE_TO_PHYSSEG_NODE(h)	((struct uvm_physseg *)(h))
12407acf3c0Scherry #define		PHYSSEG_NODE_TO_HANDLE(u)	((uvm_physseg_t)(u))
12507acf3c0Scherry 
12607acf3c0Scherry struct uvm_physseg_graph {
12707acf3c0Scherry 	struct rb_tree rb_tree;		/* Tree for entries */
12807acf3c0Scherry 	int            nentries;	/* Number of entries */
1299887ecbeSad } __aligned(COHERENCY_UNIT);
13007acf3c0Scherry 
1319887ecbeSad static struct uvm_physseg_graph uvm_physseg_graph __read_mostly;
13207acf3c0Scherry 
13307acf3c0Scherry /*
13407acf3c0Scherry  * Note on kmem(9) allocator usage:
13507acf3c0Scherry  * We take the conservative approach that plug/unplug are allowed to
13607acf3c0Scherry  * fail in high memory stress situations.
13707acf3c0Scherry  *
13807acf3c0Scherry  * We want to avoid re-entrant situations in which one plug/unplug
13907acf3c0Scherry  * operation is waiting on a previous one to complete, since this
14007acf3c0Scherry  * makes the design more complicated than necessary.
14107acf3c0Scherry  *
14207acf3c0Scherry  * We may review this and change its behaviour, once the use cases
14307acf3c0Scherry  * become more obvious.
14407acf3c0Scherry  */
14507acf3c0Scherry 
14607acf3c0Scherry /*
14707acf3c0Scherry  * Special alloc()/free() functions for boot time support:
14807acf3c0Scherry  * We assume that alloc() at boot time is only for new 'vm_physseg's
14907acf3c0Scherry  * This allows us to use a static array for memory allocation at boot
15007acf3c0Scherry  * time. Thus we avoid using kmem(9) which is not ready at this point
15107acf3c0Scherry  * in boot.
15207acf3c0Scherry  *
15307acf3c0Scherry  * After kmem(9) is ready, we use it. We currently discard any free()s
15407acf3c0Scherry  * to this static array, since the size is small enough to be a
15507acf3c0Scherry  * trivial waste on all architectures we run on.
15607acf3c0Scherry  */
15707acf3c0Scherry 
15807acf3c0Scherry static size_t nseg = 0;
15907acf3c0Scherry static struct uvm_physseg uvm_physseg[VM_PHYSSEG_MAX];
16007acf3c0Scherry 
16107acf3c0Scherry static void *
uvm_physseg_alloc(size_t sz)16207acf3c0Scherry uvm_physseg_alloc(size_t sz)
16307acf3c0Scherry {
16407acf3c0Scherry 	/*
16507acf3c0Scherry 	 * During boot time, we only support allocating vm_physseg
16607acf3c0Scherry 	 * entries from the static array.
16707acf3c0Scherry 	 * We need to assert for this.
16807acf3c0Scherry 	 */
16907acf3c0Scherry 
17007acf3c0Scherry 	if (__predict_false(uvm.page_init_done == false)) {
17107acf3c0Scherry 		if (sz % sizeof(struct uvm_physseg))
17207acf3c0Scherry 			panic("%s: tried to alloc size other than multiple"
17307acf3c0Scherry 			    " of struct uvm_physseg at boot\n", __func__);
17407acf3c0Scherry 
17507acf3c0Scherry 		size_t n = sz / sizeof(struct uvm_physseg);
17607acf3c0Scherry 		nseg += n;
17707acf3c0Scherry 
178a83cdbecSriastradh 		KASSERT(nseg > 0);
179a83cdbecSriastradh 		KASSERT(nseg <= VM_PHYSSEG_MAX);
18007acf3c0Scherry 
18107acf3c0Scherry 		return &uvm_physseg[nseg - n];
18207acf3c0Scherry 	}
18307acf3c0Scherry 
18407acf3c0Scherry 	return kmem_zalloc(sz, KM_NOSLEEP);
18507acf3c0Scherry }
18607acf3c0Scherry 
18707acf3c0Scherry static void
uvm_physseg_free(void * p,size_t sz)18807acf3c0Scherry uvm_physseg_free(void *p, size_t sz)
18907acf3c0Scherry {
19007acf3c0Scherry 	/*
19107acf3c0Scherry 	 * This is a bit tricky. We do allow simulation of free()
19207acf3c0Scherry 	 * during boot (for eg: when MD code is "steal"ing memory,
19307acf3c0Scherry 	 * and the segment has been exhausted (and thus needs to be
19407acf3c0Scherry 	 * free() - ed.
19507acf3c0Scherry 	 * free() also complicates things because we leak the
19607acf3c0Scherry 	 * free(). Therefore calling code can't assume that free()-ed
19707acf3c0Scherry 	 * memory is available for alloc() again, at boot time.
19807acf3c0Scherry 	 *
19907acf3c0Scherry 	 * Thus we can't explicitly disallow free()s during
20007acf3c0Scherry 	 * boot time. However, the same restriction for alloc()
20107acf3c0Scherry 	 * applies to free(). We only allow uvm_physseg related free()s
20207acf3c0Scherry 	 * via this function during boot time.
20307acf3c0Scherry 	 */
20407acf3c0Scherry 
20507acf3c0Scherry 	if (__predict_false(uvm.page_init_done == false)) {
20607acf3c0Scherry 		if (sz % sizeof(struct uvm_physseg))
20707acf3c0Scherry 			panic("%s: tried to free size other than struct uvm_physseg"
20807acf3c0Scherry 			    " at boot\n", __func__);
20907acf3c0Scherry 
21007acf3c0Scherry 	}
21107acf3c0Scherry 
21207acf3c0Scherry 	/*
21307acf3c0Scherry 	 * Could have been in a single if(){} block - split for
21407acf3c0Scherry 	 * clarity
21507acf3c0Scherry 	 */
21607acf3c0Scherry 
21707acf3c0Scherry 	if ((struct uvm_physseg *)p >= uvm_physseg &&
21807acf3c0Scherry 	    (struct uvm_physseg *)p < (uvm_physseg + VM_PHYSSEG_MAX)) {
21907acf3c0Scherry 		if (sz % sizeof(struct uvm_physseg))
22007acf3c0Scherry 			panic("%s: tried to free() other than struct uvm_physseg"
22107acf3c0Scherry 			    " from static array\n", __func__);
22207acf3c0Scherry 
22307acf3c0Scherry 		if ((sz / sizeof(struct uvm_physseg)) >= VM_PHYSSEG_MAX)
22407acf3c0Scherry 			panic("%s: tried to free() the entire static array!", __func__);
22507acf3c0Scherry 		return; /* Nothing to free */
22607acf3c0Scherry 	}
22707acf3c0Scherry 
22807acf3c0Scherry 	kmem_free(p, sz);
22907acf3c0Scherry }
23007acf3c0Scherry 
23107acf3c0Scherry /* XXX: Multi page size */
23207acf3c0Scherry bool
uvm_physseg_plug(paddr_t pfn,size_t pages,uvm_physseg_t * psp)23307acf3c0Scherry uvm_physseg_plug(paddr_t pfn, size_t pages, uvm_physseg_t *psp)
23407acf3c0Scherry {
23507acf3c0Scherry 	int preload;
23607acf3c0Scherry 	size_t slabpages;
23707acf3c0Scherry 	struct uvm_physseg *ps, *current_ps = NULL;
23807acf3c0Scherry 	struct vm_page *slab = NULL, *pgs = NULL;
23907acf3c0Scherry 
24007acf3c0Scherry #ifdef DEBUG
24107acf3c0Scherry 	paddr_t off;
24207acf3c0Scherry 	uvm_physseg_t upm;
24307acf3c0Scherry 	upm = uvm_physseg_find(pfn, &off);
24407acf3c0Scherry 
24507acf3c0Scherry 	ps = HANDLE_TO_PHYSSEG_NODE(upm);
24607acf3c0Scherry 
24707acf3c0Scherry 	if (ps != NULL) /* XXX; do we allow "update" plugs ? */
24807acf3c0Scherry 		return false;
24907acf3c0Scherry #endif
25007acf3c0Scherry 
25107acf3c0Scherry 	/*
25207acf3c0Scherry 	 * do we have room?
25307acf3c0Scherry 	 */
25407acf3c0Scherry 
25507acf3c0Scherry 	ps = uvm_physseg_alloc(sizeof (struct uvm_physseg));
25607acf3c0Scherry 	if (ps == NULL) {
25707acf3c0Scherry 		printf("uvm_page_physload: unable to load physical memory "
25807acf3c0Scherry 		    "segment\n");
25907acf3c0Scherry 		printf("\t%d segments allocated, ignoring 0x%"PRIxPADDR" -> 0x%"PRIxPADDR"\n",
26007acf3c0Scherry 		    VM_PHYSSEG_MAX, pfn, pfn + pages + 1);
26107acf3c0Scherry 		printf("\tincrease VM_PHYSSEG_MAX\n");
26207acf3c0Scherry 		return false;
26307acf3c0Scherry 	}
26407acf3c0Scherry 
26507acf3c0Scherry 	/* span init */
26607acf3c0Scherry 	ps->start = pfn;
26707acf3c0Scherry 	ps->end = pfn + pages;
26807acf3c0Scherry 
26907acf3c0Scherry 	/*
27007acf3c0Scherry 	 * XXX: Ugly hack because uvmexp.npages accounts for only
27107acf3c0Scherry 	 * those pages in the segment included below as well - this
27207acf3c0Scherry 	 * should be legacy and removed.
27307acf3c0Scherry 	 */
27407acf3c0Scherry 
27507acf3c0Scherry 	ps->avail_start = ps->start;
27607acf3c0Scherry 	ps->avail_end = ps->end;
27707acf3c0Scherry 
27807acf3c0Scherry 	/*
27907acf3c0Scherry 	 * check to see if this is a "preload" (i.e. uvm_page_init hasn't been
28007acf3c0Scherry 	 * called yet, so kmem is not available).
28107acf3c0Scherry 	 */
28207acf3c0Scherry 
28307acf3c0Scherry 	preload = 1; /* We are going to assume it is a preload */
28407acf3c0Scherry 
28507acf3c0Scherry 	RB_TREE_FOREACH(current_ps, &(uvm_physseg_graph.rb_tree)) {
28607acf3c0Scherry 		/* If there are non NULL pages then we are not in a preload */
28707acf3c0Scherry 		if (current_ps->pgs != NULL) {
28807acf3c0Scherry 			preload = 0;
28907acf3c0Scherry 			/* Try to scavenge from earlier unplug()s. */
29007acf3c0Scherry 			pgs = uvm_physseg_seg_alloc_from_slab(current_ps, pages);
29107acf3c0Scherry 
29207acf3c0Scherry 			if (pgs != NULL) {
29307acf3c0Scherry 				break;
29407acf3c0Scherry 			}
29507acf3c0Scherry 		}
29607acf3c0Scherry 	}
29707acf3c0Scherry 
29807acf3c0Scherry 
29907acf3c0Scherry 	/*
30007acf3c0Scherry 	 * if VM is already running, attempt to kmem_alloc vm_page structures
30107acf3c0Scherry 	 */
30207acf3c0Scherry 
30307acf3c0Scherry 	if (!preload) {
30407acf3c0Scherry 		if (pgs == NULL) { /* Brand new */
30507acf3c0Scherry 			/* Iteratively try alloc down from uvmexp.npages */
30607acf3c0Scherry 			for (slabpages = (size_t) uvmexp.npages; slabpages >= pages; slabpages--) {
30707acf3c0Scherry 				slab = kmem_zalloc(sizeof *pgs * (long unsigned int)slabpages, KM_NOSLEEP);
30807acf3c0Scherry 				if (slab != NULL)
30907acf3c0Scherry 					break;
31007acf3c0Scherry 			}
31107acf3c0Scherry 
31207acf3c0Scherry 			if (slab == NULL) {
31307acf3c0Scherry 				uvm_physseg_free(ps, sizeof(struct uvm_physseg));
31407acf3c0Scherry 				return false;
31507acf3c0Scherry 			}
31607acf3c0Scherry 
31707acf3c0Scherry 			uvm_physseg_seg_chomp_slab(ps, slab, (size_t) slabpages);
31807acf3c0Scherry 			/* We allocate enough for this plug */
31907acf3c0Scherry 			pgs = uvm_physseg_seg_alloc_from_slab(ps, pages);
32007acf3c0Scherry 
32107acf3c0Scherry 			if (pgs == NULL) {
32207acf3c0Scherry 				printf("unable to uvm_physseg_seg_alloc_from_slab() from backend\n");
32307acf3c0Scherry 				return false;
32407acf3c0Scherry 			}
32507acf3c0Scherry 		} else {
32607acf3c0Scherry 			/* Reuse scavenged extent */
32707acf3c0Scherry 			ps->ext = current_ps->ext;
32807acf3c0Scherry 		}
32907acf3c0Scherry 
33007acf3c0Scherry 		physmem += pages;
33107acf3c0Scherry 		uvmpdpol_reinit();
33207acf3c0Scherry 	} else { /* Boot time - see uvm_page.c:uvm_page_init() */
33307acf3c0Scherry 		pgs = NULL;
33407acf3c0Scherry 		ps->pgs = pgs;
33507acf3c0Scherry 	}
33607acf3c0Scherry 
33707acf3c0Scherry 	/*
33807acf3c0Scherry 	 * now insert us in the proper place in uvm_physseg_graph.rb_tree
33907acf3c0Scherry 	 */
34007acf3c0Scherry 
34107acf3c0Scherry 	current_ps = rb_tree_insert_node(&(uvm_physseg_graph.rb_tree), ps);
34207acf3c0Scherry 	if (current_ps != ps) {
34307acf3c0Scherry 		panic("uvm_page_physload: Duplicate address range detected!");
34407acf3c0Scherry 	}
34507acf3c0Scherry 	uvm_physseg_graph.nentries++;
34607acf3c0Scherry 
34707acf3c0Scherry 	/*
34807acf3c0Scherry 	 * uvm_pagefree() requires the PHYS_TO_VM_PAGE(pgs[i]) on the
34907acf3c0Scherry 	 * newly allocated pgs[] to return the correct value. This is
35007acf3c0Scherry 	 * a bit of a chicken and egg problem, since it needs
35107acf3c0Scherry 	 * uvm_physseg_find() to succeed. For this, the node needs to
35207acf3c0Scherry 	 * be inserted *before* uvm_physseg_init_seg() happens.
35307acf3c0Scherry 	 *
35407acf3c0Scherry 	 * During boot, this happens anyway, since
35507acf3c0Scherry 	 * uvm_physseg_init_seg() is called later on and separately
35607acf3c0Scherry 	 * from uvm_page.c:uvm_page_init().
35707acf3c0Scherry 	 * In the case of hotplug we need to ensure this.
35807acf3c0Scherry 	 */
35907acf3c0Scherry 
36007acf3c0Scherry 	if (__predict_true(!preload))
36107acf3c0Scherry 		uvm_physseg_init_seg(ps, pgs);
36207acf3c0Scherry 
36307acf3c0Scherry 	if (psp != NULL)
36407acf3c0Scherry 		*psp = ps;
36507acf3c0Scherry 
36607acf3c0Scherry 	return true;
36707acf3c0Scherry }
36807acf3c0Scherry 
36907acf3c0Scherry static int
uvm_physseg_compare_nodes(void * ctx,const void * nnode1,const void * nnode2)37007acf3c0Scherry uvm_physseg_compare_nodes(void *ctx, const void *nnode1, const void *nnode2)
37107acf3c0Scherry {
37207acf3c0Scherry 	const struct uvm_physseg *enode1 = nnode1;
37307acf3c0Scherry 	const struct uvm_physseg *enode2 = nnode2;
37407acf3c0Scherry 
37507acf3c0Scherry 	KASSERT(enode1->start < enode2->start || enode1->start >= enode2->end);
37607acf3c0Scherry 	KASSERT(enode2->start < enode1->start || enode2->start >= enode1->end);
37707acf3c0Scherry 
37807acf3c0Scherry 	if (enode1->start < enode2->start)
37907acf3c0Scherry 		return -1;
38007acf3c0Scherry 	if (enode1->start >= enode2->end)
38107acf3c0Scherry 		return 1;
38207acf3c0Scherry 	return 0;
38307acf3c0Scherry }
38407acf3c0Scherry 
38507acf3c0Scherry static int
uvm_physseg_compare_key(void * ctx,const void * nnode,const void * pkey)38607acf3c0Scherry uvm_physseg_compare_key(void *ctx, const void *nnode, const void *pkey)
38707acf3c0Scherry {
38807acf3c0Scherry 	const struct uvm_physseg *enode = nnode;
38907acf3c0Scherry 	const paddr_t pa = *(const paddr_t *) pkey;
39007acf3c0Scherry 
39107acf3c0Scherry 	if(enode->start <= pa && pa < enode->end)
39207acf3c0Scherry 		return 0;
39307acf3c0Scherry 	if (enode->start < pa)
39407acf3c0Scherry 		return -1;
39507acf3c0Scherry 	if (enode->end > pa)
39607acf3c0Scherry 		return 1;
39707acf3c0Scherry 
39807acf3c0Scherry 	return 0;
39907acf3c0Scherry }
40007acf3c0Scherry 
40107acf3c0Scherry static const rb_tree_ops_t uvm_physseg_tree_ops = {
40207acf3c0Scherry 	.rbto_compare_nodes = uvm_physseg_compare_nodes,
40307acf3c0Scherry 	.rbto_compare_key = uvm_physseg_compare_key,
40407acf3c0Scherry 	.rbto_node_offset = offsetof(struct uvm_physseg, rb_node),
40507acf3c0Scherry 	.rbto_context = NULL
40607acf3c0Scherry };
40707acf3c0Scherry 
40807acf3c0Scherry /*
40907acf3c0Scherry  * uvm_physseg_init: init the physmem
41007acf3c0Scherry  *
41107acf3c0Scherry  * => physmem unit should not be in use at this point
41207acf3c0Scherry  */
41307acf3c0Scherry 
41407acf3c0Scherry void
uvm_physseg_init(void)41507acf3c0Scherry uvm_physseg_init(void)
41607acf3c0Scherry {
41707acf3c0Scherry 	rb_tree_init(&(uvm_physseg_graph.rb_tree), &uvm_physseg_tree_ops);
41807acf3c0Scherry 	uvm_physseg_graph.nentries = 0;
41907acf3c0Scherry }
42007acf3c0Scherry 
42107acf3c0Scherry uvm_physseg_t
uvm_physseg_get_next(uvm_physseg_t upm)42207acf3c0Scherry uvm_physseg_get_next(uvm_physseg_t upm)
42307acf3c0Scherry {
42407acf3c0Scherry 	/* next of invalid is invalid, not fatal */
425e6ff351eScherry 	if (uvm_physseg_valid_p(upm) == false)
42607acf3c0Scherry 		return UVM_PHYSSEG_TYPE_INVALID;
42707acf3c0Scherry 
42807acf3c0Scherry 	return (uvm_physseg_t) rb_tree_iterate(&(uvm_physseg_graph.rb_tree), upm,
42907acf3c0Scherry 	    RB_DIR_RIGHT);
43007acf3c0Scherry }
43107acf3c0Scherry 
43207acf3c0Scherry uvm_physseg_t
uvm_physseg_get_prev(uvm_physseg_t upm)43307acf3c0Scherry uvm_physseg_get_prev(uvm_physseg_t upm)
43407acf3c0Scherry {
43507acf3c0Scherry 	/* prev of invalid is invalid, not fatal */
436e6ff351eScherry 	if (uvm_physseg_valid_p(upm) == false)
43707acf3c0Scherry 		return UVM_PHYSSEG_TYPE_INVALID;
43807acf3c0Scherry 
43907acf3c0Scherry 	return (uvm_physseg_t) rb_tree_iterate(&(uvm_physseg_graph.rb_tree), upm,
44007acf3c0Scherry 	    RB_DIR_LEFT);
44107acf3c0Scherry }
44207acf3c0Scherry 
44307acf3c0Scherry uvm_physseg_t
uvm_physseg_get_last(void)44407acf3c0Scherry uvm_physseg_get_last(void)
44507acf3c0Scherry {
44607acf3c0Scherry 	return (uvm_physseg_t) RB_TREE_MAX(&(uvm_physseg_graph.rb_tree));
44707acf3c0Scherry }
44807acf3c0Scherry 
44907acf3c0Scherry uvm_physseg_t
uvm_physseg_get_first(void)45007acf3c0Scherry uvm_physseg_get_first(void)
45107acf3c0Scherry {
45207acf3c0Scherry 	return (uvm_physseg_t) RB_TREE_MIN(&(uvm_physseg_graph.rb_tree));
45307acf3c0Scherry }
45407acf3c0Scherry 
45507acf3c0Scherry paddr_t
uvm_physseg_get_highest_frame(void)45607acf3c0Scherry uvm_physseg_get_highest_frame(void)
45707acf3c0Scherry {
45807acf3c0Scherry 	struct uvm_physseg *ps =
45907acf3c0Scherry 	    (uvm_physseg_t) RB_TREE_MAX(&(uvm_physseg_graph.rb_tree));
46007acf3c0Scherry 
46107acf3c0Scherry 	return ps->end - 1;
46207acf3c0Scherry }
46307acf3c0Scherry 
46407acf3c0Scherry /*
46507acf3c0Scherry  * uvm_page_physunload: unload physical memory and return it to
46607acf3c0Scherry  * caller.
46707acf3c0Scherry  */
46807acf3c0Scherry bool
uvm_page_physunload(uvm_physseg_t upm,int freelist,paddr_t * paddrp)46907acf3c0Scherry uvm_page_physunload(uvm_physseg_t upm, int freelist, paddr_t *paddrp)
47007acf3c0Scherry {
47107acf3c0Scherry 	struct uvm_physseg *seg;
47207acf3c0Scherry 
47307acf3c0Scherry 	if (__predict_true(uvm.page_init_done == true))
47407acf3c0Scherry 		panic("%s: unload attempted after uvm_page_init()\n", __func__);
47507acf3c0Scherry 
47607acf3c0Scherry 	seg = HANDLE_TO_PHYSSEG_NODE(upm);
47707acf3c0Scherry 
47807acf3c0Scherry 	if (seg->free_list != freelist) {
47907acf3c0Scherry 		return false;
48007acf3c0Scherry 	}
48107acf3c0Scherry 
48207acf3c0Scherry 	/*
48307acf3c0Scherry 	 * During cold boot, what we're about to unplug hasn't been
48407acf3c0Scherry 	 * put on the uvm freelist, nor has uvmexp.npages been
48507acf3c0Scherry 	 * updated. (This happens in uvm_page.c:uvm_page_init())
48607acf3c0Scherry 	 *
48707acf3c0Scherry 	 * For hotplug, we assume here that the pages being unloaded
48807acf3c0Scherry 	 * here are completely out of sight of uvm (ie; not on any uvm
48907acf3c0Scherry 	 * lists), and that  uvmexp.npages has been suitably
49007acf3c0Scherry 	 * decremented before we're called.
49107acf3c0Scherry 	 *
49207acf3c0Scherry 	 * XXX: will avail_end == start if avail_start < avail_end?
49307acf3c0Scherry 	 */
49407acf3c0Scherry 
49507acf3c0Scherry 	/* try from front */
49607acf3c0Scherry 	if (seg->avail_start == seg->start &&
49707acf3c0Scherry 	    seg->avail_start < seg->avail_end) {
49807acf3c0Scherry 		*paddrp = ctob(seg->avail_start);
49907acf3c0Scherry 		return uvm_physseg_unplug(seg->avail_start, 1);
50007acf3c0Scherry 	}
50107acf3c0Scherry 
50207acf3c0Scherry 	/* try from rear */
50307acf3c0Scherry 	if (seg->avail_end == seg->end &&
50407acf3c0Scherry 	    seg->avail_start < seg->avail_end) {
50507acf3c0Scherry 		*paddrp = ctob(seg->avail_end - 1);
50607acf3c0Scherry 		return uvm_physseg_unplug(seg->avail_end - 1, 1);
50707acf3c0Scherry 	}
50807acf3c0Scherry 
50907acf3c0Scherry 	return false;
51007acf3c0Scherry }
51107acf3c0Scherry 
51207acf3c0Scherry bool
uvm_page_physunload_force(uvm_physseg_t upm,int freelist,paddr_t * paddrp)51307acf3c0Scherry uvm_page_physunload_force(uvm_physseg_t upm, int freelist, paddr_t *paddrp)
51407acf3c0Scherry {
51507acf3c0Scherry 	struct uvm_physseg *seg;
51607acf3c0Scherry 
51707acf3c0Scherry 	seg = HANDLE_TO_PHYSSEG_NODE(upm);
51807acf3c0Scherry 
51907acf3c0Scherry 	if (__predict_true(uvm.page_init_done == true))
52007acf3c0Scherry 		panic("%s: unload attempted after uvm_page_init()\n", __func__);
52107acf3c0Scherry 	/* any room in this bank? */
52207acf3c0Scherry 	if (seg->avail_start >= seg->avail_end) {
52307acf3c0Scherry 		return false; /* nope */
52407acf3c0Scherry 	}
52507acf3c0Scherry 
52607acf3c0Scherry 	*paddrp = ctob(seg->avail_start);
52707acf3c0Scherry 
52807acf3c0Scherry 	/* Always unplug from front */
52907acf3c0Scherry 	return uvm_physseg_unplug(seg->avail_start, 1);
53007acf3c0Scherry }
53107acf3c0Scherry 
53207acf3c0Scherry 
53307acf3c0Scherry /*
53407acf3c0Scherry  * vm_physseg_find: find vm_physseg structure that belongs to a PA
53507acf3c0Scherry  */
53607acf3c0Scherry uvm_physseg_t
uvm_physseg_find(paddr_t pframe,psize_t * offp)53707acf3c0Scherry uvm_physseg_find(paddr_t pframe, psize_t *offp)
53807acf3c0Scherry {
53907acf3c0Scherry 	struct uvm_physseg * ps = NULL;
54007acf3c0Scherry 
54107acf3c0Scherry 	ps = rb_tree_find_node(&(uvm_physseg_graph.rb_tree), &pframe);
54207acf3c0Scherry 
54307acf3c0Scherry 	if(ps != NULL && offp != NULL)
54407acf3c0Scherry 		*offp = pframe - ps->start;
54507acf3c0Scherry 
54607acf3c0Scherry 	return ps;
54707acf3c0Scherry }
54807acf3c0Scherry 
54907acf3c0Scherry #else  /* UVM_HOTPLUG */
55007acf3c0Scherry 
55107acf3c0Scherry /*
55207acf3c0Scherry  * physical memory config is stored in vm_physmem.
55307acf3c0Scherry  */
55407acf3c0Scherry 
55507acf3c0Scherry #define	VM_PHYSMEM_PTR(i)	(&vm_physmem[i])
55607acf3c0Scherry #if VM_PHYSSEG_MAX == 1
55707acf3c0Scherry #define VM_PHYSMEM_PTR_SWAP(i, j) /* impossible */
55807acf3c0Scherry #else
55907acf3c0Scherry #define VM_PHYSMEM_PTR_SWAP(i, j)					      \
56007acf3c0Scherry 	do { vm_physmem[(i)] = vm_physmem[(j)]; } while (0)
56107acf3c0Scherry #endif
56207acf3c0Scherry 
56307acf3c0Scherry #define		HANDLE_TO_PHYSSEG_NODE(h)	(VM_PHYSMEM_PTR((int)h))
56407acf3c0Scherry #define		PHYSSEG_NODE_TO_HANDLE(u)	((int)((vsize_t) (u - vm_physmem) / sizeof(struct uvm_physseg)))
56507acf3c0Scherry 
566128aefc0Sad /* XXXCDC: uvm.physmem */
567128aefc0Sad static struct uvm_physseg vm_physmem[VM_PHYSSEG_MAX] __read_mostly;
568128aefc0Sad /* XXXCDC: uvm.nphysseg */
569128aefc0Sad static int vm_nphysseg __read_mostly = 0;
57007acf3c0Scherry #define	vm_nphysmem	vm_nphysseg
57107acf3c0Scherry 
57207acf3c0Scherry void
uvm_physseg_init(void)57307acf3c0Scherry uvm_physseg_init(void)
57407acf3c0Scherry {
57507acf3c0Scherry 	/* XXX: Provisioning for rb_tree related init(s) */
57607acf3c0Scherry 	return;
57707acf3c0Scherry }
57807acf3c0Scherry 
57907acf3c0Scherry int
uvm_physseg_get_next(uvm_physseg_t lcv)58007acf3c0Scherry uvm_physseg_get_next(uvm_physseg_t lcv)
58107acf3c0Scherry {
58207acf3c0Scherry 	/* next of invalid is invalid, not fatal */
583e6ff351eScherry 	if (uvm_physseg_valid_p(lcv) == false)
58407acf3c0Scherry 		return UVM_PHYSSEG_TYPE_INVALID;
58507acf3c0Scherry 
58607acf3c0Scherry 	return (lcv + 1);
58707acf3c0Scherry }
58807acf3c0Scherry 
58907acf3c0Scherry int
uvm_physseg_get_prev(uvm_physseg_t lcv)59007acf3c0Scherry uvm_physseg_get_prev(uvm_physseg_t lcv)
59107acf3c0Scherry {
59207acf3c0Scherry 	/* prev of invalid is invalid, not fatal */
593e6ff351eScherry 	if (uvm_physseg_valid_p(lcv) == false)
59407acf3c0Scherry 		return UVM_PHYSSEG_TYPE_INVALID;
59507acf3c0Scherry 
59607acf3c0Scherry 	return (lcv - 1);
59707acf3c0Scherry }
59807acf3c0Scherry 
59907acf3c0Scherry int
uvm_physseg_get_last(void)60007acf3c0Scherry uvm_physseg_get_last(void)
60107acf3c0Scherry {
60207acf3c0Scherry 	return (vm_nphysseg - 1);
60307acf3c0Scherry }
60407acf3c0Scherry 
60507acf3c0Scherry int
uvm_physseg_get_first(void)60607acf3c0Scherry uvm_physseg_get_first(void)
60707acf3c0Scherry {
60807acf3c0Scherry 	return 0;
60907acf3c0Scherry }
61007acf3c0Scherry 
61107acf3c0Scherry paddr_t
uvm_physseg_get_highest_frame(void)61207acf3c0Scherry uvm_physseg_get_highest_frame(void)
61307acf3c0Scherry {
61407acf3c0Scherry 	int lcv;
61507acf3c0Scherry 	paddr_t last = 0;
61607acf3c0Scherry 	struct uvm_physseg *ps;
61707acf3c0Scherry 
61807acf3c0Scherry 	for (lcv = 0; lcv < vm_nphysseg; lcv++) {
61907acf3c0Scherry 		ps = VM_PHYSMEM_PTR(lcv);
62007acf3c0Scherry 		if (last < ps->end)
62107acf3c0Scherry 			last = ps->end;
62207acf3c0Scherry 	}
62307acf3c0Scherry 
62407acf3c0Scherry 	return last;
62507acf3c0Scherry }
62607acf3c0Scherry 
62707acf3c0Scherry 
62807acf3c0Scherry static struct vm_page *
uvm_post_preload_check(void)62907acf3c0Scherry uvm_post_preload_check(void)
63007acf3c0Scherry {
63107acf3c0Scherry 	int preload, lcv;
63207acf3c0Scherry 
63307acf3c0Scherry 	/*
63407acf3c0Scherry 	 * check to see if this is a "preload" (i.e. uvm_page_init hasn't been
63507acf3c0Scherry 	 * called yet, so kmem is not available).
63607acf3c0Scherry 	 */
63707acf3c0Scherry 
63807acf3c0Scherry 	for (lcv = 0 ; lcv < vm_nphysmem ; lcv++) {
63907acf3c0Scherry 		if (VM_PHYSMEM_PTR(lcv)->pgs)
64007acf3c0Scherry 			break;
64107acf3c0Scherry 	}
64207acf3c0Scherry 	preload = (lcv == vm_nphysmem);
64307acf3c0Scherry 
64407acf3c0Scherry 	/*
64507acf3c0Scherry 	 * if VM is already running, attempt to kmem_alloc vm_page structures
64607acf3c0Scherry 	 */
64707acf3c0Scherry 
64807acf3c0Scherry 	if (!preload) {
64907acf3c0Scherry 		panic("Tried to add RAM after uvm_page_init");
65007acf3c0Scherry 	}
65107acf3c0Scherry 
65207acf3c0Scherry 	return NULL;
65307acf3c0Scherry }
65407acf3c0Scherry 
65507acf3c0Scherry /*
65607acf3c0Scherry  * uvm_page_physunload: unload physical memory and return it to
65707acf3c0Scherry  * caller.
65807acf3c0Scherry  */
65907acf3c0Scherry bool
uvm_page_physunload(uvm_physseg_t psi,int freelist,paddr_t * paddrp)66007acf3c0Scherry uvm_page_physunload(uvm_physseg_t psi, int freelist, paddr_t *paddrp)
66107acf3c0Scherry {
66207acf3c0Scherry 	int x;
66307acf3c0Scherry 	struct uvm_physseg *seg;
66407acf3c0Scherry 
66507acf3c0Scherry 	uvm_post_preload_check();
66607acf3c0Scherry 
66707acf3c0Scherry 	seg = VM_PHYSMEM_PTR(psi);
66807acf3c0Scherry 
66907acf3c0Scherry 	if (seg->free_list != freelist) {
67007acf3c0Scherry 		return false;
67107acf3c0Scherry 	}
67207acf3c0Scherry 
67307acf3c0Scherry 	/* try from front */
67407acf3c0Scherry 	if (seg->avail_start == seg->start &&
67507acf3c0Scherry 	    seg->avail_start < seg->avail_end) {
67607acf3c0Scherry 		*paddrp = ctob(seg->avail_start);
67707acf3c0Scherry 		seg->avail_start++;
67807acf3c0Scherry 		seg->start++;
67907acf3c0Scherry 		/* nothing left?   nuke it */
68007acf3c0Scherry 		if (seg->avail_start == seg->end) {
68107acf3c0Scherry 			if (vm_nphysmem == 1)
68207acf3c0Scherry 				panic("uvm_page_physget: out of memory!");
68307acf3c0Scherry 			vm_nphysmem--;
68407acf3c0Scherry 			for (x = psi ; x < vm_nphysmem ; x++)
68507acf3c0Scherry 				/* structure copy */
68607acf3c0Scherry 				VM_PHYSMEM_PTR_SWAP(x, x + 1);
68707acf3c0Scherry 		}
68807acf3c0Scherry 		return (true);
68907acf3c0Scherry 	}
69007acf3c0Scherry 
69107acf3c0Scherry 	/* try from rear */
69207acf3c0Scherry 	if (seg->avail_end == seg->end &&
69307acf3c0Scherry 	    seg->avail_start < seg->avail_end) {
69407acf3c0Scherry 		*paddrp = ctob(seg->avail_end - 1);
69507acf3c0Scherry 		seg->avail_end--;
69607acf3c0Scherry 		seg->end--;
69707acf3c0Scherry 		/* nothing left?   nuke it */
69807acf3c0Scherry 		if (seg->avail_end == seg->start) {
69907acf3c0Scherry 			if (vm_nphysmem == 1)
70007acf3c0Scherry 				panic("uvm_page_physget: out of memory!");
70107acf3c0Scherry 			vm_nphysmem--;
70207acf3c0Scherry 			for (x = psi ; x < vm_nphysmem ; x++)
70307acf3c0Scherry 				/* structure copy */
70407acf3c0Scherry 				VM_PHYSMEM_PTR_SWAP(x, x + 1);
70507acf3c0Scherry 		}
70607acf3c0Scherry 		return (true);
70707acf3c0Scherry 	}
70807acf3c0Scherry 
70907acf3c0Scherry 	return false;
71007acf3c0Scherry }
71107acf3c0Scherry 
71207acf3c0Scherry bool
uvm_page_physunload_force(uvm_physseg_t psi,int freelist,paddr_t * paddrp)71307acf3c0Scherry uvm_page_physunload_force(uvm_physseg_t psi, int freelist, paddr_t *paddrp)
71407acf3c0Scherry {
71507acf3c0Scherry 	int x;
71607acf3c0Scherry 	struct uvm_physseg *seg;
71707acf3c0Scherry 
71807acf3c0Scherry 	uvm_post_preload_check();
71907acf3c0Scherry 
72007acf3c0Scherry 	seg = VM_PHYSMEM_PTR(psi);
72107acf3c0Scherry 
72207acf3c0Scherry 	/* any room in this bank? */
72307acf3c0Scherry 	if (seg->avail_start >= seg->avail_end) {
72407acf3c0Scherry 		return false; /* nope */
72507acf3c0Scherry 	}
72607acf3c0Scherry 
72707acf3c0Scherry 	*paddrp = ctob(seg->avail_start);
72807acf3c0Scherry 	seg->avail_start++;
72907acf3c0Scherry 	/* truncate! */
73007acf3c0Scherry 	seg->start = seg->avail_start;
73107acf3c0Scherry 
73207acf3c0Scherry 	/* nothing left?   nuke it */
73307acf3c0Scherry 	if (seg->avail_start == seg->end) {
73407acf3c0Scherry 		if (vm_nphysmem == 1)
73507acf3c0Scherry 			panic("uvm_page_physget: out of memory!");
73607acf3c0Scherry 		vm_nphysmem--;
73707acf3c0Scherry 		for (x = psi ; x < vm_nphysmem ; x++)
73807acf3c0Scherry 			/* structure copy */
73907acf3c0Scherry 			VM_PHYSMEM_PTR_SWAP(x, x + 1);
74007acf3c0Scherry 	}
74107acf3c0Scherry 	return (true);
74207acf3c0Scherry }
74307acf3c0Scherry 
74407acf3c0Scherry bool
uvm_physseg_plug(paddr_t pfn,size_t pages,uvm_physseg_t * psp)74507acf3c0Scherry uvm_physseg_plug(paddr_t pfn, size_t pages, uvm_physseg_t *psp)
74607acf3c0Scherry {
74707acf3c0Scherry 	int lcv;
74807acf3c0Scherry 	struct vm_page *pgs;
74907acf3c0Scherry 	struct uvm_physseg *ps;
75007acf3c0Scherry 
75107acf3c0Scherry #ifdef DEBUG
75207acf3c0Scherry 	paddr_t off;
75307acf3c0Scherry 	uvm_physseg_t upm;
75407acf3c0Scherry 	upm = uvm_physseg_find(pfn, &off);
75507acf3c0Scherry 
756e6ff351eScherry 	if (uvm_physseg_valid_p(upm)) /* XXX; do we allow "update" plugs ? */
75707acf3c0Scherry 		return false;
75807acf3c0Scherry #endif
75907acf3c0Scherry 
76007acf3c0Scherry 	paddr_t start = pfn;
76107acf3c0Scherry 	paddr_t end = pfn + pages;
76207acf3c0Scherry 	paddr_t avail_start = start;
76307acf3c0Scherry 	paddr_t avail_end = end;
76407acf3c0Scherry 
76507acf3c0Scherry 	if (uvmexp.pagesize == 0)
76607acf3c0Scherry 		panic("uvm_page_physload: page size not set!");
76707acf3c0Scherry 
76807acf3c0Scherry 	/*
76907acf3c0Scherry 	 * do we have room?
77007acf3c0Scherry 	 */
77107acf3c0Scherry 
77207acf3c0Scherry 	if (vm_nphysmem == VM_PHYSSEG_MAX) {
77307acf3c0Scherry 		printf("uvm_page_physload: unable to load physical memory "
77407acf3c0Scherry 		    "segment\n");
77507acf3c0Scherry 		printf("\t%d segments allocated, ignoring 0x%llx -> 0x%llx\n",
77607acf3c0Scherry 		    VM_PHYSSEG_MAX, (long long)start, (long long)end);
77707acf3c0Scherry 		printf("\tincrease VM_PHYSSEG_MAX\n");
77807acf3c0Scherry 		if (psp != NULL)
77907acf3c0Scherry 			*psp = UVM_PHYSSEG_TYPE_INVALID_OVERFLOW;
78007acf3c0Scherry 		return false;
78107acf3c0Scherry 	}
78207acf3c0Scherry 
78307acf3c0Scherry 	/*
78407acf3c0Scherry 	 * check to see if this is a "preload" (i.e. uvm_page_init hasn't been
78507acf3c0Scherry 	 * called yet, so kmem is not available).
78607acf3c0Scherry 	 */
78707acf3c0Scherry 	pgs = uvm_post_preload_check();
78807acf3c0Scherry 
78907acf3c0Scherry 	/*
79007acf3c0Scherry 	 * now insert us in the proper place in vm_physmem[]
79107acf3c0Scherry 	 */
79207acf3c0Scherry 
79307acf3c0Scherry #if (VM_PHYSSEG_STRAT == VM_PSTRAT_RANDOM)
79407acf3c0Scherry 	/* random: put it at the end (easy!) */
79507acf3c0Scherry 	ps = VM_PHYSMEM_PTR(vm_nphysmem);
796a9cac8faScherry 	lcv = vm_nphysmem;
79707acf3c0Scherry #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
79807acf3c0Scherry 	{
79907acf3c0Scherry 		int x;
80007acf3c0Scherry 		/* sort by address for binary search */
80107acf3c0Scherry 		for (lcv = 0 ; lcv < vm_nphysmem ; lcv++)
80207acf3c0Scherry 			if (start < VM_PHYSMEM_PTR(lcv)->start)
80307acf3c0Scherry 				break;
80407acf3c0Scherry 		ps = VM_PHYSMEM_PTR(lcv);
80507acf3c0Scherry 		/* move back other entries, if necessary ... */
80607acf3c0Scherry 		for (x = vm_nphysmem ; x > lcv ; x--)
80707acf3c0Scherry 			/* structure copy */
80807acf3c0Scherry 			VM_PHYSMEM_PTR_SWAP(x, x - 1);
80907acf3c0Scherry 	}
81007acf3c0Scherry #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST)
81107acf3c0Scherry 	{
81207acf3c0Scherry 		int x;
81307acf3c0Scherry 		/* sort by largest segment first */
81407acf3c0Scherry 		for (lcv = 0 ; lcv < vm_nphysmem ; lcv++)
81507acf3c0Scherry 			if ((end - start) >
81607acf3c0Scherry 			    (VM_PHYSMEM_PTR(lcv)->end - VM_PHYSMEM_PTR(lcv)->start))
81707acf3c0Scherry 				break;
81807acf3c0Scherry 		ps = VM_PHYSMEM_PTR(lcv);
81907acf3c0Scherry 		/* move back other entries, if necessary ... */
82007acf3c0Scherry 		for (x = vm_nphysmem ; x > lcv ; x--)
82107acf3c0Scherry 			/* structure copy */
82207acf3c0Scherry 			VM_PHYSMEM_PTR_SWAP(x, x - 1);
82307acf3c0Scherry 	}
82407acf3c0Scherry #else
82507acf3c0Scherry 	panic("uvm_page_physload: unknown physseg strategy selected!");
82607acf3c0Scherry #endif
82707acf3c0Scherry 
82807acf3c0Scherry 	ps->start = start;
82907acf3c0Scherry 	ps->end = end;
83007acf3c0Scherry 	ps->avail_start = avail_start;
83107acf3c0Scherry 	ps->avail_end = avail_end;
83207acf3c0Scherry 
83307acf3c0Scherry 	ps->pgs = pgs;
83407acf3c0Scherry 
83507acf3c0Scherry 	vm_nphysmem++;
83607acf3c0Scherry 
83707acf3c0Scherry 	if (psp != NULL)
83807acf3c0Scherry 		*psp = lcv;
83907acf3c0Scherry 
84007acf3c0Scherry 	return true;
84107acf3c0Scherry }
84207acf3c0Scherry 
84307acf3c0Scherry /*
84407acf3c0Scherry  * when VM_PHYSSEG_MAX is 1, we can simplify these functions
84507acf3c0Scherry  */
84607acf3c0Scherry 
84707acf3c0Scherry #if VM_PHYSSEG_MAX == 1
84807acf3c0Scherry static inline int vm_physseg_find_contig(struct uvm_physseg *, int, paddr_t, psize_t *);
84907acf3c0Scherry #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
85007acf3c0Scherry static inline int vm_physseg_find_bsearch(struct uvm_physseg *, int, paddr_t, psize_t *);
85107acf3c0Scherry #else
85207acf3c0Scherry static inline int vm_physseg_find_linear(struct uvm_physseg *, int, paddr_t, psize_t *);
85307acf3c0Scherry #endif
85407acf3c0Scherry 
85507acf3c0Scherry /*
85607acf3c0Scherry  * vm_physseg_find: find vm_physseg structure that belongs to a PA
85707acf3c0Scherry  */
858128aefc0Sad inline int
uvm_physseg_find(paddr_t pframe,psize_t * offp)85907acf3c0Scherry uvm_physseg_find(paddr_t pframe, psize_t *offp)
86007acf3c0Scherry {
86107acf3c0Scherry 
86207acf3c0Scherry #if VM_PHYSSEG_MAX == 1
86307acf3c0Scherry 	return vm_physseg_find_contig(vm_physmem, vm_nphysseg, pframe, offp);
86407acf3c0Scherry #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
86507acf3c0Scherry 	return vm_physseg_find_bsearch(vm_physmem, vm_nphysseg, pframe, offp);
86607acf3c0Scherry #else
86707acf3c0Scherry 	return vm_physseg_find_linear(vm_physmem, vm_nphysseg, pframe, offp);
86807acf3c0Scherry #endif
86907acf3c0Scherry }
87007acf3c0Scherry 
87107acf3c0Scherry #if VM_PHYSSEG_MAX == 1
87207acf3c0Scherry static inline int
vm_physseg_find_contig(struct uvm_physseg * segs,int nsegs,paddr_t pframe,psize_t * offp)87307acf3c0Scherry vm_physseg_find_contig(struct uvm_physseg *segs, int nsegs, paddr_t pframe, psize_t *offp)
87407acf3c0Scherry {
87507acf3c0Scherry 
87607acf3c0Scherry 	/* 'contig' case */
87707acf3c0Scherry 	if (pframe >= segs[0].start && pframe < segs[0].end) {
87807acf3c0Scherry 		if (offp)
87907acf3c0Scherry 			*offp = pframe - segs[0].start;
88007acf3c0Scherry 		return(0);
88107acf3c0Scherry 	}
88207acf3c0Scherry 	return(-1);
88307acf3c0Scherry }
88407acf3c0Scherry 
88507acf3c0Scherry #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
88607acf3c0Scherry 
88707acf3c0Scherry static inline int
vm_physseg_find_bsearch(struct uvm_physseg * segs,int nsegs,paddr_t pframe,psize_t * offp)88807acf3c0Scherry vm_physseg_find_bsearch(struct uvm_physseg *segs, int nsegs, paddr_t pframe, psize_t *offp)
88907acf3c0Scherry {
89007acf3c0Scherry 	/* binary search for it */
89107acf3c0Scherry 	int	start, len, guess;
89207acf3c0Scherry 
89307acf3c0Scherry 	/*
89407acf3c0Scherry 	 * if try is too large (thus target is less than try) we reduce
89507acf3c0Scherry 	 * the length to trunc(len/2) [i.e. everything smaller than "try"]
89607acf3c0Scherry 	 *
89707acf3c0Scherry 	 * if the try is too small (thus target is greater than try) then
89807acf3c0Scherry 	 * we set the new start to be (try + 1).   this means we need to
89907acf3c0Scherry 	 * reduce the length to (round(len/2) - 1).
90007acf3c0Scherry 	 *
90107acf3c0Scherry 	 * note "adjust" below which takes advantage of the fact that
90207acf3c0Scherry 	 *  (round(len/2) - 1) == trunc((len - 1) / 2)
90307acf3c0Scherry 	 * for any value of len we may have
90407acf3c0Scherry 	 */
90507acf3c0Scherry 
90607acf3c0Scherry 	for (start = 0, len = nsegs ; len != 0 ; len = len / 2) {
90707acf3c0Scherry 		guess = start + (len / 2);	/* try in the middle */
90807acf3c0Scherry 
90907acf3c0Scherry 		/* start past our try? */
91007acf3c0Scherry 		if (pframe >= segs[guess].start) {
91107acf3c0Scherry 			/* was try correct? */
91207acf3c0Scherry 			if (pframe < segs[guess].end) {
91307acf3c0Scherry 				if (offp)
91407acf3c0Scherry 					*offp = pframe - segs[guess].start;
91507acf3c0Scherry 				return guess;            /* got it */
91607acf3c0Scherry 			}
91707acf3c0Scherry 			start = guess + 1;	/* next time, start here */
91807acf3c0Scherry 			len--;			/* "adjust" */
91907acf3c0Scherry 		} else {
92007acf3c0Scherry 			/*
92107acf3c0Scherry 			 * pframe before try, just reduce length of
92207acf3c0Scherry 			 * region, done in "for" loop
92307acf3c0Scherry 			 */
92407acf3c0Scherry 		}
92507acf3c0Scherry 	}
92607acf3c0Scherry 	return(-1);
92707acf3c0Scherry }
92807acf3c0Scherry 
92907acf3c0Scherry #else
93007acf3c0Scherry 
93107acf3c0Scherry static inline int
vm_physseg_find_linear(struct uvm_physseg * segs,int nsegs,paddr_t pframe,psize_t * offp)93207acf3c0Scherry vm_physseg_find_linear(struct uvm_physseg *segs, int nsegs, paddr_t pframe, psize_t *offp)
93307acf3c0Scherry {
93407acf3c0Scherry 	/* linear search for it */
93507acf3c0Scherry 	int	lcv;
93607acf3c0Scherry 
93707acf3c0Scherry 	for (lcv = 0; lcv < nsegs; lcv++) {
93807acf3c0Scherry 		if (pframe >= segs[lcv].start &&
93907acf3c0Scherry 		    pframe < segs[lcv].end) {
94007acf3c0Scherry 			if (offp)
94107acf3c0Scherry 				*offp = pframe - segs[lcv].start;
94207acf3c0Scherry 			return(lcv);		   /* got it */
94307acf3c0Scherry 		}
94407acf3c0Scherry 	}
94507acf3c0Scherry 	return(-1);
94607acf3c0Scherry }
94707acf3c0Scherry #endif
94807acf3c0Scherry #endif /* UVM_HOTPLUG */
94907acf3c0Scherry 
950128aefc0Sad /*
951128aefc0Sad  * PHYS_TO_VM_PAGE: find vm_page for a PA.  used by MI code to get vm_pages
952128aefc0Sad  * back from an I/O mapping (ugh!).  used in some MD code as well.  it can
953128aefc0Sad  * be prominent in flamegraphs, so optimise it and try to make it easy for
954128aefc0Sad  * the compiler by including next to the inline lookup routines.
955128aefc0Sad  */
956128aefc0Sad struct vm_page *
uvm_phys_to_vm_page(paddr_t pa)957128aefc0Sad uvm_phys_to_vm_page(paddr_t pa)
958128aefc0Sad {
959128aefc0Sad #if VM_PHYSSEG_STRAT != VM_PSTRAT_BSEARCH
960128aefc0Sad 	/* 'contig' and linear cases */
961128aefc0Sad 	KASSERT(vm_nphysseg > 0);
962128aefc0Sad 	struct uvm_physseg *ps = &vm_physmem[0];
963128aefc0Sad 	struct uvm_physseg *end = &vm_physmem[vm_nphysseg];
964128aefc0Sad 	paddr_t pframe = atop(pa);
965128aefc0Sad 	do {
966128aefc0Sad 		if (pframe >= ps->start && pframe < ps->end) {
967128aefc0Sad 			return &ps->pgs[pframe - ps->start];
968128aefc0Sad 		}
969128aefc0Sad 	} while (VM_PHYSSEG_MAX > 1 && __predict_false(++ps < end));
970128aefc0Sad 	return NULL;
971128aefc0Sad #else
972128aefc0Sad 	/* binary search for it */
973128aefc0Sad 	paddr_t pf = atop(pa);
974128aefc0Sad 	paddr_t	off;
975128aefc0Sad 	uvm_physseg_t	upm;
976128aefc0Sad 
977128aefc0Sad 	upm = uvm_physseg_find(pf, &off);
978128aefc0Sad 	if (upm != UVM_PHYSSEG_TYPE_INVALID)
979128aefc0Sad 		return uvm_physseg_get_pg(upm, off);
980128aefc0Sad 	return(NULL);
981128aefc0Sad #endif
982128aefc0Sad }
983128aefc0Sad 
98407acf3c0Scherry bool
uvm_physseg_valid_p(uvm_physseg_t upm)985e6ff351eScherry uvm_physseg_valid_p(uvm_physseg_t upm)
98607acf3c0Scherry {
98707acf3c0Scherry 	struct uvm_physseg *ps;
98807acf3c0Scherry 
98907acf3c0Scherry 	if (upm == UVM_PHYSSEG_TYPE_INVALID ||
99007acf3c0Scherry 	    upm == UVM_PHYSSEG_TYPE_INVALID_EMPTY ||
99107acf3c0Scherry 	    upm == UVM_PHYSSEG_TYPE_INVALID_OVERFLOW)
99207acf3c0Scherry 		return false;
99307acf3c0Scherry 
99407acf3c0Scherry 	/*
99507acf3c0Scherry 	 * This is the delicate init dance -
99607acf3c0Scherry 	 * needs to go with the dance.
99707acf3c0Scherry 	 */
99807acf3c0Scherry 	if (uvm.page_init_done != true)
99907acf3c0Scherry 		return true;
100007acf3c0Scherry 
100107acf3c0Scherry 	ps = HANDLE_TO_PHYSSEG_NODE(upm);
100207acf3c0Scherry 
100307acf3c0Scherry 	/* Extra checks needed only post uvm_page_init() */
100407acf3c0Scherry 	if (ps->pgs == NULL)
100507acf3c0Scherry 		return false;
100607acf3c0Scherry 
100707acf3c0Scherry 	/* XXX: etc. */
100807acf3c0Scherry 
100907acf3c0Scherry 	return true;
101007acf3c0Scherry 
101107acf3c0Scherry }
101207acf3c0Scherry 
101307acf3c0Scherry /*
101407acf3c0Scherry  * Boot protocol dictates that these must be able to return partially
101507acf3c0Scherry  * initialised segments.
101607acf3c0Scherry  */
101707acf3c0Scherry paddr_t
uvm_physseg_get_start(uvm_physseg_t upm)101807acf3c0Scherry uvm_physseg_get_start(uvm_physseg_t upm)
101907acf3c0Scherry {
1020e6ff351eScherry 	if (uvm_physseg_valid_p(upm) == false)
102107acf3c0Scherry 		return (paddr_t) -1;
102207acf3c0Scherry 
102307acf3c0Scherry 	return HANDLE_TO_PHYSSEG_NODE(upm)->start;
102407acf3c0Scherry }
102507acf3c0Scherry 
102607acf3c0Scherry paddr_t
uvm_physseg_get_end(uvm_physseg_t upm)102707acf3c0Scherry uvm_physseg_get_end(uvm_physseg_t upm)
102807acf3c0Scherry {
1029e6ff351eScherry 	if (uvm_physseg_valid_p(upm) == false)
103007acf3c0Scherry 		return (paddr_t) -1;
103107acf3c0Scherry 
103207acf3c0Scherry 	return HANDLE_TO_PHYSSEG_NODE(upm)->end;
103307acf3c0Scherry }
103407acf3c0Scherry 
103507acf3c0Scherry paddr_t
uvm_physseg_get_avail_start(uvm_physseg_t upm)103607acf3c0Scherry uvm_physseg_get_avail_start(uvm_physseg_t upm)
103707acf3c0Scherry {
1038e6ff351eScherry 	if (uvm_physseg_valid_p(upm) == false)
103907acf3c0Scherry 		return (paddr_t) -1;
104007acf3c0Scherry 
104107acf3c0Scherry 	return HANDLE_TO_PHYSSEG_NODE(upm)->avail_start;
104207acf3c0Scherry }
104307acf3c0Scherry 
1044c7ed8c6fSrin #if defined(UVM_PHYSSEG_LEGACY)
10456419995eSchristos void
uvm_physseg_set_avail_start(uvm_physseg_t upm,paddr_t avail_start)10466419995eSchristos uvm_physseg_set_avail_start(uvm_physseg_t upm, paddr_t avail_start)
10476419995eSchristos {
10486b706f88Scherry 	struct uvm_physseg *ps = HANDLE_TO_PHYSSEG_NODE(upm);
10496b706f88Scherry 
10506b706f88Scherry #if defined(DIAGNOSTIC)
10516b706f88Scherry 	paddr_t avail_end;
10526b706f88Scherry 	avail_end = uvm_physseg_get_avail_end(upm);
10536419995eSchristos 	KASSERT(uvm_physseg_valid_p(upm));
1054a83cdbecSriastradh 	KASSERT(avail_start < avail_end);
1055a83cdbecSriastradh 	KASSERT(avail_start >= ps->start);
10566419995eSchristos #endif
10576419995eSchristos 
10586b706f88Scherry 	ps->avail_start = avail_start;
10596b706f88Scherry }
1060519577eeSad 
1061519577eeSad void
uvm_physseg_set_avail_end(uvm_physseg_t upm,paddr_t avail_end)1062519577eeSad uvm_physseg_set_avail_end(uvm_physseg_t upm, paddr_t avail_end)
10636b706f88Scherry {
10646b706f88Scherry 	struct uvm_physseg *ps = HANDLE_TO_PHYSSEG_NODE(upm);
10656b706f88Scherry 
10666b706f88Scherry #if defined(DIAGNOSTIC)
10676b706f88Scherry 	paddr_t avail_start;
10686b706f88Scherry 	avail_start = uvm_physseg_get_avail_start(upm);
10696b706f88Scherry 	KASSERT(uvm_physseg_valid_p(upm));
1070a83cdbecSriastradh 	KASSERT(avail_end > avail_start);
1071a83cdbecSriastradh 	KASSERT(avail_end <= ps->end);
10726b706f88Scherry #endif
10736b706f88Scherry 
10746b706f88Scherry 	ps->avail_end = avail_end;
10756b706f88Scherry }
10766b706f88Scherry 
1077c7ed8c6fSrin #endif /* UVM_PHYSSEG_LEGACY */
10786b706f88Scherry 
107907acf3c0Scherry paddr_t
uvm_physseg_get_avail_end(uvm_physseg_t upm)108007acf3c0Scherry uvm_physseg_get_avail_end(uvm_physseg_t upm)
108107acf3c0Scherry {
1082e6ff351eScherry 	if (uvm_physseg_valid_p(upm) == false)
108307acf3c0Scherry 		return (paddr_t) -1;
108407acf3c0Scherry 
108507acf3c0Scherry 	return HANDLE_TO_PHYSSEG_NODE(upm)->avail_end;
108607acf3c0Scherry }
108707acf3c0Scherry 
1088128aefc0Sad inline struct vm_page *
uvm_physseg_get_pg(uvm_physseg_t upm,paddr_t idx)108907acf3c0Scherry uvm_physseg_get_pg(uvm_physseg_t upm, paddr_t idx)
109007acf3c0Scherry {
1091e6ff351eScherry 	KASSERT(uvm_physseg_valid_p(upm));
109207acf3c0Scherry 	return &HANDLE_TO_PHYSSEG_NODE(upm)->pgs[idx];
109307acf3c0Scherry }
109407acf3c0Scherry 
109507acf3c0Scherry #ifdef __HAVE_PMAP_PHYSSEG
109607acf3c0Scherry struct pmap_physseg *
uvm_physseg_get_pmseg(uvm_physseg_t upm)109707acf3c0Scherry uvm_physseg_get_pmseg(uvm_physseg_t upm)
109807acf3c0Scherry {
1099e6ff351eScherry 	KASSERT(uvm_physseg_valid_p(upm));
110007acf3c0Scherry 	return &(HANDLE_TO_PHYSSEG_NODE(upm)->pmseg);
110107acf3c0Scherry }
110207acf3c0Scherry #endif
110307acf3c0Scherry 
110407acf3c0Scherry int
uvm_physseg_get_free_list(uvm_physseg_t upm)110507acf3c0Scherry uvm_physseg_get_free_list(uvm_physseg_t upm)
110607acf3c0Scherry {
1107e6ff351eScherry 	KASSERT(uvm_physseg_valid_p(upm));
110807acf3c0Scherry 	return HANDLE_TO_PHYSSEG_NODE(upm)->free_list;
110907acf3c0Scherry }
111007acf3c0Scherry 
1111*ecaf2f40Stnn u_long
uvm_physseg_get_start_hint(uvm_physseg_t upm)111207acf3c0Scherry uvm_physseg_get_start_hint(uvm_physseg_t upm)
111307acf3c0Scherry {
1114e6ff351eScherry 	KASSERT(uvm_physseg_valid_p(upm));
111507acf3c0Scherry 	return HANDLE_TO_PHYSSEG_NODE(upm)->start_hint;
111607acf3c0Scherry }
111707acf3c0Scherry 
111807acf3c0Scherry bool
uvm_physseg_set_start_hint(uvm_physseg_t upm,u_long start_hint)1119*ecaf2f40Stnn uvm_physseg_set_start_hint(uvm_physseg_t upm, u_long start_hint)
112007acf3c0Scherry {
1121e6ff351eScherry 	if (uvm_physseg_valid_p(upm) == false)
112207acf3c0Scherry 		return false;
112307acf3c0Scherry 
112407acf3c0Scherry 	HANDLE_TO_PHYSSEG_NODE(upm)->start_hint = start_hint;
112507acf3c0Scherry 	return true;
112607acf3c0Scherry }
112707acf3c0Scherry 
112807acf3c0Scherry void
uvm_physseg_init_seg(uvm_physseg_t upm,struct vm_page * pgs)112907acf3c0Scherry uvm_physseg_init_seg(uvm_physseg_t upm, struct vm_page *pgs)
113007acf3c0Scherry {
113107acf3c0Scherry 	psize_t i;
113207acf3c0Scherry 	psize_t n;
113307acf3c0Scherry 	paddr_t paddr;
113407acf3c0Scherry 	struct uvm_physseg *seg;
11355978ddc6Sad 	struct vm_page *pg;
113607acf3c0Scherry 
1137a83cdbecSriastradh 	KASSERT(upm != UVM_PHYSSEG_TYPE_INVALID);
1138a83cdbecSriastradh 	KASSERT(pgs != NULL);
113907acf3c0Scherry 
114007acf3c0Scherry 	seg = HANDLE_TO_PHYSSEG_NODE(upm);
114107acf3c0Scherry 	KASSERT(seg != NULL);
114207acf3c0Scherry 	KASSERT(seg->pgs == NULL);
114307acf3c0Scherry 
114407acf3c0Scherry 	n = seg->end - seg->start;
114507acf3c0Scherry 	seg->pgs = pgs;
114607acf3c0Scherry 
114707acf3c0Scherry 	/* init and free vm_pages (we've already zeroed them) */
114807acf3c0Scherry 	paddr = ctob(seg->start);
114907acf3c0Scherry 	for (i = 0 ; i < n ; i++, paddr += PAGE_SIZE) {
1150a7b92da9Sad 		pg = &seg->pgs[i];
1151a7b92da9Sad 		pg->phys_addr = paddr;
115207acf3c0Scherry #ifdef __HAVE_VM_PAGE_MD
1153a7b92da9Sad 		VM_MDPAGE_INIT(pg);
115407acf3c0Scherry #endif
115507acf3c0Scherry 		if (atop(paddr) >= seg->avail_start &&
115607acf3c0Scherry 		    atop(paddr) < seg->avail_end) {
115707acf3c0Scherry 			uvmexp.npages++;
115807acf3c0Scherry 			/* add page to free pool */
1159a7b92da9Sad 			uvm_page_set_freelist(pg,
1160a7b92da9Sad 			    uvm_page_lookup_freelist(pg));
11615978ddc6Sad 			/* Disable LOCKDEBUG: too many and too early. */
11625978ddc6Sad 			mutex_init(&pg->interlock, MUTEX_NODEBUG, IPL_NONE);
11635978ddc6Sad 			uvm_pagefree(pg);
116407acf3c0Scherry 		}
116507acf3c0Scherry 	}
116607acf3c0Scherry }
116707acf3c0Scherry 
116807acf3c0Scherry void
uvm_physseg_seg_chomp_slab(uvm_physseg_t upm,struct vm_page * pgs,size_t n)116907acf3c0Scherry uvm_physseg_seg_chomp_slab(uvm_physseg_t upm, struct vm_page *pgs, size_t n)
117007acf3c0Scherry {
117107acf3c0Scherry 	struct uvm_physseg *seg = HANDLE_TO_PHYSSEG_NODE(upm);
117207acf3c0Scherry 
117307acf3c0Scherry 	/* max number of pre-boot unplug()s allowed */
117407acf3c0Scherry #define UVM_PHYSSEG_BOOT_UNPLUG_MAX VM_PHYSSEG_MAX
117507acf3c0Scherry 
117607acf3c0Scherry 	static char btslab_ex_storage[EXTENT_FIXED_STORAGE_SIZE(UVM_PHYSSEG_BOOT_UNPLUG_MAX)];
117707acf3c0Scherry 
117807acf3c0Scherry 	if (__predict_false(uvm.page_init_done == false)) {
117907acf3c0Scherry 		seg->ext = extent_create("Boot time slab", (u_long) pgs, (u_long) (pgs + n),
118007acf3c0Scherry 		    (void *)btslab_ex_storage, sizeof(btslab_ex_storage), 0);
118107acf3c0Scherry 	} else {
118207acf3c0Scherry 		seg->ext = extent_create("Hotplug slab", (u_long) pgs, (u_long) (pgs + n), NULL, 0, 0);
118307acf3c0Scherry 	}
118407acf3c0Scherry 
118507acf3c0Scherry 	KASSERT(seg->ext != NULL);
118607acf3c0Scherry 
118707acf3c0Scherry }
118807acf3c0Scherry 
118907acf3c0Scherry struct vm_page *
uvm_physseg_seg_alloc_from_slab(uvm_physseg_t upm,size_t pages)119007acf3c0Scherry uvm_physseg_seg_alloc_from_slab(uvm_physseg_t upm, size_t pages)
119107acf3c0Scherry {
119207acf3c0Scherry 	int err;
119307acf3c0Scherry 	struct uvm_physseg *seg;
119407acf3c0Scherry 	struct vm_page *pgs = NULL;
119507acf3c0Scherry 
119607acf3c0Scherry 	KASSERT(pages > 0);
119707acf3c0Scherry 
11984af99c27Schristos 	seg = HANDLE_TO_PHYSSEG_NODE(upm);
11994af99c27Schristos 
120007acf3c0Scherry 	if (__predict_false(seg->ext == NULL)) {
120107acf3c0Scherry 		/*
120207acf3c0Scherry 		 * This is a situation unique to boot time.
120307acf3c0Scherry 		 * It shouldn't happen at any point other than from
120407acf3c0Scherry 		 * the first uvm_page.c:uvm_page_init() call
120507acf3c0Scherry 		 * Since we're in a loop, we can get away with the
120607acf3c0Scherry 		 * below.
120707acf3c0Scherry 		 */
120807acf3c0Scherry 		KASSERT(uvm.page_init_done != true);
120907acf3c0Scherry 
12104af99c27Schristos 		uvm_physseg_t upmp = uvm_physseg_get_prev(upm);
12114af99c27Schristos 		KASSERT(upmp != UVM_PHYSSEG_TYPE_INVALID);
12124af99c27Schristos 
12134af99c27Schristos 		seg->ext = HANDLE_TO_PHYSSEG_NODE(upmp)->ext;
121407acf3c0Scherry 
121507acf3c0Scherry 		KASSERT(seg->ext != NULL);
121607acf3c0Scherry 	}
121707acf3c0Scherry 
121807acf3c0Scherry 	/* We allocate enough for this segment */
121907acf3c0Scherry 	err = extent_alloc(seg->ext, sizeof(*pgs) * pages, 1, 0, EX_BOUNDZERO, (u_long *)&pgs);
122007acf3c0Scherry 
122107acf3c0Scherry 	if (err != 0) {
122207acf3c0Scherry #ifdef DEBUG
122307acf3c0Scherry 		printf("%s: extent_alloc failed with error: %d \n",
122407acf3c0Scherry 		    __func__, err);
122507acf3c0Scherry #endif
122607acf3c0Scherry 	}
122707acf3c0Scherry 
122807acf3c0Scherry 	return pgs;
122907acf3c0Scherry }
123007acf3c0Scherry 
123107acf3c0Scherry /*
123207acf3c0Scherry  * uvm_page_physload: load physical memory into VM system
123307acf3c0Scherry  *
123407acf3c0Scherry  * => all args are PFs
123507acf3c0Scherry  * => all pages in start/end get vm_page structures
123607acf3c0Scherry  * => areas marked by avail_start/avail_end get added to the free page pool
123707acf3c0Scherry  * => we are limited to VM_PHYSSEG_MAX physical memory segments
123807acf3c0Scherry  */
123907acf3c0Scherry 
124007acf3c0Scherry uvm_physseg_t
uvm_page_physload(paddr_t start,paddr_t end,paddr_t avail_start,paddr_t avail_end,int free_list)124107acf3c0Scherry uvm_page_physload(paddr_t start, paddr_t end, paddr_t avail_start,
124207acf3c0Scherry     paddr_t avail_end, int free_list)
124307acf3c0Scherry {
124407acf3c0Scherry 	struct uvm_physseg *ps;
124507acf3c0Scherry 	uvm_physseg_t upm;
124607acf3c0Scherry 
124707acf3c0Scherry 	if (__predict_true(uvm.page_init_done == true))
124807acf3c0Scherry 		panic("%s: unload attempted after uvm_page_init()\n", __func__);
124907acf3c0Scherry 	if (uvmexp.pagesize == 0)
125007acf3c0Scherry 		panic("uvm_page_physload: page size not set!");
125107acf3c0Scherry 	if (free_list >= VM_NFREELIST || free_list < VM_FREELIST_DEFAULT)
125207acf3c0Scherry 		panic("uvm_page_physload: bad free list %d", free_list);
125307acf3c0Scherry 	if (start >= end)
1254979cf575Srin 		panic("uvm_page_physload: start[%" PRIxPADDR "] >= end[%"
1255979cf575Srin 		    PRIxPADDR "]", start, end);
125607acf3c0Scherry 
125707acf3c0Scherry 	if (uvm_physseg_plug(start, end - start, &upm) == false) {
125807acf3c0Scherry 		panic("uvm_physseg_plug() failed at boot.");
125907acf3c0Scherry 		/* NOTREACHED */
126007acf3c0Scherry 		return UVM_PHYSSEG_TYPE_INVALID; /* XXX: correct type */
126107acf3c0Scherry 	}
126207acf3c0Scherry 
126307acf3c0Scherry 	ps = HANDLE_TO_PHYSSEG_NODE(upm);
126407acf3c0Scherry 
126507acf3c0Scherry 	/* Legacy */
126607acf3c0Scherry 	ps->avail_start = avail_start;
126707acf3c0Scherry 	ps->avail_end = avail_end;
126807acf3c0Scherry 
126907acf3c0Scherry 	ps->free_list = free_list; /* XXX: */
127007acf3c0Scherry 
127107acf3c0Scherry 
127207acf3c0Scherry 	return upm;
127307acf3c0Scherry }
127407acf3c0Scherry 
127507acf3c0Scherry bool
uvm_physseg_unplug(paddr_t pfn,size_t pages)127607acf3c0Scherry uvm_physseg_unplug(paddr_t pfn, size_t pages)
127707acf3c0Scherry {
127807acf3c0Scherry 	uvm_physseg_t upm;
12794ad0abb1Sriastradh 	paddr_t off = 0, start __diagused, end;
128007acf3c0Scherry 	struct uvm_physseg *seg;
128107acf3c0Scherry 
128207acf3c0Scherry 	upm = uvm_physseg_find(pfn, &off);
128307acf3c0Scherry 
1284e6ff351eScherry 	if (!uvm_physseg_valid_p(upm)) {
128507acf3c0Scherry 		printf("%s: Tried to unplug from unknown offset\n", __func__);
128607acf3c0Scherry 		return false;
128707acf3c0Scherry 	}
128807acf3c0Scherry 
128907acf3c0Scherry 	seg = HANDLE_TO_PHYSSEG_NODE(upm);
129007acf3c0Scherry 
129107acf3c0Scherry 	start = uvm_physseg_get_start(upm);
129207acf3c0Scherry 	end = uvm_physseg_get_end(upm);
129307acf3c0Scherry 
129407acf3c0Scherry 	if (end < (pfn + pages)) {
129507acf3c0Scherry 		printf("%s: Tried to unplug oversized span \n", __func__);
129607acf3c0Scherry 		return false;
129707acf3c0Scherry 	}
129807acf3c0Scherry 
129907acf3c0Scherry 	KASSERT(pfn == start + off); /* sanity */
130007acf3c0Scherry 
130107acf3c0Scherry 	if (__predict_true(uvm.page_init_done == true)) {
130207acf3c0Scherry 		/* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
130307acf3c0Scherry 		if (extent_free(seg->ext, (u_long)(seg->pgs + off), sizeof(struct vm_page) * pages, EX_MALLOCOK | EX_NOWAIT) != 0)
130407acf3c0Scherry 			return false;
130507acf3c0Scherry 	}
130607acf3c0Scherry 
130707acf3c0Scherry 	if (off == 0 && (pfn + pages) == end) {
130807acf3c0Scherry #if defined(UVM_HOTPLUG) /* rbtree implementation */
130907acf3c0Scherry 		int segcount = 0;
131007acf3c0Scherry 		struct uvm_physseg *current_ps;
131107acf3c0Scherry 		/* Complete segment */
131207acf3c0Scherry 		if (uvm_physseg_graph.nentries == 1)
131307acf3c0Scherry 			panic("%s: out of memory!", __func__);
131407acf3c0Scherry 
131507acf3c0Scherry 		if (__predict_true(uvm.page_init_done == true)) {
131607acf3c0Scherry 			RB_TREE_FOREACH(current_ps, &(uvm_physseg_graph.rb_tree)) {
131707acf3c0Scherry 				if (seg->ext == current_ps->ext)
131807acf3c0Scherry 					segcount++;
131907acf3c0Scherry 			}
132007acf3c0Scherry 			KASSERT(segcount > 0);
132107acf3c0Scherry 
132207acf3c0Scherry 			if (segcount == 1) {
132307acf3c0Scherry 				extent_destroy(seg->ext);
132407acf3c0Scherry 			}
132507acf3c0Scherry 
132607acf3c0Scherry 			/*
132707acf3c0Scherry 			 * We assume that the unplug will succeed from
132807acf3c0Scherry 			 *  this point onwards
132907acf3c0Scherry 			 */
133007acf3c0Scherry 			uvmexp.npages -= (int) pages;
133107acf3c0Scherry 		}
133207acf3c0Scherry 
133307acf3c0Scherry 		rb_tree_remove_node(&(uvm_physseg_graph.rb_tree), upm);
133407acf3c0Scherry 		memset(seg, 0, sizeof(struct uvm_physseg));
133507acf3c0Scherry 		uvm_physseg_free(seg, sizeof(struct uvm_physseg));
133607acf3c0Scherry 		uvm_physseg_graph.nentries--;
133707acf3c0Scherry #else /* UVM_HOTPLUG */
133807acf3c0Scherry 		int x;
133907acf3c0Scherry 		if (vm_nphysmem == 1)
134007acf3c0Scherry 			panic("uvm_page_physget: out of memory!");
134107acf3c0Scherry 		vm_nphysmem--;
134207acf3c0Scherry 		for (x = upm ; x < vm_nphysmem ; x++)
134307acf3c0Scherry 			/* structure copy */
134407acf3c0Scherry 			VM_PHYSMEM_PTR_SWAP(x, x + 1);
134507acf3c0Scherry #endif /* UVM_HOTPLUG */
134607acf3c0Scherry 		/* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
134707acf3c0Scherry 		return true;
134807acf3c0Scherry 	}
134907acf3c0Scherry 
135007acf3c0Scherry 	if (off > 0 &&
135107acf3c0Scherry 	    (pfn + pages) < end) {
135207acf3c0Scherry #if defined(UVM_HOTPLUG) /* rbtree implementation */
135307acf3c0Scherry 		/* middle chunk - need a new segment */
135407acf3c0Scherry 		struct uvm_physseg *ps, *current_ps;
135507acf3c0Scherry 		ps = uvm_physseg_alloc(sizeof (struct uvm_physseg));
135607acf3c0Scherry 		if (ps == NULL) {
135707acf3c0Scherry 			printf("%s: Unable to allocated new fragment vm_physseg \n",
135807acf3c0Scherry 			    __func__);
135907acf3c0Scherry 			return false;
136007acf3c0Scherry 		}
136107acf3c0Scherry 
136207acf3c0Scherry 		/* Remove middle chunk */
136307acf3c0Scherry 		if (__predict_true(uvm.page_init_done == true)) {
136407acf3c0Scherry 			KASSERT(seg->ext != NULL);
136507acf3c0Scherry 			ps->ext = seg->ext;
136607acf3c0Scherry 
136707acf3c0Scherry 			/* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
136807acf3c0Scherry 			/*
136907acf3c0Scherry 			 * We assume that the unplug will succeed from
137007acf3c0Scherry 			 *  this point onwards
137107acf3c0Scherry 			 */
137207acf3c0Scherry 			uvmexp.npages -= (int) pages;
137307acf3c0Scherry 		}
137407acf3c0Scherry 
137507acf3c0Scherry 		ps->start = pfn + pages;
137607acf3c0Scherry 		ps->avail_start = ps->start; /* XXX: Legacy */
137707acf3c0Scherry 
137807acf3c0Scherry 		ps->end = seg->end;
137907acf3c0Scherry 		ps->avail_end = ps->end; /* XXX: Legacy */
138007acf3c0Scherry 
138107acf3c0Scherry 		seg->end = pfn;
138207acf3c0Scherry 		seg->avail_end = seg->end; /* XXX: Legacy */
138307acf3c0Scherry 
138407acf3c0Scherry 
138507acf3c0Scherry 		/*
138607acf3c0Scherry 		 * The new pgs array points to the beginning of the
138707acf3c0Scherry 		 * tail fragment.
138807acf3c0Scherry 		 */
138907acf3c0Scherry 		if (__predict_true(uvm.page_init_done == true))
139007acf3c0Scherry 			ps->pgs = seg->pgs + off + pages;
139107acf3c0Scherry 
139207acf3c0Scherry 		current_ps = rb_tree_insert_node(&(uvm_physseg_graph.rb_tree), ps);
139307acf3c0Scherry 		if (current_ps != ps) {
139407acf3c0Scherry 			panic("uvm_page_physload: Duplicate address range detected!");
139507acf3c0Scherry 		}
139607acf3c0Scherry 		uvm_physseg_graph.nentries++;
139707acf3c0Scherry #else /* UVM_HOTPLUG */
139807acf3c0Scherry 		panic("%s: can't unplug() from the middle of a segment without"
139907acf3c0Scherry 		    " UVM_HOTPLUG\n",  __func__);
140007acf3c0Scherry 		/* NOTREACHED */
140107acf3c0Scherry #endif /* UVM_HOTPLUG */
140207acf3c0Scherry 		return true;
140307acf3c0Scherry 	}
140407acf3c0Scherry 
140507acf3c0Scherry 	if (off == 0 && (pfn + pages) < end) {
140607acf3c0Scherry 		/* Remove front chunk */
140707acf3c0Scherry 		if (__predict_true(uvm.page_init_done == true)) {
140807acf3c0Scherry 			/* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
140907acf3c0Scherry 			/*
141007acf3c0Scherry 			 * We assume that the unplug will succeed from
141107acf3c0Scherry 			 *  this point onwards
141207acf3c0Scherry 			 */
141307acf3c0Scherry 			uvmexp.npages -= (int) pages;
141407acf3c0Scherry 		}
141507acf3c0Scherry 
141607acf3c0Scherry 		/* Truncate */
141707acf3c0Scherry 		seg->start = pfn + pages;
141807acf3c0Scherry 		seg->avail_start = seg->start; /* XXX: Legacy */
141907acf3c0Scherry 
142007acf3c0Scherry 		/*
142107acf3c0Scherry 		 * Move the pgs array start to the beginning of the
142207acf3c0Scherry 		 * tail end.
142307acf3c0Scherry 		 */
142407acf3c0Scherry 		if (__predict_true(uvm.page_init_done == true))
142507acf3c0Scherry 			seg->pgs += pages;
142607acf3c0Scherry 
142707acf3c0Scherry 		return true;
142807acf3c0Scherry 	}
142907acf3c0Scherry 
143007acf3c0Scherry 	if (off > 0 && (pfn + pages) == end) {
143107acf3c0Scherry 		/* back chunk */
143207acf3c0Scherry 
143307acf3c0Scherry 
143407acf3c0Scherry 		/* Truncate! */
143507acf3c0Scherry 		seg->end = pfn;
143607acf3c0Scherry 		seg->avail_end = seg->end; /* XXX: Legacy */
143707acf3c0Scherry 
143807acf3c0Scherry 		uvmexp.npages -= (int) pages;
143907acf3c0Scherry 
144007acf3c0Scherry 		return true;
144107acf3c0Scherry 	}
144207acf3c0Scherry 
144307acf3c0Scherry 	printf("%s: Tried to unplug unknown range \n", __func__);
144407acf3c0Scherry 
144507acf3c0Scherry 	return false;
144607acf3c0Scherry }
1447