mem-cache: Add multiple eviction stats
[gem5.git] / src / arch / x86 / pagetable_walker.cc
index 960954f15d2ef4dcfd7e055f0701e17323f3915c..aab0a711892e0090b35f8609a3b2e863be840f26 100644 (file)
  * Authors: Gabe Black
  */
 
-#include "arch/x86/pagetable.hh"
 #include "arch/x86/pagetable_walker.hh"
+
+#include <memory>
+
+#include "arch/x86/faults.hh"
+#include "arch/x86/pagetable.hh"
 #include "arch/x86/tlb.hh"
 #include "arch/x86/vtophys.hh"
 #include "base/bitfield.hh"
 
 namespace X86ISA {
 
-// Unfortunately, the placement of the base field in a page table entry is
-// very erratic and would make a mess here. It might be moved here at some
-// point in the future.
-BitUnion64(PageTableEntry)
-    Bitfield<63> nx;
-    Bitfield<11, 9> avl;
-    Bitfield<8> g;
-    Bitfield<7> ps;
-    Bitfield<6> d;
-    Bitfield<5> a;
-    Bitfield<4> pcd;
-    Bitfield<3> pwt;
-    Bitfield<2> u;
-    Bitfield<1> w;
-    Bitfield<0> p;
-EndBitUnion(PageTableEntry)
-
 Fault
 Walker::start(ThreadContext * _tc, BaseTLB::Translation *_translation,
-              RequestPtr _req, BaseTLB::Mode _mode)
+              const RequestPtr &_req, BaseTLB::Mode _mode)
 {
     // TODO: in timing mode, instead of blocking when there are other
     // outstanding requests, see if this request can be coalesced with
     // another one (i.e. either coalesce or start walk)
     WalkerState * newState = new WalkerState(this, _translation, _req);
-    newState->initState(_tc, _mode, sys->getMemoryMode() == Enums::timing);
+    newState->initState(_tc, _mode, sys->isTimingMode());
     if (currStates.size()) {
         assert(newState->isTiming());
         DPRINTF(PageTableWalker, "Walks in progress: %d\n", currStates.size());
@@ -114,18 +101,16 @@ Walker::startFunctional(ThreadContext * _tc, Addr &addr, unsigned &logBytes,
 }
 
 bool
-Walker::WalkerPort::recvTiming(PacketPtr pkt)
+Walker::WalkerPort::recvTimingResp(PacketPtr pkt)
 {
-    return walker->recvTiming(pkt);
+    return walker->recvTimingResp(pkt);
 }
 
 bool
-Walker::recvTiming(PacketPtr pkt)
+Walker::recvTimingResp(PacketPtr pkt)
 {
-    assert(pkt->isResponse());
     WalkerSenderState * senderState =
-        dynamic_cast<WalkerSenderState *>(pkt->senderState);
-    pkt->senderState = senderState->saved;
+        dynamic_cast<WalkerSenderState *>(pkt->popSenderState());
     WalkerState * senderWalk = senderState->senderWalk;
     bool walkComplete = senderWalk->recvPacket(pkt);
     delete senderState;
@@ -141,23 +126,22 @@ Walker::recvTiming(PacketPtr pkt)
         delete senderWalk;
         // Since we block requests when another is outstanding, we
         // need to check if there is a waiting request to be serviced
-        if (currStates.size()) {
-            WalkerState * newState = currStates.front();
-            if (!newState->wasStarted())
-                newState->startWalk();
-        }
+        if (currStates.size() && !startWalkWrapperEvent.scheduled())
+            // delay sending any new requests until we are finished
+            // with the responses
+            schedule(startWalkWrapperEvent, clockEdge());
     }
     return true;
 }
 
 void
-Walker::WalkerPort::recvRetry()
+Walker::WalkerPort::recvReqRetry()
 {
-    walker->recvRetry();
+    walker->recvReqRetry();
 }
 
 void
-Walker::recvRetry()
+Walker::recvReqRetry()
 {
     std::list<WalkerState *>::iterator iter;
     for (iter = currStates.begin(); iter != currStates.end(); iter++) {
@@ -170,17 +154,27 @@ Walker::recvRetry()
 
 bool Walker::sendTiming(WalkerState* sendingState, PacketPtr pkt)
 {
-    pkt->senderState = new WalkerSenderState(sendingState, pkt->senderState);
-    return port.sendTiming(pkt);
+    WalkerSenderState* walker_state = new WalkerSenderState(sendingState);
+    pkt->pushSenderState(walker_state);
+    if (port.sendTimingReq(pkt)) {
+        return true;
+    } else {
+        // undo the adding of the sender state and delete it, as we
+        // will do it again the next time we attempt to send it
+        pkt->popSenderState();
+        delete walker_state;
+        return false;
+    }
+
 }
 
-MasterPort &
-Walker::getMasterPort(const std::string &if_name, int idx)
+Port &
+Walker::getPort(const std::string &if_name, PortID idx)
 {
     if (if_name == "port")
         return port;
     else
-        return MemObject::getMasterPort(if_name, idx);
+        return ClockedObject::getPort(if_name, idx);
 }
 
 void
@@ -194,11 +188,48 @@ Walker::WalkerState::initState(ThreadContext * _tc,
     timing = _isTiming;
 }
 
+void
+Walker::startWalkWrapper()
+{
+    unsigned num_squashed = 0;
+    WalkerState *currState = currStates.front();
+    while ((num_squashed < numSquashable) && currState &&
+        currState->translation->squashed()) {
+        currStates.pop_front();
+        num_squashed++;
+
+        DPRINTF(PageTableWalker, "Squashing table walk for address %#x\n",
+            currState->req->getVaddr());
+
+        // finish the translation which will delete the translation object
+        currState->translation->finish(
+            std::make_shared<UnimpFault>("Squashed Inst"),
+            currState->req, currState->tc, currState->mode);
+
+        // delete the current request if there are no inflight packets.
+        // if there is something in flight, delete when the packets are
+        // received and inflight is zero.
+        if (currState->numInflight() == 0) {
+            delete currState;
+        } else {
+            currState->squash();
+        }
+
+        // check the next translation request, if it exists
+        if (currStates.size())
+            currState = currStates.front();
+        else
+            currState = NULL;
+    }
+    if (currState && !currState->wasStarted())
+        currState->startWalk();
+}
+
 Fault
 Walker::WalkerState::startWalk()
 {
     Fault fault = NoFault;
-    assert(started == false);
+    assert(!started);
     started = true;
     setupWalk(req->getVaddr());
     if (timing) {
@@ -216,7 +247,7 @@ Walker::WalkerState::startWalk()
             nextState = Ready;
             if (write)
                 walker->port.sendAtomic(write);
-        } while(read);
+        } while (read);
         state = Ready;
         nextState = Waiting;
     }
@@ -227,7 +258,7 @@ Fault
 Walker::WalkerState::startFunctional(Addr &addr, unsigned &logBytes)
 {
     Fault fault = NoFault;
-    assert(started == false);
+    assert(!started);
     started = true;
     setupWalk(addr);
 
@@ -240,7 +271,7 @@ Walker::WalkerState::startFunctional(Addr &addr, unsigned &logBytes)
         assert(fault == NoFault || read == NULL);
         state = nextState;
         nextState = Ready;
-    } while(read);
+    } while (read);
     logBytes = entry.logBytes;
     addr = entry.paddr;
 
@@ -255,9 +286,9 @@ Walker::WalkerState::stepWalk(PacketPtr &write)
     write = NULL;
     PageTableEntry pte;
     if (dataSize == 8)
-        pte = read->get<uint64_t>();
+        pte = read->getLE<uint64_t>();
     else
-        pte = read->get<uint32_t>();
+        pte = read->getLE<uint32_t>();
     VAddr vaddr = entry.vaddr;
     bool uncacheable = pte.pcd;
     Addr nextRead = 0;
@@ -490,20 +521,18 @@ Walker::WalkerState::stepWalk(PacketPtr &write)
         //If we didn't return, we're setting up another read.
         Request::Flags flags = oldRead->req->getFlags();
         flags.set(Request::UNCACHEABLE, uncacheable);
-        RequestPtr request =
-            new Request(nextRead, oldRead->getSize(), flags, walker->masterId);
+        RequestPtr request = std::make_shared<Request>(
+            nextRead, oldRead->getSize(), flags, walker->masterId);
         read = new Packet(request, MemCmd::ReadReq);
         read->allocate();
         // If we need to write, adjust the read packet to write the modified
         // value back to memory.
         if (doWrite) {
             write = oldRead;
-            write->set<uint64_t>(pte);
+            write->setLE<uint64_t>(pte);
             write->cmd = MemCmd::WriteReq;
-            write->clearDest();
         } else {
             write = NULL;
-            delete oldRead->req;
             delete oldRead;
         }
     }
