1/* 2 * Copyright (c) 2013-2015 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 <stephan.diestelhorst@arm.com> 38 */ 39 40/** 41 * @file 42 * Definition 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 // Ultimately we should check if the packet came from an 69 // allocating source, not just if the port is snooping 70 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping(); 71 Addr line_addr = cpkt->getBlockAddr(linesize); 72 SnoopMask req_port = portToMask(slave_port);
| 1/* 2 * Copyright (c) 2013-2015 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 <stephan.diestelhorst@arm.com> 38 */ 39 40/** 41 * @file 42 * Definition 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 // Ultimately we should check if the packet came from an 69 // allocating source, not just if the port is snooping 70 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping(); 71 Addr line_addr = cpkt->getBlockAddr(linesize); 72 SnoopMask req_port = portToMask(slave_port);
|
73 auto sf_it = cachedLocations.find(line_addr); 74 bool is_hit = (sf_it != cachedLocations.end());
| 73 reqLookupResult = cachedLocations.find(line_addr); 74 bool is_hit = (reqLookupResult != cachedLocations.end());
|
75 76 // If the snoop filter has no entry, and we should not allocate, 77 // do not create a new snoop filter entry, simply return a NULL 78 // portlist. 79 if (!is_hit && !allocate) 80 return snoopDown(lookupLatency); 81
| 75 76 // If the snoop filter has no entry, and we should not allocate, 77 // do not create a new snoop filter entry, simply return a NULL 78 // portlist. 79 if (!is_hit && !allocate) 80 return snoopDown(lookupLatency); 81
|
82 // Create a new element through operator[] and modify in-place 83 SnoopItem& sf_item = is_hit ? sf_it->second : cachedLocations[line_addr];
| 82 // If no hit in snoop filter create a new element and update iterator 83 if (!is_hit) 84 reqLookupResult = cachedLocations.emplace(line_addr, SnoopItem()).first; 85 SnoopItem& sf_item = reqLookupResult->second;
|
84 SnoopMask interested = sf_item.holder | sf_item.requested; 85 86 // Store unmodified value of snoop filter item in temp storage in 87 // case we need to revert because of a send retry in 88 // updateRequest. 89 retryItem = sf_item; 90 91 totRequests++; 92 if (is_hit) { 93 // Single bit set -> value is a power of two 94 if (isPow2(interested)) 95 hitSingleRequests++; 96 else 97 hitMultiRequests++; 98 } 99 100 DPRINTF(SnoopFilter, "%s: SF value %x.%x\n", 101 __func__, sf_item.requested, sf_item.holder); 102 103 // If we are not allocating, we are done 104 if (!allocate) 105 return snoopSelected(maskToPortList(interested & ~req_port), 106 lookupLatency); 107 108 if (cpkt->needsResponse()) { 109 if (!cpkt->memInhibitAsserted()) { 110 // Max one request per address per port 111 panic_if(sf_item.requested & req_port, "double request :( " \ 112 "SF value %x.%x\n", sf_item.requested, sf_item.holder); 113 114 // Mark in-flight requests to distinguish later on 115 sf_item.requested |= req_port; 116 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 117 __func__, sf_item.requested, sf_item.holder); 118 } else { 119 // NOTE: The memInhibit might have been asserted by a cache closer 120 // to the CPU, already -> the response will not be seen by this 121 // filter -> we do not need to keep the in-flight request, but make 122 // sure that we know that that cluster has a copy 123 panic_if(!(sf_item.holder & req_port), "Need to hold the value!"); 124 DPRINTF(SnoopFilter, 125 "%s: not marking request. SF value %x.%x\n", 126 __func__, sf_item.requested, sf_item.holder); 127 } 128 } else { // if (!cpkt->needsResponse()) 129 assert(cpkt->evictingBlock()); 130 // make sure that the sender actually had the line 131 panic_if(!(sf_item.holder & req_port), "requester %x is not a " \ 132 "holder :( SF value %x.%x\n", req_port, 133 sf_item.requested, sf_item.holder); 134 // CleanEvicts and Writebacks -> the sender and all caches above 135 // it may not have the line anymore. 136 if (!cpkt->isBlockCached()) { 137 sf_item.holder &= ~req_port; 138 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 139 __func__, sf_item.requested, sf_item.holder); 140 } 141 } 142 143 return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency); 144} 145 146void
| 86 SnoopMask interested = sf_item.holder | sf_item.requested; 87 88 // Store unmodified value of snoop filter item in temp storage in 89 // case we need to revert because of a send retry in 90 // updateRequest. 91 retryItem = sf_item; 92 93 totRequests++; 94 if (is_hit) { 95 // Single bit set -> value is a power of two 96 if (isPow2(interested)) 97 hitSingleRequests++; 98 else 99 hitMultiRequests++; 100 } 101 102 DPRINTF(SnoopFilter, "%s: SF value %x.%x\n", 103 __func__, sf_item.requested, sf_item.holder); 104 105 // If we are not allocating, we are done 106 if (!allocate) 107 return snoopSelected(maskToPortList(interested & ~req_port), 108 lookupLatency); 109 110 if (cpkt->needsResponse()) { 111 if (!cpkt->memInhibitAsserted()) { 112 // Max one request per address per port 113 panic_if(sf_item.requested & req_port, "double request :( " \ 114 "SF value %x.%x\n", sf_item.requested, sf_item.holder); 115 116 // Mark in-flight requests to distinguish later on 117 sf_item.requested |= req_port; 118 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 119 __func__, sf_item.requested, sf_item.holder); 120 } else { 121 // NOTE: The memInhibit might have been asserted by a cache closer 122 // to the CPU, already -> the response will not be seen by this 123 // filter -> we do not need to keep the in-flight request, but make 124 // sure that we know that that cluster has a copy 125 panic_if(!(sf_item.holder & req_port), "Need to hold the value!"); 126 DPRINTF(SnoopFilter, 127 "%s: not marking request. SF value %x.%x\n", 128 __func__, sf_item.requested, sf_item.holder); 129 } 130 } else { // if (!cpkt->needsResponse()) 131 assert(cpkt->evictingBlock()); 132 // make sure that the sender actually had the line 133 panic_if(!(sf_item.holder & req_port), "requester %x is not a " \ 134 "holder :( SF value %x.%x\n", req_port, 135 sf_item.requested, sf_item.holder); 136 // CleanEvicts and Writebacks -> the sender and all caches above 137 // it may not have the line anymore. 138 if (!cpkt->isBlockCached()) { 139 sf_item.holder &= ~req_port; 140 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 141 __func__, sf_item.requested, sf_item.holder); 142 } 143 } 144 145 return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency); 146} 147 148void
|
147SnoopFilter::updateRequest(const Packet* cpkt, const SlavePort& slave_port, 148 bool will_retry)
| 149SnoopFilter::finishRequest(bool will_retry, const Packet* cpkt)
|
149{
| 150{
|
150 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n", 151 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
| 151 if (reqLookupResult != cachedLocations.end()) { 152 // since we rely on the caller, do a basic check to ensure 153 // that finishRequest is being called following lookupRequest 154 assert(reqLookupResult->first == cpkt->getBlockAddr(linesize)); 155 if (will_retry) { 156 // Undo any changes made in lookupRequest to the snoop filter 157 // entry if the request will come again. retryItem holds 158 // the previous value of the snoopfilter entry. 159 reqLookupResult->second = retryItem;
|
152
| 160
|
153 // Ultimately we should check if the packet came from an 154 // allocating source, not just if the port is snooping 155 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping(); 156 if (!allocate) 157 return;
| 161 DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n", 162 __func__, retryItem.requested, retryItem.holder); 163 }
|
158
| 164
|
159 Addr line_addr = cpkt->getBlockAddr(linesize); 160 auto sf_it = cachedLocations.find(line_addr); 161 assert(sf_it != cachedLocations.end()); 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 sf_it->second = retryItem; 167 168 DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n", 169 __func__, retryItem.requested, retryItem.holder);
| 165 eraseIfNullEntry(reqLookupResult);
|
170 }
| 166 }
|
171 172 eraseIfNullEntry(sf_it);
| |
173} 174 175std::pair<SnoopFilter::SnoopList, Cycles> 176SnoopFilter::lookupSnoop(const Packet* cpkt) 177{ 178 DPRINTF(SnoopFilter, "%s: packet addr 0x%x cmd %s\n", 179 __func__, cpkt->getAddr(), cpkt->cmdString()); 180 181 assert(cpkt->isRequest()); 182 183 // Broadcast / filter upward snoops 184 const bool filter_upward = true; // @todo: Make configurable 185 186 if (!filter_upward) 187 return snoopAll(lookupLatency); 188 189 Addr line_addr = cpkt->getBlockAddr(linesize); 190 auto sf_it = cachedLocations.find(line_addr); 191 bool is_hit = (sf_it != cachedLocations.end()); 192 193 // If the snoop filter has no entry and its an uncacheable 194 // request, do not create a new snoop filter entry, simply return 195 // a NULL portlist. 196 if (!is_hit && cpkt->req->isUncacheable()) 197 return snoopDown(lookupLatency); 198 199 // If no hit in snoop filter create a new element and update iterator 200 if (!is_hit) 201 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first; 202 SnoopItem& sf_item = sf_it->second; 203 204 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 205 __func__, sf_item.requested, sf_item.holder); 206 207 SnoopMask interested = (sf_item.holder | sf_item.requested); 208 209 totSnoops++; 210 if (is_hit) { 211 // Single bit set -> value is a power of two 212 if (isPow2(interested)) 213 hitSingleSnoops++; 214 else 215 hitMultiSnoops++; 216 } 217 // ReadEx and Writes require both invalidation and exlusivity, while reads 218 // require neither. Writebacks on the other hand require exclusivity but 219 // not the invalidation. Previously Writebacks did not generate upward 220 // snoops so this was never an aissue. Now that Writebacks generate snoops 221 // we need to special case for Writebacks. 222 assert(cpkt->cmd == MemCmd::Writeback || cpkt->req->isUncacheable() || 223 (cpkt->isInvalidate() == cpkt->needsExclusive())); 224 if (cpkt->isInvalidate() && !sf_item.requested) { 225 // Early clear of the holder, if no other request is currently going on 226 // @todo: This should possibly be updated even though we do not filter 227 // upward snoops 228 sf_item.holder = 0; 229 } 230 231 eraseIfNullEntry(sf_it); 232 DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n", 233 __func__, sf_item.requested, sf_item.holder, interested); 234 235 return snoopSelected(maskToPortList(interested), lookupLatency); 236} 237 238void 239SnoopFilter::updateSnoopResponse(const Packet* cpkt, 240 const SlavePort& rsp_port, 241 const SlavePort& req_port) 242{ 243 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n", 244 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(), 245 cpkt->cmdString()); 246 247 assert(cpkt->isResponse()); 248 assert(cpkt->memInhibitAsserted()); 249 250 // Ultimately we should check if the packet came from an 251 // allocating source, not just if the port is snooping 252 bool allocate = !cpkt->req->isUncacheable() && req_port.isSnooping(); 253 if (!allocate) 254 return; 255 256 Addr line_addr = cpkt->getBlockAddr(linesize); 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 // Update the residency of the cache line. 273 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) { 274 DPRINTF(SnoopFilter, "%s: dropping %x because needs: %i shared: %i "\ 275 "SF val: %x.%x\n", __func__, rsp_mask, 276 cpkt->needsExclusive(), cpkt->sharedAsserted(), 277 sf_item.requested, sf_item.holder); 278 279 sf_item.holder &= ~rsp_mask; 280 // The snoop filter does not see any ACKs from non-responding sharers 281 // that have been invalidated :( So below assert would be nice, but.. 282 //assert(sf_item.holder == 0); 283 sf_item.holder = 0; 284 } 285 assert(cpkt->cmd != MemCmd::Writeback); 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 Addr line_addr = cpkt->getBlockAddr(linesize); 302 auto sf_it = cachedLocations.find(line_addr); 303 if (sf_it == cachedLocations.end()) 304 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first; 305 SnoopItem& sf_item = sf_it->second; 306 SnoopMask rsp_mask M5_VAR_USED = portToMask(rsp_port); 307 308 assert(cpkt->isResponse()); 309 assert(cpkt->memInhibitAsserted()); 310 311 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 312 __func__, sf_item.requested, sf_item.holder); 313 314 // Remote (to this snoop filter) snoops update the filter already when they 315 // arrive from below, because we may not see any response. 316 if (cpkt->needsExclusive()) { 317 // If the request to this snoop response hit an in-flight transaction, 318 // the holder was not reset -> no assertion & do that here, now! 319 //assert(sf_item.holder == 0); 320 sf_item.holder = 0; 321 } 322 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 323 __func__, sf_item.requested, sf_item.holder); 324 eraseIfNullEntry(sf_it); 325} 326 327void 328SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port) 329{ 330 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n", 331 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString()); 332 333 assert(cpkt->isResponse()); 334 335 // Ultimately we should check if the packet came from an 336 // allocating source, not just if the port is snooping 337 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping(); 338 if (!allocate) 339 return; 340 341 Addr line_addr = cpkt->getBlockAddr(linesize); 342 SnoopMask slave_mask = portToMask(slave_port); 343 SnoopItem& sf_item = cachedLocations[line_addr]; 344 345 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 346 __func__, sf_item.requested, sf_item.holder); 347 348 // Make sure we have seen the actual request, too 349 panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\ 350 "request bit\n", sf_item.requested, sf_item.holder); 351 352 // Update the residency of the cache line. Here we assume that the 353 // line has been zapped in all caches that are not the responder. 354 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) 355 sf_item.holder = 0; 356 sf_item.holder |= slave_mask; 357 sf_item.requested &= ~slave_mask; 358 assert(sf_item.holder | sf_item.requested); 359 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 360 __func__, sf_item.requested, sf_item.holder); 361} 362 363void 364SnoopFilter::regStats() 365{ 366 totRequests 367 .name(name() + ".tot_requests") 368 .desc("Total number of requests made to the snoop filter."); 369 370 hitSingleRequests 371 .name(name() + ".hit_single_requests") 372 .desc("Number of requests hitting in the snoop filter with a single "\ 373 "holder of the requested data."); 374 375 hitMultiRequests 376 .name(name() + ".hit_multi_requests") 377 .desc("Number of requests hitting in the snoop filter with multiple "\ 378 "(>1) holders of the requested data."); 379 380 totSnoops 381 .name(name() + ".tot_snoops") 382 .desc("Total number of snoops made to the snoop filter."); 383 384 hitSingleSnoops 385 .name(name() + ".hit_single_snoops") 386 .desc("Number of snoops hitting in the snoop filter with a single "\ 387 "holder of the requested data."); 388 389 hitMultiSnoops 390 .name(name() + ".hit_multi_snoops") 391 .desc("Number of snoops hitting in the snoop filter with multiple "\ 392 "(>1) holders of the requested data."); 393} 394 395SnoopFilter * 396SnoopFilterParams::create() 397{ 398 return new SnoopFilter(this); 399}
| 167} 168 169std::pair<SnoopFilter::SnoopList, Cycles> 170SnoopFilter::lookupSnoop(const Packet* cpkt) 171{ 172 DPRINTF(SnoopFilter, "%s: packet addr 0x%x cmd %s\n", 173 __func__, cpkt->getAddr(), cpkt->cmdString()); 174 175 assert(cpkt->isRequest()); 176 177 // Broadcast / filter upward snoops 178 const bool filter_upward = true; // @todo: Make configurable 179 180 if (!filter_upward) 181 return snoopAll(lookupLatency); 182 183 Addr line_addr = cpkt->getBlockAddr(linesize); 184 auto sf_it = cachedLocations.find(line_addr); 185 bool is_hit = (sf_it != cachedLocations.end()); 186 187 // If the snoop filter has no entry and its an uncacheable 188 // request, do not create a new snoop filter entry, simply return 189 // a NULL portlist. 190 if (!is_hit && cpkt->req->isUncacheable()) 191 return snoopDown(lookupLatency); 192 193 // If no hit in snoop filter create a new element and update iterator 194 if (!is_hit) 195 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first; 196 SnoopItem& sf_item = sf_it->second; 197 198 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 199 __func__, sf_item.requested, sf_item.holder); 200 201 SnoopMask interested = (sf_item.holder | sf_item.requested); 202 203 totSnoops++; 204 if (is_hit) { 205 // Single bit set -> value is a power of two 206 if (isPow2(interested)) 207 hitSingleSnoops++; 208 else 209 hitMultiSnoops++; 210 } 211 // ReadEx and Writes require both invalidation and exlusivity, while reads 212 // require neither. Writebacks on the other hand require exclusivity but 213 // not the invalidation. Previously Writebacks did not generate upward 214 // snoops so this was never an aissue. Now that Writebacks generate snoops 215 // we need to special case for Writebacks. 216 assert(cpkt->cmd == MemCmd::Writeback || cpkt->req->isUncacheable() || 217 (cpkt->isInvalidate() == cpkt->needsExclusive())); 218 if (cpkt->isInvalidate() && !sf_item.requested) { 219 // Early clear of the holder, if no other request is currently going on 220 // @todo: This should possibly be updated even though we do not filter 221 // upward snoops 222 sf_item.holder = 0; 223 } 224 225 eraseIfNullEntry(sf_it); 226 DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n", 227 __func__, sf_item.requested, sf_item.holder, interested); 228 229 return snoopSelected(maskToPortList(interested), lookupLatency); 230} 231 232void 233SnoopFilter::updateSnoopResponse(const Packet* cpkt, 234 const SlavePort& rsp_port, 235 const SlavePort& req_port) 236{ 237 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n", 238 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(), 239 cpkt->cmdString()); 240 241 assert(cpkt->isResponse()); 242 assert(cpkt->memInhibitAsserted()); 243 244 // Ultimately we should check if the packet came from an 245 // allocating source, not just if the port is snooping 246 bool allocate = !cpkt->req->isUncacheable() && req_port.isSnooping(); 247 if (!allocate) 248 return; 249 250 Addr line_addr = cpkt->getBlockAddr(linesize); 251 SnoopMask rsp_mask = portToMask(rsp_port); 252 SnoopMask req_mask = portToMask(req_port); 253 SnoopItem& sf_item = cachedLocations[line_addr]; 254 255 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 256 __func__, sf_item.requested, sf_item.holder); 257 258 // The source should have the line 259 panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\ 260 "the line\n", sf_item.requested, sf_item.holder); 261 262 // The destination should have had a request in 263 panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\ 264 "the original request\n", sf_item.requested, sf_item.holder); 265 266 // Update the residency of the cache line. 267 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) { 268 DPRINTF(SnoopFilter, "%s: dropping %x because needs: %i shared: %i "\ 269 "SF val: %x.%x\n", __func__, rsp_mask, 270 cpkt->needsExclusive(), cpkt->sharedAsserted(), 271 sf_item.requested, sf_item.holder); 272 273 sf_item.holder &= ~rsp_mask; 274 // The snoop filter does not see any ACKs from non-responding sharers 275 // that have been invalidated :( So below assert would be nice, but.. 276 //assert(sf_item.holder == 0); 277 sf_item.holder = 0; 278 } 279 assert(cpkt->cmd != MemCmd::Writeback); 280 sf_item.holder |= req_mask; 281 sf_item.requested &= ~req_mask; 282 assert(sf_item.requested | sf_item.holder); 283 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 284 __func__, sf_item.requested, sf_item.holder); 285} 286 287void 288SnoopFilter::updateSnoopForward(const Packet* cpkt, 289 const SlavePort& rsp_port, const MasterPort& req_port) 290{ 291 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n", 292 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(), 293 cpkt->cmdString()); 294 295 Addr line_addr = cpkt->getBlockAddr(linesize); 296 auto sf_it = cachedLocations.find(line_addr); 297 if (sf_it == cachedLocations.end()) 298 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first; 299 SnoopItem& sf_item = sf_it->second; 300 SnoopMask rsp_mask M5_VAR_USED = portToMask(rsp_port); 301 302 assert(cpkt->isResponse()); 303 assert(cpkt->memInhibitAsserted()); 304 305 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 306 __func__, sf_item.requested, sf_item.holder); 307 308 // Remote (to this snoop filter) snoops update the filter already when they 309 // arrive from below, because we may not see any response. 310 if (cpkt->needsExclusive()) { 311 // If the request to this snoop response hit an in-flight transaction, 312 // the holder was not reset -> no assertion & do that here, now! 313 //assert(sf_item.holder == 0); 314 sf_item.holder = 0; 315 } 316 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 317 __func__, sf_item.requested, sf_item.holder); 318 eraseIfNullEntry(sf_it); 319} 320 321void 322SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port) 323{ 324 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n", 325 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString()); 326 327 assert(cpkt->isResponse()); 328 329 // Ultimately we should check if the packet came from an 330 // allocating source, not just if the port is snooping 331 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping(); 332 if (!allocate) 333 return; 334 335 Addr line_addr = cpkt->getBlockAddr(linesize); 336 SnoopMask slave_mask = portToMask(slave_port); 337 SnoopItem& sf_item = cachedLocations[line_addr]; 338 339 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 340 __func__, sf_item.requested, sf_item.holder); 341 342 // Make sure we have seen the actual request, too 343 panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\ 344 "request bit\n", sf_item.requested, sf_item.holder); 345 346 // Update the residency of the cache line. Here we assume that the 347 // line has been zapped in all caches that are not the responder. 348 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) 349 sf_item.holder = 0; 350 sf_item.holder |= slave_mask; 351 sf_item.requested &= ~slave_mask; 352 assert(sf_item.holder | sf_item.requested); 353 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 354 __func__, sf_item.requested, sf_item.holder); 355} 356 357void 358SnoopFilter::regStats() 359{ 360 totRequests 361 .name(name() + ".tot_requests") 362 .desc("Total number of requests made to the snoop filter."); 363 364 hitSingleRequests 365 .name(name() + ".hit_single_requests") 366 .desc("Number of requests hitting in the snoop filter with a single "\ 367 "holder of the requested data."); 368 369 hitMultiRequests 370 .name(name() + ".hit_multi_requests") 371 .desc("Number of requests hitting in the snoop filter with multiple "\ 372 "(>1) holders of the requested data."); 373 374 totSnoops 375 .name(name() + ".tot_snoops") 376 .desc("Total number of snoops made to the snoop filter."); 377 378 hitSingleSnoops 379 .name(name() + ".hit_single_snoops") 380 .desc("Number of snoops hitting in the snoop filter with a single "\ 381 "holder of the requested data."); 382 383 hitMultiSnoops 384 .name(name() + ".hit_multi_snoops") 385 .desc("Number of snoops hitting in the snoop filter with multiple "\ 386 "(>1) holders of the requested data."); 387} 388 389SnoopFilter * 390SnoopFilterParams::create() 391{ 392 return new SnoopFilter(this); 393}
|