// special handling for coherent block requests from
             // upper-level caches
             if (pkt->needsExclusive()) {
+                // sanity check
+                assert(pkt->cmd == MemCmd::ReadExReq ||
+                       pkt->cmd == MemCmd::SCUpgradeFailReq);
+
                 // if we have a dirty copy, make sure the recipient
                 // keeps it marked dirty
                 if (blk->isDirty()) {
                 if (blk != tempBlock)
                     tags->invalidate(blk);
                 blk->invalidate();
-            } else if (blk->isWritable() && !pending_downgrade
-                      && !pkt->sharedAsserted() && !pkt->req->isInstFetch()) {
+            } else if (blk->isWritable() && !pending_downgrade &&
+                       !pkt->sharedAsserted() &&
+                       pkt->cmd != MemCmd::ReadCleanReq) {
                 // we can give the requester an exclusive copy (by not
                 // asserting shared line) on a read request if:
                 // - we have an exclusive copy at this level (& below)
         cmd = cpu_pkt->cmd;
     } else {
         // block is invalid
-        cmd = needsExclusive ? MemCmd::ReadExReq : MemCmd::ReadReq;
+        cmd = needsExclusive ? MemCmd::ReadExReq :
+            (isReadOnly ? MemCmd::ReadCleanReq : MemCmd::ReadSharedReq);
     }
     PacketPtr pkt = new Packet(cpu_pkt->req, cmd, blkSize);
 
 
 {
     /* InvalidCmd */
     { 0, InvalidCmd, "InvalidCmd" },
-    /* ReadReq */
+    /* ReadReq - Read issued by a non-caching agent such as a CPU or
+     * device, with no restrictions on alignment. */
     { SET3(IsRead, IsRequest, NeedsResponse), ReadResp, "ReadReq" },
     /* ReadResp */
     { SET3(IsRead, IsResponse, HasData), InvalidCmd, "ReadResp" },
      * that it has failed, acquires line as Dirty*/
     { SET4(IsRead, NeedsExclusive, IsResponse, HasData),
             InvalidCmd, "UpgradeFailResp" },
-    /* ReadExReq */
+    /* ReadExReq - Read issues by a cache, always cache-line aligned,
+     * and the response is guaranteed to be writeable (exclusive or
+     * even modified) */
     { SET5(IsRead, NeedsExclusive, IsInvalidate, IsRequest, NeedsResponse),
             ReadExResp, "ReadExReq" },
-    /* ReadExResp */
+    /* ReadExResp - Response matching a read exclusive, as we check
+     * the need for exclusive also on responses */
     { SET4(IsRead, NeedsExclusive, IsResponse, HasData),
             InvalidCmd, "ReadExResp" },
+    /* ReadCleanReq - Read issued by a cache, always cache-line
+     * aligned, and the response is guaranteed to not contain dirty data
+     * (exclusive or shared).*/
+    { SET3(IsRead, IsRequest, NeedsResponse), ReadResp, "ReadCleanReq" },
+    /* ReadSharedReq - Read issued by a cache, always cache-line
+     * aligned, response is shared, possibly exclusive, owned or even
+     * modified. */
+    { SET3(IsRead, IsRequest, NeedsResponse), ReadResp, "ReadSharedReq" },
     /* LoadLockedReq: note that we use plain ReadResp as response, so that
      *                we can also use ReadRespWithInvalidate when needed */
     { SET4(IsRead, IsLlsc, IsRequest, NeedsResponse),