|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * @OSF_COPYRIGHT@
24: */
25: /*
26: * Mach Operating System
27: * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
28: * All Rights Reserved.
29: *
30: * Permission to use, copy, modify and distribute this software and its
31: * documentation is hereby granted, provided that both the copyright
32: * notice and this permission notice appear in all copies of the
33: * software, derivative works or modified versions, and any portions
34: * thereof, and that both notices appear in supporting documentation.
35: *
36: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
37: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
38: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
39: *
40: * Carnegie Mellon requests users of this software to return to
41: *
42: * Software Distribution Coordinator or [email protected]
43: * School of Computer Science
44: * Carnegie Mellon University
45: * Pittsburgh PA 15213-3890
46: *
47: * any improvements or extensions that they make and grant Carnegie Mellon
48: * the rights to redistribute these changes.
49: */
50: /*
51: */
52: /*
53: * File: kern/zalloc.c
54: * Author: Avadis Tevanian, Jr.
55: *
56: * Zone-based memory allocator. A zone is a collection of fixed size
57: * data blocks for which quick allocation/deallocation is possible.
58: */
59:
60: #include <zone_debug.h>
61: #include <norma_vm.h>
62: #include <mach_kdb.h>
63: #include <kern/assert.h>
64: #include <kern/macro_help.h>
65: #include <kern/sched.h>
66: #include <kern/lock.h>
67: #include <kern/sched_prim.h>
68: #include <kern/misc_protos.h>
69: #include <kern/zalloc.h>
70: #include <kern/rtmalloc.h>
71: #include <mach/vm_param.h>
72: #include <vm/vm_kern.h>
73: #include <machine/machparam.h>
74:
75:
76: #if MACH_ASSERT
77:
78: /* Detect use of zone elt after freeing it by two methods:
79: * (1) Range-check the free-list "next" ptr for sanity.
80: * (2) Store the ptr in two different words, and compare them against
81: * each other when re-using the zone elt, to detect modifications;
82: */
83:
84: #if defined(__alpha)
85:
86: #define is_kernel_data_addr(a) \
87: (!(a) || IS_SYS_VA(a) && !((a) & (sizeof(long)-1)))
88:
89: #else /* !defined(__alpha) */
90:
91: #define is_kernel_data_addr(a) \
92: (!(a) || (a) >= VM_MIN_KERNEL_ADDRESS && !((a) & 0x3))
93:
94: #endif /* defined(__alpha) */
95:
96: /* Should we set all words of the zone element to an illegal address
97: * when it is freed, to help catch usage after freeing? The down-side
98: * is that this obscures the identity of the freed element.
99: */
100: boolean_t zfree_clear = FALSE;
101:
102: #define ADD_TO_ZONE(zone, element) \
103: MACRO_BEGIN \
104: if (zfree_clear) \
105: { int i; \
106: for (i=1; \
107: i < zone->elem_size/sizeof(vm_offset_t) - 1; \
108: i++) \
109: ((vm_offset_t *)(element))[i] = 0xdeadbeef; \
110: } \
111: ((vm_offset_t *)(element))[0] = (zone)->free_elements; \
112: (zone)->free_elements = (vm_offset_t) (element); \
113: (zone)->count--; \
114: MACRO_END
115:
116: #define REMOVE_FROM_ZONE(zone, ret, type) \
117: MACRO_BEGIN \
118: (ret) = (type) (zone)->free_elements; \
119: if ((ret) != (type) 0) { \
120: if (!is_kernel_data_addr(((vm_offset_t *)(ret))[0])) { \
121: panic("A freed zone element has been modified.\n"); \
122: } \
123: (zone)->count++; \
124: (zone)->free_elements = *((vm_offset_t *)(ret)); \
125: } \
126: MACRO_END
127: #else /* MACH_ASSERT */
128:
129: #define ADD_TO_ZONE(zone, element) \
130: MACRO_BEGIN \
131: *((vm_offset_t *)(element)) = (zone)->free_elements; \
132: (zone)->free_elements = (vm_offset_t) (element); \
133: (zone)->count--; \
134: MACRO_END
135:
136: #define REMOVE_FROM_ZONE(zone, ret, type) \
137: MACRO_BEGIN \
138: (ret) = (type) (zone)->free_elements; \
139: if ((ret) != (type) 0) { \
140: (zone)->count++; \
141: (zone)->free_elements = *((vm_offset_t *)(ret)); \
142: } \
143: MACRO_END
144:
145: #endif /* MACH_ASSERT */
146:
147: #if ZONE_DEBUG
148: #define zone_debug_enabled(z) z->active_zones.next
149: #endif /* ZONE_DEBUG */
150:
151: /*
152: * Support for garbage collection of unused zone pages:
153: */
154:
155: struct zone_page_table_entry {
156: struct zone_page_table_entry *next;
157: short in_free_list;
158: short alloc_count;
159: };
160:
161: extern struct zone_page_table_entry * zone_page_table;
162:
163: #define lock_zone_page_table() simple_lock(&zone_page_table_lock)
164: #define unlock_zone_page_table() simple_unlock(&zone_page_table_lock)
165:
166: #define zone_page(addr) \
167: (&(zone_page_table[(atop(((vm_offset_t)addr) - zone_map_min_address))]))
168:
169: /* Forwards */
170: void zone_page_init(
171: vm_offset_t addr,
172: vm_size_t size,
173: int value);
174:
175: void zone_page_alloc(
176: vm_offset_t addr,
177: vm_size_t size);
178:
179: void zone_add_free_page_list(
180: struct zone_page_table_entry **free_list,
181: vm_offset_t addr,
182: vm_size_t size);
183: void zone_page_dealloc(
184: vm_offset_t addr,
185: vm_size_t size);
186:
187: void zone_page_in_use(
188: vm_offset_t addr,
189: vm_size_t size);
190:
191: void zone_page_free(
192: vm_offset_t addr,
193: vm_size_t size);
194:
195: boolean_t zone_page_collectable(
196: vm_offset_t addr,
197: vm_size_t size);
198:
199: void zone_page_keep(
200: vm_offset_t addr,
201: vm_size_t size);
202:
203: #if ZONE_DEBUG && MACH_KDB
204: int zone_count(
205: zone_t z,
206: int tail);
207: #endif /* ZONE_DEBUG && MACH_KDB */
208:
209: vm_map_t zone_map = VM_MAP_NULL;
210:
211: zone_t zone_zone = ZONE_NULL; /* the zone containing other zones */
212:
213: /*
214: * The VM system gives us an initial chunk of memory.
215: * It has to be big enough to allocate the zone_zone
216: */
217:
218: vm_offset_t zdata;
219: vm_size_t zdata_size;
220:
221: #define lock_zone(zone) \
222: MACRO_BEGIN \
223: simple_lock(&zone->lock); \
224: MACRO_END
225:
226: #define unlock_zone(zone) \
227: MACRO_BEGIN \
228: simple_unlock(&zone->lock); \
229: MACRO_END
230:
231: #define lock_zone_init(zone) \
232: MACRO_BEGIN \
233: simple_lock_init(&zone->lock, ETAP_MISC_ZONE); \
234: MACRO_END
235:
236: #define lock_try_zone(zone) simple_lock_try(&zone->lock)
237:
238: kern_return_t zget_space(
239: vm_offset_t size,
240: vm_offset_t *result);
241:
242: decl_simple_lock_data(,zget_space_lock)
243: vm_offset_t zalloc_next_space;
244: vm_offset_t zalloc_end_of_space;
245: vm_size_t zalloc_wasted_space;
246:
247: /*
248: * Garbage collection map information
249: */
250: decl_simple_lock_data(, zone_page_table_lock)
251: struct zone_page_table_entry * zone_page_table;
252: vm_offset_t zone_map_min_address;
253: vm_offset_t zone_map_max_address;
254: integer_t zone_pages;
255:
256: /*
257: * Exclude more than one concurrent garbage collection
258: */
259: decl_mutex_data(, zone_gc_lock)
260:
261: #define from_zone_map(addr) \
262: ((vm_offset_t)(addr) >= zone_map_min_address && \
263: (vm_offset_t)(addr) < zone_map_max_address)
264:
265: #define ZONE_PAGE_USED 0
266: #define ZONE_PAGE_UNUSED -1
267:
268:
269: /*
270: * Protects first_zone, last_zone, num_zones,
271: * and the next_zone field of zones.
272: */
273: decl_simple_lock_data(, all_zones_lock)
274: zone_t first_zone;
275: zone_t *last_zone;
276: int num_zones;
277:
278: /*
279: * zinit initializes a new zone. The zone data structures themselves
280: * are stored in a zone, which is initially a static structure that
281: * is initialized by zone_init.
282: */
283: zone_t
284: zinit(
285: vm_size_t size, /* the size of an element */
286: vm_size_t max, /* maximum memory to use */
287: vm_size_t alloc, /* allocation size */
288: char *name) /* a name for the zone */
289: {
290: zone_t z;
291:
292: if (zone_zone == ZONE_NULL) {
293: if (zget_space(sizeof(struct zone), (vm_offset_t *)&z)
294: != KERN_SUCCESS)
295: return(ZONE_NULL);
296: } else
297: z = (zone_t) zalloc(zone_zone);
298: if (z == ZONE_NULL)
299: return(ZONE_NULL);
300:
301: /*
302: * Round off all the parameters appropriately.
303: */
304: if (size < sizeof(z->free_elements))
305: size = sizeof(z->free_elements);
306: size = ((size-1) + sizeof(z->free_elements)) -
307: ((size-1) % sizeof(z->free_elements));
308: if (alloc == 0)
309: alloc = PAGE_SIZE;
310: alloc = round_page(alloc);
311: max = round_page(max);
312: /*
313: * We look for an allocation size with least fragmentation
314: * in the range of 1 - 5 pages. This size will be used unless
315: * the user suggestion is larger AND has less fragmentation
316: */
317: { vm_size_t best, waste; unsigned int i;
318: best = PAGE_SIZE;
319: waste = best % size;
320: for (i = 2; i <= 5; i++){ vm_size_t tsize, twaste;
321: tsize = i * PAGE_SIZE;
322: twaste = tsize % size;
323: if (twaste < waste)
324: best = tsize, waste = twaste;
325: }
326: if (alloc <= best || (alloc % size >= waste))
327: alloc = best;
328: }
329: if (max && (max < alloc))
330: max = alloc;
331:
332: z->free_elements = 0;
333: z->cur_size = 0;
334: z->max_size = max;
335: z->elem_size = size;
336: z->alloc_size = alloc;
337: z->zone_name = name;
338: z->count = 0;
339: z->doing_alloc = FALSE;
340: z->exhaustible = FALSE;
341: z->collectable = TRUE;
342: z->allows_foreign = FALSE;
343: z->expandable = TRUE;
344: z->waiting = FALSE;
345:
346: #if ZONE_DEBUG
347: z->active_zones.next = z->active_zones.prev = 0;
348: zone_debug_enable(z);
349: #endif /* ZONE_DEBUG */
350: lock_zone_init(z);
351:
352: /*
353: * Add the zone to the all-zones list.
354: */
355:
356: z->next_zone = ZONE_NULL;
357: simple_lock(&all_zones_lock);
358: *last_zone = z;
359: last_zone = &z->next_zone;
360: num_zones++;
361: simple_unlock(&all_zones_lock);
362:
363: return(z);
364: }
365:
366: /*
367: * Cram the given memory into the specified zone.
368: */
369: void
370: zcram(
371: register zone_t zone,
372: vm_offset_t newmem,
373: vm_size_t size)
374: {
375: register vm_size_t elem_size;
376:
377: /* Basic sanity checks */
378: assert(zone != ZONE_NULL && newmem != (vm_offset_t)0);
379: assert(!zone->collectable || zone->allows_foreign
380: || (from_zone_map(newmem) && from_zone_map(newmem+size-1)));
381:
382: elem_size = zone->elem_size;
383:
384: lock_zone(zone);
385: while (size >= elem_size) {
386: ADD_TO_ZONE(zone, newmem);
387: if (from_zone_map(newmem))
388: zone_page_alloc(newmem, elem_size);
389: zone->count++; /* compensate for ADD_TO_ZONE */
390: size -= elem_size;
391: newmem += elem_size;
392: zone->cur_size += elem_size;
393: }
394: unlock_zone(zone);
395: }
396:
397: /*
398: * Contiguous space allocator for non-paged zones. Allocates "size" amount
399: * of memory from zone_map.
400: */
401:
402: kern_return_t
403: zget_space(
404: vm_offset_t size,
405: vm_offset_t *result)
406: {
407: vm_offset_t new_space = 0;
408: vm_size_t space_to_add;
409:
410: simple_lock(&zget_space_lock);
411: while ((zalloc_next_space + size) > zalloc_end_of_space) {
412: /*
413: * Add at least one page to allocation area.
414: */
415:
416: space_to_add = round_page(size);
417:
418: if (new_space == 0) {
419: kern_return_t retval;
420: /*
421: * Memory cannot be wired down while holding
422: * any locks that the pageout daemon might
423: * need to free up pages. [Making the zget_space
424: * lock a complex lock does not help in this
425: * regard.]
426: *
427: * Unlock and allocate memory. Because several
428: * threads might try to do this at once, don't
429: * use the memory before checking for available
430: * space again.
431: */
432:
433: simple_unlock(&zget_space_lock);
434:
435: retval = kernel_memory_allocate(zone_map, &new_space,
436: space_to_add, 0, KMA_KOBJECT|KMA_NOPAGEWAIT);
437: if (retval != KERN_SUCCESS)
438: return(retval);
439: zone_page_init(new_space, space_to_add,
440: ZONE_PAGE_USED);
441: simple_lock(&zget_space_lock);
442: continue;
443: }
444:
445:
446: /*
447: * Memory was allocated in a previous iteration.
448: *
449: * Check whether the new region is contiguous
450: * with the old one.
451: */
452:
453: if (new_space != zalloc_end_of_space) {
454: /*
455: * Throw away the remainder of the
456: * old space, and start a new one.
457: */
458: zalloc_wasted_space +=
459: zalloc_end_of_space - zalloc_next_space;
460: zalloc_next_space = new_space;
461: }
462:
463: zalloc_end_of_space = new_space + space_to_add;
464:
465: new_space = 0;
466: }
467: *result = zalloc_next_space;
468: zalloc_next_space += size;
469: simple_unlock(&zget_space_lock);
470:
471: if (new_space != 0)
472: kmem_free(zone_map, new_space, space_to_add);
473:
474: return(KERN_SUCCESS);
475: }
476:
477:
478: /*
479: * Steal memory for the zone package. Called from
480: * vm_page_bootstrap().
481: */
482: void
483: zone_steal_memory(void)
484: {
485: zdata_size = round_page(128*sizeof(struct zone));
486: zdata = pmap_steal_memory(zdata_size);
487: }
488:
489:
490: /*
491: * Fill a zone with enough memory to contain at least nelem elements.
492: * Memory is obtained with kmem_alloc_wired from the kernel_map.
493: * Return the number of elements actually put into the zone, which may
494: * be more than the caller asked for since the memory allocation is
495: * rounded up to a full page.
496: */
497: int
498: zfill(
499: zone_t zone,
500: int nelem)
501: {
502: kern_return_t kr;
503: vm_size_t size;
504: vm_offset_t memory;
505: int nalloc;
506:
507: assert(nelem > 0);
508: if (nelem <= 0)
509: return 0;
510:
511: size = nelem * zone->elem_size;
512: size = round_page(size);
513: kr = kmem_alloc_wired(kernel_map, &memory, size);
514: if (kr != KERN_SUCCESS)
515: return 0;
516:
517: zone_change(zone, Z_FOREIGN, TRUE);
518: zcram(zone, memory, size);
519: nalloc = size / zone->elem_size;
520: assert(nalloc >= nelem);
521:
522: return nalloc;
523: }
524:
525: /*
526: * Initialize the "zone of zones" which uses fixed memory allocated
527: * earlier in memory initialization. zone_bootstrap is called
528: * before zone_init.
529: */
530: void
531: zone_bootstrap(void)
532: {
533: vm_size_t zone_zone_size;
534: vm_offset_t zone_zone_space;
535:
536: simple_lock_init(&all_zones_lock, ETAP_MISC_ZONE_ALL);
537:
538: first_zone = ZONE_NULL;
539: last_zone = &first_zone;
540: num_zones = 0;
541:
542: simple_lock_init(&zget_space_lock, ETAP_MISC_ZONE_GET);
543: zalloc_next_space = zdata;
544: zalloc_end_of_space = zdata + zdata_size;
545: zalloc_wasted_space = 0;
546:
547: /* assertion: nobody else called zinit before us */
548: assert(zone_zone == ZONE_NULL);
549: zone_zone = zinit(sizeof(struct zone), 128 * sizeof(struct zone),
550: sizeof(struct zone), "zones");
551: zone_change(zone_zone, Z_COLLECT, FALSE);
552: zone_zone_size = zalloc_end_of_space - zalloc_next_space;
553: zget_space(zone_zone_size, &zone_zone_space);
554: zcram(zone_zone, zone_zone_space, zone_zone_size);
555: }
556:
557: void
558: zone_init(
559: vm_size_t max_zonemap_size)
560: {
561: kern_return_t retval;
562: vm_offset_t zone_min;
563: vm_offset_t zone_max;
564: vm_size_t zone_table_size;
565:
566: retval = kmem_suballoc(kernel_map, &zone_min, max_zonemap_size,
567: FALSE, TRUE, &zone_map);
568: if (retval != KERN_SUCCESS)
569: panic("zone_init: kmem_suballoc failed");
570: zone_max = zone_min + round_page(max_zonemap_size);
571: /*
572: * Setup garbage collection information:
573: */
574: zone_table_size = atop(zone_max - zone_min) *
575: sizeof(struct zone_page_table_entry);
576: if (kmem_alloc_wired(zone_map, (vm_offset_t *) &zone_page_table,
577: zone_table_size) != KERN_SUCCESS)
578: panic("zone_init");
579: zone_min = (vm_offset_t)zone_page_table + round_page(zone_table_size);
580: zone_pages = atop(zone_max - zone_min);
581: zone_map_min_address = zone_min;
582: zone_map_max_address = zone_max;
583: simple_lock_init(&zone_page_table_lock, ETAP_MISC_ZONE_PTABLE);
584: mutex_init(&zone_gc_lock, ETAP_NO_TRACE);
585: zone_page_init(zone_min, zone_max - zone_min, ZONE_PAGE_UNUSED);
586: }
587:
588:
589: /*
590: * zalloc returns an element from the specified zone.
591: */
592: vm_offset_t
593: zalloc(
594: register zone_t zone)
595: {
596: vm_offset_t addr;
597: kern_return_t retval;
598:
599: assert(zone != ZONE_NULL);
600: check_simple_locks();
601:
602: lock_zone(zone);
603:
604: REMOVE_FROM_ZONE(zone, addr, vm_offset_t);
605: while (addr == 0) {
606: /*
607: * If nothing was there, try to get more
608: */
609: if (zone->doing_alloc) {
610: /*
611: * Someone is allocating memory for this zone.
612: * Wait for it to show up, then try again.
613: */
614: assert_wait((event_t)zone, THREAD_INTERRUPTIBLE);
615: zone->waiting = TRUE;
616: unlock_zone(zone);
617: thread_block((void (*)(void)) 0);
618: lock_zone(zone);
619: }
620: else {
621: if ((zone->cur_size + zone->elem_size) >
622: zone->max_size) {
623: if (zone->exhaustible)
624: break;
625: if (zone->expandable) {
626: /*
627: * We're willing to overflow certain
628: * zones, but not without complaining.
629: *
630: * This is best used in conjunction
631: * with the collectable flag. What we
632: * want is an assurance we can get the
633: * memory back, assuming there's no
634: * leak.
635: */
636: zone->max_size += (zone->max_size >> 1);
637: } else {
638: unlock_zone(zone);
639: panic("zalloc: zone \"%s\" empty.", zone->zone_name);
640: }
641: }
642: zone->doing_alloc = TRUE;
643: unlock_zone(zone);
644:
645: if (zone->collectable) {
646: vm_offset_t space;
647: vm_size_t alloc_size;
648:
649: if (vm_pool_low())
650: alloc_size =
651: round_page(zone->elem_size);
652: else
653: alloc_size = zone->alloc_size;
654:
655: retval = kernel_memory_allocate(zone_map,
656: &space, alloc_size, 0,
657: KMA_KOBJECT|KMA_NOPAGEWAIT);
658: if (retval == KERN_SUCCESS) {
659: zone_page_init(space, alloc_size,
660: ZONE_PAGE_USED);
661: zcram(zone, space, alloc_size);
662: } else if (retval != KERN_RESOURCE_SHORTAGE) {
663: /* would like to cause a zone_gc() */
664: panic("zalloc");
665: }
666: lock_zone(zone);
667: zone->doing_alloc = FALSE;
668: if (zone->waiting) {
669: zone->waiting = FALSE;
670: thread_wakeup((event_t)zone);
671: }
672: REMOVE_FROM_ZONE(zone, addr, vm_offset_t);
673: if (addr == 0 &&
674: retval == KERN_RESOURCE_SHORTAGE) {
675: unlock_zone(zone);
676: VM_PAGE_WAIT();
677: lock_zone(zone);
678: }
679: } else {
680: vm_offset_t space;
681: retval = zget_space(zone->elem_size, &space);
682:
683: lock_zone(zone);
684: zone->doing_alloc = FALSE;
685: if (zone->waiting) {
686: zone->waiting = FALSE;
687: thread_wakeup((event_t)zone);
688: }
689: if (retval == KERN_SUCCESS) {
690: zone->count++;
691: zone->cur_size += zone->elem_size;
692: #if ZONE_DEBUG
693: if (zone_debug_enabled(zone)) {
694: enqueue_tail(&zone->active_zones, (queue_entry_t)space);
695: }
696: #endif
697: unlock_zone(zone);
698: zone_page_alloc(space, zone->elem_size);
699: #if ZONE_DEBUG
700: if (zone_debug_enabled(zone))
701: space += sizeof(queue_chain_t);
702: #endif
703: return(space);
704: }
705: if (retval == KERN_RESOURCE_SHORTAGE) {
706: unlock_zone(zone);
707: VM_PAGE_WAIT();
708: lock_zone(zone);
709: } else {
710: panic("zalloc");
711: }
712: }
713: }
714: if (addr == 0)
715: REMOVE_FROM_ZONE(zone, addr, vm_offset_t);
716: }
717:
718: #if ZONE_DEBUG
719: if (addr && zone_debug_enabled(zone)) {
720: enqueue_tail(&zone->active_zones, (queue_entry_t)addr);
721: addr += sizeof(queue_chain_t);
722: }
723: #endif
724:
725: unlock_zone(zone);
726: return(addr);
727: }
728:
729:
730: /*
731: * zget returns an element from the specified zone
732: * and immediately returns nothing if there is nothing there.
733: *
734: * This form should be used when you can not block (like when
735: * processing an interrupt).
736: */
737: vm_offset_t
738: zget(
739: register zone_t zone)
740: {
741: register vm_offset_t addr;
742:
743: assert( zone != ZONE_NULL );
744:
745: if (!lock_try_zone(zone))
746: return ((vm_offset_t)0);
747:
748: REMOVE_FROM_ZONE(zone, addr, vm_offset_t);
749: #if ZONE_DEBUG
750: if (addr && zone_debug_enabled(zone)) {
751: enqueue_tail(&zone->active_zones, (queue_entry_t)addr);
752: addr += sizeof(queue_chain_t);
753: }
754: #endif /* ZONE_DEBUG */
755: unlock_zone(zone);
756:
757: return(addr);
758: }
759:
760: #if ZONE_DEBUG
761: boolean_t zone_check = TRUE;
762: #else /* ZONE_DEBUG */
763: boolean_t zone_check = FALSE;
764: #endif /* ZONE_DEBUG */
765:
766: void
767: zfree(
768: register zone_t zone,
769: vm_offset_t elem)
770: {
771:
772: #if MACH_ASSERT
773: /* Basic sanity checks */
774: if (zone == ZONE_NULL || elem == (vm_offset_t)0)
775: panic("zfree: NULL");
776: /* zone_gc assumes zones are never freed */
777: if (zone == zone_zone)
778: panic("zfree: freeing to zone_zone breaks zone_gc!");
779: if (zone->collectable && !zone->allows_foreign &&
780: (!from_zone_map(elem) || !from_zone_map(elem+zone->elem_size-1)))
781: panic("zfree: non-allocated memory in collectable zone!");
782: #endif
783:
784: lock_zone(zone);
785: #if ZONE_DEBUG
786: if (zone_debug_enabled(zone)) {
787: queue_t tmp_elem;
788:
789: elem -= sizeof(queue_chain_t);
790: if (zone_check) {
791: /* check the zone's consistency */
792:
793: for (tmp_elem = queue_first(&zone->active_zones);
794: !queue_end(tmp_elem, &zone->active_zones);
795: tmp_elem = queue_next(tmp_elem))
796: if (elem == (vm_offset_t)tmp_elem)
797: break;
798: if (elem != (vm_offset_t)tmp_elem)
799: panic("zfree()ing element from wrong zone");
800: }
801: remqueue(&zone->active_zones, (queue_t) elem);
802: }
803: #endif /* ZONE_DEBUG */
804: if (zone_check) {
805: vm_offset_t this;
806:
807: /* check the zone's consistency */
808:
809: for (this = zone->free_elements;
810: this != 0;
811: this = * (vm_offset_t *) this)
812: if (!pmap_kernel_va(this) || this == elem)
813: panic("zfree");
814: }
815: /*
816: * If elements have one or more pages, and memory is low,
817: * put it directly back into circulation rather than
818: * back into a zone, where a non-vm_privileged task can grab it.
819: * This lessens the impact of a privileged task cycling reserved
820: * memory into a publicly accessible zone.
821: */
822: if (zone->elem_size >= PAGE_SIZE &&
823: vm_pool_low()){
824: assert( !(zone->elem_size & (zone->alloc_size-1)) );
825: zone->count--;
826: zone->cur_size -= zone->elem_size;
827: zone_page_init(elem, zone->elem_size, ZONE_PAGE_UNUSED);
828: unlock_zone(zone);
829: kmem_free(zone_map, elem, zone->elem_size);
830: return;
831: }
832: ADD_TO_ZONE(zone, elem);
833: unlock_zone(zone);
834: }
835:
836:
837: /* Change a zone's flags.
838: * This routine must be called immediately after zinit.
839: */
840: void
841: zone_change(
842: zone_t zone,
843: unsigned int item,
844: boolean_t value)
845: {
846: assert( zone != ZONE_NULL );
847: assert( value == TRUE || value == FALSE );
848:
849: switch(item){
850: case Z_EXHAUST:
851: zone->exhaustible = value;
852: break;
853: case Z_COLLECT:
854: zone->collectable = value;
855: break;
856: case Z_EXPAND:
857: zone->expandable = value;
858: break;
859: case Z_FOREIGN:
860: zone->allows_foreign = value;
861: break;
862: #if MACH_ASSERT
863: default:
864: panic("Zone_change: Wrong Item Type!");
865: /* break; */
866: #endif
867: }
868: lock_zone_init(zone);
869: }
870:
871: /*
872: * Return the expected number of free elements in the zone.
873: * This calculation will be incorrect if items are zfree'd that
874: * were never zalloc'd/zget'd. The correct way to stuff memory
875: * into a zone is by zcram.
876: */
877:
878: integer_t
879: zone_free_count(zone_t zone)
880: {
881: integer_t free_count;
882:
883: lock_zone(zone);
884: free_count = zone->cur_size/zone->elem_size - zone->count;
885: unlock_zone(zone);
886:
887: assert(free_count >= 0);
888:
889: return(free_count);
890: }
891:
892: /*
893: * zprealloc preallocates wired memory, exanding the specified
894: * zone to the specified size
895: */
896: void
897: zprealloc(
898: zone_t zone,
899: vm_size_t size)
900: {
901: vm_offset_t addr;
902:
903: if (size != 0) {
904: if (kmem_alloc_wired(zone_map, &addr, size) != KERN_SUCCESS)
905: panic("zprealloc");
906: zone_page_init(addr, size, ZONE_PAGE_USED);
907: zcram(zone, addr, size);
908: }
909: }
910:
911: /*
912: * Zone garbage collection subroutines
913: *
914: * These routines have in common the modification of entries in the
915: * zone_page_table. The latter contains one entry for every page
916: * in the zone_map.
917: *
918: * For each page table entry in the given range:
919: *
920: * zone_page_collectable - test if one (in_free_list == alloc_count)
921: * zone_page_keep - reset in_free_list
922: * zone_page_in_use - decrements in_free_list
923: * zone_page_free - increments in_free_list
924: * zone_page_init - initializes in_free_list and alloc_count
925: * zone_page_alloc - increments alloc_count
926: * zone_page_dealloc - decrements alloc_count
927: * zone_add_free_page_list - adds the page to the free list
928: *
929: * Two counts are maintained for each page, the in_free_list count and
930: * alloc_count. The alloc_count is how many zone elements have been
931: * allocated from a page. (Note that the page could contain elements
932: * that span page boundaries. The count includes these elements so
933: * one element may be counted in two pages.) In_free_list is a count
934: * of how many zone elements are currently free. If in_free_list is
935: * equal to alloc_count then the page is eligible for garbage
936: * collection.
937: *
938: * Alloc_count and in_free_list are initialized to the correct values
939: * for a particular zone when a page is zcram'ed into a zone. Subsequent
940: * gets and frees of zone elements will call zone_page_in_use and
941: * zone_page_free which modify the in_free_list count. When the zones
942: * garbage collector runs it will walk through a zones free element list,
943: * remove the elements that reside on collectable pages, and use
944: * zone_add_free_page_list to create a list of pages to be collected.
945: */
946:
947: boolean_t
948: zone_page_collectable(
949: vm_offset_t addr,
950: vm_size_t size)
951: {
952: natural_t i, j;
953:
954: #if MACH_ASSERT
955: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
956: panic("zone_page_collectable");
957: #endif
958:
959: i = atop(addr-zone_map_min_address);
960: j = atop((addr+size-1) - zone_map_min_address);
961: lock_zone_page_table();
962: for (; i <= j; i++) {
963: if (zone_page_table[i].in_free_list ==
964: zone_page_table[i].alloc_count) {
965: unlock_zone_page_table();
966: return (TRUE);
967: }
968: }
969: unlock_zone_page_table();
970: return (FALSE);
971: }
972:
973: void
974: zone_page_keep(
975: vm_offset_t addr,
976: vm_size_t size)
977: {
978: natural_t i, j;
979:
980: #if MACH_ASSERT
981: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
982: panic("zone_page_keep");
983: #endif
984:
985: i = atop(addr-zone_map_min_address);
986: j = atop((addr+size-1) - zone_map_min_address);
987: lock_zone_page_table();
988: for (; i <= j; i++) {
989: zone_page_table[i].in_free_list = 0;
990: }
991: unlock_zone_page_table();
992: }
993:
994: void
995: zone_page_in_use(
996: vm_offset_t addr,
997: vm_size_t size)
998: {
999: natural_t i, j;
1000:
1001: #if MACH_ASSERT
1002: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
1003: panic("zone_page_in_use");
1004: #endif
1005:
1006: i = atop(addr-zone_map_min_address);
1007: j = atop((addr+size-1) - zone_map_min_address);
1008: lock_zone_page_table();
1009: for (; i <= j; i++) {
1010: if (zone_page_table[i].in_free_list > 0)
1011: zone_page_table[i].in_free_list--;
1012: }
1013: unlock_zone_page_table();
1014: }
1015:
1016: void
1017: zone_page_free(
1018: vm_offset_t addr,
1019: vm_size_t size)
1020: {
1021: natural_t i, j;
1022:
1023: #if MACH_ASSERT
1024: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
1025: panic("zone_page_free");
1026: #endif
1027:
1028: i = atop(addr-zone_map_min_address);
1029: j = atop((addr+size-1) - zone_map_min_address);
1030: lock_zone_page_table();
1031: for (; i <= j; i++) {
1032: assert(zone_page_table[i].in_free_list >= 0);
1033: zone_page_table[i].in_free_list++;
1034: }
1035: unlock_zone_page_table();
1036: }
1037:
1038: void
1039: zone_page_init(
1040: vm_offset_t addr,
1041: vm_size_t size,
1042: int value)
1043: {
1044: natural_t i, j;
1045:
1046: #if MACH_ASSERT
1047: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
1048: panic("zone_page_init");
1049: #endif
1050:
1051: i = atop(addr-zone_map_min_address);
1052: j = atop((addr+size-1) - zone_map_min_address);
1053: lock_zone_page_table();
1054: for (; i <= j; i++) {
1055: zone_page_table[i].alloc_count = value;
1056: zone_page_table[i].in_free_list = 0;
1057: }
1058: unlock_zone_page_table();
1059: }
1060:
1061: void
1062: zone_page_alloc(
1063: vm_offset_t addr,
1064: vm_size_t size)
1065: {
1066: natural_t i, j;
1067:
1068: #if MACH_ASSERT
1069: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
1070: panic("zone_page_alloc");
1071: #endif
1072:
1073: i = atop(addr-zone_map_min_address);
1074: j = atop((addr+size-1) - zone_map_min_address);
1075: lock_zone_page_table();
1076: for (; i <= j; i++) {
1077: /* Set alloc_count to (ZONE_PAGE_USED + 1) if
1078: * it was previously set to ZONE_PAGE_UNUSED.
1079: */
1080: if (zone_page_table[i].alloc_count == ZONE_PAGE_UNUSED) {
1081: zone_page_table[i].alloc_count = 1;
1082: } else {
1083: zone_page_table[i].alloc_count++;
1084: }
1085: }
1086: unlock_zone_page_table();
1087: }
1088:
1089: void
1090: zone_page_dealloc(
1091: vm_offset_t addr,
1092: vm_size_t size)
1093: {
1094: natural_t i, j;
1095:
1096: #if MACH_ASSERT
1097: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
1098: panic("zone_page_dealloc");
1099: #endif
1100:
1101: i = atop(addr-zone_map_min_address);
1102: j = atop((addr+size-1) - zone_map_min_address);
1103: lock_zone_page_table();
1104: for (; i <= j; i++) {
1105: zone_page_table[i].alloc_count--;
1106: }
1107: unlock_zone_page_table();
1108: }
1109:
1110: void
1111: zone_add_free_page_list(
1112: struct zone_page_table_entry **free_list,
1113: vm_offset_t addr,
1114: vm_size_t size)
1115: {
1116: natural_t i, j;
1117:
1118: #if MACH_ASSERT
1119: if (!from_zone_map(addr) || !from_zone_map(addr+size-1))
1120: panic("zone_add_free_page_list");
1121: #endif
1122:
1123: i = atop(addr-zone_map_min_address);
1124: j = atop((addr+size-1) - zone_map_min_address);
1125: lock_zone_page_table();
1126: for (; i <= j; i++) {
1127: if (zone_page_table[i].alloc_count == 0) {
1128: zone_page_table[i].next = *free_list;
1129: *free_list = &zone_page_table[i];
1130: zone_page_table[i].alloc_count = ZONE_PAGE_UNUSED;
1131: zone_page_table[i].in_free_list = 0;
1132: }
1133: }
1134: unlock_zone_page_table();
1135: }
1136:
1137:
1138: /* This is used for walking through a zone's free element list.
1139: */
1140: struct zone_free_entry {
1141: struct zone_free_entry * next;
1142: };
1143:
1144: int reclaim_page_count = 0;
1145:
1146: /* Zone garbage collection
1147: *
1148: * zone_gc will walk through all the free elements in all the
1149: * zones that are marked collectable looking for reclaimable
1150: * pages. zone_gc is called by consider_zone_gc when the system
1151: * begins to run out of memory.
1152: */
1153: void
1154: zone_gc(void)
1155: {
1156: unsigned int max_zones;
1157: zone_t z;
1158: unsigned int i;
1159: struct zone_page_table_entry *freep;
1160: struct zone_page_table_entry *zone_free_page_list;
1161:
1162: mutex_lock(&zone_gc_lock);
1163:
1164: simple_lock(&all_zones_lock);
1165: max_zones = num_zones;
1166: z = first_zone;
1167: simple_unlock(&all_zones_lock);
1168:
1169: #if MACH_ASSERT
1170: lock_zone_page_table();
1171: for (i = 0; i < zone_pages; i++)
1172: assert(zone_page_table[i].in_free_list == 0);
1173: unlock_zone_page_table();
1174: #endif /* MACH_ASSERT */
1175:
1176: zone_free_page_list = (struct zone_page_table_entry *) 0;
1177:
1178: for (i = 0; i < max_zones; i++) {
1179: struct zone_free_entry * last;
1180: struct zone_free_entry * elt;
1181: assert(z != ZONE_NULL);
1182:
1183: if (!z->collectable) {
1184: simple_lock(&all_zones_lock);
1185: z = z->next_zone;
1186: simple_unlock(&all_zones_lock);
1187: continue;
1188: }
1189: lock_zone(z);
1190:
1191: /*
1192: * Do a quick feasability check before we scan the zone:
1193: * skip unless there is likelihood of getting 1+ pages back.
1194: */
1195: if ((z->cur_size - z->count * z->elem_size) <= (2*PAGE_SIZE)){
1196: unlock_zone(z);
1197: simple_lock(&all_zones_lock);
1198: z = z->next_zone;
1199: simple_unlock(&all_zones_lock);
1200: continue;
1201: }
1202:
1203: /* Count the free elements in each page. This loop
1204: * requires that all in_free_list entries are zero.
1205: */
1206: for (elt = (struct zone_free_entry *)(z->free_elements);
1207: elt != (struct zone_free_entry *)0; elt = elt->next) {
1208:
1209: if (!from_zone_map(elt)) continue;
1210:
1211: zone_page_free((vm_offset_t)elt, z->elem_size);
1212: }
1213:
1214: /* Now determine which elements should be removed
1215: * from the free list and, after all the elements
1216: * on a page have been removed, add the element's
1217: * page to a list of pages to be freed.
1218: */
1219: elt = (struct zone_free_entry *)(z->free_elements);
1220: last = elt;
1221: while ((elt != (struct zone_free_entry *)0)) {
1222: if (!from_zone_map(elt)) {
1223: last = elt;
1224: elt = elt->next;
1225: continue;
1226: }
1227: if (zone_page_collectable((vm_offset_t)elt,
1228: z->elem_size)) {
1229: z->cur_size -= z->elem_size;
1230: zone_page_in_use((vm_offset_t)elt,
1231: z->elem_size);
1232: zone_page_dealloc((vm_offset_t)elt,
1233: z->elem_size);
1234: zone_add_free_page_list(&zone_free_page_list,
1235: (vm_offset_t)elt,
1236: z->elem_size);
1237: if (elt == last) {
1238: elt = elt->next;
1239: z->free_elements =(vm_offset_t)elt;
1240: last = elt;
1241: } else {
1242: last->next = elt->next;
1243: elt = elt->next;
1244: }
1245: } else {
1246: /* This element is not eligible for collection
1247: * so clear in_free_list in preparation for a
1248: * subsequent garbage collection pass.
1249: */
1250: zone_page_keep((vm_offset_t)elt, z->elem_size);
1251: last = elt;
1252: elt = elt->next;
1253: }
1254: } /* end while(elt) */
1255:
1256: unlock_zone(z);
1257: simple_lock(&all_zones_lock);
1258: /*
1259: * Note that this scheme of locking only to walk the zone list
1260: * assumes that zones are never freed (checked by zfree)
1261: */
1262: z = z->next_zone;
1263: simple_unlock(&all_zones_lock);
1264: }
1265:
1266: for (freep = zone_free_page_list; freep != 0; freep = freep->next) {
1267: vm_offset_t free_addr;
1268:
1269: free_addr = zone_map_min_address +
1270: PAGE_SIZE * (freep - zone_page_table);
1271: kmem_free(zone_map, free_addr, PAGE_SIZE);
1272: reclaim_page_count++;
1273: }
1274: mutex_unlock(&zone_gc_lock);
1275: }
1276:
1277: boolean_t zone_gc_allowed = TRUE; /* XXX */
1278: unsigned zone_gc_last_tick = 0;
1279: unsigned zone_gc_max_rate = 0; /* in ticks */
1280:
1281: /*
1282: * consider_zone_gc:
1283: *
1284: * Called by the pageout daemon when the system needs more free pages.
1285: */
1286:
1287: void
1288: consider_zone_gc(void)
1289: {
1290: /*
1291: * By default, don't attempt zone GC more frequently
1292: * than once a second (which is one scheduler tick).
1293: */
1294:
1295: if (zone_gc_max_rate == 0)
1296: zone_gc_max_rate = 2; /* sched_tick is a 1 second resolution 2 here insures at least 1 second interval */
1297:
1298: if (zone_gc_allowed &&
1299: (sched_tick > (zone_gc_last_tick + zone_gc_max_rate))) {
1300: zone_gc_last_tick = sched_tick;
1301: zone_gc();
1302: }
1303: }
1304:
1305: #include <mach/kern_return.h>
1306: #include <mach/machine/vm_types.h>
1307: #include <mach_debug/zone_info.h>
1308: #include <kern/host.h>
1309: #include <vm/vm_map.h>
1310: #include <vm/vm_user.h>
1311: #include <vm/vm_kern.h>
1312:
1313: #include <mach/mach_host_server.h>
1314:
1315: kern_return_t
1316: host_zone_info(
1317: host_t host,
1318: zone_name_array_t *namesp,
1319: mach_msg_type_number_t *namesCntp,
1320: zone_info_array_t *infop,
1321: mach_msg_type_number_t *infoCntp)
1322: {
1323: zone_name_t *names;
1324: vm_offset_t names_addr;
1325: vm_size_t names_size;
1326: zone_info_t *info;
1327: vm_offset_t info_addr;
1328: vm_size_t info_size;
1329: unsigned int max_zones, i;
1330: zone_t z;
1331: kern_return_t kr;
1332:
1333: if (host == HOST_NULL)
1334: return KERN_INVALID_HOST;
1335:
1336: /*
1337: * We assume that zones aren't freed once allocated.
1338: * We won't pick up any zones that are allocated later.
1339: */
1340:
1341: simple_lock(&all_zones_lock);
1342: max_zones = num_zones;
1343: z = first_zone;
1344: simple_unlock(&all_zones_lock);
1345:
1346: if (max_zones <= *namesCntp) {
1347: /* use in-line memory */
1348:
1349: names = *namesp;
1350: } else {
1351: names_size = round_page(max_zones * sizeof *names);
1352: kr = kmem_alloc_pageable(ipc_kernel_map,
1353: &names_addr, names_size);
1354: if (kr != KERN_SUCCESS)
1355: return kr;
1356:
1357: names = (zone_name_t *) names_addr;
1358: }
1359:
1360: if (max_zones <= *infoCntp) {
1361: /* use in-line memory */
1362:
1363: info = *infop;
1364: } else {
1365: info_size = round_page(max_zones * sizeof *info);
1366: kr = kmem_alloc_pageable(ipc_kernel_map,
1367: &info_addr, info_size);
1368: if (kr != KERN_SUCCESS) {
1369: if (names != *namesp)
1370: kmem_free(ipc_kernel_map,
1371: names_addr, names_size);
1372: return kr;
1373: }
1374:
1375: info = (zone_info_t *) info_addr;
1376: }
1377:
1378: for (i = 0; i < max_zones; i++) {
1379: zone_name_t *zn = &names[i];
1380: zone_info_t *zi = &info[i];
1381: struct zone zcopy;
1382:
1383: assert(z != ZONE_NULL);
1384:
1385: lock_zone(z);
1386: zcopy = *z;
1387: unlock_zone(z);
1388:
1389: simple_lock(&all_zones_lock);
1390: z = z->next_zone;
1391: simple_unlock(&all_zones_lock);
1392:
1393: /* assuming here the name data is static */
1394: (void) strncpy(zn->zn_name, zcopy.zone_name,
1395: sizeof zn->zn_name);
1396:
1397: zi->zi_count = zcopy.count;
1398: zi->zi_cur_size = zcopy.cur_size;
1399: zi->zi_max_size = zcopy.max_size;
1400: zi->zi_elem_size = zcopy.elem_size;
1401: zi->zi_alloc_size = zcopy.alloc_size;
1402: zi->zi_exhaustible = zcopy.exhaustible;
1403: zi->zi_collectable = zcopy.collectable;
1404: }
1405:
1406: if (names != *namesp) {
1407: vm_size_t used;
1408: vm_map_copy_t copy;
1409:
1410: used = max_zones * sizeof *names;
1411:
1412: if (used != names_size)
1413: bzero((char *) (names_addr + used), names_size - used);
1414:
1415: kr = vm_map_copyin(ipc_kernel_map, names_addr, names_size,
1416: TRUE, ©);
1417: assert(kr == KERN_SUCCESS);
1418:
1419: *namesp = (zone_name_t *) copy;
1420: }
1421: *namesCntp = max_zones;
1422:
1423: if (info != *infop) {
1424: vm_size_t used;
1425: vm_map_copy_t copy;
1426:
1427: used = max_zones * sizeof *info;
1428:
1429: if (used != info_size)
1430: bzero((char *) (info_addr + used), info_size - used);
1431:
1432: kr = vm_map_copyin(ipc_kernel_map, info_addr, info_size,
1433: TRUE, ©);
1434: assert(kr == KERN_SUCCESS);
1435:
1436: *infop = (zone_info_t *) copy;
1437: }
1438: *infoCntp = max_zones;
1439:
1440: return KERN_SUCCESS;
1441: }
1442:
1443: #if MACH_KDB
1444: #include <ddb/db_command.h>
1445: #include <ddb/db_output.h>
1446: #include <kern/kern_print.h>
1447:
1448: const char *zone_labels =
1449: "ENTRY COUNT TOT_SZ MAX_SZ ELT_SZ ALLOC_SZ NAME";
1450:
1451: /* Forwards */
1452: void db_print_zone(
1453: zone_t addr);
1454:
1455: #if ZONE_DEBUG
1456: void db_zone_check_active(
1457: zone_t zone);
1458: void db_zone_print_active(
1459: zone_t zone);
1460: #endif /* ZONE_DEBUG */
1461: void db_zone_print_free(
1462: zone_t zone);
1463: void
1464: db_print_zone(
1465: zone_t addr)
1466: {
1467: struct zone zcopy;
1468:
1469: zcopy = *addr;
1470:
1471: db_printf("%8x %8x %8x %8x %6x %8x %s ",
1472: addr, zcopy.count, zcopy.cur_size,
1473: zcopy.max_size, zcopy.elem_size,
1474: zcopy.alloc_size, zcopy.zone_name);
1475: if (zcopy.exhaustible)
1476: db_printf("H");
1477: if (zcopy.collectable)
1478: db_printf("C");
1479: if (zcopy.expandable)
1480: db_printf("X");
1481: db_printf("\n");
1482: }
1483:
1484: /*ARGSUSED*/
1485: void
1486: db_show_one_zone(
1487: db_expr_t addr,
1488: int have_addr,
1489: db_expr_t count,
1490: char * modif)
1491: {
1492: struct zone *z = (zone_t)addr;
1493:
1494: if (z == ZONE_NULL || !have_addr){
1495: db_error("No Zone\n");
1496: /*NOTREACHED*/
1497: }
1498:
1499: db_printf("%s\n", zone_labels);
1500: db_print_zone(z);
1501: }
1502:
1503: /*ARGSUSED*/
1504: void
1505: db_show_all_zones(
1506: db_expr_t addr,
1507: int have_addr,
1508: db_expr_t count,
1509: char * modif)
1510: {
1511: zone_t z;
1512: unsigned total = 0;
1513:
1514: /*
1515: * Don't risk hanging by unconditionally locking,
1516: * risk of incoherent data is small (zones aren't freed).
1517: */
1518: have_addr = simple_lock_try(&all_zones_lock);
1519: count = num_zones;
1520: z = first_zone;
1521: if (have_addr) {
1522: simple_unlock(&all_zones_lock);
1523: }
1524:
1525: db_printf("%s\n", zone_labels);
1526: for ( ; count > 0; count--) {
1527: if (!z) {
1528: db_error("Mangled Zone List\n");
1529: /*NOTREACHED*/
1530: }
1531: db_print_zone(z);
1532: total += z->cur_size,
1533:
1534: have_addr = simple_lock_try(&all_zones_lock);
1535: z = z->next_zone;
1536: if (have_addr) {
1537: simple_unlock(&all_zones_lock);
1538: }
1539: }
1540: db_printf("\nTotal %8x", total);
1541: db_printf("\n\nzone_gc() has reclaimed %d pages\n",
1542: reclaim_page_count);
1543: }
1544:
1545: #if ZONE_DEBUG
1546: void
1547: db_zone_check_active(
1548: zone_t zone)
1549: {
1550: int count = 0;
1551: queue_t tmp_elem;
1552:
1553: if (!zone_debug_enabled(zone) || !zone_check)
1554: return;
1555: tmp_elem = queue_first(&zone->active_zones);
1556: while (count < zone->count) {
1557: count++;
1558: if (tmp_elem == 0) {
1559: printf("unexpected zero element, zone=0x%x, count=%d\n",
1560: zone, count);
1561: assert(FALSE);
1562: break;
1563: }
1564: if (queue_end(tmp_elem, &zone->active_zones)) {
1565: printf("unexpected queue_end, zone=0x%x, count=%d\n",
1566: zone, count);
1567: assert(FALSE);
1568: break;
1569: }
1570: tmp_elem = queue_next(tmp_elem);
1571: }
1572: if (!queue_end(tmp_elem, &zone->active_zones)) {
1573: printf("not at queue_end, zone=0x%x, tmp_elem=0x%x\n",
1574: zone, tmp_elem);
1575: assert(FALSE);
1576: }
1577: }
1578:
1579: void
1580: db_zone_print_active(
1581: zone_t zone)
1582: {
1583: int count = 0;
1584: queue_t tmp_elem;
1585:
1586: if (!zone_debug_enabled(zone)) {
1587: printf("zone 0x%x debug not enabled\n", zone);
1588: return;
1589: }
1590: if (!zone_check) {
1591: printf("zone_check FALSE\n");
1592: return;
1593: }
1594:
1595: printf("zone 0x%x, active elements %d\n", zone, zone->count);
1596: printf("active list:\n");
1597: tmp_elem = queue_first(&zone->active_zones);
1598: while (count < zone->count) {
1599: printf(" 0x%x", tmp_elem);
1600: count++;
1601: if ((count % 6) == 0)
1602: printf("\n");
1603: if (tmp_elem == 0) {
1604: printf("\nunexpected zero element, count=%d\n", count);
1605: break;
1606: }
1607: if (queue_end(tmp_elem, &zone->active_zones)) {
1608: printf("\nunexpected queue_end, count=%d\n", count);
1609: break;
1610: }
1611: tmp_elem = queue_next(tmp_elem);
1612: }
1613: if (!queue_end(tmp_elem, &zone->active_zones))
1614: printf("\nnot at queue_end, tmp_elem=0x%x\n", tmp_elem);
1615: else
1616: printf("\n");
1617: }
1618: #endif /* ZONE_DEBUG */
1619:
1620: void
1621: db_zone_print_free(
1622: zone_t zone)
1623: {
1624: int count = 0;
1625: int freecount;
1626: vm_offset_t elem;
1627:
1628: freecount = zone_free_count(zone);
1629: printf("zone 0x%x, free elements %d\n", zone, freecount);
1630: printf("free list:\n");
1631: elem = zone->free_elements;
1632: while (count < freecount) {
1633: printf(" 0x%x", elem);
1634: count++;
1635: if ((count % 6) == 0)
1636: printf("\n");
1637: if (elem == 0) {
1638: printf("\nunexpected zero element, count=%d\n", count);
1639: break;
1640: }
1641: elem = *((vm_offset_t *)elem);
1642: }
1643: if (elem != 0)
1644: printf("\nnot at end of free list, elem=0x%x\n", elem);
1645: else
1646: printf("\n");
1647: }
1648:
1649: #endif /* MACH_KDB */
1650:
1651:
1652: #if ZONE_DEBUG
1653:
1654: /* should we care about locks here ? */
1655:
1656: #if MACH_KDB
1657: vm_offset_t
1658: next_element(
1659: zone_t z,
1660: vm_offset_t elt)
1661: {
1662: if (!zone_debug_enabled(z))
1663: return(0);
1664: elt -= sizeof(queue_chain_t);
1665: elt = (vm_offset_t) queue_next((queue_t) elt);
1666: if ((queue_t) elt == &z->active_zones)
1667: return(0);
1668: elt += sizeof(queue_chain_t);
1669: return(elt);
1670: }
1671:
1672: vm_offset_t
1673: first_element(
1674: zone_t z)
1675: {
1676: vm_offset_t elt;
1677:
1678: if (!zone_debug_enabled(z))
1679: return(0);
1680: if (queue_empty(&z->active_zones))
1681: return(0);
1682: elt = (vm_offset_t) queue_first(&z->active_zones);
1683: elt += sizeof(queue_chain_t);
1684: return(elt);
1685: }
1686:
1687: /*
1688: * Second arg controls how many zone elements are printed:
1689: * 0 => none
1690: * n, n < 0 => all
1691: * n, n > 0 => last n on active list
1692: */
1693: int
1694: zone_count(
1695: zone_t z,
1696: int tail)
1697: {
1698: vm_offset_t elt;
1699: int count = 0;
1700: boolean_t print = (tail != 0);
1701:
1702: if (tail < 0)
1703: tail = z->count;
1704: if (z->count < tail)
1705: tail = 0;
1706: tail = z->count - tail;
1707: for (elt = first_element(z); elt; elt = next_element(z, elt)) {
1708: if (print && tail <= count)
1709: db_printf("%8x\n", elt);
1710: count++;
1711: }
1712: assert(count == z->count);
1713: return(count);
1714: }
1715: #endif /* MACH_KDB */
1716:
1717: #define zone_in_use(z) ( z->count || z->free_elements )
1718:
1719: void
1720: zone_debug_enable(
1721: zone_t z)
1722: {
1723: if (zone_debug_enabled(z) || zone_in_use(z) ||
1724: z->alloc_size < (z->elem_size + sizeof(queue_chain_t)))
1725: return;
1726: queue_init(&z->active_zones);
1727: z->elem_size += sizeof(queue_chain_t);
1728: }
1729:
1730: void
1731: zone_debug_disable(
1732: zone_t z)
1733: {
1734: if (!zone_debug_enabled(z) || zone_in_use(z))
1735: return;
1736: z->elem_size -= sizeof(queue_chain_t);
1737: z->active_zones.next = z->active_zones.prev = 0;
1738: }
1739: #endif /* ZONE_DEBUG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.