mem: Track TLB entries in the lookup cache as pointers.

Using the architectural page table on x86 and the functional page table
on ARM, both with the twolf benchmark in SE mode, there was no
performance penalty for doing so, and again possibly a performance
improvement. By using a pointer instead of an inline instance, it's
possible for the actual type of the TLB entry to be hidden somewhat,
taking a step towards abstracting away another aspect of the ISAs.

Since the TLB entries are no longer overwritten and now need to be
allocated and freed, this change introduces return types from the
updateCache and eraseCacheEntry functions. These functions will return
the pointer to any entry which has been displaced from the cache which
the caller can either free or ignore, depending on whether the entry
has a purpose outside of the cache.

Because the functional page table stores its entries over a longer time
period, it will generally not delete the pointer returned from those
functions. The "architechtural" page table, ie the one which is backed
by memory, doesn't have any other use for the TlbEntrys and will delete
them. That leads to more news and deletes than there used to be.

To address that, and also to speed up the architectural page table in
general, it would be a good idea to augment the functional page table
with an image of the table in memory, instead of replacing it with one.
The functional page table would provide quick lookups and also avoid
having to translate page table entries to TLB entries, making
performance essentially equivalent to the functional case. The backing
page tables, which are primarily for consumption by the physical
hardware when in KVM, can be updated when mappings change but otherwise
left alone.

If we end up doing that, we could just let the ISA specific process
classes enable whatever additional TLB machinery they need, likely
a backing copy in memory, without any knowledge or involvement from
the ISA agnostic class. We would be able to get rid of the useArchPT
setting and the bits of code in the configs which set it.

Change-Id: I2e21945cd852bb1b3d0740fe6a4c5acbfd9548c5
Reviewed-on: https://gem5-review.googlesource.com/6983
Maintainer: Gabe Black <gabeblack@google.com>
Reviewed-by: Brandon Potter <Brandon.Potter@amd.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Anthony Gutierrez <anthony.gutierrez@amd.com>
This commit is contained in:
Gabe Black
2017-12-22 19:21:30 -08:00
parent 3e8d76e2e2
commit b8b13206c8
3 changed files with 84 additions and 60 deletions

View File

@@ -164,10 +164,10 @@ MultiLevelPageTable<ISAOps>::map(Addr vaddr, Addr paddr,
p.write<PageTableEntry>(PTE_addr, PTE); p.write<PageTableEntry>(PTE_addr, PTE);
DPRINTF(MMU, "New mapping: %#x-%#x\n", vaddr, paddr); DPRINTF(MMU, "New mapping: %#x-%#x\n", vaddr, paddr);
eraseCacheEntry(vaddr); delete eraseCacheEntry(vaddr);
updateCache(vaddr, TlbEntry(pid, vaddr, paddr, delete updateCache(vaddr, new TlbEntry(pid, vaddr, paddr,
flags & Uncacheable, flags & Uncacheable,
flags & ReadOnly)); flags & ReadOnly));
} }
} }
@@ -211,8 +211,9 @@ MultiLevelPageTable<ISAOps>::remap(Addr vaddr, int64_t size, Addr new_vaddr)
DPRINTF(MMU, "Remapping: %#x-%#x\n", vaddr, new_PTE_addr); DPRINTF(MMU, "Remapping: %#x-%#x\n", vaddr, new_PTE_addr);
} }
eraseCacheEntry(vaddr); delete eraseCacheEntry(vaddr);
updateCache(new_vaddr, TlbEntry(pid, new_vaddr, paddr, delete updateCache(new_vaddr,
new TlbEntry(pid, new_vaddr, paddr,
pTableISAOps.isUncacheable(PTE), pTableISAOps.isUncacheable(PTE),
pTableISAOps.isReadOnly(PTE))); pTableISAOps.isReadOnly(PTE)));
} else { } else {
@@ -243,7 +244,7 @@ MultiLevelPageTable<ISAOps>::unmap(Addr vaddr, int64_t size)
p.write<PageTableEntry>(PTE_addr, PTE); p.write<PageTableEntry>(PTE_addr, PTE);
DPRINTF(MMU, "Unmapping: %#x\n", vaddr); DPRINTF(MMU, "Unmapping: %#x\n", vaddr);
} }
eraseCacheEntry(vaddr); delete eraseCacheEntry(vaddr);
} else { } else {
fatal("Page fault while unmapping"); fatal("Page fault while unmapping");
} }
@@ -277,16 +278,16 @@ MultiLevelPageTable<ISAOps>::lookup(Addr vaddr, TlbEntry &entry)
{ {
Addr page_addr = pageAlign(vaddr); Addr page_addr = pageAlign(vaddr);
if (pTableCache[0].valid && pTableCache[0].vaddr == page_addr) { if (pTableCache[0].entry && pTableCache[0].vaddr == page_addr) {
entry = pTableCache[0].entry; entry = *pTableCache[0].entry;
return true; return true;
} }
if (pTableCache[1].valid && pTableCache[1].vaddr == page_addr) { if (pTableCache[1].entry && pTableCache[1].vaddr == page_addr) {
entry = pTableCache[1].entry; entry = *pTableCache[1].entry;
return true; return true;
} }
if (pTableCache[2].valid && pTableCache[2].vaddr == page_addr) { if (pTableCache[2].entry && pTableCache[2].vaddr == page_addr) {
entry = pTableCache[2].entry; entry = *pTableCache[2].entry;
return true; return true;
} }
@@ -299,10 +300,11 @@ MultiLevelPageTable<ISAOps>::lookup(Addr vaddr, TlbEntry &entry)
if (pnum == 0) if (pnum == 0)
return false; return false;
entry = TlbEntry(pid, vaddr, pnum << PageShift, TlbEntry *new_entry = new TlbEntry(pid, vaddr, pnum << PageShift,
pTableISAOps.isUncacheable(PTE), pTableISAOps.isUncacheable(PTE),
pTableISAOps.isReadOnly(PTE)); pTableISAOps.isReadOnly(PTE));
updateCache(page_addr, entry); entry = *new_entry;
delete updateCache(page_addr, new_entry);
} else { } else {
return false; return false;
} }

