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