gem5  v20.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 
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 
65  cpu_side_port)
66 {
67  DPRINTF(SnoopFilter, "%s: src %s packet %s\n", __func__,
68  cpu_side_port.name(), cpkt->print());
69 
70  // check if the packet came from a cache
71  bool allocate = !cpkt->req->isUncacheable() && cpu_side_port.isSnooping()
72  && cpkt->fromCache();
73  Addr line_addr = cpkt->getBlockAddr(linesize);
74  if (cpkt->isSecure()) {
75  line_addr |= LineSecure;
76  }
77  SnoopMask req_port = portToMask(cpu_side_port);
78  reqLookupResult.it = cachedLocations.find(line_addr);
79  bool is_hit = (reqLookupResult.it != cachedLocations.end());
80 
81  // If the snoop filter has no entry, and we should not allocate,
82  // do not create a new snoop filter entry, simply return a NULL
83  // portlist.
84  if (!is_hit && !allocate)
85  return snoopDown(lookupLatency);
86 
87  // If no hit in snoop filter create a new element and update iterator
88  if (!is_hit) {
90  cachedLocations.emplace(line_addr, SnoopItem()).first;
91  }
92  SnoopItem& sf_item = reqLookupResult.it->second;
93  SnoopMask interested = sf_item.holder | sf_item.requested;
94 
95  // Store unmodified value of snoop filter item in temp storage in
96  // case we need to revert because of a send retry in
97  // updateRequest.
98  reqLookupResult.retryItem = sf_item;
99 
100  totRequests++;
101  if (is_hit) {
102  if (interested.count() == 1)
104  else
106  }
107 
108  DPRINTF(SnoopFilter, "%s: SF value %x.%x\n",
109  __func__, sf_item.requested, sf_item.holder);
110 
111  // If we are not allocating, we are done
112  if (!allocate)
113  return snoopSelected(maskToPortList(interested & ~req_port),
114  lookupLatency);
115 
116  if (cpkt->needsResponse()) {
117  if (!cpkt->cacheResponding()) {
118  // Max one request per address per port
119  panic_if((sf_item.requested & req_port).any(),
120  "double request :( SF value %x.%x\n",
121  sf_item.requested, sf_item.holder);
122 
123  // Mark in-flight requests to distinguish later on
124  sf_item.requested |= req_port;
125  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
126  __func__, sf_item.requested, sf_item.holder);
127  } else {
128  // NOTE: The memInhibit might have been asserted by a cache closer
129  // to the CPU, already -> the response will not be seen by this
130  // filter -> we do not need to keep the in-flight request, but make
131  // sure that we know that that cluster has a copy
132  panic_if((sf_item.holder & req_port).none(),
133  "Need to hold the value!");
135  "%s: not marking request. SF value %x.%x\n",
136  __func__, sf_item.requested, sf_item.holder);
137  }
138  } else { // if (!cpkt->needsResponse())
139  assert(cpkt->isEviction());
140  // make sure that the sender actually had the line
141  panic_if((sf_item.holder & req_port).none(), "requestor %x is not a " \
142  "holder :( SF value %x.%x\n", req_port,
143  sf_item.requested, sf_item.holder);
144  // CleanEvicts and Writebacks -> the sender and all caches above
145  // it may not have the line anymore.
146  if (!cpkt->isBlockCached()) {
147  sf_item.holder &= ~req_port;
148  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
149  __func__, sf_item.requested, sf_item.holder);
150  }
151  }
152 
153  return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
154 }
155 
156 void
157 SnoopFilter::finishRequest(bool will_retry, Addr addr, bool is_secure)
158 {
159  if (reqLookupResult.it != cachedLocations.end()) {
160  // since we rely on the caller, do a basic check to ensure
161  // that finishRequest is being called following lookupRequest
162  Addr line_addr = (addr & ~(Addr(linesize - 1)));
163  if (is_secure) {
164  line_addr |= LineSecure;
165  }
166  assert(reqLookupResult.it->first == line_addr);
167  if (will_retry) {
168  SnoopItem retry_item = reqLookupResult.retryItem;
169  // Undo any changes made in lookupRequest to the snoop filter
170  // entry if the request will come again. retryItem holds
171  // the previous value of the snoopfilter entry.
172  reqLookupResult.it->second = retry_item;
173 
174  DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n",
175  __func__, retry_item.requested, retry_item.holder);
176  }
177 
179  }
180 }
181 
184 {
185  DPRINTF(SnoopFilter, "%s: packet %s\n", __func__, cpkt->print());
186 
187  assert(cpkt->isRequest());
188 
189  Addr line_addr = cpkt->getBlockAddr(linesize);
190  if (cpkt->isSecure()) {
191  line_addr |= LineSecure;
192  }
193  auto sf_it = cachedLocations.find(line_addr);
194  bool is_hit = (sf_it != cachedLocations.end());
195 
196  panic_if(!is_hit && (cachedLocations.size() >= maxEntryCount),
197  "snoop filter exceeded capacity of %d cache blocks\n",
198  maxEntryCount);
199 
200  // If the snoop filter has no entry, simply return a NULL
201  // portlist, there is no point creating an entry only to remove it
202  // later
203  if (!is_hit)
204  return snoopDown(lookupLatency);
205 
206  SnoopItem& sf_item = sf_it->second;
207 
208  SnoopMask interested = (sf_item.holder | sf_item.requested);
209 
210  totSnoops++;
211 
212  if (interested.count() == 1)
213  hitSingleSnoops++;
214  else
215  hitMultiSnoops++;
216 
217  // ReadEx and Writes require both invalidation and exlusivity, while reads
218  // require neither. Writebacks on the other hand require exclusivity but
219  // not the invalidation. Previously Writebacks did not generate upward
220  // snoops so this was never an issue. Now that Writebacks generate snoops
221  // we need a special case for Writebacks. Additionally cache maintenance
222  // operations can generate snoops as they clean and/or invalidate all
223  // caches down to the specified point of reference.
224  assert(cpkt->isWriteback() || cpkt->req->isUncacheable() ||
225  (cpkt->isInvalidate() == cpkt->needsWritable()) ||
226  cpkt->req->isCacheMaintenance());
227  if (cpkt->isInvalidate() && sf_item.requested.none()) {
228  // Early clear of the holder, if no other request is currently going on
229  // @todo: This should possibly be updated even though we do not filter
230  // upward snoops
231  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
232  __func__, sf_item.requested, sf_item.holder);
233  sf_item.holder = 0;
234  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
235  __func__, sf_item.requested, sf_item.holder);
236  eraseIfNullEntry(sf_it);
237  }
238 
239  return snoopSelected(maskToPortList(interested), lookupLatency);
240 }
241 
242 void
244  const ResponsePort& rsp_port,
245  const ResponsePort& req_port)
246 {
247  DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n",
248  __func__, rsp_port.name(), req_port.name(), cpkt->print());
249 
250  assert(cpkt->isResponse());
251  assert(cpkt->cacheResponding());
252 
253  // if this snoop response is due to an uncacheable request, or is
254  // being turned into a normal response, there is nothing more to
255  // do
256  if (cpkt->req->isUncacheable() || !req_port.isSnooping()) {
257  return;
258  }
259 
260  Addr line_addr = cpkt->getBlockAddr(linesize);
261  if (cpkt->isSecure()) {
262  line_addr |= LineSecure;
263  }
264  SnoopMask rsp_mask = portToMask(rsp_port);
265  SnoopMask req_mask = portToMask(req_port);
266  SnoopItem& sf_item = cachedLocations[line_addr];
267 
268  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
269  __func__, sf_item.requested, sf_item.holder);
270 
271  // The source should have the line
272  panic_if((sf_item.holder & rsp_mask).none(),
273  "SF value %x.%x does not have the line\n",
274  sf_item.requested, sf_item.holder);
275 
276  // The destination should have had a request in
277  panic_if((sf_item.requested & req_mask).none(), "SF value %x.%x missing "\
278  "the original request\n", sf_item.requested, sf_item.holder);
279 
280  // If the snoop response has no sharers the line is passed in
281  // Modified state, and we know that there are no other copies, or
282  // they will all be invalidated imminently
283  if (!cpkt->hasSharers()) {
285  "%s: dropping %x because non-shared snoop "
286  "response SF val: %x.%x\n", __func__, rsp_mask,
287  sf_item.requested, sf_item.holder);
288  sf_item.holder = 0;
289  }
290  assert(!cpkt->isWriteback());
291  // @todo Deal with invalidating responses
292  sf_item.holder |= req_mask;
293  sf_item.requested &= ~req_mask;
294  assert((sf_item.requested | sf_item.holder).any());
295  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
296  __func__, sf_item.requested, sf_item.holder);
297 }
298 
299 void
301  const ResponsePort& rsp_port, const RequestPort& req_port)
302 {
303  DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n",
304  __func__, rsp_port.name(), req_port.name(), cpkt->print());
305 
306  assert(cpkt->isResponse());
307  assert(cpkt->cacheResponding());
308 
309  Addr line_addr = cpkt->getBlockAddr(linesize);
310  if (cpkt->isSecure()) {
311  line_addr |= LineSecure;
312  }
313  auto sf_it = cachedLocations.find(line_addr);
314  bool is_hit = sf_it != cachedLocations.end();
315 
316  // Nothing to do if it is not a hit
317  if (!is_hit)
318  return;
319 
320  // If the snoop response has no sharers the line is passed in
321  // Modified state, and we know that there are no other copies, or
322  // they will all be invalidated imminently
323  if (!cpkt->hasSharers()) {
324  SnoopItem& sf_item = sf_it->second;
325 
326  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
327  __func__, sf_item.requested, sf_item.holder);
328  sf_item.holder = 0;
329  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
330  __func__, sf_item.requested, sf_item.holder);
331 
332  eraseIfNullEntry(sf_it);
333  }
334 }
335 
336 void
338  cpu_side_port)
339 {
340  DPRINTF(SnoopFilter, "%s: src %s packet %s\n",
341  __func__, cpu_side_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() || !cpu_side_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 response_mask = portToMask(cpu_side_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 & response_mask).none(),
367  "SF value %x.%x missing request bit\n",
368  sf_item.requested, sf_item.holder);
369 
370  sf_item.requested &= ~response_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 &= ~response_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 |= response_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 }
SnoopFilter::updateSnoopForward
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...
Definition: snoop_filter.cc:300
Stats::Group::regStats
virtual void regStats()
Callback to set stat parameters.
Definition: group.cc:64
ResponsePort
A ResponsePort is a specialization of a port.
Definition: port.hh:265
Packet::isResponse
bool isResponse() const
Definition: packet.hh:560
system.hh
Packet::cacheResponding
bool cacheResponding() const
Definition: packet.hh:619
Packet::hasSharers
bool hasSharers() const
Definition: packet.hh:646
SnoopFilter::SnoopMask
std::bitset< SNOOP_MASK_SIZE > SnoopMask
The underlying type for the bitmask we use for tracking.
Definition: snoop_filter.hh:206
SnoopFilter::totSnoops
Stats::Scalar totSnoops
Definition: snoop_filter.hh:317
SnoopFilter::ReqLookupResult::retryItem
SnoopItem retryItem
Variable to temporarily store value of snoopfilter entry in case finishRequest needs to undo changes ...
Definition: snoop_filter.hh:278
SnoopFilter::snoopDown
std::pair< SnoopList, Cycles > snoopDown(Cycles latency) const
Definition: snoop_filter.hh:234
SnoopFilter::hitMultiSnoops
Stats::Scalar hitMultiSnoops
Definition: snoop_filter.hh:319
Packet::fromCache
bool fromCache() const
Definition: packet.hh:574
SnoopFilter::SnoopItem
Per cache line item tracking a bitmask of ResponsePorts who have an outstanding request to this line ...
Definition: snoop_filter.hh:213
Packet::needsWritable
bool needsWritable() const
Definition: packet.hh:561
Packet::isInvalidate
bool isInvalidate() const
Definition: packet.hh:571
SnoopFilter::finishRequest
void finishRequest(bool will_retry, Addr addr, bool is_secure)
For an un-successful request, revert the change to the snoop filter.
Definition: snoop_filter.cc:157
Packet::req
RequestPtr req
A pointer to the original request.
Definition: packet.hh:340
Packet::isEviction
bool isEviction() const
Definition: packet.hh:572
SnoopFilter::maxEntryCount
const unsigned maxEntryCount
Max capacity in terms of cache blocks tracked, for sanity checking.
Definition: snoop_filter.hh:302
SnoopFilter::lookupSnoop
std::pair< SnoopList, Cycles > lookupSnoop(const Packet *cpkt)
Handle an incoming snoop from below (the memory-side port).
Definition: snoop_filter.cc:183
Packet::isRequest
bool isRequest() const
Definition: packet.hh:559
Packet::print
void print(std::ostream &o, int verbosity=0, const std::string &prefix="") const
Definition: packet.cc:389
Packet::isSecure
bool isSecure() const
Definition: packet.hh:783
SnoopFilter::hitSingleRequests
Stats::Scalar hitSingleRequests
Definition: snoop_filter.hh:314
SnoopFilter::linesize
const unsigned linesize
Cache line size.
Definition: snoop_filter.hh:298
SnoopFilter::maskToPortList
SnoopList maskToPortList(SnoopMask ports) const
Converts a bitmask of ports into the corresponing list of ports.
Definition: snoop_filter.hh:332
SnoopFilter::totRequests
Stats::Scalar totRequests
Statistics.
Definition: snoop_filter.hh:313
SnoopFilter::ReqLookupResult::it
SnoopFilterCache::iterator it
Iterator used to store the result from lookupRequest.
Definition: snoop_filter.hh:271
SnoopFilter::eraseIfNullEntry
void eraseIfNullEntry(SnoopFilterCache::iterator &sf_it)
Removes snoop filter items which have no requestors and no holders.
Definition: snoop_filter.cc:53
SnoopFilter::hitSingleSnoops
Stats::Scalar hitSingleSnoops
Definition: snoop_filter.hh:318
DPRINTF
#define DPRINTF(x,...)
Definition: trace.hh:234
Packet::getBlockAddr
Addr getBlockAddr(unsigned int blk_size) const
Definition: packet.hh:778
SnoopFilter::SNOOP_MASK_SIZE
static const int SNOOP_MASK_SIZE
Definition: snoop_filter.hh:90
Packet::needsResponse
bool needsResponse() const
Definition: packet.hh:570
ResponsePort::isSnooping
bool isSnooping() const
Find out if the peer request port is snooping or not.
Definition: port.hh:288
SnoopFilter::lookupLatency
const Cycles lookupLatency
Latency for doing a lookup in the filter.
Definition: snoop_filter.hh:300
RequestPort
A RequestPort is a specialisation of a Port, which implements the default protocol for the three diff...
Definition: port.hh:74
std::pair
STL pair class.
Definition: stl.hh:58
Addr
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:142
Port::name
const std::string name() const
Return port name (for DPRINTF).
Definition: port.hh:106
Stats::DataWrap::name
Derived & name(const std::string &name)
Set the name and marks this stat to print at the end of simulation.
Definition: statistics.hh:274
SnoopFilter::LineSecure
@ LineSecure
block holds data from the secure memory space
Definition: snoop_filter.hh:309
SimObject::name
virtual const std::string name() const
Definition: sim_object.hh:133
SnoopFilter
This snoop filter keeps track of which connected port has a particular line of data.
Definition: snoop_filter.hh:86
SnoopFilter::lookupRequest
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:64
panic_if
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition: logging.hh:197
SnoopFilter::cachedLocations
SnoopFilterCache cachedLocations
Simple hash set of cached addresses.
Definition: snoop_filter.hh:261
SnoopFilter::updateSnoopResponse
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...
Definition: snoop_filter.cc:243
SnoopFilter::portToMask
SnoopMask portToMask(const ResponsePort &port) const
Convert a single port to a corresponding, one-hot bitmask.
Definition: snoop_filter.hh:323
Packet
A Packet is used to encapsulate a transfer between two objects in the memory system (e....
Definition: packet.hh:257
SnoopFilter::regStats
virtual void regStats()
Callback to set stat parameters.
Definition: snoop_filter.cc:391
addr
ip6_addr_t addr
Definition: inet.hh:423
SnoopFilter::reqLookupResult
struct SnoopFilter::ReqLookupResult reqLookupResult
logging.hh
SnoopFilter::SnoopItem::holder
SnoopMask holder
Definition: snoop_filter.hh:215
SnoopFilter::SnoopItem::requested
SnoopMask requested
Definition: snoop_filter.hh:214
trace.hh
SnoopFilter::updateResponse
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...
Definition: snoop_filter.cc:337
snoop_filter.hh
Packet::isBlockCached
bool isBlockCached() const
Definition: packet.hh:720
Packet::isWriteback
bool isWriteback() const
Definition: packet.hh:575
Stats::DataWrap::desc
Derived & desc(const std::string &_desc)
Set the description and marks this stat to print at the end of simulation.
Definition: statistics.hh:307
SnoopFilter::hitMultiRequests
Stats::Scalar hitMultiRequests
Definition: snoop_filter.hh:315
SnoopFilter::snoopSelected
std::pair< SnoopList, Cycles > snoopSelected(const SnoopList &_cpu_side_ports, Cycles latency) const
Definition: snoop_filter.hh:229

Generated on Wed Sep 30 2020 14:02:14 for gem5 by doxygen 1.8.17