View File

@@ -56,6 +56,8 @@ FuncPageTable::FuncPageTable(const std::string &__name,
FuncPageTable::~FuncPageTable() FuncPageTable::~FuncPageTable()
{ {
for (auto &iter : pTable)
delete iter.second;
} }
void void
@@ -68,12 +70,20 @@ FuncPageTable::map(Addr vaddr, Addr paddr, int64_t size, uint64_t flags)
DPRINTF(MMU, "Allocating Page: %#x-%#x\n", vaddr, vaddr+ size); DPRINTF(MMU, "Allocating Page: %#x-%#x\n", vaddr, vaddr+ size);
for (; size > 0; size -= pageSize, vaddr += pageSize, paddr += pageSize) { for (; size > 0; size -= pageSize, vaddr += pageSize, paddr += pageSize) {
if (!clobber && (pTable.find(vaddr) != pTable.end())) { auto it = pTable.find(vaddr);
// already mapped if (it != pTable.end()) {
fatal("FuncPageTable::allocate: addr 0x%x already mapped", vaddr); if (clobber) {
delete it->second;
} else {
// already mapped
fatal("FuncPageTable::allocate: addr %#x already mapped",
vaddr);
}
} else {
it = pTable.emplace(vaddr, nullptr).first;
} }
pTable[vaddr] = TheISA::TlbEntry(pid, vaddr, paddr, it->second = new TheISA::TlbEntry(pid, vaddr, paddr,
flags & Uncacheable, flags & Uncacheable,
flags & ReadOnly); flags & ReadOnly);
eraseCacheEntry(vaddr); eraseCacheEntry(vaddr);
@@ -93,13 +103,15 @@ FuncPageTable::remap(Addr vaddr, int64_t size, Addr new_vaddr)
for (; size > 0; for (; size > 0;
size -= pageSize, vaddr += pageSize, new_vaddr += pageSize) size -= pageSize, vaddr += pageSize, new_vaddr += pageSize)
{ {
assert(pTable.find(vaddr) != pTable.end()); auto new_it = pTable.find(new_vaddr);
auto old_it = pTable.find(vaddr);
assert(old_it != pTable.end() && new_it == pTable.end());
pTable[new_vaddr] = pTable[vaddr]; new_it->second = old_it->second;
pTable.erase(vaddr); pTable.erase(old_it);
eraseCacheEntry(vaddr); eraseCacheEntry(vaddr);
pTable[new_vaddr].updateVaddr(new_vaddr); new_it->second->updateVaddr(new_vaddr);
updateCache(new_vaddr, pTable[new_vaddr]); updateCache(new_vaddr, new_it->second);
} }
} }
@@ -107,7 +119,7 @@ void
FuncPageTable::getMappings(std::vector<std::pair<Addr, Addr>> *addr_maps) FuncPageTable::getMappings(std::vector<std::pair<Addr, Addr>> *addr_maps)
{ {
for (auto &iter : pTable) for (auto &iter : pTable)
addr_maps->push_back(make_pair(iter.first, iter.second.pageStart())); addr_maps->push_back(make_pair(iter.first, iter.second->pageStart()));
} }
void void
@@ -118,9 +130,11 @@ FuncPageTable::unmap(Addr vaddr, int64_t size)
DPRINTF(MMU, "Unmapping page: %#x-%#x\n", vaddr, vaddr+ size); DPRINTF(MMU, "Unmapping page: %#x-%#x\n", vaddr, vaddr+ size);
for (; size > 0; size -= pageSize, vaddr += pageSize) { for (; size > 0; size -= pageSize, vaddr += pageSize) {
assert(pTable.find(vaddr) != pTable.end()); auto it = pTable.find(vaddr);
pTable.erase(vaddr); assert(it != pTable.end());
eraseCacheEntry(vaddr); eraseCacheEntry(vaddr);
delete it->second;
pTable.erase(it);
} }
} }
@@ -145,16 +159,16 @@ FuncPageTable::lookup(Addr vaddr, TheISA::TlbEntry &entry)
{ {
Addr page_addr = pageAlign(vaddr); Addr page_addr = pageAlign(vaddr);
if (pTableCache[0].valid && pTableCache[0].vaddr == page_addr) { if (pTableCache[0].entry && pTableCache[0].vaddr == page_addr) {
entry = pTableCache[0].entry; entry = *pTableCache[0].entry;
return true; return true;
} }
if (pTableCache[1].valid && pTableCache[1].vaddr == page_addr) { if (pTableCache[1].entry && pTableCache[1].vaddr == page_addr) {
entry = pTableCache[1].entry; entry = *pTableCache[1].entry;
return true; return true;
} }
if (pTableCache[2].valid && pTableCache[2].vaddr == page_addr) { if (pTableCache[2].entry && pTableCache[2].vaddr == page_addr) {
entry = pTableCache[2].entry; entry = *pTableCache[2].entry;
return true; return true;
} }
@@ -165,7 +179,7 @@ FuncPageTable::lookup(Addr vaddr, TheISA::TlbEntry &entry)
} }
updateCache(page_addr, iter->second); updateCache(page_addr, iter->second);
entry = iter->second; entry = *iter->second;
return true; return true;
} }
@@ -209,7 +223,7 @@ FuncPageTable::serialize(CheckpointOut &cp) const
ScopedCheckpointSection sec(cp, csprintf("Entry%d", count++)); ScopedCheckpointSection sec(cp, csprintf("Entry%d", count++));
paramOut(cp, "vaddr", pte.first); paramOut(cp, "vaddr", pte.first);
pte.second.serialize(cp); pte.second->serialize(cp);
} }
assert(count == pTable.size()); assert(count == pTable.size());
} }
@@ -223,14 +237,13 @@ FuncPageTable::unserialize(CheckpointIn &cp)
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
ScopedCheckpointSection sec(cp, csprintf("Entry%d", i)); ScopedCheckpointSection sec(cp, csprintf("Entry%d", i));
std::unique_ptr<TheISA::TlbEntry> entry; TheISA::TlbEntry *entry = new TheISA::TlbEntry();
Addr vaddr;
paramIn(cp, "vaddr", vaddr);
entry.reset(new TheISA::TlbEntry());
entry->unserialize(cp); entry->unserialize(cp);
pTable[vaddr] = *entry; Addr vaddr;
paramIn(cp, "vaddr", vaddr);
pTable[vaddr] = entry;
} }
} }

