2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 * File: vm/task_working_set.c
27 * Author: Chris Youngworth
30 * Working set detection and maintainence module
34 #include <mach/mach_types.h>
35 #include <mach/memory_object.h>
36 #include <mach/shared_memory_server.h>
37 #include <vm/task_working_set.h>
38 #include <vm/vm_kern.h>
39 #include <vm/vm_map.h>
40 #include <vm/vm_page.h>
41 #include <vm/vm_pageout.h>
42 #include <kern/sched.h>
43 #include <kern/kalloc.h>
45 #include <vm/vm_protos.h>
48 * LP64todo - Task Working Set Support is for 32-bit only
50 extern zone_t lsf_zone
;
52 /* declarations for internal use only routines */
62 tws_write_startup_file(
67 unsigned int string_length
);
70 tws_read_startup_file(
72 tws_startup_t startup
,
73 vm_offset_t cache_size
);
76 tws_create_startup_list(
80 tws_startup_list_lookup(
81 tws_startup_t startup
,
85 tws_internal_startup_send(
91 tws_hash_line_t hash_line
,
100 tws_traverse_address_hash_list (
103 vm_offset_t page_addr
,
105 vm_object_offset_t offset
,
107 tws_hash_ptr_t
*target_ele
,
108 tws_hash_ptr_t
**previous_ptr
,
109 tws_hash_ptr_t
**free_list
,
110 unsigned int exclusive_addr
);
113 tws_traverse_object_hash_list (
117 vm_object_offset_t offset
,
118 unsigned int pagemask
,
119 tws_hash_ptr_t
*target_ele
,
120 tws_hash_ptr_t
**previous_ptr
,
121 tws_hash_ptr_t
**free_list
);
135 int tws_test_for_community(
138 vm_object_offset_t offset
,
139 unsigned int threshold
,
140 unsigned int *pagemask
);
145 vm_object_offset_t offset
,
147 tws_hash_line_t
*line
);
149 /* Note: all of the routines below depend on the associated map lock for */
150 /* synchronization, the map lock will be on when the routines are called */
151 /* and on when they return */
161 if ((style
!= TWS_HASH_STYLE_BASIC
) &&
162 (style
!= TWS_HASH_STYLE_BASIC
)) {
163 return((tws_hash_t
)NULL
);
167 tws
= (tws_hash_t
)(kalloc(sizeof(struct tws_hash
)));
168 if(tws
== (tws_hash_t
)NULL
)
171 if((tws
->table
[0] = (tws_hash_ptr_t
*)
172 kalloc(sizeof(tws_hash_ptr_t
) * lines
* rows
))
174 kfree(tws
, sizeof(struct tws_hash
));
175 return (tws_hash_t
)NULL
;
177 if((tws
->table_ele
[0] = (tws_hash_ptr_t
)
178 kalloc(sizeof(struct tws_hash_ptr
) * lines
* rows
))
180 kfree(tws
->table
[0], sizeof(tws_hash_ptr_t
) * lines
* rows
);
181 kfree(tws
, sizeof(struct tws_hash
));
182 return (tws_hash_t
)NULL
;
184 if((tws
->alt_ele
[0] = (tws_hash_ptr_t
)
185 kalloc(sizeof(struct tws_hash_ptr
) * lines
* rows
))
187 kfree(tws
->table
[0], sizeof(tws_hash_ptr_t
) * lines
* rows
);
188 kfree(tws
->table_ele
[0],
189 sizeof(struct tws_hash_ptr
) * lines
* rows
);
190 kfree(tws
, sizeof(struct tws_hash
));
191 return (tws_hash_t
)NULL
;
193 if((tws
->cache
[0] = (struct tws_hash_line
*)
194 kalloc(sizeof(struct tws_hash_line
) * lines
))
196 kfree(tws
->table
[0], sizeof(tws_hash_ptr_t
) * lines
* rows
);
197 kfree(tws
->table_ele
[0],
198 sizeof(struct tws_hash_ptr
) * lines
* rows
);
199 kfree(tws
->alt_ele
[0],
200 sizeof(struct tws_hash_ptr
) * lines
* rows
);
201 kfree(tws
, sizeof(struct tws_hash
));
202 return (tws_hash_t
)NULL
;
204 tws
->free_hash_ele
[0] = (tws_hash_ptr_t
)0;
205 tws
->obj_free_count
[0] = 0;
206 tws
->addr_free_count
[0] = 0;
208 /* most defaults are such that a bzero will initialize */
209 bzero((char *)tws
->table
[0],sizeof(tws_hash_ptr_t
)
211 bzero((char *)tws
->table_ele
[0],sizeof(struct tws_hash_ptr
)
213 bzero((char *)tws
->alt_ele
[0],sizeof(struct tws_hash_ptr
)
215 bzero((char *)tws
->cache
[0], sizeof(struct tws_hash_line
)
218 mutex_init(&tws
->lock
, 0);
220 tws
->current_line
= 0;
221 tws
->pageout_count
= 0;
223 tws
->startup_cache
= NULL
;
224 tws
->startup_name
= NULL
;
225 tws
->number_of_lines
= lines
;
226 tws
->number_of_elements
= rows
;
227 tws
->expansion_count
= 1;
228 tws
->lookup_count
= 0;
229 tws
->insert_count
= 0;
230 tws
->time_of_creation
= sched_tick
;
237 vm_page_lookup_nohint(vm_object_t object
, vm_object_offset_t offset
);
243 tws_hash_line_t hash_line
,
244 __unused vm_object_t object
,
247 struct tws_hash_ele
*hash_ele
;
248 struct tws_hash_ptr
**trailer
;
249 struct tws_hash_ptr
**free_list
;
250 tws_hash_ele_t addr_ele
;
257 if(tws
->line_count
< tws
->number_of_lines
) {
261 if(tws
->pageout_count
!= vm_pageout_scan_event_counter
) {
263 vm_pageout_scan_event_counter
;
270 hash_line
->ele_count
= 0;
272 for (i
=0; i
<tws
->number_of_elements
; i
++) {
274 hash_ele
= &(hash_line
->list
[i
]);
275 if(hash_ele
->object
!= 0) {
277 vm_object_offset_t local_off
= 0;
278 tws_hash_ptr_t cache_ele
;
280 index
= alt_tws_hash(
281 hash_ele
->page_addr
& TWS_ADDR_OFF_MASK
,
282 tws
->number_of_elements
,
283 tws
->number_of_lines
);
285 tws_traverse_address_hash_list(tws
, index
,
286 hash_ele
->page_addr
, hash_ele
->object
,
287 hash_ele
->offset
, hash_ele
->map
,
288 &cache_ele
, &trailer
, &free_list
, 0);
289 if(cache_ele
!= NULL
) {
290 addr_ele
= (tws_hash_ele_t
)((unsigned int)
291 (cache_ele
->element
) & ~TWS_ADDR_HASH
);
292 if(addr_ele
!= hash_ele
)
293 panic("tws_hash_line_clear:"
295 cache_ele
->element
= 0;
296 *trailer
= cache_ele
->next
;
297 cache_ele
->next
= *free_list
;
298 *free_list
= cache_ele
;
301 index
= alt_tws_hash(
302 (hash_ele
->page_addr
- 0x1f000)
304 tws
->number_of_elements
,
305 tws
->number_of_lines
);
307 tws_traverse_address_hash_list(tws
, index
,
308 hash_ele
->page_addr
, hash_ele
->object
,
309 hash_ele
->offset
, hash_ele
->map
,
310 &cache_ele
, &trailer
, &free_list
, 0);
312 if(cache_ele
!= NULL
) {
313 addr_ele
= (tws_hash_ele_t
)((unsigned int)
314 (cache_ele
->element
) & ~TWS_ADDR_HASH
);
315 if(addr_ele
!= hash_ele
)
316 panic("tws_hash_line_clear: "
318 cache_ele
->element
= 0;
319 *trailer
= cache_ele
->next
;
320 cache_ele
->next
= *free_list
;
321 *free_list
= cache_ele
;
325 if((hash_ele
->map
!= NULL
) && (live
)) {
329 if (object
!= hash_ele
->object
) {
331 vm_object_unlock(object
);
332 vm_object_lock(hash_ele
->object
);
335 if (dump_pmap
== 1) {
336 for (j
= 0x1; j
!= 0; j
= j
<<1) {
337 if(j
& hash_ele
->page_cache
) {
338 p
= vm_page_lookup_nohint(hash_ele
->object
,
339 hash_ele
->offset
+ local_off
);
340 if((p
!= NULL
) && (p
->wire_count
== 0)) {
341 pmap_remove_some_phys((pmap_t
)vm_map_pmap(current_map()),
345 local_off
+= PAGE_SIZE_64
;
349 if (object
!= hash_ele
->object
) {
350 vm_object_unlock(hash_ele
->object
);
352 vm_object_lock(object
);
357 if(tws
->style
== TWS_HASH_STYLE_SIGNAL
) {
358 vm_object_deallocate(hash_ele
->object
);
359 vm_map_deallocate(hash_ele
->map
);
362 index
= do_tws_hash(hash_ele
->object
, hash_ele
->offset
,
363 tws
->number_of_elements
,
364 tws
->number_of_lines
);
366 tws_traverse_object_hash_list(tws
,
367 index
, hash_ele
->object
, hash_ele
->offset
,
368 0xFFFFFFFF, &cache_ele
, &trailer
, &free_list
);
369 if((cache_ele
!= NULL
) && (cache_ele
->element
== hash_ele
)) {
370 cache_ele
->element
= 0;
371 *trailer
= cache_ele
->next
;
372 cache_ele
->next
= *free_list
;
373 *free_list
= cache_ele
;
375 hash_ele
->object
= 0;
383 vm_object_offset_t offset
,
385 tws_hash_line_t
*line
)
392 tws_hash_ptr_t cache_ele
;
393 tws_hash_ptr_t
*trailer
;
394 tws_hash_ptr_t
*free_list
;
396 /* don't cache private objects */
400 index
= do_tws_hash(object
, offset
,
401 tws
->number_of_elements
, tws
->number_of_lines
);
405 if(tws
->lookup_count
== 0)
406 tws
->insert_count
= 0;
407 if(tws
->startup_name
!= NULL
) {
409 age_of_cache
= ((sched_tick
410 - tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
411 if (age_of_cache
> 45) {
412 return KERN_OPERATION_TIMED_OUT
;
416 if(tws
->lookup_count
> (4 * tws
->expansion_count
417 * tws
->number_of_elements
* tws
->number_of_lines
) &&
418 (tws
->lookup_count
> (2 * tws
->insert_count
))) {
419 if(tws
->startup_cache
) {
421 age_of_cache
= ((sched_tick
422 - tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
423 if (age_of_cache
> 45) {
424 return KERN_OPERATION_TIMED_OUT
;
429 pagenum
= (vm_offset_t
)(offset
& TWS_INDEX_MASK
);
430 pagenum
= pagenum
>> 12;
431 pagenum
= 1 << pagenum
; /* get the appropriate page in 32 page block */
432 tws_traverse_object_hash_list(tws
, index
, object
, offset
, pagenum
,
433 &cache_ele
, &trailer
, &free_list
);
434 if(cache_ele
!= NULL
) {
435 set
= cache_ele
->element
->line
/tws
->number_of_lines
;
436 ele_line
= cache_ele
->element
->line
- set
;
437 *line
= &tws
->cache
[set
][ele_line
];
449 vm_object_offset_t offset
,
451 tws_hash_line_t
*line
)
455 if(!tws_lock_try(tws
)) {
458 kr
= tws_internal_lookup(tws
,
459 offset
, object
, line
);
465 tws_expand_working_set(
467 unsigned int line_count
,
472 struct tws_hash temp
;
474 /* Note we do an elaborate dance to preserve the header that */
475 /* task is pointing to. In this way we can avoid taking a task */
476 /* lock every time we want to access the tws */
478 if (old_tws
->number_of_lines
>= line_count
) {
481 if((new_tws
= tws_hash_create(line_count
,
482 old_tws
->number_of_elements
, old_tws
->style
)) == 0) {
483 return(KERN_NO_SPACE
);
488 for(i
= 0; i
<old_tws
->number_of_lines
; i
++) {
489 for(j
= 0; j
<old_tws
->number_of_elements
; j
++) {
490 for(k
= 0; k
<old_tws
->expansion_count
; k
++) {
491 tws_hash_ele_t entry
;
492 vm_object_offset_t paddr
;
493 unsigned int page_index
;
494 entry
= &old_tws
->cache
[k
][i
].list
[j
];
495 if(entry
->object
!= 0) {
497 for(page_index
= 1; page_index
!= 0;
498 page_index
= page_index
<< 1) {
499 if (entry
->page_cache
& page_index
) {
503 entry
->page_addr
+paddr
,
515 temp
.style
= new_tws
->style
;
516 temp
.current_line
= new_tws
->current_line
;
517 temp
.pageout_count
= new_tws
->pageout_count
;
518 temp
.line_count
= new_tws
->line_count
;
519 temp
.number_of_lines
= new_tws
->number_of_lines
;
520 temp
.number_of_elements
= new_tws
->number_of_elements
;
521 temp
.expansion_count
= new_tws
->expansion_count
;
522 temp
.lookup_count
= new_tws
->lookup_count
;
523 temp
.insert_count
= new_tws
->insert_count
;
524 for(i
= 0; i
<new_tws
->expansion_count
; i
++) {
525 temp
.obj_free_count
[i
] = new_tws
->obj_free_count
[i
];
526 temp
.addr_free_count
[i
] = new_tws
->addr_free_count
[i
];
527 temp
.free_hash_ele
[i
] = new_tws
->free_hash_ele
[i
];
528 temp
.table
[i
] = new_tws
->table
[i
];
529 temp
.table_ele
[i
] = new_tws
->table_ele
[i
];
530 temp
.alt_ele
[i
] = new_tws
->alt_ele
[i
];
531 temp
.cache
[i
] = new_tws
->cache
[i
];
534 new_tws
->style
= old_tws
->style
;
535 new_tws
->current_line
= old_tws
->current_line
;
536 new_tws
->pageout_count
= old_tws
->pageout_count
;
537 new_tws
->line_count
= old_tws
->line_count
;
538 new_tws
->number_of_lines
= old_tws
->number_of_lines
;
539 new_tws
->number_of_elements
= old_tws
->number_of_elements
;
540 new_tws
->expansion_count
= old_tws
->expansion_count
;
541 new_tws
->lookup_count
= old_tws
->lookup_count
;
542 new_tws
->insert_count
= old_tws
->insert_count
;
543 for(i
= 0; i
<old_tws
->expansion_count
; i
++) {
544 new_tws
->obj_free_count
[i
] = old_tws
->obj_free_count
[i
];
545 new_tws
->addr_free_count
[i
] = old_tws
->addr_free_count
[i
];
546 new_tws
->free_hash_ele
[i
] = old_tws
->free_hash_ele
[i
];
547 new_tws
->table
[i
] = old_tws
->table
[i
];
548 new_tws
->table_ele
[i
] = old_tws
->table_ele
[i
];
549 new_tws
->alt_ele
[i
] = old_tws
->alt_ele
[i
];
550 new_tws
->cache
[i
] = old_tws
->cache
[i
];
553 old_tws
->style
= temp
.style
;
554 old_tws
->current_line
= temp
.current_line
;
555 old_tws
->pageout_count
= temp
.pageout_count
;
556 old_tws
->line_count
= temp
.line_count
;
557 old_tws
->number_of_lines
= temp
.number_of_lines
;
558 old_tws
->number_of_elements
= temp
.number_of_elements
;
559 old_tws
->expansion_count
= temp
.expansion_count
;
560 old_tws
->lookup_count
= temp
.lookup_count
;
561 old_tws
->insert_count
= temp
.insert_count
;
562 for(i
= 0; i
<temp
.expansion_count
; i
++) {
563 old_tws
->obj_free_count
[i
] = temp
.obj_free_count
[i
];;
564 old_tws
->addr_free_count
[i
] = temp
.addr_free_count
[i
];;
565 old_tws
->free_hash_ele
[i
] = NULL
;
566 old_tws
->table
[i
] = temp
.table
[i
];
567 old_tws
->table_ele
[i
] = temp
.table_ele
[i
];
568 old_tws
->alt_ele
[i
] = temp
.alt_ele
[i
];
569 old_tws
->cache
[i
] = temp
.cache
[i
];
572 tws_hash_destroy(new_tws
);
577 tws_hash_t test_tws
= 0;
582 vm_object_offset_t offset
,
584 vm_offset_t page_addr
,
588 unsigned int alt_index
;
589 unsigned int index_enum
[2];
590 unsigned int ele_index
;
591 tws_hash_ptr_t cache_ele
;
592 tws_hash_ptr_t obj_ele
= NULL
;
593 tws_hash_ptr_t addr_ele
= NULL
;
594 tws_hash_ptr_t
*trailer
;
595 tws_hash_ptr_t
*free_list
;
596 tws_hash_ele_t target_element
= NULL
;
598 unsigned int current_line
;
601 unsigned int startup_cache_line
;
602 int age_of_cache
= 0;
604 if(!tws_lock_try(tws
)) {
608 current_line
= 0xFFFFFFFF;
611 startup_cache_line
= 0;
613 if (tws
->startup_cache
) {
614 vm_offset_t startup_page_addr
;
616 startup_page_addr
= page_addr
- (offset
- (offset
& TWS_HASH_OFF_MASK
));
618 age_of_cache
= ((sched_tick
- tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
620 startup_cache_line
= tws_startup_list_lookup(tws
->startup_cache
, startup_page_addr
);
622 /* This next bit of code, the and alternate hash */
623 /* are all made necessary because of IPC COW */
625 /* Note: the use of page_addr modified by delta from offset */
626 /* frame base means we may miss some previous entries. However */
627 /* we will not miss the present entry. This is most important */
628 /* in avoiding duplication of entries against long lived non-cow */
630 index_enum
[0] = alt_tws_hash(
631 page_addr
& TWS_ADDR_OFF_MASK
,
632 tws
->number_of_elements
, tws
->number_of_lines
);
634 index_enum
[1] = alt_tws_hash(
635 (page_addr
- 0x1f000) & TWS_ADDR_OFF_MASK
,
636 tws
->number_of_elements
, tws
->number_of_lines
);
638 for(ctr
= 0; ctr
< 2;) {
639 tws_hash_ele_t resident
;
640 tws_traverse_address_hash_list(tws
,
641 index_enum
[ctr
], page_addr
, NULL
,
643 &cache_ele
, &trailer
, &free_list
, 1);
644 if(cache_ele
!= NULL
) {
646 resident
= (tws_hash_ele_t
)((unsigned int)
647 cache_ele
->element
& ~TWS_ADDR_HASH
);
648 if((object
== resident
->object
) &&
650 (offset
& TWS_HASH_OFF_MASK
)) {
651 /* This is our object/offset */
653 |= startup_cache_line
;
654 resident
->page_cache
|=
656 (offset
& TWS_INDEX_MASK
))>>12));
658 if (age_of_cache
> 45)
659 return KERN_OPERATION_TIMED_OUT
;
662 if((object
->shadow
==
665 + object
->shadow_offset
)
666 == (offset
& TWS_HASH_OFF_MASK
))) {
667 /* if we just shadowed, inherit */
668 /* access pattern from parent */
669 startup_cache_line
|=
670 resident
->page_cache
;
671 /* thow out old entry */
672 resident
->page_cache
= 0;
675 resident
->page_cache
&=
676 ~(1<<(((vm_offset_t
)(page_addr
677 - resident
->page_addr
))
680 /* Throw out old entry if there are no */
681 /* more pages in cache */
682 if(resident
->page_cache
== 0) {
683 /* delete addr hash entry */
684 cache_ele
->element
= 0;
685 *trailer
= cache_ele
->next
;
686 cache_ele
->next
= *free_list
;
687 *free_list
= cache_ele
;
688 /* go after object hash */
692 tws
->number_of_elements
,
693 tws
->number_of_lines
);
694 tws_traverse_object_hash_list(tws
,
695 index
, resident
->object
,
697 0xFFFFFFFF, &cache_ele
,
698 &trailer
, &free_list
);
699 if(cache_ele
!= NULL
) {
701 TWS_HASH_STYLE_SIGNAL
) {
702 vm_object_deallocate(
703 cache_ele
->element
->object
);
705 cache_ele
->element
->map
);
708 cache_ele
->element
->line
;
710 /tws
->number_of_lines
;
711 current_line
-= set
*
712 tws
->number_of_lines
;
713 if(cache_ele
->element
->object
!= 0) {
714 cache_ele
->element
->object
= 0;
716 [current_line
].ele_count
--;
718 cache_ele
->element
= 0;
719 *trailer
= cache_ele
->next
;
720 cache_ele
->next
= *free_list
;
721 *free_list
= cache_ele
;
730 * We may or may not have a current line setting coming out of
731 * the code above. If we have a current line it means we can
732 * choose to back-fill the spot vacated by a previous entry.
733 * We have yet to do a definitive check using the original obj/off
734 * We will do that now and override the current line if we
738 index
= do_tws_hash(object
, offset
,
739 tws
->number_of_elements
, tws
->number_of_lines
);
741 alt_index
= index_enum
[0];
743 tws_traverse_object_hash_list(tws
, index
, object
, offset
,
744 0xFFFFFFFF, &cache_ele
, &trailer
, &free_list
);
745 if(cache_ele
!= NULL
) {
747 current_line
= cache_ele
->element
->line
;
748 set
= current_line
/tws
->number_of_lines
;
749 current_line
-= set
* tws
->number_of_lines
;
750 target_element
= cache_ele
->element
;
752 /* Now check to see if we have a hash addr for it */
753 tws_traverse_address_hash_list(tws
,
754 alt_index
, obj_ele
->element
->page_addr
,
755 obj_ele
->element
->object
,
756 obj_ele
->element
->offset
,
757 obj_ele
->element
->map
,
758 &cache_ele
, &trailer
, &free_list
, 0);
759 if(cache_ele
!= NULL
) {
760 addr_ele
= cache_ele
;
762 addr_ele
= new_addr_hash(tws
, set
, alt_index
);
763 /* if cannot allocate just do without */
764 /* we'll get it next time around */
770 if(tws
->style
== TWS_HASH_STYLE_SIGNAL
) {
771 vm_object_reference(object
);
772 vm_map_reference(map
);
775 if(current_line
== 0xFFFFFFFF) {
776 current_line
= tws
->current_line
;
777 set
= current_line
/tws
->number_of_lines
;
778 current_line
= current_line
- (set
* tws
->number_of_lines
);
782 tws
->current_line
= tws
->number_of_lines
- 1;
785 if(tws
->cache
[set
][current_line
].ele_count
786 >= tws
->number_of_elements
) {
789 if(current_line
== tws
->number_of_lines
) {
792 if (set
== tws
->expansion_count
) {
793 if((tws
->lookup_count
<
794 (2 * tws
->insert_count
)) &&
795 (set
<TWS_HASH_EXPANSION_MAX
)) {
796 tws
->lookup_count
= 0;
797 tws
->insert_count
= 0;
798 if(tws
->number_of_lines
799 < TWS_HASH_LINE_COUNT
) {
802 return KERN_NO_SPACE
;
804 /* object persistence is guaranteed by */
805 /* an elevated paging or object */
806 /* reference count in the caller. */
807 vm_object_unlock(object
);
808 if((tws
->table
[set
] = (tws_hash_ptr_t
*)
809 kalloc(sizeof(tws_hash_ptr_t
)
810 * tws
->number_of_lines
811 * tws
->number_of_elements
))
814 } else if((tws
->table_ele
[set
] =
816 kalloc(sizeof(struct tws_hash_ptr
)
817 * tws
->number_of_lines
818 * tws
->number_of_elements
))
820 kfree(tws
->table
[set
],
821 sizeof(tws_hash_ptr_t
)
822 * tws
->number_of_lines
823 * tws
->number_of_elements
);
825 } else if((tws
->alt_ele
[set
] =
827 kalloc(sizeof(struct tws_hash_ptr
)
828 * tws
->number_of_lines
829 * tws
->number_of_elements
))
831 kfree(tws
->table_ele
[set
],
832 sizeof(struct tws_hash_ptr
)
833 * tws
->number_of_lines
834 * tws
->number_of_elements
);
835 kfree(tws
->table
[set
],
836 sizeof(tws_hash_ptr_t
)
837 * tws
->number_of_lines
838 * tws
->number_of_elements
);
839 tws
->table
[set
] = NULL
;
842 } else if((tws
->cache
[set
] =
843 (struct tws_hash_line
*)
845 (struct tws_hash_line
)
846 * tws
->number_of_lines
))
848 kfree(tws
->alt_ele
[set
],
849 sizeof(struct tws_hash_ptr
)
850 * tws
->number_of_lines
851 * tws
->number_of_elements
);
852 kfree(tws
->table_ele
[set
],
853 sizeof(struct tws_hash_ptr
)
854 * tws
->number_of_lines
855 * tws
->number_of_elements
);
856 kfree(tws
->table
[set
],
857 sizeof(tws_hash_ptr_t
)
858 * tws
->number_of_lines
859 * tws
->number_of_elements
);
860 tws
->table
[set
] = NULL
;
864 tws
->free_hash_ele
[set
] =
866 tws
->obj_free_count
[set
] = 0;
867 tws
->addr_free_count
[set
] = 0;
868 bzero((char *)tws
->table
[set
],
869 sizeof(tws_hash_ptr_t
)
870 * tws
->number_of_lines
871 * tws
->number_of_elements
);
872 bzero((char *)tws
->table_ele
[set
],
873 sizeof(struct tws_hash_ptr
)
874 * tws
->number_of_lines
875 * tws
->number_of_elements
);
876 bzero((char *)tws
->alt_ele
[set
],
877 sizeof(struct tws_hash_ptr
)
878 * tws
->number_of_lines
879 * tws
->number_of_elements
);
880 bzero((char *)tws
->cache
[set
],
881 sizeof(struct tws_hash_line
)
882 * tws
->number_of_lines
);
884 vm_object_lock(object
);
886 if (tws
->startup_name
!= NULL
) {
889 age_of_cache
= ((sched_tick
- tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
893 if (age_of_cache
> 45)
894 return KERN_OPERATION_TIMED_OUT
;
898 tws
->lookup_count
= 0;
899 tws
->insert_count
= 0;
903 tws
->current_line
= set
* tws
->number_of_lines
;
905 if(set
< tws
->expansion_count
) {
906 tws_hash_line_clear(tws
,
907 &(tws
->cache
[set
][current_line
]), object
, TRUE
);
908 if(tws
->cache
[set
][current_line
].ele_count
909 >= tws
->number_of_elements
) {
910 if(tws
->style
== TWS_HASH_STYLE_SIGNAL
) {
911 vm_object_deallocate(object
);
912 vm_map_deallocate(map
);
918 tws
->expansion_count
++;
924 /* set object hash element */
925 if(obj_ele
== NULL
) {
926 obj_ele
= new_obj_hash(tws
, set
, index
);
927 if(obj_ele
== NULL
) {
928 tws
->cache
[set
][current_line
].ele_count
929 = tws
->number_of_elements
;
935 /* set address hash element */
936 if(addr_ele
== NULL
) {
937 addr_ele
= new_addr_hash(tws
, set
, alt_index
);
940 if(target_element
== NULL
) {
942 for(i
= 0; i
<tws
->number_of_elements
; i
++) {
943 if(tws
->cache
[set
][current_line
].
944 list
[ele_index
].object
== 0) {
948 if(ele_index
>= tws
->number_of_elements
)
953 if(i
== tws
->number_of_elements
)
954 panic("tws_insert: no free elements");
957 &(tws
->cache
[set
][current_line
].list
[ele_index
]);
959 tws
->cache
[set
][current_line
].ele_count
++;
962 obj_ele
->element
= target_element
;
964 addr_ele
->element
= (tws_hash_ele_t
)
965 (((unsigned int)target_element
) | TWS_ADDR_HASH
);
967 target_element
->object
= object
;
968 target_element
->offset
= offset
& TWS_HASH_OFF_MASK
;
969 target_element
->page_addr
=
970 page_addr
- (offset
- (offset
& TWS_HASH_OFF_MASK
));
971 target_element
->map
= map
;
972 target_element
->line
=
973 current_line
+ (set
* tws
->number_of_lines
);
975 target_element
->page_cache
|= startup_cache_line
;
976 target_element
->page_cache
|= 1<<(((vm_offset_t
)(offset
& TWS_INDEX_MASK
))>>12);
980 if (age_of_cache
> 45)
981 return KERN_OPERATION_TIMED_OUT
;
988 * lengthen the cluster of pages by the number of pages encountered in the
989 * working set up to the limit requested by the caller. The object needs
990 * to be locked on entry. The map does not because the tws_lookup function
991 * is used only to find if their is an entry in the cache. No transient
992 * data from the cache is de-referenced.
997 * MACH page map - an optional optimization where a bit map is maintained
998 * by the VM subsystem for internal objects to indicate which pages of
999 * the object currently reside on backing store. This existence map
1000 * duplicates information maintained by the vnode pager. It is
1001 * created at the time of the first pageout against the object, i.e.
1002 * at the same time pager for the object is created. The optimization
1003 * is designed to eliminate pager interaction overhead, if it is
1004 * 'known' that the page does not exist on backing store.
1006 * LOOK_FOR() evaluates to TRUE if the page specified by object/offset is
1007 * either marked as paged out in the existence map for the object or no
1008 * existence map exists for the object. LOOK_FOR() is one of the
1009 * criteria in the decision to invoke the pager. It is also used as one
1010 * of the criteria to terminate the scan for adjacent pages in a clustered
1011 * pagein operation. Note that LOOK_FOR() always evaluates to TRUE for
1012 * permanent objects. Note also that if the pager for an internal object
1013 * has not been created, the pager is not invoked regardless of the value
1014 * of LOOK_FOR() and that clustered pagein scans are only done on an object
1015 * for which a pager has been created.
1017 * PAGED_OUT() evaluates to TRUE if the page specified by the object/offset
1018 * is marked as paged out in the existence map for the object. PAGED_OUT()
1019 * PAGED_OUT() is used to determine if a page has already been pushed
1020 * into a copy object in order to avoid a redundant page out operation.
1022 #define LOOK_FOR(o, f) (vm_external_state_get((o)->existence_map, (f)) \
1023 != VM_EXTERNAL_STATE_ABSENT)
1024 #define PAGED_OUT(o, f) (vm_external_state_get((o)->existence_map, (f)) \
1025 == VM_EXTERNAL_STATE_EXISTS)
1026 #else /* MACH_PAGEMAP */
1028 * If the MACH page map optimization is not enabled,
1029 * LOOK_FOR() always evaluates to TRUE. The pager will always be
1030 * invoked to resolve missing pages in an object, assuming the pager
1031 * has been created for the object. In a clustered page operation, the
1032 * absence of a page on backing backing store cannot be used to terminate
1033 * a scan for adjacent pages since that information is available only in
1034 * the pager. Hence pages that may not be paged out are potentially
1035 * included in a clustered request. The vnode pager is coded to deal
1036 * with any combination of absent/present pages in a clustered
1037 * pagein request. PAGED_OUT() always evaluates to FALSE, i.e. the pager
1038 * will always be invoked to push a dirty page into a copy object assuming
1039 * a pager has been created. If the page has already been pushed, the
1040 * pager will ingore the new request.
1042 #define LOOK_FOR(o, f) TRUE
1043 #define PAGED_OUT(o, f) FALSE
1044 #endif /* MACH_PAGEMAP */
1051 vm_object_offset_t
*start
,
1052 vm_object_offset_t
*end
,
1053 vm_size_t max_length
)
1055 tws_hash_line_t line
;
1056 vm_object_offset_t before
= *start
;
1057 vm_object_offset_t after
= *end
;
1058 vm_object_offset_t original_start
= *start
;
1059 vm_object_offset_t original_end
= *end
;
1060 vm_size_t length
= (vm_size_t
)(*end
- *start
);
1063 vm_object_offset_t object_size
;
1065 vm_size_t pre_heat_size
;
1066 unsigned int ele_cache
;
1067 unsigned int end_cache
= 0;
1068 unsigned int start_cache
= 0;
1069 unsigned int memory_scarce
= 0;
1071 if((object
->private) || !(object
->pager
))
1074 if (!object
->internal
) {
1075 kret
= vnode_pager_get_object_size(
1079 object_size
= object
->size
;
1082 if((!tws
) || (!tws_lock_try(tws
))) {
1085 age_of_cache
= ((sched_tick
1086 - tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
1088 if (vm_page_free_count
< (2 * vm_page_free_target
))
1091 /* When pre-heat files are not available, resort to speculation */
1092 /* based on size of file */
1094 if (tws
->startup_cache
|| object
->internal
|| age_of_cache
> 45) {
1097 if (object_size
> (vm_object_offset_t
)(1024 * 1024))
1098 pre_heat_size
= 8 * PAGE_SIZE
;
1099 else if (object_size
> (vm_object_offset_t
)(128 * 1024))
1100 pre_heat_size
= 4 * PAGE_SIZE
;
1102 pre_heat_size
= 2 * PAGE_SIZE
;
1105 if (tws
->startup_cache
) {
1106 int target_page_count
;
1109 target_page_count
= 16;
1111 target_page_count
= 4;
1113 if (tws_test_for_community(tws
, object
, *start
, target_page_count
, &ele_cache
))
1115 start_cache
= ele_cache
;
1116 *start
= *start
& TWS_HASH_OFF_MASK
;
1117 *end
= *start
+ (32 * PAGE_SIZE_64
);
1119 if (*end
> object_size
) {
1120 *end
= round_page_64(object_size
);
1123 end_cache
= ele_cache
;
1125 while (max_length
> ((*end
- *start
) + (32 * PAGE_SIZE
))) {
1131 if ((after
+ (32 * PAGE_SIZE_64
)) <= object_size
&&
1132 (tws_test_for_community(tws
, object
, after
, 8, &ele_cache
))) {
1134 *end
= after
+ (32 * PAGE_SIZE_64
);
1135 end_cache
= ele_cache
;
1138 if (max_length
< ((*end
- *start
) + (32 * PAGE_SIZE_64
))) {
1142 before
= (*start
- PAGE_SIZE_64
) & TWS_HASH_OFF_MASK
;
1144 if (tws_test_for_community(tws
, object
, before
, 8, &ele_cache
)) {
1147 start_cache
= ele_cache
;
1155 *end
-= PAGE_SIZE_64
;
1157 if (start_cache
!= 0) {
1160 for (mask
= 1; mask
!= 0; mask
= mask
<< 1) {
1161 if (*start
== original_start
)
1163 if (!(start_cache
& mask
))
1164 *start
+= PAGE_SIZE_64
;
1169 if (end_cache
!= 0) {
1172 for (mask
= 0x80000000;
1173 mask
!= 0; mask
= mask
>> 1) {
1174 if (*end
== original_end
)
1176 if (!(end_cache
& mask
))
1177 *end
-= PAGE_SIZE_64
;
1184 if (*end
< original_end
)
1185 *end
= original_end
;
1190 while ((length
< max_length
) &&
1192 (after
+ PAGE_SIZE_64
))) {
1193 if(length
>= pre_heat_size
) {
1194 if(tws_internal_lookup(tws
, after
, object
,
1195 &line
) != KERN_SUCCESS
) {
1196 vm_object_offset_t extend
;
1198 extend
= after
+ PAGE_SIZE_64
;
1199 if(tws_internal_lookup(tws
, extend
, object
,
1200 &line
) != KERN_SUCCESS
) {
1206 if ((object
->existence_map
!= NULL
)
1207 && (!LOOK_FOR(object
, after
))) {
1211 if (vm_page_lookup(object
, after
) != VM_PAGE_NULL
) {
1213 * don't bridge resident pages
1218 if (object
->internal
) {
1220 * need to acquire a real page in
1221 * advance because this acts as
1222 * a throttling mechanism for
1223 * data_requests to the default
1224 * pager. If this fails, give up
1225 * trying to find any more pages
1226 * in the cluster and send off the
1227 * request for what we already have.
1229 if ((m
= vm_page_grab()) == VM_PAGE_NULL
) {
1232 } else if ((m
= vm_page_grab_fictitious())
1238 m
->clustered
= TRUE
;
1239 m
->list_req_pending
= TRUE
;
1241 vm_page_insert(m
, object
, after
);
1242 object
->absent_count
++;
1243 after
+= PAGE_SIZE_64
;
1244 length
+= PAGE_SIZE
;
1247 while (length
< max_length
) {
1250 before
-= PAGE_SIZE_64
;
1252 if(length
>= pre_heat_size
) {
1253 if(tws_internal_lookup(tws
, before
, object
,
1254 &line
) != KERN_SUCCESS
) {
1255 vm_object_offset_t extend
;
1260 extend
-= PAGE_SIZE_64
;
1261 if(tws_internal_lookup(tws
, extend
, object
,
1262 &line
) != KERN_SUCCESS
) {
1267 if ((object
->existence_map
!= NULL
)
1268 && (!LOOK_FOR(object
, before
))) {
1272 if (vm_page_lookup(object
, before
) != VM_PAGE_NULL
) {
1274 * don't bridge resident pages
1279 if (object
->internal
) {
1281 * need to acquire a real page in
1282 * advance because this acts as
1283 * a throttling mechanism for
1284 * data_requests to the default
1285 * pager. If this fails, give up
1286 * trying to find any more pages
1287 * in the cluster and send off the
1288 * request for what we already have.
1290 if ((m
= vm_page_grab()) == VM_PAGE_NULL
) {
1293 } else if ((m
= vm_page_grab_fictitious())
1299 m
->clustered
= TRUE
;
1300 m
->list_req_pending
= TRUE
;
1302 vm_page_insert(m
, object
, before
);
1303 object
->absent_count
++;
1304 *start
-= PAGE_SIZE_64
;
1305 length
+= PAGE_SIZE
;
1314 tws_hash_line_t hash_line
,
1315 vm_offset_t target_page
)
1319 vm_object_offset_t offset
;
1320 vm_object_offset_t before
;
1321 vm_object_offset_t after
;
1322 struct tws_hash_ele
*element
;
1326 if(tws
->style
!= TWS_HASH_STYLE_SIGNAL
)
1330 for (i
=0; i
<tws
->number_of_elements
; i
++) {
1332 vm_object_offset_t local_off
= 0;
1334 if(hash_line
->list
[i
].object
== 0)
1337 element
= &hash_line
->list
[i
];
1339 if (element
->page_addr
== target_page
)
1344 if(j
& element
->page_cache
)
1347 local_off
+= PAGE_SIZE_64
;
1349 object
= element
->object
;
1350 offset
= element
->offset
+ local_off
;
1352 /* first try a fast test to speed up no-op signal */
1353 if (((p
= vm_page_lookup(object
, offset
)) != NULL
)
1354 || (object
->pager
== NULL
)
1355 || (object
->shadow_severed
)) {
1359 if((!object
->alive
) ||
1360 (!object
->pager_created
) || (!object
->pager_ready
))
1363 if (object
->internal
) {
1364 if (object
->existence_map
== NULL
) {
1368 if(!LOOK_FOR(object
, offset
))
1373 vm_object_reference(object
);
1376 if(object
->internal
) {
1379 m
= vm_page_grab_fictitious();
1383 vm_object_deallocate(object
);
1388 vm_object_lock(object
);
1389 if (((p
= vm_page_lookup(object
, offset
)) != NULL
)
1390 || (object
->pager
== NULL
)
1391 || (object
->shadow_severed
)) {
1393 vm_object_unlock(object
);
1394 vm_object_deallocate(object
);
1399 vm_page_insert(m
, object
, offset
);
1401 if (object
->absent_count
> vm_object_absent_max
) {
1403 vm_object_unlock(object
);
1404 vm_object_deallocate(object
);
1408 m
->list_req_pending
= TRUE
;
1411 object
->absent_count
++;
1414 after
= offset
+ PAGE_SIZE_64
;
1415 tws_build_cluster(tws
, object
, &before
, &after
, 0x16000);
1416 vm_object_unlock(object
);
1418 rc
= memory_object_data_request(object
->pager
,
1419 before
+ object
->paging_offset
,
1420 (vm_size_t
)(after
- before
), VM_PROT_READ
);
1421 if (rc
!= KERN_SUCCESS
) {
1423 vm_object_lock(object
);
1424 while (offset
< after
) {
1425 m
= vm_page_lookup(object
, offset
);
1426 if(m
&& m
->absent
&& m
->busy
)
1428 offset
+= PAGE_SIZE
;
1430 vm_object_unlock(object
);
1431 vm_object_deallocate(object
);
1433 vm_object_deallocate(object
);
1441 /* tws locked on entry */
1444 tws_create_startup_list(
1448 tws_startup_t startup
;
1450 unsigned int total_elements
;
1451 unsigned int startup_size
;
1452 unsigned int sindex
;
1453 unsigned int hash_index
;
1454 tws_startup_ptr_t element
;
1456 total_elements
= tws
->expansion_count
*
1457 (tws
->number_of_lines
* tws
->number_of_elements
);
1459 startup_size
= sizeof(struct tws_startup
)
1460 + (total_elements
* sizeof(tws_startup_ptr_t
*))
1461 + (total_elements
* sizeof(struct tws_startup_ptr
))
1462 + (total_elements
* sizeof(struct tws_startup_ele
));
1463 startup
= (tws_startup_t
)(kalloc(startup_size
));
1468 bzero((char *) startup
, startup_size
);
1470 startup
->table
= (tws_startup_ptr_t
*)
1471 (((int)startup
) + (sizeof(struct tws_startup
)));
1472 startup
->ele
= (struct tws_startup_ptr
*)
1473 (((vm_offset_t
)startup
->table
) +
1474 (total_elements
* sizeof(tws_startup_ptr_t
)));
1476 startup
->array
= (struct tws_startup_ele
*)
1477 (((vm_offset_t
)startup
->ele
) +
1478 (total_elements
* sizeof(struct tws_startup_ptr
)));
1480 startup
->tws_hash_size
= startup_size
;
1481 startup
->ele_count
= 0; /* burn first hash ele, else we can't tell from zero */
1482 startup
->array_size
= total_elements
;
1483 startup
->hash_count
= 1;
1488 for(i
= 0; i
<tws
->number_of_lines
; i
++) {
1489 for(j
= 0; j
<tws
->number_of_elements
; j
++) {
1490 for(k
= 0; k
<tws
->expansion_count
; k
++) {
1491 tws_hash_ele_t entry
;
1492 unsigned int hash_retry
;
1495 entry
= &tws
->cache
[k
][i
].list
[j
];
1496 addr
= entry
->page_addr
;
1498 if(entry
->object
!= 0) {
1499 /* get a hash element */
1500 hash_index
= do_startup_hash(addr
,
1501 startup
->array_size
);
1503 if(startup
->hash_count
< total_elements
) {
1504 element
= &(startup
->ele
[startup
->hash_count
]);
1505 startup
->hash_count
+= 1;
1507 /* exit we're out of elements */
1510 /* place the hash element */
1511 element
->next
= startup
->table
[hash_index
];
1512 startup
->table
[hash_index
] = (tws_startup_ptr_t
)
1513 ((int)element
- (int)&startup
->ele
[0]);
1515 /* set entry OFFSET in hash element */
1516 element
->element
= (tws_startup_ele_t
)
1517 ((int)&startup
->array
[sindex
] -
1518 (int)&startup
->array
[0]);
1520 startup
->array
[sindex
].page_addr
= entry
->page_addr
;
1521 startup
->array
[sindex
].page_cache
= entry
->page_cache
;
1522 startup
->ele_count
++;
1535 * Returns an entire cache line. The line is deleted from the startup
1536 * cache on return. The caller can check startup->ele_count for an empty
1537 * list. Access synchronization is the responsibility of the caller.
1541 tws_startup_list_lookup(
1542 tws_startup_t startup
,
1545 unsigned int hash_index
;
1546 unsigned int page_cache_bits
;
1547 unsigned int startup_shift
;
1548 tws_startup_ele_t entry
;
1549 vm_offset_t next_addr
;
1550 tws_startup_ptr_t element
;
1551 tws_startup_ptr_t base_ele
;
1552 tws_startup_ptr_t
*previous_ptr
;
1554 page_cache_bits
= 0;
1556 hash_index
= do_startup_hash(addr
, startup
->array_size
);
1558 if(((unsigned int)&(startup
->table
[hash_index
])) >= ((unsigned int)startup
+ startup
->tws_hash_size
)) {
1559 return page_cache_bits
= 0;
1561 element
= (tws_startup_ptr_t
)((int)startup
->table
[hash_index
] +
1562 (int)&startup
->ele
[0]);
1564 previous_ptr
= &(startup
->table
[hash_index
]);
1565 while(element
> &startup
->ele
[0]) {
1566 if (((int)element
+ sizeof(struct tws_startup_ptr
))
1567 > ((int)startup
+ startup
->tws_hash_size
)) {
1568 return page_cache_bits
;
1570 entry
= (tws_startup_ele_t
)
1571 ((int)element
->element
1572 + (int)&startup
->array
[0]);
1573 if((((int)entry
+ sizeof(struct tws_startup_ele
))
1574 > ((int)startup
+ startup
->tws_hash_size
))
1575 || ((int)entry
< (int)startup
)) {
1576 return page_cache_bits
;
1578 if ((addr
>= entry
->page_addr
) &&
1579 (addr
<= (entry
->page_addr
+ 0x1F000))) {
1580 startup_shift
= (addr
- entry
->page_addr
)>>12;
1581 page_cache_bits
|= entry
->page_cache
>> startup_shift
;
1582 /* don't dump the pages, unless the addresses */
1583 /* line up perfectly. The cache may be used */
1584 /* by other mappings */
1585 entry
->page_cache
&= (1 << startup_shift
) - 1;
1586 if(addr
== entry
->page_addr
) {
1587 if(base_ele
== element
) {
1588 base_ele
= (tws_startup_ptr_t
)
1590 + (int)&startup
->ele
[0]);
1591 startup
->table
[hash_index
] = element
->next
;
1594 *previous_ptr
= element
->next
;
1595 element
= (tws_startup_ptr_t
)
1597 + (int)&startup
->ele
[0]);
1599 entry
->page_addr
= 0;
1600 startup
->ele_count
--;
1604 next_addr
= addr
+ 0x1F000;
1605 if ((next_addr
>= entry
->page_addr
) &&
1606 (next_addr
<= (entry
->page_addr
+ 0x1F000))) {
1607 startup_shift
= (next_addr
- entry
->page_addr
)>>12;
1608 page_cache_bits
|= entry
->page_cache
<< (0x1F - startup_shift
);
1609 entry
->page_cache
&= ~((1 << (startup_shift
+ 1)) - 1);
1610 if(entry
->page_cache
== 0) {
1611 if(base_ele
== element
) {
1612 base_ele
= (tws_startup_ptr_t
)
1614 + (int)&startup
->ele
[0]);
1615 startup
->table
[hash_index
] = element
->next
;
1618 *previous_ptr
= element
->next
;
1619 element
= (tws_startup_ptr_t
)
1621 + (int)&startup
->ele
[0]);
1623 entry
->page_addr
= 0;
1624 startup
->ele_count
--;
1628 previous_ptr
= &(element
->next
);
1629 element
= (tws_startup_ptr_t
)
1630 ((int) element
->next
+ (int) &startup
->ele
[0]);
1633 return page_cache_bits
;
1637 tws_send_startup_info(
1644 tws
= task
->dynamic_working_set
;
1647 return KERN_FAILURE
;
1649 return tws_internal_startup_send(tws
);
1654 tws_internal_startup_send(
1658 tws_startup_t scache
;
1661 return KERN_FAILURE
;
1664 /* used to signal write or release depending on state of tws */
1665 if(tws
->startup_cache
) {
1666 vm_offset_t startup_buf
;
1668 startup_buf
= (vm_offset_t
)tws
->startup_cache
;
1669 size
= tws
->startup_cache
->tws_hash_size
;
1670 tws
->startup_cache
= 0;
1672 kmem_free(kernel_map
, startup_buf
, size
);
1673 return KERN_SUCCESS
;
1675 if(tws
->startup_name
== NULL
) {
1677 return KERN_FAILURE
;
1679 scache
= tws_create_startup_list(tws
);
1681 return KERN_FAILURE
;
1682 bsd_write_page_cache_file(tws
->uid
, tws
->startup_name
,
1683 (caddr_t
) scache
, scache
->tws_hash_size
,
1684 tws
->mod
, tws
->fid
);
1685 kfree(scache
, scache
->tws_hash_size
);
1686 kfree(tws
->startup_name
, tws
->startup_name_length
);
1687 tws
->startup_name
= NULL
;
1689 return KERN_SUCCESS
;
1693 tws_handle_startup_file(
1698 boolean_t
*new_info
)
1701 tws_startup_t startup
;
1702 vm_offset_t cache_size
;
1703 kern_return_t error
;
1708 /* don't pre-heat kernel task */
1709 if(task
== kernel_task
)
1710 return KERN_SUCCESS
;
1711 error
= bsd_read_page_cache_file(uid
, &fid
,
1714 (vm_offset_t
*) &startup
,
1717 return KERN_FAILURE
;
1719 if(startup
== NULL
) {
1720 /* Entry for app does not exist, make */
1722 /* we will want our own copy of the shared */
1723 /* regions to pick up a true picture of all */
1724 /* the pages we will touch. */
1725 if((lsf_zone
->count
* lsf_zone
->elem_size
)
1726 > (lsf_zone
->max_size
>> 1)) {
1727 /* We don't want to run out of shared memory */
1728 /* map entries by starting too many private versions */
1729 /* of the shared library structures */
1730 return KERN_SUCCESS
;
1734 error
= tws_write_startup_file(task
,
1735 fid
, mod
, app_name
, uid
);
1740 error
= tws_read_startup_file(task
,
1741 (tws_startup_t
)startup
,
1744 kmem_free(kernel_map
,
1745 (vm_offset_t
)startup
, cache_size
);
1749 return KERN_SUCCESS
;
1753 tws_write_startup_file(
1761 unsigned int string_length
;
1763 string_length
= strlen(name
);
1767 tws
= task
->dynamic_working_set
;
1771 kern_return_t error
;
1773 /* create a dynamic working set of normal size */
1774 if((error
= task_working_set_create(task
, 0, 0, TWS_HASH_STYLE_DEFAULT
)) != KERN_SUCCESS
)
1776 /* we need to reset tws and relock */
1781 if(tws
->startup_name
!= NULL
) {
1783 return KERN_FAILURE
;
1786 tws
->startup_name
= (char *)
1787 kalloc((string_length
+ 1) * (sizeof(char)));
1788 if(tws
->startup_name
== NULL
) {
1790 return KERN_FAILURE
;
1793 bcopy(name
, (char *)tws
->startup_name
, string_length
+ 1);
1794 tws
->startup_name_length
= (string_length
+ 1) * sizeof(char);
1800 return KERN_SUCCESS
;
1803 unsigned long tws_read_startup_file_rejects
= 0;
1806 tws_read_startup_file(
1808 tws_startup_t startup
,
1809 vm_offset_t cache_size
)
1814 unsigned int ele_count
;
1818 tws
= task
->dynamic_working_set
;
1820 /* create a dynamic working set to match file size */
1822 /* start with total size of the data we got from app_profile */
1823 ele_count
= cache_size
;
1824 /* skip the startup header */
1825 ele_count
-= sizeof(struct tws_startup
);
1827 * For each startup cache entry, we have one of these:
1828 * tws_startup_ptr_t startup->table[];
1829 * struct tws_startup_ptr startup->ele[];
1830 * struct tws_startup_ele startup->array[];
1832 ele_count
/= (sizeof (tws_startup_ptr_t
) +
1833 sizeof (struct tws_startup_ptr
) +
1834 sizeof (struct tws_startup_ele
));
1837 * Sanity check: make sure the value for startup->array_size
1838 * that we read from the app_profile file matches the size
1839 * of the data we read from disk. If it doesn't match, we
1840 * can't trust the data and we just drop it all.
1842 if (cache_size
< sizeof(struct tws_startup
) ||
1843 startup
->array_size
!= ele_count
) {
1844 tws_read_startup_file_rejects
++;
1846 kmem_free(kernel_map
, (vm_offset_t
)startup
, cache_size
);
1847 return(KERN_SUCCESS
);
1851 * We'll create the task working set with the default row size
1852 * (TWS_ARRAY_SIZE), so this will give us the number of lines
1853 * we need to store all the data from the app_profile startup
1856 lines
= ele_count
/ TWS_ARRAY_SIZE
;
1858 if(lines
<= TWS_SMALL_HASH_LINE_COUNT
) {
1859 lines
= TWS_SMALL_HASH_LINE_COUNT
;
1861 kmem_free(kernel_map
, (vm_offset_t
)startup
, cache_size
);
1862 return(KERN_SUCCESS
);
1864 old_exp_count
= lines
/TWS_HASH_LINE_COUNT
;
1865 if((old_exp_count
* TWS_HASH_LINE_COUNT
) != lines
) {
1866 lines
= (old_exp_count
+ 1)
1867 * TWS_HASH_LINE_COUNT
;
1870 kern_return_t error
;
1873 if ((error
= task_working_set_create(task
, lines
, 0, TWS_HASH_STYLE_DEFAULT
)) != KERN_SUCCESS
)
1875 /* we need to reset tws and relock */
1879 tws_expand_working_set(
1880 (void *)tws
, lines
, TRUE
);
1886 if(tws
->startup_cache
!= NULL
) {
1888 return KERN_FAILURE
;
1892 /* now need to fix up internal table pointers */
1893 startup
->table
= (tws_startup_ptr_t
*)
1894 (((int)startup
) + (sizeof(struct tws_startup
)));
1895 startup
->ele
= (struct tws_startup_ptr
*)
1896 (((vm_offset_t
)startup
->table
) +
1897 (startup
->array_size
* sizeof(tws_startup_ptr_t
)));
1898 startup
->array
= (struct tws_startup_ele
*)
1899 (((vm_offset_t
)startup
->ele
) +
1900 (startup
->array_size
* sizeof(struct tws_startup_ptr
)));
1901 /* the allocation size and file size should be the same */
1902 /* just in case their not, make sure we dealloc correctly */
1903 startup
->tws_hash_size
= cache_size
;
1905 tws
->startup_cache
= startup
;
1907 return KERN_SUCCESS
;
1912 tws_hash_ws_flush(tws_hash_t tws
) {
1913 tws_startup_t scache
;
1918 if(tws
->startup_name
!= NULL
) {
1919 scache
= tws_create_startup_list(tws
);
1920 if(scache
== NULL
) {
1921 /* dump the name cache, we'll */
1922 /* get it next time */
1923 kfree(tws
->startup_name
, tws
->startup_name_length
);
1924 tws
->startup_name
= NULL
;
1928 bsd_write_page_cache_file(tws
->uid
, tws
->startup_name
,
1929 (caddr_t
) scache
, scache
->tws_hash_size
,
1930 tws
->mod
, tws
->fid
);
1931 kfree(scache
, scache
->tws_hash_size
);
1932 kfree(tws
->startup_name
, tws
->startup_name_length
);
1933 tws
->startup_name
= NULL
;
1940 tws_hash_destroy(tws_hash_t tws
)
1944 if(tws
->startup_cache
!= NULL
) {
1945 kmem_free(kernel_map
,
1946 (vm_offset_t
)tws
->startup_cache
,
1947 tws
->startup_cache
->tws_hash_size
);
1948 tws
->startup_cache
= NULL
;
1950 if(tws
->startup_name
!= NULL
) {
1951 tws_internal_startup_send(tws
);
1953 for (i
=0; i
<tws
->number_of_lines
; i
++) {
1954 for(k
=0; k
<tws
->expansion_count
; k
++) {
1955 /* clear the object refs */
1956 tws_hash_line_clear(tws
, &(tws
->cache
[k
][i
]), NULL
, FALSE
);
1960 while (i
< tws
->expansion_count
) {
1962 kfree(tws
->table
[i
],
1963 sizeof(tws_hash_ptr_t
)
1964 * tws
->number_of_lines
1965 * tws
->number_of_elements
);
1966 kfree(tws
->table_ele
[i
],
1967 sizeof(struct tws_hash_ptr
)
1968 * tws
->number_of_lines
1969 * tws
->number_of_elements
);
1970 kfree(tws
->alt_ele
[i
],
1971 sizeof(struct tws_hash_ptr
)
1972 * tws
->number_of_lines
1973 * tws
->number_of_elements
);
1974 kfree(tws
->cache
[i
],
1975 sizeof(struct tws_hash_line
) * tws
->number_of_lines
);
1978 if(tws
->startup_name
!= NULL
) {
1979 kfree(tws
->startup_name
, tws
->startup_name_length
);
1981 kfree(tws
, sizeof(struct tws_hash
));
1985 tws_hash_clear(tws_hash_t tws
)
1989 for (i
=0; i
<tws
->number_of_lines
; i
++) {
1990 for(k
=0; k
<tws
->expansion_count
; k
++) {
1991 /* clear the object refs */
1992 tws_hash_line_clear(tws
, &(tws
->cache
[k
][i
]), NULL
, FALSE
);
1998 task_working_set_create(
2006 lines
= TWS_HASH_LINE_COUNT
;
2009 rows
= TWS_ARRAY_SIZE
;
2011 if (style
== TWS_HASH_STYLE_DEFAULT
) {
2012 style
= TWS_HASH_STYLE_BASIC
;
2015 if(task
->dynamic_working_set
!= 0) {
2017 return(KERN_FAILURE
);
2018 } else if((task
->dynamic_working_set
=
2019 tws_hash_create(lines
, rows
, style
)) == 0) {
2021 return(KERN_NO_SPACE
);
2024 return KERN_SUCCESS
;
2028 /* Internal use only routines */
2032 * internal sub-function for address space lookup
2033 * returns the target element and the address of the
2034 * previous pointer The previous pointer is the address
2035 * of the pointer pointing to the target element.
2036 * TWS must be locked
2040 tws_traverse_address_hash_list (
2043 vm_offset_t page_addr
,
2045 vm_object_offset_t offset
,
2047 tws_hash_ptr_t
*target_ele
,
2048 tws_hash_ptr_t
**previous_ptr
,
2049 tws_hash_ptr_t
**free_list
,
2050 unsigned int exclusive_addr
)
2053 tws_hash_ptr_t cache_ele
;
2054 tws_hash_ptr_t base_ele
;
2057 *previous_ptr
= NULL
;
2059 for(k
=0; k
<tws
->expansion_count
; k
++) {
2061 cache_ele
= tws
->table
[k
][index
];
2062 base_ele
= cache_ele
;
2063 *previous_ptr
= (tws_hash_ptr_t
*)&(tws
->table
[k
][index
]);
2064 while(cache_ele
!= NULL
) {
2066 cache_ele
->element
& TWS_ADDR_HASH
) == 0) {
2067 *previous_ptr
= (tws_hash_ptr_t
*)&(cache_ele
->next
);
2068 cache_ele
= cache_ele
->next
;
2071 ele
= (tws_hash_ele_t
)((unsigned int)
2072 cache_ele
->element
& ~TWS_ADDR_HASH
);
2073 if ((ele
== 0) || (ele
->object
== 0)) {
2074 /* A little clean-up of empty elements */
2075 cache_ele
->element
= 0;
2076 if(base_ele
== cache_ele
) {
2077 base_ele
= cache_ele
->next
;
2078 tws
->table
[k
][index
] = cache_ele
->next
;
2079 cache_ele
->next
= tws
->free_hash_ele
[k
];
2080 tws
->free_hash_ele
[k
] = cache_ele
;
2081 cache_ele
= base_ele
;
2083 **previous_ptr
= cache_ele
->next
;
2084 cache_ele
->next
= tws
->free_hash_ele
[k
];
2085 tws
->free_hash_ele
[k
] = cache_ele
;
2086 cache_ele
= **previous_ptr
;
2091 if ((ele
->page_addr
<= page_addr
)
2092 && (page_addr
<= (ele
->page_addr
+
2093 (vm_offset_t
)TWS_INDEX_MASK
))
2094 && ((object
== NULL
)
2095 || ((object
== ele
->object
)
2096 && (offset
== ele
->offset
)
2097 && (map
== ele
->map
)))) {
2098 if(exclusive_addr
) {
2100 delta
= ((page_addr
- ele
->page_addr
)
2102 if((1 << delta
) & ele
->page_cache
) {
2103 /* We've found a match */
2104 *target_ele
= cache_ele
;
2107 &(tws
->free_hash_ele
[k
]);
2111 /* We've found a match */
2112 *target_ele
= cache_ele
;
2113 *free_list
= (tws_hash_ptr_t
*)
2114 &(tws
->free_hash_ele
[k
]);
2118 *previous_ptr
= (tws_hash_ptr_t
*)&(cache_ele
->next
);
2119 cache_ele
= cache_ele
->next
;
2126 * internal sub-function for object space lookup
2127 * returns the target element and the address of the
2128 * previous pointer The previous pointer is the address
2129 * of the pointer pointing to the target element.
2130 * TWS must be locked
2135 tws_traverse_object_hash_list (
2139 vm_object_offset_t offset
,
2140 unsigned int pagemask
,
2141 tws_hash_ptr_t
*target_ele
,
2142 tws_hash_ptr_t
**previous_ptr
,
2143 tws_hash_ptr_t
**free_list
)
2146 tws_hash_ptr_t cache_ele
;
2147 tws_hash_ptr_t base_ele
;
2150 *previous_ptr
= NULL
;
2152 for(k
=0; k
<tws
->expansion_count
; k
++) {
2153 cache_ele
= tws
->table
[k
][index
];
2154 base_ele
= cache_ele
;
2155 *previous_ptr
= &(tws
->table
[k
][index
]);
2156 while(cache_ele
!= NULL
) {
2157 if((((unsigned int)cache_ele
->element
)
2158 & TWS_ADDR_HASH
) != 0) {
2159 *previous_ptr
= &(cache_ele
->next
);
2160 cache_ele
= cache_ele
->next
;
2163 if ((cache_ele
->element
== 0) ||
2164 (cache_ele
->element
->object
== 0)) {
2165 /* A little clean-up of empty elements */
2166 cache_ele
->element
= 0;
2167 if(base_ele
== cache_ele
) {
2168 base_ele
= cache_ele
->next
;
2169 tws
->table
[k
][index
] = cache_ele
->next
;
2170 cache_ele
->next
= tws
->free_hash_ele
[k
];
2171 tws
->free_hash_ele
[k
] = cache_ele
;
2172 cache_ele
= tws
->table
[k
][index
];
2174 **previous_ptr
= cache_ele
->next
;
2175 cache_ele
->next
= tws
->free_hash_ele
[k
];
2176 tws
->free_hash_ele
[k
] = cache_ele
;
2177 cache_ele
= **previous_ptr
;
2181 if ((cache_ele
->element
->object
== object
)
2182 && (cache_ele
->element
->offset
==
2183 (offset
- (offset
& ~TWS_HASH_OFF_MASK
)))) {
2184 if((cache_ele
->element
->page_cache
& pagemask
)
2185 || (pagemask
== 0xFFFFFFFF)) {
2186 /* We've found a match */
2187 *target_ele
= cache_ele
;
2188 *free_list
= &(tws
->free_hash_ele
[k
]);
2192 *previous_ptr
= (tws_hash_ptr_t
*)&(cache_ele
->next
);
2193 cache_ele
= cache_ele
->next
;
2200 * For a given object/offset, discover whether the indexed 32 page frame
2201 * containing the object/offset exists and if their are at least threshold
2202 * pages present. Returns true if population meets threshold.
2205 tws_test_for_community(
2208 vm_object_offset_t offset
,
2209 unsigned int threshold
,
2210 unsigned int *pagemask
)
2213 tws_hash_ptr_t cache_ele
;
2214 tws_hash_ptr_t
*trailer
;
2215 tws_hash_ptr_t
*free_list
;
2218 index
= do_tws_hash(object
, offset
,
2219 tws
->number_of_elements
, tws
->number_of_lines
);
2220 tws_traverse_object_hash_list(tws
, index
, object
, offset
, 0xFFFFFFFF,
2221 &cache_ele
, &trailer
, &free_list
);
2223 if(cache_ele
!= NULL
) {
2227 for(i
=1; i
!=0; i
=i
<<1) {
2228 if(i
& cache_ele
->element
->page_cache
)
2230 if(ctr
== threshold
) {
2232 *pagemask
= cache_ele
->element
->page_cache
;
2244 * Gets new hash element for object hash from free pools
2245 * TWS must be locked
2254 tws_hash_ptr_t element
;
2256 if(tws
->obj_free_count
[set
] < tws
->number_of_lines
* tws
->number_of_elements
) {
2257 element
= &(tws
->table_ele
[set
][tws
->obj_free_count
[set
]]);
2258 tws
->obj_free_count
[set
]+=1;
2259 } else if(tws
->free_hash_ele
[set
] == NULL
) {
2262 element
= tws
->free_hash_ele
[set
];
2265 tws
->free_hash_ele
[set
] = tws
->free_hash_ele
[set
]->next
;
2267 element
->element
= 0;
2268 element
->next
= tws
->table
[set
][index
];
2269 tws
->table
[set
][index
] = element
;
2274 * Gets new hash element for addr hash from free pools
2275 * TWS must be locked
2284 tws_hash_ptr_t element
;
2286 if(tws
->addr_free_count
[set
]
2287 < tws
->number_of_lines
* tws
->number_of_elements
) {
2288 element
= &(tws
->alt_ele
[set
][tws
->addr_free_count
[set
]]);
2289 tws
->addr_free_count
[set
]+=1;
2290 } else if(tws
->free_hash_ele
[set
] == NULL
) {
2293 element
= tws
->free_hash_ele
[set
];
2296 tws
->free_hash_ele
[set
] = tws
->free_hash_ele
[set
]->next
;
2298 element
->element
= (tws_hash_ele_t
)TWS_ADDR_HASH
;
2299 element
->next
= tws
->table
[set
][index
];
2300 tws
->table
[set
][index
] = element
;