#ifndef _ASM_PGALLOC_H #define _ASM_PGALLOC_H /* The usual comment is "Caches aren't brain-dead on the ". * Unfortunately, that doesn't apply to PA-RISC. */ #include #include #include #include #include #define flush_kernel_dcache_range(start,size) \ flush_kernel_dcache_range_asm((start), (start)+(size)); static inline void flush_page_to_ram(struct page *page) { } extern void flush_cache_all_local(void); #ifdef CONFIG_SMP static inline void flush_cache_all(void) { smp_call_function((void (*)(void *))flush_cache_all_local, NULL, 1, 1); flush_cache_all_local(); } #else #define flush_cache_all flush_cache_all_local #endif #ifdef CONFIG_SMP #define flush_cache_mm(mm) flush_cache_all() #else #define flush_cache_mm(mm) flush_cache_all_local() #endif /* The following value needs to be tuned and probably scaled with the * cache size. */ #define FLUSH_THRESHOLD 0x80000 static inline void flush_user_dcache_range(unsigned long start, unsigned long end) { #ifdef CONFIG_SMP flush_user_dcache_range_asm(start,end); #else if ((end - start) < FLUSH_THRESHOLD) flush_user_dcache_range_asm(start,end); else flush_data_cache(); #endif } static inline void flush_user_icache_range(unsigned long start, unsigned long end) { #ifdef CONFIG_SMP flush_user_icache_range_asm(start,end); #else if ((end - start) < FLUSH_THRESHOLD) flush_user_icache_range_asm(start,end); else flush_instruction_cache(); #endif } static inline void flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { int sr3; if (!mm->context) { BUG(); return; } sr3 = mfsp(3); if (mm->context == sr3) { flush_user_dcache_range(start,end); flush_user_icache_range(start,end); } else { flush_cache_all(); } } static inline void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr) { int sr3; if (!vma->vm_mm->context) { BUG(); return; } sr3 = mfsp(3); if (vma->vm_mm->context == sr3) { flush_user_dcache_range(vmaddr,vmaddr + PAGE_SIZE); if (vma->vm_flags & VM_EXEC) flush_user_icache_range(vmaddr,vmaddr + PAGE_SIZE); } else { if (vma->vm_flags & VM_EXEC) flush_cache_all(); else flush_data_cache(); } } static inline void flush_dcache_page(struct page *page) { if (page->mapping && !page->mapping->i_mmap && !page->mapping->i_mmap_shared) { set_bit(PG_dcache_dirty, &page->flags); } else { flush_kernel_dcache_page(page_address(page)); } } #define flush_icache_page(vma,page) do { flush_kernel_dcache_page(page_address(page)); flush_kernel_icache_page(page_address(page)); } while (0) #define flush_icache_range(s,e) do { flush_kernel_dcache_range_asm(s,e); flush_kernel_icache_range_asm(s,e); } while (0) /* TLB flushing routines.... */ extern void flush_tlb_all(void); static inline void load_context(mm_context_t context) { mtsp(context, 3); #if SPACEID_SHIFT == 0 mtctl(context << 1,8); #else mtctl(context >> (SPACEID_SHIFT - 1),8); #endif } /* * flush_tlb_mm() * * XXX This code is NOT valid for HP-UX compatibility processes, * (although it will probably work 99% of the time). HP-UX * processes are free to play with the space id's and save them * over long periods of time, etc. so we have to preserve the * space and just flush the entire tlb. We need to check the * personality in order to do that, but the personality is not * currently being set correctly. * * Of course, Linux processes could do the same thing, but * we don't support that (and the compilers, dynamic linker, * etc. do not do that). */ static inline void flush_tlb_mm(struct mm_struct *mm) { if (mm == &init_mm) BUG(); /* Should never happen */ #ifdef CONFIG_SMP flush_tlb_all(); #else if (mm) { if (mm->context != 0) free_sid(mm->context); mm->context = alloc_sid(); if (mm == current->active_mm) load_context(mm->context); } #endif } extern __inline__ void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end) { } static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { /* For one page, it's not worth testing the split_tlb variable */ mtsp(vma->vm_mm->context,1); pdtlb(addr); pitlb(addr); } static inline void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { unsigned long npages; npages = ((end - (start & PAGE_MASK)) + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if (npages >= 512) /* XXX arbitrary, should be tuned */ flush_tlb_all(); else { mtsp(mm->context,1); if (split_tlb) { while (npages--) { pdtlb(start); pitlb(start); start += PAGE_SIZE; } } else { while (npages--) { pdtlb(start); start += PAGE_SIZE; } } } } static inline pgd_t *pgd_alloc_one_fast (void) { return NULL; /* not implemented */ } static inline pgd_t *pgd_alloc (struct mm_struct *mm) { /* the VM system never calls pgd_alloc_one_fast(), so we do it here. */ pgd_t *pgd = pgd_alloc_one_fast(); if (!pgd) { pgd = (pgd_t *)__get_free_page(GFP_KERNEL); if (pgd) clear_page(pgd); } return pgd; } static inline void pgd_free(pgd_t *pgd) { free_page((unsigned long)pgd); } #ifdef __LP64__ /* Three Level Page Table Support for pmd's */ static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmd) { pgd_val(*pgd) = _PAGE_TABLE + __pa((unsigned long)pmd); } static inline pmd_t *pmd_alloc_one_fast(struct mm_struct *mm, unsigned long address) { return NULL; /* la la */ } static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) { pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL); if (pmd) clear_page(pmd); return pmd; } static inline void pmd_free(pmd_t *pmd) { free_page((unsigned long)pmd); } #else /* Two Level Page Table Support for pmd's */ /* * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. */ #define pmd_alloc_one_fast(mm, addr) ({ BUG(); ((pmd_t *)1); }) #define pmd_alloc_one(mm, addr) ({ BUG(); ((pmd_t *)2); }) #define pmd_free(x) do { } while (0) #define pgd_populate(mm, pmd, pte) BUG() #endif static inline void pmd_populate (struct mm_struct *mm, pmd_t *pmd_entry, pte_t *pte) { pmd_val(*pmd_entry) = _PAGE_TABLE + __pa((unsigned long)pte); } static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) { return NULL; /* la la */ } static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) { pte_t *pte = (pte_t *) __get_free_page(GFP_KERNEL); if (pte) clear_page(pte); return pte; } static inline void pte_free(pte_t *pte) { free_page((unsigned long)pte); } extern int do_check_pgt_cache(int, int); #endif