gem5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
snoop_filter.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2016 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/misc.hh"
48 #include "base/trace.hh"
49 #include "debug/SnoopFilter.hh"
50 #include "sim/system.hh"
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)) {
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 = cachedLocations.find(line_addr);
78  bool is_hit = (reqLookupResult != 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)
88  reqLookupResult = cachedLocations.emplace(line_addr, SnoopItem()).first;
89  SnoopItem& sf_item = reqLookupResult->second;
90  SnoopMask interested = sf_item.holder | sf_item.requested;
91 
92  // Store unmodified value of snoop filter item in temp storage in
93  // case we need to revert because of a send retry in
94  // updateRequest.
95  retryItem = sf_item;
96 
97  totRequests++;
98  if (is_hit) {
99  // Single bit set -> value is a power of two
100  if (isPow2(interested))
102  else
104  }
105 
106  DPRINTF(SnoopFilter, "%s: SF value %x.%x\n",
107  __func__, sf_item.requested, sf_item.holder);
108 
109  // If we are not allocating, we are done
110  if (!allocate)
111  return snoopSelected(maskToPortList(interested & ~req_port),
112  lookupLatency);
113 
114  if (cpkt->needsResponse()) {
115  if (!cpkt->cacheResponding()) {
116  // Max one request per address per port
117  panic_if(sf_item.requested & req_port, "double request :( " \
118  "SF value %x.%x\n", sf_item.requested, sf_item.holder);
119 
120  // Mark in-flight requests to distinguish later on
121  sf_item.requested |= req_port;
122  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
123  __func__, sf_item.requested, sf_item.holder);
124  } else {
125  // NOTE: The memInhibit might have been asserted by a cache closer
126  // to the CPU, already -> the response will not be seen by this
127  // filter -> we do not need to keep the in-flight request, but make
128  // sure that we know that that cluster has a copy
129  panic_if(!(sf_item.holder & req_port), "Need to hold the value!");
131  "%s: not marking request. SF value %x.%x\n",
132  __func__, sf_item.requested, sf_item.holder);
133  }
134  } else { // if (!cpkt->needsResponse())
135  assert(cpkt->isEviction());
136  // make sure that the sender actually had the line
137  panic_if(!(sf_item.holder & req_port), "requester %x is not a " \
138  "holder :( SF value %x.%x\n", req_port,
139  sf_item.requested, sf_item.holder);
140  // CleanEvicts and Writebacks -> the sender and all caches above
141  // it may not have the line anymore.
142  if (!cpkt->isBlockCached()) {
143  sf_item.holder &= ~req_port;
144  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
145  __func__, sf_item.requested, sf_item.holder);
146  }
147  }
148 
149  return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
150 }
151 
152 void
153 SnoopFilter::finishRequest(bool will_retry, Addr addr, bool is_secure)
154 {
155  if (reqLookupResult != cachedLocations.end()) {
156  // since we rely on the caller, do a basic check to ensure
157  // that finishRequest is being called following lookupRequest
158  Addr line_addr = (addr & ~(Addr(linesize - 1)));
159  if (is_secure) {
160  line_addr |= LineSecure;
161  }
162  assert(reqLookupResult->first == line_addr);
163  if (will_retry) {
164  // Undo any changes made in lookupRequest to the snoop filter
165  // entry if the request will come again. retryItem holds
166  // the previous value of the snoopfilter entry.
167  reqLookupResult->second = retryItem;
168 
169  DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n",
170  __func__, retryItem.requested, retryItem.holder);
171  }
172 
174  }
175 }
176 
179 {
180  DPRINTF(SnoopFilter, "%s: packet %s\n", __func__, cpkt->print());
181 
182  assert(cpkt->isRequest());
183 
184  Addr line_addr = cpkt->getBlockAddr(linesize);
185  if (cpkt->isSecure()) {
186  line_addr |= LineSecure;
187  }
188  auto sf_it = cachedLocations.find(line_addr);
189  bool is_hit = (sf_it != cachedLocations.end());
190 
191  panic_if(!is_hit && (cachedLocations.size() >= maxEntryCount),
192  "snoop filter exceeded capacity of %d cache blocks\n",
193  maxEntryCount);
194 
195  // If the snoop filter has no entry, simply return a NULL
196  // portlist, there is no point creating an entry only to remove it
197  // later
198  if (!is_hit)
199  return snoopDown(lookupLatency);
200 
201  SnoopItem& sf_item = sf_it->second;
202 
203  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
204  __func__, sf_item.requested, sf_item.holder);
205 
206  SnoopMask interested = (sf_item.holder | sf_item.requested);
207 
208  totSnoops++;
209  // Single bit set -> value is a power of two
210  if (isPow2(interested))
211  hitSingleSnoops++;
212  else
213  hitMultiSnoops++;
214 
215  // ReadEx and Writes require both invalidation and exlusivity, while reads
216  // require neither. Writebacks on the other hand require exclusivity but
217  // not the invalidation. Previously Writebacks did not generate upward
218  // snoops so this was never an aissue. Now that Writebacks generate snoops
219  // we need to special case for Writebacks.
220  assert(cpkt->isWriteback() || cpkt->req->isUncacheable() ||
221  (cpkt->isInvalidate() == cpkt->needsWritable()));
222  if (cpkt->isInvalidate() && !sf_item.requested) {
223  // Early clear of the holder, if no other request is currently going on
224  // @todo: This should possibly be updated even though we do not filter
225  // upward snoops
226  sf_item.holder = 0;
227  }
228 
229  eraseIfNullEntry(sf_it);
230  DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n",
231  __func__, sf_item.requested, sf_item.holder, interested);
232 
233  return snoopSelected(maskToPortList(interested), lookupLatency);
234 }
235 
236 void
238  const SlavePort& rsp_port,
239  const SlavePort& req_port)
240 {
241  DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n",
242  __func__, rsp_port.name(), req_port.name(), cpkt->print());
243 
244  assert(cpkt->isResponse());
245  assert(cpkt->cacheResponding());
246 
247  // if this snoop response is due to an uncacheable request, or is
248  // being turned into a normal response, there is nothing more to
249  // do
250  if (cpkt->req->isUncacheable() || !req_port.isSnooping()) {
251  return;
252  }
253 
254  Addr line_addr = cpkt->getBlockAddr(linesize);
255  if (cpkt->isSecure()) {
256  line_addr |= LineSecure;
257  }
258  SnoopMask rsp_mask = portToMask(rsp_port);
259  SnoopMask req_mask = portToMask(req_port);
260  SnoopItem& sf_item = cachedLocations[line_addr];
261 
262  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
263  __func__, sf_item.requested, sf_item.holder);
264 
265  // The source should have the line
266  panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\
267  "the line\n", sf_item.requested, sf_item.holder);
268 
269  // The destination should have had a request in
270  panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\
271  "the original request\n", sf_item.requested, sf_item.holder);
272 
273  // If the snoop response has no sharers the line is passed in
274  // Modified state, and we know that there are no other copies, or
275  // they will all be invalidated imminently
276  if (!cpkt->hasSharers()) {
278  "%s: dropping %x because non-shared snoop "
279  "response SF val: %x.%x\n", __func__, rsp_mask,
280  sf_item.requested, sf_item.holder);
281  sf_item.holder = 0;
282  }
283  assert(!cpkt->isWriteback());
284  // @todo Deal with invalidating responses
285  sf_item.holder |= req_mask;
286  sf_item.requested &= ~req_mask;
287  assert(sf_item.requested | sf_item.holder);
288  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
289  __func__, sf_item.requested, sf_item.holder);
290 }
291 
292 void
294  const SlavePort& rsp_port, const MasterPort& req_port)
295 {
296  DPRINTF(SnoopFilter, "%s: rsp %s req %s packet %s\n",
297  __func__, rsp_port.name(), req_port.name(), cpkt->print());
298 
299  assert(cpkt->isResponse());
300  assert(cpkt->cacheResponding());
301 
302  Addr line_addr = cpkt->getBlockAddr(linesize);
303  if (cpkt->isSecure()) {
304  line_addr |= LineSecure;
305  }
306  auto sf_it = cachedLocations.find(line_addr);
307  bool is_hit = sf_it != cachedLocations.end();
308 
309  // Nothing to do if it is not a hit
310  if (!is_hit)
311  return;
312 
313  SnoopItem& sf_item = sf_it->second;
314 
315  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
316  __func__, sf_item.requested, sf_item.holder);
317 
318  // If the snoop response has no sharers the line is passed in
319  // Modified state, and we know that there are no other copies, or
320  // they will all be invalidated imminently
321  if (!cpkt->hasSharers()) {
322  sf_item.holder = 0;
323  }
324  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
325  __func__, sf_item.requested, sf_item.holder);
326  eraseIfNullEntry(sf_it);
327 
328 }
329 
330 void
331 SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port)
332 {
333  DPRINTF(SnoopFilter, "%s: src %s packet %s\n",
334  __func__, slave_port.name(), cpkt->print());
335 
336  assert(cpkt->isResponse());
337 
338  // we only allocate if the packet actually came from a cache, but
339  // start by checking if the port is snooping
340  if (cpkt->req->isUncacheable() || !slave_port.isSnooping())
341  return;
342 
343  // next check if we actually allocated an entry
344  Addr line_addr = cpkt->getBlockAddr(linesize);
345  if (cpkt->isSecure()) {
346  line_addr |= LineSecure;
347  }
348  auto sf_it = cachedLocations.find(line_addr);
349  if (sf_it == cachedLocations.end())
350  return;
351 
352  SnoopMask slave_mask = portToMask(slave_port);
353  SnoopItem& sf_item = sf_it->second;
354 
355  DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
356  __func__, sf_item.requested, sf_item.holder);
357 
358  // Make sure we have seen the actual request, too
359  panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\
360  "request bit\n", sf_item.requested, sf_item.holder);
361 
362  // Update the residency of the cache line.
363  sf_item.holder |= slave_mask;
364  sf_item.requested &= ~slave_mask;
365  assert(sf_item.holder | sf_item.requested);
366  DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
367  __func__, sf_item.requested, sf_item.holder);
368 }
369 
370 void
372 {
374 
376  .name(name() + ".tot_requests")
377  .desc("Total number of requests made to the snoop filter.");
378 
380  .name(name() + ".hit_single_requests")
381  .desc("Number of requests hitting in the snoop filter with a single "\
382  "holder of the requested data.");
383 
385  .name(name() + ".hit_multi_requests")
386  .desc("Number of requests hitting in the snoop filter with multiple "\
387  "(>1) holders of the requested data.");
388 
389  totSnoops
390  .name(name() + ".tot_snoops")
391  .desc("Total number of snoops made to the snoop filter.");
392 
394  .name(name() + ".hit_single_snoops")
395  .desc("Number of snoops hitting in the snoop filter with a single "\
396  "holder of the requested data.");
397 
399  .name(name() + ".hit_multi_snoops")
400  .desc("Number of snoops hitting in the snoop filter with multiple "\
401  "(>1) holders of the requested data.");
402 }
403 
404 SnoopFilter *
405 SnoopFilterParams::create()
406 {
407  return new SnoopFilter(this);
408 }
A MasterPort is a specialisation of a BaseMasterPort, which implements the default protocol for the t...
Definition: port.hh:167
bool isSecure() const
Definition: packet.hh:661
#define DPRINTF(x,...)
Definition: trace.hh:212
bool isUncacheable() const
Accessor functions for flags.
Definition: request.hh:767
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
bool isBlockCached() const
Definition: packet.hh:624
Stats::Scalar hitMultiSnoops
SnoopFilterCache::iterator reqLookupResult
Iterator used to store the result from lookupRequest until we call finishRequest. ...
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 std::string name() const
Return port name (for DPRINTF).
Definition: port.hh:99
const unsigned maxEntryCount
Max capacity in terms of cache blocks tracked, for sanity checking.
Addr getBlockAddr(unsigned int blk_size) const
Definition: packet.hh:656
ip6_addr_t addr
Definition: inet.hh:335
panic_if(!root,"Invalid expression\n")
Stats::Scalar totSnoops
bool fromCache() const
Definition: packet.hh:519
Definition of a snoop filter.
A SlavePort is a specialisation of a port.
Definition: port.hh:331
block holds data from the secure memory space
bool isWriteback() const
Definition: packet.hh:520
virtual void regStats()
Register statistics for this object.
Definition: sim_object.cc:105
bool isRequest() const
Definition: packet.hh:505
Stats::Scalar hitSingleSnoops
std::pair< SnoopList, Cycles > lookupSnoop(const Packet *cpkt)
Handle an incoming snoop from below (the master port).
bool isSnooping() const
Find out if the peer master port is snooping or not.
Definition: port.hh:405
SnoopMask portToMask(const SlavePort &port) const
Convert a single port to a corresponding, one-hot bitmask.
Stats::Scalar hitSingleRequests
bool hasSharers() const
Definition: packet.hh:585
const Cycles lookupLatency
Latency for doing a lookup in the filter.
const unsigned linesize
Cache line size.
Stats::Scalar totRequests
Statistics.
const RequestPtr req
A pointer to the original request.
Definition: packet.hh:304
void eraseIfNullEntry(SnoopFilterCache::iterator &sf_it)
Removes snoop filter items which have no requesters and no holders.
Definition: snoop_filter.cc:53
bool needsResponse() const
Definition: packet.hh:516
uint64_t SnoopMask
The underlying type for the bitmask we use for tracking.
std::pair< SnoopList, Cycles > snoopDown(Cycles latency) const
bool needsWritable() const
Definition: packet.hh:507
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:142
bool cacheResponding() const
Definition: packet.hh:558
void print(std::ostream &o, int verbosity=0, const std::string &prefix="") const
A Packet is used to encapsulate a transfer between two objects in the memory system (e...
Definition: packet.hh:245
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.
SnoopList maskToPortList(SnoopMask ports) const
Converts a bitmask of ports into the corresponing list of ports.
virtual void regStats()
Register statistics for this object.
Derived & name(const std::string &name)
Set the name and marks this stat to print at the end of simulation.
Definition: statistics.hh:254
std::pair< SnoopList, Cycles > snoopSelected(const SnoopList &slave_ports, Cycles latency) const
virtual const std::string name() const
Definition: sim_object.hh:117
Per cache line item tracking a bitmask of SlavePorts who have an outstanding request to this line (re...
Derived & desc(const std::string &_desc)
Set the description and marks this stat to print at the end of simulation.
Definition: statistics.hh:287
bool isEviction() const
Definition: packet.hh:518
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...
bool isInvalidate() const
Definition: packet.hh:517
This snoop filter keeps track of which connected port has a particular line of data.
Definition: snoop_filter.hh:87
bool isPow2(T v)
Checks if a number is a power of two, or zero.
Definition: bitfield.hh:198
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
bool isResponse() const
Definition: packet.hh:506
SnoopItem retryItem
Variable to temporarily store value of snoopfilter entry incase finishRequest needs to undo changes m...

Generated on Fri Jun 9 2017 13:03:50 for gem5 by doxygen 1.8.6