snoop_filter.cc revision 11129
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());
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];
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
147SnoopFilter::updateRequest(const Packet* cpkt, const SlavePort& slave_port,
148                           bool will_retry)
149{
150    DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
151            __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
152
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;
158
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);
170    }
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}
400