xref: /illumos-gate/usr/src/uts/sun4/os/memlist.c (revision 56f33205)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/sysmacros.h>
29 #include <sys/signal.h>
30 #include <sys/systm.h>
31 #include <sys/user.h>
32 #include <sys/mman.h>
33 #include <sys/class.h>
34 #include <sys/proc.h>
35 #include <sys/procfs.h>
36 #include <sys/kmem.h>
37 #include <sys/cred.h>
38 #include <sys/archsystm.h>
39 #include <sys/machsystm.h>
40 
41 #include <sys/reboot.h>
42 #include <sys/uadmin.h>
43 
44 #include <sys/vfs.h>
45 #include <sys/vnode.h>
46 #include <sys/session.h>
47 #include <sys/ucontext.h>
48 
49 #include <sys/dnlc.h>
50 #include <sys/var.h>
51 #include <sys/cmn_err.h>
52 #include <sys/debug.h>
53 #include <sys/thread.h>
54 #include <sys/vtrace.h>
55 #include <sys/consdev.h>
56 #include <sys/frame.h>
57 #include <sys/stack.h>
58 #include <sys/swap.h>
59 #include <sys/vmparam.h>
60 #include <sys/cpuvar.h>
61 
62 #include <sys/privregs.h>
63 
64 #include <vm/hat.h>
65 #include <vm/anon.h>
66 #include <vm/as.h>
67 #include <vm/page.h>
68 #include <vm/seg.h>
69 #include <vm/seg_kmem.h>
70 #include <vm/seg_map.h>
71 #include <vm/seg_vn.h>
72 #include <vm/vm_dep.h>
73 
74 #include <sys/exec.h>
75 #include <sys/acct.h>
76 #include <sys/modctl.h>
77 #include <sys/tuneable.h>
78 
79 #include <c2/audit.h>
80 
81 #include <sys/trap.h>
82 #include <sys/sunddi.h>
83 #include <sys/bootconf.h>
84 #include <sys/memlist.h>
85 #include <sys/memlist_plat.h>
86 #include <sys/systeminfo.h>
87 #include <sys/promif.h>
88 #include <sys/prom_plat.h>
89 
90 u_longlong_t	spec_hole_start = 0x80000000000ull;
91 u_longlong_t	spec_hole_end = 0xfffff80000000000ull;
92 
93 pgcnt_t
num_phys_pages()94 num_phys_pages()
95 {
96 	pgcnt_t npages = 0;
97 	struct memlist *mp;
98 
99 	for (mp = phys_install; mp != NULL; mp = mp->ml_next)
100 		npages += mp->ml_size >> PAGESHIFT;
101 
102 	return (npages);
103 }
104 
105 
106 pgcnt_t
size_virtalloc(prom_memlist_t * avail,size_t nelems)107 size_virtalloc(prom_memlist_t *avail, size_t nelems)
108 {
109 
110 	u_longlong_t	start, end;
111 	pgcnt_t		allocpages = 0;
112 	uint_t		hole_allocated = 0;
113 	uint_t		i;
114 
115 	for (i = 0; i < nelems - 1; i++) {
116 
117 		start = avail[i].addr + avail[i].size;
118 		end = avail[i + 1].addr;
119 
120 		/*
121 		 * Notes:
122 		 *
123 		 * (1) OBP on platforms with US I/II pre-allocates the hole
124 		 * represented by [spec_hole_start, spec_hole_end);
125 		 * pre-allocation is done to make this range unavailable
126 		 * for any allocation.
127 		 *
128 		 * (2) OBP on starcat always pre-allocates the hole similar to
129 		 * platforms with US I/II.
130 		 *
131 		 * (3) OBP on serengeti does _not_ pre-allocate the hole.
132 		 *
133 		 * (4) OBP ignores Spitfire Errata #21; i.e. it does _not_
134 		 * fill up or pre-allocate an additional 4GB on both sides
135 		 * of the hole.
136 		 *
137 		 * (5) kernel virtual range [spec_hole_start, spec_hole_end)
138 		 * is _not_ used on any platform including those with
139 		 * UltraSPARC III where there is no hole.
140 		 *
141 		 * Algorithm:
142 		 *
143 		 * Check if range [spec_hole_start, spec_hole_end) is
144 		 * pre-allocated by OBP; if so, subtract that range from
145 		 * allocpages.
146 		 */
147 		if (end >= spec_hole_end && start <= spec_hole_start)
148 			hole_allocated = 1;
149 
150 		allocpages += btopr(end - start);
151 	}
152 
153 	if (hole_allocated)
154 		allocpages -= btop(spec_hole_end - spec_hole_start);
155 
156 	return (allocpages);
157 }
158 
159 /*
160  * Returns the max contiguous physical memory present in the
161  * memlist "physavail".
162  */
163 uint64_t
get_max_phys_size(struct memlist * physavail)164 get_max_phys_size(
165 	struct memlist	*physavail)
166 {
167 	uint64_t	max_size = 0;
168 
169 	for (; physavail; physavail = physavail->ml_next) {
170 		if (physavail->ml_size > max_size)
171 			max_size = physavail->ml_size;
172 	}
173 
174 	return (max_size);
175 }
176 
177 
178 static void
more_pages(uint64_t base,uint64_t len)179 more_pages(uint64_t base, uint64_t len)
180 {
181 	void kphysm_add();
182 
183 	kphysm_add(base, len, 1);
184 }
185 
186 static void
less_pages(uint64_t base,uint64_t len)187 less_pages(uint64_t base, uint64_t len)
188 {
189 	uint64_t pa, end = base + len;
190 	extern int kcage_on;
191 
192 	for (pa = base; pa < end; pa += PAGESIZE) {
193 		pfn_t pfnum;
194 		page_t *pp;
195 
196 		pfnum = (pfn_t)(pa >> PAGESHIFT);
197 		if ((pp = page_numtopp_nolock(pfnum)) == NULL)
198 			cmn_err(CE_PANIC, "missing pfnum %lx", pfnum);
199 
200 		/*
201 		 * must break up any large pages that may have
202 		 * constituent pages being utilized for
203 		 * prom_alloc()'s. page_reclaim() can't handle
204 		 * large pages.
205 		 */
206 		if (pp->p_szc != 0)
207 			page_boot_demote(pp);
208 
209 		if (!PAGE_LOCKED(pp) && pp->p_lckcnt == 0) {
210 			/*
211 			 * Ahhh yes, a prom page,
212 			 * suck it off the freelist,
213 			 * lock it, and hashin on prom_pages vp.
214 			 */
215 			if (page_trylock(pp, SE_EXCL) == 0)
216 				cmn_err(CE_PANIC, "prom page locked");
217 
218 			(void) page_reclaim(pp, NULL);
219 			/*
220 			 * vnode offsets on the prom_ppages vnode
221 			 * are page numbers (gack) for >32 bit
222 			 * physical memory machines.
223 			 */
224 			(void) page_hashin(pp, &promvp,
225 			    (offset_t)pfnum, NULL);
226 
227 			if (kcage_on) {
228 				ASSERT(pp->p_szc == 0);
229 				if (PP_ISNORELOC(pp) == 0) {
230 					PP_SETNORELOC(pp);
231 					PLCNT_XFER_NORELOC(pp);
232 				}
233 			}
234 			(void) page_pp_lock(pp, 0, 1);
235 		}
236 	}
237 }
238 
239 void
diff_memlists(struct memlist * proto,struct memlist * diff,void (* func)())240 diff_memlists(struct memlist *proto, struct memlist *diff, void (*func)())
241 {
242 	uint64_t p_base, p_end, d_base, d_end;
243 
244 	while (proto != NULL) {
245 		/*
246 		 * find diff item which may overlap with proto item
247 		 * if none, apply func to all of proto item
248 		 */
249 		while (diff != NULL &&
250 		    proto->ml_address >= diff->ml_address + diff->ml_size)
251 			diff = diff->ml_next;
252 		if (diff == NULL) {
253 			(*func)(proto->ml_address, proto->ml_size);
254 			proto = proto->ml_next;
255 			continue;
256 		}
257 		if (proto->ml_address == diff->ml_address &&
258 		    proto->ml_size == diff->ml_size) {
259 			proto = proto->ml_next;
260 			diff = diff->ml_next;
261 			continue;
262 		}
263 
264 		p_base = proto->ml_address;
265 		p_end = p_base + proto->ml_size;
266 		d_base = diff->ml_address;
267 		d_end = d_base + diff->ml_size;
268 		/*
269 		 * here p_base < d_end
270 		 * there are 5 cases
271 		 */
272 
273 		/*
274 		 *	d_end
275 		 *	d_base
276 		 *  p_end
277 		 *  p_base
278 		 *
279 		 * apply func to all of proto item
280 		 */
281 		if (p_end <= d_base) {
282 			(*func)(p_base, proto->ml_size);
283 			proto = proto->ml_next;
284 			continue;
285 		}
286 
287 		/*
288 		 * ...
289 		 *	d_base
290 		 *  p_base
291 		 *
292 		 * normalize by applying func from p_base to d_base
293 		 */
294 		if (p_base < d_base)
295 			(*func)(p_base, d_base - p_base);
296 
297 		if (p_end <= d_end) {
298 			/*
299 			 *	d_end
300 			 *  p_end
301 			 *	d_base
302 			 *  p_base
303 			 *
304 			 *	-or-
305 			 *
306 			 *	d_end
307 			 *  p_end
308 			 *  p_base
309 			 *	d_base
310 			 *
311 			 * any non-overlapping ranges applied above,
312 			 * so just continue
313 			 */
314 			proto = proto->ml_next;
315 			continue;
316 		}
317 
318 		/*
319 		 *  p_end
320 		 *	d_end
321 		 *	d_base
322 		 *  p_base
323 		 *
324 		 *	-or-
325 		 *
326 		 *  p_end
327 		 *	d_end
328 		 *  p_base
329 		 *	d_base
330 		 *
331 		 * Find overlapping d_base..d_end ranges, and apply func
332 		 * where no overlap occurs.  Stop when d_base is above
333 		 * p_end
334 		 */
335 		for (p_base = d_end, diff = diff->ml_next; diff != NULL;
336 		    p_base = d_end, diff = diff->ml_next) {
337 			d_base = diff->ml_address;
338 			d_end = d_base + diff->ml_size;
339 			if (p_end <= d_base) {
340 				(*func)(p_base, p_end - p_base);
341 				break;
342 			} else
343 				(*func)(p_base, d_base - p_base);
344 		}
345 		if (diff == NULL)
346 			(*func)(p_base, p_end - p_base);
347 		proto = proto->ml_next;
348 	}
349 }
350 
351 void
sync_memlists(struct memlist * orig,struct memlist * new)352 sync_memlists(struct memlist *orig, struct memlist *new)
353 {
354 
355 	/*
356 	 * Find pages allocated via prom by looking for
357 	 * pages on orig, but no on new.
358 	 */
359 	diff_memlists(orig, new, less_pages);
360 
361 	/*
362 	 * Find pages free'd via prom by looking for
363 	 * pages on new, but not on orig.
364 	 */
365 	diff_memlists(new, orig, more_pages);
366 }
367 
368 
369 /*
370  * Find the page number of the highest installed physical
371  * page and the number of pages installed (one cannot be
372  * calculated from the other because memory isn't necessarily
373  * contiguous).
374  */
375 void
installed_top_size_memlist_array(prom_memlist_t * list,size_t nelems,pfn_t * topp,pgcnt_t * sumpagesp)376 installed_top_size_memlist_array(
377 	prom_memlist_t *list,	/* base of array */
378 	size_t	nelems,		/* number of elements */
379 	pfn_t *topp,		/* return ptr for top value */
380 	pgcnt_t *sumpagesp)	/* return prt for sum of installed pages */
381 {
382 	pfn_t top = 0;
383 	pgcnt_t sumpages = 0;
384 	pfn_t highp;		/* high page in a chunk */
385 	size_t i;
386 
387 	for (i = 0; i < nelems; list++, i++) {
388 		highp = (list->addr + list->size - 1) >> PAGESHIFT;
389 		if (top < highp)
390 			top = highp;
391 		sumpages += (list->size >> PAGESHIFT);
392 	}
393 
394 	*topp = top;
395 	*sumpagesp = sumpages;
396 }
397 
398 /*
399  * Copy a memory list.  Used in startup() to copy boot's
400  * memory lists to the kernel.
401  */
402 void
copy_memlist(prom_memlist_t * src,size_t nelems,struct memlist ** dstp)403 copy_memlist(
404 	prom_memlist_t	*src,
405 	size_t		nelems,
406 	struct memlist	**dstp)
407 {
408 	struct memlist *dst, *prev;
409 	size_t	i;
410 
411 	dst = *dstp;
412 	prev = dst;
413 
414 	for (i = 0; i < nelems; src++, i++) {
415 		dst->ml_address = src->addr;
416 		dst->ml_size = src->size;
417 		dst->ml_next = 0;
418 		if (prev == dst) {
419 			dst->ml_prev = 0;
420 			dst++;
421 		} else {
422 			dst->ml_prev = prev;
423 			prev->ml_next = dst;
424 			dst++;
425 			prev++;
426 		}
427 	}
428 
429 	*dstp = dst;
430 }
431 
432 
433 static struct bootmem_props {
434 	prom_memlist_t	*ptr;
435 	size_t		nelems;		/* actual number of elements */
436 	size_t		maxsize;	/* max buffer */
437 } bootmem_props[3];
438 
439 #define	PHYSINSTALLED	0
440 #define	PHYSAVAIL	1
441 #define	VIRTAVAIL	2
442 
443 /*
444  * Comapct contiguous memory list elements
445  */
446 static void
compact_promlist(struct bootmem_props * bpp)447 compact_promlist(struct bootmem_props *bpp)
448 {
449 	int i = 0, j;
450 	struct prom_memlist *pmp = bpp->ptr;
451 
452 	for (;;) {
453 		if (pmp[i].addr + pmp[i].size == pmp[i+1].addr) {
454 			pmp[i].size += pmp[i+1].size;
455 			bpp->nelems--;
456 			for (j = i + 1; j < bpp->nelems; j++)
457 				pmp[j] = pmp[j+1];
458 			pmp[j].addr = 0;
459 		} else
460 			i++;
461 		if (i == bpp->nelems)
462 			break;
463 	}
464 }
465 
466 /*
467  *  Sort prom memory lists into ascending order
468  */
469 static void
sort_promlist(struct bootmem_props * bpp)470 sort_promlist(struct bootmem_props *bpp)
471 {
472 	int i, j, min;
473 	struct prom_memlist *pmp = bpp->ptr;
474 	struct prom_memlist temp;
475 
476 	for (i = 0; i < bpp->nelems; i++) {
477 		min = i;
478 
479 		for (j = i+1; j < bpp->nelems; j++)  {
480 			if (pmp[j].addr < pmp[min].addr)
481 				min = j;
482 		}
483 
484 		if (i != min)  {
485 			/* Swap pmp[i] and pmp[min] */
486 			temp = pmp[min];
487 			pmp[min] = pmp[i];
488 			pmp[i] = temp;
489 		}
490 	}
491 }
492 
493 static int max_bootlist_sz;
494 
495 void
init_boot_memlists(void)496 init_boot_memlists(void)
497 {
498 	size_t	size, len;
499 	char *start;
500 	struct bootmem_props *tmp;
501 
502 	/*
503 	 * These lists can get fragmented as the prom allocates
504 	 * memory, so generously round up.
505 	 */
506 	size = prom_phys_installed_len() + prom_phys_avail_len() +
507 	    prom_virt_avail_len();
508 	size *= 4;
509 	size = roundup(size, PAGESIZE);
510 	start = prom_alloc(0, size, BO_NO_ALIGN);
511 
512 	/*
513 	 * Get physinstalled
514 	 */
515 	tmp = &bootmem_props[PHYSINSTALLED];
516 	len = prom_phys_installed_len();
517 	if (len == 0)
518 		panic("no \"reg\" in /memory");
519 	tmp->nelems = len / sizeof (struct prom_memlist);
520 	tmp->maxsize = len;
521 	tmp->ptr = (prom_memlist_t *)start;
522 	start += len;
523 	size -= len;
524 	(void) prom_phys_installed((caddr_t)tmp->ptr);
525 	sort_promlist(tmp);
526 	compact_promlist(tmp);
527 
528 	/*
529 	 * Start out giving each half of available space
530 	 */
531 	max_bootlist_sz = size;
532 	len = size / 2;
533 	tmp = &bootmem_props[PHYSAVAIL];
534 	tmp->maxsize = len;
535 	tmp->ptr = (prom_memlist_t *)start;
536 	start += len;
537 
538 	tmp = &bootmem_props[VIRTAVAIL];
539 	tmp->maxsize = len;
540 	tmp->ptr = (prom_memlist_t *)start;
541 }
542 
543 
544 void
copy_boot_memlists(prom_memlist_t ** physinstalled,size_t * physinstalled_len,prom_memlist_t ** physavail,size_t * physavail_len,prom_memlist_t ** virtavail,size_t * virtavail_len)545 copy_boot_memlists(
546     prom_memlist_t **physinstalled, size_t *physinstalled_len,
547     prom_memlist_t **physavail, size_t *physavail_len,
548     prom_memlist_t **virtavail, size_t *virtavail_len)
549 {
550 	size_t	plen, vlen, move = 0;
551 	struct bootmem_props *il, *pl, *vl;
552 
553 	plen = prom_phys_avail_len();
554 	if (plen == 0)
555 		panic("no \"available\" in /memory");
556 	vlen = prom_virt_avail_len();
557 	if (vlen == 0)
558 		panic("no \"available\" in /virtual-memory");
559 	if (plen + vlen > max_bootlist_sz)
560 		panic("ran out of prom_memlist space");
561 
562 	pl = &bootmem_props[PHYSAVAIL];
563 	vl = &bootmem_props[VIRTAVAIL];
564 
565 	/*
566 	 * re-adjust ptrs if needed
567 	 */
568 	if (plen > pl->maxsize) {
569 		/* move virt avail up */
570 		move = plen - pl->maxsize;
571 		pl->maxsize = plen;
572 		vl->ptr += move / sizeof (struct prom_memlist);
573 		vl->maxsize -= move;
574 	} else if (vlen > vl->maxsize) {
575 		/* move virt avail down */
576 		move = vlen - vl->maxsize;
577 		vl->maxsize = vlen;
578 		vl->ptr -= move / sizeof (struct prom_memlist);
579 		pl->maxsize -= move;
580 	}
581 
582 	pl->nelems = plen / sizeof (struct prom_memlist);
583 	vl->nelems = vlen / sizeof (struct prom_memlist);
584 
585 	/* now we can retrieve the properties */
586 	(void) prom_phys_avail((caddr_t)pl->ptr);
587 	(void) prom_virt_avail((caddr_t)vl->ptr);
588 
589 	/* .. and sort them */
590 	sort_promlist(pl);
591 	sort_promlist(vl);
592 
593 	il = &bootmem_props[PHYSINSTALLED];
594 	*physinstalled = il->ptr;
595 	*physinstalled_len = il->nelems;
596 
597 	*physavail = pl->ptr;
598 	*physavail_len = pl->nelems;
599 
600 	*virtavail = vl->ptr;
601 	*virtavail_len = vl->nelems;
602 }
603 
604 
605 /*
606  * Find the page number of the highest installed physical
607  * page and the number of pages installed (one cannot be
608  * calculated from the other because memory isn't necessarily
609  * contiguous).
610  */
611 void
installed_top_size(struct memlist * list,pfn_t * topp,pgcnt_t * sumpagesp)612 installed_top_size(
613 	struct memlist *list,	/* pointer to start of installed list */
614 	pfn_t *topp,		/* return ptr for top value */
615 	pgcnt_t *sumpagesp)	/* return prt for sum of installed pages */
616 {
617 	pfn_t top = 0;
618 	pfn_t highp;		/* high page in a chunk */
619 	pgcnt_t sumpages = 0;
620 
621 	for (; list; list = list->ml_next) {
622 		highp = (list->ml_address + list->ml_size - 1) >> PAGESHIFT;
623 		if (top < highp)
624 			top = highp;
625 		sumpages += (uint_t)(list->ml_size >> PAGESHIFT);
626 	}
627 
628 	*topp = top;
629 	*sumpagesp = sumpages;
630 }
631