2 * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 #ifndef CONFIG_MEMORYSTATUS
32 #error "CONFIG_FREEZE defined without matching CONFIG_MEMORYSTATUS"
35 #include <vm/default_freezer.h>
38 * Indicates that a page has been faulted back in.
40 #define FREEZER_OFFSET_ABSENT ((vm_object_offset_t)(-1))
42 lck_grp_attr_t default_freezer_handle_lck_grp_attr
;
43 lck_grp_t default_freezer_handle_lck_grp
;
46 default_freezer_init(void)
48 lck_grp_attr_setdefault(&default_freezer_handle_lck_grp_attr
);
49 lck_grp_init(&default_freezer_handle_lck_grp
, "default_freezer_handle",
50 &default_freezer_handle_lck_grp_attr
);
56 * Create the mapping table that will
57 * tell us the object/offset pair that
58 * corresponds to the page being sent
59 * out or being brought back in.
62 default_freezer_mapping_table_t
63 default_freezer_mapping_create(vm_object_t object
, vm_offset_t offset
)
65 default_freezer_mapping_table_t table
;
67 table
= kalloc(sizeof(struct default_freezer_mapping_table
));
69 memset(table
, 0, sizeof(*table
));
71 panic("Could not allocate mapping table\n");
74 table
->object
= object
;
75 table
->offset
= offset
;
81 * Table modifications/lookup are done behind
82 * the compact_object lock.
86 default_freezer_mapping_free(default_freezer_mapping_table_t
*table_p
, boolean_t all
)
88 default_freezer_mapping_table_t freezer_table
= *table_p
;
89 assert(freezer_table
);
93 default_freezer_mapping_table_t next
= freezer_table
->next
;
94 kfree(freezer_table
, sizeof(*freezer_table
));
96 } while (freezer_table
);
98 kfree(freezer_table
, sizeof(*freezer_table
));
103 default_freezer_mapping_store(
104 default_freezer_mapping_table_t table
,
105 memory_object_offset_t table_offset
,
106 memory_object_t memory_object
,
107 memory_object_offset_t offset
)
109 default_freezer_mapping_table_entry_t entry
;
114 while (table
->next
) {
118 if (table
->index
>= MAX_FREEZE_TABLE_ENTRIES
) {
119 vm_object_t compact_object
= table
->object
;
120 default_freezer_mapping_table_t next
;
122 next
= default_freezer_mapping_create(compact_object
, table_offset
);
129 index
= (table
)->index
++;
130 entry
= &(table
)->entry
[index
];
132 entry
->memory_object
= memory_object
;
133 entry
->offset
= offset
;
139 default_freezer_mapping_update(
140 default_freezer_mapping_table_t table
,
141 memory_object_t memory_object
,
142 memory_object_offset_t offset
,
143 memory_object_offset_t
*table_offset
, /*OUT: contains the offset into the compact object*/
144 boolean_t remove_entry
)
147 kern_return_t kr
= KERN_SUCCESS
;
148 vm_object_offset_t compact_offset
;
149 default_freezer_mapping_table_entry_t entry
;
156 compact_offset
= table
->offset
;
159 if (index
>= table
->index
) {
164 /* End of tables and we didn't find our candidate entry */
170 entry
= &table
->entry
[index
];
172 if (memory_object
== entry
->memory_object
&& offset
== entry
->offset
) {
173 if (remove_entry
== TRUE
) {
175 * Mark the page absent whilst retaining the object
176 * for cleanup during thaw.
178 entry
->offset
= FREEZER_OFFSET_ABSENT
;
180 if (table_offset
!= NULL
) {
181 *table_offset
= compact_offset
;
187 compact_offset
+= PAGE_SIZE
;
195 * Create a freezer memory object for this
196 * vm object. This will be one of the vm
197 * objects that will pack the compact object.
200 default_freezer_memory_object_create(
202 default_freezer_handle_t df_handle
)
205 default_freezer_memory_object_t fo
= NULL
;
207 fo
= kalloc(sizeof(struct default_freezer_memory_object
));
210 memory_object_control_t control
= NULL
;
212 memset(fo
, 0, sizeof(*fo
));
214 control
= memory_object_control_allocate(object
);
215 assert (control
!= MEMORY_OBJECT_CONTROL_NULL
);
217 df_memory_object_init((memory_object_t
)fo
, control
, 0);
218 fo
->fo_df_handle
= df_handle
;
220 default_freezer_handle_reference_locked(fo
->fo_df_handle
);
222 object
->pager
= (memory_object_t
)fo
;
223 object
->pager_created
= TRUE
;
224 object
->pager_initialized
= TRUE
;
225 object
->pager_ready
= TRUE
;
226 object
->pager_trusted
= TRUE
;
227 object
->pager_control
= control
;
229 panic(" Could not allocate freezer object\n");
234 default_freezer_pack(
235 unsigned int *purgeable_count
,
236 unsigned int *wired_count
,
237 unsigned int *clean_count
,
238 unsigned int *dirty_count
,
239 unsigned int dirty_budget
,
241 vm_object_t src_object
,
242 default_freezer_handle_t df_handle
)
244 kern_return_t kr
= KERN_SUCCESS
;
247 default_freezer_handle_lock(df_handle
);
250 kr
= vm_object_pack(purgeable_count
, wired_count
, clean_count
, dirty_count
, dirty_budget
, shared
, src_object
, df_handle
);
253 default_freezer_handle_unlock(df_handle
);
260 * Called with freezer_handle locked.
261 * default_freezer_pack locks the handle, calls
262 * vm_object_pack which, in turn, will call
263 * default_freezer_pack_page().
266 default_freezer_pack_page(
268 default_freezer_handle_t df_handle
)
271 default_freezer_mapping_table_t freeze_table
= NULL
;
272 memory_object_t memory_object
= NULL
;
273 vm_object_t compact_object
= VM_OBJECT_NULL
;
277 compact_object
= df_handle
->dfh_compact_object
;
279 assert(compact_object
);
281 freeze_table
= df_handle
->dfh_table
;
282 memory_object
= p
->object
->pager
;
284 if (memory_object
== NULL
) {
285 default_freezer_memory_object_create(p
->object
, df_handle
);
286 memory_object
= p
->object
->pager
;
288 assert(df_handle
== ((default_freezer_memory_object_t
)memory_object
)->fo_df_handle
);
291 vm_object_lock(compact_object
);
292 default_freezer_mapping_store(freeze_table
, df_handle
->dfh_compact_offset
, memory_object
, p
->offset
+ p
->object
->paging_offset
);
293 vm_page_rename(p
, compact_object
, df_handle
->dfh_compact_offset
, FALSE
);
294 vm_object_unlock(compact_object
);
296 df_handle
->dfh_compact_offset
+= PAGE_SIZE
;
301 default_freezer_unpack(
302 default_freezer_handle_t df_handle
)
305 vm_page_t compact_page
= VM_PAGE_NULL
, src_page
= VM_PAGE_NULL
;
307 vm_object_t src_object
= VM_OBJECT_NULL
;
308 vm_object_t compact_object
= VM_OBJECT_NULL
;
309 memory_object_t src_mem_object
= MEMORY_OBJECT_NULL
;
310 memory_object_offset_t src_offset
= 0;
311 vm_object_offset_t compact_offset
= 0;
312 default_freezer_memory_object_t fo
= NULL
;
313 default_freezer_mapping_table_t freeze_table
= NULL
;
314 boolean_t should_unlock_handle
= FALSE
;
319 default_freezer_handle_lock(df_handle
);
320 should_unlock_handle
= TRUE
;
322 freeze_table
= df_handle
->dfh_table
;
323 compact_object
= df_handle
->dfh_compact_object
;
325 assert(compact_object
);
326 assert(compact_object
->alive
);
327 assert(!compact_object
->terminating
);
328 assert(compact_object
->pager_ready
);
330 /* Bring the pages back in */
331 if ((kr
= vm_object_pagein(compact_object
)) != KERN_SUCCESS
) {
332 if (should_unlock_handle
) {
333 default_freezer_handle_unlock(df_handle
);
338 vm_object_lock(compact_object
);
340 for (index
= 0, compact_offset
= 0; ; index
++, compact_offset
+= PAGE_SIZE
){
341 if (index
>= freeze_table
->index
) {
342 default_freezer_mapping_table_t table_next
;
344 table_next
= freeze_table
->next
;
346 /* Free the tables as we go along */
347 default_freezer_mapping_free(&freeze_table
, FALSE
);
349 if (table_next
== NULL
){
353 freeze_table
= table_next
;
358 * Skip slots that represent deallocated memory objects.
360 src_mem_object
= freeze_table
->entry
[index
].memory_object
;
361 if (src_mem_object
== MEMORY_OBJECT_NULL
)
365 * Skip slots that represent faulted pages.
367 src_offset
= freeze_table
->entry
[index
].offset
;
368 if (src_offset
!= FREEZER_OFFSET_ABSENT
) {
370 compact_page
= vm_page_lookup(compact_object
, compact_offset
);
371 assert(compact_page
);
373 fo
= (default_freezer_memory_object_t
)src_mem_object
;
375 src_object
= memory_object_control_to_vm_object(fo
->fo_pager_control
);
377 /* Move back over from the freeze object to the original */
378 vm_object_lock(src_object
);
379 src_page
= vm_page_lookup(src_object
, src_offset
- src_object
->paging_offset
);
380 if (src_page
!= VM_PAGE_NULL
){
382 * We might be racing with a VM fault.
383 * So handle that gracefully.
385 assert(src_page
->absent
== TRUE
);
386 VM_PAGE_FREE(src_page
);
388 vm_page_rename(compact_page
, src_object
, src_offset
- src_object
->paging_offset
, FALSE
);
389 vm_object_unlock(src_object
);
394 vm_object_unlock(compact_object
);
396 vm_object_deallocate(compact_object
);
398 if (should_unlock_handle
) {
399 df_handle
->dfh_table
= NULL
;
400 df_handle
->dfh_compact_object
= VM_OBJECT_NULL
;
401 df_handle
->dfh_compact_offset
= 0;
402 default_freezer_handle_unlock(df_handle
);
404 return (KERN_SUCCESS
);
408 df_memory_object_reference(__unused memory_object_t mem_obj
)
415 df_memory_object_deallocate(memory_object_t mem_obj
)
418 default_freezer_memory_object_t fo
= (default_freezer_memory_object_t
)mem_obj
;
422 if (fo
->fo_df_handle
!= NULL
) {
424 default_freezer_mapping_table_t table
= NULL
;
425 default_freezer_mapping_table_entry_t entry
;
426 boolean_t found
= FALSE
;
428 vm_object_t compact_object
= VM_OBJECT_NULL
;
430 default_freezer_handle_lock(fo
->fo_df_handle
);
432 compact_object
= fo
->fo_df_handle
->dfh_compact_object
;
433 table
= fo
->fo_df_handle
->dfh_table
;
435 if (compact_object
== VM_OBJECT_NULL
|| table
== NULL
) {
436 /*Nothing to do. A thaw must have cleared it all out.*/
438 vm_object_lock(compact_object
);
440 /* Remove from table */
442 if (index
>= table
->index
) {
452 entry
= &table
->entry
[index
];
453 if (mem_obj
== entry
->memory_object
) {
454 /* It matches, so clear the entry */
458 entry
->memory_object
= MEMORY_OBJECT_NULL
;
460 } else if (MEMORY_OBJECT_NULL
!= entry
->memory_object
) {
461 /* We have a different valid object; we're done */
470 vm_object_unlock(compact_object
);
473 if (default_freezer_handle_deallocate_locked(fo
->fo_df_handle
)) {
474 default_freezer_handle_unlock(fo
->fo_df_handle
);
478 kfree(fo
, sizeof(*fo
));
482 df_memory_object_init(
483 memory_object_t mem_obj
,
484 memory_object_control_t control
,
485 __unused memory_object_cluster_size_t pager_page_size
)
488 default_freezer_memory_object_t fo
= (default_freezer_memory_object_t
)mem_obj
;
491 fo
->fo_pager_ops
= &default_freezer_ops
;
492 fo
->fo_pager_header
.io_bits
= IKOT_MEMORY_OBJECT
;
493 fo
->fo_pager_control
= control
;
499 df_memory_object_terminate(memory_object_t mem_obj
)
502 default_freezer_memory_object_t fo
= (default_freezer_memory_object_t
)mem_obj
;
504 memory_object_control_deallocate(fo
->fo_pager_control
);
510 df_memory_object_data_request(
511 memory_object_t mem_obj
,
512 memory_object_offset_t offset
,
513 memory_object_cluster_size_t length
,
514 vm_prot_t protection_required
,
515 memory_object_fault_info_t fault_info
)
518 vm_object_t src_object
= VM_OBJECT_NULL
, compact_object
= VM_OBJECT_NULL
;
519 memory_object_offset_t compact_offset
= 0;
520 memory_object_t pager
= NULL
;
521 kern_return_t kr
= KERN_SUCCESS
;
522 boolean_t drop_object_ref
= FALSE
;
523 vm_page_t compact_page
, dst_page
;
525 default_freezer_memory_object_t fo
= (default_freezer_memory_object_t
)mem_obj
;
526 default_freezer_handle_t df_handle
= NULL
;
528 df_handle
= fo
->fo_df_handle
;
530 if (df_handle
== NULL
) {
533 default_freezer_handle_lock(df_handle
);
535 src_object
= memory_object_control_to_vm_object(fo
->fo_pager_control
);
536 compact_object
= fo
->fo_df_handle
->dfh_compact_object
;
538 if (compact_object
== NULL
) {
541 vm_object_lock(compact_object
);
542 vm_object_reference_locked(compact_object
);
543 drop_object_ref
= TRUE
;
545 kr
= default_freezer_mapping_update(fo
->fo_df_handle
->dfh_table
,
550 vm_object_unlock(compact_object
);
552 default_freezer_handle_unlock(df_handle
);
557 /*Caller is just querying to see if we have the page*/
558 if (drop_object_ref
) {
559 vm_object_deallocate(compact_object
);
564 if (kr
!= KERN_SUCCESS
){
566 unsigned int request_flags
;
568 unsigned int page_list_count
= 0;
570 request_flags
= UPL_NO_SYNC
| UPL_RET_ONLY_ABSENT
| UPL_SET_LITE
| UPL_SET_INTERNAL
;
572 * Should we decide to activate USE_PRECIOUS (from default_pager_internal.h)
573 * here, then the request_flags will need to add these to the ones above:
575 * request_flags |= UPL_PRECIOUS | UPL_CLEAN_IN_PLACE
577 request_flags
|= UPL_REQUEST_SET_DIRTY
;
579 memory_object_super_upl_request(fo
->fo_pager_control
,
580 (memory_object_offset_t
)offset
,
581 PAGE_SIZE
, PAGE_SIZE
,
582 &upl
, NULL
, &page_list_count
,
584 upl_range_needed(upl
, 0, 1);
586 upl_abort(upl
, UPL_ABORT_UNAVAILABLE
);
589 if (drop_object_ref
) {
590 vm_object_deallocate(compact_object
);
595 vm_object_lock(compact_object
);
597 assert(compact_object
->alive
);
598 assert(!compact_object
->terminating
);
601 * note that the activity_in_progress could be non-zero, but
602 * the pager has not yet been created since the activity_in_progress
603 * count is bumped via vm_pageout_cluster, while the pager isn't created
604 * until the pageout thread runs and starts to process the pages
605 * placed on the I/O queue... once the processing of the compact object
606 * proceeds to the point where it's placed the first page on the I/O
607 * queue, we need to wait until the entire freeze operation has completed.
609 vm_object_paging_wait(compact_object
, THREAD_UNINT
);
611 if (compact_object
->pager_ready
) {
612 vm_object_paging_begin(compact_object
);
614 compact_object
->blocked_access
= TRUE
;
615 pager
= (memory_object_t
)compact_object
->pager
;
617 vm_object_unlock(compact_object
);
619 ((vm_object_fault_info_t
) fault_info
)->io_sync
= TRUE
;
622 * We have a reference on both the default_freezer
623 * memory object handle and the compact object.
625 kr
= dp_memory_object_data_request(pager
,
630 if (kr
!= KERN_SUCCESS
)
631 panic("%d: default_freezer TOC pointed us to default_pager incorrectly\n", kr
);
633 vm_object_lock(compact_object
);
635 compact_object
->blocked_access
= FALSE
;
636 vm_object_paging_end(compact_object
);
638 vm_object_lock(src_object
);
640 if ((compact_page
= vm_page_lookup(compact_object
, compact_offset
)) != VM_PAGE_NULL
){
642 dst_page
= vm_page_lookup(src_object
, offset
- src_object
->paging_offset
);
644 if (dst_page
&& !dst_page
->absent
){
646 * Someone raced us here and unpacked
647 * the object behind us.
648 * So cleanup before we return.
650 VM_PAGE_FREE(compact_page
);
652 if (dst_page
!= NULL
) {
653 VM_PAGE_FREE(dst_page
);
655 vm_page_rename(compact_page
, src_object
, offset
- src_object
->paging_offset
, FALSE
);
657 if (default_freezer_mapping_update(fo
->fo_df_handle
->dfh_table
,
661 TRUE
) != KERN_SUCCESS
) {
662 printf("Page for object: 0x%lx at offset: 0x%lx not found in table\n", (uintptr_t)src_object
, (uintptr_t)offset
);
665 PAGE_WAKEUP_DONE(compact_page
);
668 printf("%d: default_freezer: compact_object doesn't have the page for object 0x%lx at offset 0x%lx \n", kr
, (uintptr_t)compact_object
, (uintptr_t)compact_offset
);
671 vm_object_unlock(src_object
);
672 vm_object_unlock(compact_object
);
673 vm_object_deallocate(compact_object
);
679 df_memory_object_data_return(
680 __unused memory_object_t mem_obj
,
681 __unused memory_object_offset_t offset
,
682 __unused memory_object_cluster_size_t size
,
683 __unused memory_object_offset_t
*resid_offset
,
684 __unused
int *io_error
,
685 __unused boolean_t dirty
,
686 __unused boolean_t kernel_copy
,
687 __unused
int upl_flags
)
690 panic(" default_freezer: df_memory_object_data_return should not be called\n");
695 df_memory_object_data_initialize(
696 __unused memory_object_t mem_obj
,
697 __unused memory_object_offset_t offset
,
698 __unused memory_object_cluster_size_t size
)
701 panic(" default_freezer: df_memory_object_data_initialize should not be called\n");
706 df_memory_object_data_unlock(
707 __unused memory_object_t mem_obj
,
708 __unused memory_object_offset_t offset
,
709 __unused memory_object_size_t length
,
710 __unused vm_prot_t prot
)
713 panic(" default_freezer: df_memory_object_data_unlock should not be called\n");
718 df_memory_object_synchronize(
719 __unused memory_object_t mem_obj
,
720 __unused memory_object_offset_t offset
,
721 __unused memory_object_size_t length
,
722 __unused vm_sync_t flags
)
725 panic(" default_freezer: df_memory_object_synchronize should not be called\n");
730 df_memory_object_map(
731 __unused memory_object_t mem_obj
,
732 __unused vm_prot_t prot
)
735 panic(" default_freezer: df_memory_object_map should not be called\n");
740 df_memory_object_last_unmap(__unused memory_object_t mem_obj
)
743 panic(" default_freezer: df_memory_object_last_unmap should not be called\n");
749 df_memory_object_data_reclaim(
750 __unused memory_object_t mem_obj
,
751 __unused boolean_t reclaim_backing_store
)
754 panic("df_memory_object_data_reclaim\n");
760 * The freezer handle is used to make sure that
761 * we don't race against the lookup and termination
762 * of the compact object.
766 default_freezer_handle_lock(default_freezer_handle_t df_handle
) {
767 lck_rw_lock_exclusive(&df_handle
->dfh_lck
);
771 default_freezer_handle_unlock(default_freezer_handle_t df_handle
) {
772 lck_rw_done(&df_handle
->dfh_lck
);
775 default_freezer_handle_t
776 default_freezer_handle_allocate(void)
779 default_freezer_handle_t df_handle
= NULL
;
780 df_handle
= kalloc(sizeof(struct default_freezer_handle
));
783 memset(df_handle
, 0, sizeof(struct default_freezer_handle
));
784 lck_rw_init(&df_handle
->dfh_lck
, &default_freezer_handle_lck_grp
, NULL
);
785 /* No one knows of this handle yet so no need to lock it. */
786 default_freezer_handle_reference_locked(df_handle
);
788 panic("Failed to allocated default_freezer_handle structure\n");
794 default_freezer_handle_init(
795 default_freezer_handle_t df_handle
)
797 kern_return_t kr
= KERN_SUCCESS
;
798 vm_object_t compact_object
= VM_OBJECT_NULL
;
800 if (df_handle
== NULL
|| df_handle
->dfh_table
!= NULL
) {
803 /* Create our compact object */
804 compact_object
= vm_object_allocate((vm_map_offset_t
)(VM_MAX_ADDRESS
) - (vm_map_offset_t
)(VM_MIN_ADDRESS
));
805 if (!compact_object
) {
808 df_handle
->dfh_compact_object
= compact_object
;
809 df_handle
->dfh_compact_offset
= 0;
810 df_handle
->dfh_table
= default_freezer_mapping_create(df_handle
->dfh_compact_object
, df_handle
->dfh_compact_offset
);
811 if (!df_handle
->dfh_table
) {
821 default_freezer_handle_reference_locked(
822 default_freezer_handle_t df_handle
)
825 df_handle
->dfh_ref_count
++;
829 default_freezer_handle_deallocate(
830 default_freezer_handle_t df_handle
)
833 default_freezer_handle_lock(df_handle
);
834 if (default_freezer_handle_deallocate_locked(df_handle
)) {
835 default_freezer_handle_unlock(df_handle
);
840 default_freezer_handle_deallocate_locked(
841 default_freezer_handle_t df_handle
)
843 boolean_t should_unlock
= TRUE
;
846 df_handle
->dfh_ref_count
--;
847 if (df_handle
->dfh_ref_count
== 0) {
848 lck_rw_destroy(&df_handle
->dfh_lck
, &default_freezer_handle_lck_grp
);
849 kfree(df_handle
, sizeof(struct default_freezer_handle
));
850 should_unlock
= FALSE
;
852 return should_unlock
;
856 default_freezer_pageout(
857 default_freezer_handle_t df_handle
)
861 vm_object_pageout(df_handle
->dfh_compact_object
);
864 #endif /* CONFIG_FREEZE */