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