Commit 09c2e123 authored by Hugh Dickins's avatar Hugh Dickins Committed by Linus Torvalds

[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: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a6ab0c16
...@@ -442,12 +442,11 @@ unuse_pte(struct vm_area_struct *vma, unsigned long address, pte_t *dir, ...@@ -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 */ /* vma->vm_mm->page_table_lock is held */
static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, static unsigned long unuse_pmd(struct vm_area_struct *vma, pmd_t *dir,
unsigned long address, unsigned long size, unsigned long offset, unsigned long address, unsigned long end,
swp_entry_t entry, struct page *page) swp_entry_t entry, struct page *page)
{ {
pte_t * pte; pte_t *pte;
unsigned long end;
pte_t swp_pte = swp_entry_to_pte(entry); pte_t swp_pte = swp_entry_to_pte(entry);
if (pmd_none(*dir)) if (pmd_none(*dir))
...@@ -458,18 +457,13 @@ static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, ...@@ -458,18 +457,13 @@ static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
return 0; return 0;
} }
pte = pte_offset_map(dir, address); pte = pte_offset_map(dir, address);
offset += address & PMD_MASK;
address &= ~PMD_MASK;
end = address + size;
if (end > PMD_SIZE)
end = PMD_SIZE;
do { do {
/* /*
* swapoff spends a _lot_ of time in this loop! * swapoff spends a _lot_ of time in this loop!
* Test inline before going to call unuse_pte. * Test inline before going to call unuse_pte.
*/ */
if (unlikely(pte_same(*pte, swp_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); pte_unmap(pte);
/* /*
...@@ -479,22 +473,22 @@ static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, ...@@ -479,22 +473,22 @@ static unsigned long unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
activate_page(page); activate_page(page);
/* add 1 since address may be 0 */ /* add 1 since address may be 0 */
return 1 + offset + address; return 1 + address;
} }
address += PAGE_SIZE; address += PAGE_SIZE;
pte++; pte++;
} while (address && (address < end)); } while (address < end);
pte_unmap(pte - 1); pte_unmap(pte - 1);
return 0; return 0;
} }
/* vma->vm_mm->page_table_lock is held */ /* vma->vm_mm->page_table_lock is held */
static unsigned long unuse_pud(struct vm_area_struct * vma, pud_t *pud, static unsigned long unuse_pud(struct vm_area_struct *vma, pud_t *pud,
unsigned long address, unsigned long size, unsigned long offset, unsigned long address, unsigned long end,
swp_entry_t entry, struct page *page) swp_entry_t entry, struct page *page)
{ {
pmd_t * pmd; pmd_t *pmd;
unsigned long end; unsigned long next;
unsigned long foundaddr; unsigned long foundaddr;
if (pud_none(*pud)) if (pud_none(*pud))
...@@ -505,33 +499,27 @@ static unsigned long unuse_pud(struct vm_area_struct * vma, pud_t *pud, ...@@ -505,33 +499,27 @@ static unsigned long unuse_pud(struct vm_area_struct * vma, pud_t *pud,
return 0; return 0;
} }
pmd = pmd_offset(pud, address); 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 { do {
foundaddr = unuse_pmd(vma, pmd, address, end - address, next = (address + PMD_SIZE) & PMD_MASK;
offset, entry, page); if (next > end || !next)
next = end;
foundaddr = unuse_pmd(vma, pmd, address, next, entry, page);
if (foundaddr) if (foundaddr)
return foundaddr; return foundaddr;
address = (address + PMD_SIZE) & PMD_MASK; address = next;
pmd++; pmd++;
} while (address && (address < end)); } while (address < end);
return 0; return 0;
} }
/* vma->vm_mm->page_table_lock is held */ /* vma->vm_mm->page_table_lock is held */
static unsigned long unuse_pgd(struct vm_area_struct * vma, pgd_t *pgd, static unsigned long unuse_pgd(struct vm_area_struct *vma, pgd_t *pgd,
unsigned long address, unsigned long size, unsigned long address, unsigned long end,
swp_entry_t entry, struct page *page) swp_entry_t entry, struct page *page)
{ {
pud_t * pud; pud_t *pud;
unsigned long offset; unsigned long next;
unsigned long foundaddr; unsigned long foundaddr;
unsigned long end;
if (pgd_none(*pgd)) if (pgd_none(*pgd))
return 0; return 0;
...@@ -541,54 +529,48 @@ static unsigned long unuse_pgd(struct vm_area_struct * vma, pgd_t *pgd, ...@@ -541,54 +529,48 @@ static unsigned long unuse_pgd(struct vm_area_struct * vma, pgd_t *pgd,
return 0; return 0;
} }
pud = pud_offset(pgd, address); 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 { do {
foundaddr = unuse_pud(vma, pud, address, end - address, next = (address + PUD_SIZE) & PUD_MASK;
offset, entry, page); if (next > end || !next)
next = end;
foundaddr = unuse_pud(vma, pud, address, next, entry, page);
if (foundaddr) if (foundaddr)
return foundaddr; return foundaddr;
address = (address + PUD_SIZE) & PUD_MASK; address = next;
pud++; pud++;
} while (address && (address < end)); } while (address < end);
return 0; return 0;
} }
/* vma->vm_mm->page_table_lock is held */ /* 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) swp_entry_t entry, struct page *page)
{ {
pgd_t *pgd; pgd_t *pgd;
unsigned long start, end, next; unsigned long address, next, end;
unsigned long foundaddr; unsigned long foundaddr;
int i;
if (page->mapping) { if (page->mapping) {
start = page_address_in_vma(page, vma); address = page_address_in_vma(page, vma);
if (start == -EFAULT) if (address == -EFAULT)
return 0; return 0;
else else
end = start + PAGE_SIZE; end = address + PAGE_SIZE;
} else { } else {
start = vma->vm_start; address = vma->vm_start;
end = vma->vm_end; end = vma->vm_end;
} }
pgd = pgd_offset(vma->vm_mm, start); pgd = pgd_offset(vma->vm_mm, address);
for (i = pgd_index(start); i <= pgd_index(end-1); i++) { do {
next = (start + PGDIR_SIZE) & PGDIR_MASK; next = (address + PGDIR_SIZE) & PGDIR_MASK;
if (next > end || next <= start) if (next > end || !next)
next = end; next = end;
foundaddr = unuse_pgd(vma, pgd, start, next - start, entry, page); foundaddr = unuse_pgd(vma, pgd, address, next, entry, page);
if (foundaddr) if (foundaddr)
return foundaddr; return foundaddr;
start = next; address = next;
i++;
pgd++; pgd++;
} } while (address < end);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment