Re: mmap() 'Page Cache Disable' and 'Page Write Through'

David Wragg (dpw@doc.ic.ac.uk)
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/