dev: Delete the authors list from files in src/dev.
[gem5.git] / src / dev / arm / flash_device.cc
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
38 /** @file
39 * This simplistic flash model is designed to model managed SLC NAND flash.
40 * This device will need an interface module (such as NVMe or UFS); Note that
41 * this model only calculates the delay and does not perform the actual
42 * transaction.
43 *
44 * To access the memory, use either readMemory or writeMemory. This will
45 * schedule an event at the tick where the action will finish. If a callback
46 * has been given as argument then that function will be called on completion
47 * of that event. Note that this does not guarantee that there are no other
48 * actions pending in the flash device.
49 *
50 * IMPORTANT: number of planes should be a power of 2.
51 */
52
53 #include "dev/arm/flash_device.hh"
54
55 #include "base/trace.hh"
56 #include "debug/Drain.hh"
57
58 /**
59 * Create this device
60 */
61
62 FlashDevice*
63 FlashDeviceParams::create()
64 {
65 return new FlashDevice(this);
66 }
67
68
69 /**
70 * Flash Device constructor and destructor
71 */
72
73 FlashDevice::FlashDevice(const FlashDeviceParams* p):
74 AbstractNVM(p),
75 diskSize(0),
76 blockSize(p->blk_size),
77 pageSize(p->page_size),
78 GCActivePercentage(p->GC_active),
79 readLatency(p->read_lat),
80 writeLatency(p->write_lat),
81 eraseLatency(p->erase_lat),
82 dataDistribution(p->data_distribution),
83 numPlanes(p->num_planes),
84 pagesPerBlock(0),
85 pagesPerDisk(0),
86 blocksPerDisk(0),
87 planeMask(numPlanes - 1),
88 planeEventQueue(numPlanes),
89 planeEvent([this]{ actionComplete(); }, name())
90 {
91
92 /*
93 * Let 'a' be a power of two of n bits, written such that a-n is the msb
94 * and a-0 is the lsb. Since it is a power of two, only one bit (a-x,
95 * with 0 <= x <= n) is set. If we subtract one from this number the bits
96 * a-(x-1) to a-0 are set and all the other bits are cleared. Hence a
97 * bitwise AND with those two numbers results in an integer with all bits
98 * cleared.
99 */
100 if (numPlanes & planeMask)
101 fatal("Number of planes is not a power of 2 in flash device.\n");
102 }
103
104 /**
105 * Initiates all the flash functions: initializes the lookup tables, age of
106 * the device, etc. This can only be done once the disk image is known.
107 * Thats why it can't be done in the constructor.
108 */
109 void
110 FlashDevice::initializeFlash(uint64_t disk_size, uint32_t sector_size)
111 {
112 diskSize = disk_size * sector_size;
113 pagesPerBlock = blockSize / pageSize;
114 pagesPerDisk = diskSize / pageSize;
115 blocksPerDisk = diskSize / blockSize;
116
117 /** Sanity information: check flash configuration */
118 DPRINTF(FlashDevice, "diskSize: %d Bytes; %d pages per block, %d pages "
119 "per disk\n", diskSize, pagesPerBlock, pagesPerDisk);
120
121 locationTable.resize(pagesPerDisk);
122
123 /**Garbage collection related*/
124 blockValidEntries.resize(blocksPerDisk, 0);
125 blockEmptyEntries.resize(blocksPerDisk, pagesPerBlock);
126
127 /**
128 * This is a bitmap. Every bit is a page
129 * unknownPages is a vector of 32 bit integers. If every page was an
130 * integer, the total size would be pagesPerDisk; since we can map one
131 * page per bit we need ceil(pagesPerDisk/32) entries. 32 = 1 << 5 hence
132 * it will do to just shift pagesPerDisk five positions and add one. This
133 * will allocate one integer to many for this data structure in the worst
134 * case.
135 */
136 unknownPages.resize((pagesPerDisk >> 5) + 1, 0xFFFFFFFF);
137
138 for (uint32_t count = 0; count < pagesPerDisk; count++) {
139 //setup lookup table + physical aspects
140
141 if (dataDistribution == Enums::stripe) {
142 locationTable[count].page = count / blocksPerDisk;
143 locationTable[count].block = count % blocksPerDisk;
144
145 } else {
146 locationTable[count].page = count % pagesPerBlock;
147 locationTable[count].block = count / pagesPerBlock;
148 }
149 }
150 }
151
152 FlashDevice::~FlashDevice()
153 {
154 DPRINTF(FlashDevice, "Remove FlashDevice\n");
155 }
156
157 /**
158 * Handles the accesses to the device.
159 * The function determines when certain actions are scheduled and schedules
160 * an event that uses the callback function on completion of the action.
161 */
162 void
163 FlashDevice::accessDevice(uint64_t address, uint32_t amount, Callback *event,
164 Actions action)
165 {
166 DPRINTF(FlashDevice, "Flash calculation for %d bytes in %d pages\n"
167 , amount, pageSize);
168
169 std::vector<Tick> time(numPlanes, 0);
170 uint64_t logic_page_addr = address / pageSize;
171 uint32_t plane_address = 0;
172
173 /**
174 * The access will be broken up in a number of page accesses. The number
175 * of page accesses depends on the amount that needs to be transfered.
176 * The assumption here is that the interface is completely ignorant of
177 * the page size and that this model has to figure out all of the
178 * transaction characteristics.
179 */
180 for (uint32_t count = 0; amount > (count * pageSize); count++) {
181 uint32_t index = (locationTable[logic_page_addr].block *
182 pagesPerBlock) + (logic_page_addr % pagesPerBlock);
183
184 DPRINTF(FlashDevice, "Index 0x%8x, Block 0x%8x, pages/block %d,"
185 " logic address 0x%8x\n", index,
186 locationTable[logic_page_addr].block, pagesPerBlock,
187 logic_page_addr);
188 DPRINTF(FlashDevice, "Page %d; %d bytes up to this point\n", count,
189 (count * pageSize));
190
191 plane_address = locationTable[logic_page_addr].block & planeMask;
192
193 if (action == ActionRead) {
194 //lookup
195 //call accessTimes
196 time[plane_address] += accessTimes(locationTable[logic_page_addr]
197 .block, ActionRead);
198
199 /*stats*/
200 stats.readAccess.sample(logic_page_addr);
201 stats.readLatency.sample(time[plane_address]);
202 } else { //write
203 //lookup
204 //call accessTimes if appropriate, page may be unknown, so lets
205 //give it the benefit of the doubt
206
207 if (getUnknownPages(index))
208 time[plane_address] += accessTimes
209 (locationTable[logic_page_addr].block, ActionWrite);
210
211 else //A remap is needed
212 time[plane_address] += remap(logic_page_addr);
213
214 /*stats*/
215 stats.writeAccess.sample(logic_page_addr);
216 stats.writeLatency.sample(time[plane_address]);
217 }
218
219 /**
220 * Check if the page is known and used. unknownPages is a bitmap of
221 * all the pages. It tracks wether we can be sure that the
222 * information of this page is taken into acount in the model (is it
223 * considered in blockValidEntries and blockEmptyEntries?). If it has
224 * been used in the past, then it is known.
225 */
226 if (getUnknownPages(index)) {
227 clearUnknownPages(index);
228 --blockEmptyEntries[locationTable[logic_page_addr].block];
229 ++blockValidEntries[locationTable[logic_page_addr].block];
230 }
231
232 stats.fileSystemAccess.sample(address);
233 ++logic_page_addr;
234 }
235
236 /**
237 * previous part of the function found the times spend in different
238 * planes, now lets find the maximum to know when to callback the disk
239 */
240 for (uint32_t count = 0; count < numPlanes; count++){
241 plane_address = (time[plane_address] > time[count]) ? plane_address
242 : count;
243
244 DPRINTF(FlashDevice, "Plane %d is busy for %d ticks\n", count,
245 time[count]);
246
247 if (time[count] != 0) {
248
249 struct CallBackEntry cbe;
250 /**
251 * If there are no events for this plane, then add the current
252 * time to the occupation time; otherwise, plan it after the
253 * last event. If by chance that event is handled in this tick,
254 * then we would still end up with the same result.
255 */
256 if (planeEventQueue[count].empty())
257 cbe.time = time[count] + curTick();
258 else
259 cbe.time = time[count] +
260 planeEventQueue[count].back().time;
261 cbe.function = NULL;
262 planeEventQueue[count].push_back(cbe);
263
264 DPRINTF(FlashDevice, "scheduled at: %ld\n", cbe.time);
265
266 if (!planeEvent.scheduled())
267 schedule(planeEvent, planeEventQueue[count].back().time);
268 else if (planeEventQueue[count].back().time < planeEvent.when())
269 reschedule(planeEvent,
270 planeEventQueue[plane_address].back().time, true);
271 }
272 }
273
274 //worst case two plane finish at the same time, each triggers an event
275 //and this callback will be called once. Maybe before the other plane
276 //could execute its event, but in the same tick.
277 planeEventQueue[plane_address].back().function = event;
278 DPRINTF(FlashDevice, "Callback queued for plane %d; %d in queue\n",
279 plane_address, planeEventQueue[plane_address].size());
280 DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when());
281 }
282
283 /**
284 * When a plane completes its action, this event is triggered. When a
285 * callback function was associated with that event, it will be called.
286 */
287
288 void
289 FlashDevice::actionComplete()
290 {
291 DPRINTF(FlashDevice, "Plane action completed\n");
292 uint8_t plane_address = 0;
293
294 uint8_t next_event = 0;
295
296 /**Search for a callback that is supposed to happen in this Tick*/
297 for (plane_address = 0; plane_address < numPlanes; plane_address++) {
298 if (!planeEventQueue[plane_address].empty()) {
299 /**
300 * Invariant: All queued events are scheduled in the present
301 * or future.
302 */
303 assert(planeEventQueue[plane_address].front().time >= curTick());
304
305 if (planeEventQueue[plane_address].front().time == curTick()) {
306 /**
307 * To ensure that the follow-up action is executed correctly,
308 * the callback entry first need to be cleared before it can
309 * be called.
310 */
311 Callback *temp = planeEventQueue[plane_address].front().
312 function;
313 planeEventQueue[plane_address].pop_front();
314
315 /**Found a callback, lets make it happen*/
316 if (temp != NULL) {
317 DPRINTF(FlashDevice, "Callback, %d\n", plane_address);
318 temp->process();
319 }
320 }
321 }
322 }
323
324 /** Find when to schedule the planeEvent next */
325 for (plane_address = 0; plane_address < numPlanes; plane_address++) {
326 if (!planeEventQueue[plane_address].empty())
327 if (planeEventQueue[next_event].empty() ||
328 (planeEventQueue[plane_address].front().time <
329 planeEventQueue[next_event].front().time))
330 next_event = plane_address;
331 }
332
333 /**Schedule the next plane that will be ready (if any)*/
334 if (!planeEventQueue[next_event].empty()) {
335 DPRINTF(FlashDevice, "Schedule plane: %d\n", plane_address);
336 reschedule(planeEvent, planeEventQueue[next_event].front().time, true);
337 }
338
339 checkDrain();
340
341 DPRINTF(FlashDevice, "returing from flash event\n");
342 DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when());
343 }
344
345 /**
346 * Handles the remapping of the pages. It is a (I hope) sensible statistic
347 * approach. asumption: garbage collection happens when a clean is needed
348 * (may become stochastic function).
349 */
350 Tick
351 FlashDevice::remap(uint64_t logic_page_addr)
352 {
353 /**
354 * Are there any empty left in this Block, or do we need to do an erase
355 */
356 if (blockEmptyEntries[locationTable[logic_page_addr].block] > 0) {
357 //just a remap
358 //update tables
359 --blockEmptyEntries[locationTable[logic_page_addr].block];
360 //access to this table won't be sequential anymore
361 locationTable[logic_page_addr].page = pagesPerBlock + 2;
362 //access new block
363 Tick time = accessTimes(locationTable[logic_page_addr].block,
364 ActionWrite);
365
366 DPRINTF(FlashDevice, "Remap returns %d ticks\n", time);
367 return time;
368
369 } else {
370 //calculate how much time GC would have taken
371 uint32_t block = locationTable[logic_page_addr].block;
372 Tick time = ((GCActivePercentage *
373 (accessTimes(block, ActionCopy) +
374 accessTimes(block, ActionErase)))
375 / 100);
376
377 //use block as the logical start address of the block
378 block = locationTable[logic_page_addr].block * pagesPerBlock;
379
380 //assumption: clean will improve locality
381 for (uint32_t count = 0; count < pagesPerBlock; count++) {
382 assert(block + count < pagesPerDisk);
383 locationTable[block + count].page = (block + count) %
384 pagesPerBlock;
385 }
386
387 blockEmptyEntries[locationTable[logic_page_addr].block] =
388 pagesPerBlock;
389 /*stats*/
390 ++stats.totalGCActivations;
391
392 DPRINTF(FlashDevice, "Remap with erase action returns %d ticks\n",
393 time);
394
395 return time;
396 }
397
398 }
399
400 /**
401 * Calculates the accesstime per operation needed
402 */
403 Tick
404 FlashDevice::accessTimes(uint64_t block, Actions action)
405 {
406 Tick time = 0;
407
408 switch(action) {
409 case ActionRead: {
410 /**Just read the page*/
411 time = readLatency;
412 } break;
413
414 case ActionWrite: {
415 /**Write the page, and read the result*/
416 time = writeLatency + readLatency;
417 } break;
418
419 case ActionErase: {
420 /**Erase and check wether it was successfull*/
421 time = eraseLatency + readLatency;
422 } break;
423
424 case ActionCopy: {
425 /**Copy every valid page*/
426 uint32_t validpages = blockValidEntries[block];
427 time = validpages * (readLatency + writeLatency);
428 } break;
429
430 default: break;
431 }
432
433 //Used to determine sequential action.
434 DPRINTF(FlashDevice, "Access returns %d ticks\n", time);
435 return time;
436 }
437
438 /**
439 * clearUnknownPages. defines that a page is known and used
440 * unknownPages is a bitmap of all the pages. It tracks wether we can be sure
441 * that the information of this page is taken into acount in the model (is it
442 * considered in blockValidEntries and blockEmptyEntries?). If it has been
443 * used in the past, then it is known. But it needs to be tracked to make
444 * decisions about write accesses, and indirectly about copy actions. one
445 * unknownPage entry is a 32 bit integer. So if we have a page index, then
446 * that means that we need entry floor(index/32) (index >> 5) and we need to
447 * select the bit which number is equal to the remainder of index/32
448 * (index%32). The bit is cleared to make sure that we see it as considered
449 * in the future.
450 */
451
452 inline
453 void
454 FlashDevice::clearUnknownPages(uint32_t index)
455 {
456 unknownPages[index >> 5] &= ~(0x01 << (index % 32));
457 }
458
459 /**
460 * getUnknownPages. Verify wether a page is known
461 */
462
463 inline
464 bool
465 FlashDevice::getUnknownPages(uint32_t index)
466 {
467 return unknownPages[index >> 5] & (0x01 << (index % 32));
468 }
469
470 void
471 FlashDevice::regStats()
472 {
473 AbstractNVM::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
520 void
521 FlashDevice::serialize(CheckpointOut &cp) const
522 {
523 SERIALIZE_SCALAR(planeMask);
524
525 SERIALIZE_CONTAINER(unknownPages);
526 SERIALIZE_CONTAINER(blockValidEntries);
527 SERIALIZE_CONTAINER(blockEmptyEntries);
528
529 int location_table_size = locationTable.size();
530 SERIALIZE_SCALAR(location_table_size);
531 for (uint32_t count = 0; count < location_table_size; count++) {
532 paramOut(cp, csprintf("locationTable[%d].page", count),
533 locationTable[count].page);
534 paramOut(cp, csprintf("locationTable[%d].block", count),
535 locationTable[count].block);
536 }
537 };
538
539 /**
540 * Unserialize; needed to restore from checkpoints
541 */
542
543 void
544 FlashDevice::unserialize(CheckpointIn &cp)
545 {
546 UNSERIALIZE_SCALAR(planeMask);
547
548 UNSERIALIZE_CONTAINER(unknownPages);
549 UNSERIALIZE_CONTAINER(blockValidEntries);
550 UNSERIALIZE_CONTAINER(blockEmptyEntries);
551
552 int location_table_size;
553 UNSERIALIZE_SCALAR(location_table_size);
554 locationTable.resize(location_table_size);
555 for (uint32_t count = 0; count < location_table_size; count++) {
556 paramIn(cp, csprintf("locationTable[%d].page", count),
557 locationTable[count].page);
558 paramIn(cp, csprintf("locationTable[%d].block", count),
559 locationTable[count].block);
560 }
561 };
562
563 /**
564 * Drain; needed to enable checkpoints
565 */
566
567 DrainState
568 FlashDevice::drain()
569 {
570 if (planeEvent.scheduled()) {
571 DPRINTF(Drain, "Flash device is draining...\n");
572 return DrainState::Draining;
573 } else {
574 DPRINTF(Drain, "Flash device in drained state\n");
575 return DrainState::Drained;
576 }
577 }
578
579 /**
580 * Checkdrain; needed to enable checkpoints
581 */
582
583 void
584 FlashDevice::checkDrain()
585 {
586 if (drainState() != DrainState::Draining)
587 return;
588
589 if (planeEvent.when() > curTick()) {
590 DPRINTF(Drain, "Flash device is still draining\n");
591 } else {
592 DPRINTF(Drain, "Flash device is done draining\n");
593 signalDrainDone();
594 }
595 }