ruby: Fix block_on behavior
authorJoel Hestness <jthestness@gmail.com>
Fri, 15 Apr 2016 17:34:02 +0000 (12:34 -0500)
committerJoel Hestness <jthestness@gmail.com>
Fri, 15 Apr 2016 17:34:02 +0000 (12:34 -0500)
Ruby's controller block_on behavior aimed to block MessageBuffer requests into
SLICC controllers when a Locked_RMW was in flight. Unfortunately, this
functionality only partially works: When non-Locked_RMW memory accesses are
issued to the sequencer to an address with an in-flight Locked_RMW, the
sequencer may pass those accesses through to the controller. At the controller,
a number of incorrect activities can occur depending on the protocol. In
MOESI_hammer, for example, an intermediate IFETCH will cause an L1D to L2
transfer, which cannot be serviced, because the block_on functionality blocks
the trigger queue, resulting in a deadlock. Further, if an intermediate store
arrives (e.g. from a separate SMT thread), the sequencer allows the request
through to the controller, and the atomicity of the Locked_RMW may be broken.

To avoid these problems, disallow the Sequencer from passing any memory
accesses to the controller besides Locked_RMW_Write when a Locked_RMW is in-
flight.

src/mem/ruby/slicc_interface/AbstractController.cc
src/mem/ruby/slicc_interface/AbstractController.hh
src/mem/ruby/system/Sequencer.cc

index 2a53e53beb96efc51c66c8b1fce51038ba90f03d..5d8b6eeea19df7830098982367f534cdc49a84a4 100644 (file)
@@ -192,6 +192,12 @@ AbstractController::blockOnQueue(Addr addr, MessageBuffer* port)
     m_block_map[addr] = port;
 }
 
+bool
+AbstractController::isBlocked(Addr addr) const
+{
+    return m_is_blocking && (m_block_map.find(addr) != m_block_map.end());
+}
+
 void
 AbstractController::unblock(Addr addr)
 {
index cfd11b8eb51ea14081d92ad283d631b28175e012..6f49e5ec42fda925d49d447b3b3479c47c694733 100644 (file)
@@ -73,6 +73,7 @@ class AbstractController : public MemObject, public Consumer
 
     // return instance name
     void blockOnQueue(Addr, MessageBuffer*);
+    bool isBlocked(Addr) const;
     void unblock(Addr);
     bool isBlocked(Addr);
 
index dedade3cf8f4a9e5a6a64f71a3338895412d6032..b7df371e1c16a31d21c733fe120c547256224808 100644 (file)
@@ -173,6 +173,16 @@ Sequencer::insertRequest(PacketPtr pkt, RubyRequestType request_type)
     }
 
     Addr line_addr = makeLineAddress(pkt->getAddr());
+
+    // Check if the line is blocked for a Locked_RMW
+    if (m_controller->isBlocked(line_addr) &&
+        (request_type != RubyRequestType_Locked_RMW_Write)) {
+        // Return that this request's cache line address aliases with
+        // a prior request that locked the cache line. The request cannot
+        // proceed until the cache line is unlocked by a Locked_RMW_Write
+        return RequestStatus_Aliased;
+    }
+
     // Create a default entry, mapping the address to NULL, the cast is
     // there to make gcc 4.4 happy
     RequestTable::value_type default_entry(line_addr,
@@ -382,7 +392,15 @@ Sequencer::writeCallback(Addr address, DataBlock& data,
     if (!m_usingNetworkTester)
         success = handleLlsc(address, request);
 
+    // Handle SLICC block_on behavior for Locked_RMW accesses. NOTE: the
+    // address variable here is assumed to be a line address, so when
+    // blocking buffers, must check line addresses.
     if (request->m_type == RubyRequestType_Locked_RMW_Read) {
+        // blockOnQueue blocks all first-level cache controller queues
+        // waiting on memory accesses for the specified address that go to
+        // the specified queue. In this case, a Locked_RMW_Write must go to
+        // the mandatory_q before unblocking the first-level controller.
+        // This will block standard loads, stores, ifetches, etc.
         m_controller->blockOnQueue(address, m_mandatory_q_ptr);
     } else if (request->m_type == RubyRequestType_Locked_RMW_Write) {
         m_controller->unblock(address);