Cache/Memory Mapping Problems
Virtual caches (real ones with
virtual index and tagging) seem a wonderful free ride, since the
whole cache search process can start earlier and run in parallel with
page-based address translation.
A plain virtual cache must be emptied out whenever there's a memory
map change, which is intolerable unless the cache is very small. But if
you use the ASID to extend the virtual address, entries from different
processes are disambiguated.
OS programmers know why virtual caches are a bad idea: The trouble
with virtual caches is that the data in the cache can survive a change
to the page tables.
In general, the virtual cache ought to be checked after any mapping
is rescinded. That's costly, so OS engineers try to minimize updates,
miss some corner case, and end up with bugs.
In a heroic attempt to make Linux work successfully even with
virtual caches, the kernel provides a set of rules and function calls
that should be provided as part of the port to an architecture with
troublesome caches.
They're the functions with names starting flush cache xxx()
described in the kernel documentation (Documentation/cachetlb.txt.) I
don't like the word "flush" to describe cache operations: It's been
used to mean too many things. So note carefully that in the Linux
kernel a "cache flush" is something you do to get rid of cache entries
that relate to obsolete memory mappings.
In a system where all caches are physically indexed and tagged, none
of these calls needs to do anything.
Fortunately, virtual D-caches are rare on MIPS CPUs. Some recent
CPUs have virtual I-caches: Implement the "flush" functions as
described in the documentation and you should be all right.
But L1 caches with physical tags but virtual indexes are common on
MIPS CPUs. They solve the problems described in this section, but they
lead to a different problem called a "cache alias"(read on).
Cache Aliases
We're now getting to something more pernicious. MIPS CPU designers were
among the first to realize that the benefits of using the virtual
address to index their cache could be combined with the benefit of
using the physical address to tag it. This can lead to cache aliases.
The R4000 CPU was the first to use virtually indexed caches. As
originally conceived, the CPU always came with an L2 cache (the cache memory was off chip, but the L2
controller is included with the CPU), and it used the L2 cache
to detect L1-cache aliases. If you loaded an alias to a line that was
already present in the L1, the CPU generated an exception, which could
be used to clean up.
But the temptation to produce a smaller, cheaper R4000 variant by
omitting the L2 cache memory chips and the pins that wired them up
proved too strong. Contemporary UNIX systems had a fairly stylized way
of using virtual memory, which meant that you could control memory
allocation to avoid ever loading an alias.
In retrospect we can see that generating aliases is a bug, and the
careful memory management was a workaround for it. But it worked, and
people forgot, and it became a feature.
There are basically two ways to deal with cache aliases. The first
is to try to ensure that whenever a page is shared, all the virtual
references to it have the same "page color" (that means that the
references may be different, but the difference between them is a
multiple of the cache set size).
Any data visible twice in same-color pages will be stored at the
same cache index and handled correctly. It's possible to ensure that
all user-space mappings of a page are of the same color.
But unlike the old BSD systems, Linux provides
features where correct page coloring is impossible. Those will be cases
where you have both a user-space and kernel mapping to the same page
(in many cases, on a MIPS kernel, the kernel "mapping" will be a kseg0
address). So the MIPS port has special code to detect those cases and
clean out any old alias mappings.
TheCache/TLB documentation (that's
Documentation/cachetlb.txt, as mentioned in the section above)
makes a heroic attempt to deal with cache aliases as "just another
symptom" of virtual caches in general. It provides some notes on how to
configure the kernel to do what it can on page coloring and how to
handle kernel/user-space aliases.
<>Next in Part 6:
CP0 pipeline
hazards, multiprocessors and coherent caches.
To read Part 4, go to "
What we really want".
To read Part 3, go to "
What
Happens on a System Call"
To read Part 2, go to "
How hardware and software work together."
To read Part 1, go to "
GNU/Linux from eight miles high"
>
This
series of articles is based on material from "See
MIPS Run Linux," by Dominic Sweetman, used with the permission of
the publisher, Morgan Kaufmann/Elsevier, which retains full copyrights.
It can be purchased on line.
Dominic Sweetman is a
software/hardware boundary expert based in London, England, who
previously served as managing director at Algorithmics Ltd.