snoop_filter.cc (11129:48c02e8b0bbb) snoop_filter.cc (11131:22e739752f47)
1/*
2 * Copyright (c) 2013-2015 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 <stephan.diestelhorst@arm.com>
38 */
39
40/**
41 * @file
42 * Definition of a snoop filter.
43 */
44
45#include "base/misc.hh"
46#include "base/trace.hh"
47#include "debug/SnoopFilter.hh"
48#include "mem/snoop_filter.hh"
49#include "sim/system.hh"
50
51void
52SnoopFilter::eraseIfNullEntry(SnoopFilterCache::iterator& sf_it)
53{
54 SnoopItem& sf_item = sf_it->second;
55 if (!(sf_item.requested | sf_item.holder)) {
56 cachedLocations.erase(sf_it);
57 DPRINTF(SnoopFilter, "%s: Removed SF entry.\n",
58 __func__);
59 }
60}
61
62std::pair<SnoopFilter::SnoopList, Cycles>
63SnoopFilter::lookupRequest(const Packet* cpkt, const SlavePort& slave_port)
64{
65 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
66 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
67
68 // Ultimately we should check if the packet came from an
69 // allocating source, not just if the port is snooping
70 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping();
71 Addr line_addr = cpkt->getBlockAddr(linesize);
72 SnoopMask req_port = portToMask(slave_port);
1/*
2 * Copyright (c) 2013-2015 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 <stephan.diestelhorst@arm.com>
38 */
39
40/**
41 * @file
42 * Definition of a snoop filter.
43 */
44
45#include "base/misc.hh"
46#include "base/trace.hh"
47#include "debug/SnoopFilter.hh"
48#include "mem/snoop_filter.hh"
49#include "sim/system.hh"
50
51void
52SnoopFilter::eraseIfNullEntry(SnoopFilterCache::iterator& sf_it)
53{
54 SnoopItem& sf_item = sf_it->second;
55 if (!(sf_item.requested | sf_item.holder)) {
56 cachedLocations.erase(sf_it);
57 DPRINTF(SnoopFilter, "%s: Removed SF entry.\n",
58 __func__);
59 }
60}
61
62std::pair<SnoopFilter::SnoopList, Cycles>
63SnoopFilter::lookupRequest(const Packet* cpkt, const SlavePort& slave_port)
64{
65 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
66 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
67
68 // Ultimately we should check if the packet came from an
69 // allocating source, not just if the port is snooping
70 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping();
71 Addr line_addr = cpkt->getBlockAddr(linesize);
72 SnoopMask req_port = portToMask(slave_port);
73 auto sf_it = cachedLocations.find(line_addr);
74 bool is_hit = (sf_it != cachedLocations.end());
73 reqLookupResult = cachedLocations.find(line_addr);
74 bool is_hit = (reqLookupResult != cachedLocations.end());
75
76 // If the snoop filter has no entry, and we should not allocate,
77 // do not create a new snoop filter entry, simply return a NULL
78 // portlist.
79 if (!is_hit && !allocate)
80 return snoopDown(lookupLatency);
81
75
76 // If the snoop filter has no entry, and we should not allocate,
77 // do not create a new snoop filter entry, simply return a NULL
78 // portlist.
79 if (!is_hit && !allocate)
80 return snoopDown(lookupLatency);
81
82 // Create a new element through operator[] and modify in-place
83 SnoopItem& sf_item = is_hit ? sf_it->second : cachedLocations[line_addr];
82 // If no hit in snoop filter create a new element and update iterator
83 if (!is_hit)
84 reqLookupResult = cachedLocations.emplace(line_addr, SnoopItem()).first;
85 SnoopItem& sf_item = reqLookupResult->second;
84 SnoopMask interested = sf_item.holder | sf_item.requested;
85
86 // Store unmodified value of snoop filter item in temp storage in
87 // case we need to revert because of a send retry in
88 // updateRequest.
89 retryItem = sf_item;
90
91 totRequests++;
92 if (is_hit) {
93 // Single bit set -> value is a power of two
94 if (isPow2(interested))
95 hitSingleRequests++;
96 else
97 hitMultiRequests++;
98 }
99
100 DPRINTF(SnoopFilter, "%s: SF value %x.%x\n",
101 __func__, sf_item.requested, sf_item.holder);
102
103 // If we are not allocating, we are done
104 if (!allocate)
105 return snoopSelected(maskToPortList(interested & ~req_port),
106 lookupLatency);
107
108 if (cpkt->needsResponse()) {
109 if (!cpkt->memInhibitAsserted()) {
110 // Max one request per address per port
111 panic_if(sf_item.requested & req_port, "double request :( " \
112 "SF value %x.%x\n", sf_item.requested, sf_item.holder);
113
114 // Mark in-flight requests to distinguish later on
115 sf_item.requested |= req_port;
116 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
117 __func__, sf_item.requested, sf_item.holder);
118 } else {
119 // NOTE: The memInhibit might have been asserted by a cache closer
120 // to the CPU, already -> the response will not be seen by this
121 // filter -> we do not need to keep the in-flight request, but make
122 // sure that we know that that cluster has a copy
123 panic_if(!(sf_item.holder & req_port), "Need to hold the value!");
124 DPRINTF(SnoopFilter,
125 "%s: not marking request. SF value %x.%x\n",
126 __func__, sf_item.requested, sf_item.holder);
127 }
128 } else { // if (!cpkt->needsResponse())
129 assert(cpkt->evictingBlock());
130 // make sure that the sender actually had the line
131 panic_if(!(sf_item.holder & req_port), "requester %x is not a " \
132 "holder :( SF value %x.%x\n", req_port,
133 sf_item.requested, sf_item.holder);
134 // CleanEvicts and Writebacks -> the sender and all caches above
135 // it may not have the line anymore.
136 if (!cpkt->isBlockCached()) {
137 sf_item.holder &= ~req_port;
138 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
139 __func__, sf_item.requested, sf_item.holder);
140 }
141 }
142
143 return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
144}
145
146void
86 SnoopMask interested = sf_item.holder | sf_item.requested;
87
88 // Store unmodified value of snoop filter item in temp storage in
89 // case we need to revert because of a send retry in
90 // updateRequest.
91 retryItem = sf_item;
92
93 totRequests++;
94 if (is_hit) {
95 // Single bit set -> value is a power of two
96 if (isPow2(interested))
97 hitSingleRequests++;
98 else
99 hitMultiRequests++;
100 }
101
102 DPRINTF(SnoopFilter, "%s: SF value %x.%x\n",
103 __func__, sf_item.requested, sf_item.holder);
104
105 // If we are not allocating, we are done
106 if (!allocate)
107 return snoopSelected(maskToPortList(interested & ~req_port),
108 lookupLatency);
109
110 if (cpkt->needsResponse()) {
111 if (!cpkt->memInhibitAsserted()) {
112 // Max one request per address per port
113 panic_if(sf_item.requested & req_port, "double request :( " \
114 "SF value %x.%x\n", sf_item.requested, sf_item.holder);
115
116 // Mark in-flight requests to distinguish later on
117 sf_item.requested |= req_port;
118 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
119 __func__, sf_item.requested, sf_item.holder);
120 } else {
121 // NOTE: The memInhibit might have been asserted by a cache closer
122 // to the CPU, already -> the response will not be seen by this
123 // filter -> we do not need to keep the in-flight request, but make
124 // sure that we know that that cluster has a copy
125 panic_if(!(sf_item.holder & req_port), "Need to hold the value!");
126 DPRINTF(SnoopFilter,
127 "%s: not marking request. SF value %x.%x\n",
128 __func__, sf_item.requested, sf_item.holder);
129 }
130 } else { // if (!cpkt->needsResponse())
131 assert(cpkt->evictingBlock());
132 // make sure that the sender actually had the line
133 panic_if(!(sf_item.holder & req_port), "requester %x is not a " \
134 "holder :( SF value %x.%x\n", req_port,
135 sf_item.requested, sf_item.holder);
136 // CleanEvicts and Writebacks -> the sender and all caches above
137 // it may not have the line anymore.
138 if (!cpkt->isBlockCached()) {
139 sf_item.holder &= ~req_port;
140 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
141 __func__, sf_item.requested, sf_item.holder);
142 }
143 }
144
145 return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
146}
147
148void
147SnoopFilter::updateRequest(const Packet* cpkt, const SlavePort& slave_port,
148 bool will_retry)
149SnoopFilter::finishRequest(bool will_retry, const Packet* cpkt)
149{
150{
150 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
151 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
151 if (reqLookupResult != cachedLocations.end()) {
152 // since we rely on the caller, do a basic check to ensure
153 // that finishRequest is being called following lookupRequest
154 assert(reqLookupResult->first == cpkt->getBlockAddr(linesize));
155 if (will_retry) {
156 // Undo any changes made in lookupRequest to the snoop filter
157 // entry if the request will come again. retryItem holds
158 // the previous value of the snoopfilter entry.
159 reqLookupResult->second = retryItem;
152
160
153 // Ultimately we should check if the packet came from an
154 // allocating source, not just if the port is snooping
155 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping();
156 if (!allocate)
157 return;
161 DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n",
162 __func__, retryItem.requested, retryItem.holder);
163 }
158
164
159 Addr line_addr = cpkt->getBlockAddr(linesize);
160 auto sf_it = cachedLocations.find(line_addr);
161 assert(sf_it != cachedLocations.end());
162 if (will_retry) {
163 // Undo any changes made in lookupRequest to the snoop filter
164 // entry if the request will come again. retryItem holds
165 // the previous value of the snoopfilter entry.
166 sf_it->second = retryItem;
167
168 DPRINTF(SnoopFilter, "%s: restored SF value %x.%x\n",
169 __func__, retryItem.requested, retryItem.holder);
165 eraseIfNullEntry(reqLookupResult);
170 }
166 }
171
172 eraseIfNullEntry(sf_it);
173}
174
175std::pair<SnoopFilter::SnoopList, Cycles>
176SnoopFilter::lookupSnoop(const Packet* cpkt)
177{
178 DPRINTF(SnoopFilter, "%s: packet addr 0x%x cmd %s\n",
179 __func__, cpkt->getAddr(), cpkt->cmdString());
180
181 assert(cpkt->isRequest());
182
183 // Broadcast / filter upward snoops
184 const bool filter_upward = true; // @todo: Make configurable
185
186 if (!filter_upward)
187 return snoopAll(lookupLatency);
188
189 Addr line_addr = cpkt->getBlockAddr(linesize);
190 auto sf_it = cachedLocations.find(line_addr);
191 bool is_hit = (sf_it != cachedLocations.end());
192
193 // If the snoop filter has no entry and its an uncacheable
194 // request, do not create a new snoop filter entry, simply return
195 // a NULL portlist.
196 if (!is_hit && cpkt->req->isUncacheable())
197 return snoopDown(lookupLatency);
198
199 // If no hit in snoop filter create a new element and update iterator
200 if (!is_hit)
201 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first;
202 SnoopItem& sf_item = sf_it->second;
203
204 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
205 __func__, sf_item.requested, sf_item.holder);
206
207 SnoopMask interested = (sf_item.holder | sf_item.requested);
208
209 totSnoops++;
210 if (is_hit) {
211 // Single bit set -> value is a power of two
212 if (isPow2(interested))
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 aissue. Now that Writebacks generate snoops
221 // we need to special case for Writebacks.
222 assert(cpkt->cmd == MemCmd::Writeback || cpkt->req->isUncacheable() ||
223 (cpkt->isInvalidate() == cpkt->needsExclusive()));
224 if (cpkt->isInvalidate() && !sf_item.requested) {
225 // Early clear of the holder, if no other request is currently going on
226 // @todo: This should possibly be updated even though we do not filter
227 // upward snoops
228 sf_item.holder = 0;
229 }
230
231 eraseIfNullEntry(sf_it);
232 DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n",
233 __func__, sf_item.requested, sf_item.holder, interested);
234
235 return snoopSelected(maskToPortList(interested), lookupLatency);
236}
237
238void
239SnoopFilter::updateSnoopResponse(const Packet* cpkt,
240 const SlavePort& rsp_port,
241 const SlavePort& req_port)
242{
243 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
244 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
245 cpkt->cmdString());
246
247 assert(cpkt->isResponse());
248 assert(cpkt->memInhibitAsserted());
249
250 // Ultimately we should check if the packet came from an
251 // allocating source, not just if the port is snooping
252 bool allocate = !cpkt->req->isUncacheable() && req_port.isSnooping();
253 if (!allocate)
254 return;
255
256 Addr line_addr = cpkt->getBlockAddr(linesize);
257 SnoopMask rsp_mask = portToMask(rsp_port);
258 SnoopMask req_mask = portToMask(req_port);
259 SnoopItem& sf_item = cachedLocations[line_addr];
260
261 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
262 __func__, sf_item.requested, sf_item.holder);
263
264 // The source should have the line
265 panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\
266 "the line\n", sf_item.requested, sf_item.holder);
267
268 // The destination should have had a request in
269 panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\
270 "the original request\n", sf_item.requested, sf_item.holder);
271
272 // Update the residency of the cache line.
273 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) {
274 DPRINTF(SnoopFilter, "%s: dropping %x because needs: %i shared: %i "\
275 "SF val: %x.%x\n", __func__, rsp_mask,
276 cpkt->needsExclusive(), cpkt->sharedAsserted(),
277 sf_item.requested, sf_item.holder);
278
279 sf_item.holder &= ~rsp_mask;
280 // The snoop filter does not see any ACKs from non-responding sharers
281 // that have been invalidated :( So below assert would be nice, but..
282 //assert(sf_item.holder == 0);
283 sf_item.holder = 0;
284 }
285 assert(cpkt->cmd != MemCmd::Writeback);
286 sf_item.holder |= req_mask;
287 sf_item.requested &= ~req_mask;
288 assert(sf_item.requested | sf_item.holder);
289 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
290 __func__, sf_item.requested, sf_item.holder);
291}
292
293void
294SnoopFilter::updateSnoopForward(const Packet* cpkt,
295 const SlavePort& rsp_port, const MasterPort& req_port)
296{
297 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
298 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
299 cpkt->cmdString());
300
301 Addr line_addr = cpkt->getBlockAddr(linesize);
302 auto sf_it = cachedLocations.find(line_addr);
303 if (sf_it == cachedLocations.end())
304 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first;
305 SnoopItem& sf_item = sf_it->second;
306 SnoopMask rsp_mask M5_VAR_USED = portToMask(rsp_port);
307
308 assert(cpkt->isResponse());
309 assert(cpkt->memInhibitAsserted());
310
311 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
312 __func__, sf_item.requested, sf_item.holder);
313
314 // Remote (to this snoop filter) snoops update the filter already when they
315 // arrive from below, because we may not see any response.
316 if (cpkt->needsExclusive()) {
317 // If the request to this snoop response hit an in-flight transaction,
318 // the holder was not reset -> no assertion & do that here, now!
319 //assert(sf_item.holder == 0);
320 sf_item.holder = 0;
321 }
322 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
323 __func__, sf_item.requested, sf_item.holder);
324 eraseIfNullEntry(sf_it);
325}
326
327void
328SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port)
329{
330 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
331 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
332
333 assert(cpkt->isResponse());
334
335 // Ultimately we should check if the packet came from an
336 // allocating source, not just if the port is snooping
337 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping();
338 if (!allocate)
339 return;
340
341 Addr line_addr = cpkt->getBlockAddr(linesize);
342 SnoopMask slave_mask = portToMask(slave_port);
343 SnoopItem& sf_item = cachedLocations[line_addr];
344
345 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
346 __func__, sf_item.requested, sf_item.holder);
347
348 // Make sure we have seen the actual request, too
349 panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\
350 "request bit\n", sf_item.requested, sf_item.holder);
351
352 // Update the residency of the cache line. Here we assume that the
353 // line has been zapped in all caches that are not the responder.
354 if (cpkt->needsExclusive() || !cpkt->sharedAsserted())
355 sf_item.holder = 0;
356 sf_item.holder |= slave_mask;
357 sf_item.requested &= ~slave_mask;
358 assert(sf_item.holder | sf_item.requested);
359 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
360 __func__, sf_item.requested, sf_item.holder);
361}
362
363void
364SnoopFilter::regStats()
365{
366 totRequests
367 .name(name() + ".tot_requests")
368 .desc("Total number of requests made to the snoop filter.");
369
370 hitSingleRequests
371 .name(name() + ".hit_single_requests")
372 .desc("Number of requests hitting in the snoop filter with a single "\
373 "holder of the requested data.");
374
375 hitMultiRequests
376 .name(name() + ".hit_multi_requests")
377 .desc("Number of requests hitting in the snoop filter with multiple "\
378 "(>1) holders of the requested data.");
379
380 totSnoops
381 .name(name() + ".tot_snoops")
382 .desc("Total number of snoops made to the snoop filter.");
383
384 hitSingleSnoops
385 .name(name() + ".hit_single_snoops")
386 .desc("Number of snoops hitting in the snoop filter with a single "\
387 "holder of the requested data.");
388
389 hitMultiSnoops
390 .name(name() + ".hit_multi_snoops")
391 .desc("Number of snoops hitting in the snoop filter with multiple "\
392 "(>1) holders of the requested data.");
393}
394
395SnoopFilter *
396SnoopFilterParams::create()
397{
398 return new SnoopFilter(this);
399}
167}
168
169std::pair<SnoopFilter::SnoopList, Cycles>
170SnoopFilter::lookupSnoop(const Packet* cpkt)
171{
172 DPRINTF(SnoopFilter, "%s: packet addr 0x%x cmd %s\n",
173 __func__, cpkt->getAddr(), cpkt->cmdString());
174
175 assert(cpkt->isRequest());
176
177 // Broadcast / filter upward snoops
178 const bool filter_upward = true; // @todo: Make configurable
179
180 if (!filter_upward)
181 return snoopAll(lookupLatency);
182
183 Addr line_addr = cpkt->getBlockAddr(linesize);
184 auto sf_it = cachedLocations.find(line_addr);
185 bool is_hit = (sf_it != cachedLocations.end());
186
187 // If the snoop filter has no entry and its an uncacheable
188 // request, do not create a new snoop filter entry, simply return
189 // a NULL portlist.
190 if (!is_hit && cpkt->req->isUncacheable())
191 return snoopDown(lookupLatency);
192
193 // If no hit in snoop filter create a new element and update iterator
194 if (!is_hit)
195 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first;
196 SnoopItem& sf_item = sf_it->second;
197
198 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
199 __func__, sf_item.requested, sf_item.holder);
200
201 SnoopMask interested = (sf_item.holder | sf_item.requested);
202
203 totSnoops++;
204 if (is_hit) {
205 // Single bit set -> value is a power of two
206 if (isPow2(interested))
207 hitSingleSnoops++;
208 else
209 hitMultiSnoops++;
210 }
211 // ReadEx and Writes require both invalidation and exlusivity, while reads
212 // require neither. Writebacks on the other hand require exclusivity but
213 // not the invalidation. Previously Writebacks did not generate upward
214 // snoops so this was never an aissue. Now that Writebacks generate snoops
215 // we need to special case for Writebacks.
216 assert(cpkt->cmd == MemCmd::Writeback || cpkt->req->isUncacheable() ||
217 (cpkt->isInvalidate() == cpkt->needsExclusive()));
218 if (cpkt->isInvalidate() && !sf_item.requested) {
219 // Early clear of the holder, if no other request is currently going on
220 // @todo: This should possibly be updated even though we do not filter
221 // upward snoops
222 sf_item.holder = 0;
223 }
224
225 eraseIfNullEntry(sf_it);
226 DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n",
227 __func__, sf_item.requested, sf_item.holder, interested);
228
229 return snoopSelected(maskToPortList(interested), lookupLatency);
230}
231
232void
233SnoopFilter::updateSnoopResponse(const Packet* cpkt,
234 const SlavePort& rsp_port,
235 const SlavePort& req_port)
236{
237 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
238 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
239 cpkt->cmdString());
240
241 assert(cpkt->isResponse());
242 assert(cpkt->memInhibitAsserted());
243
244 // Ultimately we should check if the packet came from an
245 // allocating source, not just if the port is snooping
246 bool allocate = !cpkt->req->isUncacheable() && req_port.isSnooping();
247 if (!allocate)
248 return;
249
250 Addr line_addr = cpkt->getBlockAddr(linesize);
251 SnoopMask rsp_mask = portToMask(rsp_port);
252 SnoopMask req_mask = portToMask(req_port);
253 SnoopItem& sf_item = cachedLocations[line_addr];
254
255 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
256 __func__, sf_item.requested, sf_item.holder);
257
258 // The source should have the line
259 panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\
260 "the line\n", sf_item.requested, sf_item.holder);
261
262 // The destination should have had a request in
263 panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\
264 "the original request\n", sf_item.requested, sf_item.holder);
265
266 // Update the residency of the cache line.
267 if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) {
268 DPRINTF(SnoopFilter, "%s: dropping %x because needs: %i shared: %i "\
269 "SF val: %x.%x\n", __func__, rsp_mask,
270 cpkt->needsExclusive(), cpkt->sharedAsserted(),
271 sf_item.requested, sf_item.holder);
272
273 sf_item.holder &= ~rsp_mask;
274 // The snoop filter does not see any ACKs from non-responding sharers
275 // that have been invalidated :( So below assert would be nice, but..
276 //assert(sf_item.holder == 0);
277 sf_item.holder = 0;
278 }
279 assert(cpkt->cmd != MemCmd::Writeback);
280 sf_item.holder |= req_mask;
281 sf_item.requested &= ~req_mask;
282 assert(sf_item.requested | sf_item.holder);
283 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
284 __func__, sf_item.requested, sf_item.holder);
285}
286
287void
288SnoopFilter::updateSnoopForward(const Packet* cpkt,
289 const SlavePort& rsp_port, const MasterPort& req_port)
290{
291 DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
292 __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
293 cpkt->cmdString());
294
295 Addr line_addr = cpkt->getBlockAddr(linesize);
296 auto sf_it = cachedLocations.find(line_addr);
297 if (sf_it == cachedLocations.end())
298 sf_it = cachedLocations.emplace(line_addr, SnoopItem()).first;
299 SnoopItem& sf_item = sf_it->second;
300 SnoopMask rsp_mask M5_VAR_USED = portToMask(rsp_port);
301
302 assert(cpkt->isResponse());
303 assert(cpkt->memInhibitAsserted());
304
305 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
306 __func__, sf_item.requested, sf_item.holder);
307
308 // Remote (to this snoop filter) snoops update the filter already when they
309 // arrive from below, because we may not see any response.
310 if (cpkt->needsExclusive()) {
311 // If the request to this snoop response hit an in-flight transaction,
312 // the holder was not reset -> no assertion & do that here, now!
313 //assert(sf_item.holder == 0);
314 sf_item.holder = 0;
315 }
316 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
317 __func__, sf_item.requested, sf_item.holder);
318 eraseIfNullEntry(sf_it);
319}
320
321void
322SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port)
323{
324 DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
325 __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
326
327 assert(cpkt->isResponse());
328
329 // Ultimately we should check if the packet came from an
330 // allocating source, not just if the port is snooping
331 bool allocate = !cpkt->req->isUncacheable() && slave_port.isSnooping();
332 if (!allocate)
333 return;
334
335 Addr line_addr = cpkt->getBlockAddr(linesize);
336 SnoopMask slave_mask = portToMask(slave_port);
337 SnoopItem& sf_item = cachedLocations[line_addr];
338
339 DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
340 __func__, sf_item.requested, sf_item.holder);
341
342 // Make sure we have seen the actual request, too
343 panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\
344 "request bit\n", sf_item.requested, sf_item.holder);
345
346 // Update the residency of the cache line. Here we assume that the
347 // line has been zapped in all caches that are not the responder.
348 if (cpkt->needsExclusive() || !cpkt->sharedAsserted())
349 sf_item.holder = 0;
350 sf_item.holder |= slave_mask;
351 sf_item.requested &= ~slave_mask;
352 assert(sf_item.holder | sf_item.requested);
353 DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
354 __func__, sf_item.requested, sf_item.holder);
355}
356
357void
358SnoopFilter::regStats()
359{
360 totRequests
361 .name(name() + ".tot_requests")
362 .desc("Total number of requests made to the snoop filter.");
363
364 hitSingleRequests
365 .name(name() + ".hit_single_requests")
366 .desc("Number of requests hitting in the snoop filter with a single "\
367 "holder of the requested data.");
368
369 hitMultiRequests
370 .name(name() + ".hit_multi_requests")
371 .desc("Number of requests hitting in the snoop filter with multiple "\
372 "(>1) holders of the requested data.");
373
374 totSnoops
375 .name(name() + ".tot_snoops")
376 .desc("Total number of snoops made to the snoop filter.");
377
378 hitSingleSnoops
379 .name(name() + ".hit_single_snoops")
380 .desc("Number of snoops hitting in the snoop filter with a single "\
381 "holder of the requested data.");
382
383 hitMultiSnoops
384 .name(name() + ".hit_multi_snoops")
385 .desc("Number of snoops hitting in the snoop filter with multiple "\
386 "(>1) holders of the requested data.");
387}
388
389SnoopFilter *
390SnoopFilterParams::create()
391{
392 return new SnoopFilter(this);
393}