From: NIIBE Y. <gn...@m1...> - 2001-08-04 10:34:59
|
NIIBE Yutaka wrote: > 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. It found that the bug is actually in vmscan.c:try_to_swapout. Here's updated version: * mm/vmscan.c (try_to_swap_out): Bug fix. Flush the page before we clear the PTE. * arch/sh/mm/cache-sh4.c (copy_user_page, clear_user_page): Don't need to write back (it's done in copy_page). (check_cache_page, __flush_cache_page, __flush_icache_page): Removed. (flush_dcache_page): Flush P1 D-cache only (we should not have valid U0 cache entry here). (flush_cache_page): Flush only U0. (we should no have valid P1 cache entry here). * arch/sh/mm/fault.c (update_mmu_cache): Flush P1 D-cache only (we should not have valid U0 cache entry 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, __flush_icache_page): Removed. * 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 -p -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 10:28:16 @@ -44,7 +44,9 @@ ENTRY(__copy_user_page) 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 @@ -54,11 +56,10 @@ ENTRY(__copy_user_page) mov.l r3,@-r10 mov.l r2,@-r10 mov.l r1,@-r10 - mov r10,r0 - add #28,r10 + ocbwb @r10 cmp/eq r11,r8 bf/s 1b - ocbwb @r0 + add #28,r10 ! mov.l @r15+,r11 mov.l @r15+,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 -p -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 10:28:16 @@ -211,95 +211,16 @@ void flush_cache_sigtramp(unsigned long } /* - * Writeback&Invalidate the D-cache of the page - * Invalidate the I-cache of the page, if needed - */ -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; - unsigned long flags; - - phys|=CACHE_VALID; - - save_and_cli(flags); - if (u0) { - jump_to_P2(); - /* Loop half 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)); - addr += (1<<CACHE_IC_ENTRY_SHIFT)) { - data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); - if (data == phys) - ctrl_outl(0, addr); - } - back_to_P1(); - } else { - jump_to_P2(); - /* 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(); - } - restore_flags(flags); -} - -/* * 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); - } else - set_bit(PG_dcache_dirty, &pg->flags); + if (!page->mapping + || page->mapping->i_mmap || page->mapping->i_mmap_shared) + __flush_purge_region(page_address(page), PAGE_SIZE); + else + set_bit(PG_dcache_dirty, &page->flags); } void flush_cache_all(void) @@ -357,88 +278,46 @@ void flush_cache_range(struct mm_struct * * 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) { pgd_t *dir; pmd_t *pmd; pte_t *pte; pte_t entry; unsigned long phys; + unsigned long addr, data; + unsigned long flags; + + __flush_purge_region((void *)(address&PAGE_MASK), PAGE_SIZE); + if ((vma->vm_flags & VM_EXEC) == 0) + return; - dir = pgd_offset(vma->vm_mm, addr); - pmd = pmd_offset(dir, addr); + dir = pgd_offset(vma->vm_mm, address); + pmd = pmd_offset(dir, address); if (pmd_none(*pmd)) return; if (pmd_bad(*pmd)) return; - pte = pte_offset(pmd, addr); + pte = pte_offset(pmd, address); 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 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; + phys = (pte_val(entry) & PTE_PHYS_MASK) | 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; - } - } - - 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; - } + /* 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"); - } } /* Page is 4K, OC size is 16K, there are four lines. */ @@ -451,10 +330,9 @@ void check_cache_page(struct page *pg) */ void clear_user_page(void *to, unsigned long address) { - if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) { + if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) clear_page(to); - __flush_wback_region(to, PAGE_SIZE); - } else { + else { pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | _PAGE_DIRTY | _PAGE_ACCESSED | @@ -488,10 +366,9 @@ void clear_user_page(void *to, unsigned */ void copy_user_page(void *to, void *from, unsigned long address) { - if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) { + if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) copy_page(to, from); - __flush_wback_region(to, PAGE_SIZE); - } else { + else { pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | _PAGE_DIRTY | _PAGE_ACCESSED | 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 -p -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 10:28:16 @@ -39,6 +39,9 @@ ENTRY(clear_page) 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 -p -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 10:28:16 @@ -39,10 +39,13 @@ ENTRY(copy_page) 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 @@ ENTRY(copy_page) 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 -p -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 10:28:16 @@ -294,10 +294,8 @@ void update_mmu_cache(struct vm_area_str 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); } #endif Index: drivers/block/rd.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/drivers/block/rd.c,v retrieving revision 1.8 diff -u -p -r1.8 rd.c --- drivers/block/rd.c 2001/07/31 00:05:21 1.8 +++ drivers/block/rd.c 2001/08/04 10:28:16 @@ -226,6 +226,7 @@ static int rd_make_request(request_queue * sbh might be - NeilBrown */ bdata = bh_kmap(sbh); + flush_cache_all(); if (rw == READ) { if (sbh != rbh) memcpy(bdata, rbh->b_data, rbh->b_size); Index: drivers/cdrom/gdrom.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/drivers/cdrom/gdrom.c,v retrieving revision 1.4 diff -u -p -r1.4 gdrom.c --- drivers/cdrom/gdrom.c 2001/08/03 23:50:59 1.4 +++ drivers/cdrom/gdrom.c 2001/08/04 10:28:16 @@ -144,7 +144,7 @@ gdrom_intr(int irq, void *dev_id, struct } 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 -p -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 10:28:18 @@ -25,10 +25,23 @@ static __inline__ void ide_insw(unsigned 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 -p -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 10:28:18 @@ -69,10 +69,6 @@ extern void __flush_wback_region(void *s /* Flush (write-back & invalidate) a region (smaller than a page) */ 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_icache_page(unsigned long u0, unsigned long phys); - /* Initialization of P3 area for copy_user_page */ extern void p3_cache_init(void); #endif @@ -101,6 +97,8 @@ extern unsigned long empty_zero_page[102 #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 +203,7 @@ extern unsigned long empty_zero_page[102 */ #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: mm/vmscan.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/mm/vmscan.c,v retrieving revision 1.41 diff -u -p -r1.41 vmscan.c --- mm/vmscan.c 2001/07/31 00:05:21 1.41 +++ mm/vmscan.c 2001/08/04 10:28:24 @@ -65,6 +65,7 @@ static void try_to_swap_out(struct mm_st * is needed on CPUs which update the accessed and dirty * bits in hardware. */ + flush_cache_page(vma, address); pte = ptep_get_and_clear(page_table); flush_tlb_page(vma, address); @@ -102,7 +103,6 @@ drop_pte: * Basically, this just makes it possible for us to do * some real work in the future in "refill_inactive()". */ - flush_cache_page(vma, address); if (!pte_dirty(pte)) goto drop_pte; -- |