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

Generated on Thu May 28 2020 16:21:35 for gem5 by doxygen 1.8.13