mem: Promote deferred targets only when the block is valid

When a response indicates that there are no other sharers of the
block, the cache can promote its copy of the block to writable and
potential service deferred targets even if the request didn't ask for
a writable copy.

Previously, a response would guarantee the presence of the block in
the cache. A response could either be filling, upgrading or a response
to an invalidation due to a pending whole line write. Responses to
cache maintenance invalidations break this assumption. This change
adds an extra check to make sure that the block was already valid or
that the response is filling before promoting the block.

Change-Id: I6839f683a05d4dad4205c23f365a925b7b05e366
Reviewed-by: Curtis Dunham <curtis.dunham@arm.com>
Reviewed-by: Anouk Van Laer <anouk.vanlaer@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/5048
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
diff --git a/src/mem/cache/cache.cc b/src/mem/cache/cache.cc
index 97df562..48974c7 100644
--- a/src/mem/cache/cache.cc
+++ b/src/mem/cache/cache.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2016 ARM Limited
+ * Copyright (c) 2010-2017 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -1336,16 +1336,17 @@
 
     Tick forward_time = clockEdge(forwardLatency) + pkt->headerDelay;
 
-    // upgrade deferred targets if the response has no sharers, and is
-    // thus passing writable
-    if (!pkt->hasSharers()) {
-        mshr->promoteWritable();
-    }
-
     bool is_fill = !mshr->isForward &&
         (pkt->isRead() || pkt->cmd == MemCmd::UpgradeResp);
 
     CacheBlk *blk = tags->findBlock(pkt->getAddr(), pkt->isSecure());
+    const bool valid_blk = blk && blk->isValid();
+    // If the response indicates that there are no sharers and we
+    // either had the block already or the response is filling we can
+    // promote our copy to writable
+    if (!pkt->hasSharers() && (is_fill || valid_blk)) {
+        mshr->promoteWritable();
+    }
 
     if (is_fill && !is_error) {
         DPRINTF(Cache, "Block for addr %#llx being updated in Cache\n",