1*43a90889SApple OSS Distributions /*
2*43a90889SApple OSS Distributions * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
3*43a90889SApple OSS Distributions *
4*43a90889SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*43a90889SApple OSS Distributions *
6*43a90889SApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code
7*43a90889SApple OSS Distributions * as defined in and that are subject to the Apple Public Source License
8*43a90889SApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in
9*43a90889SApple OSS Distributions * compliance with the License. The rights granted to you under the License
10*43a90889SApple OSS Distributions * may not be used to create, or enable the creation or redistribution of,
11*43a90889SApple OSS Distributions * unlawful or unlicensed copies of an Apple operating system, or to
12*43a90889SApple OSS Distributions * circumvent, violate, or enable the circumvention or violation of, any
13*43a90889SApple OSS Distributions * terms of an Apple operating system software license agreement.
14*43a90889SApple OSS Distributions *
15*43a90889SApple OSS Distributions * Please obtain a copy of the License at
16*43a90889SApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*43a90889SApple OSS Distributions *
18*43a90889SApple OSS Distributions * The Original Code and all software distributed under the License are
19*43a90889SApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*43a90889SApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*43a90889SApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*43a90889SApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*43a90889SApple OSS Distributions * Please see the License for the specific language governing rights and
24*43a90889SApple OSS Distributions * limitations under the License.
25*43a90889SApple OSS Distributions *
26*43a90889SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*43a90889SApple OSS Distributions */
28*43a90889SApple OSS Distributions
29*43a90889SApple OSS Distributions #include <vm/vm_page_internal.h>
30*43a90889SApple OSS Distributions #include <vm/vm_object_internal.h>
31*43a90889SApple OSS Distributions #include <vm/vm_kern_xnu.h>
32*43a90889SApple OSS Distributions #include <vm/vm_pageout_xnu.h>
33*43a90889SApple OSS Distributions #include <vm/vm_phantom_cache_internal.h>
34*43a90889SApple OSS Distributions #include <vm/vm_compressor_internal.h>
35*43a90889SApple OSS Distributions #include <vm/vm_protos_internal.h>
36*43a90889SApple OSS Distributions
37*43a90889SApple OSS Distributions
38*43a90889SApple OSS Distributions uint32_t phantom_cache_eval_period_in_msecs = 250;
39*43a90889SApple OSS Distributions uint32_t phantom_cache_thrashing_threshold_ssd = 1000;
40*43a90889SApple OSS Distributions #if !XNU_TARGET_OS_OSX
41*43a90889SApple OSS Distributions uint32_t phantom_cache_thrashing_threshold = 500;
42*43a90889SApple OSS Distributions #else /* !XNU_TARGET_OS_OSX */
43*43a90889SApple OSS Distributions uint32_t phantom_cache_thrashing_threshold = 50;
44*43a90889SApple OSS Distributions #endif /* !XNU_TARGET_OS_OSX */
45*43a90889SApple OSS Distributions
46*43a90889SApple OSS Distributions /*
47*43a90889SApple OSS Distributions * Number of consecutive thrashing periods required before
48*43a90889SApple OSS Distributions * vm_phantom_cache_check_pressure() returns true.
49*43a90889SApple OSS Distributions */
50*43a90889SApple OSS Distributions #if !XNU_TARGET_OS_OSX
51*43a90889SApple OSS Distributions unsigned phantom_cache_contiguous_periods = 4;
52*43a90889SApple OSS Distributions #else /* !XNU_TARGET_OS_OSX */
53*43a90889SApple OSS Distributions unsigned phantom_cache_contiguous_periods = 2;
54*43a90889SApple OSS Distributions #endif /* !XNU_TARGET_OS_OSX */
55*43a90889SApple OSS Distributions
56*43a90889SApple OSS Distributions clock_sec_t pc_start_of_eval_period_sec = 0;
57*43a90889SApple OSS Distributions clock_nsec_t pc_start_of_eval_period_nsec = 0;
58*43a90889SApple OSS Distributions boolean_t pc_need_eval_reset = FALSE;
59*43a90889SApple OSS Distributions
60*43a90889SApple OSS Distributions /* One bit per recent sampling period. Bit 0 = current period. */
61*43a90889SApple OSS Distributions uint32_t pc_history = 0;
62*43a90889SApple OSS Distributions
63*43a90889SApple OSS Distributions uint32_t sample_period_ghost_added_count = 0;
64*43a90889SApple OSS Distributions uint32_t sample_period_ghost_added_count_ssd = 0;
65*43a90889SApple OSS Distributions uint32_t sample_period_ghost_found_count = 0;
66*43a90889SApple OSS Distributions uint32_t sample_period_ghost_found_count_ssd = 0;
67*43a90889SApple OSS Distributions
68*43a90889SApple OSS Distributions uint32_t vm_phantom_object_id = 1;
69*43a90889SApple OSS Distributions #define VM_PHANTOM_OBJECT_ID_AFTER_WRAP 1000000
70*43a90889SApple OSS Distributions
71*43a90889SApple OSS Distributions vm_ghost_t vm_phantom_cache;
72*43a90889SApple OSS Distributions uint32_t vm_phantom_cache_nindx = 1;
73*43a90889SApple OSS Distributions uint32_t vm_phantom_cache_num_entries = 0;
74*43a90889SApple OSS Distributions uint32_t vm_phantom_cache_size;
75*43a90889SApple OSS Distributions
76*43a90889SApple OSS Distributions typedef uint32_t vm_phantom_hash_entry_t;
77*43a90889SApple OSS Distributions vm_phantom_hash_entry_t *vm_phantom_cache_hash;
78*43a90889SApple OSS Distributions uint32_t vm_phantom_cache_hash_size;
79*43a90889SApple OSS Distributions uint32_t vm_ghost_hash_mask; /* Mask for hash function */
80*43a90889SApple OSS Distributions uint32_t vm_ghost_bucket_hash; /* Basic bucket hash */
81*43a90889SApple OSS Distributions
82*43a90889SApple OSS Distributions
83*43a90889SApple OSS Distributions int pg_masks[4] = {
84*43a90889SApple OSS Distributions 0x1, 0x2, 0x4, 0x8
85*43a90889SApple OSS Distributions };
86*43a90889SApple OSS Distributions
87*43a90889SApple OSS Distributions
88*43a90889SApple OSS Distributions #define vm_phantom_hash(obj_id, offset) (\
89*43a90889SApple OSS Distributions ( (natural_t)((uintptr_t)obj_id * vm_ghost_bucket_hash) + (offset ^ vm_ghost_bucket_hash)) & vm_ghost_hash_mask)
90*43a90889SApple OSS Distributions
91*43a90889SApple OSS Distributions
92*43a90889SApple OSS Distributions struct phantom_cache_stats {
93*43a90889SApple OSS Distributions uint32_t pcs_wrapped;
94*43a90889SApple OSS Distributions uint32_t pcs_added_page_to_entry;
95*43a90889SApple OSS Distributions uint32_t pcs_added_new_entry;
96*43a90889SApple OSS Distributions uint32_t pcs_replaced_entry;
97*43a90889SApple OSS Distributions
98*43a90889SApple OSS Distributions uint32_t pcs_lookup_found_page_in_cache;
99*43a90889SApple OSS Distributions uint32_t pcs_lookup_entry_not_in_cache;
100*43a90889SApple OSS Distributions uint32_t pcs_lookup_page_not_in_entry;
101*43a90889SApple OSS Distributions
102*43a90889SApple OSS Distributions uint32_t pcs_updated_phantom_state;
103*43a90889SApple OSS Distributions } phantom_cache_stats;
104*43a90889SApple OSS Distributions
105*43a90889SApple OSS Distributions
106*43a90889SApple OSS Distributions
107*43a90889SApple OSS Distributions void
vm_phantom_cache_init(void)108*43a90889SApple OSS Distributions vm_phantom_cache_init(void)
109*43a90889SApple OSS Distributions {
110*43a90889SApple OSS Distributions unsigned int num_entries;
111*43a90889SApple OSS Distributions unsigned int log1;
112*43a90889SApple OSS Distributions unsigned int size;
113*43a90889SApple OSS Distributions
114*43a90889SApple OSS Distributions if (!VM_CONFIG_COMPRESSOR_IS_ACTIVE) {
115*43a90889SApple OSS Distributions return;
116*43a90889SApple OSS Distributions }
117*43a90889SApple OSS Distributions #if !XNU_TARGET_OS_OSX
118*43a90889SApple OSS Distributions num_entries = (uint32_t)(((max_mem / PAGE_SIZE) / 10) / VM_GHOST_PAGES_PER_ENTRY);
119*43a90889SApple OSS Distributions #else /* !XNU_TARGET_OS_OSX */
120*43a90889SApple OSS Distributions num_entries = (uint32_t)(((max_mem / PAGE_SIZE) / 4) / VM_GHOST_PAGES_PER_ENTRY);
121*43a90889SApple OSS Distributions #endif /* !XNU_TARGET_OS_OSX */
122*43a90889SApple OSS Distributions vm_phantom_cache_num_entries = 1;
123*43a90889SApple OSS Distributions
124*43a90889SApple OSS Distributions while (vm_phantom_cache_num_entries < num_entries) {
125*43a90889SApple OSS Distributions vm_phantom_cache_num_entries <<= 1;
126*43a90889SApple OSS Distributions }
127*43a90889SApple OSS Distributions
128*43a90889SApple OSS Distributions /*
129*43a90889SApple OSS Distributions * We index this with g_next_index, so don't exceed the width of that bitfield.
130*43a90889SApple OSS Distributions */
131*43a90889SApple OSS Distributions if (vm_phantom_cache_num_entries > (1 << VM_GHOST_INDEX_BITS)) {
132*43a90889SApple OSS Distributions vm_phantom_cache_num_entries = (1 << VM_GHOST_INDEX_BITS);
133*43a90889SApple OSS Distributions }
134*43a90889SApple OSS Distributions
135*43a90889SApple OSS Distributions vm_phantom_cache_size = sizeof(struct vm_ghost) * vm_phantom_cache_num_entries;
136*43a90889SApple OSS Distributions vm_phantom_cache_hash_size = sizeof(vm_phantom_hash_entry_t) * vm_phantom_cache_num_entries;
137*43a90889SApple OSS Distributions
138*43a90889SApple OSS Distributions kmem_alloc(kernel_map, (vm_offset_t *)&vm_phantom_cache,
139*43a90889SApple OSS Distributions vm_phantom_cache_size,
140*43a90889SApple OSS Distributions KMA_DATA | KMA_NOFAIL | KMA_KOBJECT | KMA_ZERO | KMA_PERMANENT,
141*43a90889SApple OSS Distributions VM_KERN_MEMORY_PHANTOM_CACHE);
142*43a90889SApple OSS Distributions
143*43a90889SApple OSS Distributions kmem_alloc(kernel_map, (vm_offset_t *)&vm_phantom_cache_hash,
144*43a90889SApple OSS Distributions vm_phantom_cache_hash_size,
145*43a90889SApple OSS Distributions KMA_NOFAIL | KMA_KOBJECT | KMA_ZERO | KMA_PERMANENT,
146*43a90889SApple OSS Distributions VM_KERN_MEMORY_PHANTOM_CACHE);
147*43a90889SApple OSS Distributions
148*43a90889SApple OSS Distributions vm_ghost_hash_mask = vm_phantom_cache_num_entries - 1;
149*43a90889SApple OSS Distributions
150*43a90889SApple OSS Distributions /*
151*43a90889SApple OSS Distributions * Calculate object_id shift value for hashing algorithm:
152*43a90889SApple OSS Distributions * O = log2(sizeof(struct vm_object))
153*43a90889SApple OSS Distributions * B = log2(vm_page_bucket_count)
154*43a90889SApple OSS Distributions * hash shifts the object_id left by
155*43a90889SApple OSS Distributions * B/2 - O
156*43a90889SApple OSS Distributions */
157*43a90889SApple OSS Distributions size = vm_phantom_cache_num_entries;
158*43a90889SApple OSS Distributions for (log1 = 0; size > 1; log1++) {
159*43a90889SApple OSS Distributions size /= 2;
160*43a90889SApple OSS Distributions }
161*43a90889SApple OSS Distributions
162*43a90889SApple OSS Distributions vm_ghost_bucket_hash = 1 << ((log1 + 1) >> 1); /* Get (ceiling of sqrt of table size) */
163*43a90889SApple OSS Distributions vm_ghost_bucket_hash |= 1 << ((log1 + 1) >> 2); /* Get (ceiling of quadroot of table size) */
164*43a90889SApple OSS Distributions vm_ghost_bucket_hash |= 1; /* Set bit and add 1 - always must be 1 to insure unique series */
165*43a90889SApple OSS Distributions
166*43a90889SApple OSS Distributions if (vm_ghost_hash_mask & vm_phantom_cache_num_entries) {
167*43a90889SApple OSS Distributions printf("vm_phantom_cache_init: WARNING -- strange page hash\n");
168*43a90889SApple OSS Distributions }
169*43a90889SApple OSS Distributions }
170*43a90889SApple OSS Distributions
171*43a90889SApple OSS Distributions
172*43a90889SApple OSS Distributions void
vm_phantom_cache_add_ghost(vm_page_t m)173*43a90889SApple OSS Distributions vm_phantom_cache_add_ghost(vm_page_t m)
174*43a90889SApple OSS Distributions {
175*43a90889SApple OSS Distributions vm_ghost_t vpce;
176*43a90889SApple OSS Distributions vm_object_t object;
177*43a90889SApple OSS Distributions int ghost_index;
178*43a90889SApple OSS Distributions int pg_mask;
179*43a90889SApple OSS Distributions boolean_t isSSD = FALSE;
180*43a90889SApple OSS Distributions vm_phantom_hash_entry_t ghost_hash_index;
181*43a90889SApple OSS Distributions
182*43a90889SApple OSS Distributions object = VM_PAGE_OBJECT(m);
183*43a90889SApple OSS Distributions
184*43a90889SApple OSS Distributions LCK_MTX_ASSERT(&vm_page_queue_lock, LCK_MTX_ASSERT_OWNED);
185*43a90889SApple OSS Distributions vm_object_lock_assert_exclusive(object);
186*43a90889SApple OSS Distributions
187*43a90889SApple OSS Distributions if (vm_phantom_cache_num_entries == 0) {
188*43a90889SApple OSS Distributions return;
189*43a90889SApple OSS Distributions }
190*43a90889SApple OSS Distributions if (object->pager == MEMORY_OBJECT_NULL) {
191*43a90889SApple OSS Distributions /*
192*43a90889SApple OSS Distributions * This object must have lost its memory object due to a force-unmount
193*43a90889SApple OSS Distributions * or ungraft, for example; this page won't come back, so no need to
194*43a90889SApple OSS Distributions * track it.
195*43a90889SApple OSS Distributions */
196*43a90889SApple OSS Distributions return;
197*43a90889SApple OSS Distributions }
198*43a90889SApple OSS Distributions
199*43a90889SApple OSS Distributions pg_mask = pg_masks[(m->vmp_offset >> PAGE_SHIFT) & VM_GHOST_PAGE_MASK];
200*43a90889SApple OSS Distributions
201*43a90889SApple OSS Distributions if (object->phantom_object_id == 0) {
202*43a90889SApple OSS Distributions vnode_pager_get_isSSD(object->pager, &isSSD);
203*43a90889SApple OSS Distributions
204*43a90889SApple OSS Distributions if (isSSD == TRUE) {
205*43a90889SApple OSS Distributions object->phantom_isssd = TRUE;
206*43a90889SApple OSS Distributions }
207*43a90889SApple OSS Distributions
208*43a90889SApple OSS Distributions object->phantom_object_id = vm_phantom_object_id++;
209*43a90889SApple OSS Distributions
210*43a90889SApple OSS Distributions if (vm_phantom_object_id == 0) {
211*43a90889SApple OSS Distributions vm_phantom_object_id = VM_PHANTOM_OBJECT_ID_AFTER_WRAP;
212*43a90889SApple OSS Distributions }
213*43a90889SApple OSS Distributions } else {
214*43a90889SApple OSS Distributions if ((vpce = vm_phantom_cache_lookup_ghost(m, 0))) {
215*43a90889SApple OSS Distributions vpce->g_pages_held |= pg_mask;
216*43a90889SApple OSS Distributions
217*43a90889SApple OSS Distributions phantom_cache_stats.pcs_added_page_to_entry++;
218*43a90889SApple OSS Distributions goto done;
219*43a90889SApple OSS Distributions }
220*43a90889SApple OSS Distributions }
221*43a90889SApple OSS Distributions /*
222*43a90889SApple OSS Distributions * if we're here then the vm_ghost_t of this vm_page_t
223*43a90889SApple OSS Distributions * is not present in the phantom cache... take the next
224*43a90889SApple OSS Distributions * available entry in the LRU first evicting the existing
225*43a90889SApple OSS Distributions * entry if we've wrapped the ring
226*43a90889SApple OSS Distributions */
227*43a90889SApple OSS Distributions ghost_index = vm_phantom_cache_nindx++;
228*43a90889SApple OSS Distributions
229*43a90889SApple OSS Distributions if (vm_phantom_cache_nindx == vm_phantom_cache_num_entries) {
230*43a90889SApple OSS Distributions vm_phantom_cache_nindx = 1;
231*43a90889SApple OSS Distributions
232*43a90889SApple OSS Distributions phantom_cache_stats.pcs_wrapped++;
233*43a90889SApple OSS Distributions }
234*43a90889SApple OSS Distributions vpce = &vm_phantom_cache[ghost_index];
235*43a90889SApple OSS Distributions
236*43a90889SApple OSS Distributions if (vpce->g_obj_id) {
237*43a90889SApple OSS Distributions /*
238*43a90889SApple OSS Distributions * we're going to replace an existing entry
239*43a90889SApple OSS Distributions * so first remove it from the hash
240*43a90889SApple OSS Distributions */
241*43a90889SApple OSS Distributions vm_ghost_t nvpce;
242*43a90889SApple OSS Distributions
243*43a90889SApple OSS Distributions ghost_hash_index = vm_phantom_hash(vpce->g_obj_id, vpce->g_obj_offset);
244*43a90889SApple OSS Distributions
245*43a90889SApple OSS Distributions nvpce = &vm_phantom_cache[vm_phantom_cache_hash[ghost_hash_index]];
246*43a90889SApple OSS Distributions
247*43a90889SApple OSS Distributions if (nvpce == vpce) {
248*43a90889SApple OSS Distributions vm_phantom_cache_hash[ghost_hash_index] = vpce->g_next_index;
249*43a90889SApple OSS Distributions } else {
250*43a90889SApple OSS Distributions for (;;) {
251*43a90889SApple OSS Distributions if (nvpce->g_next_index == 0) {
252*43a90889SApple OSS Distributions panic("didn't find ghost in hash");
253*43a90889SApple OSS Distributions }
254*43a90889SApple OSS Distributions
255*43a90889SApple OSS Distributions if (&vm_phantom_cache[nvpce->g_next_index] == vpce) {
256*43a90889SApple OSS Distributions nvpce->g_next_index = vpce->g_next_index;
257*43a90889SApple OSS Distributions break;
258*43a90889SApple OSS Distributions }
259*43a90889SApple OSS Distributions nvpce = &vm_phantom_cache[nvpce->g_next_index];
260*43a90889SApple OSS Distributions }
261*43a90889SApple OSS Distributions }
262*43a90889SApple OSS Distributions phantom_cache_stats.pcs_replaced_entry++;
263*43a90889SApple OSS Distributions } else {
264*43a90889SApple OSS Distributions phantom_cache_stats.pcs_added_new_entry++;
265*43a90889SApple OSS Distributions }
266*43a90889SApple OSS Distributions
267*43a90889SApple OSS Distributions vpce->g_pages_held = pg_mask;
268*43a90889SApple OSS Distributions vpce->g_obj_offset = (m->vmp_offset >> (PAGE_SHIFT + VM_GHOST_PAGE_SHIFT)) & VM_GHOST_OFFSET_MASK;
269*43a90889SApple OSS Distributions vpce->g_obj_id = object->phantom_object_id;
270*43a90889SApple OSS Distributions
271*43a90889SApple OSS Distributions ghost_hash_index = vm_phantom_hash(vpce->g_obj_id, vpce->g_obj_offset);
272*43a90889SApple OSS Distributions vpce->g_next_index = vm_phantom_cache_hash[ghost_hash_index];
273*43a90889SApple OSS Distributions vm_phantom_cache_hash[ghost_hash_index] = ghost_index;
274*43a90889SApple OSS Distributions
275*43a90889SApple OSS Distributions done:
276*43a90889SApple OSS Distributions vm_pageout_vminfo.vm_phantom_cache_added_ghost++;
277*43a90889SApple OSS Distributions
278*43a90889SApple OSS Distributions if (object->phantom_isssd) {
279*43a90889SApple OSS Distributions OSAddAtomic(1, &sample_period_ghost_added_count_ssd);
280*43a90889SApple OSS Distributions } else {
281*43a90889SApple OSS Distributions OSAddAtomic(1, &sample_period_ghost_added_count);
282*43a90889SApple OSS Distributions }
283*43a90889SApple OSS Distributions }
284*43a90889SApple OSS Distributions
285*43a90889SApple OSS Distributions
286*43a90889SApple OSS Distributions vm_ghost_t
vm_phantom_cache_lookup_ghost(vm_page_t m,uint32_t pg_mask)287*43a90889SApple OSS Distributions vm_phantom_cache_lookup_ghost(vm_page_t m, uint32_t pg_mask)
288*43a90889SApple OSS Distributions {
289*43a90889SApple OSS Distributions uint64_t g_obj_offset;
290*43a90889SApple OSS Distributions uint32_t g_obj_id;
291*43a90889SApple OSS Distributions uint32_t ghost_index;
292*43a90889SApple OSS Distributions vm_object_t object;
293*43a90889SApple OSS Distributions
294*43a90889SApple OSS Distributions object = VM_PAGE_OBJECT(m);
295*43a90889SApple OSS Distributions
296*43a90889SApple OSS Distributions if ((g_obj_id = object->phantom_object_id) == 0) {
297*43a90889SApple OSS Distributions /*
298*43a90889SApple OSS Distributions * no entries in phantom cache for this object
299*43a90889SApple OSS Distributions */
300*43a90889SApple OSS Distributions return NULL;
301*43a90889SApple OSS Distributions }
302*43a90889SApple OSS Distributions g_obj_offset = (m->vmp_offset >> (PAGE_SHIFT + VM_GHOST_PAGE_SHIFT)) & VM_GHOST_OFFSET_MASK;
303*43a90889SApple OSS Distributions
304*43a90889SApple OSS Distributions ghost_index = vm_phantom_cache_hash[vm_phantom_hash(g_obj_id, g_obj_offset)];
305*43a90889SApple OSS Distributions
306*43a90889SApple OSS Distributions while (ghost_index) {
307*43a90889SApple OSS Distributions vm_ghost_t vpce;
308*43a90889SApple OSS Distributions
309*43a90889SApple OSS Distributions vpce = &vm_phantom_cache[ghost_index];
310*43a90889SApple OSS Distributions
311*43a90889SApple OSS Distributions if (vpce->g_obj_id == g_obj_id && vpce->g_obj_offset == g_obj_offset) {
312*43a90889SApple OSS Distributions if (pg_mask == 0 || (vpce->g_pages_held & pg_mask)) {
313*43a90889SApple OSS Distributions phantom_cache_stats.pcs_lookup_found_page_in_cache++;
314*43a90889SApple OSS Distributions
315*43a90889SApple OSS Distributions return vpce;
316*43a90889SApple OSS Distributions }
317*43a90889SApple OSS Distributions phantom_cache_stats.pcs_lookup_page_not_in_entry++;
318*43a90889SApple OSS Distributions
319*43a90889SApple OSS Distributions return NULL;
320*43a90889SApple OSS Distributions }
321*43a90889SApple OSS Distributions ghost_index = vpce->g_next_index;
322*43a90889SApple OSS Distributions }
323*43a90889SApple OSS Distributions phantom_cache_stats.pcs_lookup_entry_not_in_cache++;
324*43a90889SApple OSS Distributions
325*43a90889SApple OSS Distributions return NULL;
326*43a90889SApple OSS Distributions }
327*43a90889SApple OSS Distributions
328*43a90889SApple OSS Distributions
329*43a90889SApple OSS Distributions
330*43a90889SApple OSS Distributions void
vm_phantom_cache_update(vm_page_t m)331*43a90889SApple OSS Distributions vm_phantom_cache_update(vm_page_t m)
332*43a90889SApple OSS Distributions {
333*43a90889SApple OSS Distributions int pg_mask;
334*43a90889SApple OSS Distributions vm_ghost_t vpce;
335*43a90889SApple OSS Distributions vm_object_t object;
336*43a90889SApple OSS Distributions
337*43a90889SApple OSS Distributions object = VM_PAGE_OBJECT(m);
338*43a90889SApple OSS Distributions
339*43a90889SApple OSS Distributions LCK_MTX_ASSERT(&vm_page_queue_lock, LCK_MTX_ASSERT_OWNED);
340*43a90889SApple OSS Distributions vm_object_lock_assert_exclusive(object);
341*43a90889SApple OSS Distributions
342*43a90889SApple OSS Distributions if (vm_phantom_cache_num_entries == 0) {
343*43a90889SApple OSS Distributions return;
344*43a90889SApple OSS Distributions }
345*43a90889SApple OSS Distributions
346*43a90889SApple OSS Distributions pg_mask = pg_masks[(m->vmp_offset >> PAGE_SHIFT) & VM_GHOST_PAGE_MASK];
347*43a90889SApple OSS Distributions
348*43a90889SApple OSS Distributions if ((vpce = vm_phantom_cache_lookup_ghost(m, pg_mask))) {
349*43a90889SApple OSS Distributions vpce->g_pages_held &= ~pg_mask;
350*43a90889SApple OSS Distributions
351*43a90889SApple OSS Distributions phantom_cache_stats.pcs_updated_phantom_state++;
352*43a90889SApple OSS Distributions vm_pageout_vminfo.vm_phantom_cache_found_ghost++;
353*43a90889SApple OSS Distributions
354*43a90889SApple OSS Distributions if (object->phantom_isssd) {
355*43a90889SApple OSS Distributions OSAddAtomic(1, &sample_period_ghost_found_count_ssd);
356*43a90889SApple OSS Distributions } else {
357*43a90889SApple OSS Distributions OSAddAtomic(1, &sample_period_ghost_found_count);
358*43a90889SApple OSS Distributions }
359*43a90889SApple OSS Distributions }
360*43a90889SApple OSS Distributions }
361*43a90889SApple OSS Distributions
362*43a90889SApple OSS Distributions
363*43a90889SApple OSS Distributions #define PHANTOM_CACHE_DEBUG 1
364*43a90889SApple OSS Distributions
365*43a90889SApple OSS Distributions #if PHANTOM_CACHE_DEBUG
366*43a90889SApple OSS Distributions
367*43a90889SApple OSS Distributions int sample_period_ghost_counts_indx = 0;
368*43a90889SApple OSS Distributions
369*43a90889SApple OSS Distributions struct {
370*43a90889SApple OSS Distributions uint32_t added;
371*43a90889SApple OSS Distributions uint32_t found;
372*43a90889SApple OSS Distributions uint32_t added_ssd;
373*43a90889SApple OSS Distributions uint32_t found_ssd;
374*43a90889SApple OSS Distributions uint32_t elapsed_ms;
375*43a90889SApple OSS Distributions boolean_t pressure_detected;
376*43a90889SApple OSS Distributions } sample_period_ghost_counts[256];
377*43a90889SApple OSS Distributions
378*43a90889SApple OSS Distributions #endif
379*43a90889SApple OSS Distributions
380*43a90889SApple OSS Distributions /*
381*43a90889SApple OSS Distributions * Determine if the file cache is thrashing from sampling interval statistics.
382*43a90889SApple OSS Distributions *
383*43a90889SApple OSS Distributions * Pages added to the phantom cache = pages evicted from the file cache.
384*43a90889SApple OSS Distributions * Pages found in the phantom cache = reads of pages that were recently evicted.
385*43a90889SApple OSS Distributions * Threshold is the latency-dependent number of reads we consider thrashing.
386*43a90889SApple OSS Distributions */
387*43a90889SApple OSS Distributions static boolean_t
is_thrashing(uint32_t added,uint32_t found,uint32_t threshold)388*43a90889SApple OSS Distributions is_thrashing(uint32_t added, uint32_t found, uint32_t threshold)
389*43a90889SApple OSS Distributions {
390*43a90889SApple OSS Distributions /* Ignore normal activity below the threshold. */
391*43a90889SApple OSS Distributions if (added < threshold || found < threshold) {
392*43a90889SApple OSS Distributions return FALSE;
393*43a90889SApple OSS Distributions }
394*43a90889SApple OSS Distributions
395*43a90889SApple OSS Distributions /*
396*43a90889SApple OSS Distributions * When thrashing in a way that we can mitigate, most of the pages read
397*43a90889SApple OSS Distributions * into the file cache were recently evicted, and 'found' will be close
398*43a90889SApple OSS Distributions * to 'added'.
399*43a90889SApple OSS Distributions *
400*43a90889SApple OSS Distributions * When replacing the current working set because a new app is
401*43a90889SApple OSS Distributions * launched, we see very high read traffic with sporadic phantom cache
402*43a90889SApple OSS Distributions * hits.
403*43a90889SApple OSS Distributions *
404*43a90889SApple OSS Distributions * This is not thrashing, or freeing up memory wouldn't help much
405*43a90889SApple OSS Distributions * anyway.
406*43a90889SApple OSS Distributions */
407*43a90889SApple OSS Distributions if (found < added / 2) {
408*43a90889SApple OSS Distributions return FALSE;
409*43a90889SApple OSS Distributions }
410*43a90889SApple OSS Distributions
411*43a90889SApple OSS Distributions return TRUE;
412*43a90889SApple OSS Distributions }
413*43a90889SApple OSS Distributions
414*43a90889SApple OSS Distributions /*
415*43a90889SApple OSS Distributions * the following function is never called
416*43a90889SApple OSS Distributions * from multiple threads simultaneously due
417*43a90889SApple OSS Distributions * to a condition variable used to serialize
418*43a90889SApple OSS Distributions * at the compressor level... thus no need
419*43a90889SApple OSS Distributions * to provide locking for the sample processing
420*43a90889SApple OSS Distributions */
421*43a90889SApple OSS Distributions boolean_t
vm_phantom_cache_check_pressure()422*43a90889SApple OSS Distributions vm_phantom_cache_check_pressure()
423*43a90889SApple OSS Distributions {
424*43a90889SApple OSS Distributions clock_sec_t cur_ts_sec;
425*43a90889SApple OSS Distributions clock_nsec_t cur_ts_nsec;
426*43a90889SApple OSS Distributions uint64_t elapsed_msecs_in_eval;
427*43a90889SApple OSS Distributions boolean_t pressure_detected = FALSE;
428*43a90889SApple OSS Distributions
429*43a90889SApple OSS Distributions clock_get_system_nanotime(&cur_ts_sec, &cur_ts_nsec);
430*43a90889SApple OSS Distributions
431*43a90889SApple OSS Distributions elapsed_msecs_in_eval = vm_compressor_compute_elapsed_msecs(cur_ts_sec, cur_ts_nsec, pc_start_of_eval_period_sec, pc_start_of_eval_period_nsec);
432*43a90889SApple OSS Distributions
433*43a90889SApple OSS Distributions /*
434*43a90889SApple OSS Distributions * Reset evaluation period after phantom_cache_eval_period_in_msecs or
435*43a90889SApple OSS Distributions * whenever vm_phantom_cache_restart_sample has been called.
436*43a90889SApple OSS Distributions */
437*43a90889SApple OSS Distributions if (elapsed_msecs_in_eval >= phantom_cache_eval_period_in_msecs) {
438*43a90889SApple OSS Distributions pc_need_eval_reset = TRUE;
439*43a90889SApple OSS Distributions }
440*43a90889SApple OSS Distributions
441*43a90889SApple OSS Distributions if (pc_need_eval_reset == TRUE) {
442*43a90889SApple OSS Distributions #if PHANTOM_CACHE_DEBUG
443*43a90889SApple OSS Distributions /*
444*43a90889SApple OSS Distributions * maintain some info about the last 256 sample periods
445*43a90889SApple OSS Distributions */
446*43a90889SApple OSS Distributions sample_period_ghost_counts[sample_period_ghost_counts_indx].added = sample_period_ghost_added_count;
447*43a90889SApple OSS Distributions sample_period_ghost_counts[sample_period_ghost_counts_indx].found = sample_period_ghost_found_count;
448*43a90889SApple OSS Distributions sample_period_ghost_counts[sample_period_ghost_counts_indx].added_ssd = sample_period_ghost_added_count_ssd;
449*43a90889SApple OSS Distributions sample_period_ghost_counts[sample_period_ghost_counts_indx].found_ssd = sample_period_ghost_found_count_ssd;
450*43a90889SApple OSS Distributions sample_period_ghost_counts[sample_period_ghost_counts_indx].elapsed_ms = (uint32_t)elapsed_msecs_in_eval;
451*43a90889SApple OSS Distributions
452*43a90889SApple OSS Distributions sample_period_ghost_counts_indx++;
453*43a90889SApple OSS Distributions
454*43a90889SApple OSS Distributions if (sample_period_ghost_counts_indx >= 256) {
455*43a90889SApple OSS Distributions sample_period_ghost_counts_indx = 0;
456*43a90889SApple OSS Distributions }
457*43a90889SApple OSS Distributions #endif
458*43a90889SApple OSS Distributions sample_period_ghost_added_count = 0;
459*43a90889SApple OSS Distributions sample_period_ghost_found_count = 0;
460*43a90889SApple OSS Distributions sample_period_ghost_added_count_ssd = 0;
461*43a90889SApple OSS Distributions sample_period_ghost_found_count_ssd = 0;
462*43a90889SApple OSS Distributions
463*43a90889SApple OSS Distributions pc_start_of_eval_period_sec = cur_ts_sec;
464*43a90889SApple OSS Distributions pc_start_of_eval_period_nsec = cur_ts_nsec;
465*43a90889SApple OSS Distributions pc_history <<= 1;
466*43a90889SApple OSS Distributions pc_need_eval_reset = FALSE;
467*43a90889SApple OSS Distributions } else {
468*43a90889SApple OSS Distributions /*
469*43a90889SApple OSS Distributions * Since the trashing rate is really a function of the read latency of the disk
470*43a90889SApple OSS Distributions * we have to consider both the SSD and spinning disk case since the file cache
471*43a90889SApple OSS Distributions * could be backed by either or even both flavors. When the object is first
472*43a90889SApple OSS Distributions * assigned a phantom_object_id, we query the pager to determine if the backing
473*43a90889SApple OSS Distributions * backing media is an SSD and remember that answer in the vm_object. We use
474*43a90889SApple OSS Distributions * that info to maintains counts for both the SSD and spinning disk cases.
475*43a90889SApple OSS Distributions */
476*43a90889SApple OSS Distributions if (is_thrashing(sample_period_ghost_added_count,
477*43a90889SApple OSS Distributions sample_period_ghost_found_count,
478*43a90889SApple OSS Distributions phantom_cache_thrashing_threshold) ||
479*43a90889SApple OSS Distributions is_thrashing(sample_period_ghost_added_count_ssd,
480*43a90889SApple OSS Distributions sample_period_ghost_found_count_ssd,
481*43a90889SApple OSS Distributions phantom_cache_thrashing_threshold_ssd)) {
482*43a90889SApple OSS Distributions /* Thrashing in the current period: Set bit 0. */
483*43a90889SApple OSS Distributions pc_history |= 1;
484*43a90889SApple OSS Distributions }
485*43a90889SApple OSS Distributions }
486*43a90889SApple OSS Distributions
487*43a90889SApple OSS Distributions /*
488*43a90889SApple OSS Distributions * Declare pressure_detected after phantom_cache_contiguous_periods.
489*43a90889SApple OSS Distributions *
490*43a90889SApple OSS Distributions * Create a bitmask with the N low bits set. These bits must all be set
491*43a90889SApple OSS Distributions * in pc_history. The high bits of pc_history are ignored.
492*43a90889SApple OSS Distributions */
493*43a90889SApple OSS Distributions uint32_t bitmask = (1u << phantom_cache_contiguous_periods) - 1;
494*43a90889SApple OSS Distributions if ((pc_history & bitmask) == bitmask) {
495*43a90889SApple OSS Distributions pressure_detected = TRUE;
496*43a90889SApple OSS Distributions }
497*43a90889SApple OSS Distributions
498*43a90889SApple OSS Distributions if (vm_page_pageable_external_count > ((AVAILABLE_MEMORY) * 50) / 100) {
499*43a90889SApple OSS Distributions pressure_detected = FALSE;
500*43a90889SApple OSS Distributions }
501*43a90889SApple OSS Distributions
502*43a90889SApple OSS Distributions #if PHANTOM_CACHE_DEBUG
503*43a90889SApple OSS Distributions sample_period_ghost_counts[sample_period_ghost_counts_indx].pressure_detected = pressure_detected;
504*43a90889SApple OSS Distributions #endif
505*43a90889SApple OSS Distributions return pressure_detected;
506*43a90889SApple OSS Distributions }
507*43a90889SApple OSS Distributions
508*43a90889SApple OSS Distributions /*
509*43a90889SApple OSS Distributions * Restart the current sampling because conditions have changed significantly,
510*43a90889SApple OSS Distributions * and we don't want to react to old data.
511*43a90889SApple OSS Distributions *
512*43a90889SApple OSS Distributions * This function can be called from any thread.
513*43a90889SApple OSS Distributions */
514*43a90889SApple OSS Distributions void
vm_phantom_cache_restart_sample(void)515*43a90889SApple OSS Distributions vm_phantom_cache_restart_sample(void)
516*43a90889SApple OSS Distributions {
517*43a90889SApple OSS Distributions pc_need_eval_reset = TRUE;
518*43a90889SApple OSS Distributions }
519