diff options
author | Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | 2014-11-05 11:27:40 -0500 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2014-11-14 01:24:21 -0500 |
commit | f30c59e921f12b209852e1ddc197dc6a8fb0142b (patch) | |
tree | 392c066f3053fcf6f4683fbe6da3a0a06b091cc7 | |
parent | 06743521d0eae1263a09bccb1a92a9fbb94660b3 (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.h | 1 | ||||
-rw-r--r-- | include/linux/hugetlb.h | 46 | ||||
-rw-r--r-- | mm/gup.c | 81 |
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 | ||
383 | int pgd_huge(pgd_t pgd); | 384 | int 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 | ||
187 | static inline int pgd_write(pgd_t pgd) | ||
188 | { | ||
189 | BUG(); | ||
190 | return 0; | ||
191 | } | ||
192 | #endif | ||
193 | |||
194 | #ifndef pud_write | ||
195 | static 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 | */ | ||
210 | typedef struct { unsigned long pd; } hugepd_t; | ||
211 | #define is_hugepd(hugepd) (0) | ||
212 | #define __hugepd(x) ((hugepd_t) { (x) }) | ||
213 | static 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 | ||
220 | extern 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 | ||
@@ -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 | ||
878 | static 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 | |||
878 | static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end, | 921 | static 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 | ||
912 | static int gup_pud_range(pgd_t *pgdp, unsigned long addr, unsigned long end, | 963 | static 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); |