summaryrefslogtreecommitdiffstats
path: root/mm/memory.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
commit545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch)
treee9ce4bc598d06374bda906f18365984bf22a526a /mm/memory.c
parent4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff)
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'mm/memory.c')
-rw-r--r--mm/memory.c63
1 files changed, 40 insertions, 23 deletions
diff --git a/mm/memory.c b/mm/memory.c
index 3e94fd1f3..772fbbec2 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -143,8 +143,7 @@ void clear_page_tables(struct task_struct * tsk)
/*
* This function frees up all page tables of a process when it exits. It
- * is the same as "clear_page_tables()", except it also changes the process'
- * page table directory to the kernel page tables and then frees the old
+ * is the same as "clear_page_tables()", except it also frees the old
* page table directory.
*/
void free_page_tables(struct mm_struct * mm)
@@ -153,13 +152,15 @@ void free_page_tables(struct mm_struct * mm)
pgd_t * page_dir;
page_dir = mm->pgd;
- if (!page_dir || page_dir == swapper_pg_dir) {
- printk("Trying to free kernel page-directory: not good\n");
- return;
+ if (page_dir) {
+ if (page_dir == swapper_pg_dir) {
+ printk("free_page_tables: Trying to free kernel pgd\n");
+ return;
+ }
+ for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
+ free_one_pgd(page_dir + i);
+ pgd_free(page_dir);
}
- for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
- free_one_pgd(page_dir + i);
- pgd_free(page_dir);
}
int new_page_tables(struct task_struct * tsk)
@@ -291,19 +292,20 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
return error;
}
-static inline void free_pte(pte_t page)
+/*
+ * Return indicates whether a page was freed so caller can adjust rss
+ */
+static inline int free_pte(pte_t page)
{
if (pte_present(page)) {
unsigned long addr = pte_page(page);
if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr)))
- return;
+ return 0;
free_page(addr);
- if (current->mm->rss <= 0)
- return;
- current->mm->rss--;
- return;
+ return 1;
}
swap_free(pte_val(page));
+ return 0;
}
static inline void forget_pte(pte_t page)
@@ -314,22 +316,24 @@ static inline void forget_pte(pte_t page)
}
}
-static inline void zap_pte_range(pmd_t * pmd, unsigned long address, unsigned long size)
+static inline int zap_pte_range(pmd_t * pmd, unsigned long address, unsigned long size)
{
pte_t * pte;
+ int freed;
if (pmd_none(*pmd))
- return;
+ return 0;
if (pmd_bad(*pmd)) {
printk("zap_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd));
pmd_clear(pmd);
- return;
+ return 0;
}
pte = pte_offset(pmd, address);
address &= ~PMD_MASK;
if (address + size > PMD_SIZE)
size = PMD_SIZE - address;
size >>= PAGE_SHIFT;
+ freed = 0;
for (;;) {
pte_t page;
if (!size)
@@ -340,32 +344,36 @@ static inline void zap_pte_range(pmd_t * pmd, unsigned long address, unsigned lo
if (pte_none(page))
continue;
pte_clear(pte-1);
- free_pte(page);
+ freed += free_pte(page);
}
+ return freed;
}
-static inline void zap_pmd_range(pgd_t * dir, unsigned long address, unsigned long size)
+static inline int zap_pmd_range(pgd_t * dir, unsigned long address, unsigned long size)
{
pmd_t * pmd;
unsigned long end;
+ int freed;
if (pgd_none(*dir))
- return;
+ return 0;
if (pgd_bad(*dir)) {
printk("zap_pmd_range: bad pgd (%08lx)\n", pgd_val(*dir));
pgd_clear(dir);
- return;
+ return 0;
}
pmd = pmd_offset(dir, address);
address &= ~PGDIR_MASK;
end = address + size;
if (end > PGDIR_SIZE)
end = PGDIR_SIZE;
+ freed = 0;
do {
- zap_pte_range(pmd, address, end - address);
+ freed += zap_pte_range(pmd, address, end - address);
address = (address + PMD_SIZE) & PMD_MASK;
pmd++;
} while (address < end);
+ return freed;
}
/*
@@ -375,13 +383,22 @@ void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long s
{
pgd_t * dir;
unsigned long end = address + size;
+ int freed = 0;
dir = pgd_offset(mm, address);
while (address < end) {
- zap_pmd_range(dir, address, end - address);
+ freed += zap_pmd_range(dir, address, end - address);
address = (address + PGDIR_SIZE) & PGDIR_MASK;
dir++;
}
+ /*
+ * Update rss for the mm_struct (not necessarily current->mm)
+ */
+ if (mm->rss > 0) {
+ mm->rss -= freed;
+ if (mm->rss < 0)
+ mm->rss = 0;
+ }
}
static inline void zeromap_pte_range(pte_t * pte, unsigned long address, unsigned long size, pte_t zero_pte)