snoop_filter.cc revision 10821
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 assert(0 == "Handle non-writeback, here"); 138 } 139 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 140 __func__, sf_item.requested, sf_item.holder); 141 } 142} 143 144std::pair<SnoopFilter::SnoopList, Cycles> 145SnoopFilter::lookupSnoop(const Packet* cpkt) 146{ 147 DPRINTF(SnoopFilter, "%s: packet addr 0x%x cmd %s\n", 148 __func__, cpkt->getAddr(), cpkt->cmdString()); 149 150 assert(cpkt->isRequest()); 151 152 // Broadcast / filter upward snoops 153 const bool filter_upward = true; // @todo: Make configurable 154 155 if (!filter_upward) 156 return snoopAll(lookupLatency); 157 158 Addr line_addr = cpkt->getAddr() & ~(linesize - 1); 159 auto sf_it = cachedLocations.find(line_addr); 160 bool is_hit = (sf_it != cachedLocations.end()); 161 // Create a new element through operator[] and modify in-place 162 SnoopItem& sf_item = is_hit ? sf_it->second : cachedLocations[line_addr]; 163 164 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 165 __func__, sf_item.requested, sf_item.holder); 166 167 SnoopMask interested = (sf_item.holder | sf_item.requested); 168 169 totSnoops++; 170 if (is_hit) { 171 // Single bit set -> value is a power of two 172 if (isPow2(interested)) 173 hitSingleSnoops++; 174 else 175 hitMultiSnoops++; 176 } 177 178 assert(cpkt->isInvalidate() == cpkt->needsExclusive()); 179 if (cpkt->isInvalidate() && !sf_item.requested) { 180 // Early clear of the holder, if no other request is currently going on 181 // @todo: This should possibly be updated even though we do not filter 182 // upward snoops 183 sf_item.holder = 0; 184 } 185 186 DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n", 187 __func__, sf_item.requested, sf_item.holder, interested); 188 189 return snoopSelected(maskToPortList(interested), lookupLatency); 190} 191 192void 193SnoopFilter::updateSnoopResponse(const Packet* cpkt, 194 const SlavePort& rsp_port, 195 const SlavePort& req_port) 196{ 197 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n", 198 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(), 199 cpkt->cmdString()); 200 201 assert(cpkt->isResponse()); 202 assert(cpkt->memInhibitAsserted()); 203 204 if (cpkt->req->isUncacheable()) 205 return; 206 207 Addr line_addr = cpkt->getAddr() & ~(linesize - 1); 208 SnoopMask rsp_mask = portToMask(rsp_port); 209 SnoopMask req_mask = portToMask(req_port); 210 SnoopItem& sf_item = cachedLocations[line_addr]; 211 212 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 213 __func__, sf_item.requested, sf_item.holder); 214 215 // The source should have the line 216 panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\ 217 "the line\n", sf_item.requested, sf_item.holder); 218 219 // The destination should have had a request in 220 panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\ 221 "the original request\n", sf_item.requested, sf_item.holder); 222 223 // Update the residency of the cache line. 224 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) { 225 DPRINTF(SnoopFilter, "%s: dropping %x because needs: %i shared: %i "\ 226 "SF val: %x.%x\n", __func__, rsp_mask, 227 cpkt->needsExclusive(), cpkt->sharedAsserted(), 228 sf_item.requested, sf_item.holder); 229 230 sf_item.holder &= ~rsp_mask; 231 // The snoop filter does not see any ACKs from non-responding sharers 232 // that have been invalidated :( So below assert would be nice, but.. 233 //assert(sf_item.holder == 0); 234 sf_item.holder = 0; 235 } 236 assert(cpkt->cmd != MemCmd::Writeback); 237 sf_item.holder |= req_mask; 238 sf_item.requested &= ~req_mask; 239 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 240 __func__, sf_item.requested, sf_item.holder); 241} 242 243void 244SnoopFilter::updateSnoopForward(const Packet* cpkt, 245 const SlavePort& rsp_port, const MasterPort& req_port) 246{ 247 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n", 248 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(), 249 cpkt->cmdString()); 250 251 Addr line_addr = cpkt->getAddr() & ~(linesize - 1); 252 SnoopItem& sf_item = cachedLocations[line_addr]; 253 SnoopMask rsp_mask M5_VAR_USED = portToMask(rsp_port); 254 255 assert(cpkt->isResponse()); 256 assert(cpkt->memInhibitAsserted()); 257 258 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 259 __func__, sf_item.requested, sf_item.holder); 260 261 // Remote (to this snoop filter) snoops update the filter already when they 262 // arrive from below, because we may not see any response. 263 if (cpkt->needsExclusive()) { 264 // If the request to this snoop response hit an in-flight transaction, 265 // the holder was not reset -> no assertion & do that here, now! 266 //assert(sf_item.holder == 0); 267 sf_item.holder = 0; 268 } 269 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 270 __func__, sf_item.requested, sf_item.holder); 271} 272 273void 274SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port) 275{ 276 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n", 277 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString()); 278 279 assert(cpkt->isResponse()); 280 281 if (cpkt->req->isUncacheable()) 282 return; 283 284 Addr line_addr = cpkt->getAddr() & ~(linesize - 1); 285 SnoopMask slave_mask = portToMask(slave_port); 286 SnoopItem& sf_item = cachedLocations[line_addr]; 287 288 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n", 289 __func__, sf_item.requested, sf_item.holder); 290 291 // Make sure we have seen the actual request, too 292 panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\ 293 "request bit\n", sf_item.requested, sf_item.holder); 294 295 // Update the residency of the cache line. 296 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) 297 sf_item.holder = 0; 298 sf_item.holder |= slave_mask; 299 sf_item.requested &= ~slave_mask; 300 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n", 301 __func__, sf_item.requested, sf_item.holder); 302} 303 304void 305SnoopFilter::regStats() 306{ 307 totRequests 308 .name(name() + ".tot_requests") 309 .desc("Total number of requests made to the snoop filter."); 310 311 hitSingleRequests 312 .name(name() + ".hit_single_requests") 313 .desc("Number of requests hitting in the snoop filter with a single "\ 314 "holder of the requested data."); 315 316 hitMultiRequests 317 .name(name() + ".hit_multi_requests") 318 .desc("Number of requests hitting in the snoop filter with multiple "\ 319 "(>1) holders of the requested data."); 320 321 totSnoops 322 .name(name() + ".tot_snoops") 323 .desc("Total number of snoops made to the snoop filter."); 324 325 hitSingleSnoops 326 .name(name() + ".hit_single_snoops") 327 .desc("Number of snoops hitting in the snoop filter with a single "\ 328 "holder of the requested data."); 329 330 hitMultiSnoops 331 .name(name() + ".hit_multi_snoops") 332 .desc("Number of snoops hitting in the snoop filter with multiple "\ 333 "(>1) holders of the requested data."); 334} 335 336SnoopFilter * 337SnoopFilterParams::create() 338{ 339 return new SnoopFilter(this); 340} 341