aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2014-11-05 11:27:40 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2014-11-14 01:24:21 -0500
commitf30c59e921f12b209852e1ddc197dc6a8fb0142b (patch)
tree392c066f3053fcf6f4683fbe6da3a0a06b091cc7
parent06743521d0eae1263a09bccb1a92a9fbb94660b3 (diff)
mm: Update generic gup implementation to handle hugepage directory
Update generic gup implementation with powerpc specific details. On powerpc at pmd level we can have hugepte, normal pmd pointer or a pointer to the hugepage directory. Tested-by: Steve Capper <steve.capper@linaro.org> Acked-by: Steve Capper <steve.capper@linaro.org> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/include/asm/page.h1
-rw-r--r--include/linux/hugetlb.h46
-rw-r--r--mm/gup.c81
3 files changed, 120 insertions, 8 deletions
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index 26fe1ae15212..f973fce73a43 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -380,6 +380,7 @@ static inline int hugepd_ok(hugepd_t hpd)
380#endif 380#endif
381 381
382#define is_hugepd(pdep) (hugepd_ok(*((hugepd_t *)(pdep)))) 382#define is_hugepd(pdep) (hugepd_ok(*((hugepd_t *)(pdep))))
383#define pgd_huge pgd_huge
383int pgd_huge(pgd_t pgd); 384int pgd_huge(pgd_t pgd);
384#else /* CONFIG_HUGETLB_PAGE */ 385#else /* CONFIG_HUGETLB_PAGE */
385#define is_hugepd(pdep) 0 386#define is_hugepd(pdep) 0
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 6e6d338641fe..e6b62f30ab21 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -175,6 +175,52 @@ static inline void __unmap_hugepage_range(struct mmu_gather *tlb,
175} 175}
176 176
177#endif /* !CONFIG_HUGETLB_PAGE */ 177#endif /* !CONFIG_HUGETLB_PAGE */
178/*
179 * hugepages at page global directory. If arch support
180 * hugepages at pgd level, they need to define this.
181 */
182#ifndef pgd_huge
183#define pgd_huge(x) 0
184#endif
185
186#ifndef pgd_write
187static inline int pgd_write(pgd_t pgd)
188{
189 BUG();
190 return 0;
191}
192#endif
193
194#ifndef pud_write
195static inline int pud_write(pud_t pud)
196{
197 BUG();
198 return 0;
199}
200#endif
201
202#ifndef is_hugepd
203/*
204 * Some architectures requires a hugepage directory format that is
205 * required to support multiple hugepage sizes. For example
206 * a4fe3ce76 "powerpc/mm: Allow more flexible layouts for hugepage pagetables"
207 * introduced the same on powerpc. This allows for a more flexible hugepage
208 * pagetable layout.
209 */
210typedef struct { unsigned long pd; } hugepd_t;
211#define is_hugepd(hugepd) (0)
212#define __hugepd(x) ((hugepd_t) { (x) })
213static inline int gup_huge_pd(hugepd_t hugepd, unsigned long addr,
214 unsigned pdshift, unsigned long end,
215 int write, struct page **pages, int *nr)
216{
217 return 0;
218}
219#else
220extern int gup_huge_pd(hugepd_t hugepd, unsigned long addr,
221 unsigned pdshift, unsigned long end,
222 int write, struct page **pages, int *nr);
223#endif
178 224
179#define HUGETLB_ANON_FILE "anon_hugepage" 225#define HUGETLB_ANON_FILE "anon_hugepage"
180 226
diff --git a/mm/gup.c b/mm/gup.c
index cd62c8c90d4a..0ca1df9075ab 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -3,7 +3,6 @@
3#include <linux/err.h> 3#include <linux/err.h>
4#include <linux/spinlock.h> 4#include <linux/spinlock.h>
5 5
6#include <linux/hugetlb.h>
7#include <linux/mm.h> 6#include <linux/mm.h>
8#include <linux/pagemap.h> 7#include <linux/pagemap.h>
9#include <linux/rmap.h> 8#include <linux/rmap.h>
@@ -12,6 +11,7 @@
12 11
13#include <linux/sched.h> 12#include <linux/sched.h>
14#include <linux/rwsem.h> 13#include <linux/rwsem.h>
14#include <linux/hugetlb.h>
15#include <asm/pgtable.h> 15#include <asm/pgtable.h>
16 16
17#include "internal.h" 17#include "internal.h"
@@ -875,6 +875,49 @@ static int gup_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr,
875 return 1; 875 return 1;
876} 876}
877 877
878static int gup_huge_pgd(pgd_t orig, pgd_t *pgdp, unsigned long addr,
879 unsigned long end, int write,
880 struct page **pages, int *nr)
881{
882 int refs;
883 struct page *head, *page, *tail;
884
885 if (write && !pgd_write(orig))
886 return 0;
887
888 refs = 0;
889 head = pgd_page(orig);
890 page = head + ((addr & ~PGDIR_MASK) >> PAGE_SHIFT);
891 tail = page;
892 do {
893 VM_BUG_ON_PAGE(compound_head(page) != head, page);
894 pages[*nr] = page;
895 (*nr)++;
896 page++;
897 refs++;
898 } while (addr += PAGE_SIZE, addr != end);
899
900 if (!page_cache_add_speculative(head, refs)) {
901 *nr -= refs;
902 return 0;
903 }
904
905 if (unlikely(pgd_val(orig) != pgd_val(*pgdp))) {
906 *nr -= refs;
907 while (refs--)
908 put_page(head);
909 return 0;
910 }
911
912 while (refs--) {
913 if (PageTail(tail))
914 get_huge_page_tail(tail);
915 tail++;
916 }
917
918 return 1;
919}
920
878static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end, 921static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
879 int write, struct page **pages, int *nr) 922 int write, struct page **pages, int *nr)
880{ 923{
@@ -902,6 +945,14 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
902 pages, nr)) 945 pages, nr))
903 return 0; 946 return 0;
904 947
948 } else if (unlikely(is_hugepd(__hugepd(pmd_val(pmd))))) {
949 /*
950 * architecture have different format for hugetlbfs
951 * pmd format and THP pmd format
952 */
953 if (!gup_huge_pd(__hugepd(pmd_val(pmd)), addr,
954 PMD_SHIFT, next, write, pages, nr))
955 return 0;
905 } else if (!gup_pte_range(pmd, addr, next, write, pages, nr)) 956 } else if (!gup_pte_range(pmd, addr, next, write, pages, nr))
906 return 0; 957 return 0;
907 } while (pmdp++, addr = next, addr != end); 958 } while (pmdp++, addr = next, addr != end);
@@ -909,22 +960,26 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
909 return 1; 960 return 1;
910} 961}
911 962
912static int gup_pud_range(pgd_t *pgdp, unsigned long addr, unsigned long end, 963static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end,
913 int write, struct page **pages, int *nr) 964 int write, struct page **pages, int *nr)
914{ 965{
915 unsigned long next; 966 unsigned long next;
916 pud_t *pudp; 967 pud_t *pudp;
917 968
918 pudp = pud_offset(pgdp, addr); 969 pudp = pud_offset(&pgd, addr);
919 do { 970 do {
920 pud_t pud = ACCESS_ONCE(*pudp); 971 pud_t pud = ACCESS_ONCE(*pudp);
921 972
922 next = pud_addr_end(addr, end); 973 next = pud_addr_end(addr, end);
923 if (pud_none(pud)) 974 if (pud_none(pud))
924 return 0; 975 return 0;
925 if (pud_huge(pud)) { 976 if (unlikely(pud_huge(pud))) {
926 if (!gup_huge_pud(pud, pudp, addr, next, write, 977 if (!gup_huge_pud(pud, pudp, addr, next, write,
927 pages, nr)) 978 pages, nr))
979 return 0;
980 } else if (unlikely(is_hugepd(__hugepd(pud_val(pud))))) {
981 if (!gup_huge_pd(__hugepd(pud_val(pud)), addr,
982 PUD_SHIFT, next, write, pages, nr))
928 return 0; 983 return 0;
929 } else if (!gup_pmd_range(pud, addr, next, write, pages, nr)) 984 } else if (!gup_pmd_range(pud, addr, next, write, pages, nr))
930 return 0; 985 return 0;
@@ -970,10 +1025,20 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
970 local_irq_save(flags); 1025 local_irq_save(flags);
971 pgdp = pgd_offset(mm, addr); 1026 pgdp = pgd_offset(mm, addr);
972 do { 1027 do {
1028 pgd_t pgd = ACCESS_ONCE(*pgdp);
1029
973 next = pgd_addr_end(addr, end); 1030 next = pgd_addr_end(addr, end);
974 if (pgd_none(*pgdp)) 1031 if (pgd_none(pgd))
975 break; 1032 break;
976 else if (!gup_pud_range(pgdp, addr, next, write, pages, &nr)) 1033 if (unlikely(pgd_huge(pgd))) {
1034 if (!gup_huge_pgd(pgd, pgdp, addr, next, write,
1035 pages, &nr))
1036 break;
1037 } else if (unlikely(is_hugepd(__hugepd(pgd_val(pgd))))) {
1038 if (!gup_huge_pd(__hugepd(pgd_val(pgd)), addr,
1039 PGDIR_SHIFT, next, write, pages, &nr))
1040 break;
1041 } else if (!gup_pud_range(pgd, addr, next, write, pages, &nr))
977 break; 1042 break;
978 } while (pgdp++, addr = next, addr != end); 1043 } while (pgdp++, addr = next, addr != end);
979 local_irq_restore(flags); 1044 local_irq_restore(flags);