Messages in this thread Patch in this message | | | Subject | Re: mmap() 'Page Cache Disable' and 'Page Write Through' | From | David Wragg <> | Date | 10 Apr 1999 21:18:11 +0000 |
| |
"Kendall Bennett" <KendallB@scitechsoft.com> writes: > Hence it would be much better if the Linux kernel could allocate > mappings via mmap() with the PCD *and* PWT bits set, but only do this > if the caller actually needs this (ie: doing this for the framebuffer > mapping is not desireable!). One way to do this, is to change the > kernel so that if the /dev/mem device is opened with O_SYNC it will > set both the PCD and PWT bits. If it is opened without O_SYNC, it > will ony set the PCD bit (for backwards compatibility with older i486 > and Pentium systems).
Below is a patch against 2.2.5 that implements this.
Features:
- On Pentium and pre-Pentium systems, it works just as before. Their chipsets should disable caching outside of main memory using the KEN# pin, but changing the behaviour isn't worth the risk.
- The PPro and successors have no cache enable/disable pins -- the MTRRs are supposed to be used instead. So the right thing to do is to normally leave PCD and PWT unset on pages, so the MTRRs can work properly.
- O_SYNC disables caching by setting both the PCD and PWT bits. The extra PWT has no effect on Pentium and pre-Pentium systems. On the PPro and successors, both bits need to be set to disable caching whatever the MTRR type may be.
- I renamed _PAGE_WT to _PAGE_PWT, because that's what the Intel docs actually call it.
So with this patch, O_SYNC can be used to map /dev/mem as uncached, without interactions with the MTRRs.
It won't make any difference for non-Intel x86 processors (though it won't fix any similar problems they might have; I'd be grateful if anyone familiar with the memory type facilities on AMD and Cyrix processors could check this out).
(drivers/video/fbmem.c and drivers/video/sgivwfb.c use _PAGE_PCD and so probably need tweaking. But pgprot_noncached and noncached_address should be moved into arch-specific headers first.)
Dave Wragg
diff -rua linux-2.2.5/drivers/char/mem.c linux-2.2.5.mod/drivers/char/mem.c --- linux-2.2.5/drivers/char/mem.c Sat Mar 27 19:07:04 1999 +++ linux-2.2.5.mod/drivers/char/mem.c Sat Apr 10 18:06:33 1999 @@ -138,8 +138,11 @@ static inline unsigned long pgprot_noncached(unsigned long prot) { #if defined(__i386__) + /* On PPro and successors, PCD alone doesn't always mean + uncached because of interactions with the MTRRs. PCD | PWT + means definitely uncached. */ if (boot_cpu_data.x86 > 3) - prot |= _PAGE_PCD; + prot |= _PAGE_PCD | _PAGE_PWT; #elif defined(__powerpc__) prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; #elif defined(__mc68000__) @@ -155,6 +158,28 @@ return prot; } +/* + * Architectures vary in how they handle caching for addresses + * outside of main memory. + */ +static inline int noncached_address(unsigned long addr) +{ +#if defined(__i386__) + /* + * On the PPro and successors, the MTRRs are used to set + * memory types for physical addresses outside main memory, + * so blindly setting PCD or PWT on those pages is wrong. + * For Pentiums and earlier, the surround logic should disable + * caching for the high addresses through the KEN pin, but + * we maintain the tradition of paranoia in this code. + */ + return !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) + && addr >= __pa(high_memory); +#else + return addr >= __pa(high_memory); +#endif +} + static int mmap_mem(struct file * file, struct vm_area_struct * vma) { unsigned long offset = vma->vm_offset; @@ -166,15 +191,17 @@ * Accessing memory above the top the kernel knows about or * through a file pointer that was marked O_SYNC will be * done non-cached. - * - * Set VM_IO, as this is likely a non-cached access to an - * I/O area, and we don't want to include that in a core - * file. */ - if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) { - pgprot_val(vma->vm_page_prot) = pgprot_noncached(pgprot_val(vma->vm_page_prot)); + if (noncached_address(offset) || (file->f_flags & O_SYNC)) + pgprot_val(vma->vm_page_prot) + = pgprot_noncached(pgprot_val(vma->vm_page_prot)); + + /* + * Don't dump addresses that are not real memory to a core file. + */ + if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) vma->vm_flags |= VM_IO; - } + if (remap_page_range(vma->vm_start, offset, vma->vm_end-vma->vm_start, vma->vm_page_prot)) return -EAGAIN; diff -rua linux-2.2.5/include/asm-i386/pgtable.h linux-2.2.5.mod/include/asm-i386/pgtable.h --- linux-2.2.5/include/asm-i386/pgtable.h Sat Apr 10 17:44:42 1999 +++ linux-2.2.5.mod/include/asm-i386/pgtable.h Sat Apr 10 17:52:07 1999 @@ -220,7 +220,7 @@ #define _PAGE_PRESENT 0x001 #define _PAGE_RW 0x002 #define _PAGE_USER 0x004 -#define _PAGE_WT 0x008 +#define _PAGE_PWT 0x008 #define _PAGE_PCD 0x010 #define _PAGE_ACCESSED 0x020 #define _PAGE_DIRTY 0x040
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |