summaryrefslogtreecommitdiffstats
path: root/mm/vmscan.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /mm/vmscan.c
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'mm/vmscan.c')
-rw-r--r--mm/vmscan.c171
1 files changed, 89 insertions, 82 deletions
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 1ae052b94..8ee000fc0 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -17,6 +17,7 @@
#include <linux/smp_lock.h>
#include <linux/pagemap.h>
#include <linux/init.h>
+#include <linux/bigmem.h>
#include <asm/pgtable.h>
@@ -31,8 +32,7 @@
* using a process that no longer actually exists (it might
* have died while we slept).
*/
-static int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
- unsigned long address, pte_t * page_table, int gfp_mask)
+static int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pte_t * page_table, int gfp_mask)
{
pte_t pte;
unsigned long entry;
@@ -47,15 +47,12 @@ static int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
goto out_failed;
page = mem_map + MAP_NR(page_addr);
- spin_lock(&tsk->mm->page_table_lock);
+ spin_lock(&vma->vm_mm->page_table_lock);
if (pte_val(pte) != pte_val(*page_table))
goto out_failed_unlock;
- /*
- * Dont be too eager to get aging right if
- * memory is dangerously low.
- */
- if (!low_on_memory && pte_young(pte)) {
+ /* Don't look at this pte if it's been accessed recently. */
+ if (pte_young(pte)) {
/*
* Transfer the "accessed" bit from the page
* tables to the global page map.
@@ -67,7 +64,8 @@ static int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
if (PageReserved(page)
|| PageLocked(page)
- || ((gfp_mask & __GFP_DMA) && !PageDMA(page)))
+ || ((gfp_mask & __GFP_DMA) && !PageDMA(page))
+ || (!(gfp_mask & __GFP_BIGMEM) && PageBIGMEM(page)))
goto out_failed_unlock;
/*
@@ -136,15 +134,16 @@ drop_pte:
*/
flush_cache_page(vma, address);
if (vma->vm_ops && vma->vm_ops->swapout) {
- pid_t pid = tsk->pid;
+ int error;
pte_clear(page_table);
- spin_unlock(&tsk->mm->page_table_lock);
+ spin_unlock(&vma->vm_mm->page_table_lock);
flush_tlb_page(vma, address);
vma->vm_mm->rss--;
-
- if (vma->vm_ops->swapout(vma, page))
- kill_proc(pid, SIGBUS, 1);
- goto out_free_success;
+ error = vma->vm_ops->swapout(vma, page);
+ if (!error)
+ goto out_free_success;
+ __free_page(page);
+ return error;
}
/*
@@ -153,14 +152,16 @@ drop_pte:
* we have the swap cache set up to associate the
* page with that swap entry.
*/
- entry = get_swap_page();
+ entry = acquire_swap_entry(page);
if (!entry)
- goto out_failed; /* No swap space left */
+ goto out_failed_unlock; /* No swap space left */
+ if (!(page = prepare_bigmem_swapout(page)))
+ goto out_swap_free_unlock;
+
vma->vm_mm->rss--;
- tsk->nswap++;
set_pte(page_table, __pte(entry));
- spin_unlock(&tsk->mm->page_table_lock);
+ spin_unlock(&vma->vm_mm->page_table_lock);
flush_tlb_page(vma, address);
swap_duplicate(entry); /* One for the process, one for the swap cache */
@@ -175,9 +176,14 @@ out_free_success:
__free_page(page);
return 1;
out_failed_unlock:
- spin_unlock(&tsk->mm->page_table_lock);
+ spin_unlock(&vma->vm_mm->page_table_lock);
out_failed:
return 0;
+out_swap_free_unlock:
+ swap_free(entry);
+ spin_unlock(&vma->vm_mm->page_table_lock);
+ return 0;
+
}
/*
@@ -194,8 +200,7 @@ out_failed:
* (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de
*/
-static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * vma,
- pmd_t *dir, unsigned long address, unsigned long end, int gfp_mask)
+static inline int swap_out_pmd(struct vm_area_struct * vma, pmd_t *dir, unsigned long address, unsigned long end, int gfp_mask)
{
pte_t * pte;
unsigned long pmd_end;
@@ -216,8 +221,8 @@ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct *
do {
int result;
- tsk->mm->swap_address = address + PAGE_SIZE;
- result = try_to_swap_out(tsk, vma, address, pte, gfp_mask);
+ vma->vm_mm->swap_address = address + PAGE_SIZE;
+ result = try_to_swap_out(vma, address, pte, gfp_mask);
if (result)
return result;
address += PAGE_SIZE;
@@ -226,8 +231,7 @@ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct *
return 0;
}
-static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct * vma,
- pgd_t *dir, unsigned long address, unsigned long end, int gfp_mask)
+static inline int swap_out_pgd(struct vm_area_struct * vma, pgd_t *dir, unsigned long address, unsigned long end, int gfp_mask)
{
pmd_t * pmd;
unsigned long pgd_end;
@@ -247,7 +251,7 @@ static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct *
end = pgd_end;
do {
- int result = swap_out_pmd(tsk, vma, pmd, address, end, gfp_mask);
+ int result = swap_out_pmd(vma, pmd, address, end, gfp_mask);
if (result)
return result;
address = (address + PMD_SIZE) & PMD_MASK;
@@ -256,8 +260,7 @@ static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct *
return 0;
}
-static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
- unsigned long address, int gfp_mask)
+static int swap_out_vma(struct vm_area_struct * vma, unsigned long address, int gfp_mask)
{
pgd_t *pgdir;
unsigned long end;
@@ -266,11 +269,11 @@ static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
if (vma->vm_flags & VM_LOCKED)
return 0;
- pgdir = pgd_offset(tsk->mm, address);
+ pgdir = pgd_offset(vma->vm_mm, address);
end = vma->vm_end;
while (address < end) {
- int result = swap_out_pgd(tsk, vma, pgdir, address, end, gfp_mask);
+ int result = swap_out_pgd(vma, pgdir, address, end, gfp_mask);
if (result)
return result;
address = (address + PGDIR_SIZE) & PGDIR_MASK;
@@ -279,7 +282,7 @@ static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
return 0;
}
-static int swap_out_process(struct task_struct * p, int gfp_mask)
+static int swap_out_mm(struct mm_struct * mm, int gfp_mask)
{
unsigned long address;
struct vm_area_struct* vma;
@@ -287,18 +290,18 @@ static int swap_out_process(struct task_struct * p, int gfp_mask)
/*
* Go through process' page directory.
*/
- address = p->mm->swap_address;
+ address = mm->swap_address;
/*
* Find the proper vm-area
*/
- vma = find_vma(p->mm, address);
+ vma = find_vma(mm, address);
if (vma) {
if (address < vma->vm_start)
address = vma->vm_start;
for (;;) {
- int result = swap_out_vma(p, vma, address, gfp_mask);
+ int result = swap_out_vma(vma, address, gfp_mask);
if (result)
return result;
vma = vma->vm_next;
@@ -309,8 +312,8 @@ static int swap_out_process(struct task_struct * p, int gfp_mask)
}
/* We didn't find anything for the process */
- p->mm->swap_cnt = 0;
- p->mm->swap_address = 0;
+ mm->swap_cnt = 0;
+ mm->swap_address = 0;
return 0;
}
@@ -321,9 +324,11 @@ static int swap_out_process(struct task_struct * p, int gfp_mask)
*/
static int swap_out(unsigned int priority, int gfp_mask)
{
- struct task_struct * p, * pbest;
- int counter, assign, max_cnt;
+ struct task_struct * p;
+ int counter;
+ int __ret = 0;
+ lock_kernel();
/*
* We make one or two passes through the task list, indexed by
* assign = {0, 1}:
@@ -338,46 +343,61 @@ static int swap_out(unsigned int priority, int gfp_mask)
* Think of swap_cnt as a "shadow rss" - it tells us which process
* we want to page out (always try largest first).
*/
- counter = nr_tasks / (priority+1);
+ counter = nr_threads / (priority+1);
if (counter < 1)
counter = 1;
- if (counter > nr_tasks)
- counter = nr_tasks;
+ if (counter > nr_threads)
+ counter = nr_threads;
for (; counter >= 0; counter--) {
- assign = 0;
- max_cnt = 0;
- pbest = NULL;
+ int assign = 0;
+ int max_cnt = 0;
+ struct mm_struct *best = NULL;
+ int pid = 0;
select:
read_lock(&tasklist_lock);
p = init_task.next_task;
for (; p != &init_task; p = p->next_task) {
- if (!p->swappable)
+ struct mm_struct *mm = p->mm;
+ if (!p->swappable || !mm)
continue;
- if (p->mm->rss <= 0)
+ if (mm->rss <= 0)
continue;
/* Refresh swap_cnt? */
if (assign)
- p->mm->swap_cnt = p->mm->rss;
- if (p->mm->swap_cnt > max_cnt) {
- max_cnt = p->mm->swap_cnt;
- pbest = p;
+ mm->swap_cnt = mm->rss;
+ if (mm->swap_cnt > max_cnt) {
+ max_cnt = mm->swap_cnt;
+ best = mm;
+ pid = p->pid;
}
}
read_unlock(&tasklist_lock);
- if (!pbest) {
+ if (!best) {
if (!assign) {
assign = 1;
goto select;
}
goto out;
- }
+ } else {
+ int ret;
+
+ atomic_inc(&best->mm_count);
+ ret = swap_out_mm(best, gfp_mask);
+ mmdrop(best);
+
+ if (!ret)
+ continue;
- if (swap_out_process(pbest, gfp_mask))
- return 1;
+ if (ret < 0)
+ kill_proc(pid, SIGBUS, 1);
+ __ret = 1;
+ goto out;
+ }
}
out:
- return 0;
+ unlock_kernel();
+ return __ret;
}
/*
@@ -394,8 +414,6 @@ static int do_try_to_free_pages(unsigned int gfp_mask)
int priority;
int count = SWAP_CLUSTER_MAX;
- lock_kernel();
-
/* Always trim SLAB caches when memory gets low. */
kmem_cache_reap(gfp_mask);
@@ -423,32 +441,10 @@ static int do_try_to_free_pages(unsigned int gfp_mask)
shrink_dcache_memory(priority, gfp_mask);
} while (--priority >= 0);
done:
- unlock_kernel();
return priority >= 0;
}
-/*
- * Before we start the kernel thread, print out the
- * kswapd initialization message (otherwise the init message
- * may be printed in the middle of another driver's init
- * message). It looks very bad when that happens.
- */
-void __init kswapd_setup(void)
-{
- int i;
- char *revision="$Revision: 1.5 $", *s, *e;
-
- swap_setup();
-
- if ((s = strchr(revision, ':')) &&
- (e = strchr(s, '$')))
- s++, i = e - s;
- else
- s = revision, i = -1;
- printk ("Starting kswapd v%.*s\n", i, s);
-}
-
static struct task_struct *kswapd_process;
/*
@@ -499,7 +495,9 @@ int kswapd(void *unused)
* up on a more timely basis.
*/
do {
- if (nr_free_pages >= freepages.high)
+ /* kswapd is critical to provide GFP_ATOMIC
+ allocations (not GFP_BIGMEM ones). */
+ if (nr_free_pages - nr_free_bigpages >= freepages.high)
break;
if (!do_try_to_free_pages(GFP_KSWAPD))
@@ -535,4 +533,13 @@ int try_to_free_pages(unsigned int gfp_mask)
retval = do_try_to_free_pages(gfp_mask);
return retval;
}
-
+
+static int __init kswapd_init(void)
+{
+ printk("Starting kswapd v1.6\n");
+ swap_setup();
+ kernel_thread(kswapd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ return 0;
+}
+
+module_init(kswapd_init)