aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorHugh Dickins <hugh@veritas.com>2005-01-11 01:41:06 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-11 01:41:06 -0800
commit09c2e123359cbaeb6b1ea6393fb4ec30e1d46aef (patch)
tree82a35b5ddccd37d4f655f983f7b9a915e65ec1a9 /mm
parenta6ab0c16edd9bf13db04afc7cd88cb137fe770aa (diff)
downloadhistory-09c2e123359cbaeb6b1ea6393fb4ec30e1d46aef.tar.gz
[PATCH] 4level swapoff hang fix
The 4level mods have caused 2level swapoff to miss entries and hang. There's probably a one-line fix for that, but the error is really caused by previous awkwardness - each mask applied on two levels, an "address" that's an offset plus an "offset" that's an address. Simplify the four levels to behave in the same address/next/end way and the bug vanishes. Signed-off-by: Hugh Dickins <hugh@veritas.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/swapfile.c96
1 files changed, 39 insertions, 57 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 3ef403b591d64e..547ecd9c060d5a 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -442,12 +442,11 @@ unuse_pte(struct vm_area_struct *vma, unsigned long address, pte_t *dir,
}
/* vma->vm_mm->page_table_lock is held */
-static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
- unsigned long address, unsigned long size, unsigned long offset,
+static unsigned long unuse_pmd(struct vm_area_struct *vma, pmd_t *dir,
+ unsigned long address, unsigned long end,
swp_entry_t entry, struct page *page)
{
- pte_t * pte;
- unsigned long end;
+ pte_t *pte;
pte_t swp_pte = swp_entry_to_pte(entry);
if (pmd_none(*dir))
@@ -458,18 +457,13 @@ static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
return 0;
}
pte = pte_offset_map(dir, address);
- offset += address & PMD_MASK;
- address &= ~PMD_MASK;
- end = address + size;
- if (end > PMD_SIZE)
- end = PMD_SIZE;
do {
/*
* swapoff spends a _lot_ of time in this loop!
* Test inline before going to call unuse_pte.
*/
if (unlikely(pte_same(*pte, swp_pte))) {
- unuse_pte(vma, offset + address, pte, entry, page);
+ unuse_pte(vma, address, pte, entry, page);
pte_unmap(pte);
/*
@@ -479,22 +473,22 @@ static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
activate_page(page);
/* add 1 since address may be 0 */
- return 1 + offset + address;
+ return 1 + address;
}
address += PAGE_SIZE;
pte++;
- } while (address && (address < end));
+ } while (address < end);
pte_unmap(pte - 1);
return 0;
}
/* vma->vm_mm->page_table_lock is held */
-static unsigned long unuse_pud(struct vm_area_struct * vma, pud_t *pud,
- unsigned long address, unsigned long size, unsigned long offset,
+static unsigned long unuse_pud(struct vm_area_struct *vma, pud_t *pud,
+ unsigned long address, unsigned long end,
swp_entry_t entry, struct page *page)
{
- pmd_t * pmd;
- unsigned long end;
+ pmd_t *pmd;
+ unsigned long next;
unsigned long foundaddr;
if (pud_none(*pud))
@@ -505,33 +499,27 @@ static unsigned long unuse_pud(struct vm_area_struct * vma, pud_t *pud,
return 0;
}
pmd = pmd_offset(pud, address);
- offset += address & PUD_MASK;
- address &= ~PUD_MASK;
- end = address + size;
- if (end > PUD_SIZE)
- end = PUD_SIZE;
- if (address >= end)
- BUG();
do {
- foundaddr = unuse_pmd(vma, pmd, address, end - address,
- offset, entry, page);
+ next = (address + PMD_SIZE) & PMD_MASK;
+ if (next > end || !next)
+ next = end;
+ foundaddr = unuse_pmd(vma, pmd, address, next, entry, page);
if (foundaddr)
return foundaddr;
- address = (address + PMD_SIZE) & PMD_MASK;
+ address = next;
pmd++;
- } while (address && (address < end));
+ } while (address < end);
return 0;
}
/* vma->vm_mm->page_table_lock is held */
-static unsigned long unuse_pgd(struct vm_area_struct * vma, pgd_t *pgd,
- unsigned long address, unsigned long size,
+static unsigned long unuse_pgd(struct vm_area_struct *vma, pgd_t *pgd,
+ unsigned long address, unsigned long end,
swp_entry_t entry, struct page *page)
{
- pud_t * pud;
- unsigned long offset;
+ pud_t *pud;
+ unsigned long next;
unsigned long foundaddr;
- unsigned long end;
if (pgd_none(*pgd))
return 0;
@@ -541,54 +529,48 @@ static unsigned long unuse_pgd(struct vm_area_struct * vma, pgd_t *pgd,
return 0;
}
pud = pud_offset(pgd, address);
- offset = address & PGDIR_MASK;
- address &= ~PGDIR_MASK;
- end = address + size;
- if (end > PGDIR_SIZE)
- end = PGDIR_SIZE;
- BUG_ON (address >= end);
do {
- foundaddr = unuse_pud(vma, pud, address, end - address,
- offset, entry, page);
+ next = (address + PUD_SIZE) & PUD_MASK;
+ if (next > end || !next)
+ next = end;
+ foundaddr = unuse_pud(vma, pud, address, next, entry, page);
if (foundaddr)
return foundaddr;
- address = (address + PUD_SIZE) & PUD_MASK;
+ address = next;
pud++;
- } while (address && (address < end));
+ } while (address < end);
return 0;
}
/* vma->vm_mm->page_table_lock is held */
-static unsigned long unuse_vma(struct vm_area_struct * vma,
+static unsigned long unuse_vma(struct vm_area_struct *vma,
swp_entry_t entry, struct page *page)
{
pgd_t *pgd;
- unsigned long start, end, next;
+ unsigned long address, next, end;
unsigned long foundaddr;
- int i;
if (page->mapping) {
- start = page_address_in_vma(page, vma);
- if (start == -EFAULT)
+ address = page_address_in_vma(page, vma);
+ if (address == -EFAULT)
return 0;
else
- end = start + PAGE_SIZE;
+ end = address + PAGE_SIZE;
} else {
- start = vma->vm_start;
+ address = vma->vm_start;
end = vma->vm_end;
}
- pgd = pgd_offset(vma->vm_mm, start);
- for (i = pgd_index(start); i <= pgd_index(end-1); i++) {
- next = (start + PGDIR_SIZE) & PGDIR_MASK;
- if (next > end || next <= start)
+ pgd = pgd_offset(vma->vm_mm, address);
+ do {
+ next = (address + PGDIR_SIZE) & PGDIR_MASK;
+ if (next > end || !next)
next = end;
- foundaddr = unuse_pgd(vma, pgd, start, next - start, entry, page);
+ foundaddr = unuse_pgd(vma, pgd, address, next, entry, page);
if (foundaddr)
return foundaddr;
- start = next;
- i++;
+ address = next;
pgd++;
- }
+ } while (address < end);
return 0;
}