/* * Handle a fault on the vmalloc area. */ static inline int vmalloc_fault(pgd_t *pgd, unsigned long address) { pmd_t *pmd_k; pte_t *pte_k; /* Make sure we are in vmalloc area */ if (!(address >= VMALLOC_START && address < VMALLOC_END)) return -1; /* * Synchronize this task's top level page-table * with the 'reference' page table. */ pmd_k = vmalloc_sync_one(pgd, address); if (!pmd_k) return -1; pte_k = pte_offset_kernel(pmd_k, address); if (!pte_present(*pte_k)) return -1; return 0; }
void vmalloc_sync_all(void) { #ifdef __tilegx__ /* Currently all L1 kernel pmd's are static and shared. */ BUILD_BUG_ON(pgd_index(VMALLOC_END - PAGE_SIZE) != pgd_index(VMALLOC_START)); #else /* * Note that races in the updates of insync and start aren't * problematic: insync can only get set bits added, and updates to * start are only improving performance (without affecting correctness * if undone). */ static DECLARE_BITMAP(insync, PTRS_PER_PGD); static unsigned long start = PAGE_OFFSET; unsigned long address; BUILD_BUG_ON(PAGE_OFFSET & ~PGDIR_MASK); for (address = start; address >= PAGE_OFFSET; address += PGDIR_SIZE) { if (!test_bit(pgd_index(address), insync)) { unsigned long flags; struct list_head *pos; spin_lock_irqsave(&pgd_lock, flags); list_for_each(pos, &pgd_list) if (!vmalloc_sync_one(list_to_pgd(pos), address)) { /* Must be at first entry in list. */ BUG_ON(pos != pgd_list.next); break; } spin_unlock_irqrestore(&pgd_lock, flags); if (pos != pgd_list.next) set_bit(pgd_index(address), insync); } if (address == start && test_bit(pgd_index(address), insync)) start = address + PGDIR_SIZE; }