flash_device.cc revision 10910:32f3d1c454ec
17090SN/A/*
27090SN/A * Copyright (c) 2013-2015 ARM Limited
37090SN/A * All rights reserved
47090SN/A *
57090SN/A * The license below extends only to copyright in the software and shall
67090SN/A * not be construed as granting a license to any other intellectual
77090SN/A * property including but not limited to intellectual property relating
87090SN/A * to a hardware implementation of the functionality of the software
97090SN/A * licensed hereunder.  You may use the software subject to the license
107090SN/A * terms below provided that you ensure that this notice is replicated
117090SN/A * unmodified and in its entirety in all distributions of the software,
127090SN/A * modified or unmodified, in source code or in binary form.
134486SN/A *
144486SN/A * Redistribution and use in source and binary forms, with or without
154486SN/A * modification, are permitted provided that the following conditions are
164486SN/A * met: redistributions of source code must retain the above copyright
174486SN/A * notice, this list of conditions and the following disclaimer;
184486SN/A * redistributions in binary form must reproduce the above copyright
194486SN/A * notice, this list of conditions and the following disclaimer in the
204486SN/A * documentation and/or other materials provided with the distribution;
214486SN/A * neither the name of the copyright holders nor the names of its
224486SN/A * contributors may be used to endorse or promote products derived from
234486SN/A * this software without specific prior written permission.
244486SN/A *
254486SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
264486SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
274486SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
284486SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
294486SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
304486SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
314486SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
324486SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
334486SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
344486SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
354486SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
364486SN/A *
374486SN/A * Authors: Rene de Jong
384486SN/A */
397584SAli.Saidi@arm.com
407584SAli.Saidi@arm.com/** @file
414486SN/A * This simplistic flash model is designed to model managed SLC NAND flash.
423630SN/A * This device will need an interface module (such as NVMe or UFS); Note that
433630SN/A * this model only calculates the delay and does not perform the actual
447587SAli.Saidi@arm.com * transaction.
455478SN/A *
465478SN/A * To access the memory, use either readMemory or writeMemory. This will
477584SAli.Saidi@arm.com * schedule an event at the tick where the action will finish. If a callback
483630SN/A * has been given as argument then that function will be called on completion
497584SAli.Saidi@arm.com * of that event. Note that this does not guarantee that there are no other
507584SAli.Saidi@arm.com * actions pending in the flash device.
517584SAli.Saidi@arm.com *
527584SAli.Saidi@arm.com * IMPORTANT: number of planes should be a power of 2.
533898SN/A */
547587SAli.Saidi@arm.com
557587SAli.Saidi@arm.com#include "dev/arm/flash_device.hh"
567587SAli.Saidi@arm.com
577587SAli.Saidi@arm.com#include "debug/Drain.hh"
587587SAli.Saidi@arm.com
597584SAli.Saidi@arm.com/**
607584SAli.Saidi@arm.com * Create this device
617584SAli.Saidi@arm.com */
627584SAli.Saidi@arm.com
637584SAli.Saidi@arm.comFlashDevice*
647584SAli.Saidi@arm.comFlashDeviceParams::create()
657584SAli.Saidi@arm.com{
667584SAli.Saidi@arm.com    return new FlashDevice(this);
677584SAli.Saidi@arm.com}
687584SAli.Saidi@arm.com
697584SAli.Saidi@arm.com
707584SAli.Saidi@arm.com/**
717584SAli.Saidi@arm.com * Flash Device constructor and destructor
727584SAli.Saidi@arm.com */
737584SAli.Saidi@arm.com
747584SAli.Saidi@arm.comFlashDevice::FlashDevice(const FlashDeviceParams* p):
757584SAli.Saidi@arm.com    AbstractNVM(p),
767584SAli.Saidi@arm.com    diskSize(0),
777584SAli.Saidi@arm.com    blockSize(p->blk_size),
787584SAli.Saidi@arm.com    pageSize(p->page_size),
797584SAli.Saidi@arm.com    GCActivePercentage(p->GC_active),
807584SAli.Saidi@arm.com    readLatency(p->read_lat),
817584SAli.Saidi@arm.com    writeLatency(p->write_lat),
827584SAli.Saidi@arm.com    eraseLatency(p->erase_lat),
837584SAli.Saidi@arm.com    dataDistribution(p->data_distribution),
847584SAli.Saidi@arm.com    numPlanes(p->num_planes),
857584SAli.Saidi@arm.com    pagesPerBlock(0),
867584SAli.Saidi@arm.com    pagesPerDisk(0),
877584SAli.Saidi@arm.com    blocksPerDisk(0),
887584SAli.Saidi@arm.com    planeMask(numPlanes - 1),
897584SAli.Saidi@arm.com    drainManager(NULL),
907584SAli.Saidi@arm.com    planeEventQueue(numPlanes),
917584SAli.Saidi@arm.com    planeEvent(this)
927584SAli.Saidi@arm.com{
937584SAli.Saidi@arm.com
943630SN/A    /*
953630SN/A     * Let 'a' be a power of two of n bits, written such that a-n is the msb
967584SAli.Saidi@arm.com     * and a-0 is the lsb. Since it is a power of two, only one bit (a-x,
977584SAli.Saidi@arm.com     * with 0 <= x <= n) is set. If we subtract one from this number the bits
987584SAli.Saidi@arm.com     * a-(x-1) to a-0 are set and all the other bits are cleared. Hence a
997584SAli.Saidi@arm.com     * bitwise AND with those two numbers results in an integer with all bits
1007584SAli.Saidi@arm.com     * cleared.
1017584SAli.Saidi@arm.com     */
1027584SAli.Saidi@arm.com    if(numPlanes & planeMask)
1037584SAli.Saidi@arm.com        fatal("Number of planes is not a power of 2 in flash device.\n");
1047584SAli.Saidi@arm.com}
1057584SAli.Saidi@arm.com
1067584SAli.Saidi@arm.com/**
1077584SAli.Saidi@arm.com * Initiates all the flash functions: initializes the lookup tables, age of
1087584SAli.Saidi@arm.com * the device, etc. This can only be done once the disk image is known.
1097584SAli.Saidi@arm.com * Thats why it can't be done in the constructor.
1107584SAli.Saidi@arm.com */
1117584SAli.Saidi@arm.comvoid
1127584SAli.Saidi@arm.comFlashDevice::initializeFlash(uint64_t disk_size, uint32_t sector_size)
1137584SAli.Saidi@arm.com{
1147584SAli.Saidi@arm.com    diskSize = disk_size * sector_size;
1157584SAli.Saidi@arm.com    pagesPerBlock = blockSize / pageSize;
1167584SAli.Saidi@arm.com    pagesPerDisk = diskSize / pageSize;
1177584SAli.Saidi@arm.com    blocksPerDisk = diskSize / blockSize;
1187584SAli.Saidi@arm.com
1197584SAli.Saidi@arm.com    /** Sanity information: check flash configuration */
1207584SAli.Saidi@arm.com    DPRINTF(FlashDevice, "diskSize: %d Bytes; %d pages per block, %d pages "
1217584SAli.Saidi@arm.com            "per disk\n", diskSize, pagesPerBlock, pagesPerDisk);
1227584SAli.Saidi@arm.com
1237584SAli.Saidi@arm.com    locationTable.resize(pagesPerDisk);
1247584SAli.Saidi@arm.com
1254104SN/A    /**Garbage collection related*/
1264104SN/A    blockValidEntries.resize(blocksPerDisk, 0);
1277584SAli.Saidi@arm.com    blockEmptyEntries.resize(blocksPerDisk, pagesPerBlock);
1287584SAli.Saidi@arm.com
1294104SN/A    /**
1303630SN/A     * This is a bitmap. Every bit is a page
1313630SN/A     * unknownPages is a vector of 32 bit integers. If every page was an
1323630SN/A     * integer, the total size would be pagesPerDisk; since we can map one
1333630SN/A     * page per bit we need ceil(pagesPerDisk/32) entries. 32 = 1 << 5 hence
1347584SAli.Saidi@arm.com     * it will do to just shift pagesPerDisk five positions and add one. This
1357584SAli.Saidi@arm.com     * will allocate one integer to many for this data structure in the worst
1367584SAli.Saidi@arm.com     * case.
1377584SAli.Saidi@arm.com     */
1387584SAli.Saidi@arm.com    unknownPages.resize((pagesPerDisk >> 5) + 1, 0xFFFFFFFF);
1397584SAli.Saidi@arm.com
1407584SAli.Saidi@arm.com    for (uint32_t count = 0; count < pagesPerDisk; count++) {
1417584SAli.Saidi@arm.com        //setup lookup table + physical aspects
1427584SAli.Saidi@arm.com
1437584SAli.Saidi@arm.com        if (dataDistribution == Enums::stripe) {
1447584SAli.Saidi@arm.com            locationTable[count].page = count / blocksPerDisk;
1457584SAli.Saidi@arm.com            locationTable[count].block = count % blocksPerDisk;
1467584SAli.Saidi@arm.com
1477584SAli.Saidi@arm.com        } else {
1487584SAli.Saidi@arm.com            locationTable[count].page = count % pagesPerBlock;
1497584SAli.Saidi@arm.com            locationTable[count].block = count / pagesPerBlock;
1507584SAli.Saidi@arm.com        }
1517584SAli.Saidi@arm.com    }
1527584SAli.Saidi@arm.com}
1537584SAli.Saidi@arm.com
1547584SAli.Saidi@arm.comFlashDevice::~FlashDevice()
1557584SAli.Saidi@arm.com{
1567584SAli.Saidi@arm.com    DPRINTF(FlashDevice, "Remove FlashDevice\n");
1577584SAli.Saidi@arm.com}
1587584SAli.Saidi@arm.com
1597584SAli.Saidi@arm.com/**
1607584SAli.Saidi@arm.com * Handles the accesses to the device.
1617584SAli.Saidi@arm.com * The function determines when certain actions are scheduled and schedules
1627584SAli.Saidi@arm.com * an event that uses the callback function on completion of the action.
1637584SAli.Saidi@arm.com */
1647584SAli.Saidi@arm.comvoid
1657584SAli.Saidi@arm.comFlashDevice::accessDevice(uint64_t address, uint32_t amount, Callback *event,
1667584SAli.Saidi@arm.com                          Actions action)
1677584SAli.Saidi@arm.com{
1687584SAli.Saidi@arm.com    DPRINTF(FlashDevice, "Flash calculation for %d bytes in %d pages\n"
1697584SAli.Saidi@arm.com            , amount, pageSize);
1707584SAli.Saidi@arm.com
1717584SAli.Saidi@arm.com    std::vector<Tick> time(numPlanes, 0);
1727584SAli.Saidi@arm.com    uint64_t logic_page_addr = address / pageSize;
1737584SAli.Saidi@arm.com    uint32_t plane_address = 0;
1747584SAli.Saidi@arm.com
1757584SAli.Saidi@arm.com    /**
1767584SAli.Saidi@arm.com     * The access will be broken up in a number of page accesses. The number
1777584SAli.Saidi@arm.com     * of page accesses depends on the amount that needs to be transfered.
1787584SAli.Saidi@arm.com     * The assumption here is that the interface is completely ignorant of
1797584SAli.Saidi@arm.com     * the page size and that this model has to figure out all of the
1807584SAli.Saidi@arm.com     * transaction characteristics.
1817584SAli.Saidi@arm.com     */
1827584SAli.Saidi@arm.com    for (uint32_t count = 0; amount > (count * pageSize); count++) {
1837584SAli.Saidi@arm.com        uint32_t index = (locationTable[logic_page_addr].block *
1847584SAli.Saidi@arm.com                          pagesPerBlock) + (logic_page_addr % pagesPerBlock);
1857584SAli.Saidi@arm.com
1867584SAli.Saidi@arm.com        DPRINTF(FlashDevice, "Index 0x%8x, Block 0x%8x, pages/block %d,"
1877584SAli.Saidi@arm.com                " logic address 0x%8x\n", index,
1887584SAli.Saidi@arm.com                locationTable[logic_page_addr].block, pagesPerBlock,
1897584SAli.Saidi@arm.com                logic_page_addr);
1907584SAli.Saidi@arm.com        DPRINTF(FlashDevice, "Page %d; %d bytes up to this point\n", count,
1917584SAli.Saidi@arm.com                (count * pageSize));
1927584SAli.Saidi@arm.com
1937584SAli.Saidi@arm.com        plane_address = locationTable[logic_page_addr].block & planeMask;
1947584SAli.Saidi@arm.com
1957584SAli.Saidi@arm.com        if (action == ActionRead) {
1967584SAli.Saidi@arm.com            //lookup
1977584SAli.Saidi@arm.com            //call accessTimes
1987584SAli.Saidi@arm.com            time[plane_address] += accessTimes(locationTable[logic_page_addr]
1997584SAli.Saidi@arm.com                                               .block, ActionRead);
2007584SAli.Saidi@arm.com
2017584SAli.Saidi@arm.com            /*stats*/
2027584SAli.Saidi@arm.com            stats.readAccess.sample(logic_page_addr);
2037584SAli.Saidi@arm.com            stats.readLatency.sample(time[plane_address]);
2047584SAli.Saidi@arm.com        } else { //write
2057584SAli.Saidi@arm.com            //lookup
2067584SAli.Saidi@arm.com            //call accessTimes if appropriate, page may be unknown, so lets
2077584SAli.Saidi@arm.com            //give it the benefit of the doubt
2087584SAli.Saidi@arm.com
2097584SAli.Saidi@arm.com            if (getUnknownPages(index))
2107584SAli.Saidi@arm.com                time[plane_address] += accessTimes
2117584SAli.Saidi@arm.com                    (locationTable[logic_page_addr].block, ActionWrite);
2127584SAli.Saidi@arm.com
2137584SAli.Saidi@arm.com            else //A remap is needed
2147584SAli.Saidi@arm.com                time[plane_address] += remap(logic_page_addr);
2157584SAli.Saidi@arm.com
2167584SAli.Saidi@arm.com            /*stats*/
2177584SAli.Saidi@arm.com            stats.writeAccess.sample(logic_page_addr);
218            stats.writeLatency.sample(time[plane_address]);
219        }
220
221        /**
222         * Check if the page is known and used. unknownPages is a bitmap of
223         * all the pages. It tracks wether we can be sure that the
224         * information of this page is taken into acount in the model (is it
225         * considered in blockValidEntries and blockEmptyEntries?). If it has
226         * been used in the past, then it is known.
227         */
228        if (getUnknownPages(index)) {
229            clearUnknownPages(index);
230            --blockEmptyEntries[locationTable[logic_page_addr].block];
231            ++blockValidEntries[locationTable[logic_page_addr].block];
232        }
233
234        stats.fileSystemAccess.sample(address);
235        ++logic_page_addr;
236    }
237
238    /**
239     * previous part of the function found the times spend in different
240     * planes, now lets find the maximum to know when to callback the disk
241     */
242    for (uint32_t count = 0; count < numPlanes; count++){
243        plane_address = (time[plane_address] > time[count]) ? plane_address
244            : count;
245
246        DPRINTF(FlashDevice, "Plane %d is busy for %d ticks\n", count,
247                time[count]);
248
249        if  (time[count] != 0) {
250
251            struct CallBackEntry cbe;
252            /**
253             * If there are no events for this plane, then add the current
254             * time to the occupation time; otherwise, plan it after the
255             * last event. If by chance that event is handled in this tick,
256             * then we would still end up with the same result.
257             */
258            if (planeEventQueue[count].empty())
259                cbe.time = time[count] + curTick();
260            else
261                cbe.time = time[count] +
262                           planeEventQueue[count].back().time;
263            cbe.function = NULL;
264            planeEventQueue[count].push_back(cbe);
265
266            DPRINTF(FlashDevice, "scheduled at: %ld\n", cbe.time);
267
268            if (!planeEvent.scheduled())
269                schedule(planeEvent, planeEventQueue[count].back().time);
270            else if (planeEventQueue[count].back().time < planeEvent.when())
271                reschedule(planeEvent,
272                    planeEventQueue[plane_address].back().time, true);
273        }
274    }
275
276    //worst case two plane finish at the same time, each triggers an event
277    //and this callback will be called once. Maybe before the other plane
278    //could execute its event, but in the same tick.
279    planeEventQueue[plane_address].back().function = event;
280    DPRINTF(FlashDevice, "Callback queued for plane %d; %d in queue\n",
281            plane_address, planeEventQueue[plane_address].size());
282    DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when());
283}
284
285/**
286 * When a plane completes its action, this event is triggered. When a
287 * callback function was associated with that event, it will be called.
288 */
289
290void
291FlashDevice::actionComplete()
292{
293    DPRINTF(FlashDevice, "Plane action completed\n");
294    uint8_t plane_address = 0;
295
296    uint8_t next_event = 0;
297
298    /**Search for a callback that is supposed to happen in this Tick*/
299    for (plane_address = 0; plane_address < numPlanes; plane_address++) {
300        if (!planeEventQueue[plane_address].empty()) {
301            /**
302             * Invariant: All queued events are scheduled in the present
303             *  or future.
304             */
305            assert(planeEventQueue[plane_address].front().time >= curTick());
306
307            if (planeEventQueue[plane_address].front().time == curTick()) {
308                /**
309                 * To ensure that the follow-up action is executed correctly,
310                 * the callback entry first need to be cleared before it can
311                 * be called.
312                 */
313                Callback *temp = planeEventQueue[plane_address].front().
314                                 function;
315                planeEventQueue[plane_address].pop_front();
316
317                /**Found a callback, lets make it happen*/
318                if (temp != NULL) {
319                    DPRINTF(FlashDevice, "Callback, %d\n", plane_address);
320                    temp->process();
321                }
322            }
323        }
324    }
325
326    /** Find when to schedule the planeEvent next */
327    for (plane_address = 0; plane_address < numPlanes; plane_address++) {
328        if (!planeEventQueue[plane_address].empty())
329            if (planeEventQueue[next_event].empty() ||
330                    (planeEventQueue[plane_address].front().time <
331                     planeEventQueue[next_event].front().time))
332                next_event = plane_address;
333    }
334
335    /**Schedule the next plane that will be ready (if any)*/
336    if (!planeEventQueue[next_event].empty()) {
337        DPRINTF(FlashDevice, "Schedule plane: %d\n", plane_address);
338        reschedule(planeEvent, planeEventQueue[next_event].front().time, true);
339    }
340
341    checkDrain();
342
343    DPRINTF(FlashDevice, "returing from flash event\n");
344    DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when());
345}
346
347/**
348 * Handles the remapping of the pages. It is a (I hope) sensible statistic
349 * approach. asumption: garbage collection happens when a clean is needed
350 * (may become stochastic function).
351 */
352Tick
353FlashDevice::remap(uint64_t logic_page_addr)
354{
355    /**
356     * Are there any empty left in this Block, or do we need to do an erase
357     */
358    if (blockEmptyEntries[locationTable[logic_page_addr].block] > 0) {
359        //just a remap
360        //update tables
361        --blockEmptyEntries[locationTable[logic_page_addr].block];
362        //access to this table won't be sequential anymore
363        locationTable[logic_page_addr].page = pagesPerBlock + 2;
364        //access new block
365        Tick time = accessTimes(locationTable[logic_page_addr].block,
366                                ActionWrite);
367
368        DPRINTF(FlashDevice, "Remap returns %d ticks\n", time);
369        return time;
370
371    } else {
372        //calculate how much time GC would have taken
373        uint32_t block = locationTable[logic_page_addr].block;
374        Tick time = ((GCActivePercentage *
375                       (accessTimes(block, ActionCopy) +
376                        accessTimes(block, ActionErase)))
377                     / 100);
378
379        //use block as the logical start address of the block
380        block = locationTable[logic_page_addr].block * pagesPerBlock;
381
382        //assumption: clean will improve locality
383        for (uint32_t count = 0; count < pageSize; count++) {
384            locationTable[block + count].page = (block + count) %
385                pagesPerBlock;
386            ++count;
387        }
388
389        blockEmptyEntries[locationTable[logic_page_addr].block] =
390            pagesPerBlock;
391        /*stats*/
392        ++stats.totalGCActivations;
393
394        DPRINTF(FlashDevice, "Remap with erase action returns %d ticks\n",
395                time);
396
397        return time;
398    }
399
400}
401
402/**
403 * Calculates the accesstime per operation needed
404 */
405Tick
406FlashDevice::accessTimes(uint64_t block, Actions action)
407{
408    Tick time = 0;
409
410    switch(action) {
411      case ActionRead: {
412          /**Just read the page*/
413          time = readLatency;
414      } break;
415
416      case ActionWrite: {
417          /**Write the page, and read the result*/
418          time = writeLatency + readLatency;
419      } break;
420
421      case ActionErase: {
422          /**Erase and check wether it was successfull*/
423          time = eraseLatency + readLatency;
424      } break;
425
426      case ActionCopy: {
427          /**Copy every valid page*/
428          uint32_t validpages = blockValidEntries[block];
429          time = validpages * (readLatency + writeLatency);
430      } break;
431
432      default: break;
433    }
434
435    //Used to determine sequential action.
436    DPRINTF(FlashDevice, "Access returns %d ticks\n", time);
437    return time;
438}
439
440/**
441 * clearUnknownPages. defines that a page is known and used
442 * unknownPages is a bitmap of all the pages. It tracks wether we can be sure
443 * that the information of this page is taken into acount in the model (is it
444 * considered in blockValidEntries and blockEmptyEntries?). If it has been
445 * used in the past, then it is known. But it needs to be tracked to make
446 * decisions about write accesses, and indirectly about copy actions. one
447 * unknownPage entry is a 32 bit integer. So if we have a page index, then
448 * that means that we need entry floor(index/32) (index >> 5) and we need to
449 * select the bit which number is equal to the remainder of index/32
450 * (index%32). The bit is cleared to make sure that we see it as considered
451 * in the future.
452 */
453
454inline
455void
456FlashDevice::clearUnknownPages(uint32_t index)
457{
458    unknownPages[index >> 5] &= ~(0x01 << (index % 32));
459}
460
461/**
462 * getUnknownPages. Verify wether a page is known
463 */
464
465inline
466bool
467FlashDevice::getUnknownPages(uint32_t index)
468{
469    return unknownPages[index >> 5] & (0x01 << (index % 32));
470}
471
472void
473FlashDevice::regStats()
474{
475    using namespace Stats;
476
477    std::string fd_name = name() + ".FlashDevice";
478
479    // Register the stats
480    /** Amount of GC activations*/
481    stats.totalGCActivations
482        .name(fd_name + ".totalGCActivations")
483        .desc("Number of Garbage collector activations")
484        .flags(none);
485
486    /** Histogram of address accesses*/
487    stats.writeAccess
488        .init(2)
489        .name(fd_name + ".writeAccessHist")
490        .desc("Histogram of write addresses")
491        .flags(pdf);
492    stats.readAccess
493        .init(2)
494        .name(fd_name + ".readAccessHist")
495        .desc("Histogram of read addresses")
496        .flags(pdf);
497    stats.fileSystemAccess
498        .init(100)
499        .name(fd_name + ".fileSystemAccessHist")
500        .desc("Histogram of file system accesses")
501        .flags(pdf);
502
503    /** Histogram of access latencies*/
504    stats.writeLatency
505        .init(100)
506        .name(fd_name + ".writeLatencyHist")
507        .desc("Histogram of write latency")
508        .flags(pdf);
509    stats.readLatency
510        .init(100)
511        .name(fd_name + ".readLatencyHist")
512        .desc("Histogram of read latency")
513        .flags(pdf);
514}
515
516/**
517 * Serialize; needed to create checkpoints
518 */
519
520void
521FlashDevice::serialize(CheckpointOut &cp) const
522{
523    SERIALIZE_SCALAR(planeMask);
524
525    int unknown_pages_size = unknownPages.size();
526    SERIALIZE_SCALAR(unknown_pages_size);
527    for (uint32_t count = 0; count < unknownPages.size(); count++)
528        SERIALIZE_SCALAR(unknownPages[count]);
529
530    int location_table_size = locationTable.size();
531    SERIALIZE_SCALAR(location_table_size);
532    for (uint32_t count = 0; count < location_table_size; count++) {
533        SERIALIZE_SCALAR(locationTable[count].page);
534        SERIALIZE_SCALAR(locationTable[count].block);
535        }
536
537    int block_valid_entries_size = blockValidEntries.size();
538    SERIALIZE_SCALAR(block_valid_entries_size);
539    for (uint32_t count = 0; count < blockValidEntries.size(); count++)
540        SERIALIZE_SCALAR(blockValidEntries[count]);
541
542    int block_empty_entries_size = blockEmptyEntries.size();
543    SERIALIZE_SCALAR(block_empty_entries_size);
544    for (uint32_t count = 0; count < blockEmptyEntries.size(); count++)
545        SERIALIZE_SCALAR(blockEmptyEntries[count]);
546
547};
548
549/**
550 * Unserialize; needed to restore from checkpoints
551 */
552
553void
554FlashDevice::unserialize(CheckpointIn &cp)
555{
556    UNSERIALIZE_SCALAR(planeMask);
557
558    int unknown_pages_size;
559    UNSERIALIZE_SCALAR(unknown_pages_size);
560    unknownPages.resize(unknown_pages_size);
561    for (uint32_t count = 0; count < unknown_pages_size; count++)
562        UNSERIALIZE_SCALAR(unknownPages[count]);
563
564    int location_table_size;
565    UNSERIALIZE_SCALAR(location_table_size);
566    locationTable.resize(location_table_size);
567    for (uint32_t count = 0; count < location_table_size; count++) {
568        UNSERIALIZE_SCALAR(locationTable[count].page);
569        UNSERIALIZE_SCALAR(locationTable[count].block);
570        }
571
572    int block_valid_entries_size;
573    UNSERIALIZE_SCALAR(block_valid_entries_size);
574    blockValidEntries.resize(block_valid_entries_size);
575    for (uint32_t count = 0; count < block_valid_entries_size; count++)
576        UNSERIALIZE_SCALAR(blockValidEntries[count]);
577
578    int block_empty_entries_size;
579    UNSERIALIZE_SCALAR(block_empty_entries_size);
580    blockEmptyEntries.resize(block_empty_entries_size);
581    for (uint32_t count = 0; count < block_empty_entries_size; count++)
582        UNSERIALIZE_SCALAR(blockEmptyEntries[count]);
583
584};
585
586/**
587 * Drain; needed to enable checkpoints
588 */
589
590unsigned int
591FlashDevice::drain(DrainManager *dm)
592{
593    unsigned int count = 0;
594
595    if (planeEvent.scheduled()) {
596        count = 1;
597        drainManager = dm;
598    } else {
599        DPRINTF(Drain, "Flash device in drained state\n");
600    }
601
602    if (count) {
603        DPRINTF(Drain, "Flash device is draining...\n");
604        setDrainState(DrainState::Draining);
605    } else {
606        DPRINTF(Drain, "Flash device drained\n");
607        setDrainState(DrainState::Drained);
608    }
609    return count;
610}
611
612/**
613 * Checkdrain; needed to enable checkpoints
614 */
615
616void
617FlashDevice::checkDrain()
618{
619    if (drainManager == NULL) {
620        return;
621    }
622
623    if (planeEvent.when() > curTick()) {
624        DPRINTF(Drain, "Flash device is still draining\n");
625    } else {
626        DPRINTF(Drain, "Flash device is done draining\n");
627        drainManager->signalDrainDone();
628        drainManager = NULL;
629    }
630}
631