snoop_filter.cc revision 11605
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: packet src %s addr 0x%x cmd %s\n", 66 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString()); 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 addr 0x%x cmd %s\n", 180 __func__, cpkt->getAddr(), cpkt->cmdString()); 181 182 assert(cpkt->isRequest()); 183 184 Addr line_addr = cpkt->getBlockAddr(linesize); 185 if (cpkt->isSecure()) { 186 line_addr |= LineSecure; 187 } 188 auto sf_it = cachedLocations.find(line_addr); 189 bool is_hit = (sf_it != cachedLocations.end()); 190 191 panic_if(!is_hit && (cachedLocations.size() >= maxEntryCount), 192 "snoop filter exceeded capacity of %d cache blocks\n", 193 maxEntryCount); 194 195 // If the snoop filter has no entry, simply return a NULL 196 // portlist, there is no point creating an entry only to remove it 197 // later 198 if (!is_hit) 199 return snoopDown(lookupLatency); 200 201 SnoopItem& sf_item = sf_it->second; 202 203 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 204 __func__, sf_item.requested, sf_item.holder); 205 206 SnoopMask interested = (sf_item.holder | sf_item.requested); 207 208 totSnoops++; 209 // Single bit set -> value is a power of two 210 if (isPow2(interested)) 211 hitSingleSnoops++; 212 else 213 hitMultiSnoops++; 214 215 // ReadEx and Writes require both invalidation and exlusivity, while reads 216 // require neither. Writebacks on the other hand require exclusivity but 217 // not the invalidation. Previously Writebacks did not generate upward 218 // snoops so this was never an aissue. Now that Writebacks generate snoops 219 // we need to special case for Writebacks. 220 assert(cpkt->isWriteback() || cpkt->req->isUncacheable() || 221 (cpkt->isInvalidate() == cpkt->needsWritable())); 222 if (cpkt->isInvalidate() && !sf_item.requested) { 223 // Early clear of the holder, if no other request is currently going on 224 // @todo: This should possibly be updated even though we do not filter 225 // upward snoops 226 sf_item.holder = 0; 227 } 228 229 eraseIfNullEntry(sf_it); 230 DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n", 231 __func__, sf_item.requested, sf_item.holder, interested); 232 233 return snoopSelected(maskToPortList(interested), lookupLatency); 234} 235 236void 237SnoopFilter::updateSnoopResponse(const Packet* cpkt, 238 const SlavePort& rsp_port, 239 const SlavePort& req_port) 240{ 241 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n", 242 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(), 243 cpkt->cmdString()); 244 245 assert(cpkt->isResponse()); 246 assert(cpkt->cacheResponding()); 247 248 // if this snoop response is due to an uncacheable request, or is 249 // being turned into a normal response, there is nothing more to 250 // do 251 if (cpkt->req->isUncacheable() || !req_port.isSnooping()) { 252 return; 253 } 254 255 Addr line_addr = cpkt->getBlockAddr(linesize); 256 if (cpkt->isSecure()) { 257 line_addr |= LineSecure; 258 } 259 SnoopMask rsp_mask = portToMask(rsp_port); 260 SnoopMask req_mask = portToMask(req_port); 261 SnoopItem& sf_item = cachedLocations[line_addr]; 262 263 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 264 __func__, sf_item.requested, sf_item.holder); 265 266 // The source should have the line 267 panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\ 268 "the line\n", sf_item.requested, sf_item.holder); 269 270 // The destination should have had a request in 271 panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\ 272 "the original request\n", sf_item.requested, sf_item.holder); 273 274 // If the snoop response has no sharers the line is passed in 275 // Modified state, and we know that there are no other copies, or 276 // they will all be invalidated imminently 277 if (!cpkt->hasSharers()) { 278 DPRINTF(SnoopFilter, 279 "%s: dropping %x because non-shared snoop " 280 "response SF val: %x.%x\n", __func__, rsp_mask, 281 sf_item.requested, sf_item.holder); 282 sf_item.holder = 0; 283 } 284 assert(!cpkt->isWriteback()); 285 // @todo Deal with invalidating responses 286 sf_item.holder |= req_mask; 287 sf_item.requested &= ~req_mask; 288 assert(sf_item.requested | sf_item.holder); 289 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 290 __func__, sf_item.requested, sf_item.holder); 291} 292 293void 294SnoopFilter::updateSnoopForward(const Packet* cpkt, 295 const SlavePort& rsp_port, const MasterPort& req_port) 296{ 297 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n", 298 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(), 299 cpkt->cmdString()); 300 301 assert(cpkt->isResponse()); 302 assert(cpkt->cacheResponding()); 303 304 Addr line_addr = cpkt->getBlockAddr(linesize); 305 if (cpkt->isSecure()) { 306 line_addr |= LineSecure; 307 } 308 auto sf_it = cachedLocations.find(line_addr); 309 bool is_hit = sf_it != cachedLocations.end(); 310 311 // Nothing to do if it is not a hit 312 if (!is_hit) 313 return; 314 315 SnoopItem& sf_item = sf_it->second; 316 317 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 318 __func__, sf_item.requested, sf_item.holder); 319 320 // If the snoop response has no sharers the line is passed in 321 // Modified state, and we know that there are no other copies, or 322 // they will all be invalidated imminently 323 if (!cpkt->hasSharers()) { 324 sf_item.holder = 0; 325 } 326 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 327 __func__, sf_item.requested, sf_item.holder); 328 eraseIfNullEntry(sf_it); 329 330} 331 332void 333SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port) 334{ 335 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n", 336 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString()); 337 338 assert(cpkt->isResponse()); 339 340 // we only allocate if the packet actually came from a cache, but 341 // start by checking if the port is snooping 342 if (cpkt->req->isUncacheable() || !slave_port.isSnooping()) 343 return; 344 345 // next check if we actually allocated an entry 346 Addr line_addr = cpkt->getBlockAddr(linesize); 347 if (cpkt->isSecure()) { 348 line_addr |= LineSecure; 349 } 350 auto sf_it = cachedLocations.find(line_addr); 351 if (sf_it == cachedLocations.end()) 352 return; 353 354 SnoopMask slave_mask = portToMask(slave_port); 355 SnoopItem& sf_item = sf_it->second; 356 357 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 358 __func__, sf_item.requested, sf_item.holder); 359 360 // Make sure we have seen the actual request, too 361 panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\ 362 "request bit\n", sf_item.requested, sf_item.holder); 363 364 // Update the residency of the cache line. If the response has no 365 // sharers we know that the line has been invalidated in all 366 // branches that are not where we are responding to. 367 if (!cpkt->hasSharers()) 368 sf_item.holder = 0; 369 sf_item.holder |= slave_mask; 370 sf_item.requested &= ~slave_mask; 371 assert(sf_item.holder | sf_item.requested); 372 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 373 __func__, sf_item.requested, sf_item.holder); 374} 375 376void 377SnoopFilter::regStats() 378{ 379 SimObject::regStats(); 380 381 totRequests 382 .name(name() + ".tot_requests") 383 .desc("Total number of requests made to the snoop filter."); 384 385 hitSingleRequests 386 .name(name() + ".hit_single_requests") 387 .desc("Number of requests hitting in the snoop filter with a single "\ 388 "holder of the requested data."); 389 390 hitMultiRequests 391 .name(name() + ".hit_multi_requests") 392 .desc("Number of requests hitting in the snoop filter with multiple "\ 393 "(>1) holders of the requested data."); 394 395 totSnoops 396 .name(name() + ".tot_snoops") 397 .desc("Total number of snoops made to the snoop filter."); 398 399 hitSingleSnoops 400 .name(name() + ".hit_single_snoops") 401 .desc("Number of snoops hitting in the snoop filter with a single "\ 402 "holder of the requested data."); 403 404 hitMultiSnoops 405 .name(name() + ".hit_multi_snoops") 406 .desc("Number of snoops hitting in the snoop filter with multiple "\ 407 "(>1) holders of the requested data."); 408} 409 410SnoopFilter * 411SnoopFilterParams::create() 412{ 413 return new SnoopFilter(this); 414} 415