physical.cc revision 10482
12810SN/A/*
29614Srene.dejong@arm.com * Copyright (c) 2012, 2014 ARM Limited
38856Sandreas.hansson@arm.com * All rights reserved
48856Sandreas.hansson@arm.com *
58856Sandreas.hansson@arm.com * The license below extends only to copyright in the software and shall
68856Sandreas.hansson@arm.com * not be construed as granting a license to any other intellectual
78856Sandreas.hansson@arm.com * property including but not limited to intellectual property relating
88856Sandreas.hansson@arm.com * to a hardware implementation of the functionality of the software
98856Sandreas.hansson@arm.com * licensed hereunder.  You may use the software subject to the license
108856Sandreas.hansson@arm.com * terms below provided that you ensure that this notice is replicated
118856Sandreas.hansson@arm.com * unmodified and in its entirety in all distributions of the software,
128856Sandreas.hansson@arm.com * modified or unmodified, in source code or in binary form.
138856Sandreas.hansson@arm.com *
142810SN/A * Redistribution and use in source and binary forms, with or without
152810SN/A * modification, are permitted provided that the following conditions are
162810SN/A * met: redistributions of source code must retain the above copyright
172810SN/A * notice, this list of conditions and the following disclaimer;
182810SN/A * redistributions in binary form must reproduce the above copyright
192810SN/A * notice, this list of conditions and the following disclaimer in the
202810SN/A * documentation and/or other materials provided with the distribution;
212810SN/A * neither the name of the copyright holders nor the names of its
222810SN/A * contributors may be used to endorse or promote products derived from
232810SN/A * this software without specific prior written permission.
242810SN/A *
252810SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
262810SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
272810SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
282810SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
292810SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
302810SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
312810SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
322810SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
332810SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
342810SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
352810SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
362810SN/A *
372810SN/A * Authors: Andreas Hansson
382810SN/A */
392810SN/A
402810SN/A#include <sys/mman.h>
412810SN/A#include <sys/types.h>
422810SN/A#include <sys/user.h>
432810SN/A#include <fcntl.h>
442810SN/A#include <unistd.h>
452810SN/A#include <zlib.h>
462810SN/A
472810SN/A#include <cerrno>
488232Snate@binkert.org#include <climits>
499152Satgutier@umich.edu#include <cstdio>
509795Sandreas.hansson@arm.com#include <iostream>
519795Sandreas.hansson@arm.com#include <string>
5210263Satgutier@umich.edu
535338Sstever@gmail.com#include "base/trace.hh"
549795Sandreas.hansson@arm.com#include "debug/AddrRanges.hh"
555338Sstever@gmail.com#include "debug/Checkpoint.hh"
568786Sgblack@eecs.umich.edu#include "mem/abstract_mem.hh"
572810SN/A#include "mem/physical.hh"
582810SN/A
592810SN/Ausing namespace std;
608856Sandreas.hansson@arm.com
618856Sandreas.hansson@arm.comPhysicalMemory::PhysicalMemory(const string& _name,
628856Sandreas.hansson@arm.com                               const vector<AbstractMemory*>& _memories) :
638922Swilliam.wang@arm.com    _name(_name), size(0)
648914Sandreas.hansson@arm.com{
658856Sandreas.hansson@arm.com    // add the memories from the system to the address map as
668856Sandreas.hansson@arm.com    // appropriate
674475SN/A    for (const auto& m : _memories) {
6811053Sandreas.hansson@arm.com        // only add the memory if it is part of the global address map
695034SN/A        if (m->isInAddrMap()) {
7010360Sandreas.hansson@arm.com            memories.push_back(m);
7110622Smitch.hayenga@arm.com
7210622Smitch.hayenga@arm.com            // calculate the total size once and for all
734628SN/A            size += m->size();
7411053Sandreas.hansson@arm.com
7510693SMarco.Balboni@ARM.com            // add the range to our interval tree and make sure it does not
7610693SMarco.Balboni@ARM.com            // intersect an existing range
7710693SMarco.Balboni@ARM.com            fatal_if(addrMap.insert(m->getAddrRange(), m) == addrMap.end(),
789263Smrinmoy.ghosh@arm.com                     "Memory address range for %s is overlapping\n",
795034SN/A                     m->name());
806122SSteve.Reinhardt@amd.com        } else {
8110884Sandreas.hansson@arm.com            // this type of memory is used e.g. as reference memory by
824626SN/A            // Ruby, and they also needs a backing store, but should
8310360Sandreas.hansson@arm.com            // not be part of the global address map
844626SN/A            DPRINTF(AddrRanges,
855034SN/A                    "Skipping memory %s that is not in global address map\n",
868883SAli.Saidi@ARM.com                    m->name());
878833Sdam.sunwoo@arm.com
884458SN/A            // sanity check
892810SN/A            fatal_if(m->getAddrRange().interleaved(),
902810SN/A                     "Memory %s that is not in the global address map cannot "
913013SN/A                     "be interleaved\n", m->name());
928856Sandreas.hansson@arm.com
932810SN/A            // simply do it independently, also note that this kind of
943013SN/A            // memories are allowed to overlap in the logic address
9510714Sandreas.hansson@arm.com            // map
962810SN/A            vector<AbstractMemory*> unmapped_mems{m};
979614Srene.dejong@arm.com            createBackingStore(m->getAddrRange(), unmapped_mems);
989614Srene.dejong@arm.com        }
999614Srene.dejong@arm.com    }
10010345SCurtis.Dunham@arm.com
10110714Sandreas.hansson@arm.com    // iterate over the increasing addresses and chunks of contiguous
10210345SCurtis.Dunham@arm.com    // space to be mapped to backing store, create it and inform the
1039614Srene.dejong@arm.com    // memories
1042810SN/A    vector<AddrRange> intlv_ranges;
1052810SN/A    vector<AbstractMemory*> curr_memories;
1062810SN/A    for (const auto& r : addrMap) {
1078856Sandreas.hansson@arm.com        // simply skip past all memories that are null and hence do
1082810SN/A        // not need any backing store
1093013SN/A        if (!r.second->isNull()) {
11010714Sandreas.hansson@arm.com            // if the range is interleaved then save it for now
1113013SN/A            if (r.first.interleaved()) {
1128856Sandreas.hansson@arm.com                // if we already got interleaved ranges that are not
11310714Sandreas.hansson@arm.com                // part of the same range, then first do a merge
1148922Swilliam.wang@arm.com                // before we add the new one
1152897SN/A                if (!intlv_ranges.empty() &&
1162810SN/A                    !intlv_ranges.back().mergesWith(r.first)) {
1172810SN/A                    AddrRange merged_range(intlv_ranges);
11810344Sandreas.hansson@arm.com                    createBackingStore(merged_range, curr_memories);
11910344Sandreas.hansson@arm.com                    intlv_ranges.clear();
12010344Sandreas.hansson@arm.com                    curr_memories.clear();
12110714Sandreas.hansson@arm.com                }
12210344Sandreas.hansson@arm.com                intlv_ranges.push_back(r.first);
12310344Sandreas.hansson@arm.com                curr_memories.push_back(r.second);
12410344Sandreas.hansson@arm.com            } else {
12510713Sandreas.hansson@arm.com                vector<AbstractMemory*> single_memory{r.second};
12610344Sandreas.hansson@arm.com                createBackingStore(r.first, single_memory);
1272844SN/A            }
1282810SN/A        }
1292858SN/A    }
1302858SN/A
1318856Sandreas.hansson@arm.com    // if there is still interleaved ranges waiting to be merged, go
1328922Swilliam.wang@arm.com    // ahead and do it
1338711Sandreas.hansson@arm.com    if (!intlv_ranges.empty()) {
1342858SN/A        AddrRange merged_range(intlv_ranges);
1352858SN/A        createBackingStore(merged_range, curr_memories);
1369294Sandreas.hansson@arm.com    }
1379294Sandreas.hansson@arm.com}
1388922Swilliam.wang@arm.com
1398922Swilliam.wang@arm.comvoid
1408922Swilliam.wang@arm.comPhysicalMemory::createBackingStore(AddrRange range,
1418922Swilliam.wang@arm.com                                   const vector<AbstractMemory*>& _memories)
1428922Swilliam.wang@arm.com{
1438922Swilliam.wang@arm.com    panic_if(range.interleaved(),
1448922Swilliam.wang@arm.com             "Cannot create backing store for interleaved range %s\n",
1458922Swilliam.wang@arm.com              range.to_string());
1469294Sandreas.hansson@arm.com
1479294Sandreas.hansson@arm.com    // perform the actual mmap
1488922Swilliam.wang@arm.com    DPRINTF(AddrRanges, "Creating backing store for range %s with size %d\n",
1498922Swilliam.wang@arm.com            range.to_string(), range.size());
1508922Swilliam.wang@arm.com    int map_flags = MAP_ANON | MAP_PRIVATE;
1518922Swilliam.wang@arm.com    uint8_t* pmem = (uint8_t*) mmap(NULL, range.size(),
1528922Swilliam.wang@arm.com                                    PROT_READ | PROT_WRITE,
1538922Swilliam.wang@arm.com                                    map_flags, -1, 0);
1548922Swilliam.wang@arm.com
1554628SN/A    if (pmem == (uint8_t*) MAP_FAILED) {
15610821Sandreas.hansson@arm.com        perror("mmap");
15710821Sandreas.hansson@arm.com        fatal("Could not mmap %d bytes for range %s!\n", range.size(),
15810821Sandreas.hansson@arm.com              range.to_string());
15910821Sandreas.hansson@arm.com    }
16010821Sandreas.hansson@arm.com
16110821Sandreas.hansson@arm.com    // remember this backing store so we can checkpoint it and unmap
16210821Sandreas.hansson@arm.com    // it appropriately
16310821Sandreas.hansson@arm.com    backingStore.push_back(make_pair(range, pmem));
16410821Sandreas.hansson@arm.com
16510821Sandreas.hansson@arm.com    // point the memories to their backing store
16610821Sandreas.hansson@arm.com    for (const auto& m : _memories) {
1672858SN/A        DPRINTF(AddrRanges, "Mapping memory %s to backing store\n",
1682810SN/A                m->name());
1692810SN/A        m->setBackingStore(pmem);
1702810SN/A    }
1712810SN/A}
1722810SN/A
1734022SN/APhysicalMemory::~PhysicalMemory()
1744022SN/A{
1754022SN/A    // unmap the backing store
1762810SN/A    for (auto& s : backingStore)
1772810SN/A        munmap((char*)s.second, s.first.size());
1788833Sdam.sunwoo@arm.com}
1792810SN/A
1802810SN/Abool
1812810SN/APhysicalMemory::isMemAddr(Addr addr) const
1822810SN/A{
1838833Sdam.sunwoo@arm.com    // see if the address is within the last matched range
1848833Sdam.sunwoo@arm.com    if (!rangeCache.contains(addr)) {
1858833Sdam.sunwoo@arm.com        // lookup in the interval tree
1862810SN/A        const auto& r = addrMap.find(addr);
1872810SN/A        if (r == addrMap.end()) {
1884871SN/A            // not in the cache, and not in the tree
1894871SN/A            return false;
1904871SN/A        }
1914871SN/A        // the range is in the tree, update the cache
19210885Sandreas.hansson@arm.com        rangeCache = r->first;
19310885Sandreas.hansson@arm.com    }
1944871SN/A
1954871SN/A    assert(addrMap.find(addr) != addrMap.end());
1964871SN/A
1974871SN/A    // either matched the cache or found in the tree
1984871SN/A    return true;
1992810SN/A}
2002810SN/A
2012810SN/AAddrRangeList
2028833Sdam.sunwoo@arm.comPhysicalMemory::getConfAddrRanges() const
2032810SN/A{
2044871SN/A    // this could be done once in the constructor, but since it is unlikely to
2058833Sdam.sunwoo@arm.com    // be called more than once the iteration should not be a problem
2068833Sdam.sunwoo@arm.com    AddrRangeList ranges;
2078833Sdam.sunwoo@arm.com    vector<AddrRange> intlv_ranges;
2082810SN/A    for (const auto& r : addrMap) {
2092810SN/A        if (r.second->isConfReported()) {
2102810SN/A            // if the range is interleaved then save it for now
2112810SN/A            if (r.first.interleaved()) {
2128833Sdam.sunwoo@arm.com                // if we already got interleaved ranges that are not
2132810SN/A                // part of the same range, then first do a merge
2144871SN/A                // before we add the new one
2158833Sdam.sunwoo@arm.com                if (!intlv_ranges.empty() &&
2168833Sdam.sunwoo@arm.com                    !intlv_ranges.back().mergesWith(r.first)) {
2178833Sdam.sunwoo@arm.com                    ranges.push_back(AddrRange(intlv_ranges));
2182810SN/A                    intlv_ranges.clear();
2192810SN/A                }
2204022SN/A                intlv_ranges.push_back(r.first);
2214022SN/A            } else {
2224022SN/A                // keep the current range
2232810SN/A                ranges.push_back(r.first);
2242810SN/A            }
2258833Sdam.sunwoo@arm.com        }
2262810SN/A    }
2272810SN/A
2282810SN/A    // if there is still interleaved ranges waiting to be merged,
2292810SN/A    // go ahead and do it
2308833Sdam.sunwoo@arm.com    if (!intlv_ranges.empty()) {
2318833Sdam.sunwoo@arm.com        ranges.push_back(AddrRange(intlv_ranges));
2328833Sdam.sunwoo@arm.com    }
2332810SN/A
2342810SN/A    return ranges;
2352810SN/A}
2362810SN/A
2372810SN/Avoid
2388833Sdam.sunwoo@arm.comPhysicalMemory::access(PacketPtr pkt)
2392810SN/A{
2404871SN/A    assert(pkt->isRequest());
2418833Sdam.sunwoo@arm.com    Addr addr = pkt->getAddr();
2428833Sdam.sunwoo@arm.com    const auto& m = addrMap.find(addr);
2438833Sdam.sunwoo@arm.com    assert(m != addrMap.end());
2442810SN/A    m->second->access(pkt);
2452810SN/A}
2462810SN/A
2472810SN/Avoid
2488833Sdam.sunwoo@arm.comPhysicalMemory::functionalAccess(PacketPtr pkt)
2492810SN/A{
2504871SN/A    assert(pkt->isRequest());
2518833Sdam.sunwoo@arm.com    Addr addr = pkt->getAddr();
2528833Sdam.sunwoo@arm.com    const auto& m = addrMap.find(addr);
2538833Sdam.sunwoo@arm.com    assert(m != addrMap.end());
2542810SN/A    m->second->functionalAccess(pkt);
2552810SN/A}
2564022SN/A
2574022SN/Avoid
2584022SN/APhysicalMemory::serialize(ostream& os)
2592810SN/A{
2602810SN/A    // serialize all the locked addresses and their context ids
2618833Sdam.sunwoo@arm.com    vector<Addr> lal_addr;
2622810SN/A    vector<int> lal_cid;
2632810SN/A
2642810SN/A    for (auto& m : memories) {
2652810SN/A        const list<LockedAddr>& locked_addrs = m->getLockedAddrList();
2668833Sdam.sunwoo@arm.com        for (const auto& l : locked_addrs) {
2678833Sdam.sunwoo@arm.com            lal_addr.push_back(l.addr);
2688833Sdam.sunwoo@arm.com            lal_cid.push_back(l.contextId);
2692810SN/A        }
2702810SN/A    }
2712810SN/A
2722810SN/A    arrayParamOut(os, "lal_addr", lal_addr);
2732810SN/A    arrayParamOut(os, "lal_cid", lal_cid);
2748833Sdam.sunwoo@arm.com
2752810SN/A    // serialize the backing stores
2764871SN/A    unsigned int nbr_of_stores = backingStore.size();
2778833Sdam.sunwoo@arm.com    SERIALIZE_SCALAR(nbr_of_stores);
2788833Sdam.sunwoo@arm.com
2798833Sdam.sunwoo@arm.com    unsigned int store_id = 0;
2802810SN/A    // store each backing store memory segment in a file
2812810SN/A    for (auto& s : backingStore) {
2822810SN/A        nameOut(os, csprintf("%s.store%d", name(), store_id));
2832810SN/A        serializeStore(os, store_id++, s.first, s.second);
2848833Sdam.sunwoo@arm.com    }
2852810SN/A}
2864871SN/A
2878833Sdam.sunwoo@arm.comvoid
2888833Sdam.sunwoo@arm.comPhysicalMemory::serializeStore(ostream& os, unsigned int store_id,
2898833Sdam.sunwoo@arm.com                               AddrRange range, uint8_t* pmem)
2902810SN/A{
2912810SN/A    // we cannot use the address range for the name as the
2924022SN/A    // memories that are not part of the address map can overlap
2934022SN/A    string filename = name() + ".store" + to_string(store_id) + ".pmem";
2944022SN/A    long range_size = range.size();
2952810SN/A
2962810SN/A    DPRINTF(Checkpoint, "Serializing physical memory %s with size %d\n",
2972810SN/A            filename, range_size);
2982810SN/A
2992810SN/A    SERIALIZE_SCALAR(store_id);
3002810SN/A    SERIALIZE_SCALAR(filename);
3018833Sdam.sunwoo@arm.com    SERIALIZE_SCALAR(range_size);
3022810SN/A
3038833Sdam.sunwoo@arm.com    // write memory file
3048833Sdam.sunwoo@arm.com    string filepath = Checkpoint::dir() + "/" + filename.c_str();
3058833Sdam.sunwoo@arm.com    gzFile compressed_mem = gzopen(filepath.c_str(), "wb");
3062810SN/A    if (compressed_mem == NULL)
3072810SN/A        fatal("Can't open physical memory checkpoint file '%s'\n",
3082810SN/A              filename);
3092810SN/A
3102810SN/A    uint64_t pass_size = 0;
3118833Sdam.sunwoo@arm.com
3122810SN/A    // gzwrite fails if (int)len < 0 (gzwrite returns int)
3132810SN/A    for (uint64_t written = 0; written < range.size();
3148833Sdam.sunwoo@arm.com         written += pass_size) {
3158833Sdam.sunwoo@arm.com        pass_size = (uint64_t)INT_MAX < (range.size() - written) ?
3168833Sdam.sunwoo@arm.com            (uint64_t)INT_MAX : (range.size() - written);
3172810SN/A
3182810SN/A        if (gzwrite(compressed_mem, pmem + written,
3192810SN/A                    (unsigned int) pass_size) != (int) pass_size) {
3202810SN/A            fatal("Write failed on physical memory checkpoint file '%s'\n",
3218833Sdam.sunwoo@arm.com                  filename);
3222810SN/A        }
3232810SN/A    }
3248833Sdam.sunwoo@arm.com
3258833Sdam.sunwoo@arm.com    // close the compressed stream and check that the exit status
3268833Sdam.sunwoo@arm.com    // is zero
3272810SN/A    if (gzclose(compressed_mem))
3282810SN/A        fatal("Close failed on physical memory checkpoint file '%s'\n",
3294022SN/A              filename);
3304022SN/A
3314022SN/A}
3322810SN/A
3332810SN/Avoid
3342810SN/APhysicalMemory::unserialize(Checkpoint* cp, const string& section)
3352810SN/A{
3362810SN/A    // unserialize the locked addresses and map them to the
3372810SN/A    // appropriate memory controller
3388833Sdam.sunwoo@arm.com    vector<Addr> lal_addr;
3392810SN/A    vector<int> lal_cid;
3408833Sdam.sunwoo@arm.com    arrayParamIn(cp, section, "lal_addr", lal_addr);
3418833Sdam.sunwoo@arm.com    arrayParamIn(cp, section, "lal_cid", lal_cid);
3428833Sdam.sunwoo@arm.com    for(size_t i = 0; i < lal_addr.size(); ++i) {
3432810SN/A        const auto& m = addrMap.find(lal_addr[i]);
3442810SN/A        m->second->addLockedAddr(LockedAddr(lal_addr[i], lal_cid[i]));
3452810SN/A    }
3462810SN/A
3472810SN/A    // unserialize the backing stores
3488833Sdam.sunwoo@arm.com    unsigned int nbr_of_stores;
3492810SN/A    UNSERIALIZE_SCALAR(nbr_of_stores);
3502810SN/A
3518833Sdam.sunwoo@arm.com    for (unsigned int i = 0; i < nbr_of_stores; ++i) {
3528833Sdam.sunwoo@arm.com        unserializeStore(cp, csprintf("%s.store%d", section, i));
3538833Sdam.sunwoo@arm.com    }
3542810SN/A
3552810SN/A}
3562810SN/A
3572810SN/Avoid
3588833Sdam.sunwoo@arm.comPhysicalMemory::unserializeStore(Checkpoint* cp, const string& section)
3592810SN/A{
3602810SN/A    const uint32_t chunk_size = 16384;
3618833Sdam.sunwoo@arm.com
3628833Sdam.sunwoo@arm.com    unsigned int store_id;
3638833Sdam.sunwoo@arm.com    UNSERIALIZE_SCALAR(store_id);
3642810SN/A
3652810SN/A    string filename;
3664022SN/A    UNSERIALIZE_SCALAR(filename);
3674022SN/A    string filepath = cp->cptDir + "/" + filename;
3684022SN/A
3692810SN/A    // mmap memoryfile
3702810SN/A    gzFile compressed_mem = gzopen(filepath.c_str(), "rb");
3712810SN/A    if (compressed_mem == NULL)
3722810SN/A        fatal("Can't open physical memory checkpoint file '%s'", filename);
3732810SN/A
3742810SN/A    // we've already got the actual backing store mapped
3752810SN/A    uint8_t* pmem = backingStore[store_id].second;
3762810SN/A    AddrRange range = backingStore[store_id].first;
3778833Sdam.sunwoo@arm.com
3788833Sdam.sunwoo@arm.com    long range_size;
3798833Sdam.sunwoo@arm.com    UNSERIALIZE_SCALAR(range_size);
3808833Sdam.sunwoo@arm.com
3812810SN/A    DPRINTF(Checkpoint, "Unserializing physical memory %s with size %d\n",
3822810SN/A            filename, range_size);
3832810SN/A
3842810SN/A    if (range_size != range.size())
3852810SN/A        fatal("Memory range size has changed! Saw %lld, expected %lld\n",
3868833Sdam.sunwoo@arm.com              range_size, range.size());
3872810SN/A
3882810SN/A    uint64_t curr_size = 0;
3898833Sdam.sunwoo@arm.com    long* temp_page = new long[chunk_size];
3908833Sdam.sunwoo@arm.com    long* pmem_current;
3918833Sdam.sunwoo@arm.com    uint32_t bytes_read;
3922810SN/A    while (curr_size < range.size()) {
3932810SN/A        bytes_read = gzread(compressed_mem, temp_page, chunk_size);
3942810SN/A        if (bytes_read == 0)
3952810SN/A            break;
3968833Sdam.sunwoo@arm.com
3972810SN/A        assert(bytes_read % sizeof(long) == 0);
3982810SN/A
3998833Sdam.sunwoo@arm.com        for (uint32_t x = 0; x < bytes_read / sizeof(long); x++) {
4008833Sdam.sunwoo@arm.com            // Only copy bytes that are non-zero, so we don't give
4018833Sdam.sunwoo@arm.com            // the VM system hell
4022810SN/A            if (*(temp_page + x) != 0) {
4032810SN/A                pmem_current = (long*)(pmem + curr_size + x * sizeof(long));
4042810SN/A                *pmem_current = *(temp_page + x);
4052810SN/A            }
4062810SN/A        }
4072810SN/A        curr_size += bytes_read;
4082810SN/A    }
4092810SN/A
4102810SN/A    delete[] temp_page;
4112810SN/A
4122810SN/A    if (gzclose(compressed_mem))
4132810SN/A        fatal("Close failed on physical memory checkpoint file '%s'\n",
4142810SN/A              filename);
4152810SN/A}
4162810SN/A