gem5  v19.0.0.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
snoop_filter.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2017,2019 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 
45 #include "mem/snoop_filter.hh"
46 
47 #include "base/logging.hh"
48 #include "base/trace.hh"
49 #include "debug/SnoopFilter.hh"
50 #include "sim/system.hh"
51 
53 
54 void
55 SnoopFilter::eraseIfNullEntry(SnoopFilterCache::iterator& sf_it)
56 {
57  SnoopItem& sf_item = sf_it->second;
58  if ((sf_item.requested | sf_item.holder).none()) {
59  cachedLocations.erase(sf_it);
60  DPRINTF(SnoopFilter, "%s: Removed SF entry.\n",
61  __func__);
62  }
63 }
64 
66 SnoopFilter::lookupRequest(const Packet* cpkt, const SlavePort& slave_port)
67 {
68  DPRINTF(SnoopFilter, "%s: src %s packet %s\n", __func__,
69  slave_port.name(), cpkt->print());
70 
71  // check if the packet came from a cache
72  bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping() &&
73  cpkt->fromCache();
74  Addr line_addr = cpkt->getBlockAddr(linesize);
75  if (cpkt->isSecure()) {
76  line_addr |= LineSecure;
77  }
78  SnoopMask req_port = portToMask(slave_port);
79  reqLookupResult.it = cachedLocations.find(line_addr);
80  bool is_hit = (reqLookupResult.it != cachedLocations.end());
81 
82  // If the snoop filter has no entry, and we should not allocate,
83  // do not create a new snoop filter entry, simply return a NULL
84  // portlist.
85  if (!is_hit && !allocate)
86  return snoopDown(lookupLatency);
87 
88  // If no hit in snoop filter create a new element and update iterator
89  if (!is_hit) {
91  cachedLocations.emplace(line_addr, SnoopItem()).first;
92  }
93  SnoopItem& sf_item = reqLookupResult.it->second;
94  SnoopMask interested = sf_item.holder | sf_item.requested;
95 
96  // Store unmodified value of snoop filter item in temp storage in
97  // case we need to revert because of a send retry in
98  // updateRequest.
99  reqLookupResult.retryItem = sf_item;
100 
101  totRequests++;
102  if (is_hit) {
103  if (interested.count() == 1)
105  else
107  }
108 
109  DPRINTF(SnoopFilter, "%s: SF value %x.%x\n",
110  __func__, sf_item.requested, sf_item.holder);
111 
112  // If we are not allocating, we are done
113  if (!allocate)
114  return snoopSelected(maskToPortList(interested & ~req_port),
115  lookupLatency);
116 
117  if (cpkt->needsResponse()) {
118  if (!cpkt->cacheResponding()) {
119  // Max one request per address per port
120  panic_if((sf_item.requested & req_port).any(),
121  "double request :( SF value %x.%x\n",
122  sf_item.requested, sf_item.holder);
123 
124  // Mark in-flight requests to distinguish later on
125  sf_item.requested |= req_port;
126  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
127  __func__, sf_item.requested, sf_item.holder);
128  } else {
129  // NOTE: The memInhibit might have been asserted by a cache closer
130  // to the CPU, already -> the response will not be seen by this
131  // filter -> we do not need to keep the in-flight request, but make
132  // sure that we know that that cluster has a copy
133  panic_if((sf_item.holder & req_port).none(),
134  "Need to hold the value!");
136  "%s: not marking request. SF value %x.%x\n",
137  __func__, sf_item.requested, sf_item.holder);
138  }
139  } else { // if (!cpkt->needsResponse())
140  assert(cpkt->isEviction());
141  // make sure that the sender actually had the line
142  panic_if((sf_item.holder & req_port).none(), "requester %x is not a " \
143  "holder :( SF value %x.%x\n", req_port,
144  sf_item.requested, sf_item.holder);
145  // CleanEvicts and Writebacks -> the sender and all caches above
146  // it may not have the line anymore.
147  if (!cpkt->isBlockCached()) {
148  sf_item.holder &= ~req_port;
149  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
150  __func__, sf_item.requested, sf_item.holder);
151  }
152  }
153 
154  return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
155 }
156 
157 void
158 SnoopFilter::finishRequest(bool will_retry, Addr addr, bool is_secure)
159 {
160  if (reqLookupResult.it != cachedLocations.end()) {
161  // since we rely on the caller, do a basic check to ensure
162  // that finishRequest is being called following lookupRequest
163  Addr line_addr = (addr & ~(Addr(linesize - 1)));
164  if (is_secure) {
165  line_addr |= LineSecure;
166  }
167  assert(reqLookupResult.it->first == line_addr);
168  if (will_retry) {
169  SnoopItem retry_item = reqLookupResult.retryItem;
170  // Undo any changes made in lookupRequest to the snoop filter
171  // entry if the request will come again. retryItem holds
172  // the previous value of the snoopfilter entry.
173  reqLookupResult.it->second = retry_item;
174 
175  DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n",
176  __func__, retry_item.requested, retry_item.holder);
177  }
178 
180  }
181 }
182 
185 {
186  DPRINTF(SnoopFilter, "%s: packet %s\n", __func__, cpkt->print());
187 
188  assert(cpkt->isRequest());
189 
190  Addr line_addr = cpkt->getBlockAddr(linesize);
191  if (cpkt->isSecure()) {
192  line_addr |= LineSecure;
193  }
194  auto sf_it = cachedLocations.find(line_addr);
195  bool is_hit = (sf_it != cachedLocations.end());
196 
197  panic_if(!is_hit && (cachedLocations.size() >= maxEntryCount),
198  "snoop filter exceeded capacity of %d cache blocks\n",
199  maxEntryCount);
200 
201  // If the snoop filter has no entry, simply return a NULL
202  // portlist, there is no point creating an entry only to remove it
203  // later
204  if (!is_hit)
205  return snoopDown(lookupLatency);
206 
207  SnoopItem& sf_item = sf_it->second;
208 
209  SnoopMask interested = (sf_item.holder | sf_item.requested);
210 
211  totSnoops++;
212 
213  if (interested.count() == 1)
214  hitSingleSnoops++;
215  else
216  hitMultiSnoops++;
217 
218  // ReadEx and Writes require both invalidation and exlusivity, while reads
219  // require neither. Writebacks on the other hand require exclusivity but
220  // not the invalidation. Previously Writebacks did not generate upward
221  // snoops so this was never an issue. Now that Writebacks generate snoops
222  // we need a special case for Writebacks. Additionally cache maintenance
223  // operations can generate snoops as they clean and/or invalidate all
224  // caches down to the specified point of reference.
225  assert(cpkt->isWriteback() || cpkt->req->isUncacheable() ||
226  (cpkt->isInvalidate() == cpkt->needsWritable()) ||
227  cpkt->req->isCacheMaintenance());
228  if (cpkt->isInvalidate() && sf_item.requested.none()) {
229  // Early clear of the holder, if no other request is currently going on
230  // @todo: This should possibly be updated even though we do not filter
231  // upward snoops
232  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
233  __func__, sf_item.requested, sf_item.holder);
234  sf_item.holder = 0;
235  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
236  __func__, sf_item.requested, sf_item.holder);
237  eraseIfNullEntry(sf_it);
238  }
239 
240  return snoopSelected(maskToPortList(interested), lookupLatency);
241 }
242 
243 void
245  const SlavePort& rsp_port,
246  const SlavePort& req_port)
247 {
248  DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n",
249  __func__, rsp_port.name(), req_port.name(), cpkt->print());
250 
251  assert(cpkt->isResponse());
252  assert(cpkt->cacheResponding());
253 
254  // if this snoop response is due to an uncacheable request, or is
255  // being turned into a normal response, there is nothing more to
256  // do
257  if (cpkt->req->isUncacheable() || !req_port.isSnooping()) {
258  return;
259  }
260 
261  Addr line_addr = cpkt->getBlockAddr(linesize);
262  if (cpkt->isSecure()) {
263  line_addr |= LineSecure;
264  }
265  SnoopMask rsp_mask = portToMask(rsp_port);
266  SnoopMask req_mask = portToMask(req_port);
267  SnoopItem& sf_item = cachedLocations[line_addr];
268 
269  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
270  __func__, sf_item.requested, sf_item.holder);
271 
272  // The source should have the line
273  panic_if((sf_item.holder & rsp_mask).none(),
274  "SF value %x.%x does not have the line\n",
275  sf_item.requested, sf_item.holder);
276 
277  // The destination should have had a request in
278  panic_if((sf_item.requested & req_mask).none(), "SF value %x.%x missing "\
279  "the original request\n", sf_item.requested, sf_item.holder);
280 
281  // If the snoop response has no sharers the line is passed in
282  // Modified state, and we know that there are no other copies, or
283  // they will all be invalidated imminently
284  if (!cpkt->hasSharers()) {
286  "%s: dropping %x because non-shared snoop "
287  "response SF val: %x.%x\n", __func__, rsp_mask,
288  sf_item.requested, sf_item.holder);
289  sf_item.holder = 0;
290  }
291  assert(!cpkt->isWriteback());
292  // @todo Deal with invalidating responses
293  sf_item.holder |= req_mask;
294  sf_item.requested &= ~req_mask;
295  assert((sf_item.requested | sf_item.holder).any());
296  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
297  __func__, sf_item.requested, sf_item.holder);
298 }
299 
300 void
302  const SlavePort& rsp_port, const MasterPort& req_port)
303 {
304  DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n",
305  __func__, rsp_port.name(), req_port.name(), cpkt->print());
306 
307  assert(cpkt->isResponse());
308  assert(cpkt->cacheResponding());
309 
310  Addr line_addr = cpkt->getBlockAddr(linesize);
311  if (cpkt->isSecure()) {
312  line_addr |= LineSecure;
313  }
314  auto sf_it = cachedLocations.find(line_addr);
315  bool is_hit = sf_it != cachedLocations.end();
316 
317  // Nothing to do if it is not a hit
318  if (!is_hit)
319  return;
320 
321  // If the snoop response has no sharers the line is passed in
322  // Modified state, and we know that there are no other copies, or
323  // they will all be invalidated imminently
324  if (!cpkt->hasSharers()) {
325  SnoopItem& sf_item = sf_it->second;
326 
327  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
328  __func__, sf_item.requested, sf_item.holder);
329  sf_item.holder = 0;
330  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
331  __func__, sf_item.requested, sf_item.holder);
332 
333  eraseIfNullEntry(sf_it);
334  }
335 }
336 
337 void
338 SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port)
339 {
340  DPRINTF(SnoopFilter, "%s: src %s packet %s\n",
341  __func__, slave_port.name(), cpkt->print());
342 
343  assert(cpkt->isResponse());
344 
345  // we only allocate if the packet actually came from a cache, but
346  // start by checking if the port is snooping
347  if (cpkt->req->isUncacheable() || !slave_port.isSnooping())
348  return;
349 
350  // next check if we actually allocated an entry
351  Addr line_addr = cpkt->getBlockAddr(linesize);
352  if (cpkt->isSecure()) {
353  line_addr |= LineSecure;
354  }
355  auto sf_it = cachedLocations.find(line_addr);
356  if (sf_it == cachedLocations.end())
357  return;
358 
359  SnoopMask slave_mask = portToMask(slave_port);
360  SnoopItem& sf_item = sf_it->second;
361 
362  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
363  __func__, sf_item.requested, sf_item.holder);
364 
365  // Make sure we have seen the actual request, too
366  panic_if((sf_item.requested & slave_mask).none(),
367  "SF value %x.%x missing request bit\n",
368  sf_item.requested, sf_item.holder);
369 
370  sf_item.requested &= ~slave_mask;
371  // Update the residency of the cache line.
372 
373  if (cpkt->req->isCacheMaintenance()) {
374  // A cache clean response does not carry any data so it
375  // shouldn't change the holders, unless it is invalidating.
376  if (cpkt->isInvalidate()) {
377  sf_item.holder &= ~slave_mask;
378  }
379  eraseIfNullEntry(sf_it);
380  } else {
381  // Any other response implies that a cache above will have the
382  // block.
383  sf_item.holder |= slave_mask;
384  assert((sf_item.holder | sf_item.requested).any());
385  }
386  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
387  __func__, sf_item.requested, sf_item.holder);
388 }
389 
390 void
392 {
394 
396  .name(name() + ".tot_requests")
397  .desc("Total number of requests made to the snoop filter.");
398 
400  .name(name() + ".hit_single_requests")
401  .desc("Number of requests hitting in the snoop filter with a single "\
402  "holder of the requested data.");
403 
405  .name(name() + ".hit_multi_requests")
406  .desc("Number of requests hitting in the snoop filter with multiple "\
407  "(>1) holders of the requested data.");
408 
409  totSnoops
410  .name(name() + ".tot_snoops")
411  .desc("Total number of snoops made to the snoop filter.");
412 
414  .name(name() + ".hit_single_snoops")
415  .desc("Number of snoops hitting in the snoop filter with a single "\
416  "holder of the requested data.");
417 
419  .name(name() + ".hit_multi_snoops")
420  .desc("Number of snoops hitting in the snoop filter with multiple "\
421  "(>1) holders of the requested data.");
422 }
423 
424 SnoopFilter *
425 SnoopFilterParams::create()
426 {
427  return new SnoopFilter(this);
428 }
A MasterPort is a specialisation of a BaseMasterPort, which implements the default protocol for the t...
Definition: port.hh:75
#define DPRINTF(x,...)
Definition: trace.hh:229
void updateSnoopForward(const Packet *cpkt, const SlavePort &rsp_port, const MasterPort &req_port)
Pass snoop responses that travel downward through the snoop filter and let them update the snoop filt...
Stats::Scalar hitMultiRequests
Stats::Scalar hitMultiSnoops
SnoopMask portToMask(const SlavePort &port) const
Convert a single port to a corresponding, one-hot bitmask.
STL pair class.
Definition: stl.hh:61
void finishRequest(bool will_retry, Addr addr, bool is_secure)
For an un-successful request, revert the change to the snoop filter.
const unsigned maxEntryCount
Max capacity in terms of cache blocks tracked, for sanity checking.
ip6_addr_t addr
Definition: inet.hh:335
bool cacheResponding() const
Definition: packet.hh:591
std::bitset< SNOOP_MASK_SIZE > SnoopMask
The underlying type for the bitmask we use for tracking.
Stats::Scalar totSnoops
virtual void regStats()
Callback to set stat parameters.
Definition: group.cc:66
std::pair< SnoopList, Cycles > snoopDown(Cycles latency) const
Definition of a snoop filter.
A SlavePort is a specialisation of a port.
Definition: port.hh:258
block holds data from the secure memory space
SnoopList maskToPortList(SnoopMask ports) const
Converts a bitmask of ports into the corresponing list of ports.
bool isInvalidate() const
Definition: packet.hh:543
Stats::Scalar hitSingleSnoops
SnoopFilterCache::iterator it
Iterator used to store the result from lookupRequest.
bool needsWritable() const
Definition: packet.hh:533
Addr getBlockAddr(unsigned int blk_size) const
Definition: packet.hh:750
std::pair< SnoopList, Cycles > lookupSnoop(const Packet *cpkt)
Handle an incoming snoop from below (the master port).
RequestPtr req
A pointer to the original request.
Definition: packet.hh:327
SnoopFilter(const SnoopFilterParams *p)
Definition: snoop_filter.hh:96
bool isBlockCached() const
Definition: packet.hh:692
bool isRequest() const
Definition: packet.hh:531
struct SnoopFilter::ReqLookupResult reqLookupResult
bool needsResponse() const
Definition: packet.hh:542
Stats::Scalar hitSingleRequests
const Cycles lookupLatency
Latency for doing a lookup in the filter.
const unsigned linesize
Cache line size.
bool isResponse() const
Definition: packet.hh:532
Stats::Scalar totRequests
Statistics.
void eraseIfNullEntry(SnoopFilterCache::iterator &sf_it)
Removes snoop filter items which have no requesters and no holders.
Definition: snoop_filter.cc:55
std::pair< SnoopList, Cycles > snoopSelected(const SnoopList &slave_ports, Cycles latency) const
bool isSnooping() const
Find out if the peer master port is snooping or not.
Definition: port.hh:280
static const int SNOOP_MASK_SIZE
Definition: snoop_filter.hh:92
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:142
virtual const std::string name() const
Definition: sim_object.hh:120
A Packet is used to encapsulate a transfer between two objects in the memory system (e...
Definition: packet.hh:255
void updateResponse(const Packet *cpkt, const SlavePort &slave_port)
Update the snoop filter with a response from below (outer / other cache, or memory) and update the tr...
SnoopFilterCache cachedLocations
Simple hash set of cached addresses.
bool hasSharers() const
Definition: packet.hh:618
virtual void regStats()
Callback to set stat parameters.
Derived & name(const std::string &name)
Set the name and marks this stat to print at the end of simulation.
Definition: statistics.hh:279
bool fromCache() const
Definition: packet.hh:546
void print(std::ostream &o, int verbosity=0, const std::string &prefix="") const
Definition: packet.cc:376
bool isEviction() const
Definition: packet.hh:544
const std::string name() const
Return port name (for DPRINTF).
Definition: port.hh:106
Per cache line item tracking a bitmask of SlavePorts who have an outstanding request to this line (re...
bool isSecure() const
Definition: packet.hh:755
Derived & desc(const std::string &_desc)
Set the description and marks this stat to print at the end of simulation.
Definition: statistics.hh:312
void updateSnoopResponse(const Packet *cpkt, const SlavePort &rsp_port, const SlavePort &req_port)
Let the snoop filter see any snoop responses that turn into request responses and indicate cache to c...
SnoopItem retryItem
Variable to temporarily store value of snoopfilter entry in case finishRequest needs to undo changes ...
This snoop filter keeps track of which connected port has a particular line of data.
Definition: snoop_filter.hh:88
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition: logging.hh:185
bool isWriteback() const
Definition: packet.hh:547
std::pair< SnoopList, Cycles > lookupRequest(const Packet *cpkt, const SlavePort &slave_port)
Lookup a request (from a slave port) in the snoop filter and return a list of other slave ports that ...
Definition: snoop_filter.cc:66

Generated on Fri Feb 28 2020 16:27:02 for gem5 by doxygen 1.8.13