1 /* $NetBSD: uvm_physseg.c,v 1.20 2024/01/13 09:44:42 tnn Exp $ */
2 
3 /*
4  * Copyright (c) 1997 Charles D. Cranor and Washington University.
5  * Copyright (c) 1991, 1993, The Regents of the University of California.
6  *
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * The Mach Operating System project at Carnegie-Mellon University.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *        @(#)vm_page.h   7.3 (Berkeley) 4/21/91
37  * from: Id: uvm_page.h,v 1.1.2.6 1998/02/04 02:31:42 chuck Exp
38  *
39  *
40  * Copyright (c) 1987, 1990 Carnegie-Mellon University.
41  * All rights reserved.
42  *
43  * Permission to use, copy, modify and distribute this software and
44  * its documentation is hereby granted, provided that both the copyright
45  * notice and this permission notice appear in all copies of the
46  * software, derivative works or modified versions, and any portions
47  * thereof, and that both notices appear in supporting documentation.
48  *
49  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
50  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
51  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
52  *
53  * Carnegie Mellon requests users of this software to return to
54  *
55  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
56  *  School of Computer Science
57  *  Carnegie Mellon University
58  *  Pittsburgh PA 15213-3890
59  *
60  * any improvements or extensions that they make and grant Carnegie the
61  * rights to redistribute these changes.
62  */
63 
64 /*
65  * Consolidated API from uvm_page.c and others.
66  * Consolidated and designed by Cherry G. Mathew <cherry@zyx.in>
67  * rbtree(3) backing implementation by:
68  * Santhosh N. Raju <santhosh.raju@gmail.com>
69  */
70 
71 #ifdef _KERNEL_OPT
72 #include "opt_uvm.h"
73 #endif
74 
75 #include <sys/param.h>
76 #include <sys/types.h>
77 #include <sys/extent.h>
78 #include <sys/kmem.h>
79 
80 #include <uvm/uvm.h>
81 #include <uvm/uvm_page.h>
82 #include <uvm/uvm_param.h>
83 #include <uvm/uvm_pdpolicy.h>
84 #include <uvm/uvm_physseg.h>
85 
86 /*
87  * uvm_physseg: describes one segment of physical memory
88  */
89 struct uvm_physseg {
90           /* used during RB tree lookup for PHYS_TO_VM_PAGE(). */
91 #if defined(UVM_HOTPLUG)
92           struct  rb_node rb_node;      /* tree information */
93 #endif
94           paddr_t   start;                        /* PF# of first page in segment */
95           paddr_t   end;                          /* (PF# of last page in segment) + 1 */
96           struct    vm_page *pgs;                 /* vm_page structures (from start) */
97 
98           /* less performance sensitive fields. */
99           paddr_t   avail_start;                  /* PF# of first free page in segment */
100           paddr_t   avail_end;                    /* (PF# of last free page in segment) +1  */
101           struct  extent *ext;                    /* extent(9) structure to manage pgs[] */
102           int       free_list;                    /* which free list they belong on */
103           u_long    start_hint;                   /* start looking for free pages here */
104 #ifdef __HAVE_PMAP_PHYSSEG
105           struct    pmap_physseg pmseg; /* pmap specific (MD) data */
106 #endif
107 };
108 
109 /*
110  * These functions are reserved for uvm(9) internal use and are not
111  * exported in the header file uvm_physseg.h
112  *
113  * Thus they are redefined here.
114  */
115 void uvm_physseg_init_seg(uvm_physseg_t, struct vm_page *);
116 void uvm_physseg_seg_chomp_slab(uvm_physseg_t, struct vm_page *, size_t);
117 
118 /* returns a pgs array */
119 struct vm_page *uvm_physseg_seg_alloc_from_slab(uvm_physseg_t, size_t);
120 
121 #if defined(UVM_HOTPLUG) /* rbtree impementation */
122 
123 #define             HANDLE_TO_PHYSSEG_NODE(h)     ((struct uvm_physseg *)(h))
124 #define             PHYSSEG_NODE_TO_HANDLE(u)     ((uvm_physseg_t)(u))
125 
126 struct uvm_physseg_graph {
127           struct rb_tree rb_tree;                 /* Tree for entries */
128           int            nentries;      /* Number of entries */
129 } __aligned(COHERENCY_UNIT);
130 
131 static struct uvm_physseg_graph uvm_physseg_graph __read_mostly;
132 
133 /*
134  * Note on kmem(9) allocator usage:
135  * We take the conservative approach that plug/unplug are allowed to
136  * fail in high memory stress situations.
137  *
138  * We want to avoid re-entrant situations in which one plug/unplug
139  * operation is waiting on a previous one to complete, since this
140  * makes the design more complicated than necessary.
141  *
142  * We may review this and change its behaviour, once the use cases
143  * become more obvious.
144  */
145 
146 /*
147  * Special alloc()/free() functions for boot time support:
148  * We assume that alloc() at boot time is only for new 'vm_physseg's
149  * This allows us to use a static array for memory allocation at boot
150  * time. Thus we avoid using kmem(9) which is not ready at this point
151  * in boot.
152  *
153  * After kmem(9) is ready, we use it. We currently discard any free()s
154  * to this static array, since the size is small enough to be a
155  * trivial waste on all architectures we run on.
156  */
157 
158 static size_t nseg = 0;
159 static struct uvm_physseg uvm_physseg[VM_PHYSSEG_MAX];
160 
161 static void *
uvm_physseg_alloc(size_t sz)162 uvm_physseg_alloc(size_t sz)
163 {
164           /*
165            * During boot time, we only support allocating vm_physseg
166            * entries from the static array.
167            * We need to assert for this.
168            */
169 
170           if (__predict_false(uvm.page_init_done == false)) {
171                     if (sz % sizeof(struct uvm_physseg))
172                               panic("%s: tried to alloc size other than multiple"
173                                   " of struct uvm_physseg at boot\n", __func__);
174 
175                     size_t n = sz / sizeof(struct uvm_physseg);
176                     nseg += n;
177 
178                     KASSERT(nseg > 0);
179                     KASSERT(nseg <= VM_PHYSSEG_MAX);
180 
181                     return &uvm_physseg[nseg - n];
182           }
183 
184           return kmem_zalloc(sz, KM_NOSLEEP);
185 }
186 
187 static void
uvm_physseg_free(void * p,size_t sz)188 uvm_physseg_free(void *p, size_t sz)
189 {
190           /*
191            * This is a bit tricky. We do allow simulation of free()
192            * during boot (for eg: when MD code is "steal"ing memory,
193            * and the segment has been exhausted (and thus needs to be
194            * free() - ed.
195            * free() also complicates things because we leak the
196            * free(). Therefore calling code can't assume that free()-ed
197            * memory is available for alloc() again, at boot time.
198            *
199            * Thus we can't explicitly disallow free()s during
200            * boot time. However, the same restriction for alloc()
201            * applies to free(). We only allow uvm_physseg related free()s
202            * via this function during boot time.
203            */
204 
205           if (__predict_false(uvm.page_init_done == false)) {
206                     if (sz % sizeof(struct uvm_physseg))
207                               panic("%s: tried to free size other than struct uvm_physseg"
208                                   " at boot\n", __func__);
209 
210           }
211 
212           /*
213            * Could have been in a single if(){} block - split for
214            * clarity
215            */
216 
217           if ((struct uvm_physseg *)p >= uvm_physseg &&
218               (struct uvm_physseg *)p < (uvm_physseg + VM_PHYSSEG_MAX)) {
219                     if (sz % sizeof(struct uvm_physseg))
220                               panic("%s: tried to free() other than struct uvm_physseg"
221                                   " from static array\n", __func__);
222 
223                     if ((sz / sizeof(struct uvm_physseg)) >= VM_PHYSSEG_MAX)
224                               panic("%s: tried to free() the entire static array!", __func__);
225                     return; /* Nothing to free */
226           }
227 
228           kmem_free(p, sz);
229 }
230 
231 /* XXX: Multi page size */
232 bool
uvm_physseg_plug(paddr_t pfn,size_t pages,uvm_physseg_t * psp)233 uvm_physseg_plug(paddr_t pfn, size_t pages, uvm_physseg_t *psp)
234 {
235           int preload;
236           size_t slabpages;
237           struct uvm_physseg *ps, *current_ps = NULL;
238           struct vm_page *slab = NULL, *pgs = NULL;
239 
240 #ifdef DEBUG
241           paddr_t off;
242           uvm_physseg_t upm;
243           upm = uvm_physseg_find(pfn, &off);
244 
245           ps = HANDLE_TO_PHYSSEG_NODE(upm);
246 
247           if (ps != NULL) /* XXX; do we allow "update" plugs ? */
248                     return false;
249 #endif
250 
251           /*
252            * do we have room?
253            */
254 
255           ps = uvm_physseg_alloc(sizeof (struct uvm_physseg));
256           if (ps == NULL) {
257                     printf("uvm_page_physload: unable to load physical memory "
258                         "segment\n");
259                     printf("\t%d segments allocated, ignoring 0x%"PRIxPADDR" -> 0x%"PRIxPADDR"\n",
260                         VM_PHYSSEG_MAX, pfn, pfn + pages + 1);
261                     printf("\tincrease VM_PHYSSEG_MAX\n");
262                     return false;
263           }
264 
265           /* span init */
266           ps->start = pfn;
267           ps->end = pfn + pages;
268 
269           /*
270            * XXX: Ugly hack because uvmexp.npages accounts for only
271            * those pages in the segment included below as well - this
272            * should be legacy and removed.
273            */
274 
275           ps->avail_start = ps->start;
276           ps->avail_end = ps->end;
277 
278           /*
279            * check to see if this is a "preload" (i.e. uvm_page_init hasn't been
280            * called yet, so kmem is not available).
281            */
282 
283           preload = 1; /* We are going to assume it is a preload */
284 
285           RB_TREE_FOREACH(current_ps, &(uvm_physseg_graph.rb_tree)) {
286                     /* If there are non NULL pages then we are not in a preload */
287                     if (current_ps->pgs != NULL) {
288                               preload = 0;
289                               /* Try to scavenge from earlier unplug()s. */
290                               pgs = uvm_physseg_seg_alloc_from_slab(current_ps, pages);
291 
292                               if (pgs != NULL) {
293                                         break;
294                               }
295                     }
296           }
297 
298 
299           /*
300            * if VM is already running, attempt to kmem_alloc vm_page structures
301            */
302 
303           if (!preload) {
304                     if (pgs == NULL) { /* Brand new */
305                               /* Iteratively try alloc down from uvmexp.npages */
306                               for (slabpages = (size_t) uvmexp.npages; slabpages >= pages; slabpages--) {
307                                         slab = kmem_zalloc(sizeof *pgs * (long unsigned int)slabpages, KM_NOSLEEP);
308                                         if (slab != NULL)
309                                                   break;
310                               }
311 
312                               if (slab == NULL) {
313                                         uvm_physseg_free(ps, sizeof(struct uvm_physseg));
314                                         return false;
315                               }
316 
317                               uvm_physseg_seg_chomp_slab(ps, slab, (size_t) slabpages);
318                               /* We allocate enough for this plug */
319                               pgs = uvm_physseg_seg_alloc_from_slab(ps, pages);
320 
321                               if (pgs == NULL) {
322                                         printf("unable to uvm_physseg_seg_alloc_from_slab() from backend\n");
323                                         return false;
324                               }
325                     } else {
326                               /* Reuse scavenged extent */
327                               ps->ext = current_ps->ext;
328                     }
329 
330                     physmem += pages;
331                     uvmpdpol_reinit();
332           } else { /* Boot time - see uvm_page.c:uvm_page_init() */
333                     pgs = NULL;
334                     ps->pgs = pgs;
335           }
336 
337           /*
338            * now insert us in the proper place in uvm_physseg_graph.rb_tree
339            */
340 
341           current_ps = rb_tree_insert_node(&(uvm_physseg_graph.rb_tree), ps);
342           if (current_ps != ps) {
343                     panic("uvm_page_physload: Duplicate address range detected!");
344           }
345           uvm_physseg_graph.nentries++;
346 
347           /*
348            * uvm_pagefree() requires the PHYS_TO_VM_PAGE(pgs[i]) on the
349            * newly allocated pgs[] to return the correct value. This is
350            * a bit of a chicken and egg problem, since it needs
351            * uvm_physseg_find() to succeed. For this, the node needs to
352            * be inserted *before* uvm_physseg_init_seg() happens.
353            *
354            * During boot, this happens anyway, since
355            * uvm_physseg_init_seg() is called later on and separately
356            * from uvm_page.c:uvm_page_init().
357            * In the case of hotplug we need to ensure this.
358            */
359 
360           if (__predict_true(!preload))
361                     uvm_physseg_init_seg(ps, pgs);
362 
363           if (psp != NULL)
364                     *psp = ps;
365 
366           return true;
367 }
368 
369 static int
uvm_physseg_compare_nodes(void * ctx,const void * nnode1,const void * nnode2)370 uvm_physseg_compare_nodes(void *ctx, const void *nnode1, const void *nnode2)
371 {
372           const struct uvm_physseg *enode1 = nnode1;
373           const struct uvm_physseg *enode2 = nnode2;
374 
375           KASSERT(enode1->start < enode2->start || enode1->start >= enode2->end);
376           KASSERT(enode2->start < enode1->start || enode2->start >= enode1->end);
377 
378           if (enode1->start < enode2->start)
379                     return -1;
380           if (enode1->start >= enode2->end)
381                     return 1;
382           return 0;
383 }
384 
385 static int
uvm_physseg_compare_key(void * ctx,const void * nnode,const void * pkey)386 uvm_physseg_compare_key(void *ctx, const void *nnode, const void *pkey)
387 {
388           const struct uvm_physseg *enode = nnode;
389           const paddr_t pa = *(const paddr_t *) pkey;
390 
391           if(enode->start <= pa && pa < enode->end)
392                     return 0;
393           if (enode->start < pa)
394                     return -1;
395           if (enode->end > pa)
396                     return 1;
397 
398           return 0;
399 }
400 
401 static const rb_tree_ops_t uvm_physseg_tree_ops = {
402           .rbto_compare_nodes = uvm_physseg_compare_nodes,
403           .rbto_compare_key = uvm_physseg_compare_key,
404           .rbto_node_offset = offsetof(struct uvm_physseg, rb_node),
405           .rbto_context = NULL
406 };
407 
408 /*
409  * uvm_physseg_init: init the physmem
410  *
411  * => physmem unit should not be in use at this point
412  */
413 
414 void
uvm_physseg_init(void)415 uvm_physseg_init(void)
416 {
417           rb_tree_init(&(uvm_physseg_graph.rb_tree), &uvm_physseg_tree_ops);
418           uvm_physseg_graph.nentries = 0;
419 }
420 
421 uvm_physseg_t
uvm_physseg_get_next(uvm_physseg_t upm)422 uvm_physseg_get_next(uvm_physseg_t upm)
423 {
424           /* next of invalid is invalid, not fatal */
425           if (uvm_physseg_valid_p(upm) == false)
426                     return UVM_PHYSSEG_TYPE_INVALID;
427 
428           return (uvm_physseg_t) rb_tree_iterate(&(uvm_physseg_graph.rb_tree), upm,
429               RB_DIR_RIGHT);
430 }
431 
432 uvm_physseg_t
uvm_physseg_get_prev(uvm_physseg_t upm)433 uvm_physseg_get_prev(uvm_physseg_t upm)
434 {
435           /* prev of invalid is invalid, not fatal */
436           if (uvm_physseg_valid_p(upm) == false)
437                     return UVM_PHYSSEG_TYPE_INVALID;
438 
439           return (uvm_physseg_t) rb_tree_iterate(&(uvm_physseg_graph.rb_tree), upm,
440               RB_DIR_LEFT);
441 }
442 
443 uvm_physseg_t
uvm_physseg_get_last(void)444 uvm_physseg_get_last(void)
445 {
446           return (uvm_physseg_t) RB_TREE_MAX(&(uvm_physseg_graph.rb_tree));
447 }
448 
449 uvm_physseg_t
uvm_physseg_get_first(void)450 uvm_physseg_get_first(void)
451 {
452           return (uvm_physseg_t) RB_TREE_MIN(&(uvm_physseg_graph.rb_tree));
453 }
454 
455 paddr_t
uvm_physseg_get_highest_frame(void)456 uvm_physseg_get_highest_frame(void)
457 {
458           struct uvm_physseg *ps =
459               (uvm_physseg_t) RB_TREE_MAX(&(uvm_physseg_graph.rb_tree));
460 
461           return ps->end - 1;
462 }
463 
464 /*
465  * uvm_page_physunload: unload physical memory and return it to
466  * caller.
467  */
468 bool
uvm_page_physunload(uvm_physseg_t upm,int freelist,paddr_t * paddrp)469 uvm_page_physunload(uvm_physseg_t upm, int freelist, paddr_t *paddrp)
470 {
471           struct uvm_physseg *seg;
472 
473           if (__predict_true(uvm.page_init_done == true))
474                     panic("%s: unload attempted after uvm_page_init()\n", __func__);
475 
476           seg = HANDLE_TO_PHYSSEG_NODE(upm);
477 
478           if (seg->free_list != freelist) {
479                     return false;
480           }
481 
482           /*
483            * During cold boot, what we're about to unplug hasn't been
484            * put on the uvm freelist, nor has uvmexp.npages been
485            * updated. (This happens in uvm_page.c:uvm_page_init())
486            *
487            * For hotplug, we assume here that the pages being unloaded
488            * here are completely out of sight of uvm (ie; not on any uvm
489            * lists), and that  uvmexp.npages has been suitably
490            * decremented before we're called.
491            *
492            * XXX: will avail_end == start if avail_start < avail_end?
493            */
494 
495           /* try from front */
496           if (seg->avail_start == seg->start &&
497               seg->avail_start < seg->avail_end) {
498                     *paddrp = ctob(seg->avail_start);
499                     return uvm_physseg_unplug(seg->avail_start, 1);
500           }
501 
502           /* try from rear */
503           if (seg->avail_end == seg->end &&
504               seg->avail_start < seg->avail_end) {
505                     *paddrp = ctob(seg->avail_end - 1);
506                     return uvm_physseg_unplug(seg->avail_end - 1, 1);
507           }
508 
509           return false;
510 }
511 
512 bool
uvm_page_physunload_force(uvm_physseg_t upm,int freelist,paddr_t * paddrp)513 uvm_page_physunload_force(uvm_physseg_t upm, int freelist, paddr_t *paddrp)
514 {
515           struct uvm_physseg *seg;
516 
517           seg = HANDLE_TO_PHYSSEG_NODE(upm);
518 
519           if (__predict_true(uvm.page_init_done == true))
520                     panic("%s: unload attempted after uvm_page_init()\n", __func__);
521           /* any room in this bank? */
522           if (seg->avail_start >= seg->avail_end) {
523                     return false; /* nope */
524           }
525 
526           *paddrp = ctob(seg->avail_start);
527 
528           /* Always unplug from front */
529           return uvm_physseg_unplug(seg->avail_start, 1);
530 }
531 
532 
533 /*
534  * vm_physseg_find: find vm_physseg structure that belongs to a PA
535  */
536 uvm_physseg_t
uvm_physseg_find(paddr_t pframe,psize_t * offp)537 uvm_physseg_find(paddr_t pframe, psize_t *offp)
538 {
539           struct uvm_physseg * ps = NULL;
540 
541           ps = rb_tree_find_node(&(uvm_physseg_graph.rb_tree), &pframe);
542 
543           if(ps != NULL && offp != NULL)
544                     *offp = pframe - ps->start;
545 
546           return ps;
547 }
548 
549 #else  /* UVM_HOTPLUG */
550 
551 /*
552  * physical memory config is stored in vm_physmem.
553  */
554 
555 #define   VM_PHYSMEM_PTR(i)   (&vm_physmem[i])
556 #if VM_PHYSSEG_MAX == 1
557 #define VM_PHYSMEM_PTR_SWAP(i, j) /* impossible */
558 #else
559 #define VM_PHYSMEM_PTR_SWAP(i, j)                                                     \
560           do { vm_physmem[(i)] = vm_physmem[(j)]; } while (0)
561 #endif
562 
563 #define             HANDLE_TO_PHYSSEG_NODE(h)     (VM_PHYSMEM_PTR((int)h))
564 #define             PHYSSEG_NODE_TO_HANDLE(u)     ((int)((vsize_t) (u - vm_physmem) / sizeof(struct uvm_physseg)))
565 
566 /* XXXCDC: uvm.physmem */
567 static struct uvm_physseg vm_physmem[VM_PHYSSEG_MAX] __read_mostly;
568 /* XXXCDC: uvm.nphysseg */
569 static int vm_nphysseg __read_mostly = 0;
570 #define   vm_nphysmem         vm_nphysseg
571 
572 void
uvm_physseg_init(void)573 uvm_physseg_init(void)
574 {
575           /* XXX: Provisioning for rb_tree related init(s) */
576           return;
577 }
578 
579 int
uvm_physseg_get_next(uvm_physseg_t lcv)580 uvm_physseg_get_next(uvm_physseg_t lcv)
581 {
582           /* next of invalid is invalid, not fatal */
583           if (uvm_physseg_valid_p(lcv) == false)
584                     return UVM_PHYSSEG_TYPE_INVALID;
585 
586           return (lcv + 1);
587 }
588 
589 int
uvm_physseg_get_prev(uvm_physseg_t lcv)590 uvm_physseg_get_prev(uvm_physseg_t lcv)
591 {
592           /* prev of invalid is invalid, not fatal */
593           if (uvm_physseg_valid_p(lcv) == false)
594                     return UVM_PHYSSEG_TYPE_INVALID;
595 
596           return (lcv - 1);
597 }
598 
599 int
uvm_physseg_get_last(void)600 uvm_physseg_get_last(void)
601 {
602           return (vm_nphysseg - 1);
603 }
604 
605 int
uvm_physseg_get_first(void)606 uvm_physseg_get_first(void)
607 {
608           return 0;
609 }
610 
611 paddr_t
uvm_physseg_get_highest_frame(void)612 uvm_physseg_get_highest_frame(void)
613 {
614           int lcv;
615           paddr_t last = 0;
616           struct uvm_physseg *ps;
617 
618           for (lcv = 0; lcv < vm_nphysseg; lcv++) {
619                     ps = VM_PHYSMEM_PTR(lcv);
620                     if (last < ps->end)
621                               last = ps->end;
622           }
623 
624           return last;
625 }
626 
627 
628 static struct vm_page *
uvm_post_preload_check(void)629 uvm_post_preload_check(void)
630 {
631           int preload, lcv;
632 
633           /*
634            * check to see if this is a "preload" (i.e. uvm_page_init hasn't been
635            * called yet, so kmem is not available).
636            */
637 
638           for (lcv = 0 ; lcv < vm_nphysmem ; lcv++) {
639                     if (VM_PHYSMEM_PTR(lcv)->pgs)
640                               break;
641           }
642           preload = (lcv == vm_nphysmem);
643 
644           /*
645            * if VM is already running, attempt to kmem_alloc vm_page structures
646            */
647 
648           if (!preload) {
649                     panic("Tried to add RAM after uvm_page_init");
650           }
651 
652           return NULL;
653 }
654 
655 /*
656  * uvm_page_physunload: unload physical memory and return it to
657  * caller.
658  */
659 bool
uvm_page_physunload(uvm_physseg_t psi,int freelist,paddr_t * paddrp)660 uvm_page_physunload(uvm_physseg_t psi, int freelist, paddr_t *paddrp)
661 {
662           int x;
663           struct uvm_physseg *seg;
664 
665           uvm_post_preload_check();
666 
667           seg = VM_PHYSMEM_PTR(psi);
668 
669           if (seg->free_list != freelist) {
670                     return false;
671           }
672 
673           /* try from front */
674           if (seg->avail_start == seg->start &&
675               seg->avail_start < seg->avail_end) {
676                     *paddrp = ctob(seg->avail_start);
677                     seg->avail_start++;
678                     seg->start++;
679                     /* nothing left?   nuke it */
680                     if (seg->avail_start == seg->end) {
681                               if (vm_nphysmem == 1)
682                                         panic("uvm_page_physget: out of memory!");
683                               vm_nphysmem--;
684                               for (x = psi ; x < vm_nphysmem ; x++)
685                                         /* structure copy */
686                                         VM_PHYSMEM_PTR_SWAP(x, x + 1);
687                     }
688                     return (true);
689           }
690 
691           /* try from rear */
692           if (seg->avail_end == seg->end &&
693               seg->avail_start < seg->avail_end) {
694                     *paddrp = ctob(seg->avail_end - 1);
695                     seg->avail_end--;
696                     seg->end--;
697                     /* nothing left?   nuke it */
698                     if (seg->avail_end == seg->start) {
699                               if (vm_nphysmem == 1)
700                                         panic("uvm_page_physget: out of memory!");
701                               vm_nphysmem--;
702                               for (x = psi ; x < vm_nphysmem ; x++)
703                                         /* structure copy */
704                                         VM_PHYSMEM_PTR_SWAP(x, x + 1);
705                     }
706                     return (true);
707           }
708 
709           return false;
710 }
711 
712 bool
uvm_page_physunload_force(uvm_physseg_t psi,int freelist,paddr_t * paddrp)713 uvm_page_physunload_force(uvm_physseg_t psi, int freelist, paddr_t *paddrp)
714 {
715           int x;
716           struct uvm_physseg *seg;
717 
718           uvm_post_preload_check();
719 
720           seg = VM_PHYSMEM_PTR(psi);
721 
722           /* any room in this bank? */
723           if (seg->avail_start >= seg->avail_end) {
724                     return false; /* nope */
725           }
726 
727           *paddrp = ctob(seg->avail_start);
728           seg->avail_start++;
729           /* truncate! */
730           seg->start = seg->avail_start;
731 
732           /* nothing left?   nuke it */
733           if (seg->avail_start == seg->end) {
734                     if (vm_nphysmem == 1)
735                               panic("uvm_page_physget: out of memory!");
736                     vm_nphysmem--;
737                     for (x = psi ; x < vm_nphysmem ; x++)
738                               /* structure copy */
739                               VM_PHYSMEM_PTR_SWAP(x, x + 1);
740           }
741           return (true);
742 }
743 
744 bool
uvm_physseg_plug(paddr_t pfn,size_t pages,uvm_physseg_t * psp)745 uvm_physseg_plug(paddr_t pfn, size_t pages, uvm_physseg_t *psp)
746 {
747           int lcv;
748           struct vm_page *pgs;
749           struct uvm_physseg *ps;
750 
751 #ifdef DEBUG
752           paddr_t off;
753           uvm_physseg_t upm;
754           upm = uvm_physseg_find(pfn, &off);
755 
756           if (uvm_physseg_valid_p(upm)) /* XXX; do we allow "update" plugs ? */
757                     return false;
758 #endif
759 
760           paddr_t start = pfn;
761           paddr_t end = pfn + pages;
762           paddr_t avail_start = start;
763           paddr_t avail_end = end;
764 
765           if (uvmexp.pagesize == 0)
766                     panic("uvm_page_physload: page size not set!");
767 
768           /*
769            * do we have room?
770            */
771 
772           if (vm_nphysmem == VM_PHYSSEG_MAX) {
773                     printf("uvm_page_physload: unable to load physical memory "
774                         "segment\n");
775                     printf("\t%d segments allocated, ignoring 0x%llx -> 0x%llx\n",
776                         VM_PHYSSEG_MAX, (long long)start, (long long)end);
777                     printf("\tincrease VM_PHYSSEG_MAX\n");
778                     if (psp != NULL)
779                               *psp = UVM_PHYSSEG_TYPE_INVALID_OVERFLOW;
780                     return false;
781           }
782 
783           /*
784            * check to see if this is a "preload" (i.e. uvm_page_init hasn't been
785            * called yet, so kmem is not available).
786            */
787           pgs = uvm_post_preload_check();
788 
789           /*
790            * now insert us in the proper place in vm_physmem[]
791            */
792 
793 #if (VM_PHYSSEG_STRAT == VM_PSTRAT_RANDOM)
794           /* random: put it at the end (easy!) */
795           ps = VM_PHYSMEM_PTR(vm_nphysmem);
796           lcv = vm_nphysmem;
797 #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
798           {
799                     int x;
800                     /* sort by address for binary search */
801                     for (lcv = 0 ; lcv < vm_nphysmem ; lcv++)
802                               if (start < VM_PHYSMEM_PTR(lcv)->start)
803                                         break;
804                     ps = VM_PHYSMEM_PTR(lcv);
805                     /* move back other entries, if necessary ... */
806                     for (x = vm_nphysmem ; x > lcv ; x--)
807                               /* structure copy */
808                               VM_PHYSMEM_PTR_SWAP(x, x - 1);
809           }
810 #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST)
811           {
812                     int x;
813                     /* sort by largest segment first */
814                     for (lcv = 0 ; lcv < vm_nphysmem ; lcv++)
815                               if ((end - start) >
816                                   (VM_PHYSMEM_PTR(lcv)->end - VM_PHYSMEM_PTR(lcv)->start))
817                                         break;
818                     ps = VM_PHYSMEM_PTR(lcv);
819                     /* move back other entries, if necessary ... */
820                     for (x = vm_nphysmem ; x > lcv ; x--)
821                               /* structure copy */
822                               VM_PHYSMEM_PTR_SWAP(x, x - 1);
823           }
824 #else
825           panic("uvm_page_physload: unknown physseg strategy selected!");
826 #endif
827 
828           ps->start = start;
829           ps->end = end;
830           ps->avail_start = avail_start;
831           ps->avail_end = avail_end;
832 
833           ps->pgs = pgs;
834 
835           vm_nphysmem++;
836 
837           if (psp != NULL)
838                     *psp = lcv;
839 
840           return true;
841 }
842 
843 /*
844  * when VM_PHYSSEG_MAX is 1, we can simplify these functions
845  */
846 
847 #if VM_PHYSSEG_MAX == 1
848 static inline int vm_physseg_find_contig(struct uvm_physseg *, int, paddr_t, psize_t *);
849 #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
850 static inline int vm_physseg_find_bsearch(struct uvm_physseg *, int, paddr_t, psize_t *);
851 #else
852 static inline int vm_physseg_find_linear(struct uvm_physseg *, int, paddr_t, psize_t *);
853 #endif
854 
855 /*
856  * vm_physseg_find: find vm_physseg structure that belongs to a PA
857  */
858 inline int
uvm_physseg_find(paddr_t pframe,psize_t * offp)859 uvm_physseg_find(paddr_t pframe, psize_t *offp)
860 {
861 
862 #if VM_PHYSSEG_MAX == 1
863           return vm_physseg_find_contig(vm_physmem, vm_nphysseg, pframe, offp);
864 #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
865           return vm_physseg_find_bsearch(vm_physmem, vm_nphysseg, pframe, offp);
866 #else
867           return vm_physseg_find_linear(vm_physmem, vm_nphysseg, pframe, offp);
868 #endif
869 }
870 
871 #if VM_PHYSSEG_MAX == 1
872 static inline int
vm_physseg_find_contig(struct uvm_physseg * segs,int nsegs,paddr_t pframe,psize_t * offp)873 vm_physseg_find_contig(struct uvm_physseg *segs, int nsegs, paddr_t pframe, psize_t *offp)
874 {
875 
876           /* 'contig' case */
877           if (pframe >= segs[0].start && pframe < segs[0].end) {
878                     if (offp)
879                               *offp = pframe - segs[0].start;
880                     return(0);
881           }
882           return(-1);
883 }
884 
885 #elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
886 
887 static inline int
vm_physseg_find_bsearch(struct uvm_physseg * segs,int nsegs,paddr_t pframe,psize_t * offp)888 vm_physseg_find_bsearch(struct uvm_physseg *segs, int nsegs, paddr_t pframe, psize_t *offp)
889 {
890           /* binary search for it */
891           int       start, len, guess;
892 
893           /*
894            * if try is too large (thus target is less than try) we reduce
895            * the length to trunc(len/2) [i.e. everything smaller than "try"]
896            *
897            * if the try is too small (thus target is greater than try) then
898            * we set the new start to be (try + 1).   this means we need to
899            * reduce the length to (round(len/2) - 1).
900            *
901            * note "adjust" below which takes advantage of the fact that
902            *  (round(len/2) - 1) == trunc((len - 1) / 2)
903            * for any value of len we may have
904            */
905 
906           for (start = 0, len = nsegs ; len != 0 ; len = len / 2) {
907                     guess = start + (len / 2);    /* try in the middle */
908 
909                     /* start past our try? */
910                     if (pframe >= segs[guess].start) {
911                               /* was try correct? */
912                               if (pframe < segs[guess].end) {
913                                         if (offp)
914                                                   *offp = pframe - segs[guess].start;
915                                         return guess;            /* got it */
916                               }
917                               start = guess + 1;  /* next time, start here */
918                               len--;                        /* "adjust" */
919                     } else {
920                               /*
921                                * pframe before try, just reduce length of
922                                * region, done in "for" loop
923                                */
924                     }
925           }
926           return(-1);
927 }
928 
929 #else
930 
931 static inline int
vm_physseg_find_linear(struct uvm_physseg * segs,int nsegs,paddr_t pframe,psize_t * offp)932 vm_physseg_find_linear(struct uvm_physseg *segs, int nsegs, paddr_t pframe, psize_t *offp)
933 {
934           /* linear search for it */
935           int       lcv;
936 
937           for (lcv = 0; lcv < nsegs; lcv++) {
938                     if (pframe >= segs[lcv].start &&
939                         pframe < segs[lcv].end) {
940                               if (offp)
941                                         *offp = pframe - segs[lcv].start;
942                               return(lcv);                     /* got it */
943                     }
944           }
945           return(-1);
946 }
947 #endif
948 #endif /* UVM_HOTPLUG */
949 
950 /*
951  * PHYS_TO_VM_PAGE: find vm_page for a PA.  used by MI code to get vm_pages
952  * back from an I/O mapping (ugh!).  used in some MD code as well.  it can
953  * be prominent in flamegraphs, so optimise it and try to make it easy for
954  * the compiler by including next to the inline lookup routines.
955  */
956 struct vm_page *
uvm_phys_to_vm_page(paddr_t pa)957 uvm_phys_to_vm_page(paddr_t pa)
958 {
959 #if VM_PHYSSEG_STRAT != VM_PSTRAT_BSEARCH
960           /* 'contig' and linear cases */
961           KASSERT(vm_nphysseg > 0);
962           struct uvm_physseg *ps = &vm_physmem[0];
963           struct uvm_physseg *end = &vm_physmem[vm_nphysseg];
964           paddr_t pframe = atop(pa);
965           do {
966                     if (pframe >= ps->start && pframe < ps->end) {
967                               return &ps->pgs[pframe - ps->start];
968                     }
969           } while (VM_PHYSSEG_MAX > 1 && __predict_false(++ps < end));
970           return NULL;
971 #else
972           /* binary search for it */
973           paddr_t pf = atop(pa);
974           paddr_t   off;
975           uvm_physseg_t       upm;
976 
977           upm = uvm_physseg_find(pf, &off);
978           if (upm != UVM_PHYSSEG_TYPE_INVALID)
979                     return uvm_physseg_get_pg(upm, off);
980           return(NULL);
981 #endif
982 }
983 
984 bool
uvm_physseg_valid_p(uvm_physseg_t upm)985 uvm_physseg_valid_p(uvm_physseg_t upm)
986 {
987           struct uvm_physseg *ps;
988 
989           if (upm == UVM_PHYSSEG_TYPE_INVALID ||
990               upm == UVM_PHYSSEG_TYPE_INVALID_EMPTY ||
991               upm == UVM_PHYSSEG_TYPE_INVALID_OVERFLOW)
992                     return false;
993 
994           /*
995            * This is the delicate init dance -
996            * needs to go with the dance.
997            */
998           if (uvm.page_init_done != true)
999                     return true;
1000 
1001           ps = HANDLE_TO_PHYSSEG_NODE(upm);
1002 
1003           /* Extra checks needed only post uvm_page_init() */
1004           if (ps->pgs == NULL)
1005                     return false;
1006 
1007           /* XXX: etc. */
1008 
1009           return true;
1010 
1011 }
1012 
1013 /*
1014  * Boot protocol dictates that these must be able to return partially
1015  * initialised segments.
1016  */
1017 paddr_t
uvm_physseg_get_start(uvm_physseg_t upm)1018 uvm_physseg_get_start(uvm_physseg_t upm)
1019 {
1020           if (uvm_physseg_valid_p(upm) == false)
1021                     return (paddr_t) -1;
1022 
1023           return HANDLE_TO_PHYSSEG_NODE(upm)->start;
1024 }
1025 
1026 paddr_t
uvm_physseg_get_end(uvm_physseg_t upm)1027 uvm_physseg_get_end(uvm_physseg_t upm)
1028 {
1029           if (uvm_physseg_valid_p(upm) == false)
1030                     return (paddr_t) -1;
1031 
1032           return HANDLE_TO_PHYSSEG_NODE(upm)->end;
1033 }
1034 
1035 paddr_t
uvm_physseg_get_avail_start(uvm_physseg_t upm)1036 uvm_physseg_get_avail_start(uvm_physseg_t upm)
1037 {
1038           if (uvm_physseg_valid_p(upm) == false)
1039                     return (paddr_t) -1;
1040 
1041           return HANDLE_TO_PHYSSEG_NODE(upm)->avail_start;
1042 }
1043 
1044 #if defined(UVM_PHYSSEG_LEGACY)
1045 void
uvm_physseg_set_avail_start(uvm_physseg_t upm,paddr_t avail_start)1046 uvm_physseg_set_avail_start(uvm_physseg_t upm, paddr_t avail_start)
1047 {
1048           struct uvm_physseg *ps = HANDLE_TO_PHYSSEG_NODE(upm);
1049 
1050 #if defined(DIAGNOSTIC)
1051           paddr_t avail_end;
1052           avail_end = uvm_physseg_get_avail_end(upm);
1053           KASSERT(uvm_physseg_valid_p(upm));
1054           KASSERT(avail_start < avail_end);
1055           KASSERT(avail_start >= ps->start);
1056 #endif
1057 
1058           ps->avail_start = avail_start;
1059 }
1060 
1061 void
uvm_physseg_set_avail_end(uvm_physseg_t upm,paddr_t avail_end)1062 uvm_physseg_set_avail_end(uvm_physseg_t upm, paddr_t avail_end)
1063 {
1064           struct uvm_physseg *ps = HANDLE_TO_PHYSSEG_NODE(upm);
1065 
1066 #if defined(DIAGNOSTIC)
1067           paddr_t avail_start;
1068           avail_start = uvm_physseg_get_avail_start(upm);
1069           KASSERT(uvm_physseg_valid_p(upm));
1070           KASSERT(avail_end > avail_start);
1071           KASSERT(avail_end <= ps->end);
1072 #endif
1073 
1074           ps->avail_end = avail_end;
1075 }
1076 
1077 #endif /* UVM_PHYSSEG_LEGACY */
1078 
1079 paddr_t
uvm_physseg_get_avail_end(uvm_physseg_t upm)1080 uvm_physseg_get_avail_end(uvm_physseg_t upm)
1081 {
1082           if (uvm_physseg_valid_p(upm) == false)
1083                     return (paddr_t) -1;
1084 
1085           return HANDLE_TO_PHYSSEG_NODE(upm)->avail_end;
1086 }
1087 
1088 inline struct vm_page *
uvm_physseg_get_pg(uvm_physseg_t upm,paddr_t idx)1089 uvm_physseg_get_pg(uvm_physseg_t upm, paddr_t idx)
1090 {
1091           KASSERT(uvm_physseg_valid_p(upm));
1092           return &HANDLE_TO_PHYSSEG_NODE(upm)->pgs[idx];
1093 }
1094 
1095 #ifdef __HAVE_PMAP_PHYSSEG
1096 struct pmap_physseg *
uvm_physseg_get_pmseg(uvm_physseg_t upm)1097 uvm_physseg_get_pmseg(uvm_physseg_t upm)
1098 {
1099           KASSERT(uvm_physseg_valid_p(upm));
1100           return &(HANDLE_TO_PHYSSEG_NODE(upm)->pmseg);
1101 }
1102 #endif
1103 
1104 int
uvm_physseg_get_free_list(uvm_physseg_t upm)1105 uvm_physseg_get_free_list(uvm_physseg_t upm)
1106 {
1107           KASSERT(uvm_physseg_valid_p(upm));
1108           return HANDLE_TO_PHYSSEG_NODE(upm)->free_list;
1109 }
1110 
1111 u_long
uvm_physseg_get_start_hint(uvm_physseg_t upm)1112 uvm_physseg_get_start_hint(uvm_physseg_t upm)
1113 {
1114           KASSERT(uvm_physseg_valid_p(upm));
1115           return HANDLE_TO_PHYSSEG_NODE(upm)->start_hint;
1116 }
1117 
1118 bool
uvm_physseg_set_start_hint(uvm_physseg_t upm,u_long start_hint)1119 uvm_physseg_set_start_hint(uvm_physseg_t upm, u_long start_hint)
1120 {
1121           if (uvm_physseg_valid_p(upm) == false)
1122                     return false;
1123 
1124           HANDLE_TO_PHYSSEG_NODE(upm)->start_hint = start_hint;
1125           return true;
1126 }
1127 
1128 void
uvm_physseg_init_seg(uvm_physseg_t upm,struct vm_page * pgs)1129 uvm_physseg_init_seg(uvm_physseg_t upm, struct vm_page *pgs)
1130 {
1131           psize_t i;
1132           psize_t n;
1133           paddr_t paddr;
1134           struct uvm_physseg *seg;
1135           struct vm_page *pg;
1136 
1137           KASSERT(upm != UVM_PHYSSEG_TYPE_INVALID);
1138           KASSERT(pgs != NULL);
1139 
1140           seg = HANDLE_TO_PHYSSEG_NODE(upm);
1141           KASSERT(seg != NULL);
1142           KASSERT(seg->pgs == NULL);
1143 
1144           n = seg->end - seg->start;
1145           seg->pgs = pgs;
1146 
1147           /* init and free vm_pages (we've already zeroed them) */
1148           paddr = ctob(seg->start);
1149           for (i = 0 ; i < n ; i++, paddr += PAGE_SIZE) {
1150                     pg = &seg->pgs[i];
1151                     pg->phys_addr = paddr;
1152 #ifdef __HAVE_VM_PAGE_MD
1153                     VM_MDPAGE_INIT(pg);
1154 #endif
1155                     if (atop(paddr) >= seg->avail_start &&
1156                         atop(paddr) < seg->avail_end) {
1157                               uvmexp.npages++;
1158                               /* add page to free pool */
1159                               uvm_page_set_freelist(pg,
1160                                   uvm_page_lookup_freelist(pg));
1161                               /* Disable LOCKDEBUG: too many and too early. */
1162                               mutex_init(&pg->interlock, MUTEX_NODEBUG, IPL_NONE);
1163                               uvm_pagefree(pg);
1164                     }
1165           }
1166 }
1167 
1168 void
uvm_physseg_seg_chomp_slab(uvm_physseg_t upm,struct vm_page * pgs,size_t n)1169 uvm_physseg_seg_chomp_slab(uvm_physseg_t upm, struct vm_page *pgs, size_t n)
1170 {
1171           struct uvm_physseg *seg = HANDLE_TO_PHYSSEG_NODE(upm);
1172 
1173           /* max number of pre-boot unplug()s allowed */
1174 #define UVM_PHYSSEG_BOOT_UNPLUG_MAX VM_PHYSSEG_MAX
1175 
1176           static char btslab_ex_storage[EXTENT_FIXED_STORAGE_SIZE(UVM_PHYSSEG_BOOT_UNPLUG_MAX)];
1177 
1178           if (__predict_false(uvm.page_init_done == false)) {
1179                     seg->ext = extent_create("Boot time slab", (u_long) pgs, (u_long) (pgs + n),
1180                         (void *)btslab_ex_storage, sizeof(btslab_ex_storage), 0);
1181           } else {
1182                     seg->ext = extent_create("Hotplug slab", (u_long) pgs, (u_long) (pgs + n), NULL, 0, 0);
1183           }
1184 
1185           KASSERT(seg->ext != NULL);
1186 
1187 }
1188 
1189 struct vm_page *
uvm_physseg_seg_alloc_from_slab(uvm_physseg_t upm,size_t pages)1190 uvm_physseg_seg_alloc_from_slab(uvm_physseg_t upm, size_t pages)
1191 {
1192           int err;
1193           struct uvm_physseg *seg;
1194           struct vm_page *pgs = NULL;
1195 
1196           KASSERT(pages > 0);
1197 
1198           seg = HANDLE_TO_PHYSSEG_NODE(upm);
1199 
1200           if (__predict_false(seg->ext == NULL)) {
1201                     /*
1202                      * This is a situation unique to boot time.
1203                      * It shouldn't happen at any point other than from
1204                      * the first uvm_page.c:uvm_page_init() call
1205                      * Since we're in a loop, we can get away with the
1206                      * below.
1207                      */
1208                     KASSERT(uvm.page_init_done != true);
1209 
1210                     uvm_physseg_t upmp = uvm_physseg_get_prev(upm);
1211                     KASSERT(upmp != UVM_PHYSSEG_TYPE_INVALID);
1212 
1213                     seg->ext = HANDLE_TO_PHYSSEG_NODE(upmp)->ext;
1214 
1215                     KASSERT(seg->ext != NULL);
1216           }
1217 
1218           /* We allocate enough for this segment */
1219           err = extent_alloc(seg->ext, sizeof(*pgs) * pages, 1, 0, EX_BOUNDZERO, (u_long *)&pgs);
1220 
1221           if (err != 0) {
1222 #ifdef DEBUG
1223                     printf("%s: extent_alloc failed with error: %d \n",
1224                         __func__, err);
1225 #endif
1226           }
1227 
1228           return pgs;
1229 }
1230 
1231 /*
1232  * uvm_page_physload: load physical memory into VM system
1233  *
1234  * => all args are PFs
1235  * => all pages in start/end get vm_page structures
1236  * => areas marked by avail_start/avail_end get added to the free page pool
1237  * => we are limited to VM_PHYSSEG_MAX physical memory segments
1238  */
1239 
1240 uvm_physseg_t
uvm_page_physload(paddr_t start,paddr_t end,paddr_t avail_start,paddr_t avail_end,int free_list)1241 uvm_page_physload(paddr_t start, paddr_t end, paddr_t avail_start,
1242     paddr_t avail_end, int free_list)
1243 {
1244           struct uvm_physseg *ps;
1245           uvm_physseg_t upm;
1246 
1247           if (__predict_true(uvm.page_init_done == true))
1248                     panic("%s: unload attempted after uvm_page_init()\n", __func__);
1249           if (uvmexp.pagesize == 0)
1250                     panic("uvm_page_physload: page size not set!");
1251           if (free_list >= VM_NFREELIST || free_list < VM_FREELIST_DEFAULT)
1252                     panic("uvm_page_physload: bad free list %d", free_list);
1253           if (start >= end)
1254                     panic("uvm_page_physload: start[%" PRIxPADDR "] >= end[%"
1255                         PRIxPADDR "]", start, end);
1256 
1257           if (uvm_physseg_plug(start, end - start, &upm) == false) {
1258                     panic("uvm_physseg_plug() failed at boot.");
1259                     /* NOTREACHED */
1260                     return UVM_PHYSSEG_TYPE_INVALID; /* XXX: correct type */
1261           }
1262 
1263           ps = HANDLE_TO_PHYSSEG_NODE(upm);
1264 
1265           /* Legacy */
1266           ps->avail_start = avail_start;
1267           ps->avail_end = avail_end;
1268 
1269           ps->free_list = free_list; /* XXX: */
1270 
1271 
1272           return upm;
1273 }
1274 
1275 bool
uvm_physseg_unplug(paddr_t pfn,size_t pages)1276 uvm_physseg_unplug(paddr_t pfn, size_t pages)
1277 {
1278           uvm_physseg_t upm;
1279           paddr_t off = 0, start __diagused, end;
1280           struct uvm_physseg *seg;
1281 
1282           upm = uvm_physseg_find(pfn, &off);
1283 
1284           if (!uvm_physseg_valid_p(upm)) {
1285                     printf("%s: Tried to unplug from unknown offset\n", __func__);
1286                     return false;
1287           }
1288 
1289           seg = HANDLE_TO_PHYSSEG_NODE(upm);
1290 
1291           start = uvm_physseg_get_start(upm);
1292           end = uvm_physseg_get_end(upm);
1293 
1294           if (end < (pfn + pages)) {
1295                     printf("%s: Tried to unplug oversized span \n", __func__);
1296                     return false;
1297           }
1298 
1299           KASSERT(pfn == start + off); /* sanity */
1300 
1301           if (__predict_true(uvm.page_init_done == true)) {
1302                     /* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
1303                     if (extent_free(seg->ext, (u_long)(seg->pgs + off), sizeof(struct vm_page) * pages, EX_MALLOCOK | EX_NOWAIT) != 0)
1304                               return false;
1305           }
1306 
1307           if (off == 0 && (pfn + pages) == end) {
1308 #if defined(UVM_HOTPLUG) /* rbtree implementation */
1309                     int segcount = 0;
1310                     struct uvm_physseg *current_ps;
1311                     /* Complete segment */
1312                     if (uvm_physseg_graph.nentries == 1)
1313                               panic("%s: out of memory!", __func__);
1314 
1315                     if (__predict_true(uvm.page_init_done == true)) {
1316                               RB_TREE_FOREACH(current_ps, &(uvm_physseg_graph.rb_tree)) {
1317                                         if (seg->ext == current_ps->ext)
1318                                                   segcount++;
1319                               }
1320                               KASSERT(segcount > 0);
1321 
1322                               if (segcount == 1) {
1323                                         extent_destroy(seg->ext);
1324                               }
1325 
1326                               /*
1327                                * We assume that the unplug will succeed from
1328                                *  this point onwards
1329                                */
1330                               uvmexp.npages -= (int) pages;
1331                     }
1332 
1333                     rb_tree_remove_node(&(uvm_physseg_graph.rb_tree), upm);
1334                     memset(seg, 0, sizeof(struct uvm_physseg));
1335                     uvm_physseg_free(seg, sizeof(struct uvm_physseg));
1336                     uvm_physseg_graph.nentries--;
1337 #else /* UVM_HOTPLUG */
1338                     int x;
1339                     if (vm_nphysmem == 1)
1340                               panic("uvm_page_physget: out of memory!");
1341                     vm_nphysmem--;
1342                     for (x = upm ; x < vm_nphysmem ; x++)
1343                               /* structure copy */
1344                               VM_PHYSMEM_PTR_SWAP(x, x + 1);
1345 #endif /* UVM_HOTPLUG */
1346                     /* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
1347                     return true;
1348           }
1349 
1350           if (off > 0 &&
1351               (pfn + pages) < end) {
1352 #if defined(UVM_HOTPLUG) /* rbtree implementation */
1353                     /* middle chunk - need a new segment */
1354                     struct uvm_physseg *ps, *current_ps;
1355                     ps = uvm_physseg_alloc(sizeof (struct uvm_physseg));
1356                     if (ps == NULL) {
1357                               printf("%s: Unable to allocated new fragment vm_physseg \n",
1358                                   __func__);
1359                               return false;
1360                     }
1361 
1362                     /* Remove middle chunk */
1363                     if (__predict_true(uvm.page_init_done == true)) {
1364                               KASSERT(seg->ext != NULL);
1365                               ps->ext = seg->ext;
1366 
1367                               /* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
1368                               /*
1369                                * We assume that the unplug will succeed from
1370                                *  this point onwards
1371                                */
1372                               uvmexp.npages -= (int) pages;
1373                     }
1374 
1375                     ps->start = pfn + pages;
1376                     ps->avail_start = ps->start; /* XXX: Legacy */
1377 
1378                     ps->end = seg->end;
1379                     ps->avail_end = ps->end; /* XXX: Legacy */
1380 
1381                     seg->end = pfn;
1382                     seg->avail_end = seg->end; /* XXX: Legacy */
1383 
1384 
1385                     /*
1386                      * The new pgs array points to the beginning of the
1387                      * tail fragment.
1388                      */
1389                     if (__predict_true(uvm.page_init_done == true))
1390                               ps->pgs = seg->pgs + off + pages;
1391 
1392                     current_ps = rb_tree_insert_node(&(uvm_physseg_graph.rb_tree), ps);
1393                     if (current_ps != ps) {
1394                               panic("uvm_page_physload: Duplicate address range detected!");
1395                     }
1396                     uvm_physseg_graph.nentries++;
1397 #else /* UVM_HOTPLUG */
1398                     panic("%s: can't unplug() from the middle of a segment without"
1399                         " UVM_HOTPLUG\n",  __func__);
1400                     /* NOTREACHED */
1401 #endif /* UVM_HOTPLUG */
1402                     return true;
1403           }
1404 
1405           if (off == 0 && (pfn + pages) < end) {
1406                     /* Remove front chunk */
1407                     if (__predict_true(uvm.page_init_done == true)) {
1408                               /* XXX: KASSERT() that seg->pgs[] are not on any uvm lists */
1409                               /*
1410                                * We assume that the unplug will succeed from
1411                                *  this point onwards
1412                                */
1413                               uvmexp.npages -= (int) pages;
1414                     }
1415 
1416                     /* Truncate */
1417                     seg->start = pfn + pages;
1418                     seg->avail_start = seg->start; /* XXX: Legacy */
1419 
1420                     /*
1421                      * Move the pgs array start to the beginning of the
1422                      * tail end.
1423                      */
1424                     if (__predict_true(uvm.page_init_done == true))
1425                               seg->pgs += pages;
1426 
1427                     return true;
1428           }
1429 
1430           if (off > 0 && (pfn + pages) == end) {
1431                     /* back chunk */
1432 
1433 
1434                     /* Truncate! */
1435                     seg->end = pfn;
1436                     seg->avail_end = seg->end; /* XXX: Legacy */
1437 
1438                     uvmexp.npages -= (int) pages;
1439 
1440                     return true;
1441           }
1442 
1443           printf("%s: Tried to unplug unknown range \n", __func__);
1444 
1445           return false;
1446 }
1447