| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ftlnp.h" |
| |
| // Type Definitions |
| typedef struct { |
| ui32 ppn0; // first physical page number if run_cnt != 0 |
| ui32 run_cnt; // number of staged page reads |
| ui8* buf; // pointer to output buffer |
| } StagedRd; |
| |
| // Local Function Definitions |
| |
| // flush_pending_reads: Read all pages that are pending |
| // |
| // Inputs: ftl = pointer to FTL control block |
| // staged = pointer to structure holding 1st page number, |
| // page count, and output buffer pointer |
| // |
| // Returns: 0 on success, -1 on error |
| // |
| static int flush_pending_reads(FTLN ftl, StagedRd* staged) { |
| int status; |
| ui32* b_ptr; |
| |
| // Issue pending reads. |
| ftl->stats.read_page += staged->run_cnt; |
| status = ndmReadPages(ftl->start_pn + staged->ppn0, staged->run_cnt, staged->buf, ftl->spare_buf, |
| ftl->ndm); |
| |
| // Adjust data buffer pointer. |
| staged->buf += staged->run_cnt * ftl->page_size; |
| |
| // Get handle on blocks[] entry and increment block wear count. |
| b_ptr = &ftl->bdata[staged->ppn0 / ftl->pgs_per_blk]; |
| INC_RC(ftl, b_ptr, staged->run_cnt); |
| |
| // Check if error was reported. |
| if (status) { |
| // If block needs to be recycled, set block read count to its max. |
| if (status == 1) { |
| SET_MAX_RC(ftl, b_ptr); |
| status = 0; |
| } |
| |
| // Else if fatal error, set errno and fatal I/O flag, return -1. |
| else if (status == -2) |
| return FtlnFatErr(ftl); |
| } |
| |
| // Reset pending sequence and return status. |
| staged->run_cnt = 0; |
| return status; |
| } |
| |
| // Global Function Definitions |
| |
| // FtlnRdPages: Read count worth of virtual pages from FTL |
| // |
| // Inputs: buf = pointer to where data is copied to |
| // vpn = first volume page to read |
| // count = number of consecutive pages to read |
| // vol = pointer to FTL control block |
| // |
| // Returns: 0 on success, -1 on error |
| // |
| int FtlnRdPages(void* buf, ui32 vpn, int count, void* vol) { |
| FTLN ftl = vol; |
| StagedRd staged; |
| ui32 ppn; |
| |
| // Ensure request is within volume's range of provided pages. |
| if (vpn + count > ftl->num_vpages) { |
| ftl->logger.error(__FILE__, __LINE__, "FTL Read failed. Attempting to read page %u is out of range(max %u).", |
| vpn + count - 1, ftl->num_pages - 1); |
| return FsError2(FTL_ASSERT, ENOSPC); |
| } |
| |
| // If no pages to read, return success. |
| if (count == 0) |
| return 0; |
| |
| // If there's at least a block with a maximum read count, recycle. |
| if (ftl->max_rc_blk != (ui32)-1) |
| if (FtlnRecCheck(ftl, 0)) { |
| ftl->logger.error(__FILE__, __LINE__, "FTL read recycle failed for page %u."); |
| return -1; |
| } |
| |
| // Set errno and return -1 if fatal I/O error occurred. |
| if (ftl->flags & FTLN_FATAL_ERR) |
| return FsError2(NDM_EIO, EIO); |
| |
| // Initialize structure for staging deferred consecutive page reads. |
| staged.buf = buf; |
| staged.run_cnt = 0; |
| |
| // Loop to read whole pages. |
| do { |
| // Check if reads are staged and PPN lookup could cause recycle. |
| if (staged.run_cnt) { |
| // If next PPN lookup could cause recycle, flush saved PPNs. |
| if (FtlnRecNeeded(ftl, -1)) { |
| if (flush_pending_reads(ftl, &staged)) |
| return -1; |
| } |
| |
| #if FS_ASSERT |
| // Else confirm no physical page number changes due to recycle. |
| else |
| ftl->assert_no_recycle = TRUE; |
| #endif |
| } |
| |
| // Prepare to potentially write one map page. Return -1 if error. |
| if (FtlnRecCheck(ftl, -1)) { |
| ftl->logger.error(__FILE__, __LINE__, "Failed to obtain free pages through block recycling."); |
| return -1; |
| } |
| |
| // Convert the virtual page number to its physical page number. |
| if (FtlnMapGetPpn(ftl, vpn, &ppn) < 0) { |
| ftl->logger.error(__FILE__, __LINE__, "Failed to obtain map physical page number."); |
| return -1; |
| } |
| |
| #if FS_ASSERT |
| // End check for no physical page number changes. |
| ftl->assert_no_recycle = FALSE; |
| #endif |
| |
| // Check if page is unmapped. |
| if (ppn == (ui32)-1) { |
| // Flush pending reads if any. |
| if (staged.run_cnt) |
| if (flush_pending_reads(ftl, &staged)) |
| return -1; |
| |
| // Fill page with the value for unwritten data and |
| // advance buffer pointer. |
| memset(staged.buf, 0xFF, ftl->page_size); |
| staged.buf += ftl->page_size; |
| } |
| |
| // Else have valid mapped page number. |
| else { |
| // If next in sequence and in same block, add page to list. |
| if ((staged.ppn0 + staged.run_cnt == ppn) && |
| (staged.ppn0 / ftl->pgs_per_blk == ppn / ftl->pgs_per_blk)) |
| ++staged.run_cnt; |
| |
| // Else flush pending reads, if any, and start new list. |
| else { |
| if (staged.run_cnt) |
| if (flush_pending_reads(ftl, &staged)) |
| return -1; |
| staged.ppn0 = ppn; |
| staged.run_cnt = 1; |
| } |
| } |
| |
| // Adjust virtual page number and count. |
| ++vpn; |
| } while (--count > 0); |
| |
| // Flush pending reads if any. |
| if (staged.run_cnt) |
| if (flush_pending_reads(ftl, &staged)) |
| return -1; |
| |
| // Return success. |
| return 0; |
| } |
| |
| // FtlnRdPage: Read one physical page from flash |
| // |
| // Inputs: ftl = pointer to FTL control block |
| // ppn = physical page number of page to read from flash |
| // rd_buf = buffer to hold read contents |
| // |
| // Returns: 0 on success, -1 on error |
| // |
| int FtlnRdPage(FTLN ftl, ui32 ppn, void* rd_buf) { |
| int status; |
| ui32* b_ptr; |
| |
| // Set errno and return -1 if fatal I/O error occurred. |
| if (ftl->flags & FTLN_FATAL_ERR) |
| return FsError2(NDM_EIO, EIO); |
| |
| // Read page from flash. If error, set errno/fatal flag/return -1. |
| ++ftl->stats.read_page; |
| status = ndmReadPages(ftl->start_pn + ppn, 1, rd_buf, ftl->spare_buf, ftl->ndm); |
| if (status < 0) |
| return FtlnFatErr(ftl); |
| |
| // Get handle on block entry in blocks[]. |
| b_ptr = &ftl->bdata[ppn / ftl->pgs_per_blk]; |
| |
| // If recycle requested, set read count to max. Else increment it. |
| if (status) |
| SET_MAX_RC(ftl, b_ptr); |
| else |
| INC_RC(ftl, b_ptr, 1); |
| |
| // Return success. |
| return 0; |
| } |