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