snoop_filter.cc revision 11744:5d33c6972dda
1/* 2 * Copyright (c) 2013-2016 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Stephan Diestelhorst 38 */ 39 40/** 41 * @file 42 * Implementation of a snoop filter. 43 */ 44 45#include "base/misc.hh" 46#include "base/trace.hh" 47#include "debug/SnoopFilter.hh" 48#include "mem/snoop_filter.hh" 49#include "sim/system.hh" 50 51void 52SnoopFilter::eraseIfNullEntry(SnoopFilterCache::iterator& sf_it) 53{ 54 SnoopItem& sf_item = sf_it->second; 55 if (!(sf_item.requested | sf_item.holder)) { 56 cachedLocations.erase(sf_it); 57 DPRINTF(SnoopFilter, "%s: Removed SF entry.\n", 58 __func__); 59 } 60} 61 62std::pair<SnoopFilter::SnoopList, Cycles> 63SnoopFilter::lookupRequest(const Packet* cpkt, const SlavePort& slave_port) 64{ 65 DPRINTF(SnoopFilter, "%s: src %s packet %s\n", __func__, 66 slave_port.name(), cpkt->print()); 67 68 // check if the packet came from a cache 69 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping() && 70 cpkt->fromCache(); 71 Addr line_addr = cpkt->getBlockAddr(linesize); 72 if (cpkt->isSecure()) { 73 line_addr |= LineSecure; 74 } 75 SnoopMask req_port = portToMask(slave_port); 76 reqLookupResult = cachedLocations.find(line_addr); 77 bool is_hit = (reqLookupResult != cachedLocations.end()); 78 79 // If the snoop filter has no entry, and we should not allocate, 80 // do not create a new snoop filter entry, simply return a NULL 81 // portlist. 82 if (!is_hit && !allocate) 83 return snoopDown(lookupLatency); 84 85 // If no hit in snoop filter create a new element and update iterator 86 if (!is_hit) 87 reqLookupResult = cachedLocations.emplace(line_addr, SnoopItem()).first; 88 SnoopItem& sf_item = reqLookupResult->second; 89 SnoopMask interested = sf_item.holder | sf_item.requested; 90 91 // Store unmodified value of snoop filter item in temp storage in 92 // case we need to revert because of a send retry in 93 // updateRequest. 94 retryItem = sf_item; 95 96 totRequests++; 97 if (is_hit) { 98 // Single bit set -> value is a power of two 99 if (isPow2(interested)) 100 hitSingleRequests++; 101 else 102 hitMultiRequests++; 103 } 104 105 DPRINTF(SnoopFilter, "%s: SF value %x.%x\n", 106 __func__, sf_item.requested, sf_item.holder); 107 108 // If we are not allocating, we are done 109 if (!allocate) 110 return snoopSelected(maskToPortList(interested & ~req_port), 111 lookupLatency); 112 113 if (cpkt->needsResponse()) { 114 if (!cpkt->cacheResponding()) { 115 // Max one request per address per port 116 panic_if(sf_item.requested & req_port, "double request :( " \ 117 "SF value %x.%x\n", sf_item.requested, sf_item.holder); 118 119 // Mark in-flight requests to distinguish later on 120 sf_item.requested |= req_port; 121 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 122 __func__, sf_item.requested, sf_item.holder); 123 } else { 124 // NOTE: The memInhibit might have been asserted by a cache closer 125 // to the CPU, already -> the response will not be seen by this 126 // filter -> we do not need to keep the in-flight request, but make 127 // sure that we know that that cluster has a copy 128 panic_if(!(sf_item.holder & req_port), "Need to hold the value!"); 129 DPRINTF(SnoopFilter, 130 "%s: not marking request. SF value %x.%x\n", 131 __func__, sf_item.requested, sf_item.holder); 132 } 133 } else { // if (!cpkt->needsResponse()) 134 assert(cpkt->isEviction()); 135 // make sure that the sender actually had the line 136 panic_if(!(sf_item.holder & req_port), "requester %x is not a " \ 137 "holder :( SF value %x.%x\n", req_port, 138 sf_item.requested, sf_item.holder); 139 // CleanEvicts and Writebacks -> the sender and all caches above 140 // it may not have the line anymore. 141 if (!cpkt->isBlockCached()) { 142 sf_item.holder &= ~req_port; 143 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 144 __func__, sf_item.requested, sf_item.holder); 145 } 146 } 147 148 return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency); 149} 150 151void 152SnoopFilter::finishRequest(bool will_retry, Addr addr, bool is_secure) 153{ 154 if (reqLookupResult != cachedLocations.end()) { 155 // since we rely on the caller, do a basic check to ensure 156 // that finishRequest is being called following lookupRequest 157 Addr line_addr = (addr & ~(Addr(linesize - 1))); 158 if (is_secure) { 159 line_addr |= LineSecure; 160 } 161 assert(reqLookupResult->first == line_addr); 162 if (will_retry) { 163 // Undo any changes made in lookupRequest to the snoop filter 164 // entry if the request will come again. retryItem holds 165 // the previous value of the snoopfilter entry. 166 reqLookupResult->second = retryItem; 167 168 DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n", 169 __func__, retryItem.requested, retryItem.holder); 170 } 171 172 eraseIfNullEntry(reqLookupResult); 173 } 174} 175 176std::pair<SnoopFilter::SnoopList, Cycles> 177SnoopFilter::lookupSnoop(const Packet* cpkt) 178{ 179 DPRINTF(SnoopFilter, "%s: packet %s\n", __func__, cpkt->print()); 180 181 assert(cpkt->isRequest()); 182 183 Addr line_addr = cpkt->getBlockAddr(linesize); 184 if (cpkt->isSecure()) { 185 line_addr |= LineSecure; 186 } 187 auto sf_it = cachedLocations.find(line_addr); 188 bool is_hit = (sf_it != cachedLocations.end()); 189 190 panic_if(!is_hit && (cachedLocations.size() >= maxEntryCount), 191 "snoop filter exceeded capacity of %d cache blocks\n", 192 maxEntryCount); 193 194 // If the snoop filter has no entry, simply return a NULL 195 // portlist, there is no point creating an entry only to remove it 196 // later 197 if (!is_hit) 198 return snoopDown(lookupLatency); 199 200 SnoopItem& sf_item = sf_it->second; 201 202 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 203 __func__, sf_item.requested, sf_item.holder); 204 205 SnoopMask interested = (sf_item.holder | sf_item.requested); 206 207 totSnoops++; 208 // Single bit set -> value is a power of two 209 if (isPow2(interested)) 210 hitSingleSnoops++; 211 else 212 hitMultiSnoops++; 213 214 // ReadEx and Writes require both invalidation and exlusivity, while reads 215 // require neither. Writebacks on the other hand require exclusivity but 216 // not the invalidation. Previously Writebacks did not generate upward 217 // snoops so this was never an aissue. Now that Writebacks generate snoops 218 // we need to special case for Writebacks. 219 assert(cpkt->isWriteback() || cpkt->req->isUncacheable() || 220 (cpkt->isInvalidate() == cpkt->needsWritable())); 221 if (cpkt->isInvalidate() && !sf_item.requested) { 222 // Early clear of the holder, if no other request is currently going on 223 // @todo: This should possibly be updated even though we do not filter 224 // upward snoops 225 sf_item.holder = 0; 226 } 227 228 eraseIfNullEntry(sf_it); 229 DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n", 230 __func__, sf_item.requested, sf_item.holder, interested); 231 232 return snoopSelected(maskToPortList(interested), lookupLatency); 233} 234 235void 236SnoopFilter::updateSnoopResponse(const Packet* cpkt, 237 const SlavePort& rsp_port, 238 const SlavePort& req_port) 239{ 240 DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n", 241 __func__, rsp_port.name(), req_port.name(), cpkt->print()); 242 243 assert(cpkt->isResponse()); 244 assert(cpkt->cacheResponding()); 245 246 // if this snoop response is due to an uncacheable request, or is 247 // being turned into a normal response, there is nothing more to 248 // do 249 if (cpkt->req->isUncacheable() || !req_port.isSnooping()) { 250 return; 251 } 252 253 Addr line_addr = cpkt->getBlockAddr(linesize); 254 if (cpkt->isSecure()) { 255 line_addr |= LineSecure; 256 } 257 SnoopMask rsp_mask = portToMask(rsp_port); 258 SnoopMask req_mask = portToMask(req_port); 259 SnoopItem& sf_item = cachedLocations[line_addr]; 260 261 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 262 __func__, sf_item.requested, sf_item.holder); 263 264 // The source should have the line 265 panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\ 266 "the line\n", sf_item.requested, sf_item.holder); 267 268 // The destination should have had a request in 269 panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\ 270 "the original request\n", sf_item.requested, sf_item.holder); 271 272 // If the snoop response has no sharers the line is passed in 273 // Modified state, and we know that there are no other copies, or 274 // they will all be invalidated imminently 275 if (!cpkt->hasSharers()) { 276 DPRINTF(SnoopFilter, 277 "%s: dropping %x because non-shared snoop " 278 "response SF val: %x.%x\n", __func__, rsp_mask, 279 sf_item.requested, sf_item.holder); 280 sf_item.holder = 0; 281 } 282 assert(!cpkt->isWriteback()); 283 // @todo Deal with invalidating responses 284 sf_item.holder |= req_mask; 285 sf_item.requested &= ~req_mask; 286 assert(sf_item.requested | sf_item.holder); 287 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 288 __func__, sf_item.requested, sf_item.holder); 289} 290 291void 292SnoopFilter::updateSnoopForward(const Packet* cpkt, 293 const SlavePort& rsp_port, const MasterPort& req_port) 294{ 295 DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n", 296 __func__, rsp_port.name(), req_port.name(), cpkt->print()); 297 298 assert(cpkt->isResponse()); 299 assert(cpkt->cacheResponding()); 300 301 Addr line_addr = cpkt->getBlockAddr(linesize); 302 if (cpkt->isSecure()) { 303 line_addr |= LineSecure; 304 } 305 auto sf_it = cachedLocations.find(line_addr); 306 bool is_hit = sf_it != cachedLocations.end(); 307 308 // Nothing to do if it is not a hit 309 if (!is_hit) 310 return; 311 312 SnoopItem& sf_item = sf_it->second; 313 314 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 315 __func__, sf_item.requested, sf_item.holder); 316 317 // If the snoop response has no sharers the line is passed in 318 // Modified state, and we know that there are no other copies, or 319 // they will all be invalidated imminently 320 if (!cpkt->hasSharers()) { 321 sf_item.holder = 0; 322 } 323 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 324 __func__, sf_item.requested, sf_item.holder); 325 eraseIfNullEntry(sf_it); 326 327} 328 329void 330SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port) 331{ 332 DPRINTF(SnoopFilter, "%s: src %s packet %s\n", 333 __func__, slave_port.name(), cpkt->print()); 334 335 assert(cpkt->isResponse()); 336 337 // we only allocate if the packet actually came from a cache, but 338 // start by checking if the port is snooping 339 if (cpkt->req->isUncacheable() || !slave_port.isSnooping()) 340 return; 341 342 // next check if we actually allocated an entry 343 Addr line_addr = cpkt->getBlockAddr(linesize); 344 if (cpkt->isSecure()) { 345 line_addr |= LineSecure; 346 } 347 auto sf_it = cachedLocations.find(line_addr); 348 if (sf_it == cachedLocations.end()) 349 return; 350 351 SnoopMask slave_mask = portToMask(slave_port); 352 SnoopItem& sf_item = sf_it->second; 353 354 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 355 __func__, sf_item.requested, sf_item.holder); 356 357 // Make sure we have seen the actual request, too 358 panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\ 359 "request bit\n", sf_item.requested, sf_item.holder); 360 361 // Update the residency of the cache line. If the response has no 362 // sharers we know that the line has been invalidated in all 363 // branches that are not where we are responding to. 364 if (!cpkt->hasSharers()) 365 sf_item.holder = 0; 366 sf_item.holder |= slave_mask; 367 sf_item.requested &= ~slave_mask; 368 assert(sf_item.holder | sf_item.requested); 369 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 370 __func__, sf_item.requested, sf_item.holder); 371} 372 373void 374SnoopFilter::regStats() 375{ 376 SimObject::regStats(); 377 378 totRequests 379 .name(name() + ".tot_requests") 380 .desc("Total number of requests made to the snoop filter."); 381 382 hitSingleRequests 383 .name(name() + ".hit_single_requests") 384 .desc("Number of requests hitting in the snoop filter with a single "\ 385 "holder of the requested data."); 386 387 hitMultiRequests 388 .name(name() + ".hit_multi_requests") 389 .desc("Number of requests hitting in the snoop filter with multiple "\ 390 "(>1) holders of the requested data."); 391 392 totSnoops 393 .name(name() + ".tot_snoops") 394 .desc("Total number of snoops made to the snoop filter."); 395 396 hitSingleSnoops 397 .name(name() + ".hit_single_snoops") 398 .desc("Number of snoops hitting in the snoop filter with a single "\ 399 "holder of the requested data."); 400 401 hitMultiSnoops 402 .name(name() + ".hit_multi_snoops") 403 .desc("Number of snoops hitting in the snoop filter with multiple "\ 404 "(>1) holders of the requested data."); 405} 406 407SnoopFilter * 408SnoopFilterParams::create() 409{ 410 return new SnoopFilter(this); 411} 412