View File

@@ -58,9 +58,8 @@ class PageTableBase : public Serializable
{ {
protected: protected:
struct cacheElement { struct cacheElement {
bool valid;
Addr vaddr; Addr vaddr;
TheISA::TlbEntry entry; TheISA::TlbEntry *entry;
}; };
struct cacheElement pTableCache[3]; struct cacheElement pTableCache[3];
@@ -78,9 +77,9 @@ class PageTableBase : public Serializable
pid(_pid), _name(__name) pid(_pid), _name(__name)
{ {
assert(isPowerOf2(pageSize)); assert(isPowerOf2(pageSize));
pTableCache[0].valid = false; pTableCache[0].entry = nullptr;
pTableCache[1].valid = false; pTableCache[1].entry = nullptr;
pTableCache[2].valid = false; pTableCache[2].entry = nullptr;
} }
virtual ~PageTableBase() {}; virtual ~PageTableBase() {};
@@ -162,36 +161,46 @@ class PageTableBase : public Serializable
* Update the page table cache. * Update the page table cache.
* @param vaddr virtual address (page aligned) to check * @param vaddr virtual address (page aligned) to check
* @param pte page table entry to return * @param pte page table entry to return
* @return A pointer to any entry which is displaced from the cache.
*/ */
inline void updateCache(Addr vaddr, TheISA::TlbEntry entry) TheISA::TlbEntry *
updateCache(Addr vaddr, TheISA::TlbEntry *entry)
{ {
TheISA::TlbEntry *evicted = pTableCache[2].entry;
pTableCache[2].entry = pTableCache[1].entry; pTableCache[2].entry = pTableCache[1].entry;
pTableCache[2].vaddr = pTableCache[1].vaddr; pTableCache[2].vaddr = pTableCache[1].vaddr;
pTableCache[2].valid = pTableCache[1].valid;
pTableCache[1].entry = pTableCache[0].entry; pTableCache[1].entry = pTableCache[0].entry;
pTableCache[1].vaddr = pTableCache[0].vaddr; pTableCache[1].vaddr = pTableCache[0].vaddr;
pTableCache[1].valid = pTableCache[0].valid;
pTableCache[0].entry = entry; pTableCache[0].entry = entry;
pTableCache[0].vaddr = vaddr; pTableCache[0].vaddr = vaddr;
pTableCache[0].valid = true;
return evicted;
} }
/** /**
* Erase an entry from the page table cache. * Erase an entry from the page table cache.
* @param vaddr virtual address (page aligned) to check * @param vaddr virtual address (page aligned) to check
* @return A pointer to the entry (if any) which is kicked out.
*/ */
inline void eraseCacheEntry(Addr vaddr) TheISA::TlbEntry *
eraseCacheEntry(Addr vaddr)
{ {
TheISA::TlbEntry *evicted = nullptr;
// Invalidate cached entries if necessary // Invalidate cached entries if necessary
if (pTableCache[0].valid && pTableCache[0].vaddr == vaddr) { if (pTableCache[0].entry && pTableCache[0].vaddr == vaddr) {
pTableCache[0].valid = false; evicted = pTableCache[0].entry;
} else if (pTableCache[1].valid && pTableCache[1].vaddr == vaddr) { pTableCache[0].entry = nullptr;
pTableCache[1].valid = false; } else if (pTableCache[1].entry && pTableCache[1].vaddr == vaddr) {
} else if (pTableCache[2].valid && pTableCache[2].vaddr == vaddr) { evicted = pTableCache[1].entry;
pTableCache[2].valid = false; pTableCache[1].entry = nullptr;
} else if (pTableCache[2].entry && pTableCache[2].vaddr == vaddr) {
evicted = pTableCache[2].entry;
pTableCache[2].entry = nullptr;
} }
return evicted;
} }
virtual void getMappings(std::vector<std::pair<Addr, Addr>> virtual void getMappings(std::vector<std::pair<Addr, Addr>>
@@ -204,7 +213,7 @@ class PageTableBase : public Serializable
class FuncPageTable : public PageTableBase class FuncPageTable : public PageTableBase
{ {
private: private:
typedef std::unordered_map<Addr, TheISA::TlbEntry> PTable; typedef std::unordered_map<Addr, TheISA::TlbEntry *> PTable;
typedef PTable::iterator PTableItr; typedef PTable::iterator PTableItr;
PTable pTable; PTable pTable;