* [PATCH] fix cache coherency issues @ 2006-02-13 16:15 Atsushi Nemoto 2006-02-14 1:59 ` Yoichi Yuasa 2006-05-22 15:34 ` Atsushi Nemoto 0 siblings, 2 replies; 19+ messages in thread From: Atsushi Nemoto @ 2006-02-13 16:15 UTC (permalink / raw) To: linux-mips; +Cc: ralf There are several cache-related problems with multi-threaded program and fork(). Problem-1: copy-on-write and signal trampoline on harvard-cache. [previously reported with subject: missing data cache flush for signal trampoline on fork] A program which is heavily using signal and fork occasionally killed by SIGSEGV, etc. When it was killed, PC is always near the stack pointer. This would happen on CPUs without MIPS_CACHE_IC_F_DC. D-cache aliasing is irrelevant. 1. To handle a delivered signal, a signal-trampoline code are written to the stack page. 2. They are flushed to memory immediately and I-cache are invalidated. 3. If other thread called fork() before the signal handler is executed, all writable pages (including the stack page) are marked as COW page. 4. When the user signal handler is to write to the stack, the page will be copied to new physical page by copy_user_highpage(), but not flushed to main memory. 5. Then flush_cache_page() is called for the stack page, but it does not flush the cache written by copy_user_highpage() because new PTE is not established yet. 6. When returned from the user signal handler, the signal trampoline code might not be written to main memory. Garbage code will be executed and the program will die. Problem-2: copy-on-write and dcache-aliasing [previously reported with subject: dcache aliasing problem on fork] 1. Now there is a process containing two thread (T1 and T2). The thread T1 calls fork(). Then dup_mmap() function called on T1 context. static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) ... flush_cache_mm(current->mm); ... /* A */ (write-protect all Copy-On-Write pages) ... /* B */ flush_tlb_mm(current->mm); ... 2. When preemption happens between A and B (or on SMP kernel), the thread T2 can run and modify data on COW pages without page fault (modified data will stay in cache). 3. Some time after fork() completed, the thread T2 may cause a page fault by write-protect on a COW page. 4. Then data of the COW page will be copied to newly allocated physical page (copy_cow_page()). It reads data via kernel mapping. The kernel mapping can have different 'color' with user space mapping of the thread T2 (dcache aliasing). Therefore copy_cow_page() will copy stale data. Then the modified data in cache will be lost. This patch fixes above problems using custom copy_user_highpage(). It uses kmap_coherent() to map an user page for kernel with same color. Also copy_to_user_page() and copy_from_user_page() are rewritten using the kmap_coherent() to avoid extra cache flushing. The main part of this patch was originally written by Ralf Baechle. arch/mips/mm/init.c | 132 ++++++++++++++++++++++++++++++++++++++++++ include/asm-mips/cacheflush.h | 14 ++-- include/asm-mips/fixmap.h | 8 +- include/asm-mips/page.h | 14 +--- include/linux/highmem.h | 7 +- mm/memory.c | 8 +- 6 files changed, 160 insertions(+), 23 deletions(-) Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 0ff9a34..333fd59 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -30,11 +30,15 @@ #include <asm/cachectl.h> #include <asm/cpu.h> #include <asm/dma.h> +#include <asm/interrupt.h> +#include <asm/kmap_types.h> +#include <asm/mipsmtregs.h> #include <asm/mmu_context.h> #include <asm/sections.h> #include <asm/pgtable.h> #include <asm/pgalloc.h> #include <asm/tlb.h> +#include <asm/fixmap.h> DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); @@ -79,6 +83,134 @@ unsigned long setup_zero_pages(void) return 1UL << order; } +/* + * These are almost like kmap_atomic / kunmap_atmic except they take an + * additional address argument as the hint. + */ + +static inline void *kmap_coherent(struct page *page, unsigned long addr) +{ + unsigned long vaddr, flags, entrylo; + enum fixed_addresses idx; + unsigned long asid; + unsigned int vpflags; + unsigned int wired; + + if (!cpu_has_dc_aliases) + return page_address(page); + inc_preempt_count(); + + if (cpu_has_mipsmt) + vpflags = dvpe(); + local_irq_save(flags); + + idx = (addr >> 12) & 7; + vaddr = __fix_to_virt(FIX_CMAP_END - idx); + + asid = read_c0_entryhi(); + wired = read_c0_wired(); + write_c0_wired(wired + 1); + write_c0_index(wired); + write_c0_entryhi(vaddr & ~0x1fffUL); + entrylo = (page_to_pfn(page) << 6) | (pgprot_val(PAGE_KERNEL) >> 6); + write_c0_entrylo0(entrylo); + write_c0_entrylo1(entrylo); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + write_c0_entryhi(asid); + + local_irq_restore(flags); + if (cpu_has_mipsmt) + evpe(vpflags); + + return (void*) vaddr; +} + +#define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1))) + +static inline void kunmap_coherent(void) +{ + unsigned int vpflags, wired; + unsigned long flags, asid; + + if (!cpu_has_dc_aliases) + return; + if (cpu_has_mipsmt) + vpflags = dvpe(); + local_irq_save(flags); + + asid = read_c0_entryhi(); + wired = read_c0_wired() - 1; + write_c0_wired(wired); + write_c0_index(wired); + write_c0_entryhi(UNIQUE_ENTRYHI(wired)); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + write_c0_entryhi(asid); + + local_irq_restore(flags); + if (cpu_has_mipsmt) + evpe(vpflags); + + dec_preempt_count(); + preempt_check_resched(); +} + +void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + char *vfrom, *vto; + /* + * Map 'from' page to same color with vaddr and map 'to' page + * to kernel, and flush 'to' page if needed. + * Just using kmap_coherent for both 'from' and 'to' (and no + * flushing) is not enough because: + * 1. The signal trampoline code should be flushed to main memory. + * 2. The page 'to' might have been cached via kernel mapping. + */ + vfrom = kmap_coherent(from, vaddr); + vto = kmap_atomic(to, KM_USER1); + copy_page(vto, vfrom); + if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) || + (cpu_has_dc_aliases && pages_do_alias((unsigned long)vto, vaddr))) + flush_data_cache_page((unsigned long)vto); + kunmap_atomic(vto, KM_USER1); + kunmap_coherent(); + /* Make sure this page is cleared on other CPU's too before using it */ + smp_wmb(); +} + +EXPORT_SYMBOL(copy_user_highpage); + +void __copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, const void *src, + unsigned long len) +{ + char *vto; + + vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(vto, src, len); + kunmap_coherent(); + flush_icache_page(vma, page); +} + +EXPORT_SYMBOL(__copy_to_user_page); + +void __copy_from_user_page(struct page *page, unsigned long vaddr, + void *dst, unsigned long len) +{ + char *vfrom; + + vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(dst, vfrom, len); + kunmap_coherent(); +} + +EXPORT_SYMBOL(__copy_from_user_page); + + #ifdef CONFIG_HIGHMEM pte_t *kmap_pte; pgprot_t kmap_prot; diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index aeae9fa..7e4c30d 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -53,23 +53,23 @@ extern void (*flush_icache_range)(unsign #define flush_cache_vmap(start, end) flush_cache_all() #define flush_cache_vunmap(start, end) flush_cache_all() +extern void __copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, const void *src, + unsigned long len); static inline void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); - flush_icache_page(vma, page); + __copy_to_user_page(vma, page, vaddr, src, len); } +extern void __copy_from_user_page(struct page *page, unsigned long vaddr, + void *dst, unsigned long len); static inline void copy_from_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); + __copy_from_user_page(page, vaddr, dst, len); } extern void (*flush_cache_sigtramp)(unsigned long addr); diff --git a/include/asm-mips/fixmap.h b/include/asm-mips/fixmap.h index 73a3028..3dc8af6 100644 --- a/include/asm-mips/fixmap.h +++ b/include/asm-mips/fixmap.h @@ -46,8 +46,12 @@ * fix-mapped? */ enum fixed_addresses { +#define FIX_N_COLOURS 8 + FIX_CMAP_BEGIN, + FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS, #ifdef CONFIG_HIGHMEM - FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ + /* reserved pte's for temporary kernel mappings */ + FIX_KMAP_BEGIN = FIX_CMAP_END + 1, FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #endif __end_of_fixed_addresses @@ -70,7 +74,7 @@ extern void __set_fixmap (enum fixed_add * the start of the fixmap, and leave one page empty * at the top of mem.. */ -#define FIXADDR_TOP (0xffffe000UL) +#define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000) #define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index ee25a77..a896495 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -62,15 +62,11 @@ static inline void clear_user_page(void flush_data_cache_page((unsigned long)addr); } -static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, - struct page *to) -{ - extern void (*flush_data_cache_page)(unsigned long addr); - - copy_page(vto, vfrom); - if (pages_do_alias((unsigned long)vto, vaddr)) - flush_data_cache_page((unsigned long)vto); -} +struct vm_area_struct; +extern void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma); + +#define __HAVE_ARCH_COPY_USER_HIGHPAGE /* * These are used to make use of C type-checking.. diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 6bece92..768914a 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -78,7 +78,10 @@ static inline void memclear_highpage_flu kunmap_atomic(kaddr, KM_USER0); } -static inline void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr) +#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE + +static inline void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) { char *vfrom, *vto; @@ -91,6 +94,8 @@ static inline void copy_user_highpage(st smp_wmb(); } +#endif + static inline void copy_highpage(struct page *to, struct page *from) { char *vfrom, *vto; diff --git a/mm/memory.c b/mm/memory.c index 2bee1f2..653b08f 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1386,7 +1386,7 @@ static inline pte_t maybe_mkwrite(pte_t return pte; } -static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va) +static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va, struct vm_area_struct *vma) { /* * If the source page was a PFN mapping, we don't have @@ -1410,7 +1410,7 @@ static inline void cow_user_page(struct return; } - copy_user_highpage(dst, src, va); + copy_user_highpage(dst, src, va, vma); } /* @@ -1475,7 +1475,7 @@ gotten: new_page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!new_page) goto oom; - cow_user_page(new_page, old_page, address); + cow_user_page(new_page, old_page, address, vma); } /* @@ -2074,7 +2074,7 @@ retry: page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!page) goto oom; - copy_user_highpage(page, new_page, address); + copy_user_highpage(page, new_page, address, vma); page_cache_release(new_page); new_page = page; anon = 1; ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-13 16:15 [PATCH] fix cache coherency issues Atsushi Nemoto @ 2006-02-14 1:59 ` Yoichi Yuasa 2006-02-14 2:15 ` Atsushi Nemoto 2006-05-22 15:34 ` Atsushi Nemoto 1 sibling, 1 reply; 19+ messages in thread From: Yoichi Yuasa @ 2006-02-14 1:59 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: yoichi_yuasa, linux-mips, ralf Hi Nemoto-san, VR41xx cannot be booted with 2.6.16-rc2 + patch. It freeze after "Freeing unused kernel memory: 168k freed". Yoichi ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 1:59 ` Yoichi Yuasa @ 2006-02-14 2:15 ` Atsushi Nemoto 2006-02-14 2:26 ` Yoichi Yuasa 0 siblings, 1 reply; 19+ messages in thread From: Atsushi Nemoto @ 2006-02-14 2:15 UTC (permalink / raw) To: yoichi_yuasa; +Cc: linux-mips, ralf >>>>> On Tue, 14 Feb 2006 10:59:28 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: yuasa> VR41xx cannot be booted with 2.6.16-rc2 + patch. It freeze yuasa> after "Freeing unused kernel memory: 168k freed". Good morning! Thank you very much for testing. Could you give me a full boot log and some other informations? (icache/dcache size/associativity, highmem/preempt/smp enabled?) --- Atsushi Nemoto ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 2:15 ` Atsushi Nemoto @ 2006-02-14 2:26 ` Yoichi Yuasa 2006-02-14 3:08 ` Atsushi Nemoto 0 siblings, 1 reply; 19+ messages in thread From: Yoichi Yuasa @ 2006-02-14 2:26 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: yoichi_yuasa, linux-mips, ralf Good morning, On Tue, 14 Feb 2006 11:15:47 +0900 (JST) Atsushi Nemoto <anemo@mba.ocn.ne.jp> wrote: > >>>>> On Tue, 14 Feb 2006 10:59:28 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: > yuasa> VR41xx cannot be booted with 2.6.16-rc2 + patch. It freeze > yuasa> after "Freeing unused kernel memory: 168k freed". Oops, I'm testing 2.6.16-rc3 + patch on VR41xx. ^^^ > > Good morning! Thank you very much for testing. > > Could you give me a full boot log and some other informations? > (icache/dcache size/associativity, highmem/preempt/smp enabled?) Here is the boot log. Linux version 2.6.16-rc3 (yuasa@localhost) (gcc version 3.3.2) #5 Tue Feb 14 10:32:44 JST 2006 CPU revision is: 00000c83 PClock: 199065600Hz VTClock: 99532800Hz TClock: 49766400Hz Determined physical RAM map: User-defined physical RAM map: memory: 04000000 @ 00000000 (usable) Built 1 zonelists Kernel command line: mem=64M console=ttyVR0,115200 ip=any root=/dev/nfs Primary instruction cache 16kB, physically tagged, 2-way, linesize 16 bytes. Primary data cache 16kB, 2-way, linesize 16 bytes. Synthesized TLB refill handler (21 instructions). Synthesized TLB load handler fastpath (33 instructions). Synthesized TLB store handler fastpath (33 instructions). Synthesized TLB modify handler fastpath (32 instructions). PID hash table entries: 512 (order: 9, 8192 bytes) Using 12.442 MHz high precision timer. Console: colour dummy device 80x25 Dentry cache hash table entries: 16384 (order: 4, 65536 bytes) Inode-cache hash table entries: 8192 (order: 3, 32768 bytes) Memory: 61116k/65536k available (2887k kernel code, 4356k reserved, 532k data, 168k init, 0k highmem) Mount-cache hash table entries: 512 Checking for 'wait' instruction... unavailable. NET: Registered protocol family 16 SCSI subsystem initialized PCI: Bridge: 0000:01:00.0 IO window: disabled. MEM window: 11a00000-11afffff PREFETCH window: disabled. PCI: Bridge: 0000:00:12.0 IO window: 1000000-1000fff MEM window: 11a00000-11bfffff PREFETCH window: disabled. SGI XFS with ACLs, no debug enabled SGI XFS Quota Management subsystem io scheduler noop registered io scheduler anticipatory registered (default) io scheduler deadline registered io scheduler cfq registered GIU: major number 254 SIU: ttyVR0 at MMIO 0xf000800 (irq = 17) is a SIU SIU: ttyVR1 at MMIO 0xf000820 (irq = 29) is a DSIU RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize r8169 Gigabit Ethernet driver 2.2LK loaded r8169: PowerManagement capability not found. eth0: RTL8169 at 0xb1b00000, 00:20:0f:00:24:04, IRQ 53 r8169 Gigabit Ethernet driver 2.2LK loaded r8169: PowerManagement capability not found. eth1: RTL8169 at 0xb1b00100, 00:20:0f:00:24:05, IRQ 53 Uniform Multi-Platform E-IDE driver Revision: 7.00alpha2 ide: Assuming 33MHz system bus speed for PIO modes; override with idebus=xx SiI680: IDE controller at PCI slot 0000:00:11.0 SiI680: chipset revision 2 SiI680: BASE CLOCK == 133 SiI680: 100% native mode on irq 48 ide0: MMIO-DMA , BIOS settings: hda:pio, hdb:pio ide1: MMIO-DMA , BIOS settings: hdc:pio, hdd:pio hda: Hitachi CVM1.1.1, CFA DISK drive ide0 at 0xb1c80080-0xb1c80087,0xb1c8008a on irq 48 hda: max request size: 64KiB hda: 250368 sectors (128 MB) w/1KiB Cache, CHS=978/8/32 hda: hda1 NET: Registered protocol family 2 IP route cache hash table entries: 1024 (order: 0, 4096 bytes) TCP established hash table entries: 4096 (order: 2, 16384 bytes) TCP bind hash table entries: 4096 (order: 2, 16384 bytes) TCP: Hash tables configured (established 4096 bind 4096) TCP reno registered TCP bic registered NET: Registered protocol family 1 NET: Registered protocol family 17 r8169: eth0: link up r8169: eth1: link down Sending BOOTP requests . OK IP-Config: Got BOOTP answer from 192.168.2.1, my address is 192.168.2.101 IP-Config: Complete: device=eth0, addr=192.168.2.101, mask=255.255.255.0, gw=255.255.255.255, host=192.168.2.101, domain=tripeaks.co.jp, nis-domain=(none), bootserver=192.168.2.1, rootserver=192.168.2.1, rootpath=/opt/nfsroot-tb0287 Looking up port of RPC 100003/2 on 192.168.2.1 Looking up port of RPC 100005/1 on 192.168.2.1 VFS: Mounted root (nfs filesystem) readonly. Freeing unused kernel memory: 168k freed Yoichi ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 2:26 ` Yoichi Yuasa @ 2006-02-14 3:08 ` Atsushi Nemoto 2006-02-14 7:07 ` Yoichi Yuasa 0 siblings, 1 reply; 19+ messages in thread From: Atsushi Nemoto @ 2006-02-14 3:08 UTC (permalink / raw) To: yoichi_yuasa; +Cc: linux-mips, ralf >>>>> On Tue, 14 Feb 2006 11:26:53 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: yuasa> Here is the boot log. Thanks. Could you try with this patch? http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=20060204.015356.74753400.anemo%40mba.ocn.ne.jp Or this quick workaround? diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 1b71d91..5bd413f 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -425,7 +425,7 @@ static inline void local_r4k_flush_cache * Do indexed flush, too much work to get the (possible) TLB refills * to work correctly. */ - addr = INDEX_BASE + (addr & (dcache_size - 1)); + addr = INDEX_BASE + (addr & (current_cpu_data.dcache.waysize - 1)); if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) { r4k_blast_dcache_page_indexed(addr); if (exec && !cpu_icache_snoops_remote_store) ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 3:08 ` Atsushi Nemoto @ 2006-02-14 7:07 ` Yoichi Yuasa 2006-02-14 7:42 ` Atsushi Nemoto 0 siblings, 1 reply; 19+ messages in thread From: Yoichi Yuasa @ 2006-02-14 7:07 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: yoichi_yuasa, linux-mips, ralf Hi, On Tue, 14 Feb 2006 12:08:46 +0900 (JST) Atsushi Nemoto <anemo@mba.ocn.ne.jp> wrote: > >>>>> On Tue, 14 Feb 2006 11:26:53 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: > yuasa> Here is the boot log. > > Thanks. Could you try with this patch? > > http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=20060204.015356.74753400.anemo%40mba.ocn.ne.jp I added the patch and tested it. It has same problem. Yoichi ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 7:07 ` Yoichi Yuasa @ 2006-02-14 7:42 ` Atsushi Nemoto 2006-02-14 8:56 ` Yoichi Yuasa 0 siblings, 1 reply; 19+ messages in thread From: Atsushi Nemoto @ 2006-02-14 7:42 UTC (permalink / raw) To: yoichi_yuasa; +Cc: linux-mips, ralf >>>>> On Tue, 14 Feb 2006 16:07:29 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: yuasa> I added the patch and tested it. It has same problem. Thank you. I realize the reason just now. VR41XX's PTE format is a bit different from others. I should use mk_pte() to wrap these difference. Could you try this patch? 64BIT_PHYS_ADDR + MIPS32_R1 part are not tested ;-) --- linux-mips/arch/mips/mm/init.c 2006-02-14 15:30:58.000000000 +0900 +++ linux/arch/mips/mm/init.c 2006-02-14 16:29:51.000000000 +0900 @@ -95,6 +95,7 @@ static inline void *kmap_coherent(struct unsigned long asid; unsigned int vpflags; unsigned int wired; + pte_t pte; if (!cpu_has_dc_aliases) return page_address(page); @@ -111,8 +112,13 @@ static inline void *kmap_coherent(struct wired = read_c0_wired(); write_c0_wired(wired + 1); write_c0_index(wired); - write_c0_entryhi(vaddr & ~0x1fffUL); - entrylo = (page_to_pfn(page) << 6) | (pgprot_val(PAGE_KERNEL) >> 6); + write_c0_entryhi(vaddr & (PAGE_MASK << 1)); + pte = mk_pte(page, PAGE_KERNEL); +#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32_R1) + entrylo = pte.pte_high; +#else + entrylo = pte_val(pte) >> 6; +#endif write_c0_entrylo0(entrylo); write_c0_entrylo1(entrylo); mtc0_tlbw_hazard(); ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 7:42 ` Atsushi Nemoto @ 2006-02-14 8:56 ` Yoichi Yuasa 2006-02-14 10:12 ` Atsushi Nemoto 0 siblings, 1 reply; 19+ messages in thread From: Yoichi Yuasa @ 2006-02-14 8:56 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: yoichi_yuasa, linux-mips, ralf On Tue, 14 Feb 2006 16:42:16 +0900 (JST) Atsushi Nemoto <anemo@mba.ocn.ne.jp> wrote: > >>>>> On Tue, 14 Feb 2006 16:07:29 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: > yuasa> I added the patch and tested it. It has same problem. > > Thank you. I realize the reason just now. VR41XX's PTE format is a > bit different from others. I should use mk_pte() to wrap these > difference. > > Could you try this patch? 64BIT_PHYS_ADDR + MIPS32_R1 part are not > tested ;-) This patch fixed the boot problem, but the kernel still has cache coherency problem. ~# ./cachetest Test separation: 4096 bytes: FAIL - cache not coherent Test separation: 8192 bytes: pass Test separation: 16384 bytes: pass Test separation: 32768 bytes: pass Test separation: 65536 bytes: pass Test separation: 131072 bytes: pass Test separation: 262144 bytes: pass Test separation: 524288 bytes: pass Test separation: 1048576 bytes: pass Test separation: 2097152 bytes: pass Test separation: 4194304 bytes: pass Test separation: 8388608 bytes: pass Test separation: 16777216 bytes: pass VM page alias coherency test: minimum fast spacing: 8192 (2 pages) I'm using the following test program. http://lkml.org/lkml/2003/8/29/6 Yoichi ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 8:56 ` Yoichi Yuasa @ 2006-02-14 10:12 ` Atsushi Nemoto 2006-02-14 13:14 ` Atsushi Nemoto 2006-02-14 13:48 ` Kevin D. Kissell 0 siblings, 2 replies; 19+ messages in thread From: Atsushi Nemoto @ 2006-02-14 10:12 UTC (permalink / raw) To: yoichi_yuasa; +Cc: anemo, linux-mips, ralf >>>>> On Tue, 14 Feb 2006 17:56:57 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: yuasa> This patch fixed the boot problem, but the kernel still has yuasa> cache coherency problem. yuasa> ~# ./cachetest yuasa> Test separation: 4096 bytes: FAIL - cache not coherent Thank you for testing. As for the cachetest program, I think the test program is wrong. It try to mmap offset 0 of a shared file to odd address page with MAP_FIXED. It means "I want non-coherent mapping if dcache alias exists". Currently the kernel surely gives what the program want. The kernel might have to return EINVAL in such case, but I'm not sure which is the right behavior. Please look at David S. Miller's comments, for example, http://lkml.org/lkml/2003/9/1/48 --- Atsushi Nemoto ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 10:12 ` Atsushi Nemoto @ 2006-02-14 13:14 ` Atsushi Nemoto 2006-02-14 13:48 ` Kevin D. Kissell 1 sibling, 0 replies; 19+ messages in thread From: Atsushi Nemoto @ 2006-02-14 13:14 UTC (permalink / raw) To: ralf; +Cc: linux-mips Revised. There are several cache-related problems with multi-threaded program and fork(). Problem-1: copy-on-write and signal trampoline on harvard-cache. [previously reported with subject: missing data cache flush for signal trampoline on fork] A program which is heavily using signal and fork occasionally killed by SIGSEGV, etc. When it was killed, PC is always near the stack pointer. This would happen on CPUs without MIPS_CACHE_IC_F_DC. D-cache aliasing is irrelevant. 1. To handle a delivered signal, a signal-trampoline code are written to the stack page. 2. They are flushed to memory immediately and I-cache are invalidated. 3. If other thread called fork() before the signal handler is executed, all writable pages (including the stack page) are marked as COW page. 4. When the user signal handler is to write to the stack, the page will be copied to new physical page by copy_user_highpage(), but not flushed to main memory. 5. Then flush_cache_page() is called for the stack page, but it does not flush the cache written by copy_user_highpage() because new PTE is not established yet. 6. When returned from the user signal handler, the signal trampoline code might not be written to main memory. Garbage code will be executed and the program will die. Problem-2: copy-on-write and dcache-aliasing [previously reported with subject: dcache aliasing problem on fork] 1. Now there is a process containing two thread (T1 and T2). The thread T1 calls fork(). Then dup_mmap() function called on T1 context. static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) ... flush_cache_mm(current->mm); ... /* A */ (write-protect all Copy-On-Write pages) ... /* B */ flush_tlb_mm(current->mm); ... 2. When preemption happens between A and B (or on SMP kernel), the thread T2 can run and modify data on COW pages without page fault (modified data will stay in cache). 3. Some time after fork() completed, the thread T2 may cause a page fault by write-protect on a COW page. 4. Then data of the COW page will be copied to newly allocated physical page (copy_cow_page()). It reads data via kernel mapping. The kernel mapping can have different 'color' with user space mapping of the thread T2 (dcache aliasing). Therefore copy_cow_page() will copy stale data. Then the modified data in cache will be lost. This patch fixes above problems using custom copy_user_highpage(). It uses kmap_coherent() to map an user page for kernel with same color. Also copy_to_user_page() and copy_from_user_page() are rewritten using the kmap_coherent() to avoid extra cache flushing. The main part of this patch was originally written by Ralf Baechle. arch/mips/mm/init.c | 138 ++++++++++++++++++++++++++++++++++++++++++ include/asm-mips/cacheflush.h | 14 ++-- include/asm-mips/fixmap.h | 8 +- include/asm-mips/page.h | 14 +--- include/linux/highmem.h | 7 +- mm/memory.c | 8 +- 6 files changed, 166 insertions(+), 23 deletions(-) Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 0ff9a34..dee0b2b 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -30,11 +30,15 @@ #include <asm/cachectl.h> #include <asm/cpu.h> #include <asm/dma.h> +#include <asm/interrupt.h> +#include <asm/kmap_types.h> +#include <asm/mipsmtregs.h> #include <asm/mmu_context.h> #include <asm/sections.h> #include <asm/pgtable.h> #include <asm/pgalloc.h> #include <asm/tlb.h> +#include <asm/fixmap.h> DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); @@ -79,6 +83,140 @@ unsigned long setup_zero_pages(void) return 1UL << order; } +/* + * These are almost like kmap_atomic / kunmap_atmic except they take an + * additional address argument as the hint. + */ + +static inline void *kmap_coherent(struct page *page, unsigned long addr) +{ + unsigned long vaddr, flags, entrylo; + enum fixed_addresses idx; + unsigned long asid; + unsigned int vpflags; + unsigned int wired; + pte_t pte; + + if (!cpu_has_dc_aliases) + return page_address(page); + inc_preempt_count(); + + if (cpu_has_mipsmt) + vpflags = dvpe(); + local_irq_save(flags); + + idx = (addr >> 12) & 7; + vaddr = __fix_to_virt(FIX_CMAP_END - idx); + + asid = read_c0_entryhi(); + wired = read_c0_wired(); + write_c0_wired(wired + 1); + write_c0_index(wired); + write_c0_entryhi(vaddr & (PAGE_MASK << 1)); + pte = mk_pte(page, PAGE_KERNEL); +#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32_R1) + entrylo = pte.pte_high; +#else + entrylo = pte_val(pte) >> 6; +#endif + write_c0_entrylo0(entrylo); + write_c0_entrylo1(entrylo); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + write_c0_entryhi(asid); + + local_irq_restore(flags); + if (cpu_has_mipsmt) + evpe(vpflags); + + return (void*) vaddr; +} + +#define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1))) + +static inline void kunmap_coherent(void) +{ + unsigned int vpflags, wired; + unsigned long flags, asid; + + if (!cpu_has_dc_aliases) + return; + if (cpu_has_mipsmt) + vpflags = dvpe(); + local_irq_save(flags); + + asid = read_c0_entryhi(); + wired = read_c0_wired() - 1; + write_c0_wired(wired); + write_c0_index(wired); + write_c0_entryhi(UNIQUE_ENTRYHI(wired)); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + write_c0_entryhi(asid); + + local_irq_restore(flags); + if (cpu_has_mipsmt) + evpe(vpflags); + + dec_preempt_count(); + preempt_check_resched(); +} + +void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + char *vfrom, *vto; + /* + * Map 'from' page to same color with vaddr and map 'to' page + * to kernel, and flush 'to' page if needed. + * Just using kmap_coherent for both 'from' and 'to' (and no + * flushing) is not enough because: + * 1. The signal trampoline code should be flushed to main memory. + * 2. The page 'to' might have been cached via kernel mapping. + */ + vfrom = kmap_coherent(from, vaddr); + vto = kmap_atomic(to, KM_USER1); + copy_page(vto, vfrom); + if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) || + (cpu_has_dc_aliases && pages_do_alias((unsigned long)vto, vaddr))) + flush_data_cache_page((unsigned long)vto); + kunmap_atomic(vto, KM_USER1); + kunmap_coherent(); + /* Make sure this page is cleared on other CPU's too before using it */ + smp_wmb(); +} + +EXPORT_SYMBOL(copy_user_highpage); + +void __copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, const void *src, + unsigned long len) +{ + char *vto; + + vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(vto, src, len); + kunmap_coherent(); + flush_icache_page(vma, page); +} + +EXPORT_SYMBOL(__copy_to_user_page); + +void __copy_from_user_page(struct page *page, unsigned long vaddr, + void *dst, unsigned long len) +{ + char *vfrom; + + vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(dst, vfrom, len); + kunmap_coherent(); +} + +EXPORT_SYMBOL(__copy_from_user_page); + + #ifdef CONFIG_HIGHMEM pte_t *kmap_pte; pgprot_t kmap_prot; diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index aeae9fa..7e4c30d 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -53,23 +53,23 @@ extern void (*flush_icache_range)(unsign #define flush_cache_vmap(start, end) flush_cache_all() #define flush_cache_vunmap(start, end) flush_cache_all() +extern void __copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, const void *src, + unsigned long len); static inline void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); - flush_icache_page(vma, page); + __copy_to_user_page(vma, page, vaddr, src, len); } +extern void __copy_from_user_page(struct page *page, unsigned long vaddr, + void *dst, unsigned long len); static inline void copy_from_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); + __copy_from_user_page(page, vaddr, dst, len); } extern void (*flush_cache_sigtramp)(unsigned long addr); diff --git a/include/asm-mips/fixmap.h b/include/asm-mips/fixmap.h index 73a3028..3dc8af6 100644 --- a/include/asm-mips/fixmap.h +++ b/include/asm-mips/fixmap.h @@ -46,8 +46,12 @@ * fix-mapped? */ enum fixed_addresses { +#define FIX_N_COLOURS 8 + FIX_CMAP_BEGIN, + FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS, #ifdef CONFIG_HIGHMEM - FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ + /* reserved pte's for temporary kernel mappings */ + FIX_KMAP_BEGIN = FIX_CMAP_END + 1, FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #endif __end_of_fixed_addresses @@ -70,7 +74,7 @@ extern void __set_fixmap (enum fixed_add * the start of the fixmap, and leave one page empty * at the top of mem.. */ -#define FIXADDR_TOP (0xffffe000UL) +#define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000) #define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index ee25a77..a896495 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -62,15 +62,11 @@ static inline void clear_user_page(void flush_data_cache_page((unsigned long)addr); } -static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, - struct page *to) -{ - extern void (*flush_data_cache_page)(unsigned long addr); - - copy_page(vto, vfrom); - if (pages_do_alias((unsigned long)vto, vaddr)) - flush_data_cache_page((unsigned long)vto); -} +struct vm_area_struct; +extern void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma); + +#define __HAVE_ARCH_COPY_USER_HIGHPAGE /* * These are used to make use of C type-checking.. diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 6bece92..768914a 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -78,7 +78,10 @@ static inline void memclear_highpage_flu kunmap_atomic(kaddr, KM_USER0); } -static inline void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr) +#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE + +static inline void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) { char *vfrom, *vto; @@ -91,6 +94,8 @@ static inline void copy_user_highpage(st smp_wmb(); } +#endif + static inline void copy_highpage(struct page *to, struct page *from) { char *vfrom, *vto; diff --git a/mm/memory.c b/mm/memory.c index 2bee1f2..653b08f 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1386,7 +1386,7 @@ static inline pte_t maybe_mkwrite(pte_t return pte; } -static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va) +static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va, struct vm_area_struct *vma) { /* * If the source page was a PFN mapping, we don't have @@ -1410,7 +1410,7 @@ static inline void cow_user_page(struct return; } - copy_user_highpage(dst, src, va); + copy_user_highpage(dst, src, va, vma); } /* @@ -1475,7 +1475,7 @@ gotten: new_page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!new_page) goto oom; - cow_user_page(new_page, old_page, address); + cow_user_page(new_page, old_page, address, vma); } /* @@ -2074,7 +2074,7 @@ retry: page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!page) goto oom; - copy_user_highpage(page, new_page, address); + copy_user_highpage(page, new_page, address, vma); page_cache_release(new_page); new_page = page; anon = 1; ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-14 10:12 ` Atsushi Nemoto 2006-02-14 13:14 ` Atsushi Nemoto @ 2006-02-14 13:48 ` Kevin D. Kissell 1 sibling, 0 replies; 19+ messages in thread From: Kevin D. Kissell @ 2006-02-14 13:48 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: yoichi_yuasa, linux-mips, ralf Atsushi Nemoto wrote: >>>>>> On Tue, 14 Feb 2006 17:56:57 +0900, Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> said: > yuasa> This patch fixed the boot problem, but the kernel still has > yuasa> cache coherency problem. > > yuasa> ~# ./cachetest > yuasa> Test separation: 4096 bytes: FAIL - cache not coherent > > Thank you for testing. > > As for the cachetest program, I think the test program is wrong. > > It try to mmap offset 0 of a shared file to odd address page with > MAP_FIXED. It means "I want non-coherent mapping if dcache alias > exists". Currently the kernel surely gives what the program want. > > The kernel might have to return EINVAL in such case, but I'm not sure > which is the right behavior. Please look at David S. Miller's > comments, for example, http://lkml.org/lkml/2003/9/1/48 I've been maintaining for years that EINVAL is the correct thing to do when the kernel detects that a user is requesting a mapping that the kernel knows will create aliasing problems, but this ran counter to the "serves the user right" philosophy that seemed to dominate the Linux kernel community. I'm pleased to see that the tide seems to be shifting. As an old-time BSD/SysV hacker from the 1980s, I view system calls as a contract with the user: if the kernel says it's OK, it's supposed to work, period. But I better get down off my soapbox before I launch into my rant about the OOM killer being proof of a failed paradigm... ;o) Regards, Kevin K. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-02-13 16:15 [PATCH] fix cache coherency issues Atsushi Nemoto 2006-02-14 1:59 ` Yoichi Yuasa @ 2006-05-22 15:34 ` Atsushi Nemoto 2006-08-23 15:31 ` Atsushi Nemoto 1 sibling, 1 reply; 19+ messages in thread From: Atsushi Nemoto @ 2006-05-22 15:34 UTC (permalink / raw) To: linux-mips; +Cc: ralf On Tue, 14 Feb 2006 01:15:08 +0900 (JST), Atsushi Nemoto <anemo@mba.ocn.ne.jp> wrote: > There are several cache-related problems with multi-threaded program > and fork(). > ... > This patch fixes above problems using custom copy_user_highpage(). It > uses kmap_coherent() to map an user page for kernel with same color. > Also copy_to_user_page() and copy_from_user_page() are rewritten using > the kmap_coherent() to avoid extra cache flushing. I found a hole in the rewrite of copy_to_user_page. flush_icache_page does not flush data cache via user mapping. Revised. There are several cache-related problems with multi-threaded program and fork(). Problem-1: copy-on-write and signal trampoline on harvard-cache. [previously reported with subject: missing data cache flush for signal trampoline on fork] A program which is heavily using signal and fork occasionally killed by SIGSEGV, etc. When it was killed, PC is always near the stack pointer. This would happen on CPUs without MIPS_CACHE_IC_F_DC. D-cache aliasing is irrelevant. 1. To handle a delivered signal, a signal-trampoline code are written to the stack page. 2. They are flushed to memory immediately and I-cache are invalidated. 3. If other thread called fork() before the signal handler is executed, all writable pages (including the stack page) are marked as COW page. 4. When the user signal handler is to write to the stack, the page will be copied to new physical page by copy_user_highpage(), but not flushed to main memory. 5. Then flush_cache_page() is called for the stack page, but it does not flush the cache written by copy_user_highpage() because new PTE is not established yet. 6. When returned from the user signal handler, the signal trampoline code might not be written to main memory. Garbage code will be executed and the program will die. Problem-2: copy-on-write and dcache-aliasing [previously reported with subject: dcache aliasing problem on fork] 1. Now there is a process containing two thread (T1 and T2). The thread T1 calls fork(). Then dup_mmap() function called on T1 context. static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) ... flush_cache_mm(current->mm); ... /* A */ (write-protect all Copy-On-Write pages) ... /* B */ flush_tlb_mm(current->mm); ... 2. When preemption happens between A and B (or on SMP kernel), the thread T2 can run and modify data on COW pages without page fault (modified data will stay in cache). 3. Some time after fork() completed, the thread T2 may cause a page fault by write-protect on a COW page. 4. Then data of the COW page will be copied to newly allocated physical page (copy_cow_page()). It reads data via kernel mapping. The kernel mapping can have different 'color' with user space mapping of the thread T2 (dcache aliasing). Therefore copy_cow_page() will copy stale data. Then the modified data in cache will be lost. This patch fixes above problems using custom copy_user_highpage(). It uses kmap_coherent() to map an user page for kernel with same color. Also copy_to_user_page() and copy_from_user_page() are rewritten using the kmap_coherent() to avoid extra cache flushing. The main part of this patch was originally written by Ralf Baechle. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> arch/mips/mm/init.c | 133 ++++++++++++++++++++++++++++++++++++++++++ include/asm-mips/cacheflush.h | 14 ++-- include/asm-mips/fixmap.h | 8 +- include/asm-mips/page.h | 14 +--- include/linux/highmem.h | 7 +- mm/memory.c | 8 +- 6 files changed, 161 insertions(+), 23 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index c22308b..01811bb 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -31,11 +31,15 @@ #include <asm/cachectl.h> #include <asm/cpu.h> #include <asm/dma.h> +#include <asm/interrupt.h> +#include <asm/kmap_types.h> +#include <asm/mipsmtregs.h> #include <asm/mmu_context.h> #include <asm/sections.h> #include <asm/pgtable.h> #include <asm/pgalloc.h> #include <asm/tlb.h> +#include <asm/fixmap.h> DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); @@ -81,6 +85,135 @@ unsigned long setup_zero_pages(void) return 1UL << order; } +/* + * These are almost like kmap_atomic / kunmap_atmic except they take an + * additional address argument as the hint. + */ + +static inline void *kmap_coherent(struct page *page, unsigned long addr) +{ + unsigned long vaddr, flags, entrylo; + enum fixed_addresses idx; + unsigned long asid; + unsigned int vpflags; + unsigned int wired; + + if (!cpu_has_dc_aliases) + return page_address(page); + inc_preempt_count(); + + if (cpu_has_mipsmt) + vpflags = dvpe(); + local_irq_save(flags); + + idx = (addr >> 12) & 7; + vaddr = __fix_to_virt(FIX_CMAP_END - idx); + + asid = read_c0_entryhi(); + wired = read_c0_wired(); + write_c0_wired(wired + 1); + write_c0_index(wired); + write_c0_entryhi(vaddr & ~0x1fffUL); + entrylo = (page_to_pfn(page) << 6) | (pgprot_val(PAGE_KERNEL) >> 6); + write_c0_entrylo0(entrylo); + write_c0_entrylo1(entrylo); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + write_c0_entryhi(asid); + + local_irq_restore(flags); + if (cpu_has_mipsmt) + evpe(vpflags); + + return (void*) vaddr; +} + +#define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1))) + +static inline void kunmap_coherent(void) +{ + unsigned int vpflags, wired; + unsigned long flags, asid; + + if (!cpu_has_dc_aliases) + return; + if (cpu_has_mipsmt) + vpflags = dvpe(); + local_irq_save(flags); + + asid = read_c0_entryhi(); + wired = read_c0_wired() - 1; + write_c0_wired(wired); + write_c0_index(wired); + write_c0_entryhi(UNIQUE_ENTRYHI(wired)); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + write_c0_entryhi(asid); + + local_irq_restore(flags); + if (cpu_has_mipsmt) + evpe(vpflags); + + dec_preempt_count(); + preempt_check_resched(); +} + +void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + char *vfrom, *vto; + /* + * Map 'from' page to same color with vaddr and map 'to' page + * to kernel, and flush 'to' page if needed. + * Just using kmap_coherent for both 'from' and 'to' (and no + * flushing) is not enough because: + * 1. The signal trampoline code should be flushed to main memory. + * 2. The page 'to' might have been cached via kernel mapping. + */ + vfrom = kmap_coherent(from, vaddr); + vto = kmap_atomic(to, KM_USER1); + copy_page(vto, vfrom); + if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) || + (cpu_has_dc_aliases && pages_do_alias((unsigned long)vto, vaddr))) + flush_data_cache_page((unsigned long)vto); + kunmap_atomic(vto, KM_USER1); + kunmap_coherent(); + /* Make sure this page is cleared on other CPU's too before using it */ + smp_wmb(); +} + +EXPORT_SYMBOL(copy_user_highpage); + +void __copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, const void *src, + unsigned long len) +{ + char *vto; + + vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(vto, src, len); + kunmap_coherent(); + if (vma->vm_flags & VM_EXEC) + flush_cache_page(vma, vaddr, page_to_pfn(page)); +} + +EXPORT_SYMBOL(__copy_to_user_page); + +void __copy_from_user_page(struct page *page, unsigned long vaddr, + void *dst, unsigned long len) +{ + char *vfrom; + + vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(dst, vfrom, len); + kunmap_coherent(); +} + +EXPORT_SYMBOL(__copy_from_user_page); + + #ifdef CONFIG_HIGHMEM pte_t *kmap_pte; pgprot_t kmap_prot; diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index 47bc8f6..ed1e255 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -53,23 +53,23 @@ extern void (*flush_icache_range)(unsign #define flush_cache_vmap(start, end) flush_cache_all() #define flush_cache_vunmap(start, end) flush_cache_all() +extern void __copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, const void *src, + unsigned long len); static inline void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); - flush_icache_page(vma, page); + __copy_to_user_page(vma, page, vaddr, src, len); } +extern void __copy_from_user_page(struct page *page, unsigned long vaddr, + void *dst, unsigned long len); static inline void copy_from_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); + __copy_from_user_page(page, vaddr, dst, len); } extern void (*flush_cache_sigtramp)(unsigned long addr); diff --git a/include/asm-mips/fixmap.h b/include/asm-mips/fixmap.h index 73a3028..3dc8af6 100644 --- a/include/asm-mips/fixmap.h +++ b/include/asm-mips/fixmap.h @@ -46,8 +46,12 @@ * fix-mapped? */ enum fixed_addresses { +#define FIX_N_COLOURS 8 + FIX_CMAP_BEGIN, + FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS, #ifdef CONFIG_HIGHMEM - FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ + /* reserved pte's for temporary kernel mappings */ + FIX_KMAP_BEGIN = FIX_CMAP_END + 1, FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #endif __end_of_fixed_addresses @@ -70,7 +74,7 @@ extern void __set_fixmap (enum fixed_add * the start of the fixmap, and leave one page empty * at the top of mem.. */ -#define FIXADDR_TOP (0xffffe000UL) +#define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000) #define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index a1eab13..b88bb7a 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -62,15 +62,11 @@ static inline void clear_user_page(void flush_data_cache_page((unsigned long)addr); } -static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, - struct page *to) -{ - extern void (*flush_data_cache_page)(unsigned long addr); - - copy_page(vto, vfrom); - if (pages_do_alias((unsigned long)vto, vaddr)) - flush_data_cache_page((unsigned long)vto); -} +struct vm_area_struct; +extern void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma); + +#define __HAVE_ARCH_COPY_USER_HIGHPAGE /* * These are used to make use of C type-checking.. diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 892c4ea..26e49a1 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -90,7 +90,10 @@ static inline void memclear_highpage_flu kunmap_atomic(kaddr, KM_USER0); } -static inline void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr) +#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE + +static inline void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) { char *vfrom, *vto; @@ -103,6 +106,8 @@ static inline void copy_user_highpage(st smp_wmb(); } +#endif + static inline void copy_highpage(struct page *to, struct page *from) { char *vfrom, *vto; diff --git a/mm/memory.c b/mm/memory.c index 0ec7bc6..b38e1b0 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1394,7 +1394,7 @@ static inline pte_t maybe_mkwrite(pte_t return pte; } -static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va) +static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va, struct vm_area_struct *vma) { /* * If the source page was a PFN mapping, we don't have @@ -1418,7 +1418,7 @@ static inline void cow_user_page(struct return; } - copy_user_highpage(dst, src, va); + copy_user_highpage(dst, src, va, vma); } /* @@ -1483,7 +1483,7 @@ gotten: new_page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!new_page) goto oom; - cow_user_page(new_page, old_page, address); + cow_user_page(new_page, old_page, address, vma); } /* @@ -2082,7 +2082,7 @@ retry: page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!page) goto oom; - copy_user_highpage(page, new_page, address); + copy_user_highpage(page, new_page, address, vma); page_cache_release(new_page); new_page = page; anon = 1; ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-05-22 15:34 ` Atsushi Nemoto @ 2006-08-23 15:31 ` Atsushi Nemoto 2006-08-23 16:52 ` Nigel Stephens 0 siblings, 1 reply; 19+ messages in thread From: Atsushi Nemoto @ 2006-08-23 15:31 UTC (permalink / raw) To: linux-mips; +Cc: ralf Revised again (and again...). With recent commits to git, the "copy-on-write and signal trampoline on harvard-cache" issue have been solved. Now this patch solves dcache aliasing issue and potentially reduces dcache flushing on harvard-cache. Problem: copy-on-write and dcache-aliasing [previously reported with subject: dcache aliasing problem on fork] 1. Now there is a process containing two thread (T1 and T2). The thread T1 calls fork(). Then dup_mmap() function called on T1 context. static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) ... flush_cache_mm(current->mm); ... /* A */ (write-protect all Copy-On-Write pages) ... /* B */ flush_tlb_mm(current->mm); ... 2. When preemption happens between A and B (or on SMP kernel), the thread T2 can run and modify data on COW pages without page fault (modified data will stay in cache). 3. Some time after fork() completed, the thread T2 may cause a page fault by write-protect on a COW page. 4. Then data of the COW page will be copied to newly allocated physical page (copy_cow_page()). It reads data via kernel mapping. The kernel mapping can have different 'color' with user space mapping of the thread T2 (dcache aliasing). Therefore copy_cow_page() will copy stale data. Then the modified data in cache will be lost. This patch fixes above problems using custom copy_user_highpage(). It uses kmap_coherent() to map an user page for kernel with same color. Also copy_to_user_page() and copy_from_user_page() are rewritten using the kmap_coherent() to avoid extra cache flushing. To make copy_user_highpage() more effective, the argument "vma" is added to the function and cow_user_page(). The main part of this patch was originally written by Ralf Baechle. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> arch/mips/mm/init.c | 213 ++++++++++++++++++++++++++++++++++++++++-- arch/mips/mm/pgtable-32.c | 7 - arch/mips/mm/pgtable-64.c | 11 ++ include/asm-mips/cacheflush.h | 19 --- include/asm-mips/fixmap.h | 14 ++ include/asm-mips/page.h | 17 +-- include/linux/highmem.h | 7 + mm/memory.c | 8 - 8 files changed, 252 insertions(+), 44 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index c52497b..2cfdc0b 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -30,11 +30,39 @@ #include <asm/bootinfo.h> #include <asm/cachectl.h> #include <asm/cpu.h> #include <asm/dma.h> +#include <asm/kmap_types.h> #include <asm/mmu_context.h> #include <asm/sections.h> #include <asm/pgtable.h> #include <asm/pgalloc.h> #include <asm/tlb.h> +#include <asm/fixmap.h> + +/* CP0 hazard avoidance. */ +#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ + "nop; nop; nop; nop; nop; nop;\n\t" \ + ".set reorder\n\t") + +/* Atomicity and interruptability */ +#ifdef CONFIG_MIPS_MT_SMTC + +#include <asm/mipsmtregs.h> + +#define ENTER_CRITICAL(flags) \ + { \ + unsigned int mvpflags; \ + local_irq_save(flags);\ + mvpflags = dvpe() +#define EXIT_CRITICAL(flags) \ + evpe(mvpflags); \ + local_irq_restore(flags); \ + } +#else + +#define ENTER_CRITICAL(flags) local_irq_save(flags) +#define EXIT_CRITICAL(flags) local_irq_restore(flags) + +#endif /* CONFIG_MIPS_MT_SMTC */ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); @@ -80,13 +108,183 @@ unsigned long setup_zero_pages(void) return 1UL << order; } -#ifdef CONFIG_HIGHMEM -pte_t *kmap_pte; -pgprot_t kmap_prot; +/* + * These are almost like kmap_atomic / kunmap_atmic except they take an + * additional address argument as the hint. + */ #define kmap_get_fixmap_pte(vaddr) \ pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)), (vaddr)) +#ifdef CONFIG_MIPS_MT_SMTC +static pte_t *kmap_coherent_pte; +static void __init kmap_coherent_init(void) +{ + unsigned long vaddr; + + /* cache the first coherent kmap pte */ + vaddr = __fix_to_virt(FIX_CMAP_BEGIN); + kmap_coherent_pte = kmap_get_fixmap_pte(vaddr); +} +#else +static inline void kmap_coherent_init(void) {} +#endif + +static inline void *kmap_coherent(struct page *page, unsigned long addr) +{ + enum fixed_addresses idx; + unsigned long vaddr, flags, entrylo; + unsigned long old_ctx; + pte_t pte; + unsigned int tlbidx; + + inc_preempt_count(); + idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1); +#ifdef CONFIG_MIPS_MT_SMTC + idx += FIX_N_COLOURS * smp_processor_id(); +#endif + vaddr = __fix_to_virt(FIX_CMAP_END - idx); + pte = mk_pte(page, PAGE_KERNEL); +#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32_R1) + entrylo = pte.pte_high; +#else + entrylo = pte_val(pte) >> 6; +#endif + + ENTER_CRITICAL(flags); + old_ctx = read_c0_entryhi(); + write_c0_entryhi(vaddr & (PAGE_MASK << 1)); + write_c0_entrylo0(entrylo); + write_c0_entrylo1(entrylo); +#ifdef CONFIG_MIPS_MT_SMTC + set_pte(kmap_coherent_pte - (FIX_CMAP_END - idx), pte); + /* preload TLB instead of local_flush_tlb_one() */ + mtc0_tlbw_hazard(); + tlb_probe(); + BARRIER; + tlbidx = read_c0_index(); + mtc0_tlbw_hazard(); + if (tlbidx < 0) + tlb_write_random(); + else + tlb_write_indexed(); +#else + tlbidx = read_c0_wired(); + write_c0_wired(tlbidx + 1); + write_c0_index(tlbidx); + mtc0_tlbw_hazard(); + tlb_write_indexed(); +#endif + tlbw_use_hazard(); + write_c0_entryhi(old_ctx); + EXIT_CRITICAL(flags); + + return (void*) vaddr; +} + +#define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1))) + +static inline void kunmap_coherent(struct page *page) +{ +#ifndef CONFIG_MIPS_MT_SMTC + unsigned int wired; + unsigned long flags, old_ctx; + + ENTER_CRITICAL(flags); + old_ctx = read_c0_entryhi(); + wired = read_c0_wired() - 1; + write_c0_wired(wired); + write_c0_index(wired); + write_c0_entryhi(UNIQUE_ENTRYHI(wired)); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + write_c0_entryhi(old_ctx); + EXIT_CRITICAL(flags); +#endif + dec_preempt_count(); + preempt_check_resched(); +} + +void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + void *vfrom, *vto; + + vto = kmap_atomic(to, KM_USER1); + if (cpu_has_dc_aliases) { + vfrom = kmap_coherent(from, vaddr); + copy_page(vto, vfrom); + kunmap_coherent(from); + } else { + vfrom = kmap_atomic(from, KM_USER0); + copy_page(vto, vfrom); + kunmap_atomic(vfrom, KM_USER0); + } + if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) || + pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) + flush_data_cache_page((unsigned long)vto); + kunmap_atomic(vto, KM_USER1); + /* Make sure this page is cleared on other CPU's too before using it */ + smp_wmb(); +} + +EXPORT_SYMBOL(copy_user_highpage); + +void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *to) +{ + if (cpu_has_dc_aliases) { + struct page *from = virt_to_page(vfrom); + vfrom = kmap_coherent(from, vaddr); + copy_page(vto, vfrom); + kunmap_coherent(from); + } else + copy_page(vto, vfrom); + if (!cpu_has_ic_fills_f_dc || + pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) + flush_data_cache_page((unsigned long)vto); +} + +EXPORT_SYMBOL(copy_user_page); + +void copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) +{ + if (cpu_has_dc_aliases) { + void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(vto, src, len); + kunmap_coherent(page); + } else + memcpy(dst, src, len); + if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) + flush_cache_page(vma, vaddr, page_to_pfn(page)); +} + +EXPORT_SYMBOL(copy_to_user_page); + +void copy_from_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) +{ + if (cpu_has_dc_aliases) { + void *vfrom = + kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(dst, vfrom, len); + kunmap_coherent(page); + } else + memcpy(dst, src, len); +} + +EXPORT_SYMBOL(copy_from_user_page); + + +#ifdef CONFIG_HIGHMEM +pte_t *kmap_pte; +pgprot_t kmap_prot; + static void __init kmap_init(void) { unsigned long kmap_vstart; @@ -97,11 +295,12 @@ static void __init kmap_init(void) kmap_prot = PAGE_KERNEL; } +#endif /* CONFIG_HIGHMEM */ -#ifdef CONFIG_32BIT void __init fixrange_init(unsigned long start, unsigned long end, pgd_t *pgd_base) { +#if defined(CONFIG_HIGHMEM) || defined(CONFIG_MIPS_MT_SMTC) pgd_t *pgd; pud_t *pud; pmd_t *pmd; @@ -122,7 +321,7 @@ void __init fixrange_init(unsigned long for (; (k < PTRS_PER_PMD) && (vaddr != end); pmd++, k++) { if (pmd_none(*pmd)) { pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); - set_pmd(pmd, __pmd(pte)); + set_pmd(pmd, __pmd((unsigned long)pte)); if (pte != pte_offset_kernel(pmd, 0)) BUG(); } @@ -132,9 +331,8 @@ void __init fixrange_init(unsigned long } j = 0; } +#endif } -#endif /* CONFIG_32BIT */ -#endif /* CONFIG_HIGHMEM */ #ifndef CONFIG_NEED_MULTIPLE_NODES extern void pagetable_init(void); @@ -175,6 +373,7 @@ #endif #ifdef CONFIG_HIGHMEM kmap_init(); #endif + kmap_coherent_init(); max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; low = max_low_pfn; diff --git a/arch/mips/mm/pgtable-32.c b/arch/mips/mm/pgtable-32.c index 4bdaa05..4a61e62 100644 --- a/arch/mips/mm/pgtable-32.c +++ b/arch/mips/mm/pgtable-32.c @@ -31,9 +31,10 @@ void pgd_init(unsigned long page) void __init pagetable_init(void) { -#ifdef CONFIG_HIGHMEM unsigned long vaddr; - pgd_t *pgd, *pgd_base; + pgd_t *pgd_base; +#ifdef CONFIG_HIGHMEM + pgd_t *pgd; pud_t *pud; pmd_t *pmd; pte_t *pte; @@ -44,7 +45,6 @@ #endif pgd_init((unsigned long)swapper_pg_dir + sizeof(pgd_t) * USER_PTRS_PER_PGD); -#ifdef CONFIG_HIGHMEM pgd_base = swapper_pg_dir; /* @@ -53,6 +53,7 @@ #ifdef CONFIG_HIGHMEM vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; fixrange_init(vaddr, 0, pgd_base); +#ifdef CONFIG_HIGHMEM /* * Permanent kmaps: */ diff --git a/arch/mips/mm/pgtable-64.c b/arch/mips/mm/pgtable-64.c index 44b5e97..8d600d3 100644 --- a/arch/mips/mm/pgtable-64.c +++ b/arch/mips/mm/pgtable-64.c @@ -8,6 +8,7 @@ */ #include <linux/init.h> #include <linux/mm.h> +#include <asm/fixmap.h> #include <asm/pgtable.h> void pgd_init(unsigned long page) @@ -52,7 +53,17 @@ void pmd_init(unsigned long addr, unsign void __init pagetable_init(void) { + unsigned long vaddr; + pgd_t *pgd_base; + /* Initialize the entire pgd. */ pgd_init((unsigned long)swapper_pg_dir); pmd_init((unsigned long)invalid_pmd_table, (unsigned long)invalid_pte_table); + + pgd_base = swapper_pg_dir; + /* + * Fixed mappings: + */ + vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; + fixrange_init(vaddr, 0, pgd_base); } diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index 36416fd..d10517c 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -57,24 +57,13 @@ extern void (*flush_icache_range)(unsign #define flush_cache_vmap(start, end) flush_cache_all() #define flush_cache_vunmap(start, end) flush_cache_all() -static inline void copy_to_user_page(struct vm_area_struct *vma, +extern void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, - unsigned long len) -{ - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); - __flush_icache_page(vma, page); -} + unsigned long len); -static inline void copy_from_user_page(struct vm_area_struct *vma, +extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, - unsigned long len) -{ - if (cpu_has_dc_aliases) - flush_cache_page(vma, vaddr, page_to_pfn(page)); - memcpy(dst, src, len); -} + unsigned long len); extern void (*flush_cache_sigtramp)(unsigned long addr); extern void (*flush_icache_all)(void); diff --git a/include/asm-mips/fixmap.h b/include/asm-mips/fixmap.h index 6959bdb..02c8a13 100644 --- a/include/asm-mips/fixmap.h +++ b/include/asm-mips/fixmap.h @@ -45,8 +45,16 @@ #endif * fix-mapped? */ enum fixed_addresses { +#define FIX_N_COLOURS 8 + FIX_CMAP_BEGIN, +#ifdef CONFIG_MIPS_MT_SMTC + FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * NR_CPUS), +#else + FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS, +#endif #ifdef CONFIG_HIGHMEM - FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ + /* reserved pte's for temporary kernel mappings */ + FIX_KMAP_BEGIN = FIX_CMAP_END + 1, FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #endif __end_of_fixed_addresses @@ -70,9 +78,9 @@ #define set_fixmap_nocache(idx, phys) \ * at the top of mem.. */ #if defined(CONFIG_CPU_TX39XX) || defined(CONFIG_CPU_TX49XX) -#define FIXADDR_TOP (0xff000000UL - 0x2000) +#define FIXADDR_TOP ((unsigned long)(long)(int)(0xff000000 - 0x20000)) #else -#define FIXADDR_TOP (0xffffe000UL) +#define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000) #endif #define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index b2849d8..9e3e410 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -38,8 +38,6 @@ #define PAGE_MASK (~((1 << PAGE_SH #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#include <asm/cpu-features.h> - extern void clear_page(void * page); extern void copy_page(void * to, void * from); @@ -63,16 +61,13 @@ static inline void clear_user_page(void flush_data_cache_page((unsigned long)addr); } -static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, - struct page *to) -{ - extern void (*flush_data_cache_page)(unsigned long addr); +extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *to); +struct vm_area_struct; +extern void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma); - copy_page(vto, vfrom); - if (!cpu_has_ic_fills_f_dc || - pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) - flush_data_cache_page((unsigned long)vto); -} +#define __HAVE_ARCH_COPY_USER_HIGHPAGE /* * These are used to make use of C type-checking.. diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 85ce7ef..aa4f32d 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -89,7 +89,10 @@ static inline void memclear_highpage_flu kunmap_atomic(kaddr, KM_USER0); } -static inline void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr) +#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE + +static inline void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) { char *vfrom, *vto; @@ -102,6 +105,8 @@ static inline void copy_user_highpage(st smp_wmb(); } +#endif + static inline void copy_highpage(struct page *to, struct page *from) { char *vfrom, *vto; diff --git a/mm/memory.c b/mm/memory.c index 109e986..edefcfc 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1407,7 +1407,7 @@ static inline pte_t maybe_mkwrite(pte_t return pte; } -static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va) +static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va, struct vm_area_struct *vma) { /* * If the source page was a PFN mapping, we don't have @@ -1431,7 +1431,7 @@ static inline void cow_user_page(struct return; } - copy_user_highpage(dst, src, va); + copy_user_highpage(dst, src, va, vma); } /* @@ -1531,7 +1531,7 @@ gotten: new_page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!new_page) goto oom; - cow_user_page(new_page, old_page, address); + cow_user_page(new_page, old_page, address, vma); } /* @@ -2135,7 +2135,7 @@ retry: page = alloc_page_vma(GFP_HIGHUSER, vma, address); if (!page) goto oom; - copy_user_highpage(page, new_page, address); + copy_user_highpage(page, new_page, address, vma); page_cache_release(new_page); new_page = page; anon = 1; ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-08-23 15:31 ` Atsushi Nemoto @ 2006-08-23 16:52 ` Nigel Stephens 2006-08-24 1:15 ` Atsushi Nemoto 0 siblings, 1 reply; 19+ messages in thread From: Nigel Stephens @ 2006-08-23 16:52 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: linux-mips, ralf Atsushi Nemoto wrote: > > + unsigned int tlbidx; > > ... > + if (tlbidx < 0) > Doesn't tlbidx need to be declared as a signed int, else the compiler could optimize away this comparison. Nigel ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-08-23 16:52 ` Nigel Stephens @ 2006-08-24 1:15 ` Atsushi Nemoto 2006-08-24 11:15 ` Ralf Baechle 0 siblings, 1 reply; 19+ messages in thread From: Atsushi Nemoto @ 2006-08-24 1:15 UTC (permalink / raw) To: nigel; +Cc: linux-mips, ralf On Wed, 23 Aug 2006 17:52:25 +0100, Nigel Stephens <nigel@mips.com> wrote: > Doesn't tlbidx need to be declared as a signed int, else the compiler > could optimize away this comparison. You are right. I'll fix it. Thanks. --- Atsushi Nemoto ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-08-24 1:15 ` Atsushi Nemoto @ 2006-08-24 11:15 ` Ralf Baechle 2006-08-24 11:38 ` Atsushi Nemoto 0 siblings, 1 reply; 19+ messages in thread From: Ralf Baechle @ 2006-08-24 11:15 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: nigel, linux-mips On Thu, Aug 24, 2006 at 10:15:31AM +0900, Atsushi Nemoto wrote: > On Wed, 23 Aug 2006 17:52:25 +0100, Nigel Stephens <nigel@mips.com> wrote: > > Doesn't tlbidx need to be declared as a signed int, else the compiler > > could optimize away this comparison. > > You are right. I'll fix it. Thanks. Your patch also still contains copy_user_page(). The only user of it used to be copy_user_highpage() so after our rewrite it can go away. I've already applied both fixes to my working version of the patch. Your patch only maps the source page. I'm trying to map the destination page also and I'm hitting a few issues with it. Ralf ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-08-24 11:15 ` Ralf Baechle @ 2006-08-24 11:38 ` Atsushi Nemoto 2006-08-24 12:12 ` Ralf Baechle 0 siblings, 1 reply; 19+ messages in thread From: Atsushi Nemoto @ 2006-08-24 11:38 UTC (permalink / raw) To: ralf; +Cc: nigel, linux-mips On Thu, 24 Aug 2006 12:15:15 +0100, Ralf Baechle <ralf@linux-mips.org> wrote: > Your patch also still contains copy_user_page(). The only user of it used > to be copy_user_highpage() so after our rewrite it can go away. I've > already applied both fixes to my working version of the patch. Yes, it is intentional. I keep copy_user_page() just because it is described in cachetlb.txt and exported. Of course we can remove it. I do not care :-) Also I wondered we should export copy_user_highpage() or not ... > Your patch only maps the source page. I'm trying to map the destination > page also and I'm hitting a few issues with it. If you wanted to map the destination, you must writeback the dcache via kernel mapping first. The dcache can contain dirty data for the page by previous usage. And if the page was executable, we must flush the destination page after copy_page() (via coherent mapping) anyway for I/D coherency. So now I think mapping the destination is not worth to do. --- Atsushi Nemoto ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-08-24 11:38 ` Atsushi Nemoto @ 2006-08-24 12:12 ` Ralf Baechle 2006-08-24 15:12 ` Atsushi Nemoto 0 siblings, 1 reply; 19+ messages in thread From: Ralf Baechle @ 2006-08-24 12:12 UTC (permalink / raw) To: Atsushi Nemoto; +Cc: nigel, linux-mips On Thu, Aug 24, 2006 at 08:38:38PM +0900, Atsushi Nemoto wrote: > Date: Thu, 24 Aug 2006 20:38:38 +0900 (JST) > To: ralf@linux-mips.org > Cc: nigel@mips.com, linux-mips@linux-mips.org > Subject: Re: [PATCH] fix cache coherency issues > From: Atsushi Nemoto <anemo@mba.ocn.ne.jp> > Content-Type: Text/Plain; charset=us-ascii > > On Thu, 24 Aug 2006 12:15:15 +0100, Ralf Baechle <ralf@linux-mips.org> wrote: > > Your patch also still contains copy_user_page(). The only user of it used > > to be copy_user_highpage() so after our rewrite it can go away. I've > > already applied both fixes to my working version of the patch. > > Yes, it is intentional. I keep copy_user_page() just because it is > described in cachetlb.txt and exported. > > Of course we can remove it. I do not care :-) Also I wondered we > should export copy_user_highpage() or not ... > > > Your patch only maps the source page. I'm trying to map the destination > > page also and I'm hitting a few issues with it. > > If you wanted to map the destination, you must writeback the dcache > via kernel mapping first. The dcache can contain dirty data for the > page by previous usage. And if the page was executable, we must flush > the destination page after copy_page() (via coherent mapping) anyway > for I/D coherency. > > So now I think mapping the destination is not worth to do. I figured it was worth a try. It means the process will start running with a hot copy of the COW page instead of a cold copy and I can use hit invalidates instead of hit wbinv on the kernel address of the to page. Lmbenching now, stay tuned ... Ralf ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] fix cache coherency issues 2006-08-24 12:12 ` Ralf Baechle @ 2006-08-24 15:12 ` Atsushi Nemoto 0 siblings, 0 replies; 19+ messages in thread From: Atsushi Nemoto @ 2006-08-24 15:12 UTC (permalink / raw) To: ralf; +Cc: nigel, linux-mips On Thu, 24 Aug 2006 13:12:05 +0100, Ralf Baechle <ralf@linux-mips.org> wrote: > I figured it was worth a try. It means the process will start running with > a hot copy of the COW page instead of a cold copy and I can use hit > invalidates instead of hit wbinv on the kernel address of the to page. > > Lmbenching now, stay tuned ... Hmm, if we could invalidate _before_ copy_page() and writeback (without invalidate) _after_ the copy, it might be worth. --- Atsushi Nemoto ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2006-08-24 15:10 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-02-13 16:15 [PATCH] fix cache coherency issues Atsushi Nemoto 2006-02-14 1:59 ` Yoichi Yuasa 2006-02-14 2:15 ` Atsushi Nemoto 2006-02-14 2:26 ` Yoichi Yuasa 2006-02-14 3:08 ` Atsushi Nemoto 2006-02-14 7:07 ` Yoichi Yuasa 2006-02-14 7:42 ` Atsushi Nemoto 2006-02-14 8:56 ` Yoichi Yuasa 2006-02-14 10:12 ` Atsushi Nemoto 2006-02-14 13:14 ` Atsushi Nemoto 2006-02-14 13:48 ` Kevin D. Kissell 2006-05-22 15:34 ` Atsushi Nemoto 2006-08-23 15:31 ` Atsushi Nemoto 2006-08-23 16:52 ` Nigel Stephens 2006-08-24 1:15 ` Atsushi Nemoto 2006-08-24 11:15 ` Ralf Baechle 2006-08-24 11:38 ` Atsushi Nemoto 2006-08-24 12:12 ` Ralf Baechle 2006-08-24 15:12 ` Atsushi Nemoto
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox