2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 #include <kern/kalloc.h>
32 #include <kern/machine.h>
33 #include <kern/misc_protos.h>
34 #include <kern/thread.h>
35 #include <kern/processor.h>
36 #include <mach/machine.h>
37 #include <mach/processor_info.h>
38 #include <mach/mach_types.h>
39 #include <default_pager/default_pager_internal.h>
40 #include <IOKit/IOPlatformExpert.h>
43 #include <IOKit/IOHibernatePrivate.h>
44 #include <vm/vm_page.h>
45 #include <vm/vm_pageout.h>
47 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
49 static vm_page_t hibernate_gobble_queue
;
51 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
54 hibernate_page_list_zero(hibernate_page_list_t
*list
)
57 hibernate_bitmap_t
* bitmap
;
59 bitmap
= &list
->bank_bitmap
[0];
60 for (bank
= 0; bank
< list
->bank_count
; bank
++)
64 bzero((void *) &bitmap
->bitmap
[0], bitmap
->bitmapwords
<< 2);
65 // set out-of-bound bits at end of bitmap.
66 last_bit
= ((bitmap
->last_page
- bitmap
->first_page
+ 1) & 31);
68 bitmap
->bitmap
[bitmap
->bitmapwords
- 1] = (0xFFFFFFFF >> last_bit
);
70 bitmap
= (hibernate_bitmap_t
*) &bitmap
->bitmap
[bitmap
->bitmapwords
];
76 consider_discard(vm_page_t m
)
78 register vm_object_t object
= 0;
80 boolean_t discard
= FALSE
;
85 panic("consider_discard: private");
87 if (!vm_object_lock_try(m
->object
))
92 if (m
->wire_count
!= 0)
97 if (m
->busy
|| !object
->alive
)
99 * Somebody is playing with this page.
103 if (m
->absent
|| m
->unusual
|| m
->error
)
105 * If it's unusual in anyway, ignore it
114 refmod_state
= pmap_get_refmod(m
->phys_page
);
116 if (refmod_state
& VM_MEM_REFERENCED
)
118 if (refmod_state
& VM_MEM_MODIFIED
)
123 * If it's clean we can discard the page on wakeup.
130 vm_object_unlock(object
);
137 discard_page(vm_page_t m
)
139 if (m
->absent
|| m
->unusual
|| m
->error
)
141 * If it's unusual in anyway, ignore
147 int refmod_state
= pmap_disconnect(m
->phys_page
);
149 if (refmod_state
& VM_MEM_REFERENCED
)
151 if (refmod_state
& VM_MEM_MODIFIED
)
156 panic("discard_page(%p) dirty", m
);
158 panic("discard_page(%p) laundry", m
);
160 panic("discard_page(%p) private", m
);
162 panic("discard_page(%p) fictitious", m
);
168 Bits zero in the bitmaps => needs to be saved. All pages default to be saved,
169 pages known to VM to not need saving are subtracted.
170 Wired pages to be saved are present in page_list_wired, pageable in page_list.
172 extern vm_page_t vm_lopage_queue_free
;
175 hibernate_page_list_setall(hibernate_page_list_t
* page_list
,
176 hibernate_page_list_t
* page_list_wired
,
179 uint64_t start
, end
, nsec
;
181 uint32_t pages
= page_list
->page_count
;
182 uint32_t count_zf
= 0, count_inactive
= 0, count_active
= 0;
183 uint32_t count_wire
= pages
;
184 uint32_t count_discard_active
= 0, count_discard_inactive
= 0;
187 HIBLOG("hibernate_page_list_setall start\n");
189 clock_get_uptime(&start
);
191 hibernate_page_list_zero(page_list
);
192 hibernate_page_list_zero(page_list_wired
);
194 m
= (vm_page_t
) hibernate_gobble_queue
;
199 hibernate_page_bitset(page_list
, TRUE
, m
->phys_page
);
200 hibernate_page_bitset(page_list_wired
, TRUE
, m
->phys_page
);
201 m
= (vm_page_t
) m
->pageq
.next
;
204 m
= (vm_page_t
) vm_page_queue_free
;
209 hibernate_page_bitset(page_list
, TRUE
, m
->phys_page
);
210 hibernate_page_bitset(page_list_wired
, TRUE
, m
->phys_page
);
211 m
= (vm_page_t
) m
->pageq
.next
;
214 m
= (vm_page_t
) vm_lopage_queue_free
;
219 hibernate_page_bitset(page_list
, TRUE
, m
->phys_page
);
220 hibernate_page_bitset(page_list_wired
, TRUE
, m
->phys_page
);
221 m
= (vm_page_t
) m
->pageq
.next
;
224 queue_iterate( &vm_page_queue_zf
,
229 if ((kIOHibernateModeDiscardCleanInactive
& gIOHibernateMode
)
230 && consider_discard(m
))
232 hibernate_page_bitset(page_list
, TRUE
, m
->phys_page
);
233 count_discard_inactive
++;
238 hibernate_page_bitset(page_list_wired
, TRUE
, m
->phys_page
);
241 queue_iterate( &vm_page_queue_inactive
,
246 if ((kIOHibernateModeDiscardCleanInactive
& gIOHibernateMode
)
247 && consider_discard(m
))
249 hibernate_page_bitset(page_list
, TRUE
, m
->phys_page
);
250 count_discard_inactive
++;
255 hibernate_page_bitset(page_list_wired
, TRUE
, m
->phys_page
);
258 queue_iterate( &vm_page_queue_active
,
263 if ((kIOHibernateModeDiscardCleanActive
& gIOHibernateMode
)
264 && consider_discard(m
))
266 hibernate_page_bitset(page_list
, TRUE
, m
->phys_page
);
267 count_discard_active
++;
272 hibernate_page_bitset(page_list_wired
, TRUE
, m
->phys_page
);
275 // pull wired from hibernate_bitmap
278 hibernate_bitmap_t
* bitmap
;
279 hibernate_bitmap_t
* bitmap_wired
;
281 bitmap
= &page_list
->bank_bitmap
[0];
282 bitmap_wired
= &page_list_wired
->bank_bitmap
[0];
283 for (bank
= 0; bank
< page_list
->bank_count
; bank
++)
285 for (i
= 0; i
< bitmap
->bitmapwords
; i
++)
286 bitmap
->bitmap
[i
] = bitmap
->bitmap
[i
] | ~bitmap_wired
->bitmap
[i
];
287 bitmap
= (hibernate_bitmap_t
*) &bitmap
->bitmap
[bitmap
->bitmapwords
];
288 bitmap_wired
= (hibernate_bitmap_t
*) &bitmap_wired
->bitmap
[bitmap_wired
->bitmapwords
];
291 // machine dependent adjustments
292 hibernate_page_list_setall_machine(page_list
, page_list_wired
, &pages
);
294 clock_get_uptime(&end
);
295 absolutetime_to_nanoseconds(end
- start
, &nsec
);
296 HIBLOG("hibernate_page_list_setall time: %qd ms\n", nsec
/ 1000000ULL);
298 HIBLOG("pages %d, wire %d, act %d, inact %d, zf %d, could discard act %d inact %d\n",
299 pages
, count_wire
, count_active
, count_inactive
, count_zf
,
300 count_discard_active
, count_discard_inactive
);
306 hibernate_page_list_discard(hibernate_page_list_t
* page_list
)
308 uint64_t start
, end
, nsec
;
311 uint32_t count_discard_active
= 0, count_discard_inactive
= 0;
313 clock_get_uptime(&start
);
315 m
= (vm_page_t
) queue_first(&vm_page_queue_zf
);
316 while (m
&& !queue_end(&vm_page_queue_zf
, (queue_entry_t
)m
))
318 next
= (vm_page_t
) m
->pageq
.next
;
319 if (hibernate_page_bittst(page_list
, m
->phys_page
))
322 count_discard_inactive
++;
327 m
= (vm_page_t
) queue_first(&vm_page_queue_inactive
);
328 while (m
&& !queue_end(&vm_page_queue_inactive
, (queue_entry_t
)m
))
330 next
= (vm_page_t
) m
->pageq
.next
;
331 if (hibernate_page_bittst(page_list
, m
->phys_page
))
334 count_discard_inactive
++;
339 m
= (vm_page_t
) queue_first(&vm_page_queue_active
);
340 while (m
&& !queue_end(&vm_page_queue_active
, (queue_entry_t
)m
))
342 next
= (vm_page_t
) m
->pageq
.next
;
343 if (hibernate_page_bittst(page_list
, m
->phys_page
))
346 count_discard_active
++;
351 clock_get_uptime(&end
);
352 absolutetime_to_nanoseconds(end
- start
, &nsec
);
353 HIBLOG("hibernate_page_list_discard time: %qd ms, discarded act %d inact %d\n",
355 count_discard_active
, count_discard_inactive
);
358 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
361 hibernate_setup(IOHibernateImageHeader
* header
,
362 uint32_t free_page_ratio
,
363 uint32_t free_page_time
,
364 hibernate_page_list_t
** page_list_ret
,
365 hibernate_page_list_t
** page_list_wired_ret
,
366 boolean_t
* encryptedswap
)
368 hibernate_page_list_t
* page_list
= NULL
;
369 hibernate_page_list_t
* page_list_wired
= NULL
;
371 uint32_t i
, gobble_count
;
373 *page_list_ret
= NULL
;
374 *page_list_wired_ret
= NULL
;
377 page_list
= hibernate_page_list_allocate();
379 return (KERN_RESOURCE_SHORTAGE
);
380 page_list_wired
= hibernate_page_list_allocate();
381 if (!page_list_wired
)
383 kfree(page_list
, page_list
->list_size
);
384 return (KERN_RESOURCE_SHORTAGE
);
387 *encryptedswap
= dp_encryption
;
389 // pages we could force out to reduce hibernate image size
390 gobble_count
= (((uint64_t) page_list
->page_count
) * ((uint64_t) free_page_ratio
)) / 100;
392 // no failures hereafter
394 hibernate_processor_setup(header
);
396 HIBLOG("hibernate_alloc_pages flags %08lx, gobbling %d pages\n",
397 header
->processorFlags
, gobble_count
);
401 uint64_t start
, end
, timeout
, nsec
;
402 clock_interval_to_deadline(free_page_time
, 1000 * 1000 /*ms*/, &timeout
);
403 clock_get_uptime(&start
);
405 for (i
= 0; i
< gobble_count
; i
++)
407 while (VM_PAGE_NULL
== (m
= vm_page_grab()))
409 clock_get_uptime(&end
);
419 m
->pageq
.next
= (queue_entry_t
) hibernate_gobble_queue
;
420 hibernate_gobble_queue
= m
;
423 clock_get_uptime(&end
);
424 absolutetime_to_nanoseconds(end
- start
, &nsec
);
425 HIBLOG("Gobbled %d pages, time: %qd ms\n", i
, nsec
/ 1000000ULL);
428 *page_list_ret
= page_list
;
429 *page_list_wired_ret
= page_list_wired
;
431 return (KERN_SUCCESS
);
435 hibernate_teardown(hibernate_page_list_t
* page_list
,
436 hibernate_page_list_t
* page_list_wired
)
441 m
= (vm_page_t
) hibernate_gobble_queue
;
444 next
= (vm_page_t
) m
->pageq
.next
;
449 hibernate_gobble_queue
= VM_PAGE_NULL
;
452 HIBLOG("Freed %d pages\n", count
);
455 kfree(page_list
, page_list
->list_size
);
457 kfree(page_list_wired
, page_list_wired
->list_size
);
459 return (KERN_SUCCESS
);