1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ftlnp.h"
6 
7 #if INC_FTL_NDM
8 // Configuration
9 #define DEBUG_RESUME FALSE
10 
11 // Symbol Definitions
12 #define COPY_BLK_END 0xFFFFFFFD
13 #define COPY_BLK_MARK 0xFFFFFFFE
14 
15 // Global Variable Declarations
16 CircLink FtlnVols = {&FtlnVols, &FtlnVols};
17 #if FTLN_DEBUG_PTR
18 FTLN Ftln;
19 #endif
20 #ifdef FTL_RESUME_STRESS
21 extern int FtlMblkResumeCnt, FtlVblkResumeCnt;
22 #endif
23 
24 // Local Function Definitions
25 
26 #if INC_ELIST
27 //  proc_elist: Process elist map page
28 //
29 //       Input: ftl = pointer to FTL control block
30 //
31 //     Returns: NDM_PAGE_VALID (1) or NDM_PAGE_INVALID (2)
32 //
proc_elist(FTLN ftl)33 static int proc_elist(FTLN ftl) {
34     ui32 b, wc, *lp = (ui32 *)(ftl->main_buf + FTLN_META_DATA_BEG);
35 
36     // Loop to process each block number/wear count entry in page.
37     do {
38         // Get number of proposed erased block and its wear count.
39         b = RD32_LE(lp);
40         ++lp;
41         wc = RD32_LE(lp);
42         ++lp;
43 
44         // List validly ends with -1.
45         if (b > ftl->num_blks) {
46             PfAssert(b == (ui32)-1);
47             break;
48         }
49 
50         // Check block's wear count.
51         if ((wc > ftl->high_wc) || (ftl->high_wc - wc > 0xFF))
52             return NDM_PAGE_INVALID;
53 
54         // Skip the elist block itself. It is definitely not erased.
55         if (b != ftl->elist_blk) {
56 #if DEBUG_ELIST
57             // Verify that this block is unwritten.
58             FtlnCheckBlank(ftl, b);
59 #endif
60 
61             // Verify block is unused and not a map block.
62             if (NUM_USED(ftl->bdata[b]) || IS_MAP_BLK(ftl->bdata[b]))
63                 return NDM_PAGE_INVALID;
64 
65             // If not already marked free, increment free block count.
66             if (!IS_FREE(ftl->bdata[b]))
67                 ++ftl->num_free_blks;
68 
69             // Set block's state and wear count lag.
70             ftl->bdata[b] = FREE_BLK_FLAG | ERASED_BLK_FLAG;
71             ftl->blk_wc_lag[b] = ftl->high_wc - wc;
72         }
73     } while (lp < (ui32*)(ftl->main_buf + ftl->page_size));
74 
75     // Finished and no check failed. Page is valid.
76     return NDM_PAGE_VALID;
77 }
78 #endif
79 
80 // map_page_check: Check contents of map page for validity
81 //
82 //      Inputs: ftl = pointer to FTL control block
83 //              apn = absolute physical page number (+ ftl->start_pn)
84 //              process = do stored request if map page is meta-page
85 //
86 //     Returns: -1 if fatal error, else NDM_PAGE_ERASED (0),
87 //              NDM_PAGE_VALID (1), or NDM_PAGE_INVALID (2)
88 //
map_page_check(FTLN ftl,ui32 apn,int process)89 static int map_page_check(FTLN ftl, ui32 apn, int process) {
90     ui32 mpn, n, *ppns = (ui32 *)ftl->main_buf;
91     int status;
92 
93     // Call driver validity check. Return -1 if error.
94     ++ftl->stats.page_check;
95     status = ftl->page_check(apn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
96     if (status < 0)
97         return FtlnFatErr(ftl);
98 
99     // If page is erased or invalid, return its status.
100     if (status != NDM_PAGE_VALID)
101         return status;
102 
103     // If MPN too big, page is invalid.
104     mpn = GET_SA_VPN(ftl->spare_buf);
105     if (mpn >= ftl->num_map_pgs)
106         return NDM_PAGE_INVALID;
107 
108     // If meta-page, check version, type, and format. Process if enabled.
109     if (mpn == ftl->num_map_pgs - 1) {
110         ui32 type, vers = RD32_LE(&ppns[0]);
111 
112         // Check if first metapage version number.
113         if (vers == FTLN_META_VER0) {
114             ui32 b, i;
115 
116             // If recycle block wrong, page is invalid.
117             b = RD32_LE(&ppns[1]);
118             if (b >= ftl->num_blks && b != (ui32)-1)
119                 return NDM_PAGE_INVALID;
120 
121             // Rest of the page should be erased. If not, page is invalid.
122             for (i = 2; i < ftl->page_size / sizeof(ui32); ++i)
123                 if (RD32_LE(&ppns[i]) != (ui32)-1)
124                     return NDM_PAGE_INVALID;
125         }
126 
127         // Else check if second metapage version.
128         else if (vers == FTLN_META_VER1) {
129             // Read the meta-page type.
130             type = RD32_LE(&ppns[1]);
131 
132             // Check if 'continue format' metadata.
133             if (type == CONT_FORMAT) {
134                 // Rest of meta-page should be erased.
135                 for (n = 2; n < ftl->page_size / sizeof(ui32); ++n)
136                     if (RD32_LE(&ppns[n]) != (ui32)-1)
137                         return NDM_PAGE_INVALID;
138 
139                 // If enabled, resume the format.
140                 if (process)
141                     if (FtlnFormat(ftl, (apn - ftl->start_pn) / ftl->pgs_per_blk))
142                         return -1;
143             }
144 #if INC_ELIST
145             // Check if 'erased block list' metapage.
146             else if (type == ERASED_LIST) {
147                 // Just save block number if called from build_map(). Called
148                 // once for each used page in the elist block.
149                 if (process == FALSE)
150                     ftl->elist_blk = (apn - ftl->start_pn) / ftl->pgs_per_blk;
151 
152                 // Else read/check/process each elist page contents if caller
153                 // is meta_read(). Called once, using last elist page number.
154                 else {
155                     ui32 ap0 = ftl->start_pn + ftl->elist_blk * ftl->pgs_per_blk;
156 
157                     // Process each elist page, from last to first.
158                     for (;;) {
159                         //---------------------------------------------------------
160                         // Verify and apply elist page. Return if page invalid.
161                         //---------------------------------------------------------
162                         status = proc_elist(ftl);
163                         if (status != NDM_PAGE_VALID)
164                             return status;
165 
166                         //---------------------------------------------------------
167                         // If first (perhaps only) page was processed, finished!
168                         //---------------------------------------------------------
169                         if (apn == ap0)
170                             break;
171 
172 //---------------------------------------------------------
173 // Move to next written page in backwards direction. If
174 // MLC flash, move to page whose pair has higher offset.
175 //---------------------------------------------------------
176 #if INC_FTL_NDM_MLC && (INC_FTL_NDM_SLC || INC_FTL_NOR_WR1)
177                         if (ftl->type == NDM_MLC)
178 #endif
179 #if INC_NDM_MLC
180                             for (;;) {
181                                 ui32 pg_offset = --apn % ftl->pgs_per_blk;
182 
183                                 if (pg_offset == 0)
184                                     break;
185                                 if (ftl->pair_offset(pg_offset, ftl->ndm) >= pg_offset)
186                                     break;
187                             }
188 #endif
189 #if INC_FTL_NDM_MLC && (INC_FTL_NDM_SLC || INC_FTL_NOR_WR1)
190                         else
191 #endif
192 #if INC_FTL_NDM_SLC || INC_FTL_NOR_WR1
193                             --apn;
194 #endif
195 
196                         //---------------------------------------------------------
197                         // Call driver to read/check next page. Return -1 if error.
198                         //---------------------------------------------------------
199                         ++ftl->stats.page_check;
200                         status = ftl->page_check(apn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
201                         if (status < 0)
202                             return FtlnFatErr(ftl);
203 
204                         //---------------------------------------------------------
205                         // If page is erased or invalid, return its status.
206                         //---------------------------------------------------------
207                         if (status != NDM_PAGE_VALID)
208                             return status;
209 
210                         //---------------------------------------------------------
211                         // Verify the metadata version is correct.
212                         //---------------------------------------------------------
213                         if (RD32_LE(&ppns[0]) != FTLN_META_VER1)
214                             return NDM_PAGE_INVALID;
215 
216                         //---------------------------------------------------------
217                         // Verify the metadata type is correct.
218                         //---------------------------------------------------------
219                         if (RD32_LE(&ppns[1]) != ERASED_LIST)
220                             return NDM_PAGE_INVALID;
221                     }
222                 }
223             }
224 #endif
225 
226             // Else meta page type is invalid.
227             else
228                 return NDM_PAGE_INVALID;
229         }
230 
231         // Else meta page version is invalid.
232         else
233             return NDM_PAGE_INVALID;
234     }
235 
236     // Else regular map page.
237     else {
238         ui32 pn;
239         ui8* maddr = ftl->main_buf;
240 
241         // Check every entry for validity.
242         for (n = 0; n < ftl->mappings_per_mpg; ++n) {
243             // Read entry's mapping from map page and update entry address.
244             pn = GET_MAP_PPN(maddr);
245             maddr += FTLN_PN_SZ;
246 
247             // Invalid page if entry is neither valid nor the unmapped value.
248             if (pn >= ftl->num_pages && pn != UNMAPPED_PN)
249                 return NDM_PAGE_INVALID;
250         }
251     }
252 
253     // All checks passed! Page is valid.
254     return NDM_PAGE_VALID;
255 }
256 
257 //   build_map: Scan volume blocks and for map ones, read all valid
258 //              map pages to build the MPNs array
259 //
260 //       Input: ftl = pointer to FTL control block
261 //
262 //     Returns: 0 on success, -1 on error
263 //
build_map(FTLN ftl)264 static int build_map(FTLN ftl) {
265     int status;
266     ui32 b, bc, *bcs, mpn, n, pn, po, *b_ptr;
267 
268     // Allocate space to hold block count for each map page array entry.
269     bcs = FsCalloc(ftl->num_map_pgs, sizeof(ui32));
270     if (bcs == NULL)
271         return -1;
272 
273     // Loop over every block looking for map blocks. This list was made
274     // by format_status() and only has one with the highest BC, but may
275     // include old map blocks that didn't get erased after their recycle.
276     for (b = 0; b < ftl->num_blks; ++b) {
277         // Skip blocks that don't hold any map pages.
278         if (!IS_MAP_BLK(ftl->bdata[b]))
279             continue;
280 
281         // Compute first page on block.
282         pn = ftl->start_pn + b * ftl->pgs_per_blk;
283 
284         // For each page in map block, check if MPN array needs updating.
285         for (po = 0, bc = (ui32)-1; po < ftl->pgs_per_blk; ++po, ++pn) {
286 #if INC_FTL_NDM_MLC
287             // For MLC devices, skip pages not written by the FTL, those
288             // whose pair offset is lower than their offset.
289             if (ftl->type == NDM_MLC && ftl->pair_offset(po, ftl->ndm) < po)
290                 continue;
291 #endif
292 
293             // Check if page is on newest map block and not its first page.
294             // The newest map block is only one that potentially has (as its
295             // partially written last page) an invalid page. Look for that.
296             if (po && bc == ftl->high_bc) {
297                 // Check if page contents are valid. Return -1 if fatal error.
298                 status = map_page_check(ftl, pn, FALSE);
299                 if (status < 0) {
300                     FsFree(bcs);
301                     return -1;
302                 }
303 
304                 // If invalid last page, break to advance to next map block.
305                 if (status == NDM_PAGE_INVALID)
306                     break;
307 
308                 // Else erased last page, break to advance to next map block.
309                 else if (status == NDM_PAGE_ERASED)
310                     break;
311 
312                 // Remember highest valid map page on most recent map block.
313                 ftl->high_bc_mblk_po = po;
314             }
315 
316             // Else page on older map block or first on newest map block.
317             else {
318                 // Read page's spare area.
319                 ++ftl->stats.read_spare;
320                 status = ftl->read_spare(pn, ftl->spare_buf, ftl->ndm);
321 
322                 // Return if fatal error.
323                 if (status == -2) {
324                     FsFree(bcs);
325                     return FtlnFatErr(ftl);
326                 }
327 
328                 // Break to skip block if uncorrectable ECC error occurred.
329                 if (status < 0)
330                     break;
331             }
332 
333             // If first page, retrieve block count. Otherwise compare with
334             // block count of block's already-checked-valid first page.
335             if (po == 0)
336                 bc = GET_SA_BC(ftl->spare_buf);
337             else if (bc != GET_SA_BC(ftl->spare_buf)) {
338 #if FTLN_DEBUG > 1
339                 printf("build_ma: b = %u, po = %u, i_bc = %u vs 0_bc = %u\n", b, po,
340                        GET_SA_BC(ftl->spare_buf), bc);
341 #endif
342 
343                 // Should not be, but page is invalid. Break to skip block.
344                 break;
345             }
346 
347             // Block count is retrieved by now.
348             PfAssert(bc != (ui32)-1);
349 
350             // Adjust map block read count.
351             b_ptr = &ftl->bdata[b];
352             INC_RC(ftl, b_ptr, 1);
353 
354             // Retrieve MPN and check that it is valid.
355             mpn = GET_SA_VPN(ftl->spare_buf);
356             if (mpn > ftl->num_map_pgs) {
357 #if FTLN_DEBUG > 1
358                 printf("build_ma: b = %u, po = %u, mpn = %u, max = %u\n", b, po, mpn,
359                        ftl->num_map_pgs);
360 #endif
361 
362                 // Should not be, but page is invalid. Break to skip block.
363                 break;
364             }
365 
366             // If no entry for this MPN in array OR entry in same block as
367             // current block OR entry in a block with a lower block count,
368             // update array entry with current page.
369             if (ftl->mpns[mpn] == (ui32)-1 || ftl->mpns[mpn] / ftl->pgs_per_blk == b ||
370                 bcs[mpn] < bc) {
371                 // If not metapage, adjust used counts of referenced blks.
372                 if (mpn < ftl->num_map_pgs - 1) {
373                     // If old MPN array entry already set, decrement old block's
374                     // used pages count.
375                     if (ftl->mpns[mpn] != (ui32)-1) {
376                         uint ob = ftl->mpns[mpn] / ftl->pgs_per_blk;
377 
378                         PfAssert(IS_MAP_BLK(ftl->bdata[ob]));
379                         DEC_USED(ftl->bdata[ob]);
380                     }
381 
382                     // Increment used count for new block.
383                     PfAssert(IS_MAP_BLK(ftl->bdata[b]));
384                     INC_USED(ftl->bdata[b]);
385                 }
386 #if FTLN_DEBUG > 1
387                 printf("build_ma: mpn = %u, old_pn = %d, new_pn = %u\n", mpn, ftl->mpns[mpn],
388                        b * ftl->pgs_per_blk + po);
389 #endif
390 
391                 // Save the map page number and (temporarily) the block count.
392                 ftl->mpns[mpn] = b * ftl->pgs_per_blk + po;
393                 bcs[mpn] = bc;
394             }
395         }
396     }
397 
398     // Free temporary block counts space.
399     FsFree(bcs);
400 
401 #if INC_ELIST
402     // If present, change state of elist block from map block to free.
403     if (ftl->elist_blk != (ui32)-1) {
404         ftl->bdata[ftl->elist_blk] = FREE_BLK_FLAG;
405         ++ftl->num_free_blks;
406     }
407 #endif
408 
409     // Loop over map blocks to build volume block's used page counts.
410     for (mpn = 0; mpn < ftl->num_map_pgs - 1; ++mpn) {
411         ui8* maddr;
412 
413         // Skip unused map pages.
414         pn = ftl->mpns[mpn];
415         if (pn == (ui32)-1)
416             continue;
417 
418 #if FTLN_DEBUG > 1
419         printf("  -> MPN[%2u] = %u\n", mpn, pn);
420 #endif
421 
422         // Read map page. Return -1 if error.
423         if (FtlnRdPage(ftl, pn, ftl->main_buf))
424             return -1;
425 
426         // Loop over every physical page number entry on map page.
427         maddr = ftl->main_buf;
428         for (n = 0; n < ftl->mappings_per_mpg; ++n) {
429             // Read entry's mapping from map page and update entry address.
430             pn = GET_MAP_PPN(maddr);
431             maddr += FTLN_PN_SZ;
432 
433             // Continue if no mapping at this entry.
434             if (pn >= ftl->num_pages)
435                 continue;
436 
437             // Get page's block number and verify its status.
438             b = pn / ftl->pgs_per_blk;
439             PfAssert(!IS_FREE(ftl->bdata[b]) && !IS_MAP_BLK(ftl->bdata[b]));
440             if (IS_FREE(ftl->bdata[b]) || IS_MAP_BLK(ftl->bdata[b]))
441                 return -1;
442 
443             // Increment the used page count for this volume block.
444             INC_USED(ftl->bdata[b]);
445 
446             // Record the highest used page offset in block's read count.
447             po = pn % ftl->pgs_per_blk;
448             if (po > GET_RC(ftl->bdata[b]))
449                 SET_RC(ftl->bdata[b], po);
450         }
451     }
452 
453     // If not recovered from the copy-end page (after interrupted vblk
454     // resume), find the volume block with the lowest used page offset.
455     if (ftl->copy_end_found == FALSE) {
456         ftl->resume_po = ftl->pgs_per_blk;
457         for (b = 0; b < ftl->num_blks; ++b) {
458             if (NUM_USED(ftl->bdata[b]) && !IS_MAP_BLK(ftl->bdata[b])) {
459                 po = GET_RC(ftl->bdata[b]);
460                 if (po < ftl->resume_po) {
461                     ftl->resume_vblk = b;
462                     ftl->resume_po = po;
463                     if (po == 0)
464                         break;
465                 }
466             }
467         }
468     }
469 #if FTLN_DEBUG > 1
470     printf("vol block %d has lowest used page offset (%d)\n", ftl->resume_vblk, ftl->resume_po);
471 #endif
472 
473     // Clean temporary use of vol block read-wear field for page offset.
474     for (b = 0; b < ftl->num_blks; ++b)
475         if (NUM_USED(ftl->bdata[b]) && !IS_MAP_BLK(ftl->bdata[b]))
476             ftl->bdata[b] &= ~RC_MASK;
477 
478     // Return success.
479     return 0;
480 }
481 
482 //  set_wc_lag: Set block's wear count lag and possibly adjust the
483 //              highest/lowest overall wear counts
484 //
485 //      Inputs: ftl = pointer to FTL control block
486 //              b = block number
487 //              wc = wear count for block
488 //         I/O: *low_wc = lowest wear count encountered so far
489 //
set_wc_lag(FTLN ftl,ui32 b,ui32 wc,ui32 * low_wc)490 static void set_wc_lag(FTLN ftl, ui32 b, ui32 wc, ui32* low_wc) {
491     // If this block has lowest wear count, update lowest.
492     if (*low_wc > wc)
493         *low_wc = wc;
494 
495     // If it has highest wear count, update highest and also update wear
496     // count offsets of all used (not free) blocks below it.
497     if (wc > ftl->high_wc) {
498         ui32 lb, increase = wc - ftl->high_wc;
499 
500         // Loop over all lower numbered blocks.
501         for (lb = 0; lb < b; ++lb) {
502             // Skip blocks that don't have a valid wear count value.
503             if (GET_RC(ftl->bdata[lb]) == 100)
504                 continue;
505 
506             // Update previously set wear count lags, avoiding ui8 overflow.
507             if (ftl->blk_wc_lag[lb] + increase > 0xFF) {
508                 ftl->blk_wc_lag[lb] = 0xFF;
509 #if FTLN_DEBUG
510                 ++ftl->max_wc_over;
511 #endif
512             } else
513                 ftl->blk_wc_lag[lb] += increase;
514 
515 #if FTLN_DEBUG
516             // If new value, record maximum encountered wear lag.
517             if (ftl->max_wc_lag < ftl->blk_wc_lag[lb])
518                 ftl->max_wc_lag = ftl->blk_wc_lag[lb];
519 #endif
520         }
521 
522         // Remember new high wear count.
523         ftl->high_wc = wc;
524     }
525 
526     // Set block wear count lag, avoiding ui8 overflow.
527     if (ftl->high_wc - wc > 0xFF) {
528         ftl->blk_wc_lag[b] = 0xFF;
529 #if FTLN_DEBUG
530         ++ftl->max_wc_over;
531 #endif
532     } else
533         ftl->blk_wc_lag[b] = ftl->high_wc - wc;
534 
535 #if FTLN_DEBUG
536     // If new value, record maximum encountered wear lag.
537     if (ftl->max_wc_lag < ftl->blk_wc_lag[b])
538         ftl->max_wc_lag = ftl->blk_wc_lag[b];
539 #endif
540 }
541 
542 // format_status: Check if FTL volume is formatted
543 //
544 //       Input: ftl = pointer to FTL control block
545 //
546 //     Returns: TRUE if formatted, FALSE if unformatted, -1 if error
547 //
format_status(FTLN ftl)548 static int format_status(FTLN ftl) {
549     ui32 b, n, avg_lag, pn, bc, wc, low_wc = (ui32)-1;
550     int rc, formatted = FALSE;
551 
552     // Scan first page on all blocks to determine block status.
553     for (ftl->num_free_blks = b = 0; b < ftl->num_blks; ++b) {
554         // Compute page number of block's first page.
555         pn = ftl->start_pn + b * ftl->pgs_per_blk;
556 
557         // Read spare area for first page. Return -1 if fatal error.
558         ++ftl->stats.read_spare;
559         rc = ftl->read_spare(pn, ftl->spare_buf, ftl->ndm);
560         if (rc == -2)
561             return FtlnFatErr(ftl);
562 
563         // Read metadata from spare area.
564         bc = GET_SA_BC(ftl->spare_buf);
565         wc = GET_SA_WC(ftl->spare_buf);
566 
567         // Check if the block count is 0xFFFFFFFF.
568         if (bc == 0xFFFFFFFF) {
569             // If spare data looks erased, mark block as free.
570             if (wc == 0x0FFFFFFF) {
571                 ftl->bdata[b] = FREE_BLK_FLAG;
572                 ++ftl->num_free_blks;
573                 PfAssert(GET_RC(ftl->bdata[b]) != 100);
574                 SET_RC(ftl->bdata[b], 100);  // flag to use average wear count
575             }
576 
577             // Else classify as volume block.
578             else {
579                 // If its wear count is in expected range, record it.
580                 if ((wc <= ftl->high_wc + 32) && ((wc + 32 >= low_wc) || (low_wc == (ui32)-1)))
581                     set_wc_lag(ftl, b, wc, &low_wc);
582 
583                 // Else only use wear count if block has other non-empty pages
584                 // with same BC/wear, to discard partially written counts.
585                 else
586                     for (n = 1;;) {
587                         ui32 bc2, wc2;
588 
589                         // Read spare area for higher page. Return -1 if fatal error.
590                         ++ftl->stats.read_spare;
591                         rc = ftl->read_spare(pn + n, ftl->spare_buf, ftl->ndm);
592                         if (rc == -2)
593                             return FtlnFatErr(ftl);
594 
595                         // If read good and counts match, set block wear count lag.
596                         bc2 = GET_SA_BC(ftl->spare_buf);
597                         wc2 = GET_SA_WC(ftl->spare_buf);
598                         if ((rc == 0) && (bc == bc2) && (wc == wc2)) {
599                             set_wc_lag(ftl, b, wc, &low_wc);
600                             break;
601                         }
602 
603 #if INC_FTL_NDM_MLC
604                         // If MLC, try first next page that has no earlier pair, in
605                         // case FTL skipped volume pages for sync operation.
606                         if ((ftl->type == NDM_MLC) && (n == 1)) {
607                             n = ndmPastPrevPair(ftl->ndm, pn + 1) - pn;
608                             if (n != 1)
609                                 continue;
610                         }
611 #endif
612 
613                         // Done. Mark block as needing average wear count and break.
614                         PfAssert(GET_RC(ftl->bdata[b]) != 100);
615                         SET_RC(ftl->bdata[b], 100);
616                         break;
617                     }
618             }
619         }
620 
621         // Else check if this is an interrupted volume block transfer.
622         else if (bc == COPY_BLK_MARK) {
623 #if DEBUG_RESUME
624             puts("encountered copy-blk mark");
625 #endif
626             // Call driver validity check. Return -1 if error.
627             ++ftl->stats.page_check;
628             rc = ftl->page_check(pn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
629             if (rc < 0)
630                 return FtlnFatErr(ftl);
631 
632             // If page is invalid, mark block free and continue.
633             if (rc != NDM_PAGE_VALID) {
634                 ftl->bdata[b] = FREE_BLK_FLAG;
635                 ++ftl->num_free_blks;
636                 PfAssert(GET_RC(ftl->bdata[b]) != 100);
637                 SET_RC(ftl->bdata[b], 100);  // flag to use average wear count
638                 continue;
639             }
640 
641             // Set block wear count lag.
642             set_wc_lag(ftl, b, wc, &low_wc);
643 
644             // Search for copy-end page, indicating the 'copy to' finished.
645             for (n = 1; n < ftl->pgs_per_blk; ++n) {
646                 ui32 vpn;
647 
648                 // Read spare data. Return if fatal error. Skip if ECC error.
649                 ++ftl->stats.read_spare;
650                 rc = ftl->read_spare(pn + n, ftl->spare_buf, ftl->ndm);
651                 if (rc == -2)
652                     return FtlnFatErr(ftl);
653                 if (rc)
654                     continue;
655 
656                 // Read metadata from spare area.
657                 vpn = GET_SA_VPN(ftl->spare_buf);
658                 bc = GET_SA_BC(ftl->spare_buf);
659                 wc = GET_SA_WC(ftl->spare_buf);
660 
661                 // Check if this is the copy-end page.
662                 if ((vpn == COPY_BLK_END) && (bc == vpn) && (wc == 0)) {
663 #if DEBUG_RESUME
664                     puts("encountered copy-blk end");
665 #endif
666                     // Read and check the copy-end page. Return -1 if error.
667                     ++ftl->stats.page_check;
668                     rc = ftl->page_check(pn + n, ftl->main_buf, ftl->spare_buf, ftl->ndm);
669                     if (rc < 0)
670                         return FtlnFatErr(ftl);
671 
672                     // Break if page is invalid.
673                     if (rc != NDM_PAGE_VALID)
674                         break;
675 
676                     // Flag that the copy-end page has been found.
677                     ftl->copy_end_found = TRUE;
678 
679                     // Save parameters of the interrupted vblk resume transfer.
680                     ftl->resume_vblk = RD32_LE(&ftl->main_buf[0]);
681                     PfAssert(ftl->resume_vblk < ftl->num_blks);
682                     ftl->resume_tblk = b;
683                     ftl->resume_po = n - 1;
684 #if DEBUG_RESUME
685                     {
686                         ui32 vb = ftl->resume_vblk;
687 
688                         printf("resume_vblk=%d bdata=0x%X", vb, ftl->bdata[vb]);
689                         if (IS_FREE(ftl->bdata[vb]))
690                             puts(" free blk");
691                         else if (IS_MAP_BLK(ftl->bdata[vb]))
692                             puts(" map blk");
693                         else
694                             putchar('\n');
695                     }
696 #endif
697 
698                     // Mark the resume temporary block free and break.
699                     ftl->bdata[b] = FREE_BLK_FLAG;
700                     ++ftl->num_free_blks;
701                     break;
702                 }
703             }
704 
705             // If copy-end not found, erase block. Return -1 if I/O error.
706             if (!ftl->copy_end_found)
707                 if (FtlnEraseBlk(ftl, b))
708                     return -1;
709         }
710 
711         // Else this looks like a map block.
712         else {
713             // Check block's first map page for validity. Return -1 if error.
714             rc = map_page_check(ftl, pn, FALSE);
715             if (rc < 0)
716                 return -1;
717 
718             // If first page is invalid, whole block is invalid. Free it.
719             if (rc != NDM_PAGE_VALID) {
720                 ftl->bdata[b] = FREE_BLK_FLAG;
721                 ++ftl->num_free_blks;
722                 PfAssert(GET_RC(ftl->bdata[b]) != 100);
723                 SET_RC(ftl->bdata[b], 100);  // flag to use average wear count
724             }
725 
726             // Else this is a valid map page and block.
727             else {
728                 // Remember that volume is formatted. Mark block as map block.
729                 formatted = TRUE;
730                 SET_MAP_BLK(ftl->bdata[b]);  // clear used/read pages cnt
731 
732                 // Set block wear count lag.
733                 set_wc_lag(ftl, b, wc, &low_wc);
734 
735                 // If this is the highest block count so far, remember it.
736                 if (ftl->high_bc < bc) {
737                     ftl->high_bc = bc;
738                     ftl->high_bc_mblk = b;
739                 }
740 
741                 // Else if this is the second block with highest block count,
742                 // it's an interrupted map block transfer.
743                 else if (ftl->high_bc == bc && ftl->high_bc_mblk != (ui32)-1) {
744                     // Erase block that was destination of interrupted transfer.
745                     if (ftl->blk_wc_lag[b] > ftl->blk_wc_lag[ftl->high_bc_mblk]) {
746                         rc = FtlnEraseBlk(ftl, ftl->high_bc_mblk);
747                         ftl->high_bc_mblk = b;
748                     } else
749                         rc = FtlnEraseBlk(ftl, b);
750                     if (rc)
751                         return -1;
752                 }
753             }
754         }
755     }
756 
757     // If volume is unformatted, return FALSE.
758     if (formatted == FALSE)
759         return FALSE;
760 
761     // Compute the average 'high_wc' lag.
762     for (avg_lag = n = b = 0; b < ftl->num_blks; ++b)
763         if (GET_RC(ftl->bdata[b]) != 100) {
764             avg_lag += ftl->blk_wc_lag[b];
765             ++n;
766         }
767     if (n)
768         avg_lag = (avg_lag + n / 2) / n;
769 
770     // Apply average wear offset to every block marked as needing it.
771     for (b = 0; b < ftl->num_blks; ++b)
772         if ((ftl->bdata[b] & RC_MASK) == 100) {
773             ftl->bdata[b] &= ~RC_MASK;
774             ftl->blk_wc_lag[b] = avg_lag;
775         }
776 
777     // Depending when powerfail recovery was interrupted, at this point
778     // the volume block being resumed might look like a free block or a
779     // volume block. Need it to be a volume block.
780     if (ftl->copy_end_found) {
781         PfAssert(!IS_MAP_BLK(ftl->bdata[ftl->resume_vblk]));
782         if (IS_FREE(ftl->bdata[ftl->resume_vblk])) {
783             ftl->bdata[ftl->resume_vblk] = 0;
784             --ftl->num_free_blks;
785         }
786     }
787 
788     // Volume is formatted, return TRUE.
789     PfAssert(ftl->num_free_blks < ftl->num_blks);
790     return TRUE;
791 }
792 
793 #if INC_FAT_MBR
794 //    read_bpb: Read the FAT boot sector to set frst_data_sect
795 //
796 //       Input: ftl = pointer to FTL control block
797 //
798 //     Returns: 0 for success or no boot sector, -1 on error
799 //
read_bpb(FTLN ftl)800 static int read_bpb(FTLN ftl) {
801     ui32 pn;
802     FATPartition part;
803 
804     // Prepare to (potentially) write one map page.
805     if (FtlnRecCheck(ftl, -1))
806         return -1;
807 
808     // Retrieve physical page number for sector 0. Return -1 if error.
809     if (FtlnMapGetPpn(ftl, 0, &pn) < 0)
810         return -1;
811 
812     // Return 0 if boot sector is unmapped.
813     if (pn == (ui32)-1)
814         return 0;
815 
816     // Read sector 0. Return -1 if error.
817     if (FtlnRdPage(ftl, pn, ftl->main_buf))
818         return -1;
819 
820     // If one valid partition, use it to read location of boot sector.
821     if (FatGetPartitions(ftl->main_buf, &part, 1) == 1) {
822         ftl->vol_frst_sect = part.first_sect;
823         ftl->num_vsects -= ftl->vol_frst_sect;
824     }
825 
826     // Read boot sector into temporary buffer. Return -1 if error.
827     if (FtlnRdSects(ftl->main_buf, ftl->vol_frst_sect, 1, ftl))
828         return -1;
829 
830     // Extract frst_clust_sect from the boot information. Return status.
831     return FtlnSetClustSect1(ftl, ftl->main_buf, TRUE);
832 }
833 #endif // INC_FAT_MBR
834 
835 //   meta_read: Read FTL meta information page
836 //
837 //       Input: ftl = pointer to FTL control block
838 //
839 //     Returns: 0 if successful, else -1 for I/O error
840 //
meta_read(FTLN ftl)841 static int meta_read(FTLN ftl) {
842     ui32 pn = ftl->mpns[ftl->num_map_pgs - 1];
843 
844     // If no meta page, return 0.
845     if (pn >= ftl->num_pages)
846         return 0;
847 
848     // Read meta page. check/process its contents. Return -1 if error.
849     if (map_page_check(ftl, ftl->start_pn + pn, TRUE) < 0)
850         return -1;
851 
852     // Mark meta page invalid since no longer needed. Return success.
853     ftl->mpns[ftl->num_map_pgs - 1] = (ui32)-1;
854     return 0;
855 }
856 
857 // copy_end_mark: Write the copy-end page, marking completion of the
858 //                copy from the volume block to the temporary block
859 //
860 //      Inputs: ftl = pointer to FTL control block
861 //              b = block number of the vblk resume temporary block
862 //
copy_end_mark(CFTLN ftl,ui32 b)863 static int copy_end_mark(CFTLN ftl, ui32 b) {
864     ui32 pn = ftl->start_pn + b * ftl->pgs_per_blk + ftl->resume_po + 1;
865 
866     // Page data is number of volume block with lowest used page offset.
867     memset(ftl->main_buf, 0xFF, ftl->page_size);
868     WR32_LE(ftl->resume_vblk, &ftl->main_buf[0]);
869 
870     // Initialize spare area, including VPN and block/wear counts.
871     memset(ftl->spare_buf, 0xFF, ftl->eb_size);
872     SET_SA_VPN(COPY_BLK_END, ftl->spare_buf);
873     SET_SA_BC(COPY_BLK_END, ftl->spare_buf);
874     SET_SA_WC(0, ftl->spare_buf);
875 
876     // Write page that marks the end of a volume resume copy block.
877     return ftl->write_page(pn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
878 }
879 
880 // resume_copy: Copy one volume block
881 //
882 //      Inputs: ftl = pointer to FTL control block
883 //              src_b = number of block to copy from
884 //              dst_b = number of block to copy to
885 //              bc = block count value: 0xFFFFFFFF or COPY_BLK_MARK
886 //
887 //     Returns: 0 on success, -1 on error
888 //
resume_copy(FTLN ftl,ui32 src_b,ui32 dst_b,ui32 bc)889 static int resume_copy(FTLN ftl, ui32 src_b, ui32 dst_b, ui32 bc) {
890     int rc;
891     ui32 po, vpn;
892     ui32 src_pg0 = ftl->start_pn + src_b * ftl->pgs_per_blk;
893     ui32 dst_pg0 = ftl->start_pn + dst_b * ftl->pgs_per_blk;
894     ui32 wc = ftl->high_wc - ftl->blk_wc_lag[src_b];
895 
896     // Copy all used pages from selected volume block to free block.
897     for (po = 0; po <= ftl->resume_po; ++po) {
898         // Read source page's spare area.
899         ++ftl->stats.read_spare;
900         rc = ftl->read_spare(src_pg0 + po, ftl->spare_buf, ftl->ndm);
901 
902         // Return -1 if fatal error, skip page if ECC error on spare read.
903         if (rc) {
904             if (rc == -2)
905                 return FtlnFatErr(ftl);
906             else
907                 continue;
908         }
909 
910         // Get virtual page number from spare. Skip page if out of range.
911         vpn = GET_SA_VPN(ftl->spare_buf);
912         if (vpn > ftl->num_vpages)
913             continue;
914 
915         // Initialize spare area, including VPN and block/wear counts.
916         memset(ftl->spare_buf, 0xFF, ftl->eb_size);
917         SET_SA_VPN(vpn, ftl->spare_buf);
918         SET_SA_BC(bc, ftl->spare_buf);
919         SET_SA_WC(wc, ftl->spare_buf);
920 
921         // Invoke page transfer routine. If error, return -1.
922         ++ftl->stats.transfer_page;
923         if (ftl->xfer_page(src_pg0 + po, dst_pg0 + po, ftl->main_buf, ftl->spare_buf, ftl->ndm))
924             return FtlnFatErr(ftl);
925     }
926 
927     // Return success.
928     return 0;
929 }
930 
931 //   init_ftln: Prepare a TargetFTL-NDM volume for use
932 //
933 //       Input: ftl = pointer to FTL control block
934 //
935 //     Returns: 0 on success, -1 on failure
936 //
init_ftln(FTLN ftl)937 static int init_ftln(FTLN ftl) {
938     int formatted;
939     ui32 b, n;
940 
941     // Analyze volume to see if it is formatted. Return -1 if error.
942     formatted = format_status(ftl);
943     if (formatted < 0)
944         return -1;
945 
946     // If unformatted, blocks are free w/zero 'high_wc' lag.
947     if (formatted == FALSE) {
948         for (b = 0; b < ftl->num_blks; ++b) {
949             ftl->blk_wc_lag[b] = 0;
950             ftl->bdata[b] = FREE_BLK_FLAG;
951         }
952         ftl->num_free_blks = ftl->num_blks;
953         ftl->high_bc = 1;  // initial block count of unformatted volumes
954         return 0;
955     }
956 
957     // Look for all the valid map pages on all the map blocks.
958     if (build_map(ftl))
959         return -1;
960 
961     // If below limit, convert unused volume blocks to free blocks.
962     if (ftl->num_free_blks < FTLN_MIN_FREE_BLKS)
963         for (b = 0; b < ftl->num_blks; ++b) {
964             if (ftl->bdata[b] == 0)  // map/free flags clear and no use counts
965             {
966                 ftl->bdata[b] = FREE_BLK_FLAG;
967                 ++ftl->num_free_blks;
968             }
969         }
970 
971     // Read and process meta page, if any. Return -1 if error.
972     if (meta_read(ftl) < 0)
973         return -1;
974 
975     // Erase unused map blocks. Return -1 if error
976     for (b = 0; b < ftl->num_blks; ++b)
977         if (IS_MAP_BLK(ftl->bdata[b]) && (NUM_USED(ftl->bdata[b]) == 0))
978             if (FtlnEraseBlk(ftl, b))
979                 return -1;
980 
981     // If free block count is below reserved number, a recycle has been
982     // interrupted by a power failure. Must avoid losing additional free
983     // blocks from additional power failures. Resume restores the free
984     // map and volume page lists by copying valid entries to an erased
985     // block, ensuring they don't have undetectable corruption from an
986     // interrupted page write or block erase command. If resume is
987     // interrupted by a power failure, no free blocks are lost.
988     if (ftl->num_free_blks < FTLN_MIN_FREE_BLKS) {
989 #if DEBUG_RESUME
990         printf("Resuming: %u free blocks\n", ftl->num_free_blks);
991         printf("map block %u has used page offset of %u/%u\n", ftl->high_bc_mblk,
992                ftl->high_bc_mblk_po, ftl->pgs_per_blk);
993         printf("vol block %u has used page offset of %u/%u\n", ftl->resume_vblk, ftl->resume_po,
994                ftl->pgs_per_blk);
995 #endif
996 
997         // Resume needs one free block and should have it.
998         PfAssert(ftl->num_free_blks >= 1);
999         if (ftl->num_free_blks < 1)
1000             return -1;
1001 
1002         // Check if low page-offset volume block has unused pages.
1003         if (ftl->resume_po < ftl->pgs_per_blk - 1) {
1004 #ifdef FTL_RESUME_STRESS
1005             ++FtlVblkResumeCnt;
1006 #endif
1007 
1008             // Get the number of used pages on the volume block.
1009             n = NUM_USED(ftl->bdata[ftl->resume_vblk]);
1010 
1011             // If volume block transfer was interrupted, but the 'copy to'
1012             // finished, use the discovered 'copy to' block.
1013             if (ftl->copy_end_found) {
1014                 b = ftl->resume_tblk;
1015                 --ftl->num_free_blks;
1016             }
1017 
1018             // Else get a free block and copy the volume block to it.
1019             else {
1020                 // Find free block w/highest wear count. Error if none free.
1021                 b = FtlnHiWcFreeBlk(ftl);
1022                 if (b == (ui32)-1)
1023                     return b;
1024 
1025                 // If the block is unerased, erase it now. Return -1 if error.
1026                 if ((ftl->bdata[b] & ERASED_BLK_FLAG) == 0)
1027                     if (FtlnEraseBlk(ftl, b))
1028                         return (ui32)-1;
1029 
1030                 // Decrement free block count.
1031                 --ftl->num_free_blks;
1032 
1033                 // Copy used pages to temp block.
1034                 if (resume_copy(ftl, ftl->resume_vblk, b, COPY_BLK_MARK))
1035                     return -1;
1036 
1037                 // Write "end of copy" mark on next temp block page.
1038                 if (copy_end_mark(ftl, b))
1039                     return -1;
1040             }
1041 
1042             // Erase the volume block with the lowest used page-offset.
1043             if (FtlnEraseBlk(ftl, ftl->resume_vblk))
1044                 return -1;
1045 
1046             // Copy the temp block's contents back to the volume block.
1047             if (resume_copy(ftl, b, ftl->resume_vblk, 0xFFFFFFFF))
1048                 return -1;
1049 
1050             // Mark resumed block as a volume block with 'n' used pages.
1051             ftl->bdata[ftl->resume_vblk] = n << 20;  // clr free & erased flags
1052 
1053             // Erase the temp copy block.
1054             if (FtlnEraseBlk(ftl, b))
1055                 return -1;
1056 
1057             // Assign the resumed ftl->free_vpn value.
1058             ftl->free_vpn = ftl->resume_vblk * ftl->pgs_per_blk + ftl->resume_po + 1;
1059         }
1060 
1061         // Check if high-block-count map block has unused pages.
1062         if (ftl->high_bc_mblk_po < ftl->pgs_per_blk - 1) {
1063 #ifdef FTL_RESUME_STRESS
1064             ++FtlMblkResumeCnt;
1065 #endif
1066 
1067             // Find free block with lowest wear count. Error if none free.
1068             b = FtlnLoWcFreeBlk(ftl);
1069             if (b == (ui32)-1)
1070                 return b;
1071 
1072             // If the block is unerased, erase it now. Return -1 if error.
1073             if ((ftl->bdata[b] & ERASED_BLK_FLAG) == 0)
1074                 if (FtlnEraseBlk(ftl, b))
1075                     return (ui32)-1;
1076 
1077             // Decrement free block count.
1078             --ftl->num_free_blks;
1079 
1080             // Set free MPN pointer to first page in block (wo BC increment).
1081             ftl->free_mpn = b * ftl->pgs_per_blk;
1082 
1083             // Clear free block flag and read count, set map block flag.
1084             SET_MAP_BLK(ftl->bdata[b]);  // clr free flag & read wear count
1085 
1086             // Set wear count of copy to be one higher than source block.
1087             if (ftl->blk_wc_lag[ftl->high_bc_mblk])
1088                 ftl->blk_wc_lag[b] = ftl->blk_wc_lag[ftl->high_bc_mblk] - 1;
1089             else {
1090                 ftl->blk_wc_lag[ftl->high_bc_mblk] = 1;
1091                 ftl->blk_wc_lag[b] = 0;
1092             }
1093 
1094             // Copy the used pages to a free block, then erase the original.
1095             if (FtlnRecycleMapBlk(ftl, ftl->high_bc_mblk))
1096                 return -1;
1097         }
1098     }
1099 
1100 #if INC_FAT_MBR
1101     // For FAT volumes, read in boot sector, if it exists, to set
1102     // frst_clust_sect. Return -1 if error.
1103     if (FLAG_IS_SET(ftl->flags, FTLN_FAT_VOL))
1104         if (read_bpb(ftl))
1105             return -1;
1106 #endif
1107 
1108 #if FTLN_DEBUG > 1
1109     printf("init_ftln: FTL formatted - hi_bc = %u, hi_wc = %u\n", ftl->high_bc, ftl->high_wc);
1110 #endif
1111 
1112     // Do recycles if needed and return status.
1113     return FtlnRecCheck(ftl, 0);
1114 }
1115 
1116 //    free_ftl: Callback used inside FtlnFreeFTL()
1117 //
1118 //       Input: vol = FTL handle
1119 //
free_ftl(void * vol)1120 static void free_ftl(void* vol) {
1121     FTLN ftl = vol;
1122 
1123 #if FTLN_DEBUG > 1
1124     // Display FTL statistics.
1125     FtlnStats(ftl);
1126 #endif
1127 
1128     // Free FTL memory allocations.
1129     if (ftl->bdata)
1130         FsFree(ftl->bdata);
1131     if (ftl->blk_wc_lag)
1132         FsFree(ftl->blk_wc_lag);
1133     if (ftl->mpns)
1134         FsFree(ftl->mpns);
1135     if (ftl->main_buf)
1136         FsAfreeClear(&ftl->main_buf);
1137     if (ftl->map_cache)
1138         ftlmcDelete(&ftl->map_cache);
1139 #if INC_FTL_PAGE_CACHE
1140     if (ftl->vol_cache)
1141         ftlvcDelete(ftl->vol_cache);
1142 #endif
1143     FsFree(ftl);
1144 }
1145 
1146 // Global Function Definitions
1147 
1148 //  FtlnAddVol: Add a new TargetFTL-NDM volume
1149 //
1150 //      Inputs: ftl_dvr = pointer to FTL NDM driver control block
1151 //              ftl_type = FTL type (FTLN_XFS_VOL or FTLN_FAT_VOL)
1152 //              sect_size = FAT or XFS sector size
1153 //              fs_vol = pointer to FS driver's volume control block
1154 //
1155 //     Returns: Newly created FTL handle on success, NULL on error
1156 //
FtlnAddVol(FtlNdmVol * ftl_dvr,int ftl_type,int sect_size,void * fs_vol)1157 void* FtlnAddVol(FtlNdmVol* ftl_dvr, int ftl_type, int sect_size, void* fs_vol) {
1158     ui32 n, vol_blks;
1159     ui8* buf;
1160     FTLN ftl;
1161 
1162     // Ensure shared FTL vstat fields remain at same offset.
1163     PfAssert(offsetof(vstat_fat, garbage_level) == offsetof(vstat_xfs, garbage_level));
1164     PfAssert(offsetof(vstat_fat, ftl_type) == offsetof(vstat_xfs, ftl_type));
1165 
1166     // If number of blocks less than 7, FTL-NDM cannot work.
1167     if (ftl_dvr->num_blocks < 7) {
1168         FsError(EINVAL);
1169         return NULL;
1170     }
1171 
1172 #if CACHE_LINE_SIZE
1173     // Ensure driver page size is a multiple of the CPU cache line size.
1174     if (ftl_dvr->page_size % CACHE_LINE_SIZE) {
1175         FsError(EINVAL);
1176         return NULL;
1177     }
1178 #endif
1179 
1180     // Ensure physical page size is a multiple of FAT sector size and
1181     // not bigger than the device block size.
1182     if (ftl_dvr->page_size % FAT_SECT_SZ || ftl_dvr->page_size == 0 ||
1183         ftl_dvr->page_size > ftl_dvr->block_size) {
1184         FsError(EINVAL);
1185         return NULL;
1186     }
1187 
1188 #if OS_PARM_CHECK
1189     // Ensure FTL flags are valid.
1190     if (ftl_dvr->flags &
1191         ~(FSF_EXTRA_FREE
1192 #if INC_FTL_PAGE_CACHE
1193           | FSF_FTL_PAGE_CACHE
1194 #endif
1195           | FSF_READ_WEAR_LIMIT)) {
1196         FsError(EINVAL);
1197         return NULL;
1198     }
1199 #endif
1200 
1201     // Ensure driver has an NDM pointer.
1202     PfAssert(ftl_dvr->ndm);
1203 
1204     // Allocate memory for FTL control block. Return NULL if unable.
1205     ftl = FsCalloc(1, sizeof(struct ftln));
1206     if (ftl == NULL)
1207         return NULL;
1208 #if FTLN_DEBUG_PTR
1209     Ftln = ftl;
1210 #endif
1211 
1212     // Set callback to free FTL resources from generic FTL NDM layer.
1213     ftl->free_ftl = free_ftl;
1214 
1215     // Acquire exclusive access to upper file system.
1216     semPend(FileSysSem, WAIT_FOREVER);
1217 
1218     // Add volume to list of FTL volumes.
1219     CIRC_LIST_APPEND(&ftl->link, &FtlnVols);
1220 
1221     // Set all FTL driver dependent variables.
1222     ftl->page_size = ftl_dvr->page_size;
1223     ftl->eb_size = ftl_dvr->eb_size;
1224     ftl->block_size = ftl_dvr->block_size;
1225     ftl->num_blks = ftl_dvr->num_blocks;
1226     ftl->start_pn = ftl_dvr->start_page;
1227     ftl->ndm = ftl_dvr->ndm;
1228     ftl->type = ftl_dvr->type;
1229     SET_FLAG(ftl->flags, ftl_type);
1230     ftl->sect_size = sect_size;
1231 
1232     // Derive other driver dependent variables.
1233     ftl->pgs_per_blk = ftl->block_size / ftl->page_size;
1234     if (ftl->pgs_per_blk > PGS_PER_BLK_MAX) {
1235         FsError(EINVAL);
1236         semPostBin(FileSysSem);
1237         goto FtlnAddV_err;
1238     }
1239     ftl->num_pages = ftl->pgs_per_blk * ftl->num_blks;
1240 #if !FTLN_LEGACY
1241 #if FTLN_3B_PN
1242     if (ftl->num_pages > 0x1000000) {
1243         FsError(EFBIG);
1244         semPostBin(FileSysSem);
1245         goto FtlnAddV_err;
1246     }
1247 #endif
1248 #endif // !FTLN_LEGACY
1249     ftl->sects_per_page = ftl->page_size / ftl->sect_size;
1250 
1251     // Release file system exclusive access.
1252     semPostBin(FileSysSem);
1253 
1254     // Copy the driver callback functions.
1255     ftl->write_page = ftl_dvr->write_data_and_spare;
1256     ftl->write_pages = ftl_dvr->write_pages;
1257     ftl->read_spare = ftl_dvr->read_spare;
1258     ftl->read_pages = ftl_dvr->read_pages;
1259     ftl->page_check = ftl_dvr->page_check;
1260     ftl->xfer_page = ftl_dvr->transfer_page;
1261     ftl->erase_block = ftl_dvr->erase_block;
1262 #if INC_FTL_NDM_MLC
1263     ftl->pair_offset = ftl_dvr->pair_offset;
1264 #endif
1265 
1266     // Compute how many volume pages are mapped by a single map page.
1267     ftl->mappings_per_mpg = ftl->page_size / FTLN_PN_SZ;
1268 
1269 #if !FTLN_LEGACY
1270     // Determine largest possible number of volume blocks.
1271     for (vol_blks = ftl->num_blks - FTLN_MIN_FREE_BLKS - 1;; --vol_blks) {
1272         // Determine number of map pages for given number of vol blocks.
1273         n = (vol_blks * ftl->pgs_per_blk + ftl->mappings_per_mpg - 1) / ftl->mappings_per_mpg;
1274         n = n + 1;  // plus one for metapage
1275 
1276         // Convert to number of map blocks.
1277         n = (n * ftl->page_size + ftl->block_size - 1) / ftl->block_size;
1278 
1279 #if INC_FTL_NDM_MLC
1280         // If MLC, double the required number of map blocks because only
1281         // half their pages are used.
1282         if (ftl->type == NDM_MLC)
1283             n *= 2;
1284 #endif
1285 
1286         // Break if this number of volume blocks fits into the partition.
1287         if (vol_blks + n + FTLN_MIN_FREE_BLKS <= ftl->num_blks)
1288             break;
1289     }
1290 
1291 #if INC_FTL_NDM_MLC
1292     // If MLC, remove another 5% of volume space to account for having
1293     // to advance the free volume pointer to skip possible page pair
1294     // corruption anytime FTL metadata is synched to flash.
1295     if (ftl->type == NDM_MLC)
1296         vol_blks = 95 * vol_blks / 100;
1297 #endif
1298 
1299 #else // FTLN_LEGACY
1300 
1301 // Compute the number of volume blocks using:
1302 //                 VB = TB - MB - 4        (1)
1303 // where VB = # volume blocks, TB = # total blocks, MB = # map blocks
1304 // and:
1305 //            MB = (4 * VB * PB + 1) / BS  (2)
1306 // where PB = # pages per block and BS = block size in bytes
1307 // Combining (1) and (2) yields (with rounding):
1308 //   VB = TB - (4 * (TP + PB) + 6 * BS - 2) / (BS + 4 * PB)
1309 // For MLC devices, double the number of map blocks in computation,
1310 // because we only use half the pages in MLC map blocks.
1311 #if INC_FTL_NDM_MLC && (INC_FTL_NDM_SLC || INC_FTL_NOR_WR1)
1312     if (ftl->type == NDM_SLC)
1313 #endif
1314 #if INC_FTL_NDM_SLC || INC_FTL_NOR_WR1
1315     {
1316         vol_blks = ftl->num_blks -
1317                    (4 * (ftl->num_pages + ftl->pgs_per_blk) + 6 * ftl->block_size - 2) /
1318                        (ftl->block_size + 4 * ftl->pgs_per_blk);
1319     }
1320 #endif
1321 #if INC_FTL_NDM_MLC && (INC_FTL_NDM_SLC || INC_FTL_NOR_WR1)
1322     else
1323 #endif
1324 #if INC_FTL_NDM_MLC
1325     {
1326         vol_blks = ftl->num_blks -
1327                    (8 * (ftl->num_pages + ftl->pgs_per_blk) + 5 * ftl->block_size + 1) /
1328                        (ftl->block_size + 8 * ftl->pgs_per_blk);
1329 
1330         // Remove another 5% from volume space to account for the fact
1331         // that the free volume pointer must be advanced to skip page
1332         // pair corruption anytime the FTL meta information is flushed.
1333         vol_blks = 95 * vol_blks / 100;
1334     }
1335 #endif
1336 #endif // FTLN_LEGACY
1337 
1338     // Compute number of volume pages and subtract extra free percentage.
1339     // If driver specifies an acceptable amount, use it. Otherwise, use
1340     // 2%. Increasing number of map pages makes recycles more efficient
1341     // because the ratio of used to dirty pages is lower in map blocks.
1342     ftl->num_vpages = vol_blks * ftl->pgs_per_blk;
1343     n = ftl_dvr->extra_free;
1344     if (FLAG_IS_CLR(ftl_dvr->flags, FSF_EXTRA_FREE) || n < 2 || n > 50)
1345         n = 2;
1346     n = (n * ftl->num_vpages) / 100;
1347     if (n == 0)
1348         n = 1;
1349     ftl->num_vpages -= n;
1350 
1351 #if INC_FTL_NDM_MLC
1352     // For MLC devices, account for the fact that the last recycled
1353     // volume block cannot be fully used. To be safe, assume worst case
1354     // scenario for max pair offset - half a block.
1355     if (ftl->type == NDM_MLC)
1356         ftl->num_vpages -= ftl->pgs_per_blk / 2;
1357 #endif
1358 
1359     // Compute number of map pages based on number of volume pages.
1360     ftl->num_map_pgs = 1 + (ftl->num_vpages + ftl->mappings_per_mpg - 1) / ftl->mappings_per_mpg;
1361     PfAssert(ftl->num_vpages / ftl->mappings_per_mpg < ftl->num_map_pgs);
1362 
1363 #if FTLN_DEBUG > 1
1364     printf("\nVOL PAGES = %u, FTL PAGES = %u, %u%% usage\n", ftl->num_vpages, ftl->num_pages,
1365            (ftl->num_vpages * 100) / ftl->num_pages);
1366 #endif
1367 
1368     // Set the number of sectors based on the number of pages.
1369     ftl->num_vsects = ftl->num_vpages * ftl->sects_per_page;
1370 
1371 // Allocate one or two main data pages and spare buffers. Max spare
1372 // use is one block worth of spare areas for multi-page writes.
1373 #if INC_SECT_FTL
1374     n = 2 * ftl->page_size + ftl->eb_size * ftl->pgs_per_blk;
1375 #else
1376     n = 1 * ftl->page_size + ftl->eb_size * ftl->pgs_per_blk;
1377 #endif
1378     buf = FsAalloc(n);
1379     if (buf == NULL)
1380         goto FtlnAddV_err;
1381     ftl->main_buf = buf;
1382     buf += ftl->page_size;
1383 #if INC_SECT_FTL
1384     ftl->swap_page = buf;
1385     buf += ftl->page_size;
1386 #endif
1387     ftl->spare_buf = buf;
1388 
1389     // Allocate memory for the block data and wear count lag arrays.
1390     ftl->bdata = FsCalloc(ftl->num_blks, sizeof(ui32));
1391     if (ftl->bdata == NULL)
1392         goto FtlnAddV_err;
1393     ftl->blk_wc_lag = FsCalloc(ftl->num_blks, sizeof(ui8));
1394     if (ftl->blk_wc_lag == NULL)
1395         goto FtlnAddV_err;
1396     ftl->high_wc = 0;
1397 
1398     // Allocate memory for map pages array (holds physical page numbers).
1399     ftl->mpns = FsMalloc(ftl->num_map_pgs * sizeof(ui32));
1400     if (ftl->mpns == NULL)
1401         goto FtlnAddV_err;
1402 
1403 // For SLC devices, adjust driver cached MPNs if too big or zero.
1404 #if INC_FTL_NDM_MLC && (INC_FTL_NDM_SLC || INC_FTL_NOR_WR1)
1405     if (ftl->type == NDM_SLC)
1406 #endif
1407 #if INC_FTL_NDM_SLC || INC_FTL_NOR_WR1
1408     {
1409         if (ftl->num_map_pgs < ftl_dvr->cached_map_pages || ftl_dvr->cached_map_pages == 0)
1410             ftl_dvr->cached_map_pages = ftl->num_map_pgs;
1411     }
1412 #endif
1413 
1414 // For MLC devices, cache all map pages so that no map write occurs
1415 // due to cache preemption.
1416 #if INC_FTL_NDM_MLC && (INC_FTL_NDM_SLC || INC_FTL_NOR_WR1)
1417     else
1418 #endif
1419 #if INC_NDM_MLC
1420         ftl_dvr->cached_map_pages = ftl->num_map_pgs;
1421 #endif
1422 
1423     // Allocate map page cache for new volume.
1424     ftl->map_cache = ftlmcNew(ftl, ftl_dvr->cached_map_pages, FtlnMapWr, FtlnMapRd, ftl->page_size);
1425     if (ftl->map_cache == NULL)
1426         goto FtlnAddV_err;
1427 
1428 #if INC_FTL_PAGE_CACHE
1429     // If FAT volume and driver requests it, allocate volume page cache.
1430     if (FLAG_IS_SET(ftl->flags, FTLN_FAT_VOL) && FLAG_IS_SET(ftl_dvr->flags, FSF_FTL_PAGE_CACHE)) {
1431         ftl->vol_cache =
1432             ftlvcNew(ftl, ftl_dvr->cached_vol_pages, FtlnVpnWr, FtlnVpnRd, ftl->page_size);
1433         if (ftl->vol_cache == NULL)
1434             goto FtlnAddV_err;
1435     }
1436 #endif
1437 
1438     // Set block read wear limit and mark no block at limit.
1439     if (FLAG_IS_SET(ftl_dvr->flags, FSF_READ_WEAR_LIMIT))
1440         ftl->max_rc = ftl_dvr->read_wear_limit;
1441 #if INC_FTL_NDM_MLC
1442     else
1443 #if INC_FTL_NDM_SLC || INC_FTL_NOR_WR1
1444         if (ftl_dvr->type == NDM_MLC)
1445 #endif
1446         ftl->max_rc = MLC_NAND_RC_LIMIT;
1447 #endif
1448 #if INC_FTL_NDM_SLC
1449     else
1450 #if INC_FTL_NOR_WR1
1451         if (ftl_dvr->type == NDM_SLC)
1452 #endif
1453         ftl->max_rc = SLC_NAND_RC_LIMIT;
1454 #endif
1455 #if INC_FTL_NOR_WR1
1456     else
1457         ftl->max_rc = NOR_RC_LIMIT;
1458 #endif
1459     if (ftl->max_rc > RC_MASK) {
1460         FsError(EINVAL);
1461         goto FtlnAddV_err;
1462     }
1463 
1464     // Initialize volume state.
1465     FtlnStateRst(ftl);
1466 
1467     // Initialize the NAND FTL.
1468     if (init_ftln(ftl))
1469         goto FtlnAddV_err;
1470 
1471 #if FTLN_DEBUG > 1
1472     // Display FTL statistics.
1473     FtlnStats(ftl);
1474 #endif
1475 
1476 #if INC_SECT_FTL
1477     // If FAT volume, prepare for registration with TargetFAT.
1478     if (ftl_type == FTLN_FAT_VOL) {
1479         FatVol* fat = (FatVol*)fs_vol;
1480 
1481 // Initialize TargetFAT driver structure.
1482 #if INC_FAT_MBR
1483         fat->start_sect = ftl->vol_frst_sect;
1484         fat->num_sects = ftl->num_vsects - ftl->vol_frst_sect;
1485 #else
1486         fat->start_sect = 0;
1487         fat->num_sects = ftl->num_vsects;
1488 #endif
1489         fat->vol = ftl;
1490         fat->write_sectors = FtlnWrSects;
1491         fat->read_sectors = FtlnRdSects;
1492         fat->report = FtlnReport;
1493         fat->flags |= FSF_BLUNK_FTL;
1494 
1495 #if FAT_DEBUG
1496         printf("\nFAT STATS:\n");
1497         printf("  - start_sect      = %u\n", fat->start_sect);
1498         printf("  - num_sects       = %u\n", fat->num_sects);
1499         printf("  - min_clust_size  = %u\n", fat->min_clust_size);
1500         printf("  - flags           = 0x%X\n\n", fat->flags);
1501 #endif
1502 
1503         // Standard FTL-FAT settings.
1504         fat->fixed = TRUE;
1505         fat->num_heads = FAT_NUM_HEADS;
1506         fat->sects_per_trk = FAT_SECTS_PER_TRACK;
1507 
1508         // Save volume name.
1509         strcpy(ftl->vol_name, fat->name);
1510     }
1511 #endif // INC_SECT_FTL
1512 
1513 #if INC_PAGE_FTL
1514     // If XFS volume, prepare for registration with TargetXFS.
1515     if (ftl_type == FTLN_XFS_VOL) {
1516         XfsVol* xfs = (XfsVol*)fs_vol;
1517 
1518         // Initialize TargetXFS driver structure with FTL specific fields.
1519         xfs->start_page = 0;
1520         xfs->num_pages = ftl->num_vsects;
1521         xfs->page_size = ftl->page_size;
1522         xfs->vol = ftl;
1523         xfs->write_pages = FtlnWrSects;
1524         xfs->read_pages = FtlnRdSects;
1525         xfs->report = FtlnReport;
1526 
1527 #if FTLN_DEBUG > 1
1528         printf("\nXFS STATS:\n");
1529         printf("  - start_page      = %u\n", xfs->start_page);
1530         printf("  - num_pages       = %u\n\n", xfs->num_pages);
1531 #endif
1532 
1533         // Save volume name.
1534         strcpy(ftl->vol_name, xfs->name);
1535     }
1536 #endif // INC_PAGE_FTL
1537 
1538     // Return pointer to FTL control block.
1539     return ftl;
1540 
1541 // Error exit.
1542 FtlnAddV_err:
1543     semPend(FileSysSem, WAIT_FOREVER);
1544     CIRC_NODE_REMOVE(&ftl->link);
1545     free_ftl(ftl);
1546     semPostBin(FileSysSem);
1547     return NULL;
1548 }
1549 
1550 #if INC_SECT_FTL
1551 // FtlNdmAddFatFTL: Create a new TargetFTL-NDM FAT FTL
1552 //
1553 //      Inputs: ftl_dvr = pointer to FTL NDM driver control block
1554 //              fat = pointer to FAT pseudo driver control block
1555 //
1556 //     Returns: Pointer to FTL control block on success, else NULL
1557 //
FtlNdmAddFatFTL(FtlNdmVol * ftl_dvr,FatVol * fat)1558 void* FtlNdmAddFatFTL(FtlNdmVol* ftl_dvr, FatVol* fat) {
1559     // Determine sector size.
1560     fat->sect_size = FatGetSectSize(fat);
1561     if (fat->sect_size == 0)
1562         return NULL;
1563 
1564     // Create new FTL and return FTL handle.
1565     return FtlnAddVol(ftl_dvr, FTLN_FAT_VOL, fat->sect_size, fat);
1566 } //lint !e818
1567 #endif // INC_SECT_FTL
1568 
1569 #if INC_PAGE_FTL
1570 // FtlNdmAddXfsFTL: Create a new TargetFTL-NDM XFS FTL
1571 //
1572 //      Inputs: ftl_dvr = pointer to FTL NDM driver control block
1573 //              xfs = pointer to XFS pseudo driver control block
1574 //
1575 //     Returns: -1 if error, 0 for success
1576 //
FtlNdmAddXfsFTL(FtlNdmVol * ftl_dvr,XfsVol * xfs)1577 void* FtlNdmAddXfsFTL(FtlNdmVol* ftl_dvr, XfsVol* xfs) {
1578     // Create new FTL and return FTL handle.
1579     return FtlnAddVol(ftl_dvr, FTLN_XFS_VOL, ftl_dvr->page_size, xfs);
1580 } //lint !e818
1581 #endif // INC_PAGE_FTL
1582 
1583 // FtlnFreeFTL: Free an existing FTL volume
1584 //
1585 //       Input: handle = FTL handle
1586 //
FtlnFreeFTL(void * handle)1587 void FtlnFreeFTL(void* handle) {
1588     FTLN ftln = handle;
1589 
1590     // Acquire exclusive access to upper file system.
1591     semPend(FileSysSem, WAIT_FOREVER);
1592 
1593     // Remove FTL from list and free its memory.
1594     CIRC_NODE_REMOVE(&ftln->link);
1595     ftln->free_ftl(ftln);
1596 
1597     // Release exclusive access to upper file system.
1598     semPostBin(FileSysSem);
1599 }
1600 
1601 //  FtlnDelVol: Delete an existing FTL NDM volume (both FTL and FS)
1602 //
1603 //       Input: ftl = pointer to FTL control block
1604 //
1605 //        Note: Called with exclusive file system access held
1606 //
1607 //     Returns: 0 if success, -1 if error
1608 //
FtlnDelVol(FTLN ftl)1609 int FtlnDelVol(FTLN ftl) {
1610     // Remove volume from list of volumes.
1611     CIRC_NODE_REMOVE(&ftl->link);
1612 
1613     // Release file system exclusive access.
1614     semPostBin(FileSysSem);
1615 
1616 // Delete file system volume.
1617 #if INC_PAGE_FTL && INC_SECT_FTL
1618     if (FLAG_IS_SET(ftl->flags, FTLN_XFS_VOL))
1619 #endif
1620 #if INC_PAGE_FTL
1621         (void)XfsDelVol(ftl->vol_name);
1622 #endif
1623 #if INC_PAGE_FTL && INC_SECT_FTL
1624     else
1625 #endif
1626 #if INC_SECT_FTL
1627         (void)FatDelVol(ftl->vol_name);
1628 #endif
1629 
1630     // Acquire exclusive access to upper file system.
1631     semPend(FileSysSem, WAIT_FOREVER);
1632 
1633     // Delete FTL and return success.
1634     ftl->free_ftl(ftl);
1635     return 0;
1636 }
1637 
1638 // FtlNdmDelVol: Delete an existing FTL NDM volume
1639 //
1640 //       Input: name = name of volume to delete
1641 //
1642 //     Returns: 0 on success, -1 on failure
1643 //
1644 //        Note: Called with FileSysSem semaphore held
1645 //
FtlNdmDelVol(const char * name)1646 int FtlNdmDelVol(const char* name) {
1647     CircLink* circ;
1648 
1649     // Acquire global file system semaphore.
1650     semPend(FileSysSem, WAIT_FOREVER);
1651 
1652     // Search all TargetFTL-NDM volume for name match.
1653     for (circ = CIRC_LIST_HEAD(&FtlnVols);; circ = circ->next_bck) {
1654         FTLN ftln;
1655 
1656         // Stop when end of list is reached.
1657         if (CIRC_LIST_AT_END(circ, &FtlnVols)) {
1658             // Release file system exclusive access.
1659             semPostBin(FileSysSem);
1660 
1661             // Volume not found, assign errno and return -1.
1662             return FsError(ENOENT);
1663         }
1664 
1665         // If volume found, delete it and return success.
1666         ftln = (FTLN)circ;
1667         if (FNameEqu(ftln->vol_name, name)) {
1668             int rc;
1669 
1670             // Delete this TargetFTL-NDM volume.
1671             rc = FtlnDelVol(ftln);
1672 
1673             // Release file system exclusive access and return status.
1674             semPostBin(FileSysSem);
1675             return rc;
1676         }
1677     }
1678 }
1679 #endif // INC_FTL_NDM
1680