@@ -514,7 +543,6 @@ void
 Walker::WalkerState::endWalk()
 {
     nextState = Ready;
-    delete read->req;
     delete read;
     read = NULL;
 }
@@ -561,8 +589,10 @@ Walker::WalkerState::setupWalk(Addr vaddr)
     Request::Flags flags = Request::PHYSICAL;
     if (cr3.pcd)
         flags.set(Request::UNCACHEABLE);
-    RequestPtr request = new Request(topAddr, dataSize, flags,
-                                     walker->masterId);
+
+    RequestPtr request = std::make_shared<Request>(
+        topAddr, dataSize, flags, walker->masterId);
+
     read = new Packet(request, MemCmd::ReadReq);
     read->allocate();
 }
@@ -571,63 +601,59 @@ bool
 Walker::WalkerState::recvPacket(PacketPtr pkt)
 {
     assert(pkt->isResponse());
-    if (!pkt->wasNacked()) {
-        assert(inflight);
-        assert(state == Waiting);
+    assert(inflight);
+    assert(state == Waiting);
+    inflight--;
+    if (squashed) {
+        // if were were squashed, return true once inflight is zero and
+        // this WalkerState will be freed there.
+        return (inflight == 0);
+    }
+    if (pkt->isRead()) {
+        // should not have a pending read it we also had one outstanding
         assert(!read);
-        inflight--;
-        if (pkt->isRead()) {
-            state = nextState;
-            nextState = Ready;
-            PacketPtr write = NULL;
-            read = pkt;
-            timingFault = stepWalk(write);
-            state = Waiting;
-            assert(timingFault == NoFault || read == NULL);
-            if (write) {
-                writes.push_back(write);
-            }
-            sendPackets();
-        } else {
-            sendPackets();
-        }
-        if (inflight == 0 && read == NULL && writes.size() == 0) {
-            state = Ready;
-            nextState = Waiting;
-            if (timingFault == NoFault) {
-                /*
-                 * Finish the translation. Now that we now the right entry is
-                 * in the TLB, this should work with no memory accesses.
-                 * There could be new faults unrelated to the table walk like
-                 * permissions violations, so we'll need the return value as
-                 * well.
-                 */
-                bool delayedResponse;
-                Fault fault = walker->tlb->translate(req, tc, NULL, mode,
-                        delayedResponse, true);
-                assert(!delayedResponse);
-                // Let the CPU continue.
-                translation->finish(fault, req, tc, mode);
-            } else {
-                // There was a fault during the walk. Let the CPU know.
-                translation->finish(timingFault, req, tc, mode);
-            }
-            return true;
+
+        // @todo someone should pay for this
+        pkt->headerDelay = pkt->payloadDelay = 0;
+
+        state = nextState;
+        nextState = Ready;
+        PacketPtr write = NULL;
+        read = pkt;
+        timingFault = stepWalk(write);
+        state = Waiting;
+        assert(timingFault == NoFault || read == NULL);
+        if (write) {
+            writes.push_back(write);
         }
+        sendPackets();
     } else {
-        DPRINTF(PageTableWalker, "Request was nacked. Entering retry state\n");
-        pkt->reinitNacked();
-        if (!walker->sendTiming(this, pkt)) {
-            inflight--;
-            retrying = true;
-            if (pkt->isWrite()) {
-                writes.push_back(pkt);
-            } else {
-                assert(!read);
-                read = pkt;
-            }
+        sendPackets();
+    }
+    if (inflight == 0 && read == NULL && writes.size() == 0) {
+        state = Ready;
+        nextState = Waiting;
+        if (timingFault == NoFault) {
+            /*
+             * Finish the translation. Now that we know the right entry is
+             * in the TLB, this should work with no memory accesses.
+             * There could be new faults unrelated to the table walk like
+             * permissions violations, so we'll need the return value as
+             * well.
+             */
+            bool delayedResponse;
+            Fault fault = walker->tlb->translate(req, tc, NULL, mode,
+                                                 delayedResponse, true);
+            assert(!delayedResponse);
+            // Let the CPU continue.
+            translation->finish(fault, req, tc, mode);
+        } else {
+            // There was a fault during the walk. Let the CPU know.
+            translation->finish(timingFault, req, tc, mode);
         }
+        return true;
     }
+
     return false;
 }
 
@@ -664,6 +690,12 @@ Walker::WalkerState::sendPackets()
     }
 }
 
+unsigned
+Walker::WalkerState::numInflight() const
+{
+    return inflight;
+}
+
 bool
 Walker::WalkerState::isRetrying()
 {
@@ -682,6 +714,12 @@ Walker::WalkerState::wasStarted()
     return started;
 }
 
+void
+Walker::WalkerState::squash()
+{
+    squashed = true;
+}
+
 void
 Walker::WalkerState::retry()
 {
@@ -696,7 +734,8 @@ Walker::WalkerState::pageFault(bool present)
     HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
     if (mode == BaseTLB::Execute && !enableNX)
         mode = BaseTLB::Read;
-    return new PageFault(entry.vaddr, present, mode, m5reg.cpl == 3, false);
+    return std::make_shared<PageFault>(entry.vaddr, present, mode,
+                                       m5reg.cpl == 3, false);
 }
 
 /* end namespace X86ISA */ }