blob: b36c8bedb7cf35bac1b750a321f012e53b707b4a [file] [log] [blame]
// Copyright 2019 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 "ftl.h"
#include <limits.h>
#include <stdio.h>
#include <zircon/assert.h>
#include <algorithm>
#include <array>
#include <new>
#include <fbl/vector.h>
#include "ftl_internal.h"
namespace {
using internal::SpareArea;
int GetWearCount(const NandBroker& nand, uint32_t block, int page_multiplier) {
if (nand.ftl() && nand.ftl()->IsBadBlock(block)) {
return -1;
}
if (!nand.ReadPages(block * nand.Info().pages_per_block, page_multiplier)) {
printf("Read failed for block %u\n", block);
return -1;
}
SpareArea* oob = reinterpret_cast<SpareArea*>(nand.oob());
return internal::IsFtlBlock(*oob) ? internal::DecodeWear(*oob) : -1;
}
class FtlData final : public FtlInfo {
public:
explicit FtlData(const NandBroker* nand) : nand_(nand) {}
~FtlData() final {}
bool Initialize();
const internal::NdmData& ndm() const { return ndm_; }
// FtlInfo interface:
void DumpInfo() const final { return ndm_.DumpInfo(); }
bool IsBadBlock(uint32_t block) const final { return ndm_.IsBadBlock(block); }
uint32_t LastFtlBlock() const final { return ndm_.LastFtlBlock(); }
bool IsMapPage(uint32_t page) const final;
private:
const NandBroker* nand_;
internal::NdmData ndm_;
};
bool FtlData::Initialize() { return ndm_.FindHeader(*nand_); }
bool FtlData::IsMapPage(uint32_t page) const {
page /= ndm_.page_multiplier();
const SpareArea* oob = reinterpret_cast<const SpareArea*>(nand_->oob());
ZX_DEBUG_ASSERT(nand_->Info().oob_size <= sizeof(*oob));
return IsMapBlock(oob[page]);
}
} // namespace
// static
std::unique_ptr<FtlInfo> FtlInfo::Factory(const NandBroker* nand) {
auto ftl = std::make_unique<FtlData>(nand);
if (!ftl->Initialize()) {
return nullptr;
}
return ftl;
}
bool WearCounts(const NandBroker& nand) {
uint32_t num_blocks = nand.Info().num_blocks;
int page_multiplier = 2;
if (nand.ftl()) {
const FtlData* ftl = reinterpret_cast<const FtlData*>(nand.ftl());
num_blocks = ftl->LastFtlBlock();
page_multiplier = ftl->ndm().page_multiplier();
}
int min = INT_MAX;
int max = 0;
int sum = 0;
int count = 0;
// Build a wear count histogram. The expected size is 255 so the first wear
// count can be either 255 below the max or 255 above the min; in other words,
// 512 buckets should be enough. 1000 provides a reasonable extra range.
std::array<int, 1000> histogram = {};
int offset = -1;
for (uint32_t block = 0; block < num_blocks; block++) {
int value = GetWearCount(nand, block, page_multiplier);
if (value > 0) {
min = std::min(min, value);
max = std::max(max, value);
sum += value;
count++;
if (offset < 0) {
// Place the first found count at the center of the range.
offset = std::max(0, value - 500);
}
int bucket = value - offset;
if (bucket < 0 || bucket > 999) {
printf("Out of range for histogram: %d (block %u) vs start: %d\n", value, block, offset);
continue;
}
histogram[bucket] += 1;
}
}
if (count) {
sum /= count;
printf("Wear counts: min %d, max %d, delta %d, average %d, count %d\n", min, max, max - min,
sum, count);
for (int i = min; i <= max; i++) {
int bucket = i - offset;
if (bucket >= 0 && bucket < 1000) {
printf("[%06d] %d\n", i, histogram[bucket]);
}
}
} else {
printf("No wear count found\n");
}
return true;
}