2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 * File: vm/task_working_set.c
26 * Author: Chris Youngworth
29 * Working set detection and maintainence module
33 #include <mach/mach_types.h>
34 #include <mach/memory_object.h>
35 #include <mach/shared_memory_server.h>
36 #include <vm/task_working_set.h>
37 #include <vm/vm_kern.h>
38 #include <vm/vm_map.h>
39 #include <vm/vm_page.h>
40 #include <vm/vm_pageout.h>
41 #include <kern/sched.h>
42 #include <kern/kalloc.h>
44 #include <vm/vm_protos.h>
47 * LP64todo - Task Working Set Support is for 32-bit only
49 extern zone_t lsf_zone
;
51 /* declarations for internal use only routines */
61 tws_write_startup_file(
66 unsigned int string_length
);
69 tws_read_startup_file(
71 tws_startup_t startup
,
72 vm_offset_t cache_size
);
75 tws_create_startup_list(
79 tws_startup_list_lookup(
80 tws_startup_t startup
,
84 tws_internal_startup_send(
90 tws_hash_line_t hash_line
,
99 tws_traverse_address_hash_list (
102 vm_offset_t page_addr
,
104 vm_object_offset_t offset
,
106 tws_hash_ptr_t
*target_ele
,
107 tws_hash_ptr_t
**previous_ptr
,
108 tws_hash_ptr_t
**free_list
,
109 unsigned int exclusive_addr
);
112 tws_traverse_object_hash_list (
116 vm_object_offset_t offset
,
117 unsigned int pagemask
,
118 tws_hash_ptr_t
*target_ele
,
119 tws_hash_ptr_t
**previous_ptr
,
120 tws_hash_ptr_t
**free_list
);
134 int tws_test_for_community(
137 vm_object_offset_t offset
,
138 unsigned int threshold
,
139 unsigned int *pagemask
);
144 vm_object_offset_t offset
,
146 tws_hash_line_t
*line
);
148 /* Note: all of the routines below depend on the associated map lock for */
149 /* synchronization, the map lock will be on when the routines are called */
150 /* and on when they return */
160 if ((style
!= TWS_HASH_STYLE_BASIC
) &&
161 (style
!= TWS_HASH_STYLE_BASIC
)) {
162 return((tws_hash_t
)NULL
);
166 tws
= (tws_hash_t
)(kalloc(sizeof(struct tws_hash
)));
167 if(tws
== (tws_hash_t
)NULL
)
170 if((tws
->table
[0] = (tws_hash_ptr_t
*)
171 kalloc(sizeof(tws_hash_ptr_t
) * lines
* rows
))
173 kfree(tws
, sizeof(struct tws_hash
));
174 return (tws_hash_t
)NULL
;
176 if((tws
->table_ele
[0] = (tws_hash_ptr_t
)
177 kalloc(sizeof(struct tws_hash_ptr
) * lines
* rows
))
179 kfree(tws
->table
[0], sizeof(tws_hash_ptr_t
) * lines
* rows
);
180 kfree(tws
, sizeof(struct tws_hash
));
181 return (tws_hash_t
)NULL
;
183 if((tws
->alt_ele
[0] = (tws_hash_ptr_t
)
184 kalloc(sizeof(struct tws_hash_ptr
) * lines
* rows
))
186 kfree(tws
->table
[0], sizeof(tws_hash_ptr_t
) * lines
* rows
);
187 kfree(tws
->table_ele
[0],
188 sizeof(struct tws_hash_ptr
) * lines
* rows
);
189 kfree(tws
, sizeof(struct tws_hash
));
190 return (tws_hash_t
)NULL
;
192 if((tws
->cache
[0] = (struct tws_hash_line
*)
193 kalloc(sizeof(struct tws_hash_line
) * lines
))
195 kfree(tws
->table
[0], sizeof(tws_hash_ptr_t
) * lines
* rows
);
196 kfree(tws
->table_ele
[0],
197 sizeof(struct tws_hash_ptr
) * lines
* rows
);
198 kfree(tws
->alt_ele
[0],
199 sizeof(struct tws_hash_ptr
) * lines
* rows
);
200 kfree(tws
, sizeof(struct tws_hash
));
201 return (tws_hash_t
)NULL
;
203 tws
->free_hash_ele
[0] = (tws_hash_ptr_t
)0;
204 tws
->obj_free_count
[0] = 0;
205 tws
->addr_free_count
[0] = 0;
207 /* most defaults are such that a bzero will initialize */
208 bzero((char *)tws
->table
[0],sizeof(tws_hash_ptr_t
)
210 bzero((char *)tws
->table_ele
[0],sizeof(struct tws_hash_ptr
)
212 bzero((char *)tws
->alt_ele
[0],sizeof(struct tws_hash_ptr
)
214 bzero((char *)tws
->cache
[0], sizeof(struct tws_hash_line
)
217 mutex_init(&tws
->lock
, 0);
219 tws
->current_line
= 0;
220 tws
->pageout_count
= 0;
222 tws
->startup_cache
= NULL
;
223 tws
->startup_name
= NULL
;
224 tws
->number_of_lines
= lines
;
225 tws
->number_of_elements
= rows
;
226 tws
->expansion_count
= 1;
227 tws
->lookup_count
= 0;
228 tws
->insert_count
= 0;
229 tws
->time_of_creation
= sched_tick
;
236 vm_page_lookup_nohint(vm_object_t object
, vm_object_offset_t offset
);
242 tws_hash_line_t hash_line
,
243 __unused vm_object_t object
,
246 struct tws_hash_ele
*hash_ele
;
247 struct tws_hash_ptr
**trailer
;
248 struct tws_hash_ptr
**free_list
;
249 tws_hash_ele_t addr_ele
;
256 if(tws
->line_count
< tws
->number_of_lines
) {
260 if(tws
->pageout_count
!= vm_pageout_scan_event_counter
) {
262 vm_pageout_scan_event_counter
;
269 hash_line
->ele_count
= 0;
271 for (i
=0; i
<tws
->number_of_elements
; i
++) {
273 hash_ele
= &(hash_line
->list
[i
]);
274 if(hash_ele
->object
!= 0) {
276 vm_object_offset_t local_off
= 0;
277 tws_hash_ptr_t cache_ele
;
279 index
= alt_tws_hash(
280 hash_ele
->page_addr
& TWS_ADDR_OFF_MASK
,
281 tws
->number_of_elements
,
282 tws
->number_of_lines
);
284 tws_traverse_address_hash_list(tws
, index
,
285 hash_ele
->page_addr
, hash_ele
->object
,
286 hash_ele
->offset
, hash_ele
->map
,
287 &cache_ele
, &trailer
, &free_list
, 0);
288 if(cache_ele
!= NULL
) {
289 addr_ele
= (tws_hash_ele_t
)((unsigned int)
290 (cache_ele
->element
) & ~TWS_ADDR_HASH
);
291 if(addr_ele
!= hash_ele
)
292 panic("tws_hash_line_clear:"
294 cache_ele
->element
= 0;
295 *trailer
= cache_ele
->next
;
296 cache_ele
->next
= *free_list
;
297 *free_list
= cache_ele
;
300 index
= alt_tws_hash(
301 (hash_ele
->page_addr
- 0x1f000)
303 tws
->number_of_elements
,
304 tws
->number_of_lines
);
306 tws_traverse_address_hash_list(tws
, index
,
307 hash_ele
->page_addr
, hash_ele
->object
,
308 hash_ele
->offset
, hash_ele
->map
,
309 &cache_ele
, &trailer
, &free_list
, 0);
311 if(cache_ele
!= NULL
) {
312 addr_ele
= (tws_hash_ele_t
)((unsigned int)
313 (cache_ele
->element
) & ~TWS_ADDR_HASH
);
314 if(addr_ele
!= hash_ele
)
315 panic("tws_hash_line_clear: "
317 cache_ele
->element
= 0;
318 *trailer
= cache_ele
->next
;
319 cache_ele
->next
= *free_list
;
320 *free_list
= cache_ele
;
324 if((hash_ele
->map
!= NULL
) && (live
)) {
328 if (object
!= hash_ele
->object
) {
330 vm_object_unlock(object
);
331 vm_object_lock(hash_ele
->object
);
334 if (dump_pmap
== 1) {
335 for (j
= 0x1; j
!= 0; j
= j
<<1) {
336 if(j
& hash_ele
->page_cache
) {
337 p
= vm_page_lookup_nohint(hash_ele
->object
,
338 hash_ele
->offset
+ local_off
);
339 if((p
!= NULL
) && (p
->wire_count
== 0)) {
340 pmap_remove_some_phys((pmap_t
)vm_map_pmap(current_map()),
344 local_off
+= PAGE_SIZE_64
;
348 if (object
!= hash_ele
->object
) {
349 vm_object_unlock(hash_ele
->object
);
351 vm_object_lock(object
);
356 if(tws
->style
== TWS_HASH_STYLE_SIGNAL
) {
357 vm_object_deallocate(hash_ele
->object
);
358 vm_map_deallocate(hash_ele
->map
);
361 index
= do_tws_hash(hash_ele
->object
, hash_ele
->offset
,
362 tws
->number_of_elements
,
363 tws
->number_of_lines
);
365 tws_traverse_object_hash_list(tws
,
366 index
, hash_ele
->object
, hash_ele
->offset
,
367 0xFFFFFFFF, &cache_ele
, &trailer
, &free_list
);
368 if((cache_ele
!= NULL
) && (cache_ele
->element
== hash_ele
)) {
369 cache_ele
->element
= 0;
370 *trailer
= cache_ele
->next
;
371 cache_ele
->next
= *free_list
;
372 *free_list
= cache_ele
;
374 hash_ele
->object
= 0;
382 vm_object_offset_t offset
,
384 tws_hash_line_t
*line
)
391 tws_hash_ptr_t cache_ele
;
392 tws_hash_ptr_t
*trailer
;
393 tws_hash_ptr_t
*free_list
;
395 /* don't cache private objects */
399 index
= do_tws_hash(object
, offset
,
400 tws
->number_of_elements
, tws
->number_of_lines
);
404 if(tws
->lookup_count
== 0)
405 tws
->insert_count
= 0;
406 if(tws
->startup_name
!= NULL
) {
408 age_of_cache
= ((sched_tick
409 - tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
410 if (age_of_cache
> 45) {
411 return KERN_OPERATION_TIMED_OUT
;
415 if(tws
->lookup_count
> (4 * tws
->expansion_count
416 * tws
->number_of_elements
* tws
->number_of_lines
) &&
417 (tws
->lookup_count
> (2 * tws
->insert_count
))) {
418 if(tws
->startup_cache
) {
420 age_of_cache
= ((sched_tick
421 - tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
422 if (age_of_cache
> 45) {
423 return KERN_OPERATION_TIMED_OUT
;
428 pagenum
= (vm_offset_t
)(offset
& TWS_INDEX_MASK
);
429 pagenum
= pagenum
>> 12;
430 pagenum
= 1 << pagenum
; /* get the appropriate page in 32 page block */
431 tws_traverse_object_hash_list(tws
, index
, object
, offset
, pagenum
,
432 &cache_ele
, &trailer
, &free_list
);
433 if(cache_ele
!= NULL
) {
434 set
= cache_ele
->element
->line
/tws
->number_of_lines
;
435 ele_line
= cache_ele
->element
->line
- set
;
436 *line
= &tws
->cache
[set
][ele_line
];
448 vm_object_offset_t offset
,
450 tws_hash_line_t
*line
)
454 if(!tws_lock_try(tws
)) {
457 kr
= tws_internal_lookup(tws
,
458 offset
, object
, line
);
464 tws_expand_working_set(
466 unsigned int line_count
,
471 struct tws_hash temp
;
473 /* Note we do an elaborate dance to preserve the header that */
474 /* task is pointing to. In this way we can avoid taking a task */
475 /* lock every time we want to access the tws */
477 if (old_tws
->number_of_lines
>= line_count
) {
480 if((new_tws
= tws_hash_create(line_count
,
481 old_tws
->number_of_elements
, old_tws
->style
)) == 0) {
482 return(KERN_NO_SPACE
);
487 for(i
= 0; i
<old_tws
->number_of_lines
; i
++) {
488 for(j
= 0; j
<old_tws
->number_of_elements
; j
++) {
489 for(k
= 0; k
<old_tws
->expansion_count
; k
++) {
490 tws_hash_ele_t entry
;
491 vm_object_offset_t paddr
;
492 unsigned int page_index
;
493 entry
= &old_tws
->cache
[k
][i
].list
[j
];
494 if(entry
->object
!= 0) {
496 for(page_index
= 1; page_index
!= 0;
497 page_index
= page_index
<< 1) {
498 if (entry
->page_cache
& page_index
) {
502 entry
->page_addr
+paddr
,
514 temp
.style
= new_tws
->style
;
515 temp
.current_line
= new_tws
->current_line
;
516 temp
.pageout_count
= new_tws
->pageout_count
;
517 temp
.line_count
= new_tws
->line_count
;
518 temp
.number_of_lines
= new_tws
->number_of_lines
;
519 temp
.number_of_elements
= new_tws
->number_of_elements
;
520 temp
.expansion_count
= new_tws
->expansion_count
;
521 temp
.lookup_count
= new_tws
->lookup_count
;
522 temp
.insert_count
= new_tws
->insert_count
;
523 for(i
= 0; i
<new_tws
->expansion_count
; i
++) {
524 temp
.obj_free_count
[i
] = new_tws
->obj_free_count
[i
];
525 temp
.addr_free_count
[i
] = new_tws
->addr_free_count
[i
];
526 temp
.free_hash_ele
[i
] = new_tws
->free_hash_ele
[i
];
527 temp
.table
[i
] = new_tws
->table
[i
];
528 temp
.table_ele
[i
] = new_tws
->table_ele
[i
];
529 temp
.alt_ele
[i
] = new_tws
->alt_ele
[i
];
530 temp
.cache
[i
] = new_tws
->cache
[i
];
533 new_tws
->style
= old_tws
->style
;
534 new_tws
->current_line
= old_tws
->current_line
;
535 new_tws
->pageout_count
= old_tws
->pageout_count
;
536 new_tws
->line_count
= old_tws
->line_count
;
537 new_tws
->number_of_lines
= old_tws
->number_of_lines
;
538 new_tws
->number_of_elements
= old_tws
->number_of_elements
;
539 new_tws
->expansion_count
= old_tws
->expansion_count
;
540 new_tws
->lookup_count
= old_tws
->lookup_count
;
541 new_tws
->insert_count
= old_tws
->insert_count
;
542 for(i
= 0; i
<old_tws
->expansion_count
; i
++) {
543 new_tws
->obj_free_count
[i
] = old_tws
->obj_free_count
[i
];
544 new_tws
->addr_free_count
[i
] = old_tws
->addr_free_count
[i
];
545 new_tws
->free_hash_ele
[i
] = old_tws
->free_hash_ele
[i
];
546 new_tws
->table
[i
] = old_tws
->table
[i
];
547 new_tws
->table_ele
[i
] = old_tws
->table_ele
[i
];
548 new_tws
->alt_ele
[i
] = old_tws
->alt_ele
[i
];
549 new_tws
->cache
[i
] = old_tws
->cache
[i
];
552 old_tws
->style
= temp
.style
;
553 old_tws
->current_line
= temp
.current_line
;
554 old_tws
->pageout_count
= temp
.pageout_count
;
555 old_tws
->line_count
= temp
.line_count
;
556 old_tws
->number_of_lines
= temp
.number_of_lines
;
557 old_tws
->number_of_elements
= temp
.number_of_elements
;
558 old_tws
->expansion_count
= temp
.expansion_count
;
559 old_tws
->lookup_count
= temp
.lookup_count
;
560 old_tws
->insert_count
= temp
.insert_count
;
561 for(i
= 0; i
<temp
.expansion_count
; i
++) {
562 old_tws
->obj_free_count
[i
] = temp
.obj_free_count
[i
];;
563 old_tws
->addr_free_count
[i
] = temp
.addr_free_count
[i
];;
564 old_tws
->free_hash_ele
[i
] = NULL
;
565 old_tws
->table
[i
] = temp
.table
[i
];
566 old_tws
->table_ele
[i
] = temp
.table_ele
[i
];
567 old_tws
->alt_ele
[i
] = temp
.alt_ele
[i
];
568 old_tws
->cache
[i
] = temp
.cache
[i
];
571 tws_hash_destroy(new_tws
);
576 tws_hash_t test_tws
= 0;
581 vm_object_offset_t offset
,
583 vm_offset_t page_addr
,
587 unsigned int alt_index
;
588 unsigned int index_enum
[2];
589 unsigned int ele_index
;
590 tws_hash_ptr_t cache_ele
;
591 tws_hash_ptr_t obj_ele
= NULL
;
592 tws_hash_ptr_t addr_ele
= NULL
;
593 tws_hash_ptr_t
*trailer
;
594 tws_hash_ptr_t
*free_list
;
595 tws_hash_ele_t target_element
= NULL
;
597 unsigned int current_line
;
600 unsigned int startup_cache_line
;
601 int age_of_cache
= 0;
603 if(!tws_lock_try(tws
)) {
607 current_line
= 0xFFFFFFFF;
610 startup_cache_line
= 0;
612 if (tws
->startup_cache
) {
613 vm_offset_t startup_page_addr
;
615 startup_page_addr
= page_addr
- (offset
- (offset
& TWS_HASH_OFF_MASK
));
617 age_of_cache
= ((sched_tick
- tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
619 startup_cache_line
= tws_startup_list_lookup(tws
->startup_cache
, startup_page_addr
);
621 /* This next bit of code, the and alternate hash */
622 /* are all made necessary because of IPC COW */
624 /* Note: the use of page_addr modified by delta from offset */
625 /* frame base means we may miss some previous entries. However */
626 /* we will not miss the present entry. This is most important */
627 /* in avoiding duplication of entries against long lived non-cow */
629 index_enum
[0] = alt_tws_hash(
630 page_addr
& TWS_ADDR_OFF_MASK
,
631 tws
->number_of_elements
, tws
->number_of_lines
);
633 index_enum
[1] = alt_tws_hash(
634 (page_addr
- 0x1f000) & TWS_ADDR_OFF_MASK
,
635 tws
->number_of_elements
, tws
->number_of_lines
);
637 for(ctr
= 0; ctr
< 2;) {
638 tws_hash_ele_t resident
;
639 tws_traverse_address_hash_list(tws
,
640 index_enum
[ctr
], page_addr
, NULL
,
642 &cache_ele
, &trailer
, &free_list
, 1);
643 if(cache_ele
!= NULL
) {
645 resident
= (tws_hash_ele_t
)((unsigned int)
646 cache_ele
->element
& ~TWS_ADDR_HASH
);
647 if((object
== resident
->object
) &&
649 (offset
& TWS_HASH_OFF_MASK
)) {
650 /* This is our object/offset */
652 |= startup_cache_line
;
653 resident
->page_cache
|=
655 (offset
& TWS_INDEX_MASK
))>>12));
657 if (age_of_cache
> 45)
658 return KERN_OPERATION_TIMED_OUT
;
661 if((object
->shadow
==
664 + object
->shadow_offset
)
665 == (offset
& TWS_HASH_OFF_MASK
))) {
666 /* if we just shadowed, inherit */
667 /* access pattern from parent */
668 startup_cache_line
|=
669 resident
->page_cache
;
670 /* thow out old entry */
671 resident
->page_cache
= 0;
674 resident
->page_cache
&=
675 ~(1<<(((vm_offset_t
)(page_addr
676 - resident
->page_addr
))
679 /* Throw out old entry if there are no */
680 /* more pages in cache */
681 if(resident
->page_cache
== 0) {
682 /* delete addr hash entry */
683 cache_ele
->element
= 0;
684 *trailer
= cache_ele
->next
;
685 cache_ele
->next
= *free_list
;
686 *free_list
= cache_ele
;
687 /* go after object hash */
691 tws
->number_of_elements
,
692 tws
->number_of_lines
);
693 tws_traverse_object_hash_list(tws
,
694 index
, resident
->object
,
696 0xFFFFFFFF, &cache_ele
,
697 &trailer
, &free_list
);
698 if(cache_ele
!= NULL
) {
700 TWS_HASH_STYLE_SIGNAL
) {
701 vm_object_deallocate(
702 cache_ele
->element
->object
);
704 cache_ele
->element
->map
);
707 cache_ele
->element
->line
;
709 /tws
->number_of_lines
;
710 current_line
-= set
*
711 tws
->number_of_lines
;
712 if(cache_ele
->element
->object
!= 0) {
713 cache_ele
->element
->object
= 0;
715 [current_line
].ele_count
--;
717 cache_ele
->element
= 0;
718 *trailer
= cache_ele
->next
;
719 cache_ele
->next
= *free_list
;
720 *free_list
= cache_ele
;
729 * We may or may not have a current line setting coming out of
730 * the code above. If we have a current line it means we can
731 * choose to back-fill the spot vacated by a previous entry.
732 * We have yet to do a definitive check using the original obj/off
733 * We will do that now and override the current line if we
737 index
= do_tws_hash(object
, offset
,
738 tws
->number_of_elements
, tws
->number_of_lines
);
740 alt_index
= index_enum
[0];
742 tws_traverse_object_hash_list(tws
, index
, object
, offset
,
743 0xFFFFFFFF, &cache_ele
, &trailer
, &free_list
);
744 if(cache_ele
!= NULL
) {
746 current_line
= cache_ele
->element
->line
;
747 set
= current_line
/tws
->number_of_lines
;
748 current_line
-= set
* tws
->number_of_lines
;
749 target_element
= cache_ele
->element
;
751 /* Now check to see if we have a hash addr for it */
752 tws_traverse_address_hash_list(tws
,
753 alt_index
, obj_ele
->element
->page_addr
,
754 obj_ele
->element
->object
,
755 obj_ele
->element
->offset
,
756 obj_ele
->element
->map
,
757 &cache_ele
, &trailer
, &free_list
, 0);
758 if(cache_ele
!= NULL
) {
759 addr_ele
= cache_ele
;
761 addr_ele
= new_addr_hash(tws
, set
, alt_index
);
762 /* if cannot allocate just do without */
763 /* we'll get it next time around */
769 if(tws
->style
== TWS_HASH_STYLE_SIGNAL
) {
770 vm_object_reference(object
);
771 vm_map_reference(map
);
774 if(current_line
== 0xFFFFFFFF) {
775 current_line
= tws
->current_line
;
776 set
= current_line
/tws
->number_of_lines
;
777 current_line
= current_line
- (set
* tws
->number_of_lines
);
781 tws
->current_line
= tws
->number_of_lines
- 1;
784 if(tws
->cache
[set
][current_line
].ele_count
785 >= tws
->number_of_elements
) {
788 if(current_line
== tws
->number_of_lines
) {
791 if (set
== tws
->expansion_count
) {
792 if((tws
->lookup_count
<
793 (2 * tws
->insert_count
)) &&
794 (set
<TWS_HASH_EXPANSION_MAX
)) {
795 tws
->lookup_count
= 0;
796 tws
->insert_count
= 0;
797 if(tws
->number_of_lines
798 < TWS_HASH_LINE_COUNT
) {
801 return KERN_NO_SPACE
;
803 /* object persistence is guaranteed by */
804 /* an elevated paging or object */
805 /* reference count in the caller. */
806 vm_object_unlock(object
);
807 if((tws
->table
[set
] = (tws_hash_ptr_t
*)
808 kalloc(sizeof(tws_hash_ptr_t
)
809 * tws
->number_of_lines
810 * tws
->number_of_elements
))
813 } else if((tws
->table_ele
[set
] =
815 kalloc(sizeof(struct tws_hash_ptr
)
816 * tws
->number_of_lines
817 * tws
->number_of_elements
))
819 kfree(tws
->table
[set
],
820 sizeof(tws_hash_ptr_t
)
821 * tws
->number_of_lines
822 * tws
->number_of_elements
);
824 } else if((tws
->alt_ele
[set
] =
826 kalloc(sizeof(struct tws_hash_ptr
)
827 * tws
->number_of_lines
828 * tws
->number_of_elements
))
830 kfree(tws
->table_ele
[set
],
831 sizeof(struct tws_hash_ptr
)
832 * tws
->number_of_lines
833 * tws
->number_of_elements
);
834 kfree(tws
->table
[set
],
835 sizeof(tws_hash_ptr_t
)
836 * tws
->number_of_lines
837 * tws
->number_of_elements
);
838 tws
->table
[set
] = NULL
;
841 } else if((tws
->cache
[set
] =
842 (struct tws_hash_line
*)
844 (struct tws_hash_line
)
845 * tws
->number_of_lines
))
847 kfree(tws
->alt_ele
[set
],
848 sizeof(struct tws_hash_ptr
)
849 * tws
->number_of_lines
850 * tws
->number_of_elements
);
851 kfree(tws
->table_ele
[set
],
852 sizeof(struct tws_hash_ptr
)
853 * tws
->number_of_lines
854 * tws
->number_of_elements
);
855 kfree(tws
->table
[set
],
856 sizeof(tws_hash_ptr_t
)
857 * tws
->number_of_lines
858 * tws
->number_of_elements
);
859 tws
->table
[set
] = NULL
;
863 tws
->free_hash_ele
[set
] =
865 tws
->obj_free_count
[set
] = 0;
866 tws
->addr_free_count
[set
] = 0;
867 bzero((char *)tws
->table
[set
],
868 sizeof(tws_hash_ptr_t
)
869 * tws
->number_of_lines
870 * tws
->number_of_elements
);
871 bzero((char *)tws
->table_ele
[set
],
872 sizeof(struct tws_hash_ptr
)
873 * tws
->number_of_lines
874 * tws
->number_of_elements
);
875 bzero((char *)tws
->alt_ele
[set
],
876 sizeof(struct tws_hash_ptr
)
877 * tws
->number_of_lines
878 * tws
->number_of_elements
);
879 bzero((char *)tws
->cache
[set
],
880 sizeof(struct tws_hash_line
)
881 * tws
->number_of_lines
);
883 vm_object_lock(object
);
885 if (tws
->startup_name
!= NULL
) {
888 age_of_cache
= ((sched_tick
- tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
892 if (age_of_cache
> 45)
893 return KERN_OPERATION_TIMED_OUT
;
897 tws
->lookup_count
= 0;
898 tws
->insert_count
= 0;
902 tws
->current_line
= set
* tws
->number_of_lines
;
904 if(set
< tws
->expansion_count
) {
905 tws_hash_line_clear(tws
,
906 &(tws
->cache
[set
][current_line
]), object
, TRUE
);
907 if(tws
->cache
[set
][current_line
].ele_count
908 >= tws
->number_of_elements
) {
909 if(tws
->style
== TWS_HASH_STYLE_SIGNAL
) {
910 vm_object_deallocate(object
);
911 vm_map_deallocate(map
);
917 tws
->expansion_count
++;
923 /* set object hash element */
924 if(obj_ele
== NULL
) {
925 obj_ele
= new_obj_hash(tws
, set
, index
);
926 if(obj_ele
== NULL
) {
927 tws
->cache
[set
][current_line
].ele_count
928 = tws
->number_of_elements
;
934 /* set address hash element */
935 if(addr_ele
== NULL
) {
936 addr_ele
= new_addr_hash(tws
, set
, alt_index
);
939 if(target_element
== NULL
) {
941 for(i
= 0; i
<tws
->number_of_elements
; i
++) {
942 if(tws
->cache
[set
][current_line
].
943 list
[ele_index
].object
== 0) {
947 if(ele_index
>= tws
->number_of_elements
)
952 if(i
== tws
->number_of_elements
)
953 panic("tws_insert: no free elements");
956 &(tws
->cache
[set
][current_line
].list
[ele_index
]);
958 tws
->cache
[set
][current_line
].ele_count
++;
961 obj_ele
->element
= target_element
;
963 addr_ele
->element
= (tws_hash_ele_t
)
964 (((unsigned int)target_element
) | TWS_ADDR_HASH
);
966 target_element
->object
= object
;
967 target_element
->offset
= offset
& TWS_HASH_OFF_MASK
;
968 target_element
->page_addr
=
969 page_addr
- (offset
- (offset
& TWS_HASH_OFF_MASK
));
970 target_element
->map
= map
;
971 target_element
->line
=
972 current_line
+ (set
* tws
->number_of_lines
);
974 target_element
->page_cache
|= startup_cache_line
;
975 target_element
->page_cache
|= 1<<(((vm_offset_t
)(offset
& TWS_INDEX_MASK
))>>12);
979 if (age_of_cache
> 45)
980 return KERN_OPERATION_TIMED_OUT
;
987 * lengthen the cluster of pages by the number of pages encountered in the
988 * working set up to the limit requested by the caller. The object needs
989 * to be locked on entry. The map does not because the tws_lookup function
990 * is used only to find if their is an entry in the cache. No transient
991 * data from the cache is de-referenced.
996 * MACH page map - an optional optimization where a bit map is maintained
997 * by the VM subsystem for internal objects to indicate which pages of
998 * the object currently reside on backing store. This existence map
999 * duplicates information maintained by the vnode pager. It is
1000 * created at the time of the first pageout against the object, i.e.
1001 * at the same time pager for the object is created. The optimization
1002 * is designed to eliminate pager interaction overhead, if it is
1003 * 'known' that the page does not exist on backing store.
1005 * LOOK_FOR() evaluates to TRUE if the page specified by object/offset is
1006 * either marked as paged out in the existence map for the object or no
1007 * existence map exists for the object. LOOK_FOR() is one of the
1008 * criteria in the decision to invoke the pager. It is also used as one
1009 * of the criteria to terminate the scan for adjacent pages in a clustered
1010 * pagein operation. Note that LOOK_FOR() always evaluates to TRUE for
1011 * permanent objects. Note also that if the pager for an internal object
1012 * has not been created, the pager is not invoked regardless of the value
1013 * of LOOK_FOR() and that clustered pagein scans are only done on an object
1014 * for which a pager has been created.
1016 * PAGED_OUT() evaluates to TRUE if the page specified by the object/offset
1017 * is marked as paged out in the existence map for the object. PAGED_OUT()
1018 * PAGED_OUT() is used to determine if a page has already been pushed
1019 * into a copy object in order to avoid a redundant page out operation.
1021 #define LOOK_FOR(o, f) (vm_external_state_get((o)->existence_map, (f)) \
1022 != VM_EXTERNAL_STATE_ABSENT)
1023 #define PAGED_OUT(o, f) (vm_external_state_get((o)->existence_map, (f)) \
1024 == VM_EXTERNAL_STATE_EXISTS)
1025 #else /* MACH_PAGEMAP */
1027 * If the MACH page map optimization is not enabled,
1028 * LOOK_FOR() always evaluates to TRUE. The pager will always be
1029 * invoked to resolve missing pages in an object, assuming the pager
1030 * has been created for the object. In a clustered page operation, the
1031 * absence of a page on backing backing store cannot be used to terminate
1032 * a scan for adjacent pages since that information is available only in
1033 * the pager. Hence pages that may not be paged out are potentially
1034 * included in a clustered request. The vnode pager is coded to deal
1035 * with any combination of absent/present pages in a clustered
1036 * pagein request. PAGED_OUT() always evaluates to FALSE, i.e. the pager
1037 * will always be invoked to push a dirty page into a copy object assuming
1038 * a pager has been created. If the page has already been pushed, the
1039 * pager will ingore the new request.
1041 #define LOOK_FOR(o, f) TRUE
1042 #define PAGED_OUT(o, f) FALSE
1043 #endif /* MACH_PAGEMAP */
1050 vm_object_offset_t
*start
,
1051 vm_object_offset_t
*end
,
1052 vm_size_t max_length
)
1054 tws_hash_line_t line
;
1055 vm_object_offset_t before
= *start
;
1056 vm_object_offset_t after
= *end
;
1057 vm_object_offset_t original_start
= *start
;
1058 vm_object_offset_t original_end
= *end
;
1059 vm_size_t length
= (vm_size_t
)(*end
- *start
);
1062 vm_object_offset_t object_size
;
1064 vm_size_t pre_heat_size
;
1065 unsigned int ele_cache
;
1066 unsigned int end_cache
= 0;
1067 unsigned int start_cache
= 0;
1068 unsigned int memory_scarce
= 0;
1070 if((object
->private) || !(object
->pager
))
1073 if (!object
->internal
) {
1074 kret
= vnode_pager_get_object_size(
1078 object_size
= object
->size
;
1081 if((!tws
) || (!tws_lock_try(tws
))) {
1084 age_of_cache
= ((sched_tick
1085 - tws
->time_of_creation
) >> SCHED_TICK_SHIFT
);
1087 if (vm_page_free_count
< (2 * vm_page_free_target
))
1090 /* When pre-heat files are not available, resort to speculation */
1091 /* based on size of file */
1093 if (tws
->startup_cache
|| object
->internal
|| age_of_cache
> 45) {
1096 if (object_size
> (vm_object_offset_t
)(1024 * 1024))
1097 pre_heat_size
= 8 * PAGE_SIZE
;
1098 else if (object_size
> (vm_object_offset_t
)(128 * 1024))
1099 pre_heat_size
= 4 * PAGE_SIZE
;
1101 pre_heat_size
= 2 * PAGE_SIZE
;
1104 if (tws
->startup_cache
) {
1105 int target_page_count
;
1108 target_page_count
= 16;
1110 target_page_count
= 4;
1112 if (tws_test_for_community(tws
, object
, *start
, target_page_count
, &ele_cache
))
1114 start_cache
= ele_cache
;
1115 *start
= *start
& TWS_HASH_OFF_MASK
;
1116 *end
= *start
+ (32 * PAGE_SIZE_64
);
1118 if (*end
> object_size
) {
1119 *end
= round_page_64(object_size
);
1122 end_cache
= ele_cache
;
1124 while (max_length
> ((*end
- *start
) + (32 * PAGE_SIZE
))) {
1130 if ((after
+ (32 * PAGE_SIZE_64
)) <= object_size
&&
1131 (tws_test_for_community(tws
, object
, after
, 8, &ele_cache
))) {
1133 *end
= after
+ (32 * PAGE_SIZE_64
);
1134 end_cache
= ele_cache
;
1137 if (max_length
< ((*end
- *start
) + (32 * PAGE_SIZE_64
))) {
1141 before
= (*start
- PAGE_SIZE_64
) & TWS_HASH_OFF_MASK
;
1143 if (tws_test_for_community(tws
, object
, before
, 8, &ele_cache
)) {
1146 start_cache
= ele_cache
;
1154 *end
-= PAGE_SIZE_64
;
1156 if (start_cache
!= 0) {
1159 for (mask
= 1; mask
!= 0; mask
= mask
<< 1) {
1160 if (*start
== original_start
)
1162 if (!(start_cache
& mask
))
1163 *start
+= PAGE_SIZE_64
;
1168 if (end_cache
!= 0) {
1171 for (mask
= 0x80000000;
1172 mask
!= 0; mask
= mask
>> 1) {
1173 if (*end
== original_end
)
1175 if (!(end_cache
& mask
))
1176 *end
-= PAGE_SIZE_64
;
1183 if (*end
< original_end
)
1184 *end
= original_end
;
1189 while ((length
< max_length
) &&
1191 (after
+ PAGE_SIZE_64
))) {
1192 if(length
>= pre_heat_size
) {
1193 if(tws_internal_lookup(tws
, after
, object
,
1194 &line
) != KERN_SUCCESS
) {
1195 vm_object_offset_t extend
;
1197 extend
= after
+ PAGE_SIZE_64
;
1198 if(tws_internal_lookup(tws
, extend
, object
,
1199 &line
) != KERN_SUCCESS
) {
1205 if ((object
->existence_map
!= NULL
)
1206 && (!LOOK_FOR(object
, after
))) {
1210 if (vm_page_lookup(object
, after
) != VM_PAGE_NULL
) {
1212 * don't bridge resident pages
1217 if (object
->internal
) {
1219 * need to acquire a real page in
1220 * advance because this acts as
1221 * a throttling mechanism for
1222 * data_requests to the default
1223 * pager. If this fails, give up
1224 * trying to find any more pages
1225 * in the cluster and send off the
1226 * request for what we already have.
1228 if ((m
= vm_page_grab()) == VM_PAGE_NULL
) {
1231 } else if ((m
= vm_page_grab_fictitious())
1237 m
->clustered
= TRUE
;
1238 m
->list_req_pending
= TRUE
;
1240 vm_page_insert(m
, object
, after
);
1241 object
->absent_count
++;
1242 after
+= PAGE_SIZE_64
;
1243 length
+= PAGE_SIZE
;
1246 while (length
< max_length
) {
1249 before
-= PAGE_SIZE_64
;
1251 if(length
>= pre_heat_size
) {
1252 if(tws_internal_lookup(tws
, before
, object
,
1253 &line
) != KERN_SUCCESS
) {
1254 vm_object_offset_t extend
;
1259 extend
-= PAGE_SIZE_64
;
1260 if(tws_internal_lookup(tws
, extend
, object
,
1261 &line
) != KERN_SUCCESS
) {
1266 if ((object
->existence_map
!= NULL
)
1267 && (!LOOK_FOR(object
, before
))) {
1271 if (vm_page_lookup(object
, before
) != VM_PAGE_NULL
) {
1273 * don't bridge resident pages
1278 if (object
->internal
) {
1280 * need to acquire a real page in
1281 * advance because this acts as
1282 * a throttling mechanism for
1283 * data_requests to the default
1284 * pager. If this fails, give up
1285 * trying to find any more pages
1286 * in the cluster and send off the
1287 * request for what we already have.
1289 if ((m
= vm_page_grab()) == VM_PAGE_NULL
) {
1292 } else if ((m
= vm_page_grab_fictitious())
1298 m
->clustered
= TRUE
;
1299 m
->list_req_pending
= TRUE
;
1301 vm_page_insert(m
, object
, before
);
1302 object
->absent_count
++;
1303 *start
-= PAGE_SIZE_64
;
1304 length
+= PAGE_SIZE
;
1313 tws_hash_line_t hash_line
,
1314 vm_offset_t target_page
)
1318 vm_object_offset_t offset
;
1319 vm_object_offset_t before
;
1320 vm_object_offset_t after
;
1321 struct tws_hash_ele
*element
;
1325 if(tws
->style
!= TWS_HASH_STYLE_SIGNAL
)
1329 for (i
=0; i
<tws
->number_of_elements
; i
++) {
1331 vm_object_offset_t local_off
= 0;
1333 if(hash_line
->list
[i
].object
== 0)
1336 element
= &hash_line
->list
[i
];
1338 if (element
->page_addr
== target_page
)
1343 if(j
& element
->page_cache
)
1346 local_off
+= PAGE_SIZE_64
;
1348 object
= element
->object
;
1349 offset
= element
->offset
+ local_off
;
1351 /* first try a fast test to speed up no-op signal */
1352 if (((p
= vm_page_lookup(object
, offset
)) != NULL
)
1353 || (object
->pager
== NULL
)
1354 || (object
->shadow_severed
)) {
1358 if((!object
->alive
) ||
1359 (!object
->pager_created
) || (!object
->pager_ready
))
1362 if (object
->internal
) {
1363 if (object
->existence_map
== NULL
) {
1367 if(!LOOK_FOR(object
, offset
))
1372 vm_object_reference(object
);
1375 if(object
->internal
) {
1378 m
= vm_page_grab_fictitious();
1382 vm_object_deallocate(object
);
1387 vm_object_lock(object
);
1388 if (((p
= vm_page_lookup(object
, offset
)) != NULL
)
1389 || (object
->pager
== NULL
)
1390 || (object
->shadow_severed
)) {
1392 vm_object_unlock(object
);
1393 vm_object_deallocate(object
);
1398 vm_page_insert(m
, object
, offset
);
1400 if (object
->absent_count
> vm_object_absent_max
) {
1402 vm_object_unlock(object
);
1403 vm_object_deallocate(object
);
1407 m
->list_req_pending
= TRUE
;
1410 object
->absent_count
++;
1413 after
= offset
+ PAGE_SIZE_64
;
1414 tws_build_cluster(tws
, object
, &before
, &after
, 0x16000);
1415 vm_object_unlock(object
);
1417 rc
= memory_object_data_request(object
->pager
,
1418 before
+ object
->paging_offset
,
1419 (vm_size_t
)(after
- before
), VM_PROT_READ
);
1420 if (rc
!= KERN_SUCCESS
) {
1422 vm_object_lock(object
);
1423 while (offset
< after
) {
1424 m
= vm_page_lookup(object
, offset
);
1425 if(m
&& m
->absent
&& m
->busy
)
1427 offset
+= PAGE_SIZE
;
1429 vm_object_unlock(object
);
1430 vm_object_deallocate(object
);
1432 vm_object_deallocate(object
);
1440 /* tws locked on entry */
1443 tws_create_startup_list(
1447 tws_startup_t startup
;
1449 unsigned int total_elements
;
1450 unsigned int startup_size
;
1451 unsigned int sindex
;
1452 unsigned int hash_index
;
1453 tws_startup_ptr_t element
;
1455 total_elements
= tws
->expansion_count
*
1456 (tws
->number_of_lines
* tws
->number_of_elements
);
1458 startup_size
= sizeof(struct tws_startup
)
1459 + (total_elements
* sizeof(tws_startup_ptr_t
*))
1460 + (total_elements
* sizeof(struct tws_startup_ptr
))
1461 + (total_elements
* sizeof(struct tws_startup_ele
));
1462 startup
= (tws_startup_t
)(kalloc(startup_size
));
1467 bzero((char *) startup
, startup_size
);
1469 startup
->table
= (tws_startup_ptr_t
*)
1470 (((int)startup
) + (sizeof(struct tws_startup
)));
1471 startup
->ele
= (struct tws_startup_ptr
*)
1472 (((vm_offset_t
)startup
->table
) +
1473 (total_elements
* sizeof(tws_startup_ptr_t
)));
1475 startup
->array
= (struct tws_startup_ele
*)
1476 (((vm_offset_t
)startup
->ele
) +
1477 (total_elements
* sizeof(struct tws_startup_ptr
)));
1479 startup
->tws_hash_size
= startup_size
;
1480 startup
->ele_count
= 0; /* burn first hash ele, else we can't tell from zero */
1481 startup
->array_size
= total_elements
;
1482 startup
->hash_count
= 1;
1487 for(i
= 0; i
<tws
->number_of_lines
; i
++) {
1488 for(j
= 0; j
<tws
->number_of_elements
; j
++) {
1489 for(k
= 0; k
<tws
->expansion_count
; k
++) {
1490 tws_hash_ele_t entry
;
1491 unsigned int hash_retry
;
1494 entry
= &tws
->cache
[k
][i
].list
[j
];
1495 addr
= entry
->page_addr
;
1497 if(entry
->object
!= 0) {
1498 /* get a hash element */
1499 hash_index
= do_startup_hash(addr
,
1500 startup
->array_size
);
1502 if(startup
->hash_count
< total_elements
) {
1503 element
= &(startup
->ele
[startup
->hash_count
]);
1504 startup
->hash_count
+= 1;
1506 /* exit we're out of elements */
1509 /* place the hash element */
1510 element
->next
= startup
->table
[hash_index
];
1511 startup
->table
[hash_index
] = (tws_startup_ptr_t
)
1512 ((int)element
- (int)&startup
->ele
[0]);
1514 /* set entry OFFSET in hash element */
1515 element
->element
= (tws_startup_ele_t
)
1516 ((int)&startup
->array
[sindex
] -
1517 (int)&startup
->array
[0]);
1519 startup
->array
[sindex
].page_addr
= entry
->page_addr
;
1520 startup
->array
[sindex
].page_cache
= entry
->page_cache
;
1521 startup
->ele_count
++;
1534 * Returns an entire cache line. The line is deleted from the startup
1535 * cache on return. The caller can check startup->ele_count for an empty
1536 * list. Access synchronization is the responsibility of the caller.
1540 tws_startup_list_lookup(
1541 tws_startup_t startup
,
1544 unsigned int hash_index
;
1545 unsigned int page_cache_bits
;
1546 unsigned int startup_shift
;
1547 tws_startup_ele_t entry
;
1548 vm_offset_t next_addr
;
1549 tws_startup_ptr_t element
;
1550 tws_startup_ptr_t base_ele
;
1551 tws_startup_ptr_t
*previous_ptr
;
1553 page_cache_bits
= 0;
1555 hash_index
= do_startup_hash(addr
, startup
->array_size
);
1557 if(((unsigned int)&(startup
->table
[hash_index
])) >= ((unsigned int)startup
+ startup
->tws_hash_size
)) {
1558 return page_cache_bits
= 0;
1560 element
= (tws_startup_ptr_t
)((int)startup
->table
[hash_index
] +
1561 (int)&startup
->ele
[0]);
1563 previous_ptr
= &(startup
->table
[hash_index
]);
1564 while(element
> &startup
->ele
[0]) {
1565 if (((int)element
+ sizeof(struct tws_startup_ptr
))
1566 > ((int)startup
+ startup
->tws_hash_size
)) {
1567 return page_cache_bits
;
1569 entry
= (tws_startup_ele_t
)
1570 ((int)element
->element
1571 + (int)&startup
->array
[0]);
1572 if((((int)entry
+ sizeof(struct tws_startup_ele
))
1573 > ((int)startup
+ startup
->tws_hash_size
))
1574 || ((int)entry
< (int)startup
)) {
1575 return page_cache_bits
;
1577 if ((addr
>= entry
->page_addr
) &&
1578 (addr
<= (entry
->page_addr
+ 0x1F000))) {
1579 startup_shift
= (addr
- entry
->page_addr
)>>12;
1580 page_cache_bits
|= entry
->page_cache
>> startup_shift
;
1581 /* don't dump the pages, unless the addresses */
1582 /* line up perfectly. The cache may be used */
1583 /* by other mappings */
1584 entry
->page_cache
&= (1 << startup_shift
) - 1;
1585 if(addr
== entry
->page_addr
) {
1586 if(base_ele
== element
) {
1587 base_ele
= (tws_startup_ptr_t
)
1589 + (int)&startup
->ele
[0]);
1590 startup
->table
[hash_index
] = element
->next
;
1593 *previous_ptr
= element
->next
;
1594 element
= (tws_startup_ptr_t
)
1596 + (int)&startup
->ele
[0]);
1598 entry
->page_addr
= 0;
1599 startup
->ele_count
--;
1603 next_addr
= addr
+ 0x1F000;
1604 if ((next_addr
>= entry
->page_addr
) &&
1605 (next_addr
<= (entry
->page_addr
+ 0x1F000))) {
1606 startup_shift
= (next_addr
- entry
->page_addr
)>>12;
1607 page_cache_bits
|= entry
->page_cache
<< (0x1F - startup_shift
);
1608 entry
->page_cache
&= ~((1 << (startup_shift
+ 1)) - 1);
1609 if(entry
->page_cache
== 0) {
1610 if(base_ele
== element
) {
1611 base_ele
= (tws_startup_ptr_t
)
1613 + (int)&startup
->ele
[0]);
1614 startup
->table
[hash_index
] = element
->next
;
1617 *previous_ptr
= element
->next
;
1618 element
= (tws_startup_ptr_t
)
1620 + (int)&startup
->ele
[0]);
1622 entry
->page_addr
= 0;
1623 startup
->ele_count
--;
1627 previous_ptr
= &(element
->next
);
1628 element
= (tws_startup_ptr_t
)
1629 ((int) element
->next
+ (int) &startup
->ele
[0]);
1632 return page_cache_bits
;
1636 tws_send_startup_info(
1643 tws
= task
->dynamic_working_set
;
1646 return KERN_FAILURE
;
1648 return tws_internal_startup_send(tws
);
1653 tws_internal_startup_send(
1657 tws_startup_t scache
;
1660 return KERN_FAILURE
;
1663 /* used to signal write or release depending on state of tws */
1664 if(tws
->startup_cache
) {
1665 vm_offset_t startup_buf
;
1667 startup_buf
= (vm_offset_t
)tws
->startup_cache
;
1668 size
= tws
->startup_cache
->tws_hash_size
;
1669 tws
->startup_cache
= 0;
1671 kmem_free(kernel_map
, startup_buf
, size
);
1672 return KERN_SUCCESS
;
1674 if(tws
->startup_name
== NULL
) {
1676 return KERN_FAILURE
;
1678 scache
= tws_create_startup_list(tws
);
1680 return KERN_FAILURE
;
1681 bsd_write_page_cache_file(tws
->uid
, tws
->startup_name
,
1682 (caddr_t
) scache
, scache
->tws_hash_size
,
1683 tws
->mod
, tws
->fid
);
1684 kfree(scache
, scache
->tws_hash_size
);
1685 kfree(tws
->startup_name
, tws
->startup_name_length
);
1686 tws
->startup_name
= NULL
;
1688 return KERN_SUCCESS
;
1692 tws_handle_startup_file(
1697 boolean_t
*new_info
)
1700 tws_startup_t startup
;
1701 vm_offset_t cache_size
;
1702 kern_return_t error
;
1707 /* don't pre-heat kernel task */
1708 if(task
== kernel_task
)
1709 return KERN_SUCCESS
;
1710 error
= bsd_read_page_cache_file(uid
, &fid
,
1713 (vm_offset_t
*) &startup
,
1716 return KERN_FAILURE
;
1718 if(startup
== NULL
) {
1719 /* Entry for app does not exist, make */
1721 /* we will want our own copy of the shared */
1722 /* regions to pick up a true picture of all */
1723 /* the pages we will touch. */
1724 if((lsf_zone
->count
* lsf_zone
->elem_size
)
1725 > (lsf_zone
->max_size
>> 1)) {
1726 /* We don't want to run out of shared memory */
1727 /* map entries by starting too many private versions */
1728 /* of the shared library structures */
1729 return KERN_SUCCESS
;
1733 error
= tws_write_startup_file(task
,
1734 fid
, mod
, app_name
, uid
);
1739 error
= tws_read_startup_file(task
,
1740 (tws_startup_t
)startup
,
1743 kmem_free(kernel_map
,
1744 (vm_offset_t
)startup
, cache_size
);
1748 return KERN_SUCCESS
;
1752 tws_write_startup_file(
1760 unsigned int string_length
;
1762 string_length
= strlen(name
);
1766 tws
= task
->dynamic_working_set
;
1770 kern_return_t error
;
1772 /* create a dynamic working set of normal size */
1773 if((error
= task_working_set_create(task
, 0, 0, TWS_HASH_STYLE_DEFAULT
)) != KERN_SUCCESS
)
1775 /* we need to reset tws and relock */
1780 if(tws
->startup_name
!= NULL
) {
1782 return KERN_FAILURE
;
1785 tws
->startup_name
= (char *)
1786 kalloc((string_length
+ 1) * (sizeof(char)));
1787 if(tws
->startup_name
== NULL
) {
1789 return KERN_FAILURE
;
1792 bcopy(name
, (char *)tws
->startup_name
, string_length
+ 1);
1793 tws
->startup_name_length
= (string_length
+ 1) * sizeof(char);
1799 return KERN_SUCCESS
;
1802 unsigned long tws_read_startup_file_rejects
= 0;
1805 tws_read_startup_file(
1807 tws_startup_t startup
,
1808 vm_offset_t cache_size
)
1813 unsigned int ele_count
;
1817 tws
= task
->dynamic_working_set
;
1819 /* create a dynamic working set to match file size */
1821 /* start with total size of the data we got from app_profile */
1822 ele_count
= cache_size
;
1823 /* skip the startup header */
1824 ele_count
-= sizeof(struct tws_startup
);
1826 * For each startup cache entry, we have one of these:
1827 * tws_startup_ptr_t startup->table[];
1828 * struct tws_startup_ptr startup->ele[];
1829 * struct tws_startup_ele startup->array[];
1831 ele_count
/= (sizeof (tws_startup_ptr_t
) +
1832 sizeof (struct tws_startup_ptr
) +
1833 sizeof (struct tws_startup_ele
));
1836 * Sanity check: make sure the value for startup->array_size
1837 * that we read from the app_profile file matches the size
1838 * of the data we read from disk. If it doesn't match, we
1839 * can't trust the data and we just drop it all.
1841 if (cache_size
< sizeof(struct tws_startup
) ||
1842 startup
->array_size
!= ele_count
) {
1843 tws_read_startup_file_rejects
++;
1845 kmem_free(kernel_map
, (vm_offset_t
)startup
, cache_size
);
1846 return(KERN_SUCCESS
);
1850 * We'll create the task working set with the default row size
1851 * (TWS_ARRAY_SIZE), so this will give us the number of lines
1852 * we need to store all the data from the app_profile startup
1855 lines
= ele_count
/ TWS_ARRAY_SIZE
;
1857 if(lines
<= TWS_SMALL_HASH_LINE_COUNT
) {
1858 lines
= TWS_SMALL_HASH_LINE_COUNT
;
1860 kmem_free(kernel_map
, (vm_offset_t
)startup
, cache_size
);
1861 return(KERN_SUCCESS
);
1863 old_exp_count
= lines
/TWS_HASH_LINE_COUNT
;
1864 if((old_exp_count
* TWS_HASH_LINE_COUNT
) != lines
) {
1865 lines
= (old_exp_count
+ 1)
1866 * TWS_HASH_LINE_COUNT
;
1869 kern_return_t error
;
1872 if ((error
= task_working_set_create(task
, lines
, 0, TWS_HASH_STYLE_DEFAULT
)) != KERN_SUCCESS
)
1874 /* we need to reset tws and relock */
1878 tws_expand_working_set(
1879 (void *)tws
, lines
, TRUE
);
1885 if(tws
->startup_cache
!= NULL
) {
1887 return KERN_FAILURE
;
1891 /* now need to fix up internal table pointers */
1892 startup
->table
= (tws_startup_ptr_t
*)
1893 (((int)startup
) + (sizeof(struct tws_startup
)));
1894 startup
->ele
= (struct tws_startup_ptr
*)
1895 (((vm_offset_t
)startup
->table
) +
1896 (startup
->array_size
* sizeof(tws_startup_ptr_t
)));
1897 startup
->array
= (struct tws_startup_ele
*)
1898 (((vm_offset_t
)startup
->ele
) +
1899 (startup
->array_size
* sizeof(struct tws_startup_ptr
)));
1900 /* the allocation size and file size should be the same */
1901 /* just in case their not, make sure we dealloc correctly */
1902 startup
->tws_hash_size
= cache_size
;
1904 tws
->startup_cache
= startup
;
1906 return KERN_SUCCESS
;
1911 tws_hash_ws_flush(tws_hash_t tws
) {
1912 tws_startup_t scache
;
1917 if(tws
->startup_name
!= NULL
) {
1918 scache
= tws_create_startup_list(tws
);
1919 if(scache
== NULL
) {
1920 /* dump the name cache, we'll */
1921 /* get it next time */
1922 kfree(tws
->startup_name
, tws
->startup_name_length
);
1923 tws
->startup_name
= NULL
;
1927 bsd_write_page_cache_file(tws
->uid
, tws
->startup_name
,
1928 (caddr_t
) scache
, scache
->tws_hash_size
,
1929 tws
->mod
, tws
->fid
);
1930 kfree(scache
, scache
->tws_hash_size
);
1931 kfree(tws
->startup_name
, tws
->startup_name_length
);
1932 tws
->startup_name
= NULL
;
1939 tws_hash_destroy(tws_hash_t tws
)
1943 if(tws
->startup_cache
!= NULL
) {
1944 kmem_free(kernel_map
,
1945 (vm_offset_t
)tws
->startup_cache
,
1946 tws
->startup_cache
->tws_hash_size
);
1947 tws
->startup_cache
= NULL
;
1949 if(tws
->startup_name
!= NULL
) {
1950 tws_internal_startup_send(tws
);
1952 for (i
=0; i
<tws
->number_of_lines
; i
++) {
1953 for(k
=0; k
<tws
->expansion_count
; k
++) {
1954 /* clear the object refs */
1955 tws_hash_line_clear(tws
, &(tws
->cache
[k
][i
]), NULL
, FALSE
);
1959 while (i
< tws
->expansion_count
) {
1961 kfree(tws
->table
[i
],
1962 sizeof(tws_hash_ptr_t
)
1963 * tws
->number_of_lines
1964 * tws
->number_of_elements
);
1965 kfree(tws
->table_ele
[i
],
1966 sizeof(struct tws_hash_ptr
)
1967 * tws
->number_of_lines
1968 * tws
->number_of_elements
);
1969 kfree(tws
->alt_ele
[i
],
1970 sizeof(struct tws_hash_ptr
)
1971 * tws
->number_of_lines
1972 * tws
->number_of_elements
);
1973 kfree(tws
->cache
[i
],
1974 sizeof(struct tws_hash_line
) * tws
->number_of_lines
);
1977 if(tws
->startup_name
!= NULL
) {
1978 kfree(tws
->startup_name
, tws
->startup_name_length
);
1980 kfree(tws
, sizeof(struct tws_hash
));
1984 tws_hash_clear(tws_hash_t tws
)
1988 for (i
=0; i
<tws
->number_of_lines
; i
++) {
1989 for(k
=0; k
<tws
->expansion_count
; k
++) {
1990 /* clear the object refs */
1991 tws_hash_line_clear(tws
, &(tws
->cache
[k
][i
]), NULL
, FALSE
);
1997 task_working_set_create(
2005 lines
= TWS_HASH_LINE_COUNT
;
2008 rows
= TWS_ARRAY_SIZE
;
2010 if (style
== TWS_HASH_STYLE_DEFAULT
) {
2011 style
= TWS_HASH_STYLE_BASIC
;
2014 if(task
->dynamic_working_set
!= 0) {
2016 return(KERN_FAILURE
);
2017 } else if((task
->dynamic_working_set
=
2018 tws_hash_create(lines
, rows
, style
)) == 0) {
2020 return(KERN_NO_SPACE
);
2023 return KERN_SUCCESS
;
2027 /* Internal use only routines */
2031 * internal sub-function for address space lookup
2032 * returns the target element and the address of the
2033 * previous pointer The previous pointer is the address
2034 * of the pointer pointing to the target element.
2035 * TWS must be locked
2039 tws_traverse_address_hash_list (
2042 vm_offset_t page_addr
,
2044 vm_object_offset_t offset
,
2046 tws_hash_ptr_t
*target_ele
,
2047 tws_hash_ptr_t
**previous_ptr
,
2048 tws_hash_ptr_t
**free_list
,
2049 unsigned int exclusive_addr
)
2052 tws_hash_ptr_t cache_ele
;
2053 tws_hash_ptr_t base_ele
;
2056 *previous_ptr
= NULL
;
2058 for(k
=0; k
<tws
->expansion_count
; k
++) {
2060 cache_ele
= tws
->table
[k
][index
];
2061 base_ele
= cache_ele
;
2062 *previous_ptr
= (tws_hash_ptr_t
*)&(tws
->table
[k
][index
]);
2063 while(cache_ele
!= NULL
) {
2065 cache_ele
->element
& TWS_ADDR_HASH
) == 0) {
2066 *previous_ptr
= (tws_hash_ptr_t
*)&(cache_ele
->next
);
2067 cache_ele
= cache_ele
->next
;
2070 ele
= (tws_hash_ele_t
)((unsigned int)
2071 cache_ele
->element
& ~TWS_ADDR_HASH
);
2072 if ((ele
== 0) || (ele
->object
== 0)) {
2073 /* A little clean-up of empty elements */
2074 cache_ele
->element
= 0;
2075 if(base_ele
== cache_ele
) {
2076 base_ele
= cache_ele
->next
;
2077 tws
->table
[k
][index
] = cache_ele
->next
;
2078 cache_ele
->next
= tws
->free_hash_ele
[k
];
2079 tws
->free_hash_ele
[k
] = cache_ele
;
2080 cache_ele
= base_ele
;
2082 **previous_ptr
= cache_ele
->next
;
2083 cache_ele
->next
= tws
->free_hash_ele
[k
];
2084 tws
->free_hash_ele
[k
] = cache_ele
;
2085 cache_ele
= **previous_ptr
;
2090 if ((ele
->page_addr
<= page_addr
)
2091 && (page_addr
<= (ele
->page_addr
+
2092 (vm_offset_t
)TWS_INDEX_MASK
))
2093 && ((object
== NULL
)
2094 || ((object
== ele
->object
)
2095 && (offset
== ele
->offset
)
2096 && (map
== ele
->map
)))) {
2097 if(exclusive_addr
) {
2099 delta
= ((page_addr
- ele
->page_addr
)
2101 if((1 << delta
) & ele
->page_cache
) {
2102 /* We've found a match */
2103 *target_ele
= cache_ele
;
2106 &(tws
->free_hash_ele
[k
]);
2110 /* We've found a match */
2111 *target_ele
= cache_ele
;
2112 *free_list
= (tws_hash_ptr_t
*)
2113 &(tws
->free_hash_ele
[k
]);
2117 *previous_ptr
= (tws_hash_ptr_t
*)&(cache_ele
->next
);
2118 cache_ele
= cache_ele
->next
;
2125 * internal sub-function for object space lookup
2126 * returns the target element and the address of the
2127 * previous pointer The previous pointer is the address
2128 * of the pointer pointing to the target element.
2129 * TWS must be locked
2134 tws_traverse_object_hash_list (
2138 vm_object_offset_t offset
,
2139 unsigned int pagemask
,
2140 tws_hash_ptr_t
*target_ele
,
2141 tws_hash_ptr_t
**previous_ptr
,
2142 tws_hash_ptr_t
**free_list
)
2145 tws_hash_ptr_t cache_ele
;
2146 tws_hash_ptr_t base_ele
;
2149 *previous_ptr
= NULL
;
2151 for(k
=0; k
<tws
->expansion_count
; k
++) {
2152 cache_ele
= tws
->table
[k
][index
];
2153 base_ele
= cache_ele
;
2154 *previous_ptr
= &(tws
->table
[k
][index
]);
2155 while(cache_ele
!= NULL
) {
2156 if((((unsigned int)cache_ele
->element
)
2157 & TWS_ADDR_HASH
) != 0) {
2158 *previous_ptr
= &(cache_ele
->next
);
2159 cache_ele
= cache_ele
->next
;
2162 if ((cache_ele
->element
== 0) ||
2163 (cache_ele
->element
->object
== 0)) {
2164 /* A little clean-up of empty elements */
2165 cache_ele
->element
= 0;
2166 if(base_ele
== cache_ele
) {
2167 base_ele
= cache_ele
->next
;
2168 tws
->table
[k
][index
] = cache_ele
->next
;
2169 cache_ele
->next
= tws
->free_hash_ele
[k
];
2170 tws
->free_hash_ele
[k
] = cache_ele
;
2171 cache_ele
= tws
->table
[k
][index
];
2173 **previous_ptr
= cache_ele
->next
;
2174 cache_ele
->next
= tws
->free_hash_ele
[k
];
2175 tws
->free_hash_ele
[k
] = cache_ele
;
2176 cache_ele
= **previous_ptr
;
2180 if ((cache_ele
->element
->object
== object
)
2181 && (cache_ele
->element
->offset
==
2182 (offset
- (offset
& ~TWS_HASH_OFF_MASK
)))) {
2183 if((cache_ele
->element
->page_cache
& pagemask
)
2184 || (pagemask
== 0xFFFFFFFF)) {
2185 /* We've found a match */
2186 *target_ele
= cache_ele
;
2187 *free_list
= &(tws
->free_hash_ele
[k
]);
2191 *previous_ptr
= (tws_hash_ptr_t
*)&(cache_ele
->next
);
2192 cache_ele
= cache_ele
->next
;
2199 * For a given object/offset, discover whether the indexed 32 page frame
2200 * containing the object/offset exists and if their are at least threshold
2201 * pages present. Returns true if population meets threshold.
2204 tws_test_for_community(
2207 vm_object_offset_t offset
,
2208 unsigned int threshold
,
2209 unsigned int *pagemask
)
2212 tws_hash_ptr_t cache_ele
;
2213 tws_hash_ptr_t
*trailer
;
2214 tws_hash_ptr_t
*free_list
;
2217 index
= do_tws_hash(object
, offset
,
2218 tws
->number_of_elements
, tws
->number_of_lines
);
2219 tws_traverse_object_hash_list(tws
, index
, object
, offset
, 0xFFFFFFFF,
2220 &cache_ele
, &trailer
, &free_list
);
2222 if(cache_ele
!= NULL
) {
2226 for(i
=1; i
!=0; i
=i
<<1) {
2227 if(i
& cache_ele
->element
->page_cache
)
2229 if(ctr
== threshold
) {
2231 *pagemask
= cache_ele
->element
->page_cache
;
2243 * Gets new hash element for object hash from free pools
2244 * TWS must be locked
2253 tws_hash_ptr_t element
;
2255 if(tws
->obj_free_count
[set
] < tws
->number_of_lines
* tws
->number_of_elements
) {
2256 element
= &(tws
->table_ele
[set
][tws
->obj_free_count
[set
]]);
2257 tws
->obj_free_count
[set
]+=1;
2258 } else if(tws
->free_hash_ele
[set
] == NULL
) {
2261 element
= tws
->free_hash_ele
[set
];
2264 tws
->free_hash_ele
[set
] = tws
->free_hash_ele
[set
]->next
;
2266 element
->element
= 0;
2267 element
->next
= tws
->table
[set
][index
];
2268 tws
->table
[set
][index
] = element
;
2273 * Gets new hash element for addr hash from free pools
2274 * TWS must be locked
2283 tws_hash_ptr_t element
;
2285 if(tws
->addr_free_count
[set
]
2286 < tws
->number_of_lines
* tws
->number_of_elements
) {
2287 element
= &(tws
->alt_ele
[set
][tws
->addr_free_count
[set
]]);
2288 tws
->addr_free_count
[set
]+=1;
2289 } else if(tws
->free_hash_ele
[set
] == NULL
) {
2292 element
= tws
->free_hash_ele
[set
];
2295 tws
->free_hash_ele
[set
] = tws
->free_hash_ele
[set
]->next
;
2297 element
->element
= (tws_hash_ele_t
)TWS_ADDR_HASH
;
2298 element
->next
= tws
->table
[set
][index
];
2299 tws
->table
[set
][index
] = element
;