]> git.saurik.com Git - apple/xnu.git/blame - osfmk/vm/task_working_set.c
xnu-792.10.96.tar.gz
[apple/xnu.git] / osfmk / vm / task_working_set.c
CommitLineData
0b4e3aa0 1/*
91447636 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
0b4e3aa0
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
37839358
A
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.
0b4e3aa0 11 *
37839358
A
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
0b4e3aa0
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
37839358
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
0b4e3aa0
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 */
24/*
25 * File: vm/task_working_set.c
26 * Author: Chris Youngworth
27 * Date: 2001
28 *
29 * Working set detection and maintainence module
30 */
31
32
9bccf70c 33#include <mach/mach_types.h>
91447636 34#include <mach/memory_object.h>
9bccf70c 35#include <mach/shared_memory_server.h>
0b4e3aa0 36#include <vm/task_working_set.h>
9bccf70c 37#include <vm/vm_kern.h>
0b4e3aa0
A
38#include <vm/vm_map.h>
39#include <vm/vm_page.h>
40#include <vm/vm_pageout.h>
41#include <kern/sched.h>
91447636
A
42#include <kern/kalloc.h>
43
44#include <vm/vm_protos.h>
0b4e3aa0 45
91447636
A
46/*
47 * LP64todo - Task Working Set Support is for 32-bit only
48 */
9bccf70c
A
49extern zone_t lsf_zone;
50
51/* declarations for internal use only routines */
91447636
A
52int startup_miss = 0;
53
54tws_hash_t
55tws_hash_create(
56 unsigned int lines,
57 unsigned int rows,
58 unsigned int style);
59
60kern_return_t
61tws_write_startup_file(
62 task_t task,
63 int fid,
64 int mod,
65 char *name,
66 unsigned int string_length);
67
68kern_return_t
69tws_read_startup_file(
70 task_t task,
71 tws_startup_t startup,
72 vm_offset_t cache_size);
9bccf70c
A
73
74tws_startup_t
75tws_create_startup_list(
76 tws_hash_t tws);
77
78unsigned int
79tws_startup_list_lookup(
80 tws_startup_t startup,
81 vm_offset_t addr);
82
83kern_return_t
84tws_internal_startup_send(
85 tws_hash_t tws);
86
91447636
A
87void
88tws_hash_line_clear(
89 tws_hash_t tws,
90 tws_hash_line_t hash_line,
91 vm_object_t object,
92 boolean_t live);
93
94void
95tws_hash_clear(
96 tws_hash_t tws);
97
9bccf70c
A
98void
99tws_traverse_address_hash_list (
100 tws_hash_t tws,
101 unsigned int index,
102 vm_offset_t page_addr,
103 vm_object_t object,
104 vm_object_offset_t offset,
105 vm_map_t map,
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);
110
111void
112tws_traverse_object_hash_list (
113 tws_hash_t tws,
114 unsigned int index,
115 vm_object_t object,
116 vm_object_offset_t offset,
91447636 117 unsigned int pagemask,
9bccf70c
A
118 tws_hash_ptr_t *target_ele,
119 tws_hash_ptr_t **previous_ptr,
120 tws_hash_ptr_t **free_list);
121
122tws_hash_ptr_t
123new_addr_hash(
124 tws_hash_t tws,
125 unsigned int set,
126 unsigned int index);
127
128tws_hash_ptr_t
129new_obj_hash(
130 tws_hash_t tws,
131 unsigned int set,
132 unsigned int index);
133
134int tws_test_for_community(
135 tws_hash_t tws,
136 vm_object_t object,
137 vm_object_offset_t offset,
138 unsigned int threshold,
91447636
A
139 unsigned int *pagemask);
140
141kern_return_t
142tws_internal_lookup(
143 tws_hash_t tws,
144 vm_object_offset_t offset,
145 vm_object_t object,
146 tws_hash_line_t *line);
0b4e3aa0
A
147
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 */
151
152tws_hash_t
153tws_hash_create(
154 unsigned int lines,
155 unsigned int rows,
156 unsigned int style)
157{
158 tws_hash_t tws;
0b4e3aa0
A
159
160 if ((style != TWS_HASH_STYLE_BASIC) &&
161 (style != TWS_HASH_STYLE_BASIC)) {
162 return((tws_hash_t)NULL);
163 }
164
165
166 tws = (tws_hash_t)(kalloc(sizeof(struct tws_hash)));
167 if(tws == (tws_hash_t)NULL)
168 return tws;
169
9bccf70c
A
170 if((tws->table[0] = (tws_hash_ptr_t *)
171 kalloc(sizeof(tws_hash_ptr_t) * lines * rows))
0b4e3aa0 172 == NULL) {
91447636 173 kfree(tws, sizeof(struct tws_hash));
0b4e3aa0
A
174 return (tws_hash_t)NULL;
175 }
9bccf70c
A
176 if((tws->table_ele[0] = (tws_hash_ptr_t)
177 kalloc(sizeof(struct tws_hash_ptr) * lines * rows))
0b4e3aa0 178 == NULL) {
91447636
A
179 kfree(tws->table[0], sizeof(tws_hash_ptr_t) * lines * rows);
180 kfree(tws, sizeof(struct tws_hash));
9bccf70c
A
181 return (tws_hash_t)NULL;
182 }
183 if((tws->alt_ele[0] = (tws_hash_ptr_t)
184 kalloc(sizeof(struct tws_hash_ptr) * lines * rows))
185 == NULL) {
91447636
A
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));
0b4e3aa0
A
190 return (tws_hash_t)NULL;
191 }
192 if((tws->cache[0] = (struct tws_hash_line *)
193 kalloc(sizeof(struct tws_hash_line) * lines))
194 == NULL) {
91447636
A
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));
0b4e3aa0
A
201 return (tws_hash_t)NULL;
202 }
9bccf70c
A
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;
0b4e3aa0
A
206
207 /* most defaults are such that a bzero will initialize */
9bccf70c
A
208 bzero((char *)tws->table[0],sizeof(tws_hash_ptr_t)
209 * lines * rows);
210 bzero((char *)tws->table_ele[0],sizeof(struct tws_hash_ptr)
211 * lines * rows);
212 bzero((char *)tws->alt_ele[0],sizeof(struct tws_hash_ptr)
213 * lines * rows);
0b4e3aa0
A
214 bzero((char *)tws->cache[0], sizeof(struct tws_hash_line)
215 * lines);
216
91447636 217 mutex_init(&tws->lock, 0);
0b4e3aa0
A
218 tws->style = style;
219 tws->current_line = 0;
220 tws->pageout_count = 0;
221 tws->line_count = 0;
9bccf70c
A
222 tws->startup_cache = NULL;
223 tws->startup_name = NULL;
0b4e3aa0
A
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;
230
231 return tws;
232}
233
91447636
A
234
235extern vm_page_t
236vm_page_lookup_nohint(vm_object_t object, vm_object_offset_t offset);
237
238
0b4e3aa0
A
239void
240tws_hash_line_clear(
241 tws_hash_t tws,
91447636
A
242 tws_hash_line_t hash_line,
243 __unused vm_object_t object,
0b4e3aa0
A
244 boolean_t live)
245{
246 struct tws_hash_ele *hash_ele;
9bccf70c
A
247 struct tws_hash_ptr **trailer;
248 struct tws_hash_ptr **free_list;
249 tws_hash_ele_t addr_ele;
0b4e3aa0 250 int index;
91447636 251 unsigned int i, j;
0b4e3aa0
A
252 int dump_pmap;
253 int hash_loop;
254
255
256 if(tws->line_count < tws->number_of_lines) {
257 tws->line_count++;
258 dump_pmap = 1;
259 } else {
260 if(tws->pageout_count != vm_pageout_scan_event_counter) {
261 tws->pageout_count =
262 vm_pageout_scan_event_counter;
263 tws->line_count = 0;
264 dump_pmap = 1;
265 } else {
266 dump_pmap = 0;
267 }
268 }
269 hash_line->ele_count = 0;
0b4e3aa0 270
9bccf70c
A
271 for (i=0; i<tws->number_of_elements; i++) {
272 hash_loop = 0;
273 hash_ele = &(hash_line->list[i]);
274 if(hash_ele->object != 0) {
275
276 vm_object_offset_t local_off = 0;
277 tws_hash_ptr_t cache_ele;
278
279 index = alt_tws_hash(
91447636 280 hash_ele->page_addr & TWS_ADDR_OFF_MASK,
9bccf70c
A
281 tws->number_of_elements,
282 tws->number_of_lines);
283
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:"
293 " out of sync\n");
294 cache_ele->element = 0;
295 *trailer = cache_ele->next;
296 cache_ele->next = *free_list;
297 *free_list = cache_ele;
298 }
299
300 index = alt_tws_hash(
301 (hash_ele->page_addr - 0x1f000)
91447636 302 & TWS_ADDR_OFF_MASK,
9bccf70c
A
303 tws->number_of_elements,
304 tws->number_of_lines);
305
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);
310
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: "
316 "out of sync\n");
317 cache_ele->element = 0;
318 *trailer = cache_ele->next;
319 cache_ele->next = *free_list;
320 *free_list = cache_ele;
321 }
322
323
324 if((hash_ele->map != NULL) && (live)) {
325 vm_page_t p;
91447636
A
326
327#if 0
328 if (object != hash_ele->object) {
329 if (object)
330 vm_object_unlock(object);
331 vm_object_lock(hash_ele->object);
332 }
333#endif
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()),
341 p->phys_page);
342 }
9bccf70c 343 }
91447636
A
344 local_off += PAGE_SIZE_64;
345 }
346 }
347#if 0
348 if (object != hash_ele->object) {
349 vm_object_unlock(hash_ele->object);
350 if (object)
351 vm_object_lock(object);
0b4e3aa0 352 }
91447636 353#endif
0b4e3aa0 354 }
9bccf70c
A
355
356 if(tws->style == TWS_HASH_STYLE_SIGNAL) {
357 vm_object_deallocate(hash_ele->object);
358 vm_map_deallocate(hash_ele->map);
359 }
0b4e3aa0 360
9bccf70c
A
361 index = do_tws_hash(hash_ele->object, hash_ele->offset,
362 tws->number_of_elements,
363 tws->number_of_lines);
0b4e3aa0 364
9bccf70c
A
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;
0b4e3aa0 373 }
9bccf70c 374 hash_ele->object = 0;
0b4e3aa0 375 }
0b4e3aa0
A
376 }
377}
378
379kern_return_t
90556fb8 380tws_internal_lookup(
0b4e3aa0
A
381 tws_hash_t tws,
382 vm_object_offset_t offset,
383 vm_object_t object,
384 tws_hash_line_t *line)
385{
0b4e3aa0 386 int index;
0b4e3aa0 387 int loop;
9bccf70c
A
388 int set;
389 int ele_line;
390 vm_offset_t pagenum;
391 tws_hash_ptr_t cache_ele;
392 tws_hash_ptr_t *trailer;
393 tws_hash_ptr_t *free_list;
0b4e3aa0
A
394
395 /* don't cache private objects */
396 if(object->private)
397 return KERN_SUCCESS;
398
0b4e3aa0
A
399 index = do_tws_hash(object, offset,
400 tws->number_of_elements, tws->number_of_lines);
401 loop = 0;
402
403 tws->lookup_count++;
404 if(tws->lookup_count == 0)
405 tws->insert_count = 0;
9bccf70c
A
406 if(tws->startup_name != NULL) {
407 int age_of_cache;
408 age_of_cache = ((sched_tick
409 - tws->time_of_creation) >> SCHED_TICK_SHIFT);
483a1d10 410 if (age_of_cache > 45) {
9bccf70c
A
411 return KERN_OPERATION_TIMED_OUT;
412 }
413 }
0b4e3aa0 414
9bccf70c
A
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) {
419 int age_of_cache;
420 age_of_cache = ((sched_tick
421 - tws->time_of_creation) >> SCHED_TICK_SHIFT);
483a1d10 422 if (age_of_cache > 45) {
9bccf70c 423 return KERN_OPERATION_TIMED_OUT;
0b4e3aa0 424 }
9bccf70c 425 }
0b4e3aa0 426 }
9bccf70c
A
427
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];
9bccf70c
A
437 return KERN_SUCCESS;
438 }
439
0b4e3aa0 440 return KERN_FAILURE;
9bccf70c
A
441
442
0b4e3aa0
A
443}
444
90556fb8
A
445kern_return_t
446tws_lookup(
447 tws_hash_t tws,
448 vm_object_offset_t offset,
449 vm_object_t object,
450 tws_hash_line_t *line)
451{
452 kern_return_t kr;
453
454 if(!tws_lock_try(tws)) {
455 return KERN_FAILURE;
456 }
457 kr = tws_internal_lookup(tws,
458 offset, object, line);
459 tws_unlock(tws);
460 return kr;
461}
462
0b4e3aa0
A
463kern_return_t
464tws_expand_working_set(
91447636
A
465 tws_hash_t old_tws,
466 unsigned int line_count,
9bccf70c 467 boolean_t dump_data)
0b4e3aa0
A
468{
469 tws_hash_t new_tws;
0b4e3aa0
A
470 unsigned int i,j,k;
471 struct tws_hash temp;
472
0b4e3aa0
A
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 */
476
477 if (old_tws->number_of_lines >= line_count) {
478 return KERN_FAILURE;
479 }
480 if((new_tws = tws_hash_create(line_count,
481 old_tws->number_of_elements, old_tws->style)) == 0) {
482 return(KERN_NO_SPACE);
483 }
484 tws_lock(old_tws);
485
9bccf70c
A
486 if(!dump_data) {
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) {
0b4e3aa0
A
495 paddr = 0;
496 for(page_index = 1; page_index != 0;
483a1d10 497 page_index = page_index << 1) {
0b4e3aa0
A
498 if (entry->page_cache & page_index) {
499 tws_insert(new_tws,
500 entry->offset+paddr,
501 entry->object,
502 entry->page_addr+paddr,
503 entry->map);
504 }
505 paddr+=PAGE_SIZE;
506 }
507
9bccf70c
A
508 }
509 }
510 }
0b4e3aa0
A
511 }
512 }
513
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++) {
9bccf70c
A
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];
0b4e3aa0 527 temp.table[i] = new_tws->table[i];
9bccf70c
A
528 temp.table_ele[i] = new_tws->table_ele[i];
529 temp.alt_ele[i] = new_tws->alt_ele[i];
0b4e3aa0
A
530 temp.cache[i] = new_tws->cache[i];
531 }
532
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++) {
9bccf70c
A
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];
0b4e3aa0 546 new_tws->table[i] = old_tws->table[i];
9bccf70c
A
547 new_tws->table_ele[i] = old_tws->table_ele[i];
548 new_tws->alt_ele[i] = old_tws->alt_ele[i];
0b4e3aa0
A
549 new_tws->cache[i] = old_tws->cache[i];
550 }
551
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++) {
9bccf70c
A
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;
0b4e3aa0 565 old_tws->table[i] = temp.table[i];
9bccf70c
A
566 old_tws->table_ele[i] = temp.table_ele[i];
567 old_tws->alt_ele[i] = temp.alt_ele[i];
0b4e3aa0
A
568 old_tws->cache[i] = temp.cache[i];
569 }
570
571 tws_hash_destroy(new_tws);
572 tws_unlock(old_tws);
573 return KERN_SUCCESS;
574}
575
9bccf70c
A
576tws_hash_t test_tws = 0;
577
0b4e3aa0
A
578kern_return_t
579tws_insert(
580 tws_hash_t tws,
581 vm_object_offset_t offset,
582 vm_object_t object,
583 vm_offset_t page_addr,
584 vm_map_t map)
585{
0b4e3aa0
A
586 unsigned int index;
587 unsigned int alt_index;
9bccf70c 588 unsigned int index_enum[2];
0b4e3aa0 589 unsigned int ele_index;
9bccf70c
A
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;
91447636
A
596 unsigned int i;
597 unsigned int current_line;
598 unsigned int set;
9bccf70c
A
599 int ctr;
600 unsigned int startup_cache_line;
483a1d10 601 int age_of_cache = 0;
9bccf70c 602
0b4e3aa0
A
603 if(!tws_lock_try(tws)) {
604 return KERN_FAILURE;
605 }
606 tws->insert_count++;
607 current_line = 0xFFFFFFFF;
91447636 608 set = 0;
0b4e3aa0 609
9bccf70c 610 startup_cache_line = 0;
483a1d10
A
611
612 if (tws->startup_cache) {
613 vm_offset_t startup_page_addr;
614
615 startup_page_addr = page_addr - (offset - (offset & TWS_HASH_OFF_MASK));
616
617 age_of_cache = ((sched_tick - tws->time_of_creation) >> SCHED_TICK_SHIFT);
618
619 startup_cache_line = tws_startup_list_lookup(tws->startup_cache, startup_page_addr);
9bccf70c 620 }
0b4e3aa0
A
621 /* This next bit of code, the and alternate hash */
622 /* are all made necessary because of IPC COW */
623
9bccf70c
A
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 */
628 /* objects */
629 index_enum[0] = alt_tws_hash(
91447636 630 page_addr & TWS_ADDR_OFF_MASK,
0b4e3aa0
A
631 tws->number_of_elements, tws->number_of_lines);
632
9bccf70c 633 index_enum[1] = alt_tws_hash(
91447636 634 (page_addr - 0x1f000) & TWS_ADDR_OFF_MASK,
9bccf70c 635 tws->number_of_elements, tws->number_of_lines);
0b4e3aa0 636
9bccf70c
A
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,
641 0, NULL,
642 &cache_ele, &trailer, &free_list, 1);
643 if(cache_ele != NULL) {
644 /* found one */
645 resident = (tws_hash_ele_t)((unsigned int)
646 cache_ele->element & ~TWS_ADDR_HASH);
647 if((object == resident->object) &&
648 resident->offset ==
649 (offset & TWS_HASH_OFF_MASK)) {
650 /* This is our object/offset */
651 resident->page_cache
652 |= startup_cache_line;
653 resident->page_cache |=
654 (1<<(((vm_offset_t)
655 (offset & TWS_INDEX_MASK))>>12));
656 tws_unlock(tws);
483a1d10 657 if (age_of_cache > 45)
9bccf70c
A
658 return KERN_OPERATION_TIMED_OUT;
659 return KERN_SUCCESS;
660 }
661 if((object->shadow ==
662 resident->object) &&
663 ((resident->offset
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;
0b4e3aa0 672 break;
9bccf70c
A
673 } else {
674 resident->page_cache &=
675 ~(1<<(((vm_offset_t)(page_addr
676 - resident->page_addr))
677 >>12));
0b4e3aa0 678 }
9bccf70c
A
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 */
688 index = do_tws_hash(
689 resident->object,
690 resident->offset,
691 tws->number_of_elements,
692 tws->number_of_lines);
693 tws_traverse_object_hash_list(tws,
694 index, resident->object,
695 resident->offset,
696 0xFFFFFFFF, &cache_ele,
697 &trailer, &free_list);
698 if(cache_ele != NULL) {
0b4e3aa0 699 if(tws->style ==
9bccf70c 700 TWS_HASH_STYLE_SIGNAL) {
0b4e3aa0 701 vm_object_deallocate(
9bccf70c 702 cache_ele->element->object);
0b4e3aa0 703 vm_map_deallocate(
9bccf70c
A
704 cache_ele->element->map);
705 }
706 current_line =
707 cache_ele->element->line;
708 set = current_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;
714 tws->cache[set]
715 [current_line].ele_count--;
0b4e3aa0 716 }
9bccf70c
A
717 cache_ele->element = 0;
718 *trailer = cache_ele->next;
719 cache_ele->next = *free_list;
720 *free_list = cache_ele;
0b4e3aa0 721 }
0b4e3aa0 722 }
9bccf70c 723 continue;
0b4e3aa0 724 }
9bccf70c 725 ctr+=1;
0b4e3aa0
A
726 }
727
9bccf70c
A
728 /*
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
734 * find an element
735 */
736
0b4e3aa0
A
737 index = do_tws_hash(object, offset,
738 tws->number_of_elements, tws->number_of_lines);
0b4e3aa0 739
9bccf70c 740 alt_index = index_enum[0];
0b4e3aa0 741
9bccf70c
A
742 tws_traverse_object_hash_list(tws, index, object, offset,
743 0xFFFFFFFF, &cache_ele, &trailer, &free_list);
744 if(cache_ele != NULL) {
745 obj_ele = cache_ele;
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;
750
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;
760 } else {
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 */
764 }
0b4e3aa0
A
765 }
766
9bccf70c 767
0b4e3aa0
A
768
769 if(tws->style == TWS_HASH_STYLE_SIGNAL) {
770 vm_object_reference(object);
771 vm_map_reference(map);
772 }
773
774 if(current_line == 0xFFFFFFFF) {
0b4e3aa0
A
775 current_line = tws->current_line;
776 set = current_line/tws->number_of_lines;
777 current_line = current_line - (set * tws->number_of_lines);
778
9bccf70c
A
779#ifdef notdef
780 if(cache_full) {
781 tws->current_line = tws->number_of_lines - 1;
782 }
783#endif
0b4e3aa0 784 if(tws->cache[set][current_line].ele_count
9bccf70c 785 >= tws->number_of_elements) {
0b4e3aa0
A
786 current_line++;
787 tws->current_line++;
788 if(current_line == tws->number_of_lines) {
789 set++;
790 current_line = 0;
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) {
799 tws->current_line--;
800 tws_unlock(tws);
801 return KERN_NO_SPACE;
802 }
55e303ae
A
803 /* object persistence is guaranteed by */
804 /* an elevated paging or object */
805 /* reference count in the caller. */
806 vm_object_unlock(object);
9bccf70c
A
807 if((tws->table[set] = (tws_hash_ptr_t *)
808 kalloc(sizeof(tws_hash_ptr_t)
809 * tws->number_of_lines
0b4e3aa0
A
810 * tws->number_of_elements))
811 == NULL) {
812 set = 0;
9bccf70c
A
813 } else if((tws->table_ele[set] =
814 (tws_hash_ptr_t)
815 kalloc(sizeof(struct tws_hash_ptr)
816 * tws->number_of_lines
0b4e3aa0
A
817 * tws->number_of_elements))
818 == NULL) {
91447636
A
819 kfree(tws->table[set],
820 sizeof(tws_hash_ptr_t)
821 * tws->number_of_lines
822 * tws->number_of_elements);
9bccf70c
A
823 set = 0;
824 } else if((tws->alt_ele[set] =
825 (tws_hash_ptr_t)
826 kalloc(sizeof(struct tws_hash_ptr)
827 * tws->number_of_lines
828 * tws->number_of_elements))
829 == NULL) {
91447636
A
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)
9bccf70c 836 * tws->number_of_lines
0b4e3aa0
A
837 * tws->number_of_elements);
838 tws->table[set] = NULL;
839 set = 0;
840
841 } else if((tws->cache[set] =
842 (struct tws_hash_line *)
843 kalloc(sizeof
844 (struct tws_hash_line)
845 * tws->number_of_lines))
846 == NULL) {
91447636
A
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);
0b4e3aa0
A
859 tws->table[set] = NULL;
860 set = 0;
861
862 } else {
9bccf70c
A
863 tws->free_hash_ele[set] =
864 (tws_hash_ptr_t)0;
865 tws->obj_free_count[set] = 0;
866 tws->addr_free_count[set] = 0;
0b4e3aa0 867 bzero((char *)tws->table[set],
9bccf70c
A
868 sizeof(tws_hash_ptr_t)
869 * tws->number_of_lines
0b4e3aa0 870 * tws->number_of_elements);
9bccf70c
A
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
0b4e3aa0
A
878 * tws->number_of_elements);
879 bzero((char *)tws->cache[set],
880 sizeof(struct tws_hash_line)
881 * tws->number_of_lines);
882 }
55e303ae 883 vm_object_lock(object);
0b4e3aa0 884 } else {
483a1d10 885 if (tws->startup_name != NULL) {
9bccf70c 886 tws->current_line--;
483a1d10
A
887
888 age_of_cache = ((sched_tick - tws->time_of_creation) >> SCHED_TICK_SHIFT);
889
9bccf70c 890 tws_unlock(tws);
483a1d10
A
891
892 if (age_of_cache > 45)
893 return KERN_OPERATION_TIMED_OUT;
894
9bccf70c
A
895 return KERN_FAILURE;
896 }
0b4e3aa0
A
897 tws->lookup_count = 0;
898 tws->insert_count = 0;
899 set = 0;
900 }
901 }
902 tws->current_line = set * tws->number_of_lines;
903 }
904 if(set < tws->expansion_count) {
905 tws_hash_line_clear(tws,
91447636 906 &(tws->cache[set][current_line]), object, TRUE);
0b4e3aa0
A
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);
912 }
913 tws_unlock(tws);
914 return KERN_FAILURE;
915 }
916 } else {
917 tws->expansion_count++;
918 }
919 }
920 }
921
9bccf70c
A
922
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;
929 tws_unlock(tws);
930 return KERN_FAILURE;
0b4e3aa0 931 }
0b4e3aa0
A
932 }
933
9bccf70c
A
934 /* set address hash element */
935 if(addr_ele == NULL) {
936 addr_ele = new_addr_hash(tws, set, alt_index);
937 }
0b4e3aa0 938
9bccf70c
A
939 if(target_element == NULL) {
940 ele_index = 0;
941 for(i = 0; i<tws->number_of_elements; i++) {
942 if(tws->cache[set][current_line].
943 list[ele_index].object == 0) {
944 break;
945 }
946 ele_index++;
947 if(ele_index >= tws->number_of_elements)
948 ele_index = 0;
949
950 }
951
952 if(i == tws->number_of_elements)
953 panic("tws_insert: no free elements");
0b4e3aa0 954
9bccf70c
A
955 target_element =
956 &(tws->cache[set][current_line].list[ele_index]);
957
958 tws->cache[set][current_line].ele_count++;
959 }
960
961 obj_ele->element = target_element;
962 if(addr_ele) {
963 addr_ele->element = (tws_hash_ele_t)
964 (((unsigned int)target_element) | TWS_ADDR_HASH);
965 }
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 =
0b4e3aa0 972 current_line + (set * tws->number_of_lines);
0b4e3aa0 973
483a1d10
A
974 target_element->page_cache |= startup_cache_line;
975 target_element->page_cache |= 1<<(((vm_offset_t)(offset & TWS_INDEX_MASK))>>12);
0b4e3aa0
A
976
977 tws_unlock(tws);
483a1d10
A
978
979 if (age_of_cache > 45)
9bccf70c 980 return KERN_OPERATION_TIMED_OUT;
483a1d10 981
0b4e3aa0
A
982 return KERN_SUCCESS;
983}
984
985/*
986 * tws_build_cluster
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.
992 *
993 */
994#if MACH_PAGEMAP
995/*
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.
1004 *
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.
1015 *
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.
1020 */
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 */
1026/*
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.
1040 */
1041#define LOOK_FOR(o, f) TRUE
1042#define PAGED_OUT(o, f) FALSE
1043#endif /* MACH_PAGEMAP */
1044
91447636 1045
0b4e3aa0
A
1046void
1047tws_build_cluster(
1048 tws_hash_t tws,
1049 vm_object_t object,
1050 vm_object_offset_t *start,
1051 vm_object_offset_t *end,
1052 vm_size_t max_length)
1053{
1054 tws_hash_line_t line;
0b4e3aa0
A
1055 vm_object_offset_t before = *start;
1056 vm_object_offset_t after = *end;
9bccf70c
A
1057 vm_object_offset_t original_start = *start;
1058 vm_object_offset_t original_end = *end;
0b4e3aa0
A
1059 vm_size_t length = (vm_size_t)(*end - *start);
1060 vm_page_t m;
1061 kern_return_t kret;
1062 vm_object_offset_t object_size;
9bccf70c 1063 int age_of_cache;
91447636 1064 vm_size_t pre_heat_size;
9bccf70c 1065 unsigned int ele_cache;
55e303ae
A
1066 unsigned int end_cache = 0;
1067 unsigned int start_cache = 0;
91447636 1068 unsigned int memory_scarce = 0;
0b4e3aa0 1069
765c9de3 1070 if((object->private) || !(object->pager))
0b4e3aa0
A
1071 return;
1072
1073 if (!object->internal) {
c0fea474 1074 /* XXX FBDP !internal doesn't mean vnode pager */
0b4e3aa0
A
1075 kret = vnode_pager_get_object_size(
1076 object->pager,
1077 &object_size);
c0fea474
A
1078 if (kret != KERN_SUCCESS) {
1079 object_size = object->size;
1080 }
0b4e3aa0 1081 } else {
765c9de3 1082 object_size = object->size;
0b4e3aa0 1083 }
9bccf70c 1084
90556fb8
A
1085 if((!tws) || (!tws_lock_try(tws))) {
1086 return;
1087 }
9bccf70c
A
1088 age_of_cache = ((sched_tick
1089 - tws->time_of_creation) >> SCHED_TICK_SHIFT);
1090
91447636
A
1091 if (vm_page_free_count < (2 * vm_page_free_target))
1092 memory_scarce = 1;
1093
9bccf70c
A
1094 /* When pre-heat files are not available, resort to speculation */
1095 /* based on size of file */
1096
483a1d10
A
1097 if (tws->startup_cache || object->internal || age_of_cache > 45) {
1098 pre_heat_size = 0;
0b4e3aa0 1099 } else {
9bccf70c 1100 if (object_size > (vm_object_offset_t)(1024 * 1024))
483a1d10 1101 pre_heat_size = 8 * PAGE_SIZE;
91447636 1102 else if (object_size > (vm_object_offset_t)(128 * 1024))
483a1d10 1103 pre_heat_size = 4 * PAGE_SIZE;
91447636
A
1104 else
1105 pre_heat_size = 2 * PAGE_SIZE;
9bccf70c
A
1106 }
1107
483a1d10 1108 if (tws->startup_cache) {
91447636
A
1109 int target_page_count;
1110
1111 if (memory_scarce)
1112 target_page_count = 16;
1113 else
1114 target_page_count = 4;
483a1d10 1115
91447636 1116 if (tws_test_for_community(tws, object, *start, target_page_count, &ele_cache))
483a1d10
A
1117 {
1118 start_cache = ele_cache;
9bccf70c
A
1119 *start = *start & TWS_HASH_OFF_MASK;
1120 *end = *start + (32 * PAGE_SIZE_64);
483a1d10
A
1121
1122 if (*end > object_size) {
1123 *end = round_page_64(object_size);
9bccf70c 1124 max_length = 0;
483a1d10
A
1125 } else
1126 end_cache = ele_cache;
1127
1128 while (max_length > ((*end - *start) + (32 * PAGE_SIZE))) {
1129 int expanded;
1130
9bccf70c
A
1131 expanded = 0;
1132 after = *end;
483a1d10
A
1133
1134 if ((after + (32 * PAGE_SIZE_64)) <= object_size &&
1135 (tws_test_for_community(tws, object, after, 8, &ele_cache))) {
1136
1137 *end = after + (32 * PAGE_SIZE_64);
9bccf70c
A
1138 end_cache = ele_cache;
1139 expanded = 1;
1140 }
483a1d10 1141 if (max_length < ((*end - *start) + (32 * PAGE_SIZE_64))) {
9bccf70c
A
1142 break;
1143 }
483a1d10
A
1144 if (*start) {
1145 before = (*start - PAGE_SIZE_64) & TWS_HASH_OFF_MASK;
1146
1147 if (tws_test_for_community(tws, object, before, 8, &ele_cache)) {
1148
1149 *start = before;
1150 start_cache = ele_cache;
1151 expanded = 1;
1152 }
9bccf70c 1153 }
483a1d10 1154 if (expanded == 0)
9bccf70c
A
1155 break;
1156 }
483a1d10
A
1157 if (end_cache)
1158 *end -= PAGE_SIZE_64;
9bccf70c 1159
483a1d10 1160 if (start_cache != 0) {
9bccf70c
A
1161 unsigned int mask;
1162
1163 for (mask = 1; mask != 0; mask = mask << 1) {
1164 if (*start == original_start)
1165 break;
1166 if (!(start_cache & mask))
1167 *start += PAGE_SIZE_64;
1168 else
1169 break;
1170 }
1171 }
483a1d10 1172 if (end_cache != 0) {
9bccf70c
A
1173 unsigned int mask;
1174
1175 for (mask = 0x80000000;
1176 mask != 0; mask = mask >> 1) {
1177 if (*end == original_end)
1178 break;
483a1d10 1179 if (!(end_cache & mask))
9bccf70c
A
1180 *end -= PAGE_SIZE_64;
1181 else
1182 break;
1183 }
1184 }
90556fb8 1185 tws_unlock(tws);
483a1d10
A
1186
1187 if (*end < original_end)
1188 *end = original_end;
9bccf70c
A
1189 return;
1190 }
0b4e3aa0
A
1191 }
1192
1193 while ((length < max_length) &&
1194 (object_size >=
765c9de3 1195 (after + PAGE_SIZE_64))) {
9bccf70c 1196 if(length >= pre_heat_size) {
90556fb8 1197 if(tws_internal_lookup(tws, after, object,
0b4e3aa0
A
1198 &line) != KERN_SUCCESS) {
1199 vm_object_offset_t extend;
1200
1201 extend = after + PAGE_SIZE_64;
90556fb8 1202 if(tws_internal_lookup(tws, extend, object,
0b4e3aa0
A
1203 &line) != KERN_SUCCESS) {
1204 break;
1205 }
9bccf70c
A
1206 }
1207 }
1208
1209 if ((object->existence_map != NULL)
1210 && (!LOOK_FOR(object, after))) {
0b4e3aa0 1211 break;
9bccf70c
A
1212 }
1213
1214 if (vm_page_lookup(object, after) != VM_PAGE_NULL) {
55e303ae
A
1215 /*
1216 * don't bridge resident pages
1217 */
1218 break;
0b4e3aa0 1219 }
9bccf70c 1220
0b4e3aa0
A
1221 if (object->internal) {
1222 /*
1223 * need to acquire a real page in
1224 * advance because this acts as
1225 * a throttling mechanism for
1226 * data_requests to the default
1227 * pager. If this fails, give up
1228 * trying to find any more pages
1229 * in the cluster and send off the
1230 * request for what we already have.
1231 */
1232 if ((m = vm_page_grab()) == VM_PAGE_NULL) {
1233 break;
1234 }
1235 } else if ((m = vm_page_grab_fictitious())
1236 == VM_PAGE_NULL) {
1237 break;
1238 }
1239 m->absent = TRUE;
1240 m->unusual = TRUE;
1241 m->clustered = TRUE;
1242 m->list_req_pending = TRUE;
1243
1244 vm_page_insert(m, object, after);
1245 object->absent_count++;
1246 after += PAGE_SIZE_64;
1247 length += PAGE_SIZE;
1248 }
1249 *end = after;
1250 while (length < max_length) {
1251 if (before == 0)
1252 break;
1253 before -= PAGE_SIZE_64;
1254
9bccf70c 1255 if(length >= pre_heat_size) {
90556fb8 1256 if(tws_internal_lookup(tws, before, object,
0b4e3aa0
A
1257 &line) != KERN_SUCCESS) {
1258 vm_object_offset_t extend;
1259
1260 extend = before;
1261 if (extend == 0)
1262 break;
1263 extend -= PAGE_SIZE_64;
90556fb8 1264 if(tws_internal_lookup(tws, extend, object,
0b4e3aa0
A
1265 &line) != KERN_SUCCESS) {
1266 break;
1267 }
9bccf70c
A
1268 }
1269 }
1270 if ((object->existence_map != NULL)
1271 && (!LOOK_FOR(object, before))) {
0b4e3aa0
A
1272 break;
1273 }
9bccf70c
A
1274
1275 if (vm_page_lookup(object, before) != VM_PAGE_NULL) {
55e303ae
A
1276 /*
1277 * don't bridge resident pages
1278 */
1279 break;
9bccf70c
A
1280 }
1281
0b4e3aa0
A
1282 if (object->internal) {
1283 /*
1284 * need to acquire a real page in
1285 * advance because this acts as
1286 * a throttling mechanism for
1287 * data_requests to the default
1288 * pager. If this fails, give up
1289 * trying to find any more pages
1290 * in the cluster and send off the
1291 * request for what we already have.
1292 */
1293 if ((m = vm_page_grab()) == VM_PAGE_NULL) {
1294 break;
1295 }
1296 } else if ((m = vm_page_grab_fictitious())
1297 == VM_PAGE_NULL) {
1298 break;
1299 }
1300 m->absent = TRUE;
1301 m->unusual = TRUE;
1302 m->clustered = TRUE;
1303 m->list_req_pending = TRUE;
1304
1305 vm_page_insert(m, object, before);
1306 object->absent_count++;
1307 *start -= PAGE_SIZE_64;
1308 length += PAGE_SIZE;
1309 }
90556fb8 1310 tws_unlock(tws);
0b4e3aa0
A
1311}
1312
91447636 1313void
0b4e3aa0
A
1314tws_line_signal(
1315 tws_hash_t tws,
1316 vm_map_t map,
1317 tws_hash_line_t hash_line,
1318 vm_offset_t target_page)
1319{
1320 unsigned int i,j;
1321 vm_object_t object;
1322 vm_object_offset_t offset;
1323 vm_object_offset_t before;
1324 vm_object_offset_t after;
1325 struct tws_hash_ele *element;
1326 vm_page_t m,p;
1327 kern_return_t rc;
1328
1329 if(tws->style != TWS_HASH_STYLE_SIGNAL)
1330 return;
1331
1332 vm_map_lock(map);
1333 for (i=0; i<tws->number_of_elements; i++) {
1334
1335 vm_object_offset_t local_off = 0;
1336
1337 if(hash_line->list[i].object == 0)
1338 continue;
1339
1340 element = &hash_line->list[i];
1341
1342 if (element->page_addr == target_page)
1343 continue;
1344
1345 j = 1;
1346 while (j != 0) {
1347 if(j & element->page_cache)
1348 break;
91447636 1349 j <<= 1;
0b4e3aa0
A
1350 local_off += PAGE_SIZE_64;
1351 }
1352 object = element->object;
1353 offset = element->offset + local_off;
1354
1355 /* first try a fast test to speed up no-op signal */
1356 if (((p = vm_page_lookup(object, offset)) != NULL)
1357 || (object->pager == NULL)
1358 || (object->shadow_severed)) {
1359 continue;
1360 }
1361
1362 if((!object->alive) ||
1363 (!object->pager_created) || (!object->pager_ready))
1364 continue;
1365
1366 if (object->internal) {
1367 if (object->existence_map == NULL) {
1368 if (object->shadow)
1369 continue;
1370 } else {
1371 if(!LOOK_FOR(object, offset))
1372 continue;
1373 }
1374 }
1375
1376 vm_object_reference(object);
1377 vm_map_unlock(map);
1378
1379 if(object->internal) {
1380 m = vm_page_grab();
1381 } else {
1382 m = vm_page_grab_fictitious();
1383 }
1384
1385 if(m == NULL) {
1386 vm_object_deallocate(object);
1387 vm_map_lock(map);
1388 continue;
1389 }
1390
1391 vm_object_lock(object);
1392 if (((p = vm_page_lookup(object, offset)) != NULL)
1393 || (object->pager == NULL)
1394 || (object->shadow_severed)) {
1395 VM_PAGE_FREE(m);
1396 vm_object_unlock(object);
1397 vm_object_deallocate(object);
1398 vm_map_lock(map);
1399 continue;
1400 }
1401
1402 vm_page_insert(m, object, offset);
1403
1404 if (object->absent_count > vm_object_absent_max) {
1405 VM_PAGE_FREE(m);
1406 vm_object_unlock(object);
1407 vm_object_deallocate(object);
1408 vm_map_lock(map);
1409 break;
1410 }
1411 m->list_req_pending = TRUE;
1412 m->absent = TRUE;
1413 m->unusual = TRUE;
1414 object->absent_count++;
1415
1416 before = offset;
1417 after = offset + PAGE_SIZE_64;
1418 tws_build_cluster(tws, object, &before, &after, 0x16000);
1419 vm_object_unlock(object);
1420
1421 rc = memory_object_data_request(object->pager,
1422 before + object->paging_offset,
1423 (vm_size_t)(after - before), VM_PROT_READ);
1424 if (rc != KERN_SUCCESS) {
1425 offset = before;
1426 vm_object_lock(object);
1427 while (offset < after) {
1428 m = vm_page_lookup(object, offset);
1429 if(m && m->absent && m->busy)
1430 VM_PAGE_FREE(m);
1431 offset += PAGE_SIZE;
1432 }
1433 vm_object_unlock(object);
1434 vm_object_deallocate(object);
1435 } else {
1436 vm_object_deallocate(object);
1437 }
1438 vm_map_lock(map);
1439 continue;
1440 }
1441 vm_map_unlock(map);
1442}
1443
9bccf70c
A
1444/* tws locked on entry */
1445
1446tws_startup_t
1447tws_create_startup_list(
1448 tws_hash_t tws)
1449{
1450
1451 tws_startup_t startup;
1452 unsigned int i,j,k;
1453 unsigned int total_elements;
1454 unsigned int startup_size;
1455 unsigned int sindex;
1456 unsigned int hash_index;
1457 tws_startup_ptr_t element;
1458
1459 total_elements = tws->expansion_count *
1460 (tws->number_of_lines * tws->number_of_elements);
1461
1462 startup_size = sizeof(struct tws_startup)
1463 + (total_elements * sizeof(tws_startup_ptr_t *))
1464 + (total_elements * sizeof(struct tws_startup_ptr))
1465 + (total_elements * sizeof(struct tws_startup_ele));
1466 startup = (tws_startup_t)(kalloc(startup_size));
1467
1468 if(startup == NULL)
1469 return startup;
1470
1471 bzero((char *) startup, startup_size);
1472
1473 startup->table = (tws_startup_ptr_t *)
1474 (((int)startup) + (sizeof(struct tws_startup)));
1475 startup->ele = (struct tws_startup_ptr *)
1476 (((vm_offset_t)startup->table) +
1477 (total_elements * sizeof(tws_startup_ptr_t)));
1478
1479 startup->array = (struct tws_startup_ele *)
1480 (((vm_offset_t)startup->ele) +
1481 (total_elements * sizeof(struct tws_startup_ptr)));
1482
1483 startup->tws_hash_size = startup_size;
1484 startup->ele_count = 0; /* burn first hash ele, else we can't tell from zero */
1485 startup->array_size = total_elements;
1486 startup->hash_count = 1;
1487
1488 sindex = 0;
1489
1490
1491 for(i = 0; i<tws->number_of_lines; i++) {
1492 for(j = 0; j<tws->number_of_elements; j++) {
1493 for(k = 0; k<tws->expansion_count; k++) {
1494 tws_hash_ele_t entry;
1495 unsigned int hash_retry;
1496 vm_offset_t addr;
1497
1498 entry = &tws->cache[k][i].list[j];
1499 addr = entry->page_addr;
1500 hash_retry = 0;
1501 if(entry->object != 0) {
1502 /* get a hash element */
1503 hash_index = do_startup_hash(addr,
1504 startup->array_size);
1505
1506 if(startup->hash_count < total_elements) {
1507 element = &(startup->ele[startup->hash_count]);
1508 startup->hash_count += 1;
1509 } else {
1510 /* exit we're out of elements */
1511 break;
1512 }
1513 /* place the hash element */
1514 element->next = startup->table[hash_index];
1515 startup->table[hash_index] = (tws_startup_ptr_t)
1516 ((int)element - (int)&startup->ele[0]);
1517
1518 /* set entry OFFSET in hash element */
1519 element->element = (tws_startup_ele_t)
1520 ((int)&startup->array[sindex] -
1521 (int)&startup->array[0]);
1522
1523 startup->array[sindex].page_addr = entry->page_addr;
1524 startup->array[sindex].page_cache = entry->page_cache;
1525 startup->ele_count++;
1526 sindex++;
1527
1528 }
1529 }
1530 }
1531 }
1532
1533 return startup;
1534}
1535
1536
1537/*
1538 * Returns an entire cache line. The line is deleted from the startup
1539 * cache on return. The caller can check startup->ele_count for an empty
1540 * list. Access synchronization is the responsibility of the caller.
1541 */
1542
1543unsigned int
1544tws_startup_list_lookup(
1545 tws_startup_t startup,
1546 vm_offset_t addr)
1547{
1548 unsigned int hash_index;
1549 unsigned int page_cache_bits;
1550 unsigned int startup_shift;
1551 tws_startup_ele_t entry;
1552 vm_offset_t next_addr;
1553 tws_startup_ptr_t element;
1554 tws_startup_ptr_t base_ele;
1555 tws_startup_ptr_t *previous_ptr;
1556
1557 page_cache_bits = 0;
1558
1559 hash_index = do_startup_hash(addr, startup->array_size);
1560
483a1d10 1561 if(((unsigned int)&(startup->table[hash_index])) >= ((unsigned int)startup + startup->tws_hash_size)) {
9bccf70c
A
1562 return page_cache_bits = 0;
1563 }
1564 element = (tws_startup_ptr_t)((int)startup->table[hash_index] +
1565 (int)&startup->ele[0]);
1566 base_ele = element;
1567 previous_ptr = &(startup->table[hash_index]);
1568 while(element > &startup->ele[0]) {
1569 if (((int)element + sizeof(struct tws_startup_ptr))
1570 > ((int)startup + startup->tws_hash_size)) {
1571 return page_cache_bits;
1572 }
1573 entry = (tws_startup_ele_t)
1574 ((int)element->element
1575 + (int)&startup->array[0]);
1576 if((((int)entry + sizeof(struct tws_startup_ele))
1577 > ((int)startup + startup->tws_hash_size))
1578 || ((int)entry < (int)startup)) {
1579 return page_cache_bits;
1580 }
1581 if ((addr >= entry->page_addr) &&
1582 (addr <= (entry->page_addr + 0x1F000))) {
1583 startup_shift = (addr - entry->page_addr)>>12;
1584 page_cache_bits |= entry->page_cache >> startup_shift;
1585 /* don't dump the pages, unless the addresses */
1586 /* line up perfectly. The cache may be used */
1587 /* by other mappings */
1588 entry->page_cache &= (1 << startup_shift) - 1;
1589 if(addr == entry->page_addr) {
1590 if(base_ele == element) {
1591 base_ele = (tws_startup_ptr_t)
1592 ((int)element->next
1593 + (int)&startup->ele[0]);
1594 startup->table[hash_index] = element->next;
1595 element = base_ele;
1596 } else {
1597 *previous_ptr = element->next;
1598 element = (tws_startup_ptr_t)
1599 ((int)*previous_ptr
1600 + (int)&startup->ele[0]);
1601 }
1602 entry->page_addr = 0;
1603 startup->ele_count--;
1604 continue;
1605 }
1606 }
1607 next_addr = addr + 0x1F000;
1608 if ((next_addr >= entry->page_addr) &&
1609 (next_addr <= (entry->page_addr + 0x1F000))) {
1610 startup_shift = (next_addr - entry->page_addr)>>12;
1611 page_cache_bits |= entry->page_cache << (0x1F - startup_shift);
1612 entry->page_cache &= ~((1 << (startup_shift + 1)) - 1);
1613 if(entry->page_cache == 0) {
1614 if(base_ele == element) {
1615 base_ele = (tws_startup_ptr_t)
1616 ((int)element->next
1617 + (int)&startup->ele[0]);
1618 startup->table[hash_index] = element->next;
1619 element = base_ele;
1620 } else {
1621 *previous_ptr = element->next;
1622 element = (tws_startup_ptr_t)
1623 ((int)*previous_ptr
1624 + (int)&startup->ele[0]);
1625 }
1626 entry->page_addr = 0;
1627 startup->ele_count--;
1628 continue;
1629 }
1630 }
1631 previous_ptr = &(element->next);
1632 element = (tws_startup_ptr_t)
1633 ((int) element->next + (int) &startup->ele[0]);
1634 }
1635
1636 return page_cache_bits;
1637}
1638
1639kern_return_t
1640tws_send_startup_info(
1641 task_t task)
1642{
1643
1644 tws_hash_t tws;
9bccf70c
A
1645
1646 task_lock(task);
91447636 1647 tws = task->dynamic_working_set;
9bccf70c
A
1648 task_unlock(task);
1649 if(tws == NULL) {
1650 return KERN_FAILURE;
1651 }
1652 return tws_internal_startup_send(tws);
1653}
1654
1655
1656kern_return_t
1657tws_internal_startup_send(
1658 tws_hash_t tws)
1659{
1660
1661 tws_startup_t scache;
1662
1663 if(tws == NULL) {
1664 return KERN_FAILURE;
1665 }
1666 tws_lock(tws);
1667 /* used to signal write or release depending on state of tws */
1668 if(tws->startup_cache) {
1669 vm_offset_t startup_buf;
1670 vm_size_t size;
1671 startup_buf = (vm_offset_t)tws->startup_cache;
1672 size = tws->startup_cache->tws_hash_size;
1673 tws->startup_cache = 0;
1674 tws_unlock(tws);
1675 kmem_free(kernel_map, startup_buf, size);
1676 return KERN_SUCCESS;
1677 }
1678 if(tws->startup_name == NULL) {
1679 tws_unlock(tws);
1680 return KERN_FAILURE;
1681 }
1682 scache = tws_create_startup_list(tws);
1683 if(scache == NULL)
1684 return KERN_FAILURE;
1685 bsd_write_page_cache_file(tws->uid, tws->startup_name,
91447636
A
1686 (caddr_t) scache, scache->tws_hash_size,
1687 tws->mod, tws->fid);
1688 kfree(scache, scache->tws_hash_size);
1689 kfree(tws->startup_name, tws->startup_name_length);
9bccf70c
A
1690 tws->startup_name = NULL;
1691 tws_unlock(tws);
1692 return KERN_SUCCESS;
1693}
1694
1695kern_return_t
1696tws_handle_startup_file(
1697 task_t task,
1698 unsigned int uid,
1699 char *app_name,
91447636 1700 void *app_vp,
9bccf70c
A
1701 boolean_t *new_info)
1702
1703{
1704 tws_startup_t startup;
1705 vm_offset_t cache_size;
1706 kern_return_t error;
1707 int fid;
1708 int mod;
1709
1710 *new_info = FALSE;
1711 /* don't pre-heat kernel task */
1712 if(task == kernel_task)
1713 return KERN_SUCCESS;
1714 error = bsd_read_page_cache_file(uid, &fid,
1715 &mod, app_name,
91447636
A
1716 app_vp,
1717 (vm_offset_t *) &startup,
9bccf70c
A
1718 &cache_size);
1719 if(error) {
1720 return KERN_FAILURE;
1721 }
1722 if(startup == NULL) {
1723 /* Entry for app does not exist, make */
1724 /* one */
1725 /* we will want our own copy of the shared */
1726 /* regions to pick up a true picture of all */
1727 /* the pages we will touch. */
1728 if((lsf_zone->count * lsf_zone->elem_size)
1729 > (lsf_zone->max_size >> 1)) {
1730 /* We don't want to run out of shared memory */
1731 /* map entries by starting too many private versions */
1732 /* of the shared library structures */
1733 return KERN_SUCCESS;
1734 }
1735 *new_info = TRUE;
55e303ae 1736
9bccf70c
A
1737 error = tws_write_startup_file(task,
1738 fid, mod, app_name, uid);
1739 if(error)
1740 return error;
9bccf70c
A
1741
1742 } else {
1743 error = tws_read_startup_file(task,
1744 (tws_startup_t)startup,
1745 cache_size);
1746 if(error) {
1747 kmem_free(kernel_map,
1748 (vm_offset_t)startup, cache_size);
1749 return error;
1750 }
1751 }
1752 return KERN_SUCCESS;
1753}
1754
1755kern_return_t
1756tws_write_startup_file(
1757 task_t task,
1758 int fid,
1759 int mod,
1760 char *name,
1761 unsigned int uid)
1762{
1763 tws_hash_t tws;
1764 unsigned int string_length;
1765
1766 string_length = strlen(name);
1767
91447636 1768restart:
9bccf70c 1769 task_lock(task);
91447636 1770 tws = task->dynamic_working_set;
9bccf70c 1771 task_unlock(task);
91447636 1772
9bccf70c 1773 if(tws == NULL) {
91447636
A
1774 kern_return_t error;
1775
9bccf70c 1776 /* create a dynamic working set of normal size */
91447636
A
1777 if((error = task_working_set_create(task, 0, 0, TWS_HASH_STYLE_DEFAULT)) != KERN_SUCCESS)
1778 return error;
1779 /* we need to reset tws and relock */
1780 goto restart;
9bccf70c
A
1781 }
1782 tws_lock(tws);
1783
1784 if(tws->startup_name != NULL) {
1785 tws_unlock(tws);
1786 return KERN_FAILURE;
1787 }
1788
1789 tws->startup_name = (char *)
1790 kalloc((string_length + 1) * (sizeof(char)));
1791 if(tws->startup_name == NULL) {
1792 tws_unlock(tws);
1793 return KERN_FAILURE;
1794 }
1795
1796 bcopy(name, (char *)tws->startup_name, string_length + 1);
1797 tws->startup_name_length = (string_length + 1) * sizeof(char);
1798 tws->uid = uid;
1799 tws->fid = fid;
1800 tws->mod = mod;
1801
1802 tws_unlock(tws);
1803 return KERN_SUCCESS;
1804}
1805
91447636
A
1806unsigned long tws_read_startup_file_rejects = 0;
1807
9bccf70c
A
1808kern_return_t
1809tws_read_startup_file(
1810 task_t task,
1811 tws_startup_t startup,
1812 vm_offset_t cache_size)
1813{
1814 tws_hash_t tws;
9bccf70c
A
1815 int lines;
1816 int old_exp_count;
91447636 1817 unsigned int ele_count;
9bccf70c 1818
91447636 1819restart:
9bccf70c 1820 task_lock(task);
91447636
A
1821 tws = task->dynamic_working_set;
1822
1823 /* create a dynamic working set to match file size */
9bccf70c 1824
91447636
A
1825 /* start with total size of the data we got from app_profile */
1826 ele_count = cache_size;
1827 /* skip the startup header */
1828 ele_count -= sizeof(struct tws_startup);
1829 /*
1830 * For each startup cache entry, we have one of these:
1831 * tws_startup_ptr_t startup->table[];
1832 * struct tws_startup_ptr startup->ele[];
1833 * struct tws_startup_ele startup->array[];
1834 */
1835 ele_count /= (sizeof (tws_startup_ptr_t) +
1836 sizeof (struct tws_startup_ptr) +
1837 sizeof (struct tws_startup_ele));
1838
1839 /*
1840 * Sanity check: make sure the value for startup->array_size
1841 * that we read from the app_profile file matches the size
1842 * of the data we read from disk. If it doesn't match, we
1843 * can't trust the data and we just drop it all.
1844 */
1845 if (cache_size < sizeof(struct tws_startup) ||
1846 startup->array_size != ele_count) {
1847 tws_read_startup_file_rejects++;
9bccf70c
A
1848 task_unlock(task);
1849 kmem_free(kernel_map, (vm_offset_t)startup, cache_size);
1850 return(KERN_SUCCESS);
1851 }
1852
91447636
A
1853 /*
1854 * We'll create the task working set with the default row size
1855 * (TWS_ARRAY_SIZE), so this will give us the number of lines
1856 * we need to store all the data from the app_profile startup
1857 * cache.
1858 */
1859 lines = ele_count / TWS_ARRAY_SIZE;
1860
9bccf70c
A
1861 if(lines <= TWS_SMALL_HASH_LINE_COUNT) {
1862 lines = TWS_SMALL_HASH_LINE_COUNT;
1863 task_unlock(task);
1864 kmem_free(kernel_map, (vm_offset_t)startup, cache_size);
1865 return(KERN_SUCCESS);
1866 } else {
1867 old_exp_count = lines/TWS_HASH_LINE_COUNT;
1868 if((old_exp_count * TWS_HASH_LINE_COUNT) != lines) {
1869 lines = (old_exp_count + 1)
1870 * TWS_HASH_LINE_COUNT;
1871 }
1872 if(tws == NULL) {
91447636
A
1873 kern_return_t error;
1874
9bccf70c 1875 task_unlock(task);
91447636
A
1876 if ((error = task_working_set_create(task, lines, 0, TWS_HASH_STYLE_DEFAULT)) != KERN_SUCCESS)
1877 return error;
1878 /* we need to reset tws and relock */
1879 goto restart;
9bccf70c
A
1880 } else {
1881 task_unlock(task);
1882 tws_expand_working_set(
91447636 1883 (void *)tws, lines, TRUE);
9bccf70c
A
1884 }
1885 }
1886
9bccf70c
A
1887 tws_lock(tws);
1888
1889 if(tws->startup_cache != NULL) {
1890 tws_unlock(tws);
1891 return KERN_FAILURE;
1892 }
1893
1894
1895 /* now need to fix up internal table pointers */
1896 startup->table = (tws_startup_ptr_t *)
1897 (((int)startup) + (sizeof(struct tws_startup)));
1898 startup->ele = (struct tws_startup_ptr *)
1899 (((vm_offset_t)startup->table) +
1900 (startup->array_size * sizeof(tws_startup_ptr_t)));
1901 startup->array = (struct tws_startup_ele *)
1902 (((vm_offset_t)startup->ele) +
1903 (startup->array_size * sizeof(struct tws_startup_ptr)));
1904 /* the allocation size and file size should be the same */
1905 /* just in case their not, make sure we dealloc correctly */
1906 startup->tws_hash_size = cache_size;
1907
9bccf70c
A
1908 tws->startup_cache = startup;
1909 tws_unlock(tws);
1910 return KERN_SUCCESS;
1911}
0b4e3aa0
A
1912
1913
90556fb8
A
1914void
1915tws_hash_ws_flush(tws_hash_t tws) {
1916 tws_startup_t scache;
1917 if(tws == NULL) {
1918 return;
1919 }
1920 tws_lock(tws);
1921 if(tws->startup_name != NULL) {
1922 scache = tws_create_startup_list(tws);
1923 if(scache == NULL) {
1924 /* dump the name cache, we'll */
1925 /* get it next time */
91447636 1926 kfree(tws->startup_name, tws->startup_name_length);
90556fb8
A
1927 tws->startup_name = NULL;
1928 tws_unlock(tws);
1929 return;
1930 }
1931 bsd_write_page_cache_file(tws->uid, tws->startup_name,
91447636 1932 (caddr_t) scache, scache->tws_hash_size,
90556fb8 1933 tws->mod, tws->fid);
91447636
A
1934 kfree(scache, scache->tws_hash_size);
1935 kfree(tws->startup_name, tws->startup_name_length);
90556fb8
A
1936 tws->startup_name = NULL;
1937 }
1938 tws_unlock(tws);
1939 return;
1940}
1941
0b4e3aa0
A
1942void
1943tws_hash_destroy(tws_hash_t tws)
1944{
91447636 1945 unsigned int i,k;
0b4e3aa0 1946
9bccf70c
A
1947 if(tws->startup_cache != NULL) {
1948 kmem_free(kernel_map,
1949 (vm_offset_t)tws->startup_cache,
1950 tws->startup_cache->tws_hash_size);
1951 tws->startup_cache = NULL;
1952 }
1953 if(tws->startup_name != NULL) {
1954 tws_internal_startup_send(tws);
1955 }
0b4e3aa0
A
1956 for (i=0; i<tws->number_of_lines; i++) {
1957 for(k=0; k<tws->expansion_count; k++) {
1958 /* clear the object refs */
91447636 1959 tws_hash_line_clear(tws, &(tws->cache[k][i]), NULL, FALSE);
0b4e3aa0
A
1960 }
1961 }
1962 i = 0;
1963 while (i < tws->expansion_count) {
1964
91447636
A
1965 kfree(tws->table[i],
1966 sizeof(tws_hash_ptr_t)
1967 * tws->number_of_lines
1968 * tws->number_of_elements);
1969 kfree(tws->table_ele[i],
1970 sizeof(struct tws_hash_ptr)
1971 * tws->number_of_lines
1972 * tws->number_of_elements);
1973 kfree(tws->alt_ele[i],
1974 sizeof(struct tws_hash_ptr)
1975 * tws->number_of_lines
1976 * tws->number_of_elements);
1977 kfree(tws->cache[i],
1978 sizeof(struct tws_hash_line) * tws->number_of_lines);
0b4e3aa0
A
1979 i++;
1980 }
9bccf70c 1981 if(tws->startup_name != NULL) {
91447636 1982 kfree(tws->startup_name, tws->startup_name_length);
9bccf70c 1983 }
91447636 1984 kfree(tws, sizeof(struct tws_hash));
0b4e3aa0
A
1985}
1986
1987void
1988tws_hash_clear(tws_hash_t tws)
1989{
91447636 1990 unsigned int i, k;
0b4e3aa0
A
1991
1992 for (i=0; i<tws->number_of_lines; i++) {
1993 for(k=0; k<tws->expansion_count; k++) {
1994 /* clear the object refs */
91447636 1995 tws_hash_line_clear(tws, &(tws->cache[k][i]), NULL, FALSE);
0b4e3aa0
A
1996 }
1997 }
1998}
1999
2000kern_return_t
2001task_working_set_create(
2002 task_t task,
2003 unsigned int lines,
2004 unsigned int rows,
2005 unsigned int style)
2006{
2007
2008 if (lines == 0) {
2009 lines = TWS_HASH_LINE_COUNT;
2010 }
2011 if (rows == 0) {
2012 rows = TWS_ARRAY_SIZE;
2013 }
2014 if (style == TWS_HASH_STYLE_DEFAULT) {
2015 style = TWS_HASH_STYLE_BASIC;
2016 }
2017 task_lock(task);
2018 if(task->dynamic_working_set != 0) {
2019 task_unlock(task);
2020 return(KERN_FAILURE);
91447636
A
2021 } else if((task->dynamic_working_set =
2022 tws_hash_create(lines, rows, style)) == 0) {
0b4e3aa0
A
2023 task_unlock(task);
2024 return(KERN_NO_SPACE);
2025 }
2026 task_unlock(task);
2027 return KERN_SUCCESS;
2028}
9bccf70c
A
2029
2030
2031/* Internal use only routines */
2032
2033
2034/*
2035 * internal sub-function for address space lookup
2036 * returns the target element and the address of the
2037 * previous pointer The previous pointer is the address
2038 * of the pointer pointing to the target element.
2039 * TWS must be locked
2040 */
2041
2042void
2043tws_traverse_address_hash_list (
2044 tws_hash_t tws,
2045 unsigned int index,
2046 vm_offset_t page_addr,
2047 vm_object_t object,
2048 vm_object_offset_t offset,
2049 vm_map_t map,
2050 tws_hash_ptr_t *target_ele,
2051 tws_hash_ptr_t **previous_ptr,
2052 tws_hash_ptr_t **free_list,
2053 unsigned int exclusive_addr)
2054{
91447636 2055 unsigned int k;
9bccf70c
A
2056 tws_hash_ptr_t cache_ele;
2057 tws_hash_ptr_t base_ele;
2058
2059 *target_ele = NULL;
2060 *previous_ptr = NULL;
2061
2062 for(k=0; k<tws->expansion_count; k++) {
2063 tws_hash_ele_t ele;
2064 cache_ele = tws->table[k][index];
2065 base_ele = cache_ele;
2066 *previous_ptr = (tws_hash_ptr_t *)&(tws->table[k][index]);
2067 while(cache_ele != NULL) {
2068 if(((unsigned int)
2069 cache_ele->element & TWS_ADDR_HASH) == 0) {
2070 *previous_ptr = (tws_hash_ptr_t *)&(cache_ele->next);
2071 cache_ele = cache_ele->next;
2072 continue;
2073 }
2074 ele = (tws_hash_ele_t)((unsigned int)
2075 cache_ele->element & ~TWS_ADDR_HASH);
2076 if ((ele == 0) || (ele->object == 0)) {
2077 /* A little clean-up of empty elements */
2078 cache_ele->element = 0;
2079 if(base_ele == cache_ele) {
2080 base_ele = cache_ele->next;
2081 tws->table[k][index] = cache_ele->next;
2082 cache_ele->next = tws->free_hash_ele[k];
2083 tws->free_hash_ele[k] = cache_ele;
2084 cache_ele = base_ele;
2085 } else {
2086 **previous_ptr = cache_ele->next;
2087 cache_ele->next = tws->free_hash_ele[k];
2088 tws->free_hash_ele[k] = cache_ele;
2089 cache_ele = **previous_ptr;
2090 }
2091 continue;
2092 }
2093
2094 if ((ele->page_addr <= page_addr)
2095 && (page_addr <= (ele->page_addr +
2096 (vm_offset_t)TWS_INDEX_MASK))
2097 && ((object == NULL)
2098 || ((object == ele->object)
2099 && (offset == ele->offset)
2100 && (map == ele->map)))) {
2101 if(exclusive_addr) {
2102 int delta;
2103 delta = ((page_addr - ele->page_addr)
2104 >> 12);
2105 if((1 << delta) & ele->page_cache) {
2106 /* We've found a match */
2107 *target_ele = cache_ele;
2108 *free_list =
2109 (tws_hash_ptr_t *)
2110 &(tws->free_hash_ele[k]);
2111 return;
2112 }
2113 } else {
2114 /* We've found a match */
2115 *target_ele = cache_ele;
2116 *free_list = (tws_hash_ptr_t *)
2117 &(tws->free_hash_ele[k]);
2118 return;
2119 }
2120 }
2121 *previous_ptr = (tws_hash_ptr_t *)&(cache_ele->next);
2122 cache_ele = cache_ele->next;
2123 }
2124 }
2125}
2126
2127
2128/*
2129 * internal sub-function for object space lookup
2130 * returns the target element and the address of the
2131 * previous pointer The previous pointer is the address
2132 * of the pointer pointing to the target element.
2133 * TWS must be locked
2134 */
2135
2136
2137void
2138tws_traverse_object_hash_list (
2139 tws_hash_t tws,
2140 unsigned int index,
2141 vm_object_t object,
2142 vm_object_offset_t offset,
91447636 2143 unsigned int pagemask,
9bccf70c
A
2144 tws_hash_ptr_t *target_ele,
2145 tws_hash_ptr_t **previous_ptr,
2146 tws_hash_ptr_t **free_list)
2147{
91447636 2148 unsigned int k;
9bccf70c
A
2149 tws_hash_ptr_t cache_ele;
2150 tws_hash_ptr_t base_ele;
2151
2152 *target_ele = NULL;
2153 *previous_ptr = NULL;
2154
2155 for(k=0; k<tws->expansion_count; k++) {
2156 cache_ele = tws->table[k][index];
2157 base_ele = cache_ele;
2158 *previous_ptr = &(tws->table[k][index]);
2159 while(cache_ele != NULL) {
2160 if((((unsigned int)cache_ele->element)
2161 & TWS_ADDR_HASH) != 0) {
2162 *previous_ptr = &(cache_ele->next);
2163 cache_ele = cache_ele->next;
2164 continue;
2165 }
2166 if ((cache_ele->element == 0) ||
2167 (cache_ele->element->object == 0)) {
2168 /* A little clean-up of empty elements */
2169 cache_ele->element = 0;
2170 if(base_ele == cache_ele) {
2171 base_ele = cache_ele->next;
2172 tws->table[k][index] = cache_ele->next;
2173 cache_ele->next = tws->free_hash_ele[k];
2174 tws->free_hash_ele[k] = cache_ele;
2175 cache_ele = tws->table[k][index];
2176 } else {
2177 **previous_ptr = cache_ele->next;
2178 cache_ele->next = tws->free_hash_ele[k];
2179 tws->free_hash_ele[k] = cache_ele;
2180 cache_ele = **previous_ptr;
2181 }
2182 continue;
2183 }
2184 if ((cache_ele->element->object == object)
2185 && (cache_ele->element->offset ==
2186 (offset - (offset & ~TWS_HASH_OFF_MASK)))) {
91447636
A
2187 if((cache_ele->element->page_cache & pagemask)
2188 || (pagemask == 0xFFFFFFFF)) {
9bccf70c
A
2189 /* We've found a match */
2190 *target_ele = cache_ele;
2191 *free_list = &(tws->free_hash_ele[k]);
2192 return;
2193 }
2194 }
2195 *previous_ptr = (tws_hash_ptr_t *)&(cache_ele->next);
2196 cache_ele = cache_ele->next;
2197 }
2198 }
2199}
2200
2201
2202/*
2203 * For a given object/offset, discover whether the indexed 32 page frame
2204 * containing the object/offset exists and if their are at least threshold
2205 * pages present. Returns true if population meets threshold.
2206 */
2207int
2208tws_test_for_community(
2209 tws_hash_t tws,
2210 vm_object_t object,
2211 vm_object_offset_t offset,
2212 unsigned int threshold,
91447636 2213 unsigned int *pagemask)
9bccf70c
A
2214{
2215 int index;
2216 tws_hash_ptr_t cache_ele;
2217 tws_hash_ptr_t *trailer;
2218 tws_hash_ptr_t *free_list;
2219 int community = 0;
2220
2221 index = do_tws_hash(object, offset,
2222 tws->number_of_elements, tws->number_of_lines);
2223 tws_traverse_object_hash_list(tws, index, object, offset, 0xFFFFFFFF,
2224 &cache_ele, &trailer, &free_list);
2225
2226 if(cache_ele != NULL) {
2227 int i;
91447636 2228 unsigned int ctr;
9bccf70c
A
2229 ctr = 0;
2230 for(i=1; i!=0; i=i<<1) {
2231 if(i & cache_ele->element->page_cache)
2232 ctr++;
2233 if(ctr == threshold) {
2234 community = 1;
91447636 2235 *pagemask = cache_ele->element->page_cache;
9bccf70c
A
2236 break;
2237 }
2238 }
2239 }
2240
2241 return community;
2242
2243}
2244
2245
2246/*
2247 * Gets new hash element for object hash from free pools
2248 * TWS must be locked
2249 */
2250
2251tws_hash_ptr_t
2252new_obj_hash(
2253 tws_hash_t tws,
2254 unsigned int set,
2255 unsigned int index)
2256{
2257 tws_hash_ptr_t element;
2258
2259 if(tws->obj_free_count[set] < tws->number_of_lines * tws->number_of_elements) {
2260 element = &(tws->table_ele[set][tws->obj_free_count[set]]);
2261 tws->obj_free_count[set]+=1;
2262 } else if(tws->free_hash_ele[set] == NULL) {
2263 return NULL;
2264 } else {
2265 element = tws->free_hash_ele[set];
2266 if(element == NULL)
2267 return element;
2268 tws->free_hash_ele[set] = tws->free_hash_ele[set]->next;
2269 }
2270 element->element = 0;
2271 element->next = tws->table[set][index];
2272 tws->table[set][index] = element;
2273 return element;
2274}
2275
2276/*
2277 * Gets new hash element for addr hash from free pools
2278 * TWS must be locked
2279 */
2280
2281tws_hash_ptr_t
2282new_addr_hash(
2283 tws_hash_t tws,
2284 unsigned int set,
2285 unsigned int index)
2286{
2287 tws_hash_ptr_t element;
2288
2289 if(tws->addr_free_count[set]
2290 < tws->number_of_lines * tws->number_of_elements) {
2291 element = &(tws->alt_ele[set][tws->addr_free_count[set]]);
2292 tws->addr_free_count[set]+=1;
2293 } else if(tws->free_hash_ele[set] == NULL) {
2294 return NULL;
2295 } else {
2296 element = tws->free_hash_ele[set];
2297 if(element == NULL)
2298 return element;
2299 tws->free_hash_ele[set] = tws->free_hash_ele[set]->next;
2300 }
2301 element->element = (tws_hash_ele_t)TWS_ADDR_HASH;
2302 element->next = tws->table[set][index];
2303 tws->table[set][index] = element;
2304 return element;
2305}