aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2009-11-23 15:03:40 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2009-11-26 22:24:30 -0500
commit39adfa540fa0b32e41b2a5a9e225384009ae6128 (patch)
tree7cbb8f5e4b2be480b9a0bd37fa57a0ebf6981ab7
parentc045256d146800ea1d741a8e9e377dada6b7e195 (diff)
powerpc/mm: Fix bug in gup_hugepd()
Commit a4fe3ce7699bfe1bd88f816b55d42d8fe1dac655 introduced a new get_user_pages() path for hugepages on powerpc. Unfortunately, there is a bug in it's loop logic, which can cause it to overrun the end of the intended region. This came about by copying the logic from the normal page path, which assumes the address and end parameters have been pagesize aligned at the top-level. Since they're not *hugepage* size aligned, the simplistic logic could step over the end of the gup region without triggering the loop end condition. This patch fixes the bug by using the technique that the normal page path uses in levels above the lowest to truncate the ending address to something we know we'll match with. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/mm/hugetlbpage.c11
1 files changed, 10 insertions, 1 deletions
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index 53b200abb025..123f7070238a 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -436,18 +436,27 @@ static noinline int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long add
436 return 1; 436 return 1;
437} 437}
438 438
439static unsigned long hugepte_addr_end(unsigned long addr, unsigned long end,
440 unsigned long sz)
441{
442 unsigned long __boundary = (addr + sz) & ~(sz-1);
443 return (__boundary - 1 < end - 1) ? __boundary : end;
444}
445
439int gup_hugepd(hugepd_t *hugepd, unsigned pdshift, 446int gup_hugepd(hugepd_t *hugepd, unsigned pdshift,
440 unsigned long addr, unsigned long end, 447 unsigned long addr, unsigned long end,
441 int write, struct page **pages, int *nr) 448 int write, struct page **pages, int *nr)
442{ 449{
443 pte_t *ptep; 450 pte_t *ptep;
444 unsigned long sz = 1UL << hugepd_shift(*hugepd); 451 unsigned long sz = 1UL << hugepd_shift(*hugepd);
452 unsigned long next;
445 453
446 ptep = hugepte_offset(hugepd, addr, pdshift); 454 ptep = hugepte_offset(hugepd, addr, pdshift);
447 do { 455 do {
456 next = hugepte_addr_end(addr, end, sz);
448 if (!gup_hugepte(ptep, sz, addr, end, write, pages, nr)) 457 if (!gup_hugepte(ptep, sz, addr, end, write, pages, nr))
449 return 0; 458 return 0;
450 } while (ptep++, addr += sz, addr != end); 459 } while (ptep++, addr = next, addr != end);
451 460
452 return 1; 461 return 1;
453} 462}