2 ** Copyright 2001-2004, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
5 #include <kernel/kernel.h>
6 #include <kernel/thread.h>
7 #include <kernel/debug.h>
8 #include <kernel/khash.h>
9 #include <kernel/sem.h>
10 #include <kernel/arch/cpu.h>
11 #include <kernel/time.h>
12 #include <kernel/vm.h>
13 #include <kernel/vm_priv.h>
14 #include <kernel/vm_cache.h>
15 #include <kernel/vm_page.h>
18 static addr_t free_memory_low_water
;
19 static addr_t free_memory_high_water
;
21 static void scan_pages(vm_address_space
*aspace
, addr_t free_target
)
23 vm_region
*first_region
;
28 unsigned int flags
, flags2
;
29 int quantum
= PAGE_SCAN_QUANTUM
;
30 int regions_examined
= 0;
31 int pages_examined
= 0;
32 int pages_present
= 0;
33 int pages_unmapped
= 0;
35 bigtime_t start_time
= system_time();
37 dprintf(" scan_pages called on aspace %p, id 0x%x, free_target %ld\n", aspace
, aspace
->id
, free_target
);
39 sem_acquire(aspace
->virtual_map
.sem
, READ_COUNT
);
41 first_region
= aspace
->virtual_map
.region_list
;
42 while(first_region
&& (first_region
->base
+ (first_region
->size
- 1)) < aspace
->scan_va
) {
43 VERIFY_VM_REGION(first_region
);
44 first_region
= first_region
->aspace_next
;
47 VERIFY_VM_REGION(first_region
);
50 first_region
= aspace
->virtual_map
.region_list
;
53 VERIFY_VM_REGION(first_region
);
56 sem_release(aspace
->virtual_map
.sem
, READ_COUNT
);
60 region
= first_region
;
63 VERIFY_VM_REGION(region
);
64 VERIFY_VM_CACHE_REF(region
->cache_ref
);
65 VERIFY_VM_CACHE(region
->cache_ref
->cache
);
67 // scan the pages in this region
68 mutex_lock(®ion
->cache_ref
->lock
);
69 if(!region
->cache_ref
->cache
->scan_skip
) {
71 for(va
= region
->base
; va
< (region
->base
+ region
->size
); va
+= PAGE_SIZE
) {
73 aspace
->translation_map
.ops
->query(&aspace
->translation_map
, va
, &pa
, &flags
);
74 if((flags
& PAGE_PRESENT
) == 0) {
80 page
= vm_lookup_page(pa
/ PAGE_SIZE
);
86 // see if this page is busy, if it is lets forget it and move on
87 if(page
->state
== PAGE_STATE_BUSY
|| page
->state
== PAGE_STATE_WIRED
) {
91 // dprintf("**va 0x%x pa 0x%x fl 0x%x, st %d\n", va, pa, flags, page->state);
95 // look for a page we can steal
96 if(!(flags
& PAGE_ACCESSED
) && page
->state
== PAGE_STATE_ACTIVE
) {
99 aspace
->translation_map
.ops
->lock(&aspace
->translation_map
);
102 aspace
->translation_map
.ops
->unmap(&aspace
->translation_map
, va
, va
+ PAGE_SIZE
);
104 // flush the tlbs of all cpus
105 aspace
->translation_map
.ops
->flush(&aspace
->translation_map
);
107 // re-query the flags on the old pte, to make sure we have accurate modified bit data
108 aspace
->translation_map
.ops
->query(&aspace
->translation_map
, va
, &pa
, &flags2
);
110 // clear the modified and accessed bits on the entries
111 aspace
->translation_map
.ops
->clear_flags(&aspace
->translation_map
, va
, PAGE_MODIFIED
|PAGE_ACCESSED
);
113 // decrement the ref count on the page. If we just unmapped it for the last time,
114 // put the page on the inactive list
115 if(atomic_add(&page
->ref_count
, -1) == 1) {
116 vm_page_set_state(page
, PAGE_STATE_INACTIVE
);
119 aspace
->translation_map
.ops
->unlock(&aspace
->translation_map
);
120 } else if(flags
& PAGE_ACCESSED
) {
121 // clear the accessed bits of this page
122 aspace
->translation_map
.ops
->lock(&aspace
->translation_map
);
123 aspace
->translation_map
.ops
->clear_flags(&aspace
->translation_map
, va
, PAGE_ACCESSED
);
124 aspace
->translation_map
.ops
->unlock(&aspace
->translation_map
);
128 // if the page is modified, but the state is active or inactive, put it on the modified list
129 if(((flags
& PAGE_MODIFIED
) || (flags2
& PAGE_MODIFIED
))
130 && (page
->state
== PAGE_STATE_ACTIVE
|| page
->state
== PAGE_STATE_INACTIVE
)) {
131 if(page
->cache_ref
->cache
->temporary
)
132 vm_page_set_state(page
, PAGE_STATE_MODIFIED_TEMPORARY
);
134 vm_page_set_state(page
, PAGE_STATE_MODIFIED
);
141 mutex_unlock(®ion
->cache_ref
->lock
);
142 // move to the next region, wrapping around and stopping if we get back to the first region
143 region
= region
->aspace_next
? region
->aspace_next
: aspace
->virtual_map
.region_list
;
144 if(region
== first_region
)
151 aspace
->scan_va
= region
? (first_region
->base
+ first_region
->size
) : aspace
->virtual_map
.base
;
152 sem_release(aspace
->virtual_map
.sem
, READ_COUNT
);
154 dprintf(" exiting scan_pages, took %Ld usecs (re %d pe %d pp %d pu %d)\n", system_time() - start_time
,
155 regions_examined
, pages_examined
, pages_present
, pages_unmapped
);
158 static int page_daemon()
160 struct hash_iterator i
;
161 vm_address_space
*old_aspace
;
162 vm_address_space
*aspace
;
164 addr_t free_memory_target
;
165 int faults_per_second
;
168 dprintf("page daemon starting\n");
171 thread_snooze(PAGE_DAEMON_INTERVAL
);
173 // scan through all of the address spaces
174 vm_aspace_walk_start(&i
);
175 aspace
= vm_aspace_walk_next(&i
);
177 VERIFY_VM_ASPACE(aspace
);
179 mapped_size
= aspace
->translation_map
.ops
->get_mapped_size(&aspace
->translation_map
);
181 dprintf("page_daemon: looking at aspace %p, id 0x%x, mapped size %ld\n", aspace
, aspace
->id
, mapped_size
);
184 if(now
- aspace
->last_working_set_adjust
> WORKING_SET_ADJUST_INTERVAL
) {
185 faults_per_second
= (aspace
->fault_count
* 1000000) / (now
- aspace
->last_working_set_adjust
);
186 dprintf(" faults_per_second = %d\n", faults_per_second
);
187 aspace
->last_working_set_adjust
= now
;
188 aspace
->fault_count
= 0;
190 if(faults_per_second
> MAX_FAULTS_PER_SECOND
191 && mapped_size
>= aspace
->working_set_size
192 && aspace
->working_set_size
< aspace
->max_working_set
) {
194 aspace
->working_set_size
+= WORKING_SET_INCREMENT
;
195 dprintf(" new working set size = %ld\n", aspace
->working_set_size
);
196 } else if(faults_per_second
< MIN_FAULTS_PER_SECOND
197 && mapped_size
<= aspace
->working_set_size
198 && aspace
->working_set_size
> aspace
->min_working_set
) {
200 aspace
->working_set_size
-= WORKING_SET_DECREMENT
;
201 dprintf(" new working set size = %ld\n", aspace
->working_set_size
);
205 // decide if we need to enter or leave the trimming cycle
206 if(!trimming_cycle
&& vm_page_num_free_pages() < free_memory_low_water
)
207 trimming_cycle
= true;
208 else if(trimming_cycle
&& vm_page_num_free_pages() > free_memory_high_water
)
209 trimming_cycle
= false;
211 // scan some pages, trying to free some if needed
212 free_memory_target
= 0;
213 if(trimming_cycle
&& mapped_size
> aspace
->working_set_size
)
214 free_memory_target
= mapped_size
- aspace
->working_set_size
;
216 scan_pages(aspace
, free_memory_target
);
217 // scan_pages(aspace, 0x7fffffff);
219 // must hold a ref to the old aspace while we grab the next one,
220 // otherwise the iterator becomes out of date.
222 aspace
= vm_aspace_walk_next(&i
);
223 vm_put_aspace(old_aspace
);
232 trimming_cycle
= false;
234 // calculate the free memory low and high water at which point we enter/leave trimming phase
235 free_memory_low_water
= vm_page_num_pages() / 8;
236 free_memory_high_water
= vm_page_num_pages() / 4;
238 // create a kernel thread to select pages for pageout
239 tid
= thread_create_kernel_thread("page daemon", &page_daemon
, NULL
);
240 thread_set_priority(tid
, THREAD_MIN_RT_PRIORITY
);
241 thread_resume_thread(tid
);