From: NIIBE Y. <gn...@m1...> - 2001-08-04 05:15:19
|
Following patch will eliminate cache alias issue. Bug fix is for flush_cache_page. It's very long standing bug. When it is called from vmscan.c:try_to_swapout, the PTE is cleared before the call, so it does nothing but just return (without flushing). To flush, we need physical address, so I added PAGE argument. I'll ask this change to lkml. I think that for physically tagged cache, we need this. Other things are change the cache handling strategy. Current implementation allows having cache on P1, and write back U0 and purge P1. I think that not allowing cache on P1 is better. * kernel/ptrace.c, mm/vmscan.c: Use of flush_cache_page. * arch/sh/mm/cache-sh4.c (copy_user_page): Don't need to write back (it's done in copy_page). (clear_user_page): Likewise. (check_cache_page): Removed. (flush_cache_page): Bug fix. Added PAGE argument. With this, flush U0 cache only (we should not have P1 data here). Note that PTE comes with 0 from try_to_swap_out. (was: nothing done but return because PTE==0) (__flush_cache_page): Removed. (flush_dcache_page): Flush P1 cache only (we should not have U0 cache here). * arch/sh/mm/fault.c (update_mmu_cache): Flush P1 cache only (we should not have U0 cache here). * arch/sh/mm/copy_page.S: Write back TO, purge FROM. * arch/sh/mm/clear_page.S: Write back TO. * arch/sh/mm/__copy_user_page-sh4.S: Purge FROM. * include/asm/pgtable.h (PTE_PHYS_MASK): Defined. (pte_page): Bug fix. Use PTE_PHYS_MASK. (__flush_cache_page): Removed. (flush_cache_page): Added PAGE argument. * drivers/cdrom/gdrom.c (gdrom_intr): Purge the cache (was: write-back). * include/asm-sh/ide.h (ide_insw): Purge the cache (was: write-back). (ide_outsw, outsw): New function. Let use this for IDE driver, not to have data on cache. Index: arch/sh/mm/__copy_user_page-sh4.S =================================================================== RCS file: /cvsroot/linuxsh/kernel/arch/sh/mm/__copy_user_page-sh4.S,v retrieving revision 1.2 diff -u -r1.2 __copy_user_page-sh4.S --- arch/sh/mm/__copy_user_page-sh4.S 2001/07/28 14:22:06 1.2 +++ arch/sh/mm/__copy_user_page-sh4.S 2001/08/04 04:02:55 @@ -44,7 +44,9 @@ mov.l @r11+,r4 mov.l @r11+,r5 mov.l @r11+,r6 - mov.l @r11+,r7 + mov.l @r11,r7 + ocbp @r11 + add #4,r11 movca.l r0,@r10 add #32,r10 mov.l r7,@-r10 Index: arch/sh/mm/cache-sh4.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/arch/sh/mm/cache-sh4.c,v retrieving revision 1.9 diff -u -r1.9 cache-sh4.c --- arch/sh/mm/cache-sh4.c 2001/08/04 00:04:48 1.9 +++ arch/sh/mm/cache-sh4.c 2001/08/04 04:02:55 @@ -211,42 +211,8 @@ } /* - * Writeback&Invalidate the D-cache of the page - * Invalidate the I-cache of the page, if needed + * Invalidate the I-cache of the page */ -void __flush_cache_page(unsigned long phys, int exec) -{ - unsigned long addr, data; - unsigned long flags; - - phys|=CACHE_VALID; - - save_and_cli(flags); - jump_to_P2(); - /* Loop all the D-cache */ - for (addr = CACHE_OC_ADDRESS_ARRAY; - addr < (CACHE_OC_ADDRESS_ARRAY - +(CACHE_OC_NUM_ENTRIES<< CACHE_OC_ENTRY_SHIFT)); - addr += (1<<CACHE_OC_ENTRY_SHIFT)) { - data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); - if (data == phys) - ctrl_outl(0, addr); - } - - if (exec) - /* Loop all the I-cache */ - for (addr = CACHE_IC_ADDRESS_ARRAY; - addr < (CACHE_IC_ADDRESS_ARRAY - +(CACHE_IC_NUM_ENTRIES<< CACHE_IC_ENTRY_SHIFT)); - addr += (1<<CACHE_IC_ENTRY_SHIFT)) { - data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); - if (data == phys) - ctrl_outl(0, addr); - } - back_to_P1(); - save_and_cli(flags); -} - void __flush_icache_page(unsigned long u0, unsigned long phys) { unsigned long addr, data; @@ -257,7 +223,7 @@ save_and_cli(flags); if (u0) { jump_to_P2(); - /* Loop half of the I-cache */ + /* Loop 4K of the I-cache */ for (addr = CACHE_IC_ADDRESS_ARRAY|(u0&0x1000); addr < ((CACHE_IC_ADDRESS_ARRAY|(u0&0x1000)) +(CACHE_IC_NUM_ENTRIES/2<<CACHE_IC_ENTRY_SHIFT)); @@ -287,19 +253,17 @@ * Write back & invalidate the I/D-cache of the page. * (To avoid "alias" issues) */ -void flush_dcache_page(struct page *pg) +void flush_dcache_page(struct page *page) { - if (!pg->mapping - || pg->mapping->i_mmap || pg->mapping->i_mmap_shared) { - unsigned long phys; - - phys = PHYSADDR(page_address(pg)); - if (pg->mapping) - __flush_cache_page(phys, 1); - else - __flush_cache_page(phys, 0); + if (!page->mapping + || page->mapping->i_mmap || page->mapping->i_mmap_shared) { + __flush_purge_region(page_address(page), PAGE_SIZE); + if (page->mapping) { + unsigned long phys = PHYSADDR(page_address(page)); + __flush_icache_page(0, phys); + } } else - set_bit(PG_dcache_dirty, &pg->flags); + set_bit(PG_dcache_dirty, &page->flags); } void flush_cache_all(void) @@ -357,88 +321,42 @@ * * ADDR: Virtual Address (U0 address) */ -void flush_cache_page(struct vm_area_struct *vma, unsigned long addr) +void flush_cache_page(struct vm_area_struct *vma, unsigned long address, + struct page *page) { - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - pte_t entry; unsigned long phys; - - dir = pgd_offset(vma->vm_mm, addr); - pmd = pmd_offset(dir, addr); - if (pmd_none(*pmd)) - return; - if (pmd_bad(*pmd)) - return; - pte = pte_offset(pmd, addr); - entry = *pte; - if (pte_none(entry) || !pte_present(entry)) - return; - phys = PHYSADDR(pte_val(entry)&PAGE_MASK); - __flush_cache_page(phys, (vma->vm_flags & VM_EXEC)); -} - -/* - * Check entries of the I-cache & D-cache of the page. - * (To see "alias" issues) - */ -void check_cache_page(struct page *pg) -{ - unsigned long phys, addr, data, i; - unsigned long kaddr; - unsigned long cache_line_index; - int bingo = 0; + unsigned long addr, data; unsigned long flags; - /* Physical address of this page */ - phys = PHYSADDR(page_address(pg)); - kaddr = phys + PAGE_OFFSET; - cache_line_index = (kaddr&CACHE_OC_ENTRY_MASK)>>CACHE_OC_ENTRY_SHIFT; - + /* + * We may not have valid PTE here, so can't do OCBP with U0 address. + * (See try_to_swap_out.) + */ + phys = PHYSADDR(page_address(page)) | CACHE_VALID; save_and_cli(flags); jump_to_P2(); - /* Loop all the D-cache */ - for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) { - addr = CACHE_OC_ADDRESS_ARRAY| (i<<CACHE_OC_ENTRY_SHIFT); - data = ctrl_inl(addr); - if ((data & (CACHE_UPDATED|CACHE_VALID)) - == (CACHE_UPDATED|CACHE_VALID) - && (data&PAGE_MASK) == phys) { - data &= ~(CACHE_VALID|CACHE_UPDATED); - ctrl_outl(data, addr); - if ((i^cache_line_index)&0x180) - bingo |= 1; - } + /* Loop D-cache 4K */ + for (addr = CACHE_OC_ADDRESS_ARRAY|(address&0x3000); + addr < ((CACHE_OC_ADDRESS_ARRAY|(address&0x3000)) + +(CACHE_OC_NUM_ENTRIES/4<<CACHE_OC_ENTRY_SHIFT)); + addr += (1<<CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); } - cache_line_index &= 0xff; - /* Loop all the I-cache */ - for (i=0; i<CACHE_IC_NUM_ENTRIES; i++) { - addr = CACHE_IC_ADDRESS_ARRAY| (i<<CACHE_IC_ENTRY_SHIFT); - data = ctrl_inl(addr); - if ((data & CACHE_VALID) && (data&PAGE_MASK) == phys) { - data &= ~CACHE_VALID; - ctrl_outl(data, addr); - if (((i^cache_line_index)&0x80)) - bingo |= 2; + if (vma->vm_flags & VM_EXEC) + /* Loop I-cache 4K */ + for (addr = CACHE_IC_ADDRESS_ARRAY|(address&0x1000); + addr < ((CACHE_IC_ADDRESS_ARRAY|(address&0x1000)) + +(CACHE_IC_NUM_ENTRIES/2<<CACHE_IC_ENTRY_SHIFT)); + addr += (1<<CACHE_IC_ENTRY_SHIFT)) { + data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); } - } back_to_P1(); - restore_flags(flags); - - if (bingo) { - extern void dump_stack(void); - - if (bingo&1) - printk("BINGO!\n"); -#if 0 - if (bingo&2) - printk("Bingo!\n"); -#endif - dump_stack(); - printk("--------------------\n"); - } + save_and_cli(flags); } /* Page is 4K, OC size is 16K, there are four lines. */ @@ -453,7 +371,6 @@ { if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) { clear_page(to); - __flush_wback_region(to, PAGE_SIZE); } else { pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | @@ -490,7 +407,6 @@ { if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) { copy_page(to, from); - __flush_wback_region(to, PAGE_SIZE); } else { pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | Index: arch/sh/mm/clear_page.S =================================================================== RCS file: /cvsroot/linuxsh/kernel/arch/sh/mm/clear_page.S,v retrieving revision 1.2 diff -u -r1.2 clear_page.S --- arch/sh/mm/clear_page.S 2001/07/23 10:27:24 1.2 +++ arch/sh/mm/clear_page.S 2001/08/04 04:02:55 @@ -39,6 +39,9 @@ mov.l r0,@-r4 mov.l r0,@-r4 mov.l r0,@-r4 +#if defined(__SH4__) + ocbwb @r4 +#endif cmp/eq r5,r4 bf/s 1b add #28,r4 Index: arch/sh/mm/copy_page.S =================================================================== RCS file: /cvsroot/linuxsh/kernel/arch/sh/mm/copy_page.S,v retrieving revision 1.3 diff -u -r1.3 copy_page.S --- arch/sh/mm/copy_page.S 2001/07/28 15:23:38 1.3 +++ arch/sh/mm/copy_page.S 2001/08/04 04:02:55 @@ -39,10 +39,13 @@ mov.l @r11+,r4 mov.l @r11+,r5 mov.l @r11+,r6 - mov.l @r11+,r7 #if defined(__sh3__) + mov.l @r11+,r7 mov.l r0,@r10 #elif defined(__SH4__) + mov.l @r11,r7 + ocbp @r11 + add #4,r11 movca.l r0,@r10 #endif add #32,r10 @@ -53,6 +56,9 @@ mov.l r3,@-r10 mov.l r2,@-r10 mov.l r1,@-r10 +#if defined(__SH4__) + ocbwb @r10 +#endif cmp/eq r11,r8 bf/s 1b add #28,r10 Index: arch/sh/mm/fault.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/arch/sh/mm/fault.c,v retrieving revision 1.44 diff -u -r1.44 fault.c --- arch/sh/mm/fault.c 2001/07/18 04:27:38 1.44 +++ arch/sh/mm/fault.c 2001/08/04 04:02:55 @@ -294,10 +294,9 @@ if (__test_and_clear_bit(PG_dcache_dirty, &page->flags)) if (page->mapping) { - unsigned long phys; - /* Physical address of this page */ - phys = PHYSADDR(pte_val(pte)&PAGE_MASK); - __flush_cache_page(phys, 1); + unsigned long phys = pte_val(pte)&PTE_PHYS_MASK; + __flush_purge_region((void *)P1SEGADDR(phys), PAGE_SIZE); + __flush_icache_page(address, phys); } #endif Index: drivers/cdrom/gdrom.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/drivers/cdrom/gdrom.c,v retrieving revision 1.4 diff -u -r1.4 gdrom.c --- drivers/cdrom/gdrom.c 2001/08/03 23:50:59 1.4 +++ drivers/cdrom/gdrom.c 2001/08/04 04:02:56 @@ -144,7 +144,7 @@ } insw(GDROM_DATA, ctrl->buf, count/2); - __flush_wback_region(ctrl->buf, count); + __flush_purge_region(ctrl->buf, count); ctrl->buf += count; ctrl->size -= count; } Index: include/asm-sh/ide.h =================================================================== RCS file: /cvsroot/linuxsh/kernel/include/asm-sh/ide.h,v retrieving revision 1.16 diff -u -r1.16 ide.h --- include/asm-sh/ide.h 2001/08/03 11:22:06 1.16 +++ include/asm-sh/ide.h 2001/08/04 04:02:59 @@ -25,10 +25,23 @@ void *dst, unsigned long count) { - extern void _insw (unsigned long port, void *dst, unsigned long count); + extern void _insw(unsigned long port, void *dst, unsigned long count); _insw(port, dst, count); - __flush_wback_region(dst, (count << 1)); + __flush_purge_region(dst, (count << 1)); +} + +#undef outsw +#define outsw(port, buf, nr) ide_outsw((port), (buf), (nr)) + +static __inline__ void ide_outsw(unsigned long port, + void *src, + unsigned long count) +{ + extern void _outsw(unsigned long port, void *src, unsigned long count); + + _outsw(port, src, count); + __flush_purge_region(src, (count << 1)); } #endif Index: include/asm-sh/pgtable.h =================================================================== RCS file: /cvsroot/linuxsh/kernel/include/asm-sh/pgtable.h,v retrieving revision 1.39 diff -u -r1.39 pgtable.h --- include/asm-sh/pgtable.h 2001/08/03 11:22:06 1.39 +++ include/asm-sh/pgtable.h 2001/08/04 04:02:59 @@ -36,7 +36,7 @@ #define flush_cache_all() do { } while (0) #define flush_cache_mm(mm) do { } while (0) #define flush_cache_range(mm, start, end) do { } while (0) -#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_cache_page(vma, vmaddr, page) do { } while (0) #define flush_page_to_ram(page) do { } while (0) #define flush_dcache_page(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) @@ -56,7 +56,8 @@ extern void flush_cache_mm(struct mm_struct *mm); extern void flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end); -extern void flush_cache_page(struct vm_area_struct *vma, unsigned long addr); +extern void flush_cache_page(struct vm_area_struct *vma, unsigned long addr, + struct page *page); extern void flush_dcache_page(struct page *pg); extern void flush_icache_range(unsigned long start, unsigned long end); extern void flush_cache_sigtramp(unsigned long addr); @@ -70,7 +71,7 @@ extern void __flush_purge_region(void *start, int size); /* Flush a page */ -extern void __flush_cache_page(unsigned long phys, int exec); +extern void __flush_cache_page(unsigned long phys, unsigned long u0, int exec); extern void __flush_icache_page(unsigned long u0, unsigned long phys); /* Initialization of P3 area for copy_user_page */ @@ -101,6 +102,8 @@ #define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) #define FIRST_USER_PGD_NR 0 +#define PTE_PHYS_MASK 0x1ffff000 + #ifndef __ASSEMBLY__ /* * First 1MB map is used by fixed purpose. @@ -205,7 +208,7 @@ */ #define page_address(page) ((page)->virtual) /* P1 address of the page */ #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) -#define pte_page(x) phys_to_page(pte_val(x)) +#define pte_page(x) phys_to_page(pte_val(x)&PTE_PHYS_MASK) /* * The following only work if pte_present() is true. Index: kernel/ptrace.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/kernel/ptrace.c,v retrieving revision 1.8 diff -u -r1.8 ptrace.c --- kernel/ptrace.c 2001/07/23 00:00:57 1.8 +++ kernel/ptrace.c 2001/08/04 04:03:03 @@ -100,7 +100,7 @@ } get_page(page); spin_unlock(&mm->page_table_lock); - flush_cache_page(vma, addr); + flush_cache_page(vma, addr, page); if (write) { maddr = kmap(page); Index: mm/vmscan.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/mm/vmscan.c,v retrieving revision 1.41 diff -u -r1.41 vmscan.c --- mm/vmscan.c 2001/07/31 00:05:21 1.41 +++ mm/vmscan.c 2001/08/04 04:03:04 @@ -102,7 +102,7 @@ * Basically, this just makes it possible for us to do * some real work in the future in "refill_inactive()". */ - flush_cache_page(vma, address); + flush_cache_page(vma, address, page); if (!pte_dirty(pte)) goto drop_pte; -- |