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