@@ -124,7 +124,7 @@ static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr,
124124
125125 refs = 0 ;
126126 head = pte_page (pte );
127- page = head + ((addr & ~HPAGE_MASK ) >> PAGE_SHIFT );
127+ page = head + ((addr & ~PMD_MASK ) >> PAGE_SHIFT );
128128 do {
129129 VM_BUG_ON (compound_head (page ) != head );
130130 pages [* nr ] = page ;
@@ -162,6 +162,38 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
162162 return 1 ;
163163}
164164
165+ static noinline int gup_huge_pud (pud_t pud , unsigned long addr ,
166+ unsigned long end , int write , struct page * * pages , int * nr )
167+ {
168+ unsigned long mask ;
169+ pte_t pte = * (pte_t * )& pud ;
170+ struct page * head , * page ;
171+ int refs ;
172+
173+ mask = _PAGE_PRESENT |_PAGE_USER ;
174+ if (write )
175+ mask |= _PAGE_RW ;
176+ if ((pte_val (pte ) & mask ) != mask )
177+ return 0 ;
178+ /* hugepages are never "special" */
179+ VM_BUG_ON (pte_val (pte ) & _PAGE_SPECIAL );
180+ VM_BUG_ON (!pfn_valid (pte_pfn (pte )));
181+
182+ refs = 0 ;
183+ head = pte_page (pte );
184+ page = head + ((addr & ~PUD_MASK ) >> PAGE_SHIFT );
185+ do {
186+ VM_BUG_ON (compound_head (page ) != head );
187+ pages [* nr ] = page ;
188+ (* nr )++ ;
189+ page ++ ;
190+ refs ++ ;
191+ } while (addr += PAGE_SIZE , addr != end );
192+ get_head_page_multiple (head , refs );
193+
194+ return 1 ;
195+ }
196+
165197static int gup_pud_range (pgd_t pgd , unsigned long addr , unsigned long end ,
166198 int write , struct page * * pages , int * nr )
167199{
@@ -175,8 +207,13 @@ static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end,
175207 next = pud_addr_end (addr , end );
176208 if (pud_none (pud ))
177209 return 0 ;
178- if (!gup_pmd_range (pud , addr , next , write , pages , nr ))
179- return 0 ;
210+ if (unlikely (pud_large (pud ))) {
211+ if (!gup_huge_pud (pud , addr , next , write , pages , nr ))
212+ return 0 ;
213+ } else {
214+ if (!gup_pmd_range (pud , addr , next , write , pages , nr ))
215+ return 0 ;
216+ }
180217 } while (pudp ++ , addr = next , addr != end );
181218
182219 return 1 ;
0 commit comments