📄 vmscan.c
字号:
* or if all zones have a minor shortage.
*/
int free_shortage(void)
{
pg_data_t *pgdat = pgdat_list;
int sum = 0;
int freeable = nr_free_pages() + nr_inactive_clean_pages();
int freetarget = freepages.high + inactive_target / 3;
/* Are we low on free pages globally? */
if (freeable < freetarget)
return freetarget - freeable;
/* If not, are we very low on any particular zone? */
do {
int i;
for(i = 0; i < MAX_NR_ZONES; i++) {
zone_t *zone = pgdat->node_zones+ i;
if (zone->size && (zone->inactive_clean_pages +
zone->free_pages < zone->pages_min+1)) {
/* + 1 to have overlap with alloc_pages() !! */
sum += zone->pages_min + 1;
sum -= zone->free_pages;
sum -= zone->inactive_clean_pages;
}
}
pgdat = pgdat->node_next;
} while (pgdat);
return sum;
}
/*
* How many inactive pages are we short?
*/
int inactive_shortage(void)
{
int shortage = 0;
shortage += freepages.high;
shortage += inactive_target;
shortage -= nr_free_pages();
shortage -= nr_inactive_clean_pages();
shortage -= nr_inactive_dirty_pages;
if (shortage > 0)
return shortage;
return 0;
}
/*
* We need to make the locks finer granularity, but right
* now we need this so that we can do page allocations
* without holding the kernel lock etc.
*
* We want to try to free "count" pages, and we want to
* cluster them so that we get good swap-out behaviour.
*
* OTOH, if we're a user process (and not kswapd), we
* really care about latency. In that case we don't try
* to free too many pages.
*/
static int refill_inactive(unsigned int gfp_mask, int user)
{
int priority, count, start_count, made_progress;
count = inactive_shortage() + free_shortage();
if (user)
count = (1 << page_cluster);
start_count = count;
/* Always trim SLAB caches when memory gets low. */
kmem_cache_reap(gfp_mask);
priority = 6;
do {
made_progress = 0;
if (current->need_resched) {
__set_current_state(TASK_RUNNING);
schedule();
}
while (refill_inactive_scan(priority, 1)) {
made_progress = 1;
if (--count <= 0)
goto done;
}
/*
* don't be too light against the d/i cache since
* refill_inactive() almost never fail when there's
* really plenty of memory free.
*/
shrink_dcache_memory(priority, gfp_mask);
shrink_icache_memory(priority, gfp_mask);
/*
* Then, try to page stuff out..
*/
while (swap_out(priority, gfp_mask)) {
made_progress = 1;
if (--count <= 0)
goto done;
}
/*
* If we either have enough free memory, or if
* page_launder() will be able to make enough
* free memory, then stop.
*/
if (!inactive_shortage() || !free_shortage())
goto done;
/*
* Only switch to a lower "priority" if we
* didn't make any useful progress in the
* last loop.
*/
if (!made_progress)
priority--;
} while (priority >= 0);
/* Always end on a refill_inactive.., may sleep... */
while (refill_inactive_scan(0, 1)) {
if (--count <= 0)
goto done;
}
done:
return (count < start_count);
}
static int do_try_to_free_pages(unsigned int gfp_mask, int user)
{
int ret = 0;
/*
* If we're low on free pages, move pages from the
* inactive_dirty list to the inactive_clean list.
*
* Usually bdflush will have pre-cleaned the pages
* before we get around to moving them to the other
* list, so this is a relatively cheap operation.
*/
if (free_shortage() || nr_inactive_dirty_pages > nr_free_pages() +
nr_inactive_clean_pages())
ret += page_launder(gfp_mask, user);
/*
* If needed, we move pages from the active list
* to the inactive list. We also "eat" pages from
* the inode and dentry cache whenever we do this.
*/
if (free_shortage() || inactive_shortage()) {
shrink_dcache_memory(6, gfp_mask);
shrink_icache_memory(6, gfp_mask);
ret += refill_inactive(gfp_mask, user);
} else {
/*
* Reclaim unused slab cache memory.
*/
kmem_cache_reap(gfp_mask);
ret = 1;
}
return ret;
}
DECLARE_WAIT_QUEUE_HEAD(kswapd_wait);
DECLARE_WAIT_QUEUE_HEAD(kswapd_done);
struct task_struct *kswapd_task;
/*
* The background pageout daemon, started as a kernel thread
* from the init process.
*
* This basically trickles out pages so that we have _some_
* free memory available even if there is no other activity
* that frees anything up. This is needed for things like routing
* etc, where we otherwise might have all activity going on in
* asynchronous contexts that cannot page things out.
*
* If there are applications that are active memory-allocators
* (most normal use), this basically shouldn't matter.
*/
int kswapd(void *unused)
{
struct task_struct *tsk = current;
tsk->session = 1;
tsk->pgrp = 1;
strcpy(tsk->comm, "kswapd");
sigfillset(&tsk->blocked);
kswapd_task = tsk;
/*
* Tell the memory management that we're a "memory allocator",
* and that if we need more memory we should get access to it
* regardless (see "__alloc_pages()"). "kswapd" should
* never get caught in the normal page freeing logic.
*
* (Kswapd normally doesn't need memory anyway, but sometimes
* you need a small amount of memory in order to be able to
* page out something else, and this flag essentially protects
* us from recursively trying to free more memory as we're
* trying to free the first piece of memory in the first place).
*/
tsk->flags |= PF_MEMALLOC;
/*
* Kswapd main loop.
*/
for (;;) {
static int recalc = 0;
/* If needed, try to free some memory. */
if (inactive_shortage() || free_shortage()) {
int wait = 0;
/* Do we need to do some synchronous flushing? */
if (waitqueue_active(&kswapd_done))
wait = 1;
do_try_to_free_pages(GFP_KSWAPD, wait);
}
/*
* Do some (very minimal) background scanning. This
* will scan all pages on the active list once
* every minute. This clears old referenced bits
* and moves unused pages to the inactive list.
*/
refill_inactive_scan(6, 0);
/* Once a second, recalculate some VM stats. */
if (time_after(jiffies, recalc + HZ)) {
recalc = jiffies;
recalculate_vm_stats();
}
/*
* Wake up everybody waiting for free memory
* and unplug the disk queue.
*/
wake_up_all(&kswapd_done);
run_task_queue(&tq_disk);
/*
* We go to sleep if either the free page shortage
* or the inactive page shortage is gone. We do this
* because:
* 1) we need no more free pages or
* 2) the inactive pages need to be flushed to disk,
* it wouldn't help to eat CPU time now ...
*
* We go to sleep for one second, but if it's needed
* we'll be woken up earlier...
*/
if (!free_shortage() || !inactive_shortage()) {
interruptible_sleep_on_timeout(&kswapd_wait, HZ);
/*
* If we couldn't free enough memory, we see if it was
* due to the system just not having enough memory.
* If that is the case, the only solution is to kill
* a process (the alternative is enternal deadlock).
*
* If there still is enough memory around, we just loop
* and try free some more memory...
*/
} else if (out_of_memory()) {
oom_kill();
}
}
}
void wakeup_kswapd(int block)
{
DECLARE_WAITQUEUE(wait, current);
if (current == kswapd_task)
return;
if (!block) {
if (waitqueue_active(&kswapd_wait))
wake_up(&kswapd_wait);
return;
}
/*
* Kswapd could wake us up before we get a chance
* to sleep, so we have to be very careful here to
* prevent SMP races...
*/
__set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&kswapd_done, &wait);
if (waitqueue_active(&kswapd_wait))
wake_up(&kswapd_wait);
schedule();
remove_wait_queue(&kswapd_done, &wait);
__set_current_state(TASK_RUNNING);
}
/*
* Called by non-kswapd processes when they want more
* memory but are unable to sleep on kswapd because
* they might be holding some IO locks ...
*/
int try_to_free_pages(unsigned int gfp_mask)
{
int ret = 1;
if (gfp_mask & __GFP_WAIT) {
current->flags |= PF_MEMALLOC;
ret = do_try_to_free_pages(gfp_mask, 1);
current->flags &= ~PF_MEMALLOC;
}
return ret;
}
DECLARE_WAIT_QUEUE_HEAD(kreclaimd_wait);
/*
* Kreclaimd will move pages from the inactive_clean list to the
* free list, in order to keep atomic allocations possible under
* all circumstances. Even when kswapd is blocked on IO.
*/
int kreclaimd(void *unused)
{
struct task_struct *tsk = current;
pg_data_t *pgdat;
tsk->session = 1;
tsk->pgrp = 1;
strcpy(tsk->comm, "kreclaimd");
sigfillset(&tsk->blocked);
current->flags |= PF_MEMALLOC;
while (1) {
/*
* We sleep until someone wakes us up from
* page_alloc.c::__alloc_pages().
*/
interruptible_sleep_on(&kreclaimd_wait);
/*
* Move some pages from the inactive_clean lists to
* the free lists, if it is needed.
*/
pgdat = pgdat_list;
do {
int i;
for(i = 0; i < MAX_NR_ZONES; i++) {
zone_t *zone = pgdat->node_zones + i;
if (!zone->size)
continue;
while (zone->free_pages < zone->pages_low) {
struct page * page;
page = reclaim_page(zone);
if (!page)
break;
__free_page(page);
}
}
pgdat = pgdat->node_next;
} while (pgdat);
}
}
static int __init kswapd_init(void)
{
printk("Starting kswapd v1.8\n");
swap_setup();
kernel_thread(kswapd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
kernel_thread(kreclaimd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
return 0;
}
module_init(kswapd_init)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -