blob: a2434f987a20947c47da37a85e610a66b5184696 [file] [log] [blame]
// 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;
}