snoop_filter.cc revision 10821:581fb2484bd6
1/*
2 * Copyright (c) 2013 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
51std::pair<SnoopFilter::SnoopList, Cycles>
52SnoopFilter::lookupRequest(const Packet* cpkt, const SlavePort& slave_port)
53{
54    DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
55            __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
56
57    Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
58    SnoopMask req_port = portToMask(slave_port);
59    auto sf_it = cachedLocations.find(line_addr);
60    bool is_hit = (sf_it != cachedLocations.end());
61    // Create a new element through operator[] and modify in-place
62    SnoopItem& sf_item = is_hit ? sf_it->second : cachedLocations[line_addr];
63    SnoopMask interested = sf_item.holder | sf_item.requested;
64
65    totRequests++;
66    if (is_hit) {
67        // Single bit set -> value is a power of two
68        if (isPow2(interested))
69            hitSingleRequests++;
70        else
71            hitMultiRequests++;
72    }
73
74    DPRINTF(SnoopFilter, "%s:   SF value %x.%x\n",
75            __func__, sf_item.requested, sf_item.holder);
76
77    if (!cpkt->req->isUncacheable() && cpkt->needsResponse()) {
78        if (!cpkt->memInhibitAsserted()) {
79            // Max one request per address per port
80            panic_if(sf_item.requested & req_port, "double request :( "\
81                     "SF value %x.%x\n", sf_item.requested, sf_item.holder);
82
83            // Mark in-flight requests to distinguish later on
84            sf_item.requested |= req_port;
85        } else {
86            // NOTE: The memInhibit might have been asserted by a cache closer
87            // to the CPU, already -> the response will not be seen by this
88            // filter -> we do not need to keep the in-flight request, but make
89            // sure that we know that that cluster has a copy
90            panic_if(!(sf_item.holder & req_port), "Need to hold the value!");
91            DPRINTF(SnoopFilter, "%s:   not marking request. SF value %x.%x\n",
92                    __func__,  sf_item.requested, sf_item.holder);
93        }
94        DPRINTF(SnoopFilter, "%s:   new SF value %x.%x\n",
95                __func__,  sf_item.requested, sf_item.holder);
96    }
97    return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
98}
99
100void
101SnoopFilter::updateRequest(const Packet* cpkt, const SlavePort& slave_port,
102                           bool will_retry)
103{
104    DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
105            __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
106
107    if (cpkt->req->isUncacheable())
108        return;
109
110    Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
111    SnoopMask req_port = portToMask(slave_port);
112    SnoopItem& sf_item  = cachedLocations[line_addr];
113
114    DPRINTF(SnoopFilter, "%s:   old SF value %x.%x retry: %i\n",
115            __func__, sf_item.requested, sf_item.holder, will_retry);
116
117    if (will_retry) {
118        // Unmark a request that will come again.
119        sf_item.requested &= ~req_port;
120        return;
121    }
122
123    // will_retry == false
124    if (!cpkt->needsResponse()) {
125        // Packets that will not evoke a response but still need updates of the
126        // snoop filter; WRITEBACKs for now only
127        if (cpkt->cmd == MemCmd::Writeback) {
128            // make sure that the sender actually had the line
129            panic_if(sf_item.requested & req_port, "double request :( "\
130                     "SF value %x.%x\n", sf_item.requested, sf_item.holder);
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            // Writebacks -> the sender does not have the line anymore
135            sf_item.holder &= ~req_port;
136        } else {
137            assert(0 == "Handle non-writeback, here");
138        }
139        DPRINTF(SnoopFilter, "%s:   new SF value %x.%x\n",
140                __func__,  sf_item.requested, sf_item.holder);
141    }
142}
143
144std::pair<SnoopFilter::SnoopList, Cycles>
145SnoopFilter::lookupSnoop(const Packet* cpkt)
146{
147    DPRINTF(SnoopFilter, "%s: packet addr 0x%x cmd %s\n",
148            __func__, cpkt->getAddr(), cpkt->cmdString());
149
150    assert(cpkt->isRequest());
151
152    // Broadcast / filter upward snoops
153    const bool filter_upward = true;  // @todo: Make configurable
154
155    if (!filter_upward)
156        return snoopAll(lookupLatency);
157
158    Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
159    auto sf_it = cachedLocations.find(line_addr);
160    bool is_hit = (sf_it != cachedLocations.end());
161    // Create a new element through operator[] and modify in-place
162    SnoopItem& sf_item = is_hit ? sf_it->second : cachedLocations[line_addr];
163
164    DPRINTF(SnoopFilter, "%s:   old SF value %x.%x\n",
165            __func__, sf_item.requested, sf_item.holder);
166
167    SnoopMask interested = (sf_item.holder | sf_item.requested);
168
169    totSnoops++;
170    if (is_hit) {
171        // Single bit set -> value is a power of two
172        if (isPow2(interested))
173            hitSingleSnoops++;
174        else
175            hitMultiSnoops++;
176    }
177
178    assert(cpkt->isInvalidate() == cpkt->needsExclusive());
179    if (cpkt->isInvalidate() && !sf_item.requested) {
180        // Early clear of the holder, if no other request is currently going on
181        // @todo: This should possibly be updated even though we do not filter
182        // upward snoops
183        sf_item.holder = 0;
184    }
185
186    DPRINTF(SnoopFilter, "%s:   new SF value %x.%x interest: %x \n",
187            __func__, sf_item.requested, sf_item.holder, interested);
188
189    return snoopSelected(maskToPortList(interested), lookupLatency);
190}
191
192void
193SnoopFilter::updateSnoopResponse(const Packet* cpkt,
194                                 const SlavePort& rsp_port,
195                                 const SlavePort& req_port)
196{
197    DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
198            __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
199            cpkt->cmdString());
200
201    assert(cpkt->isResponse());
202    assert(cpkt->memInhibitAsserted());
203
204    if (cpkt->req->isUncacheable())
205        return;
206
207    Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
208    SnoopMask rsp_mask = portToMask(rsp_port);
209    SnoopMask req_mask = portToMask(req_port);
210    SnoopItem& sf_item = cachedLocations[line_addr];
211
212    DPRINTF(SnoopFilter, "%s:   old SF value %x.%x\n",
213            __func__,  sf_item.requested, sf_item.holder);
214
215    // The source should have the line
216    panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\
217             "the line\n", sf_item.requested, sf_item.holder);
218
219    // The destination should have had a request in
220    panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\
221             "the original request\n",  sf_item.requested, sf_item.holder);
222
223    // Update the residency of the cache line.
224    if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) {
225        DPRINTF(SnoopFilter, "%s:  dropping %x because needs: %i shared: %i "\
226                "SF val: %x.%x\n", __func__,  rsp_mask,
227                cpkt->needsExclusive(), cpkt->sharedAsserted(),
228                sf_item.requested, sf_item.holder);
229
230        sf_item.holder &= ~rsp_mask;
231        // The snoop filter does not see any ACKs from non-responding sharers
232        // that have been invalidated :(  So below assert would be nice, but..
233        //assert(sf_item.holder == 0);
234        sf_item.holder = 0;
235    }
236    assert(cpkt->cmd != MemCmd::Writeback);
237    sf_item.holder |=  req_mask;
238    sf_item.requested &= ~req_mask;
239    DPRINTF(SnoopFilter, "%s:   new SF value %x.%x\n",
240            __func__, sf_item.requested, sf_item.holder);
241}
242
243void
244SnoopFilter::updateSnoopForward(const Packet* cpkt,
245        const SlavePort& rsp_port, const MasterPort& req_port)
246{
247    DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
248            __func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
249            cpkt->cmdString());
250
251    Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
252    SnoopItem& sf_item = cachedLocations[line_addr];
253    SnoopMask rsp_mask M5_VAR_USED = portToMask(rsp_port);
254
255    assert(cpkt->isResponse());
256    assert(cpkt->memInhibitAsserted());
257
258    DPRINTF(SnoopFilter, "%s:   old SF value %x.%x\n",
259            __func__,  sf_item.requested, sf_item.holder);
260
261    // Remote (to this snoop filter) snoops update the filter already when they
262    // arrive from below, because we may not see any response.
263    if (cpkt->needsExclusive()) {
264        // If the request to this snoop response hit an in-flight transaction,
265        // the holder was not reset -> no assertion & do that here, now!
266        //assert(sf_item.holder == 0);
267        sf_item.holder = 0;
268    }
269    DPRINTF(SnoopFilter, "%s:   new SF value %x.%x\n",
270            __func__, sf_item.requested, sf_item.holder);
271}
272
273void
274SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port)
275{
276    DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
277            __func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
278
279    assert(cpkt->isResponse());
280
281    if (cpkt->req->isUncacheable())
282        return;
283
284    Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
285    SnoopMask slave_mask = portToMask(slave_port);
286    SnoopItem& sf_item = cachedLocations[line_addr];
287
288    DPRINTF(SnoopFilter, "%s:   old SF value %x.%x\n",
289            __func__,  sf_item.requested, sf_item.holder);
290
291    // Make sure we have seen the actual request, too
292    panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\
293             "request bit\n", sf_item.requested, sf_item.holder);
294
295    // Update the residency of the cache line.
296    if (cpkt->needsExclusive() || !cpkt->sharedAsserted())
297        sf_item.holder = 0;
298    sf_item.holder |=  slave_mask;
299    sf_item.requested &= ~slave_mask;
300    DPRINTF(SnoopFilter, "%s:   new SF value %x.%x\n",
301            __func__, sf_item.requested, sf_item.holder);
302}
303
304void
305SnoopFilter::regStats()
306{
307    totRequests
308        .name(name() + ".tot_requests")
309        .desc("Total number of requests made to the snoop filter.");
310
311    hitSingleRequests
312        .name(name() + ".hit_single_requests")
313        .desc("Number of requests hitting in the snoop filter with a single "\
314              "holder of the requested data.");
315
316    hitMultiRequests
317        .name(name() + ".hit_multi_requests")
318        .desc("Number of requests hitting in the snoop filter with multiple "\
319              "(>1) holders of the requested data.");
320
321    totSnoops
322        .name(name() + ".tot_snoops")
323        .desc("Total number of snoops made to the snoop filter.");
324
325    hitSingleSnoops
326        .name(name() + ".hit_single_snoops")
327        .desc("Number of snoops hitting in the snoop filter with a single "\
328              "holder of the requested data.");
329
330    hitMultiSnoops
331        .name(name() + ".hit_multi_snoops")
332        .desc("Number of snoops hitting in the snoop filter with multiple "\
333              "(>1) holders of the requested data.");
334}
335
336SnoopFilter *
337SnoopFilterParams::create()
338{
339    return new SnoopFilter(this);
340}
341