gem5  v22.1.0.0
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 
50 namespace gem5
51 {
52 
54 
55 void
56 SnoopFilter::eraseIfNullEntry(SnoopFilterCache::iterator& sf_it)
57 {
58  SnoopItem& sf_item = sf_it->second;
59  if ((sf_item.requested | sf_item.holder).none()) {
60  cachedLocations.erase(sf_it);
61  DPRINTF(SnoopFilter, "%s: Removed SF entry.\n",
62  __func__);
63  }
64 }
65 
68  cpu_side_port)
69 {
70  DPRINTF(SnoopFilter, "%s: src %s packet %s\n", __func__,
71  cpu_side_port.name(), cpkt->print());
72 
73  // check if the packet came from a cache
74  bool allocate = !cpkt->req->isUncacheable() && cpu_side_port.isSnooping()
75  && cpkt->fromCache();
76  Addr line_addr = cpkt->getBlockAddr(linesize);
77  if (cpkt->isSecure()) {
78  line_addr |= LineSecure;
79  }
80  SnoopMask req_port = portToMask(cpu_side_port);
81  reqLookupResult.it = cachedLocations.find(line_addr);
82  bool is_hit = (reqLookupResult.it != cachedLocations.end());
83 
84  // If the snoop filter has no entry, and we should not allocate,
85  // do not create a new snoop filter entry, simply return a NULL
86  // portlist.
87  if (!is_hit && !allocate)
88  return snoopDown(lookupLatency);
89 
90  // If no hit in snoop filter create a new element and update iterator
91  if (!is_hit) {
93  cachedLocations.emplace(line_addr, SnoopItem()).first;
94  }
95  SnoopItem& sf_item = reqLookupResult.it->second;
96  SnoopMask interested = sf_item.holder | sf_item.requested;
97 
98  // Store unmodified value of snoop filter item in temp storage in
99  // case we need to revert because of a send retry in
100  // updateRequest.
101  reqLookupResult.retryItem = sf_item;
102 
103  stats.totRequests++;
104  if (is_hit) {
105  if (interested.count() == 1)
107  else
109  }
110 
111  DPRINTF(SnoopFilter, "%s: SF value %x.%x\n",
112  __func__, sf_item.requested, sf_item.holder);
113 
114  // If we are not allocating, we are done
115  if (!allocate)
116  return snoopSelected(maskToPortList(interested & ~req_port),
117  lookupLatency);
118 
119  if (cpkt->needsResponse()) {
120  if (!cpkt->cacheResponding()) {
121  // Max one request per address per port
122  panic_if((sf_item.requested & req_port).any(),
123  "double request :( SF value %x.%x\n",
124  sf_item.requested, sf_item.holder);
125 
126  // Mark in-flight requests to distinguish later on
127  sf_item.requested |= req_port;
128  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
129  __func__, sf_item.requested, sf_item.holder);
130  } else {
131  // NOTE: The memInhibit might have been asserted by a cache closer
132  // to the CPU, already -> the response will not be seen by this
133  // filter -> we do not need to keep the in-flight request, but make
134  // sure that we know that that cluster has a copy
135  panic_if((sf_item.holder & req_port).none(),
136  "Need to hold the value!");
138  "%s: not marking request. SF value %x.%x\n",
139  __func__, sf_item.requested, sf_item.holder);
140  }
141  } else { // if (!cpkt->needsResponse())
142  assert(cpkt->isEviction());
143  // make sure that the sender actually had the line
144  panic_if((sf_item.holder & req_port).none(), "requestor %x is not a " \
145  "holder :( SF value %x.%x\n", req_port,
146  sf_item.requested, sf_item.holder);
147  // CleanEvicts and Writebacks -> the sender and all caches above
148  // it may not have the line anymore.
149  if (!cpkt->isBlockCached()) {
150  sf_item.holder &= ~req_port;
151  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
152  __func__, sf_item.requested, sf_item.holder);
153  }
154  }
155 
156  return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
157 }
158 
159 void
160 SnoopFilter::finishRequest(bool will_retry, Addr addr, bool is_secure)
161 {
162  if (reqLookupResult.it != cachedLocations.end()) {
163  // since we rely on the caller, do a basic check to ensure
164  // that finishRequest is being called following lookupRequest
165  assert(reqLookupResult.it->first == \
166  (is_secure ? ((addr & ~(Addr(linesize - 1))) | LineSecure) : \
167  (addr & ~(Addr(linesize - 1)))));
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  stats.totSnoops++;
212 
213  if (interested.count() == 1)
215  else
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 ResponsePort& rsp_port,
246  const ResponsePort& 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 ResponsePort& rsp_port, const RequestPort& 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
339  cpu_side_port)
340 {
341  DPRINTF(SnoopFilter, "%s: src %s packet %s\n",
342  __func__, cpu_side_port.name(), cpkt->print());
343 
344  assert(cpkt->isResponse());
345 
346  // we only allocate if the packet actually came from a cache, but
347  // start by checking if the port is snooping
348  if (cpkt->req->isUncacheable() || !cpu_side_port.isSnooping())
349  return;
350 
351  // next check if we actually allocated an entry
352  Addr line_addr = cpkt->getBlockAddr(linesize);
353  if (cpkt->isSecure()) {
354  line_addr |= LineSecure;
355  }
356  auto sf_it = cachedLocations.find(line_addr);
357  if (sf_it == cachedLocations.end())
358  return;
359 
360  SnoopMask response_mask = portToMask(cpu_side_port);
361  SnoopItem& sf_item = sf_it->second;
362 
363  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
364  __func__, sf_item.requested, sf_item.holder);
365 
366  // Make sure we have seen the actual request, too
367  panic_if((sf_item.requested & response_mask).none(),
368  "SF value %x.%x missing request bit\n",
369  sf_item.requested, sf_item.holder);
370 
371  sf_item.requested &= ~response_mask;
372  // Update the residency of the cache line.
373 
374  if (cpkt->req->isCacheMaintenance()) {
375  // A cache clean response does not carry any data so it
376  // shouldn't change the holders, unless it is invalidating.
377  if (cpkt->isInvalidate()) {
378  sf_item.holder &= ~response_mask;
379  }
380  eraseIfNullEntry(sf_it);
381  } else {
382  // Any other response implies that a cache above will have the
383  // block.
384  sf_item.holder |= response_mask;
385  assert((sf_item.holder | sf_item.requested).any());
386  }
387  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
388  __func__, sf_item.requested, sf_item.holder);
389 }
390 
392  : statistics::Group(parent),
393  ADD_STAT(totRequests, statistics::units::Count::get(),
394  "Total number of requests made to the snoop filter."),
395  ADD_STAT(hitSingleRequests, statistics::units::Count::get(),
396  "Number of requests hitting in the snoop filter with a single "
397  "holder of the requested data."),
398  ADD_STAT(hitMultiRequests, statistics::units::Count::get(),
399  "Number of requests hitting in the snoop filter with multiple "
400  "(>1) holders of the requested data."),
401  ADD_STAT(totSnoops, statistics::units::Count::get(),
402  "Total number of snoops made to the snoop filter."),
403  ADD_STAT(hitSingleSnoops, statistics::units::Count::get(),
404  "Number of snoops hitting in the snoop filter with a single "
405  "holder of the requested data."),
406  ADD_STAT(hitMultiSnoops, statistics::units::Count::get(),
407  "Number of snoops hitting in the snoop filter with multiple "
408  "(>1) holders of the requested data.")
409 {}
410 
411 void
413 {
415 }
416 
417 } // namespace gem5
#define DPRINTF(x,...)
Definition: trace.hh:186
A Packet is used to encapsulate a transfer between two objects in the memory system (e....
Definition: packet.hh:294
bool isSecure() const
Definition: packet.hh:834
bool isResponse() const
Definition: packet.hh:597
bool needsWritable() const
Definition: packet.hh:598
void print(std::ostream &o, int verbosity=0, const std::string &prefix="") const
Definition: packet.cc:368
bool needsResponse() const
Definition: packet.hh:607
bool fromCache() const
Definition: packet.hh:611
Addr getBlockAddr(unsigned int blk_size) const
Definition: packet.hh:829
RequestPtr req
A pointer to the original request.
Definition: packet.hh:376
bool isWriteback() const
Definition: packet.hh:612
bool cacheResponding() const
Definition: packet.hh:657
bool isInvalidate() const
Definition: packet.hh:608
bool hasSharers() const
Definition: packet.hh:684
bool isBlockCached() const
Definition: packet.hh:758
bool isEviction() const
Definition: packet.hh:609
bool isRequest() const
Definition: packet.hh:596
const std::string name() const
Return port name (for DPRINTF).
Definition: port.hh:111
A RequestPort is a specialisation of a Port, which implements the default protocol for the three diff...
Definition: port.hh:79
A ResponsePort is a specialization of a port.
Definition: port.hh:270
bool isSnooping() const
Find out if the peer request port is snooping or not.
Definition: port.hh:291
This snoop filter keeps track of which connected port has a particular line of data.
Definition: snoop_filter.hh:90
void finishRequest(bool will_retry, Addr addr, bool is_secure)
For an un-successful request, revert the change to the snoop filter.
void eraseIfNullEntry(SnoopFilterCache::iterator &sf_it)
Removes snoop filter items which have no requestors and no holders.
Definition: snoop_filter.cc:56
const unsigned linesize
Cache line size.
const Cycles lookupLatency
Latency for doing a lookup in the filter.
std::pair< SnoopList, Cycles > snoopSelected(const SnoopList &_cpu_side_ports, Cycles latency) const
SnoopMask portToMask(const ResponsePort &port) const
Convert a single port to a corresponding, one-hot bitmask.
void updateSnoopForward(const Packet *cpkt, const ResponsePort &rsp_port, const RequestPort &req_port)
Pass snoop responses that travel downward through the snoop filter and let them update the snoop filt...
void updateResponse(const Packet *cpkt, const ResponsePort &cpu_side_port)
Update the snoop filter with a response from below (outer / other cache, or memory) and update the tr...
std::pair< SnoopList, Cycles > snoopDown(Cycles latency) const
std::bitset< SNOOP_MASK_SIZE > SnoopMask
The underlying type for the bitmask we use for tracking.
std::pair< SnoopList, Cycles > lookupSnoop(const Packet *cpkt)
Handle an incoming snoop from below (the memory-side port).
SnoopList maskToPortList(SnoopMask ports) const
Converts a bitmask of ports into the corresponing list of ports.
struct gem5::SnoopFilter::ReqLookupResult reqLookupResult
const unsigned maxEntryCount
Max capacity in terms of cache blocks tracked, for sanity checking.
SnoopFilterCache cachedLocations
Simple hash set of cached addresses.
void updateSnoopResponse(const Packet *cpkt, const ResponsePort &rsp_port, const ResponsePort &req_port)
Let the snoop filter see any snoop responses that turn into request responses and indicate cache to c...
@ LineSecure
block holds data from the secure memory space
gem5::SnoopFilter::SnoopFilterStats stats
static const int SNOOP_MASK_SIZE
Definition: snoop_filter.hh:94
std::pair< SnoopList, Cycles > lookupRequest(const Packet *cpkt, const ResponsePort &cpu_side_port)
Lookup a request (from a CPU-side port) in the snoop filter and return a list of other CPU-side ports...
Definition: snoop_filter.cc:67
virtual void regStats()
Callback to set stat parameters.
Statistics container.
Definition: group.hh:94
STL pair class.
Definition: stl.hh:58
#define ADD_STAT(n,...)
Convenience macro to add a stat to a statistics group.
Definition: group.hh:75
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition: logging.hh:204
virtual void regStats()
Callback to set stat parameters.
Definition: group.cc:69
Bitfield< 3 > addr
Definition: types.hh:84
Reference material can be found at the JEDEC website: UFS standard http://www.jedec....
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:147
Definition of a snoop filter.
SnoopItem retryItem
Variable to temporarily store value of snoopfilter entry in case finishRequest needs to undo changes ...
SnoopFilterCache::iterator it
Iterator used to store the result from lookupRequest.
SnoopFilterStats(statistics::Group *parent)
Per cache line item tracking a bitmask of ResponsePorts who have an outstanding request to this line ...

Generated on Wed Dec 21 2022 10:22:39 for gem5 by doxygen 1.9.1