Skip to content

Commit 7890020

Browse files
author
Russell King (Oracle)
committed
ARM: move vmalloc() lazy-page table population
Split the vmalloc() lazy-page table population from do_translation_fault() into a new vmalloc_fault() function. Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
1 parent 59e4f3b commit 7890020

1 file changed

Lines changed: 68 additions & 58 deletions

File tree

arch/arm/mm/fault.c

Lines changed: 68 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,70 @@ static inline bool ttbr0_usermode_access_allowed(struct pt_regs *regs)
261261
}
262262
#endif
263263

264+
/*
265+
* Handle a vmalloc fault, copying the non-leaf page table entries from
266+
* init_mm.pgd. Any kernel context can trigger this, so we must not sleep
267+
* or enable interrupts. Having two CPUs execute this for the same page is
268+
* no problem, we'll just copy the same data twice.
269+
*
270+
* Returns false on failure.
271+
*/
272+
static bool __kprobes __maybe_unused vmalloc_fault(unsigned long addr)
273+
{
274+
unsigned int index;
275+
pgd_t *pgd, *pgd_k;
276+
p4d_t *p4d, *p4d_k;
277+
pud_t *pud, *pud_k;
278+
pmd_t *pmd, *pmd_k;
279+
280+
index = pgd_index(addr);
281+
282+
pgd = cpu_get_pgd() + index;
283+
pgd_k = init_mm.pgd + index;
284+
285+
p4d = p4d_offset(pgd, addr);
286+
p4d_k = p4d_offset(pgd_k, addr);
287+
288+
if (p4d_none(*p4d_k))
289+
return false;
290+
if (!p4d_present(*p4d))
291+
set_p4d(p4d, *p4d_k);
292+
293+
pud = pud_offset(p4d, addr);
294+
pud_k = pud_offset(p4d_k, addr);
295+
296+
if (pud_none(*pud_k))
297+
return false;
298+
if (!pud_present(*pud))
299+
set_pud(pud, *pud_k);
300+
301+
pmd = pmd_offset(pud, addr);
302+
pmd_k = pmd_offset(pud_k, addr);
303+
304+
#ifdef CONFIG_ARM_LPAE
305+
/*
306+
* Only one hardware entry per PMD with LPAE.
307+
*/
308+
index = 0;
309+
#else
310+
/*
311+
* On ARM one Linux PGD entry contains two hardware entries (see page
312+
* tables layout in pgtable.h). We normally guarantee that we always
313+
* fill both L1 entries. But create_mapping() doesn't follow the rule.
314+
* It can create inidividual L1 entries, so here we have to call
315+
* pmd_none() check for the entry really corresponded to address, not
316+
* for the first of pair.
317+
*/
318+
index = (addr >> SECTION_SHIFT) & 1;
319+
#endif
320+
if (pmd_none(pmd_k[index]))
321+
return false;
322+
323+
copy_pmd(pmd, pmd_k);
324+
325+
return true;
326+
}
327+
264328
static int __kprobes
265329
do_kernel_address_page_fault(struct mm_struct *mm, unsigned long addr,
266330
unsigned int fsr, struct pt_regs *regs)
@@ -496,10 +560,9 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
496560
* directly to do_kernel_address_page_fault() to handle.
497561
*
498562
* Otherwise, we're probably faulting in the vmalloc() area, so try to fix
499-
* that up. Note that we must not take any locks or enable interrupts in
500-
* this case.
563+
* that up via vmalloc_fault().
501564
*
502-
* If vmalloc() fixup fails, that means the non-leaf page tables did not
565+
* If vmalloc_fault() fails, that means the non-leaf page tables did not
503566
* contain an entry for this address, so handle this via
504567
* do_kernel_address_page_fault().
505568
*/
@@ -508,65 +571,12 @@ static int __kprobes
508571
do_translation_fault(unsigned long addr, unsigned int fsr,
509572
struct pt_regs *regs)
510573
{
511-
unsigned int index;
512-
pgd_t *pgd, *pgd_k;
513-
p4d_t *p4d, *p4d_k;
514-
pud_t *pud, *pud_k;
515-
pmd_t *pmd, *pmd_k;
516-
517574
if (addr < TASK_SIZE)
518575
return do_page_fault(addr, fsr, regs);
519576

520-
if (user_mode(regs))
521-
goto bad_area;
522-
523-
index = pgd_index(addr);
524-
525-
pgd = cpu_get_pgd() + index;
526-
pgd_k = init_mm.pgd + index;
527-
528-
p4d = p4d_offset(pgd, addr);
529-
p4d_k = p4d_offset(pgd_k, addr);
530-
531-
if (p4d_none(*p4d_k))
532-
goto bad_area;
533-
if (!p4d_present(*p4d))
534-
set_p4d(p4d, *p4d_k);
535-
536-
pud = pud_offset(p4d, addr);
537-
pud_k = pud_offset(p4d_k, addr);
538-
539-
if (pud_none(*pud_k))
540-
goto bad_area;
541-
if (!pud_present(*pud))
542-
set_pud(pud, *pud_k);
543-
544-
pmd = pmd_offset(pud, addr);
545-
pmd_k = pmd_offset(pud_k, addr);
546-
547-
#ifdef CONFIG_ARM_LPAE
548-
/*
549-
* Only one hardware entry per PMD with LPAE.
550-
*/
551-
index = 0;
552-
#else
553-
/*
554-
* On ARM one Linux PGD entry contains two hardware entries (see page
555-
* tables layout in pgtable.h). We normally guarantee that we always
556-
* fill both L1 entries. But create_mapping() doesn't follow the rule.
557-
* It can create inidividual L1 entries, so here we have to call
558-
* pmd_none() check for the entry really corresponded to address, not
559-
* for the first of pair.
560-
*/
561-
index = (addr >> SECTION_SHIFT) & 1;
562-
#endif
563-
if (pmd_none(pmd_k[index]))
564-
goto bad_area;
565-
566-
copy_pmd(pmd, pmd_k);
567-
return 0;
577+
if (!user_mode(regs) && vmalloc_fault(addr))
578+
return 0;
568579

569-
bad_area:
570580
do_kernel_address_page_fault(current->mm, addr, fsr, regs);
571581

572582
return 0;

0 commit comments

Comments
 (0)