aboutsummaryrefslogtreecommitdiffstats
path: root/mm/gup.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/gup.c')
-rw-r--r--mm/gup.c81
1 files changed, 73 insertions, 8 deletions
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);