some vm to accomodate needing to have a region search spot be
[newos.git] / kernel / vm / vm_daemons.c
blob21f5ac4f5dc4540eda8cf5b7ce36d0c06cd9e471
1 /*
2 ** Copyright 2001-2004, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
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>
17 bool trimming_cycle;
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;
24 vm_region *region;
25 vm_page *page;
26 addr_t va;
27 addr_t pa;
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;
46 if(first_region)
47 VERIFY_VM_REGION(first_region);
49 if(!first_region) {
50 first_region = aspace->virtual_map.region_list;
52 if(first_region)
53 VERIFY_VM_REGION(first_region);
55 if(!first_region) {
56 sem_release(aspace->virtual_map.sem, READ_COUNT);
57 return;
60 region = first_region;
62 for(;;) {
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(&region->cache_ref->lock);
69 if(!region->cache_ref->cache->scan_skip) {
70 regions_examined++;
71 for(va = region->base; va < (region->base + region->size); va += PAGE_SIZE) {
72 pages_examined++;
73 aspace->translation_map.ops->query(&aspace->translation_map, va, &pa, &flags);
74 if((flags & PAGE_PRESENT) == 0) {
75 continue;
78 pages_present++;
80 page = vm_lookup_page(pa / PAGE_SIZE);
81 if(!page) {
82 continue;
84 VERIFY_VM_PAGE(page);
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) {
88 continue;
91 // dprintf("**va 0x%x pa 0x%x fl 0x%x, st %d\n", va, pa, flags, page->state);
93 flags2 = 0;
94 if(free_target > 0) {
95 // look for a page we can steal
96 if(!(flags & PAGE_ACCESSED) && page->state == PAGE_STATE_ACTIVE) {
97 pages_unmapped++;
99 aspace->translation_map.ops->lock(&aspace->translation_map);
101 // unmap the page
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);
117 free_target--;
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);
133 else
134 vm_page_set_state(page, PAGE_STATE_MODIFIED);
137 if(--quantum == 0)
138 break;
141 mutex_unlock(&region->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)
145 break;
147 if(quantum == 0)
148 break;
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;
163 addr_t mapped_size;
164 addr_t free_memory_target;
165 int faults_per_second;
166 bigtime_t now;
168 dprintf("page daemon starting\n");
170 for(;;) {
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);
176 while(aspace) {
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);
183 now = system_time();
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.
221 old_aspace = aspace;
222 aspace = vm_aspace_walk_next(&i);
223 vm_put_aspace(old_aspace);
228 int vm_daemon_init()
230 thread_id tid;
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);
243 return 0;