| // 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 |