diff options
Diffstat (limited to 'mm/gup.c')
-rw-r--r-- | mm/gup.c | 81 |
1 files changed, 73 insertions, 8 deletions
@@ -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); |