Skip to content
Snippets Groups Projects
memory.c 58.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		return do_swap_page(mm, vma, address,
    					pte, pmd, write_access, entry);
    
    	ptl = &mm->page_table_lock;
    	spin_lock(ptl);
    	if (unlikely(!pte_same(*pte, entry)))
    		goto unlock;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (write_access) {
    		if (!pte_write(entry))
    
    			return do_wp_page(mm, vma, address,
    					pte, pmd, ptl, entry);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		entry = pte_mkdirty(entry);
    	}
    	entry = pte_mkyoung(entry);
    	ptep_set_access_flags(vma, address, pte, entry, write_access);
    	update_mmu_cache(vma, address, entry);
    	lazy_mmu_prot_update(entry);
    
    unlock:
    	pte_unmap_unlock(pte, ptl);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return VM_FAULT_MINOR;
    }
    
    /*
     * By the time we get here, we already hold the mm semaphore
     */
    
    int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		unsigned long address, int write_access)
    {
    	pgd_t *pgd;
    	pud_t *pud;
    	pmd_t *pmd;
    	pte_t *pte;
    
    	__set_current_state(TASK_RUNNING);
    
    	inc_page_state(pgfault);
    
    
    	if (unlikely(is_vm_hugetlb_page(vma)))
    		return hugetlb_fault(mm, vma, address, write_access);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	pgd = pgd_offset(mm, address);
    	pud = pud_alloc(mm, pgd, address);
    	if (!pud)
    
    		return VM_FAULT_OOM;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	pmd = pmd_alloc(mm, pud, address);
    	if (!pmd)
    
    		return VM_FAULT_OOM;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	pte = pte_alloc_map(mm, pmd, address);
    	if (!pte)
    
    		return VM_FAULT_OOM;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	return handle_pte_fault(mm, vma, address, pte, pmd, write_access);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    #ifndef __PAGETABLE_PUD_FOLDED
    /*
     * Allocate page upper directory.
    
     * We've already handled the fast-path in-line.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	pud_t *new = pud_alloc_one(mm, address);
    	if (!new)
    
    		return -ENOMEM;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	spin_lock(&mm->page_table_lock);
    
    	if (pgd_present(*pgd))		/* Another has populated it */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		pud_free(new);
    
    	else
    		pgd_populate(mm, pgd, new);
    
    	spin_unlock(&mm->page_table_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    #endif /* __PAGETABLE_PUD_FOLDED */
    
    #ifndef __PAGETABLE_PMD_FOLDED
    /*
     * Allocate page middle directory.
    
     * We've already handled the fast-path in-line.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	pmd_t *new = pmd_alloc_one(mm, address);
    	if (!new)
    
    		return -ENOMEM;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	spin_lock(&mm->page_table_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #ifndef __ARCH_HAS_4LEVEL_HACK
    
    	if (pud_present(*pud))		/* Another has populated it */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		pmd_free(new);
    
    	else
    		pud_populate(mm, pud, new);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #else
    
    	if (pgd_present(*pud))		/* Another has populated it */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		pmd_free(new);
    
    	else
    		pgd_populate(mm, pud, new);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #endif /* __ARCH_HAS_4LEVEL_HACK */
    
    	spin_unlock(&mm->page_table_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    #endif /* __PAGETABLE_PMD_FOLDED */
    
    int make_pages_present(unsigned long addr, unsigned long end)
    {
    	int ret, len, write;
    	struct vm_area_struct * vma;
    
    	vma = find_vma(current->mm, addr);
    	if (!vma)
    		return -1;
    	write = (vma->vm_flags & VM_WRITE) != 0;
    	if (addr >= end)
    		BUG();
    	if (end > vma->vm_end)
    		BUG();
    	len = (end+PAGE_SIZE-1)/PAGE_SIZE-addr/PAGE_SIZE;
    	ret = get_user_pages(current, current->mm, addr,
    			len, write, 0, NULL, NULL);
    	if (ret < 0)
    		return ret;
    	return ret == len ? 0 : -1;
    }
    
    /* 
     * Map a vmalloc()-space virtual address to the physical page.
     */
    struct page * vmalloc_to_page(void * vmalloc_addr)
    {
    	unsigned long addr = (unsigned long) vmalloc_addr;
    	struct page *page = NULL;
    	pgd_t *pgd = pgd_offset_k(addr);
    	pud_t *pud;
    	pmd_t *pmd;
    	pte_t *ptep, pte;
      
    	if (!pgd_none(*pgd)) {
    		pud = pud_offset(pgd, addr);
    		if (!pud_none(*pud)) {
    			pmd = pmd_offset(pud, addr);
    			if (!pmd_none(*pmd)) {
    				ptep = pte_offset_map(pmd, addr);
    				pte = *ptep;
    				if (pte_present(pte))
    					page = pte_page(pte);
    				pte_unmap(ptep);
    			}
    		}
    	}
    	return page;
    }
    
    EXPORT_SYMBOL(vmalloc_to_page);
    
    /*
     * Map a vmalloc()-space virtual address to the physical page frame number.
     */
    unsigned long vmalloc_to_pfn(void * vmalloc_addr)
    {
    	return page_to_pfn(vmalloc_to_page(vmalloc_addr));
    }
    
    EXPORT_SYMBOL(vmalloc_to_pfn);
    
    #if !defined(__HAVE_ARCH_GATE_AREA)
    
    #if defined(AT_SYSINFO_EHDR)
    
    static struct vm_area_struct gate_vma;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    static int __init gate_vma_init(void)
    {
    	gate_vma.vm_mm = NULL;
    	gate_vma.vm_start = FIXADDR_USER_START;
    	gate_vma.vm_end = FIXADDR_USER_END;
    	gate_vma.vm_page_prot = PAGE_READONLY;
    
    	gate_vma.vm_flags = VM_RESERVED;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    }
    __initcall(gate_vma_init);
    #endif
    
    struct vm_area_struct *get_gate_vma(struct task_struct *tsk)
    {
    #ifdef AT_SYSINFO_EHDR
    	return &gate_vma;
    #else
    	return NULL;
    #endif
    }
    
    int in_gate_area_no_task(unsigned long addr)
    {
    #ifdef AT_SYSINFO_EHDR
    	if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
    		return 1;
    #endif
    	return 0;
    }
    
    #endif	/* __HAVE_ARCH_GATE_AREA */