From: NIIBE Y. <gn...@m1...> - 2001-08-02 05:40:49
|
We need cache flushing when process forks. I think that this is the bug in the mainline, I'll send a report to lkml (or MM-Bugzilla?). When we forks, we set COW (copy-on-write) page to be write protected. When doing that, we need to flush the cache (at least for SH-4, which has write-back cache). Besides, in order to not having the alias absolutely, (1) I've changed the use of __flush_wback_region for I/O into __flush_purge_region, and (2) I've changed copy_user_page function so that it doesn't remain data fetched. This change does no harm for current implementation, but it is required by forthcoming change. With old implementation, problem occurs (it's quite rare, though) like this. (1) Say, a process read the variable at .data section. Read I/O request on the page are issued (say, it's to IDE driver). (2) IDE driver issues the command to the disk, and get the result. (See the `insw' implementation in asm-sh/ide.h.) By insw, the data goes to the memory with __flush_wback_range. (*** It should be __flush_purge_range. ***) (3) The page is mapped to the process. Supporse a process write to the page after that. Then, the process forks. The page is set to be write-protected, to be COW. (*** Here, we need to flush the page so that user process data goes to memory ***) (4) The forked process writes to the variable. COW page-handling occurs here. The original page is copied to new page. But if the cache entry of (2) still remains, the process sees the bogus data on the cache, not accessing the memory. 2001-08-02 NIIBE Yutaka <gn...@m1...> * mm/memory.c (copy_page_range): Write back the cache before we set PTE read-protect. * arch/sh/mm/cache-sh4.c (copy_user_page): Invalidate the cache, which is used to read the data from FROM. Let cache not to have data. * arch/sh/mm/__copy_user_page-sh4.S (__copy_user_page): Likewise. * drivers/maple/maple.c (maple_send): Call dma_cache_wback_inv. (was: __flush_wback_region). * include/asm-sh/io.h (dma_cache_wback_inv, dma_cache_inv, dma_cache_wback): Defined with new __flush_XXX_region functions. (was: bogus cache_XXX_area functions, not implemented). * drivers/cdrom/gdrom.c (gdrom_intr): Call __flush_purge_region instead of __flush_wback_region. * include/asm-sh/ide.h (ide_insw): Likewise. * arch/sh/mm/cache-sh4.c (__flush_purge_region): New function. (__flush_wback_region, __flush_invalidate_region): Last argument is SIZE (was: END). * include/asm-sh/pgtable.h (__flush_purge_region): Added. 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/02 05:28:39 @@ -44,7 +44,9 @@ mov.l @r11+,r4 mov.l @r11+,r5 mov.l @r11+,r6 - mov.l @r11+,r7 + mov.l @r11,r7 + ocbi @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.7 diff -u -r1.7 cache-sh4.c --- arch/sh/mm/cache-sh4.c 2001/07/28 15:12:46 1.7 +++ arch/sh/mm/cache-sh4.c 2001/08/02 05:28:39 @@ -125,12 +125,14 @@ * * START, END: Virtual Address (U0, P1, or P3) */ -void __flush_wback_region(void *start, void *end) +void __flush_wback_region(void *start, int size) { unsigned long v; + unsigned long begin, end; - for (v = (unsigned long)start& ~(L1_CACHE_BYTES-1); - v < (unsigned long)end; v+=L1_CACHE_BYTES) { + begin = (unsigned long)start& ~(L1_CACHE_BYTES-1); + end = begin + size; + for (v = begin; v < end; v+=L1_CACHE_BYTES) { asm volatile("ocbwb %0" : /* no output */ : "m" (__m(v))); @@ -138,14 +140,36 @@ } /* + * Write back the dirty D-caches and invalidate them. + * + * START, END: Virtual Address (U0, P1, or P3) + */ +void __flush_purge_region(void *start, int size) +{ + unsigned long v; + unsigned long begin, end; + + begin = (unsigned long)start& ~(L1_CACHE_BYTES-1); + end = begin + size; + for (v = begin; v < end; v+=L1_CACHE_BYTES) { + asm volatile("ocbp %0" + : /* no output */ + : "m" (__m(v))); + } +} + + +/* * No write back please */ -void __flush_invalidate_region(unsigned long start, unsigned long end) +void __flush_invalidate_region(void *start, int size) { unsigned long v; + unsigned long begin, end; - start &= ~(L1_CACHE_BYTES-1); - for (v = start; v < end; v+=L1_CACHE_BYTES) { + begin = (unsigned long)start& ~(L1_CACHE_BYTES-1); + end = begin + size; + for (v = begin; v < end; v+=L1_CACHE_BYTES) { asm volatile("ocbi %0" : /* no output */ : "m" (__m(v))); @@ -383,7 +407,7 @@ { if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) { clear_page(to); - __flush_wback_region(to, to+PAGE_SIZE); + __flush_wback_region(to, PAGE_SIZE); } else { pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | @@ -420,7 +444,8 @@ { if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) { copy_page(to, from); - __flush_wback_region(to, to+PAGE_SIZE); + __flush_invalidate_region(from, PAGE_SIZE); + __flush_wback_region(to, PAGE_SIZE); } else { pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | Index: arch/sh/mm/init.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/arch/sh/mm/init.c,v retrieving revision 1.17 diff -u -r1.17 init.c --- arch/sh/mm/init.c 2001/07/24 05:24:33 1.17 +++ arch/sh/mm/init.c 2001/08/02 05:28:39 @@ -144,7 +144,7 @@ /* clear the zero-page */ memset(empty_zero_page, 0, PAGE_SIZE); - __flush_wback_region(empty_zero_page, empty_zero_page+PAGE_SIZE); + __flush_wback_region(empty_zero_page, PAGE_SIZE); /* this will put all low memory onto the freelists */ totalram_pages += free_all_bootmem_node(NODE_DATA(0)); Index: drivers/cdrom/gdrom.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/drivers/cdrom/gdrom.c,v retrieving revision 1.2 diff -u -r1.2 gdrom.c --- drivers/cdrom/gdrom.c 2001/07/31 07:03:58 1.2 +++ drivers/cdrom/gdrom.c 2001/08/02 05:28:40 @@ -144,7 +144,7 @@ } insw(GDROM_DATA, ctrl->buf, count/2); - __flush_wback_region(ctrl->buf, ctrl->buf + count); + __flush_purge_region(ctrl->buf, count); ctrl->buf += count; ctrl->size -= count; } Index: drivers/maple/maple.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/drivers/maple/maple.c,v retrieving revision 1.5 diff -u -r1.5 maple.c --- drivers/maple/maple.c 2001/07/30 13:01:42 1.5 +++ drivers/maple/maple.c 2001/08/02 05:28:41 @@ -371,8 +371,7 @@ if (maple_packets>0) { for (i=0; i<(1<<MAPLE_DMA_PAGES); i++) - __flush_wback_region(maple_sendbuf+i*PAGE_SIZE, - maple_sendbuf+i*(PAGE_SIZE+1)); + dma_cache_wback_inv(maple_sendbuf+i*PAGE_SIZE, PAGE_SIZE); ctrl_outl(1, MAPLE_STATE); } Index: include/asm-sh/ide.h =================================================================== RCS file: /cvsroot/linuxsh/kernel/include/asm-sh/ide.h,v retrieving revision 1.15 diff -u -r1.15 ide.h --- include/asm-sh/ide.h 2001/07/24 05:24:33 1.15 +++ include/asm-sh/ide.h 2001/08/02 05:28:46 @@ -26,10 +26,9 @@ unsigned long count) { extern void _insw (unsigned long port, void *dst, unsigned long count); - void *end = dst + (count << 1); _insw(port, dst, count); - __flush_wback_region(dst, end); + __flush_purge_region(dst, (count << 1)); } #endif Index: include/asm-sh/io.h =================================================================== RCS file: /cvsroot/linuxsh/kernel/include/asm-sh/io.h,v retrieving revision 1.28 diff -u -r1.28 io.h --- include/asm-sh/io.h 2001/07/27 06:09:47 1.28 +++ include/asm-sh/io.h 2001/08/02 05:28:46 @@ -465,11 +465,11 @@ */ #define dma_cache_wback_inv(_start,_size) \ - cache_flush_area((unsigned long)(_start),((unsigned long)(_start)+(_size))) + __flush_purge_region(_start,_size) #define dma_cache_inv(_start,_size) \ - cache_purge_area((unsigned long)(_start),((unsigned long)(_start)+(_size))) + __flush_invalidate_region(_start,_size) #define dma_cache_wback(_start,_size) \ - cache_wback_area((unsigned long)(_start),((unsigned long)(_start)+(_size))) + __flush_wback_region(_start,_size) #endif /* __KERNEL__ */ #endif /* __ASM_SH_IO_H */ Index: include/asm-sh/pgtable.h =================================================================== RCS file: /cvsroot/linuxsh/kernel/include/asm-sh/pgtable.h,v retrieving revision 1.38 diff -u -r1.38 pgtable.h --- include/asm-sh/pgtable.h 2001/07/30 07:24:01 1.38 +++ include/asm-sh/pgtable.h 2001/08/02 05:28:46 @@ -46,6 +46,7 @@ #define p3_cache_init() do { } while (0) #define __flush_dcache_region(start, end) do { } while (0) #define __flush_wback_region(start, end) do { } while (0) +#define __flush_purge_region(start, end) do { } while (0) #elif defined(__SH4__) /* @@ -63,8 +64,10 @@ #define flush_page_to_ram(page) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) -/* Flush a region (smaller than a page) */ -extern void __flush_wback_region(void *start, void *end); +/* Flush (write-back only) a region (smaller than a page) */ +extern void __flush_wback_region(void *start, int size); +/* 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); Index: mm/memory.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/mm/memory.c,v retrieving revision 1.36 diff -u -r1.36 memory.c --- mm/memory.c 2001/07/31 00:05:21 1.36 +++ mm/memory.c 2001/08/02 05:28:47 @@ -227,6 +227,10 @@ if (cow) { ptep_set_wrprotect(src_pte); pte = *src_pte; +#if 1 /* For write-back cache system which has alias issues, we need to + * flush the cache before COW */ + flush_cache_page(vma, address); +#endif } /* If it's a shared mapping, mark it clean in the child */ -- |