blob: d19980f16475a0ef59254bbd06812f7f463a8fa0 [file] [log] [blame]
// Copyright 2022 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 "src/storage/minfs/minfs_inspect_tree.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/clock.h>
#include <safemath/checked_math.h>
namespace minfs {
fs_inspect::UsageData CalculateSpaceUsage(const Superblock& superblock, uint64_t reserved_blocks) {
return {
.total_bytes = static_cast<uint64_t>(superblock.block_count) * superblock.block_size,
.used_bytes = (superblock.alloc_block_count + reserved_blocks) * superblock.block_size,
.total_nodes = superblock.inode_count,
.used_nodes = superblock.alloc_inode_count,
};
}
MinfsInspectTree::MinfsInspectTree(async_dispatcher_t* dispatcher,
const block_client::BlockDevice* device)
: device_(device),
component_inspector_(inspect::ComponentInspector(
dispatcher,
inspect::PublishOptions{
.tree_handler_settings =
inspect::TreeHandlerSettings{
// Specify to fall back to DeepCopy mode instead of Live mode (the default
// on failures to send a Frozen copy of the tree (e.g. if we could not
// create a child copy of the backing VMO). This helps prevent any issues
// with querying the inspect tree while the filesystem is under load,
// since snapshots at the receiving end must be consistent.
// See https://fxbug.dev/42135165 for details.
.snapshot_behavior = inspect::TreeServerSendPreference::Frozen(
inspect::TreeServerSendPreference::Type::DeepCopy)},
})),
opstats_node_(component_inspector_.root().CreateChild("fs.opstats")),
node_operations_(opstats_node_) {
ZX_ASSERT(device_);
component_inspector_.inspector().CreateStatsNode();
}
void MinfsInspectTree::Initialize(const fs::FilesystemInfo& fs_info, const Superblock& superblock,
uint64_t reserved_blocks) {
// Set initial data for the fs.info and fs.usage nodes.
{
std::lock_guard guard(info_mutex_);
info_ = {
.id = fs_info.fs_id,
.type = fidl::ToUnderlying(fs_info.fs_type),
.name = fs_info.name,
.version_major = kMinfsCurrentMajorVersion,
.version_minor = kMinfsCurrentMinorVersion,
.block_size = fs_info.block_size,
.max_filename_length = fs_info.max_filename_size,
.oldest_version = fs_inspect::InfoData::OldestVersion(superblock.major_version,
superblock.oldest_minor_version),
};
}
UpdateSpaceUsage(superblock, reserved_blocks);
fs_inspect_nodes_ = fs_inspect::CreateTree(component_inspector_.root(), CreateCallbacks());
}
void MinfsInspectTree::UpdateSpaceUsage(const Superblock& superblock, uint64_t reserved_blocks) {
std::lock_guard guard(usage_mutex_);
usage_ = CalculateSpaceUsage(superblock, reserved_blocks);
}
void MinfsInspectTree::OnOutOfSpace() {
zx::time curr_time = zx::clock::get_monotonic();
std::lock_guard guard(fvm_mutex_);
if ((curr_time - last_out_of_space_event_) > kEventWindowDuration) {
++fvm_.out_of_space_events;
last_out_of_space_event_ = curr_time;
}
}
void MinfsInspectTree::OnRecoveredSpace() {
zx::time curr_time = zx::clock::get_monotonic();
std::lock_guard guard(fvm_mutex_);
if ((curr_time - last_recovered_space_event_) > kEventWindowDuration) {
++recovered_space_events_;
last_recovered_space_event_ = curr_time;
}
}
void MinfsInspectTree::AddDirtyBytes(uint64_t bytes) {
std::lock_guard guard(fvm_mutex_);
dirty_bytes_ = safemath::CheckAdd(dirty_bytes_, bytes).ValueOrDie();
}
void MinfsInspectTree::SubtractDirtyBytes(uint64_t bytes) {
std::lock_guard guard(fvm_mutex_);
dirty_bytes_ = safemath::CheckSub(dirty_bytes_, bytes).ValueOrDie();
}
fs_inspect::FvmData MinfsInspectTree::GetFvmData() {
zx::result<fs_inspect::FvmData::SizeInfo> size_info = zx::error(ZX_ERR_BAD_HANDLE);
{
std::lock_guard guard(device_mutex_);
size_info = fs_inspect::FvmData::GetSizeInfoFromDevice(*device_);
if (size_info.is_error()) {
FX_LOGS(WARNING) << "Failed to obtain size information from block device: "
<< size_info.status_string();
}
}
std::lock_guard guard(fvm_mutex_);
if (size_info.is_ok()) {
fvm_.size_info = size_info.value();
}
return fvm_;
}
inspect::LazyNodeCallbackFn MinfsInspectTree::CreateDetailNode() const {
return [this]() {
inspect::Inspector insp;
uint64_t recovered_space_events;
uint64_t dirty_bytes;
{
std::lock_guard guard(fvm_mutex_);
recovered_space_events = recovered_space_events_;
dirty_bytes = dirty_bytes_;
}
insp.GetRoot().CreateUint("recovered_space_events", recovered_space_events, &insp);
insp.GetRoot().CreateUint("dirty_bytes", dirty_bytes, &insp);
return fpromise::make_ok_promise(insp);
};
}
fs_inspect::NodeCallbacks MinfsInspectTree::CreateCallbacks() {
return {
.info_callback =
[this] {
std::lock_guard guard(info_mutex_);
return info_;
},
.usage_callback =
[this] {
std::lock_guard guard(usage_mutex_);
return usage_;
},
.fvm_callback = [this] { return GetFvmData(); },
.detail_node_callback = CreateDetailNode(),
};
}
} // namespace minfs