Fixing MTRRs on Linux

Some x86 computers have a buggy BIOS that can cause poor Linux graphics performance.  The problem is caused by the BIOS’s boot-time configuration of the Memory Type Range Registers (MTRR).  On modern systems, the graphics aperture should be configured as write-combining memory.  But buggy BIOSes will configure it as plain “uncacheable” memory.  The performance cost of the bug can be large: as an example, a ThinkPad T61 with Intel x3100 GMA  and T9300 CPU initially rendered ~120 fps in glxgears; this increased to ~580 fps with a fixed configuration.

You can determine whether your system has the bug by examining your X server log (likely in /var/log/Xorg.0.log) and looking for a line similar to:

(WW) intel(0): Failed to set up write-combining range (0xe0000000,0x10000000)

The Fix

The good news is that the MTRR can be fixed from the Linux command line via ‘cat’ and ‘echo’ and the /proc/mtrr file.  The bad news is that figuring out the right configuration is difficult and often involves some trial and error.  The process often involves converting cached memory to uncached memory, so it is best done in single-user mode, without X running.  The machine should be doing nothing else, otherwise the system is likely to slow to the the point of unusability.

Try the Easier Fix First

Someone may have already fixed the problem for your machine.  If they’ve posted it to the Internet, try their script first.  One sample is below, along with a  few links for other machines.  These may not work, since BIOS MTRR configurations can vary depending on the amount of memory in the system,  devices present, and BIOS revision.

Then Try Figuring It Out

Needed are Linux expertise, an understanding of hexadecimal arithmetic, and an understanding of the basics of how computers map memory. General steps:

  1. Use the error message from the Xorg log file (probably /var/log/Xorg.0.log) to determine the start address and size of the graphics aperture.  Example:
    (WW) intel(0): Failed to set up write-combining range (0xe0000000,0x10000000)
  2. Reorganize the MTRR settings (via /proc/mtrr) to mark the video chip’s memory as write-combining.  This generally involves:
    1. removing any MTRRs that overlap with the graphics region,
    2. adding a MTRR for the write-combining area, and
    3. breaking the removed MTRR into MTRRs that do not overlap the graphics aperture area.
  3. Create a script to replicate the MTRR settings (sample below) and run it during boot, e.g., from /etc/rc.local.

The details are beyond the scope of this post, but here are some resources that may help.

MTRR Fix For A Sample Configuration

Here are the details of an MTRR fix for a Lenovo ThinkPad T61 (8897CTO) laptop with 4GB RAM and a 64-bit kernel.  Here’s the default BIOS MTRR configuration (cat /proc/mtrr):

reg00: base=0xc0000000 (3076MB), size=1024MB: uncachable, count=1
reg01: base=0x13c000000 (5056MB), size=  64MB: uncachable, count=1
reg02: base=0x00000000 (   0MB), size=4096MB: write-back, count=1
reg03: base=0x100000000 (4096MB), size=1024MB: write-back, count=1
reg04: base=0xbf700000 (3063MB), size=   1MB: uncachable, count=1
reg05: base=0xbf800000 (3064MB), size=   8MB: uncachable, count=1

The problem is that the graphics card memory (which according to the Xorg log is at 0xe0000000) lies within the reg00 uncacheable region, but should be marked as “write-combining.”  After running this script:

echo "disable=5" > /proc/mtrr
echo "disable=4" > /proc/mtrr
echo "disable=3" > /proc/mtrr
echo "disable=2" > /proc/mtrr
echo "base=0x0 size=0x80000000 type=write-back" > /proc/mtrr
echo "base=0x80000000 size=0x40000000 type=write-back" > /proc/mtrr
echo "base=0xe0000000 size=0x10000000 type=write-combining" > /proc/mtrr
echo "base=0x100000000 size=0x40000000 type=write-back" > /proc/mtrr
echo "base=0xbf700000 size=0x100000 type=uncachable" > /proc/mtrr
echo "base=0xbf800000 size=0x800000 type=uncachable" > /proc/mtrr
echo "disable=0" > /proc/mtrr
echo "base=0xc0000000 size=0x20000000 type=uncachable" > /proc/mtrr

… the MTRR configuration changes to:

reg00: base=0xc0000000 (3072MB), size= 512MB: uncachable, count=1
reg01: base=0x13c000000 (5056MB), size=  64MB: uncachable, count=1
reg02: base=0x00000000 (   0MB), size=2048MB: write-back, count=1
reg03: base=0x80000000 (2048MB), size=1024MB: write-back, count=1
reg04: base=0xe0000000 (3584MB), size= 256MB: write-combining, count=2
reg05: base=0x100000000 (4096MB), size=1024MB: write-back, count=1
reg06: base=0xbf700000 (3063MB), size=   1MB: uncachable, count=1
reg07: base=0xbf800000 (3064MB), size=   8MB: uncachable, count=1

and glxgears frame rates are much higher.

More Information

Here are some samples that others have provided for other configurations:

A technical discussion of write-combining:

Linux /proc/mtrr documentation: