Changeset View
Standalone View
sys/vm/vm_swapout.c
Show First 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | |||||
* Swap_idle_threshold2 is the time that a process can be idle before | * Swap_idle_threshold2 is the time that a process can be idle before | ||||
* it will be swapped out, if idle swapping is enabled. | * it will be swapped out, if idle swapping is enabled. | ||||
*/ | */ | ||||
static int swap_idle_threshold2 = 10; | static int swap_idle_threshold2 = 10; | ||||
SYSCTL_INT(_vm, OID_AUTO, swap_idle_threshold2, CTLFLAG_RW, | SYSCTL_INT(_vm, OID_AUTO, swap_idle_threshold2, CTLFLAG_RW, | ||||
&swap_idle_threshold2, 0, | &swap_idle_threshold2, 0, | ||||
"Time before a process will be swapped out"); | "Time before a process will be swapped out"); | ||||
static int swapper_swapin_oom_timeout = 1; | |||||
SYSCTL_INT(_vm, OID_AUTO, swapper_swapin_oom_timeout, CTLFLAG_RW, | |||||
&swapper_swapin_oom_timeout, 0, | |||||
"Interval for swapper to try faultin killed processes on OOM"); | |||||
static int vm_pageout_req_swapout; /* XXX */ | static int vm_pageout_req_swapout; /* XXX */ | ||||
static int vm_daemon_needed; | static int vm_daemon_needed; | ||||
static struct mtx vm_daemon_mtx; | static struct mtx vm_daemon_mtx; | ||||
/* Allow for use by vm_pageout before vm_daemon is initialized. */ | /* Allow for use by vm_pageout before vm_daemon is initialized. */ | ||||
MTX_SYSINIT(vm_daemon, &vm_daemon_mtx, "vm daemon", MTX_DEF); | MTX_SYSINIT(vm_daemon, &vm_daemon_mtx, "vm daemon", MTX_DEF); | ||||
static void swapclear(struct proc *); | static void swapclear(struct proc *); | ||||
static int swapout(struct proc *); | static int swapout(struct proc *); | ||||
static void vm_swapout_map_deactivate_pages(vm_map_t, long); | static void vm_swapout_map_deactivate_pages(vm_map_t, long); | ||||
static void vm_swapout_object_deactivate_pages(pmap_t, vm_object_t, long); | static void vm_swapout_object_deactivate_pages(pmap_t, vm_object_t, long); | ||||
static void swapout_procs(int action); | static void swapout_procs(int action); | ||||
static void vm_req_vmdaemon(int req); | static void vm_req_vmdaemon(int req); | ||||
static void vm_thread_swapin(struct thread *td); | static void vm_thread_swapin(struct thread *td, bool oom_swapin); | ||||
static void vm_thread_swapout(struct thread *td); | static void vm_thread_swapout(struct thread *td); | ||||
/* | /* | ||||
* vm_swapout_object_deactivate_pages | * vm_swapout_object_deactivate_pages | ||||
* | * | ||||
* Deactivate enough pages to satisfy the inactive target | * Deactivate enough pages to satisfy the inactive target | ||||
* requirements. | * requirements. | ||||
* | * | ||||
Show All 22 Lines | for (object = first_object;; object = backing_object) { | ||||
if (object->shadow_count > 1) | if (object->shadow_count > 1) | ||||
remove_mode = 1; | remove_mode = 1; | ||||
/* | /* | ||||
* Scan the object's entire memory queue. | * Scan the object's entire memory queue. | ||||
*/ | */ | ||||
TAILQ_FOREACH(p, &object->memq, listq) { | TAILQ_FOREACH(p, &object->memq, listq) { | ||||
if (pmap_resident_count(pmap) <= desired) | if (pmap_resident_count(pmap) <= desired) | ||||
goto unlock_return; | goto unlock_return; | ||||
if (should_yield()) | if (should_yield()) | ||||
goto unlock_return; | goto unlock_return; | ||||
alc: I'm okay with this change and ... | |||||
if (vm_page_busied(p)) | if (vm_page_busied(p)) | ||||
continue; | continue; | ||||
VM_CNT_INC(v_pdpages); | VM_CNT_INC(v_pdpages); | ||||
vm_page_lock(p); | vm_page_lock(p); | ||||
if (vm_page_held(p) || | if (vm_page_held(p) || | ||||
!pmap_page_exists_quick(pmap, p)) { | !pmap_page_exists_quick(pmap, p)) { | ||||
vm_page_unlock(p); | vm_page_unlock(p); | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
vm_swapout_map_deactivate_pages(vm_map_t map, long desired) | vm_swapout_map_deactivate_pages(vm_map_t map, long desired) | ||||
{ | { | ||||
vm_map_entry_t tmpe; | vm_map_entry_t tmpe; | ||||
vm_object_t obj, bigobj; | vm_object_t obj, bigobj; | ||||
int nothingwired; | int nothingwired; | ||||
if (!vm_map_trylock_read(map)) | if (!vm_map_trylock_read(map)) | ||||
Not Done Inline ActionsThis and the below unlock changes are obviously correct and an improvement. Go ahead and commit them. alc: This and the below unlock changes are obviously correct and an improvement. Go ahead and… | |||||
return; | return; | ||||
bigobj = NULL; | bigobj = NULL; | ||||
nothingwired = TRUE; | nothingwired = TRUE; | ||||
/* | /* | ||||
* first, search out the biggest object, and try to free pages from | * first, search out the biggest object, and try to free pages from | ||||
* that. | * that. | ||||
▲ Show 20 Lines • Show All 244 Lines • ▼ Show 20 Lines | #ifdef RACCT | ||||
} | } | ||||
#endif | #endif | ||||
vmspace_free(vm); | vmspace_free(vm); | ||||
sx_slock(&allproc_lock); | sx_slock(&allproc_lock); | ||||
PRELE(p); | PRELE(p); | ||||
} | } | ||||
sx_sunlock(&allproc_lock); | sx_sunlock(&allproc_lock); | ||||
if (tryagain != 0 && attempts <= 10) { | if (tryagain != 0 && attempts <= 10) { | ||||
maybe_yield(); | maybe_yield(); | ||||
Not Done Inline Actions... this change. Go ahead and commit them. alc: ... this change. Go ahead and commit them. | |||||
goto again; | goto again; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Allow a thread's kernel stack to be paged out. | * Allow a thread's kernel stack to be paged out. | ||||
*/ | */ | ||||
Show All 20 Lines | vm_thread_swapout(struct thread *td) | ||||
} | } | ||||
VM_OBJECT_WUNLOCK(ksobj); | VM_OBJECT_WUNLOCK(ksobj); | ||||
} | } | ||||
/* | /* | ||||
* Bring the kernel stack for a specified thread back in. | * Bring the kernel stack for a specified thread back in. | ||||
*/ | */ | ||||
static void | static void | ||||
vm_thread_swapin(struct thread *td) | vm_thread_swapin(struct thread *td, bool oom_swapin) | ||||
{ | { | ||||
vm_object_t ksobj; | vm_object_t ksobj; | ||||
vm_page_t ma[KSTACK_MAX_PAGES]; | vm_page_t ma[KSTACK_MAX_PAGES]; | ||||
int a, count, i, j, pages, rv; | int a, count, i, j, pages, rv; | ||||
Not Done Inline Actions"j" is out of order. Include "i" here too? (Whatever you decide to do about "i", go ahead and commit this.) alc: "j" is out of order. Include "i" here too?
(Whatever you decide to do about "i", go ahead and… | |||||
pages = td->td_kstack_pages; | pages = td->td_kstack_pages; | ||||
ksobj = td->td_kstack_obj; | ksobj = td->td_kstack_obj; | ||||
VM_OBJECT_WLOCK(ksobj); | VM_OBJECT_WLOCK(ksobj); | ||||
(void)vm_page_grab_pages(ksobj, 0, VM_ALLOC_NORMAL | VM_ALLOC_WIRED, ma, | (void)vm_page_grab_pages(ksobj, 0, (oom_swapin ? VM_ALLOC_SYSTEM : | ||||
pages); | VM_ALLOC_NORMAL) | VM_ALLOC_WIRED, ma, pages); | ||||
for (i = 0; i < pages;) { | for (i = 0; i < pages;) { | ||||
vm_page_assert_xbusied(ma[i]); | vm_page_assert_xbusied(ma[i]); | ||||
if (ma[i]->valid == VM_PAGE_BITS_ALL) { | if (ma[i]->valid == VM_PAGE_BITS_ALL) { | ||||
vm_page_xunbusy(ma[i]); | vm_page_xunbusy(ma[i]); | ||||
i++; | i++; | ||||
continue; | continue; | ||||
} | } | ||||
vm_object_pip_add(ksobj, 1); | vm_object_pip_add(ksobj, 1); | ||||
Show All 11 Lines | for (j = i; j < i + count; j++) | ||||
vm_page_xunbusy(ma[j]); | vm_page_xunbusy(ma[j]); | ||||
i += count; | i += count; | ||||
} | } | ||||
VM_OBJECT_WUNLOCK(ksobj); | VM_OBJECT_WUNLOCK(ksobj); | ||||
pmap_qenter(td->td_kstack, ma, pages); | pmap_qenter(td->td_kstack, ma, pages); | ||||
cpu_thread_swapin(td); | cpu_thread_swapin(td); | ||||
} | } | ||||
void | static void | ||||
faultin(struct proc *p) | faultin1(struct proc *p, bool oom_swapin) | ||||
{ | { | ||||
struct thread *td; | struct thread *td; | ||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | ||||
/* | /* | ||||
* If another process is swapping in this process, | * If another process is swapping in this process, | ||||
* just wait until it finishes. | * just wait until it finishes. | ||||
*/ | */ | ||||
Show All 12 Lines | if ((p->p_flag & P_INMEM) == 0) { | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
/* | /* | ||||
* We hold no lock here because the list of threads | * We hold no lock here because the list of threads | ||||
* can not change while all threads in the process are | * can not change while all threads in the process are | ||||
* swapped out. | * swapped out. | ||||
*/ | */ | ||||
FOREACH_THREAD_IN_PROC(p, td) | FOREACH_THREAD_IN_PROC(p, td) | ||||
vm_thread_swapin(td); | vm_thread_swapin(td, oom_swapin); | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
swapclear(p); | swapclear(p); | ||||
p->p_swtick = ticks; | p->p_swtick = ticks; | ||||
wakeup(&p->p_flag); | wakeup(&p->p_flag); | ||||
/* Allow other threads to swap p out now. */ | /* Allow other threads to swap p out now. */ | ||||
--p->p_lock; | --p->p_lock; | ||||
} | } | ||||
} | } | ||||
void | |||||
faultin(struct proc *p) | |||||
{ | |||||
faultin1(p, false); | |||||
} | |||||
int wkilled; | |||||
/* | /* | ||||
* This swapin algorithm attempts to swap-in processes only if there | * This swapin algorithm attempts to swap-in processes only if there | ||||
* is enough space for them. Of course, if a process waits for a long | * is enough space for them. Of course, if a process waits for a long | ||||
* time, it will be swapped in anyway. | * time, it will be swapped in anyway. | ||||
*/ | */ | ||||
void | void | ||||
swapper(void) | swapper(void) | ||||
{ | { | ||||
struct proc *p, *pp; | struct proc *p, *pp; | ||||
Not Done Inline ActionsMerge the "*pp" definition into this one? alc: Merge the "*pp" definition into this one? | |||||
struct thread *td; | struct thread *td; | ||||
int ppri, pri, slptime, swtime; | int min_flag, ppri, pri, slptime, swtime; | ||||
Not Done Inline ActionsGo ahead and commit this with the other variable definition style fixes. alc: Go ahead and commit this with the other variable definition style fixes. | |||||
loop: | loop: | ||||
if (vm_page_count_min()) { | |||||
vm_wait_min(); | |||||
Not Done Inline ActionsAre you sure that you wanted to delete this vm_wait_min()? Doing so allows for swapping in processes in the later loop. alc: Are you sure that you wanted to delete this vm_wait_min()? Doing so allows for swapping in… | |||||
Not Done Inline ActionsFor the loop which brings in WKILLED processes, I definitely wanted to remove the wait. In fact, I considered moving the faultin() logic for the WKILLED processes to the separate thread, to not wait for the faulting the stack of other processes, or the "swapin" sleep. With your proposal, I can add a racy wakeup to avoid tsleep(). kib: For the loop which brings in WKILLED processes, I definitely wanted to remove the wait.
In… | |||||
goto loop; | |||||
} | |||||
pp = NULL; | pp = NULL; | ||||
Done Inline ActionsIf a swapped out process is killed, shouldn't we try to dispose of it regardless of whether vm_page_count_min() is true? alc: If a swapped out process is killed, shouldn't we try to dispose of it regardless of whether… | |||||
ppri = INT_MIN; | ppri = INT_MIN; | ||||
min_flag = vm_page_count_min(); | |||||
sx_slock(&allproc_lock); | sx_slock(&allproc_lock); | ||||
FOREACH_PROC_IN_SYSTEM(p) { | FOREACH_PROC_IN_SYSTEM(p) { | ||||
alcUnsubmitted Not Done Inline ActionsIs there a reason that we don't maintain a count of swapped out processes so that we can skip this loop when that count is zero? alc: Is there a reason that we don't maintain a count of swapped out processes so that we can skip… | |||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
if (p->p_state == PRS_NEW || | if (p->p_state == PRS_NEW || (p->p_flag & (P_SWAPPINGOUT | | ||||
p->p_flag & (P_SWAPPINGOUT | P_SWAPPINGIN | P_INMEM)) { | P_SWAPPINGIN | P_INMEM)) != 0) { | ||||
PROC_UNLOCK(p); | |||||
continue; | |||||
} | |||||
if (p->p_state == PRS_NORMAL && (p->p_flag & P_WKILLED) != 0) { | |||||
/* | |||||
* A swapped-out process might have mapped a | |||||
* large !portion of the system's pages as | |||||
* anonymous memory. There is no other way to | |||||
* release the memory other !than to kill the | |||||
* process, for which we need to swap !it in. | |||||
*/ | |||||
sx_sunlock(&allproc_lock); | |||||
faultin1(p, true); | |||||
PROC_UNLOCK(p); | |||||
goto loop; | |||||
} | |||||
if (min_flag) { | |||||
Not Done Inline ActionsI'd like to suggest a different way of structuring this. In essence, move the P_WKILLED check into the below, existing FOREACH_PROC_IN_SYSTEM(p) loop. In more detail, I'm suggesting: loop: min_flag = vm_page_count_min(); ... FOREACH_PROC_IN_SYSTEM(p) { PROC_LOCK(p); if (p->p_state == PRS_NEW || p->p_flag & (P_SWAPPINGOUT | P_SWAPPINGIN | P_INMEM)) { PROC_UNLOCK(p); continue; } if (p->p_state == PRS_NORMAL && (p->p_flag & P_WKILLED) != 0) { ... } if (min_flag) { PROC_UNLOCK(p); continue; } ... I see the point of not swapping in a process when vm_page_count_min(), but I'm not sure that I see the point of implementing the delay with vm_wait_min(). The below tsleep() would suffice. alc: I'd like to suggest a different way of structuring this. In essence, move the P_WKILLED check… | |||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
continue; | continue; | ||||
Not Done Inline ActionsYou are multiplying by "hz" twice, here and in pagedaemon_wait(). alc: You are multiplying by "hz" twice, here and in pagedaemon_wait(). | |||||
} | } | ||||
swtime = (ticks - p->p_swtick) / hz; | swtime = (ticks - p->p_swtick) / hz; | ||||
FOREACH_THREAD_IN_PROC(p, td) { | FOREACH_THREAD_IN_PROC(p, td) { | ||||
/* | /* | ||||
* An otherwise runnable thread of a process | * An otherwise runnable thread of a process | ||||
* swapped out has only the TDI_SWAPPED bit set. | * swapped out has only the TDI_SWAPPED bit set. | ||||
*/ | */ | ||||
thread_lock(td); | thread_lock(td); | ||||
Show All 34 Lines | loop: | ||||
* Or, this process may even be being swapped out again. | * Or, this process may even be being swapped out again. | ||||
*/ | */ | ||||
if (p->p_flag & (P_INMEM | P_SWAPPINGOUT | P_SWAPPINGIN)) { | if (p->p_flag & (P_INMEM | P_SWAPPINGOUT | P_SWAPPINGIN)) { | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
goto loop; | goto loop; | ||||
} | } | ||||
/* | /* | ||||
* We would like to bring someone in. | * We would like to bring someone in. | ||||
Not Done Inline ActionsGo ahead and commit this change to the comment. It seems unrelated to the rest anyway. alc: Go ahead and commit this change to the comment. It seems unrelated to the rest anyway. | |||||
*/ | */ | ||||
faultin(p); | faultin(p); | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
goto loop; | goto loop; | ||||
} | } | ||||
/* | /* | ||||
* First, if any processes have been sleeping or stopped for at least | * First, if any processes have been sleeping or stopped for at least | ||||
▲ Show 20 Lines • Show All 169 Lines • Show Last 20 Lines |
I'm okay with this change and ...