|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /*
26: * Mach Operating System
27: * Copyright (c) 1993,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: * Copyright (c) 1995, 1997 Apple Computer, Inc.
52: *
53: * HISTORY
54: *
55: * 23 June 1995 ? at NeXT
56: * Pulled over from CMU (MK83), added local
57: * mods: freespace suballocator and sorted
58: * zone free lists to reduce fragmentation.
59: */
60: /*
61: * File: kern/zalloc.c
62: * Author: Avadis Tevanian, Jr.
63: *
64: * Zone-based memory allocator. A zone is a collection of fixed size
65: * data blocks for which quick allocation/deallocation is possible.
66: */
67:
68: #import <mach/features.h>
69:
70: #include <kern/macro_help.h>
71: #include <kern/sched.h>
72: #include <kern/time_out.h>
73: #include <kern/zalloc.h>
74: #include <mach/vm_param.h>
75: #include <vm/vm_kern.h>
76:
77: #if MACH_DEBUG
78: #include <mach/kern_return.h>
79: #include <mach/machine/vm_types.h>
80: #include <mach_debug/zone_info.h>
81: #include <kern/host.h>
82: #include <vm/vm_map.h>
83: #include <vm/vm_user.h>
84: #include <vm/vm_kern.h>
85: #endif
86:
87: #define ADD_TO_ZONE(zone, element) \
88: MACRO_BEGIN \
89: vm_offset_t cur, *last; \
90: if ( (zone)->last_insert != 0 && \
91: (element) > (zone)->last_insert ) \
92: (vm_offset_t)last = (zone)->last_insert; \
93: else \
94: last = &(zone)->free_elements; \
95: while ( (cur = *last) != 0 && \
96: (element) > cur ) \
97: (vm_offset_t)last = cur; \
98: *(vm_offset_t *)(element) = cur; \
99: *last = (element); \
100: (zone)->last_insert = (element); \
101: (zone)->count--; \
102: MACRO_END
103:
104: #define REMOVE_FROM_ZONE(zone, ret, type) \
105: MACRO_BEGIN \
106: (ret) = (type) (zone)->free_elements; \
107: if ((ret) != (type) 0) { \
108: (zone)->count++; \
109: (zone)->free_elements = *(vm_offset_t *)(ret); \
110: if ((zone)->last_insert == (vm_offset_t)(ret)) \
111: (zone)->last_insert = 0; \
112: } \
113: MACRO_END
114:
115: zone_t zone_zone; /* this is the zone containing other zones */
116:
117: boolean_t zone_ignore_overflow = TRUE;
118:
119: vm_map_t zone_map = VM_MAP_NULL;
120: vm_size_t zone_map_size;
121: vm_size_t zone_map_size_min = 12 * 1024 * 1024;
122: vm_size_t zone_map_size_max = 128 * 1024 * 1024;
123: vm_offset_t zone_min, zone_max;
124:
125: /*
126: * The VM system gives us an initial chunk of memory.
127: * It has to be big enough to allocate the zone_zone
128: * and some initial kernel data structures, like kernel maps.
129: * It is advantageous to make it bigger than really necessary,
130: * because this memory is more efficient than normal kernel
131: * virtual memory. (It doesn't have vm_page structures backing it
132: * and it may have other machine-dependent advantages.)
133: * So for best performance, zdata_size should approximate
134: * the amount of memory you expect the zone system to consume.
135: */
136:
137: vm_offset_t zdata;
138: vm_size_t zdata_size = 512 * 1024;
139:
140: #define lock_zone(zone) \
141: MACRO_BEGIN \
142: if ((zone)->pageable) { \
143: lock_write(&(zone)->complex_lock); \
144: } else { \
145: spl_t s = splhigh(); \
146: simple_lock(&(zone)->lock); \
147: (zone)->lock_ipl = s; \
148: } \
149: MACRO_END
150:
151: #define unlock_zone(zone) \
152: MACRO_BEGIN \
153: if ((zone)->pageable) { \
154: lock_done(&(zone)->complex_lock); \
155: } else { \
156: spl_t s = (zone)->lock_ipl; \
157: simple_unlock(&(zone)->lock); \
158: splx(s); \
159: } \
160: MACRO_END
161:
162: #define lock_zone_init(zone) \
163: MACRO_BEGIN \
164: if ((zone)->pageable) { \
165: lock_init(&(zone)->complex_lock, TRUE); \
166: } else { \
167: simple_lock_init(&(zone)->lock); \
168: } \
169: MACRO_END
170:
171: vm_offset_t zget_space(
172: struct zone_free_space *free_space,
173: vm_size_t size,
174: boolean_t canblock);
175:
176: decl_simple_lock_data(,zget_space_lock)
177:
178: /*
179: * A free list entry, which is created
180: * at the front of an available region
181: * of memory. The 'pred' field points
182: * at the 'next' pointer of the previous
183: * entry (or the head pointer).
184: */
185: struct zone_free_space_entry {
186: struct zone_free_space_entry *next;
187: vm_size_t length;
188: struct zone_free_space_entry **pred;
189: int _pad[1];
190: };
191: #define ZONE_MIN_ALLOC 16 /*
192: * *Must* be:
193: * a power of two
194: * and
195: * at least sizeof (struct
196: * zone_free_space_entry)
197: */
198:
199: /*
200: * An entry in the free list hint table,
201: * which allows for quickly locating a
202: * suitably sized region needed to satisfy
203: * a request.
204: */
205: struct zone_free_space_hint {
206: struct zone_free_space_entry *entry;
207: int _pad[3];
208: };
209:
210: /*
211: * Structure used to manage memory which
212: * has been obtained from the system, but
213: * which is not currently owned by any zone.
214: * The main two parameters are 'alloc_unit'
215: * and 'alloc_max'. The former specifies the
216: * allocation granularity. Allocation requests
217: * will be rounded up to this size, which must
218: * be a power of two. The size of the largest
219: * (suggested) allocation request is specified
220: * by 'alloc_max'. The free list is headed by
221: * 'entries', and is a doubly linked list of
222: * free regions, sorted by ascending address.
223: * There also is a table of hints, indexed by
224: * region size, which is used to speed up the
225: * allocations when the free list becomes
226: * especially large. It contains one entry
227: * per size value between alloc_unit and alloc_max.
228: */
229: struct zone_free_space {
230: vm_size_t alloc_unit;
231: vm_size_t alloc_max;
232: struct zone_free_space_entry *entries;
233: integer_t num_entries;
234: integer_t hash_shift;
235: struct zone_free_space_hint *hints;
236: integer_t num_hints;
237: };
238:
239: struct zone_free_space *zone_free_space[8];
240: integer_t zone_free_space_count;
241:
242: /*
243: * There is a default allocation space (zone_free_space[0]),
244: * which is special. It is statically allocated, contains
245: * a single hint, and space is not reclaimed from it. It
246: * is used to allocate space for non collectable zones,
247: * as well as resources needed for bootstrapping purposes.
248: */
249: struct zone_free_space_hint _zone_default_space_hint;
250: struct zone_free_space _zone_default_space;
251: #define zone_default_space (&_zone_default_space)
252:
253: static void zone_free_space_select(zone_t zone);
254:
255: #define zone_collectable(z) ((z)->free_space != 0 && \
256: (z)->free_space != zone_default_space)
257:
258: /*
259: * Compute the hash address for an element
260: * of a particular size. Oversized requests
261: * all land in the last bucket.
262: */
263: static __inline__
264: integer_t
265: zone_free_space_hash(
266: struct zone_free_space *freespace,
267: vm_size_t size
268: )
269: {
270: integer_t hash;
271:
272: if ((hash = size >> freespace->hash_shift) >
273: freespace->num_hints)
274: return (freespace->num_hints);
275: else
276: return (hash);
277: }
278: #define zone_free_space_hint_from_hash(freespace, hash) \
279: ((freespace)->hints + (hash) - 1)
280:
281: /*
282: * Return the hint entry for an element of
283: * a particular size.
284: */
285: static __inline__
286: struct zone_free_space_hint *
287: zone_free_space_hint(
288: struct zone_free_space *freespace,
289: vm_size_t size
290: )
291: {
292: return zone_free_space_hint_from_hash(freespace,
293: zone_free_space_hash(freespace, size));
294: }
295:
296: /*
297: * Conditionally insert the entry into the
298: * hint table such that the hint indicates
299: * the lowest addressed entry of the particular
300: * size.
301: */
302: static __inline__
303: void
304: zone_free_space_hint_insert(
305: struct zone_free_space *freespace,
306: struct zone_free_space_entry *entry
307: )
308: {
309: struct zone_free_space_hint *hint;
310:
311: hint = zone_free_space_hint(freespace, entry->length);
312: if (hint->entry == 0 || entry < hint->entry)
313: hint->entry = entry;
314: }
315:
316: /*
317: * Delete the entry from the hint table (if
318: * present), and locate the next entry of the
319: * particular size by searching forwards in
320: * the free list. For the 'alloc_max' hint,
321: * we choose the next entry of size >= alloc_max.
322: */
323: static
324: void
325: zone_free_space_hint_delete(
326: struct zone_free_space *freespace,
327: struct zone_free_space_entry *entry
328: )
329: {
330: integer_t hash;
331: struct zone_free_space_hint *hint;
332:
333: hash = zone_free_space_hash(freespace, entry->length);
334: hint = zone_free_space_hint_from_hash(freespace, hash);
335:
336: if (entry == hint->entry) {
337: if (hash < freespace->num_hints) {
338: struct zone_free_space_entry
339: *cur, **last = &entry->next;
340:
341: while ((cur = *last) != 0 &&
342: cur->length != entry->length)
343: last = &cur->next;
344:
345: hint->entry = cur;
346: }
347: else {
348: struct zone_free_space_entry
349: *cur, **last = &entry->next;
350:
351: while ((cur = *last) != 0 &&
352: cur->length < freespace->alloc_max)
353: last = &cur->next;
354:
355: hint->entry = cur;
356: }
357: }
358: }
359:
360: /*
361: * Update the hint for an entry which has been
362: * expanded from the front. In this case, the
363: * header has moved (and is no longer valid),
364: * so we need both the length and the address
365: * of the old entry. This could be accomplished
366: * with separate delete/insert operations, but
367: * this is much more efficient for several
368: * important cases.
369: */
370: static
371: void
372: zone_free_space_hint_prepend(
373: struct zone_free_space *freespace,
374: struct zone_free_space_entry *entry,
375: vm_size_t old_length,
376: void *old_entry
377: )
378: {
379: integer_t old_hash, new_hash;
380: struct zone_free_space_hint *hint;
381:
382: /*
383: * Calculate both the new and the old hash
384: * addresses, as well as the old hint structure,
385: * which we are likely to need.
386: */
387: new_hash = zone_free_space_hash(freespace, entry->length);
388: old_hash = zone_free_space_hash(freespace, old_length);
389: hint = zone_free_space_hint_from_hash(freespace, old_hash);
390:
391: /*
392: * Hash address has changed.
393: */
394: if (old_hash != new_hash) {
395: /*
396: * Old hint was valid, update it.
397: */
398: if (old_entry == hint->entry) {
399: if (old_hash < freespace->num_hints) {
400: struct zone_free_space_entry
401: *cur, **last = &entry->next;
402:
403: while ((cur = *last) != 0 &&
404: cur->length != old_length)
405: last = &cur->next;
406:
407: hint->entry = cur;
408: }
409: else {
410: struct zone_free_space_entry
411: *cur, **last = &entry->next;
412:
413: while ((cur = *last) != 0 &&
414: cur->length <
415: freespace->alloc_max)
416: last = &cur->next;
417:
418: hint->entry = cur;
419: }
420: }
421:
422: /*
423: * Insert a hint for the new entry.
424: */
425: hint = zone_free_space_hint_from_hash(freespace, new_hash);
426: if (hint->entry == 0 || entry < hint->entry)
427: hint->entry = entry;
428: }
429: /*
430: * If the hash address has not
431: * changed, always replace a valid
432: * old hint with the new one.
433: */
434: else if (old_entry == hint->entry)
435: hint->entry = entry;
436: }
437:
438: /*
439: * Update the hint for an entry which has been
440: * expanded on the end. This could be accomplished
441: * with separate delete/insert operations, but
442: * this is much more efficient for several
443: * important cases.
444: */
445: static
446: void
447: zone_free_space_hint_append(
448: struct zone_free_space *freespace,
449: struct zone_free_space_entry *entry,
450: vm_size_t old_length
451: )
452: {
453: integer_t old_hash, new_hash;
454: struct zone_free_space_hint *hint;
455:
456: /*
457: * Calculate both the new and the old hash
458: * addresses.
459: */
460: new_hash = zone_free_space_hash(freespace, entry->length);
461: old_hash = zone_free_space_hash(freespace, old_length);
462:
463: if (old_hash != new_hash) {
464: hint = zone_free_space_hint_from_hash(freespace, old_hash);
465: /*
466: * Old hint was valid, update it.
467: */
468: if (entry == hint->entry) {
469: if (old_hash < freespace->num_hints) {
470: struct zone_free_space_entry
471: *cur, **last = &entry->next;
472:
473: while ((cur = *last) != 0 &&
474: cur->length != old_length)
475: last = &cur->next;
476:
477: hint->entry = cur;
478: }
479: else {
480: struct zone_free_space_entry
481: *cur, **last = &entry->next;
482:
483: while ((cur = *last) != 0 &&
484: cur->length <
485: freespace->alloc_max)
486: last = &cur->next;
487:
488: hint->entry = cur;
489: }
490: }
491:
492: /*
493: * Insert a hint for the new entry.
494: */
495: hint = zone_free_space_hint_from_hash(freespace, new_hash);
496: if (hint->entry == 0 || entry < hint->entry)
497: hint->entry = entry;
498: }
499: }
500:
501: /*
502: * Locate a suitably sized entry, using the
503: * allocation hints. The entry is removed
504: * from the hint table, which is also updated
505: * accordingly. The entry's position in the
506: * free list is not affected.
507: */
508: static
509: struct zone_free_space_entry *
510: zone_free_space_lookup(
511: struct zone_free_space *freespace,
512: vm_size_t size
513: )
514: {
515: integer_t hash;
516: struct zone_free_space_hint *hint;
517: struct zone_free_space_entry *entry;
518:
519: /*
520: * Perform a couple of quick checks:
521: * 1) an empty free list or
522: * 2) a suitable first entry
523: */
524: if ((entry = freespace->entries) == 0)
525: return (entry);
526: else if (entry->length >= size) {
527: zone_free_space_hint_delete(freespace, entry);
528:
529: return (entry);
530: }
531:
532: /*
533: * Calculate the hash address and hint
534: * entry corresponding to the request
535: * size. We start there and move on to
536: * the larger hints if needed.
537: */
538: hash = zone_free_space_hash(freespace, size);
539: hint = zone_free_space_hint_from_hash(freespace, hash);
540:
541: /*
542: * This loop checks the exact sized hints
543: * (hash to [num_hints - 1]). If we encounter
544: * a valid hint, we return that entry, after
545: * first searching ahead in the free list to
546: * replace it. If we come to the end of
547: * the free list while searching, we end up
548: * invalidating this hint.
549: */
550: while (hash < freespace->num_hints) {
551: if ((entry = hint->entry) != 0) {
552: struct zone_free_space_entry
553: *cur, **last = &entry->next;
554:
555: while ((cur = *last) != 0 &&
556: cur->length != entry->length)
557: last = &cur->next;
558:
559: hint->entry = cur;
560:
561: return (entry);
562: }
563:
564: hash++; hint++;
565: }
566:
567: /*
568: * Now check the last bucket.
569: */
570: if ((entry = hint->entry) != 0) {
571: struct zone_free_space_entry
572: *cur, **last = &entry->next;
573:
574: /*
575: * The last bucket contains the lowest
576: * addressed entry >= alloc_max, which
577: * isn't necessarily big enough for this
578: * request. If it isn't big enough, then
579: * search ahead in the free list for a
580: * suitable entry to return. In this case
581: * we also leave the current hint alone
582: * since we aren't going to use it.
583: */
584: if (entry->length < size) {
585: while ((cur = *last) != 0 &&
586: cur->length < size)
587: last = &cur->next;
588:
589: return (cur);
590: }
591:
592: while ((cur = *last) != 0 &&
593: cur->length < freespace->alloc_max)
594: last = &cur->next;
595:
596: hint->entry = cur;
597: }
598:
599: return (entry);
600: }
601:
602: /*
603: * Protects first_zone, last_zone, num_zones,
604: * and the next_zone field of zones.
605: */
606: decl_simple_lock_data(,all_zones_lock)
607: zone_t first_zone;
608: zone_t *last_zone;
609: int num_zones;
610:
611: /*
612: * zinit initializes a new zone. The zone data structures themselves
613: * are stored in a zone, which is initially a static structure that
614: * is initialized by zone_init.
615: */
616: zone_t zinit(size, max, alloc, pageable, name)
617: vm_size_t size; /* the size of an element */
618: vm_size_t max; /* maximum memory to use */
619: vm_size_t alloc; /* allocation size */
620: boolean_t pageable; /* is this zone pageable? */
621: char *name; /* a name for the zone */
622: {
623: register zone_t z;
624:
625: if (zone_zone == ZONE_NULL)
626: z = (zone_t) zget_space(
627: zone_default_space,
628: sizeof(struct zone),
629: FALSE);
630: else
631: z = (zone_t) zalloc(zone_zone);
632: if (z == ZONE_NULL)
633: panic("zinit");
634:
635: if (alloc == 0)
636: alloc = PAGE_SIZE;
637:
638: if (size == 0)
639: size = sizeof(z->free_elements);
640:
641: size = ((size + (ZONE_MIN_ALLOC - 1)) & ~(ZONE_MIN_ALLOC - 1));
642:
643: /*
644: * Round off all the parameters appropriately.
645: */
646:
647: if ((max = round_page(max)) < (alloc = round_page(alloc)))
648: max = alloc;
649:
650: z->last_insert = z->free_elements = 0;
651: z->cur_size = 0;
652: z->max_size = max;
653: z->elem_size = size;
654:
655: z->alloc_size = alloc;
656: z->pageable = pageable;
657: z->zone_name = name;
658: z->count = 0;
659: z->doing_alloc = FALSE;
660: z->exhaustible = z->sleepable = FALSE;
661: z->expandable = TRUE;
662: lock_zone_init(z);
663: zone_free_space_select(z);
664:
665: /*
666: * Add the zone to the all-zones list.
667: */
668:
669: z->next_zone = ZONE_NULL;
670: simple_lock(simple_lock_addr(all_zones_lock));
671: *last_zone = z;
672: last_zone = &z->next_zone;
673: num_zones++;
674: simple_unlock(simple_lock_addr(all_zones_lock));
675:
676: return(z);
677: }
678:
679: /*
680: * Cram the given memory into the specified zone.
681: */
682: void zcram(zone, newmem, size)
683: register zone_t zone;
684: vm_offset_t newmem;
685: vm_size_t size;
686: {
687: register vm_size_t elem_size;
688:
689: if (newmem == (vm_offset_t) 0) {
690: panic("zcram - memory at zero");
691: }
692: elem_size = zone->elem_size;
693:
694: lock_zone(zone);
695: while (size >= elem_size) {
696: ADD_TO_ZONE(zone, newmem);
697: zone->count++; /* compensate for ADD_TO_ZONE */
698: size -= elem_size;
699: newmem += elem_size;
700: zone->cur_size += elem_size;
701: }
702: unlock_zone(zone);
703: }
704:
705: /*
706: * Allocate (return) a new zone element from a new memory
707: * region. Remaining memory from the new region is added
708: * to the specified freelist. Before generating the new
709: * element, an attempt is made to combine the new region
710: * with an existing entry. New elements are always taken
711: * from the front of a free region.
712: */
713: vm_offset_t zone_free_space_add(freespace, size, new_space, space_to_add)
714: struct zone_free_space *freespace;
715: vm_size_t size;
716: vm_offset_t new_space;
717: vm_size_t space_to_add;
718: {
719: struct zone_free_space_entry *cur, **last;
720:
721: if (freespace == 0)
722: freespace = zone_default_space;
723:
724: /*
725: * Search the free list for an existing
726: * abutting entry.
727: */
728: last = &freespace->entries;
729: while ((cur = *last) != 0 &&
730: (vm_offset_t)cur < new_space &&
731: ((vm_offset_t)cur + cur->length) != new_space)
732: last = &cur->next;
733:
734: if (cur == 0 || ((vm_offset_t)cur + cur->length) < new_space) {
735: /*
736: * No entry was found to combine with.
737: * Take the new element from the front
738: * of the new region, and insert the
739: * remainder as a new entry.
740: */
741: if ((space_to_add - size) >= ZONE_MIN_ALLOC) {
742: /*
743: * If we are not at the end of
744: * the free list, then insert the
745: * new entry after the current entry.
746: */
747: if (cur != 0)
748: last = &cur->next;
749: (vm_offset_t)cur = new_space + size;
750: cur->length = space_to_add - size;
751: if (cur->next = *last)
752: cur->next->pred = &cur->next;
753: cur->pred = last;
754: *last = cur;
755: freespace->num_entries++;
756:
757: /*
758: * Insert this entry into the hint
759: * table.
760: */
761: zone_free_space_hint_insert(freespace, cur);
762: }
763: }
764: else
765: if (((vm_offset_t)cur + cur->length) == new_space) {
766: struct zone_free_space_entry *new;
767:
768: /*
769: * Delete the existing entry from the
770: * hint table. It is very likely that
771: * the hash address will be changing
772: * anyways.
773: */
774: zone_free_space_hint_delete(freespace, cur);
775:
776: /*
777: * Combine the new region with an existing
778: * entry, and take the new element from the
779: * front of the aggregate region. Create a
780: * new entry for the remainder, and insert it
781: * in place of the existing entry.
782: */
783: new_space = (vm_offset_t)cur;
784: (vm_offset_t)new = (vm_offset_t)cur + size;
785: new->length = cur->length + space_to_add - size;
786: if (new->next = cur->next)
787: new->next->pred = &new->next;
788: new->pred = last;
789: *last = new;
790:
791: /*
792: * Insert this entry into the hint
793: * table.
794: */
795: zone_free_space_hint_insert(freespace, new);
796: }
797:
798: return (new_space);
799: }
800:
801: #if 0
802: /* NOT USED and OUTDATED */
803: /*
804: * Ensure that no portion of the specified region
805: * is represented on the free list (either wholly
806: * or in part).
807: */
808: void zone_free_space_remove(freespace, address, size)
809: struct zone_free_space *freespace;
810: vm_offset_t address;
811: vm_size_t size;
812: {
813: struct zone_free_space_entry
814: *cur, **last;
815:
816: if (freespace == 0)
817: freespace = zone_default_space;
818:
819: /*
820: * Search the free list, looking for
821: * the first suitable entry.
822: */
823: last = &freespace->entries;
824: while ((cur = *last) != 0) {
825: if ((vm_offset_t)cur >= address) {
826: /*
827: * Entry is above (and does not
828: * overlap) the region. (skip)
829: */
830: if ((vm_offset_t)cur >= (address + size))
831: last = &cur->next;
832: else {
833: /*
834: * Entry is entirely contained
835: * within the region. (remove)
836: */
837: if (((vm_offset_t)cur + cur->length) <=
838: (address + size)) {
839: *last = cur->next;
840: freespace->num_entries--;
841: }
842: else {
843: struct zone_free_space_entry *new;
844:
845: /*
846: * Entry overlaps the end
847: * of the region. (clip)
848: */
849: (vm_offset_t)new = address + size;
850: new->length = cur->length -
851: ((vm_offset_t)new -
852: (vm_offset_t)cur);
853: new->next = cur->next;
854: *last = new;
855: }
856:
857: break;
858: }
859: }
860: else {
861: /*
862: * Entry is entirely below the
863: * region. (skip)
864: */
865: if (((vm_offset_t)cur + cur->length) <= address)
866: last = &cur->next;
867: else {
868: /*
869: * Entry overlaps the front
870: * of the region. (clip)
871: */
872: cur->length = address - (vm_offset_t)cur;
873: break;
874: }
875: }
876: }
877: }
878: #endif
879:
880: /*
881: * Return the elements on the zone free list back
882: * to the free space pool, coalescing with existing
883: * space where possible. Caller must hold the
884: * zget_space_lock, as well as the lock on the zone.
885: */
886: void zone_collect(zone)
887: struct zone *zone;
888: {
889: struct zone_free_space_entry *cur, **last, *new;
890: vm_offset_t *free, *elem, next;
891: vm_size_t elem_size = zone->elem_size;
892: struct zone_free_space *freespace = zone->free_space;
893:
894: if (!zone_collectable(zone))
895: return;
896:
897: last = &freespace->entries;
898:
899: free = &zone->free_elements;
900: while (((vm_offset_t)elem = *free) != 0) {
901: zone->cur_size -= elem_size;
902: next = *elem;
903:
904: while ((cur = *last) != 0 &&
905: ((vm_offset_t)cur +
906: cur->length) < (vm_offset_t)elem)
907: last = &cur->next;
908: /*
909: * Either at end of (maybe empty)
910: * list, or new entry before current
911: * entry.
912: */
913: if (cur == 0 || ((vm_offset_t)elem + elem_size) <
914: (vm_offset_t)cur) {
915: (vm_offset_t)new = (vm_offset_t)elem;
916: new->length = elem_size;
917: if (new->next = cur)
918: cur->pred = &new->next;
919: new->pred = last;
920: *last = new;
921: freespace->num_entries++;
922:
923: /*
924: * Insert this entry into the hint
925: * table.
926: */
927: zone_free_space_hint_insert(freespace, new);
928: }
929: else
930: /*
931: * Prepend element to current entry
932: */
933: if (((vm_offset_t)elem + elem_size) == (vm_offset_t)cur) {
934: vm_size_t old_length = cur->length;
935:
936: (vm_offset_t)new = (vm_offset_t)elem;
937: new->length = cur->length + elem_size;
938: if (new->next = cur->next)
939: new->next->pred = &new->next;
940: new->pred = last;
941: *last = new;
942:
943: /*
944: * Update the hint table.
945: */
946: zone_free_space_hint_prepend(freespace,
947: new, old_length, cur);
948: }
949: else
950: /*
951: * Append element to current entry.
952: */
953: if (((vm_offset_t)cur + cur->length) == (vm_offset_t)elem) {
954: vm_size_t old_length = cur->length;
955:
956: cur->length += elem_size;
957: /*
958: * Coalesce the current entry with the
959: * following one if we are filling the
960: * gap between them.
961: */
962: if (((vm_offset_t)cur + cur->length) ==
963: (vm_offset_t)cur->next) {
964:
965: /*
966: * Delete the obsolete entry
967: * from the hint table.
968: */
969: zone_free_space_hint_delete(
970: freespace, cur->next);
971:
972: cur->length += cur->next->length;
973: if (cur->next = cur->next->next)
974: cur->next->pred = &cur->next;
975: freespace->num_entries--;
976: }
977:
978: /*
979: * Update the hint table.
980: */
981: zone_free_space_hint_append(freespace,
982: cur, old_length);
983: }
984: else
985: /* WTF?? */;
986:
987: *free = next;
988: }
989:
990: zone->last_insert = 0;
991: }
992:
993: /*
994: * Scan the collectable free space free lists
995: * and gather up pages which can be returned to
996: * the system. Caller must hold the zget_space_lock.
997: */
998: struct zone_free_space_entry *
999: zone_free_space_reclaim(void)
1000: {
1001: struct zone_free_space_entry **last, *cur, *pages = 0;
1002: struct zone_free_space **f = &zone_free_space[0];
1003: int i;
1004:
1005: for (i = 1; i < zone_free_space_count; i++) {
1006: last = &(*++f)->entries;
1007: while ((cur = *last) != 0) {
1008: if (cur->length >= PAGE_SIZE) {
1009: vm_offset_t start, end;
1010:
1011: start = round_page((vm_offset_t)cur);
1012: end = trunc_page(
1013: (vm_offset_t)cur + cur->length);
1014: if (start < end &&
1015: start >= zone_min &&
1016: end <= zone_max) {
1017: struct zone_free_space_entry *tmp;
1018:
1019: /*
1020: * Delete this entry from the hint
1021: * table. Space which is leftover
1022: * after clipping will be added back
1023: * normally after the entries are
1024: * created.
1025: */
1026: zone_free_space_hint_delete(*f, cur);
1027:
1028: /*
1029: * If the region does not end on a
1030: * page boundary, create a new
1031: * trailing entry.
1032: */
1033: if (((vm_offset_t)cur + cur->length) != end) {
1034: (vm_offset_t)tmp = end;
1035: tmp->length = (vm_offset_t)cur +
1036: cur->length - end;
1037: if (tmp->next = cur->next)
1038: tmp->next->pred = &tmp->next;
1039:
1040: /*
1041: * Now, if the region does begin
1042: * on a page boundary, just remove
1043: * the current entry.
1044: */
1045: if ((vm_offset_t)cur == start) {
1046: *last = tmp;
1047: tmp->pred = last;
1048: }
1049: /*
1050: * Otherwise, adjust the current
1051: * entry, and link it to the new
1052: * one. Do not forget to account
1053: * for the new entry.
1054: */
1055: else {
1056: cur->length = start -
1057: (vm_offset_t)cur;
1058: cur->next = tmp;
1059: tmp->pred = &cur->next;
1060: (*f)->num_entries++;
1061:
1062: /*
1063: * Reinsert the leading
1064: * entry into the hint
1065: * table.
1066: */
1067: zone_free_space_hint_insert(
1068: *f, cur);
1069: }
1070:
1071: /*
1072: * Insert the new trailing entry
1073: * into the hint table.
1074: */
1075: zone_free_space_hint_insert(*f, tmp);
1076: }
1077: /*
1078: * If the region does not begin on a
1079: * page boundary (but the end does),
1080: * adjust the current entry.
1081: */
1082: else if ((vm_offset_t)cur != start) {
1083: cur->length = start - (vm_offset_t)cur;
1084:
1085: /*
1086: * Reinsert the entry into
1087: * the hint table.
1088: */
1089: zone_free_space_hint_insert(*f, cur);
1090: }
1091: /*
1092: * If no clipping is required, just
1093: * remove the current entry.
1094: */
1095: else {
1096: if (*last = cur->next)
1097: cur->next->pred = last;
1098: (*f)->num_entries--;
1099: }
1100:
1101: /*
1102: * Add the new page aligned region
1103: * to the list of pages to be freed.
1104: * Continue on with the next entry.
1105: */
1106: (vm_offset_t)tmp = start;
1107: tmp->length = end - start;
1108: tmp->next = pages;
1109: pages = tmp;
1110: continue;
1111: }
1112: }
1113:
1114: /*
1115: * Skip this entry.
1116: */
1117: last = &cur->next;
1118: }
1119: }
1120:
1121: return (pages);
1122: }
1123:
1124: /*
1125: * Contiguous space allocator for non-paged zones. Allocates "size" amount
1126: * of memory from zone_map.
1127: */
1128:
1129: vm_offset_t zget_space(freespace, size, canblock)
1130: struct zone_free_space *freespace;
1131: vm_size_t size;
1132: boolean_t canblock;
1133: {
1134: vm_offset_t new_space = 0;
1135: vm_offset_t result;
1136: vm_size_t space_to_add;
1137: struct zone_free_space_entry
1138: *cur, **last;
1139:
1140: if (freespace == 0)
1141: freespace = zone_default_space;
1142:
1143: /*
1144: * Round up all requests (even 0) to
1145: * our minimum allocation unit.
1146: */
1147: if (size > ZONE_MIN_ALLOC)
1148: size = ((size + (ZONE_MIN_ALLOC - 1)) & ~(ZONE_MIN_ALLOC - 1));
1149: else
1150: size = ZONE_MIN_ALLOC;
1151:
1152: simple_lock(simple_lock_addr(zget_space_lock));
1153: for (;;) {
1154: if ((cur = zone_free_space_lookup(freespace, size)) != 0) {
1155: last = cur->pred;
1156:
1157: /*
1158: * The entry which was found has been
1159: * removed from the hint table, but
1160: * remains in the free list. Trim off
1161: * the space to be returned.
1162: */
1163: if ((cur->length - size) < ZONE_MIN_ALLOC) {
1164: /*
1165: * This is a real lose if it
1166: * happens in a collectable
1167: * space, since the memory
1168: * will be lost, making it
1169: * impossible to reclaim the
1170: * page later.
1171: */
1172: if (*last = cur->next)
1173: cur->next->pred = last;
1174: freespace->num_entries--;
1175: }
1176: else {
1177: struct zone_free_space_entry *new;
1178:
1179: /*
1180: * Create a new entry for the
1181: * remaining space and position
1182: * on the free list.
1183: */
1184: (vm_offset_t)new = (vm_offset_t)cur + size;
1185: new->length = cur->length - size;
1186: if (new->next = cur->next)
1187: new->next->pred = &new->next;
1188: new->pred = last;
1189: *last = new;
1190:
1191: /*
1192: * After trimming the entry, reinsert
1193: * it back into the hint table.
1194: */
1195: zone_free_space_hint_insert(freespace, new);
1196: }
1197: result = (vm_offset_t)cur;
1198: break;
1199: }
1200: else if (new_space == 0) {
1201: /*
1202: * Add at least one page to allocation area.
1203: */
1204:
1205: space_to_add = round_page(size);
1206:
1207: if (zdata_size >= space_to_add) {
1208: zdata_size -= space_to_add;
1209: result = zone_free_space_add(
1210: freespace,
1211: size,
1212: zdata + zdata_size,
1213: space_to_add);
1214: break;
1215: }
1216:
1217: /*
1218: * Memory cannot be wired down while holding
1219: * any locks that the pageout daemon might
1220: * need to free up pages. [Making the zget_space
1221: * lock a complex lock does not help in this
1222: * regard.]
1223: *
1224: * Unlock and allocate memory. Because several
1225: * threads might try to do this at once, don't
1226: * use the memory before checking for available
1227: * space again.
1228: */
1229:
1230: simple_unlock(simple_lock_addr(zget_space_lock));
1231: {
1232: kern_return_t kr;
1233:
1234: kr = kmem_alloc_zone(zone_map,
1235: &new_space, space_to_add,
1236: canblock);
1237: if (kr != KERN_SUCCESS) {
1238: if (kr == KERN_NO_SPACE)
1239: panic("zget_space");
1240:
1241: return(0);
1242: }
1243: }
1244:
1245: simple_lock(simple_lock_addr(zget_space_lock));
1246: continue;
1247: }
1248: else {
1249: /*
1250: * Memory was allocated in a previous iteration.
1251: */
1252:
1253: result = zone_free_space_add(
1254: freespace,
1255: size,
1256: new_space,
1257: space_to_add);
1258: new_space = 0;
1259: break;
1260: }
1261: }
1262: simple_unlock(simple_lock_addr(zget_space_lock));
1263:
1264: if (new_space != 0)
1265: kmem_free(zone_map, new_space, space_to_add);
1266:
1267: return(result);
1268: }
1269:
1270: static
1271: struct zone_free_space *
1272: zone_free_space_alloc(alloc_unit, alloc_max)
1273: vm_size_t alloc_unit;
1274: vm_size_t alloc_max;
1275: {
1276: struct zone_free_space *freespace, **f;
1277:
1278: if (zone_free_space_count >=
1279: (sizeof (zone_free_space) / sizeof (freespace)))
1280: return (0);
1281:
1282: f = &zone_free_space[zone_free_space_count++];
1283:
1284: (vm_offset_t)freespace = zget_space(
1285: zone_default_space,
1286: sizeof (struct zone_free_space),
1287: FALSE);
1288: freespace->alloc_unit = alloc_unit;
1289: freespace->alloc_max = alloc_max;
1290:
1291: freespace->entries = 0;
1292: freespace->num_entries = 0;
1293:
1294: freespace->hash_shift = 0;
1295: while (!(alloc_unit & 01)) {
1296: freespace->hash_shift++; alloc_unit >>= 1;
1297: }
1298:
1299: freespace->num_hints = freespace->alloc_max >> freespace->hash_shift;
1300: (vm_offset_t)freespace->hints =
1301: zget_space(
1302: zone_default_space,
1303: freespace->num_hints *
1304: sizeof (struct zone_free_space_hint),
1305: FALSE);
1306: bzero(freespace->hints, freespace->num_hints *
1307: sizeof (struct zone_free_space_hint));
1308:
1309: *f = freespace;
1310:
1311: return (freespace);
1312: }
1313:
1314: static
1315: void
1316: zone_free_space_select(zone)
1317: zone_t zone;
1318: {
1319: struct zone_free_space **f = &zone_free_space[1];
1320: vm_size_t elem_size;
1321: int i;
1322:
1323: if (zone->cur_size > 0)
1324: return;
1325:
1326: for (i = 1; i < zone_free_space_count; i++) {
1327: elem_size = ((zone->elem_size + ((*f)->alloc_unit - 1))
1328: & ~((*f)->alloc_unit - 1));
1329: if (elem_size <= (*f)->alloc_max) {
1330: zone->elem_size = elem_size;
1331: zone->free_space = *f;
1332: break;
1333: }
1334:
1335: f++;
1336: }
1337: }
1338:
1339: /*
1340: * Initialize the "zone of zones" which uses fixed memory allocated
1341: * earlier in memory initialization. zone_bootstrap is called
1342: * before zone_init.
1343: */
1344: void zone_bootstrap(void)
1345: {
1346: simple_lock_init(simple_lock_addr(all_zones_lock));
1347: first_zone = ZONE_NULL;
1348: last_zone = &first_zone;
1349: num_zones = 0;
1350:
1351: if (sizeof (struct zone_free_space_entry) > ZONE_MIN_ALLOC)
1352: panic("zone_bootstrap");
1353:
1354: simple_lock_init(simple_lock_addr(zget_space_lock));
1355:
1356: _zone_default_space.hints = &_zone_default_space_hint;
1357: _zone_default_space.num_hints = 1;
1358:
1359: zone_free_space[0] = &_zone_default_space;
1360: zone_free_space_count = 1;
1361:
1362: zone_zone = ZONE_NULL;
1363: zone_zone = zinit(sizeof(struct zone), 128 * sizeof(struct zone),
1364: sizeof(struct zone), FALSE, "zones");
1365:
1366: zone_free_space_alloc(16, 96);
1367: zone_free_space_alloc(128, 768);
1368: zone_free_space_alloc(1024, PAGE_SIZE);
1369: }
1370:
1371: vm_size_t
1372: zone_map_sizer(void)
1373: {
1374: #if defined(__ppc__)
1375: vm_size_t map_size = mem_size / 4;
1376: #else
1377: vm_size_t map_size = mem_size / 8;
1378: #endif
1379:
1380: if (map_size < zone_map_size_min)
1381: map_size = zone_map_size_min;
1382: else
1383: if (map_size > zone_map_size_max)
1384: map_size = zone_map_size_max;
1385:
1386: return (map_size);
1387: }
1388:
1389: void zone_init(void)
1390: {
1391: zone_map_size = zone_map_sizer();
1392:
1393: zone_map = kmem_suballoc(kernel_map, &zone_min, &zone_max,
1394: zone_map_size, FALSE);
1395: }
1396:
1397:
1398: /*
1399: * zalloc returns an element from the specified zone.
1400: */
1401: static
1402: vm_offset_t zalloc_canblock(zone, canblock)
1403: register zone_t zone;
1404: boolean_t canblock;
1405: {
1406: vm_offset_t addr;
1407:
1408: if (zone == ZONE_NULL)
1409: panic ("zalloc: null zone");
1410:
1411: lock_zone(zone);
1412: REMOVE_FROM_ZONE(zone, addr, vm_offset_t);
1413: while (addr == 0) {
1414: /*
1415: * If nothing was there, try to get more
1416: */
1417: if (zone->doing_alloc) {
1418: /*
1419: * Someone is allocating memory for this zone.
1420: * Wait for it to show up, then try again.
1421: */
1422: if (!canblock) {
1423: unlock_zone(zone);
1424: return(0);
1425: }
1426: assert_wait((event_t)&zone->doing_alloc, TRUE);
1427: /* XXX say wakeup needed */
1428: unlock_zone(zone);
1429: thread_block_with_continuation((void (*)()) 0);
1430: lock_zone(zone);
1431: }
1432: else {
1433: if ((zone->cur_size + (zone->pageable ?
1434: zone->alloc_size : zone->elem_size)) >
1435: zone->max_size) {
1436: if (zone->exhaustible)
1437: break;
1438:
1439: if (zone->expandable) {
1440: /*
1441: * We're willing to overflow certain
1442: * zones, but not without complaining.
1443: *
1444: * This is best used in conjunction
1445: * with the collecatable flag. What we
1446: * want is an assurance we can get the
1447: * memory back, assuming there's no
1448: * leak.
1449: */
1450: zone->max_size += (zone->max_size >> 1);
1451: } else if (!zone_ignore_overflow) {
1452: unlock_zone(zone);
1453: if (!canblock)
1454: return(0);
1455: printf("zone \"%s\" empty.\n",
1456: zone->zone_name);
1457: panic("zalloc");
1458: }
1459: }
1460:
1461: if (zone->pageable)
1462: zone->doing_alloc = TRUE;
1463: unlock_zone(zone);
1464:
1465: if (zone->pageable) {
1466: if (kmem_alloc_pageable(zone_map, &addr,
1467: zone->alloc_size)
1468: != KERN_SUCCESS)
1469: panic("zalloc");
1470: zcram(zone, addr, zone->alloc_size);
1471: lock_zone(zone);
1472: zone->doing_alloc = FALSE;
1473: /* XXX check before doing this */
1474: thread_wakeup((event_t)&zone->doing_alloc);
1475:
1476: REMOVE_FROM_ZONE(zone, addr, vm_offset_t);
1477: } else {
1478: addr = zget_space(
1479: zone->free_space,
1480: zone->elem_size,
1481: canblock);
1482: if (addr == 0) {
1483: if (!canblock)
1484: return(0);
1485: panic("zalloc");
1486: }
1487:
1488: lock_zone(zone);
1489: zone->count++;
1490: zone->cur_size += zone->elem_size;
1491: unlock_zone(zone);
1492: return(addr);
1493: }
1494: }
1495: }
1496:
1497: unlock_zone(zone);
1498: return(addr);
1499: }
1500:
1501: vm_offset_t zalloc(zone)
1502: register zone_t zone;
1503: {
1504: return (zalloc_canblock(zone, TRUE));
1505: }
1506:
1507: vm_offset_t zalloc_noblock(zone)
1508: register zone_t zone;
1509: {
1510: return (zalloc_canblock(zone, FALSE));
1511: }
1512:
1513:
1514: /*
1515: * zget returns an element from the specified zone
1516: * and immediately returns nothing if there is nothing there.
1517: *
1518: * This form should be used when you can not block (like when
1519: * processing an interrupt).
1520: */
1521: vm_offset_t zget(zone)
1522: register zone_t zone;
1523: {
1524: register vm_offset_t addr;
1525:
1526: if (zone == ZONE_NULL)
1527: panic ("zalloc: null zone");
1528:
1529: lock_zone(zone);
1530: REMOVE_FROM_ZONE(zone, addr, vm_offset_t);
1531: unlock_zone(zone);
1532:
1533: return(addr);
1534: }
1535:
1536: boolean_t zone_check = FALSE;
1537:
1538: void zfree(zone, elem)
1539: register zone_t zone;
1540: vm_offset_t elem;
1541: {
1542: lock_zone(zone);
1543: if (zone_check) {
1544: vm_offset_t this;
1545:
1546: /* check the zone's consistency */
1547:
1548: for (this = zone->free_elements;
1549: this != 0;
1550: this = * (vm_offset_t *) this)
1551: if (this == elem)
1552: panic("zfree");
1553: }
1554: ADD_TO_ZONE(zone, elem);
1555: unlock_zone(zone);
1556: }
1557:
1558: void zcollectable(zone)
1559: zone_t zone;
1560: {
1561: /* zones are collectable by default
1562: * and cannot later be changed back to collectable */
1563: }
1564:
1565: void zchange(zone, pageable, sleepable, exhaustible, collectable)
1566: zone_t zone;
1567: boolean_t pageable;
1568: boolean_t sleepable;
1569: boolean_t exhaustible;
1570: boolean_t collectable;
1571: {
1572: zone->pageable = pageable;
1573: zone->sleepable = sleepable;
1574: zone->exhaustible = exhaustible;
1575: /* zones are collectable by default
1576: * and later can only be changed to non-collectable */
1577: if (!collectable)
1578: zone->free_space = zone_default_space;
1579: lock_zone_init(zone);
1580: }
1581:
1582: #if MACH_DEBUG
1583: kern_return_t host_zone_info(host, namesp, namesCntp, infop, infoCntp)
1584: host_t host;
1585: zone_name_array_t *namesp;
1586: unsigned int *namesCntp;
1587: zone_info_array_t *infop;
1588: unsigned int *infoCntp;
1589: {
1590: zone_name_t *names;
1591: vm_offset_t names_addr;
1592: vm_size_t names_size = 0; /*'=0' to quiet gcc warnings */
1593: zone_info_t *info;
1594: vm_offset_t info_addr;
1595: vm_size_t info_size = 0; /*'=0' to quiet gcc warnings */
1596: unsigned int max_zones, i;
1597: zone_t z;
1598: kern_return_t kr;
1599:
1600: if (host == HOST_NULL)
1601: return KERN_INVALID_HOST;
1602:
1603: /*
1604: * We assume that zones aren't freed once allocated.
1605: * We won't pick up any zones that are allocated later.
1606: */
1607:
1608: simple_lock(simple_lock_addr(all_zones_lock));
1609: max_zones = num_zones;
1610: z = first_zone;
1611: simple_unlock(simple_lock_addr(all_zones_lock));
1612:
1613: if (max_zones <= *namesCntp) {
1614: /* use in-line memory */
1615:
1616: names = *namesp;
1617: } else {
1618: names_size = round_page(max_zones * sizeof *names);
1619: kr = kmem_alloc_pageable(ipc_kernel_map,
1620: &names_addr, names_size);
1621: if (kr != KERN_SUCCESS)
1622: return kr;
1623:
1624: names = (zone_name_t *) names_addr;
1625: }
1626:
1627: if (max_zones <= *infoCntp) {
1628: /* use in-line memory */
1629:
1630: info = *infop;
1631: } else {
1632: info_size = round_page(max_zones * sizeof *info);
1633: kr = kmem_alloc_pageable(ipc_kernel_map,
1634: &info_addr, info_size);
1635: if (kr != KERN_SUCCESS) {
1636: if (names != *namesp)
1637: kmem_free(ipc_kernel_map,
1638: names_addr, names_size);
1639: return kr;
1640: }
1641:
1642: info = (zone_info_t *) info_addr;
1643: }
1644:
1645: for (i = 0; i < max_zones; i++) {
1646: zone_name_t *zn = &names[i];
1647: zone_info_t *zi = &info[i];
1648: struct zone zcopy;
1649:
1650: assert(z != ZONE_NULL);
1651:
1652: lock_zone(z);
1653: zcopy = *z;
1654: unlock_zone(z);
1655:
1656: simple_lock(simple_lock_addr(all_zones_lock));
1657: z = z->next_zone;
1658: simple_unlock(simple_lock_addr(all_zones_lock));
1659:
1660: /* assuming here the name data is static */
1661: (void) strncpy(zn->zn_name, zcopy.zone_name,
1662: sizeof zn->zn_name);
1663:
1664: zi->zi_count = zcopy.count;
1665: zi->zi_cur_size = zcopy.cur_size;
1666: zi->zi_max_size = zcopy.max_size;
1667: zi->zi_elem_size = zcopy.elem_size;
1668: zi->zi_alloc_size = zcopy.alloc_size;
1669: zi->zi_pageable = zcopy.pageable;
1670: zi->zi_sleepable = zcopy.sleepable;
1671: zi->zi_exhaustible = zcopy.exhaustible;
1672: zi->zi_collectable = zone_collectable(&zcopy);
1673: }
1674:
1675: if (names != *namesp) {
1676: vm_size_t used;
1677: #if MACH_OLD_VM_COPY
1678: #else
1679: vm_map_copy_t copy;
1680: #endif
1681:
1682: used = max_zones * sizeof *names;
1683:
1684: if (used != names_size)
1685: bzero((char *) (names_addr + used), names_size - used);
1686:
1687: #if MACH_OLD_VM_COPY
1688: kr = vm_move(
1689: ipc_kernel_map, names_addr,
1690: ipc_soft_map, names_size,
1691: TRUE, &names_addr);
1692: assert(kr == KERN_SUCCESS);
1693:
1694: *namesp = (zone_name_t *) names_addr;
1695: #else
1696: kr = vm_map_copyin(ipc_kernel_map, names_addr, names_size,
1697: TRUE, ©);
1698: assert(kr == KERN_SUCCESS);
1699:
1700: *namesp = (zone_name_t *) copy;
1701: #endif
1702: }
1703: *namesCntp = max_zones;
1704:
1705: if (info != *infop) {
1706: vm_size_t used;
1707: #if MACH_OLD_VM_COPY
1708: #else
1709: vm_map_copy_t copy;
1710: #endif
1711:
1712: used = max_zones * sizeof *info;
1713:
1714: if (used != info_size)
1715: bzero((char *) (info_addr + used), info_size - used);
1716:
1717: #if MACH_OLD_VM_COPY
1718: kr = vm_move(
1719: ipc_kernel_map, info_addr,
1720: ipc_soft_map, info_size,
1721: TRUE, &info_addr);
1722: assert(kr == KERN_SUCCESS);
1723:
1724: *infop = (zone_info_t *) info_addr;
1725: #else
1726: kr = vm_map_copyin(ipc_kernel_map, info_addr, info_size,
1727: TRUE, ©);
1728: assert(kr == KERN_SUCCESS);
1729:
1730: *infop = (zone_info_t *) copy;
1731: #endif
1732: }
1733: *infoCntp = max_zones;
1734:
1735: return KERN_SUCCESS;
1736: }
1737:
1738: kern_return_t host_zone_free_space_info(
1739: host,
1740: infop, infoCnt,
1741: chunksp, chunksCnt)
1742: host_t host;
1743: zone_free_space_info_array_t *infop;
1744: mach_msg_type_number_t *infoCnt;
1745: zone_free_space_chunk_array_t *chunksp;
1746: mach_msg_type_number_t *chunksCnt;
1747: {
1748: kern_return_t kr;
1749: vm_size_t size1, size2;
1750: vm_offset_t addr1, addr2;
1751: vm_offset_t memory1, memory2;
1752: mach_msg_type_number_t
1753: actual1, actual2;
1754: zone_free_space_info_t
1755: *info;
1756: zone_free_space_chunk_t
1757: *chunk;
1758: struct zone_free_space_entry
1759: **last, *cur;
1760: struct zone_free_space
1761: *freespace;
1762: int i;
1763:
1764: if (host == HOST_NULL)
1765: return KERN_INVALID_HOST;
1766:
1767: size1 = size2 = 0;
1768:
1769: for (;;) {
1770: vm_size_t size1_needed, size2_needed;
1771:
1772: size1_needed = size2_needed = 0;
1773:
1774: simple_lock(simple_lock_addr(zget_space_lock));
1775:
1776: actual1 = actual2 = 0;
1777:
1778: for (actual1 = 0; actual1 < zone_free_space_count; actual1++)
1779: actual2 += zone_free_space[actual1]->num_entries;
1780:
1781: if (actual1 > *infoCnt)
1782: size1_needed = round_page(
1783: actual1 * sizeof (**infop));
1784:
1785: if (actual2 > *chunksCnt)
1786: size2_needed = round_page(
1787: actual2 * sizeof (**chunksp));
1788:
1789: if (size1_needed <= size1 &&
1790: size2_needed <= size2)
1791: break;
1792:
1793: simple_unlock(simple_lock_addr(zget_space_lock));
1794:
1795: if (size1 < size1_needed) {
1796: if (size1 != 0)
1797: kmem_free(ipc_kernel_map, addr1, size1);
1798: size1 = size1_needed;
1799:
1800: kr = kmem_alloc_pageable(
1801: ipc_kernel_map, &addr1, size1);
1802: if (kr != KERN_SUCCESS) {
1803: if (size2 != 0)
1804: kmem_free(
1805: ipc_kernel_map, addr2, size2);
1806: return KERN_RESOURCE_SHORTAGE;
1807: }
1808: #if MACH_OLD_VM_COPY
1809: kr = vm_map_pageable(
1810: ipc_kernel_map, addr1, addr1 + size1, FALSE);
1811: assert(kr == KERN_SUCCESS);
1812: #else
1813: kr = vm_map_pageable(
1814: ipc_kernel_map, addr1, addr1 + size1,
1815: VM_PROT_READ|VM_PROT_WRITE);
1816: assert(kr == KERN_SUCCESS);
1817: #endif
1818: }
1819:
1820: if (size2 < size2_needed) {
1821: if (size2 != 0)
1822: kmem_free(ipc_kernel_map, addr2, size2);
1823: size2 = size2_needed;
1824:
1825: kr = kmem_alloc_pageable(
1826: ipc_kernel_map, &addr2, size2);
1827: if (kr != KERN_SUCCESS) {
1828: if (size1 != 0)
1829: kmem_free(
1830: ipc_kernel_map, addr1, size1);
1831: return KERN_RESOURCE_SHORTAGE;
1832: }
1833: #if MACH_OLD_VM_COPY
1834: kr = vm_map_pageable(
1835: ipc_kernel_map, addr2, addr2 + size2, FALSE);
1836: assert(kr == KERN_SUCCESS);
1837: #else
1838: kr = vm_map_pageable(
1839: ipc_kernel_map, addr2, addr2 + size2,
1840: VM_PROT_READ|VM_PROT_WRITE);
1841: assert(kr == KERN_SUCCESS);
1842: #endif
1843: }
1844: }
1845:
1846: if (size1 != 0)
1847: info = (zone_free_space_info_t *)addr1;
1848: else
1849: info = *infop;
1850:
1851: if (size2 != 0)
1852: chunk = (zone_free_space_chunk_t *)addr2;
1853: else
1854: chunk = *chunksp;
1855:
1856: for (i = 0; i < actual1; i++) {
1857: freespace = zone_free_space[i];
1858:
1859: info->zf_alloc_unit = freespace->alloc_unit;
1860: info->zf_alloc_max = freespace->alloc_max;
1861: info->zf_num_chunks = freespace->num_entries;
1862: info++;
1863:
1864: last = &freespace->entries;
1865: while ((cur = *last) != 0) {
1866: chunk->zf_address = (vm_offset_t)cur;
1867: chunk->zf_length = cur->length;
1868: chunk++;
1869:
1870: last = &cur->next;
1871: }
1872: }
1873:
1874: simple_unlock(simple_lock_addr(zget_space_lock));
1875:
1876: if (actual1 != 0 && size1 != 0) {
1877: vm_size_t size_used;
1878:
1879: size_used = round_page(actual1 * sizeof (**infop));
1880:
1881: #if MACH_OLD_VM_COPY
1882: kr = vm_map_pageable(
1883: ipc_kernel_map, addr1, addr1 + size_used, TRUE);
1884: assert(kr == KERN_SUCCESS);
1885:
1886: kr = vm_move(
1887: ipc_kernel_map, addr1,
1888: ipc_soft_map, size_used,
1889: TRUE, &memory1);
1890: assert(kr == KERN_SUCCESS);
1891: #else
1892: kr = vm_map_pageable(
1893: ipc_kernel_map,
1894: addr1, addr1 + size_used, VM_PROT_NONE);
1895: assert(kr == KERN_SUCCESS);
1896:
1897: kr = vm_map_copyin(
1898: ipc_kernel_map, addr1, size_used,
1899: TRUE, &memory1);
1900: assert(kr == KERN_SUCCESS);
1901: #endif
1902:
1903: if (size_used != size1)
1904: kmem_free(
1905: ipc_kernel_map,
1906: addr1 + size_used, size1 - size_used);
1907:
1908: *infop = (zone_free_space_info_t *)memory1;
1909: }
1910: else if (actual1 == 0) {
1911: #if MACH_OLD_VM_COPY
1912: *infop = (zone_free_space_info_t *)0;
1913: #else
1914: *infop = (zone_free_space_info_t *)VM_MAP_COPY_NULL;
1915: #endif
1916:
1917: if (size1 != 0)
1918: kmem_free(ipc_kernel_map, addr1, size1);
1919: }
1920:
1921: *infoCnt = actual1;
1922:
1923: if (actual2 != 0 && size2 != 0) {
1924: vm_size_t size_used;
1925:
1926: size_used = round_page(actual2 * sizeof (**chunksp));
1927:
1928: #if MACH_OLD_VM_COPY
1929: kr = vm_map_pageable(
1930: ipc_kernel_map, addr2, addr2 + size_used, TRUE);
1931: assert(kr == KERN_SUCCESS);
1932:
1933: kr = vm_move(
1934: ipc_kernel_map, addr2,
1935: ipc_soft_map, size_used,
1936: TRUE, &memory2);
1937: assert(kr == KERN_SUCCESS);
1938: #else
1939: kr = vm_map_pageable(
1940: ipc_kernel_map,
1941: addr2, addr2 + size2, VM_PROT_NONE);
1942: assert(kr == KERN_SUCCESS);
1943:
1944: kr = vm_map_copyin(
1945: ipc_kernel_map, addr2, size_used,
1946: TRUE, &memory2);
1947: assert(kr == KERN_SUCCESS);
1948: #endif
1949:
1950: if (size_used != size2)
1951: kmem_free(
1952: ipc_kernel_map,
1953: addr2 + size_used, size2 - size_used);
1954:
1955: *chunksp = (zone_free_space_chunk_t *)memory2;
1956: }
1957: else if (actual2 == 0) {
1958: #if MACH_OLD_VM_COPY
1959: *chunksp = (zone_free_space_chunk_t *)0;
1960: #else
1961: *chunksp = (zone_free_space_chunk_t *)VM_MAP_COPY_NULL;
1962: #endif
1963:
1964: if (size2 != 0)
1965: kmem_free(ipc_kernel_map, addr2, size2);
1966: }
1967:
1968: *chunksCnt = actual2;
1969:
1970: return KERN_SUCCESS;
1971: }
1972:
1973: kern_return_t host_zone_collect(
1974: host_t host,
1975: boolean_t collect_zones,
1976: boolean_t reclaim_pages)
1977: {
1978: struct zone_free_space_entry
1979: *cur, *pages = 0;
1980: zone_t z;
1981: int max_zones, i;
1982:
1983: if (host == HOST_NULL)
1984: return KERN_INVALID_HOST;
1985:
1986: if (!collect_zones)
1987: return KERN_SUCCESS;
1988:
1989: simple_lock(simple_lock_addr(zget_space_lock));
1990:
1991: simple_lock(simple_lock_addr(all_zones_lock));
1992: max_zones = num_zones;
1993: z = first_zone;
1994: simple_unlock(simple_lock_addr(all_zones_lock));
1995:
1996: for (i = 0; i < max_zones; i++) {
1997: assert(z != ZONE_NULL);
1998: /* run this at splhigh so that interupt routines that use zones
1999: can not interupt while their zone is locked */
2000: lock_zone(z);
2001:
2002: if (!z->pageable && zone_collectable(z))
2003: zone_collect(z);
2004:
2005: unlock_zone(z);
2006: simple_lock(simple_lock_addr(all_zones_lock));
2007: z = z->next_zone;
2008: simple_unlock(simple_lock_addr(all_zones_lock));
2009: }
2010:
2011: if (reclaim_pages)
2012: pages = zone_free_space_reclaim();
2013:
2014: simple_unlock(simple_lock_addr(zget_space_lock));
2015:
2016: /*
2017: * Return any reclaimed pages to
2018: * the system.
2019: */
2020: while ((cur = pages) != 0) {
2021: pages = cur->next;
2022: kmem_free(zone_map, (vm_offset_t)cur, cur->length);
2023: }
2024:
2025: return KERN_SUCCESS;
2026: }
2027: #endif MACH_DEBUG
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.