| // Copyright 2021 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. |
| |
| #ifndef SRC_STORAGE_F2FS_F2FS_H_ |
| #define SRC_STORAGE_F2FS_F2FS_H_ |
| |
| // clang-format off |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/trace/event.h> |
| #include <lib/zircon-internal/thread_annotations.h> |
| #include <fidl/fuchsia.fs/cpp/wire.h> |
| #include <fidl/fuchsia.process.lifecycle/cpp/wire.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/condition_variable.h> |
| #include <fbl/mutex.h> |
| #include <fidl/fuchsia.fs.startup/cpp/wire.h> |
| |
| #include <fcntl.h> |
| |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/fit/defer.h> |
| #include <lib/fit/function.h> |
| #include <lib/zx/result.h> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/macros.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/string_buffer.h> |
| |
| #include <atomic> |
| #include <condition_variable> |
| #include <memory> |
| #include <mutex> |
| #include <semaphore> |
| |
| #include "src/storage/lib/vfs/cpp/vfs.h" |
| #include "src/storage/lib/vfs/cpp/vnode.h" |
| #include "src/storage/lib/vfs/cpp/transaction/buffered_operations_builder.h" |
| |
| #include "src/storage/lib/vfs/cpp/paged_vfs.h" |
| #include "src/storage/lib/vfs/cpp/paged_vnode.h" |
| #include "src/storage/lib/vfs/cpp/watcher.h" |
| #include "src/storage/lib/vfs/cpp/shared_mutex.h" |
| #include "src/storage/lib/vfs/cpp/service.h" |
| |
| #include "lib/inspect/cpp/inspect.h" |
| |
| #include "src/storage/lib/vfs/cpp/fuchsia_vfs.h" |
| #include "src/storage/lib/vfs/cpp/inspect/inspect_tree.h" |
| |
| #include "src/storage/f2fs/common.h" |
| #include "src/storage/f2fs/f2fs_layout.h" |
| #include "src/storage/f2fs/bcache.h" |
| #include "src/storage/f2fs/mount.h" |
| #include "src/storage/f2fs/f2fs_internal.h" |
| #include "src/storage/f2fs/storage_buffer.h" |
| #include "src/storage/f2fs/writeback.h" |
| #include "src/storage/f2fs/reader.h" |
| #include "src/storage/f2fs/extent_cache.h" |
| #include "src/storage/f2fs/vnode.h" |
| #include "src/storage/f2fs/vnode_cache.h" |
| #include "src/storage/f2fs/dir.h" |
| #include "src/storage/f2fs/file.h" |
| #include "src/storage/f2fs/node.h" |
| #include "src/storage/f2fs/segment.h" |
| #include "src/storage/f2fs/gc.h" |
| #include "src/storage/f2fs/mkfs.h" |
| #include "src/storage/f2fs/fsck.h" |
| #include "src/storage/f2fs/service/admin.h" |
| #include "src/storage/f2fs/service/startup.h" |
| #include "src/storage/f2fs/service/lifecycle.h" |
| #include "src/storage/f2fs/component_runner.h" |
| #include "src/storage/f2fs/dir_entry_cache.h" |
| #include "src/storage/f2fs/inspect.h" |
| #include "src/storage/f2fs/memory_watcher.h" |
| // clang-format on |
| |
| namespace f2fs { |
| |
| zx::result<std::unique_ptr<Superblock>> LoadSuperblock(BcacheMapper &bc); |
| |
| class F2fs final { |
| public: |
| // Not copyable or moveable |
| F2fs(const F2fs &) = delete; |
| F2fs &operator=(const F2fs &) = delete; |
| F2fs(F2fs &&) = delete; |
| F2fs &operator=(F2fs &&) = delete; |
| |
| explicit F2fs(FuchsiaDispatcher dispatcher, std::unique_ptr<f2fs::BcacheMapper> bc, |
| const MountOptions &mount_options, PlatformVfs *vfs); |
| |
| static zx::result<std::unique_ptr<F2fs>> Create(FuchsiaDispatcher dispatcher, |
| std::unique_ptr<f2fs::BcacheMapper> bc, |
| const MountOptions &options, PlatformVfs *vfs); |
| |
| zx::result<fs::FilesystemInfo> GetFilesystemInfo(); |
| DirEntryCache &GetDirEntryCache() { return dir_entry_cache_; } |
| InspectTree &GetInspectTree() { return *inspect_tree_; } |
| |
| VnodeCache &GetVCache() { return vnode_cache_; } |
| zx_status_t InsertVnode(VnodeF2fs *vn) { return vnode_cache_.Add(vn); } |
| zx_status_t EvictVnode(VnodeF2fs *vn) { return vnode_cache_.Evict(vn); } |
| zx_status_t LookupVnode(ino_t ino, fbl::RefPtr<VnodeF2fs> *out) { |
| return vnode_cache_.Lookup(ino, out); |
| } |
| |
| zx::result<std::unique_ptr<f2fs::BcacheMapper>> TakeBc() { |
| if (!bc_) { |
| return zx::error(ZX_ERR_UNAVAILABLE); |
| } |
| return zx::ok(std::move(bc_)); |
| } |
| |
| BcacheMapper &GetBc() const { |
| ZX_DEBUG_ASSERT(bc_ != nullptr); |
| return *bc_; |
| } |
| SuperblockInfo &GetSuperblockInfo() const { |
| ZX_DEBUG_ASSERT(superblock_info_ != nullptr); |
| return *superblock_info_; |
| } |
| SegmentManager &GetSegmentManager() const { |
| ZX_DEBUG_ASSERT(segment_manager_ != nullptr); |
| return *segment_manager_; |
| } |
| NodeManager &GetNodeManager() const { |
| ZX_DEBUG_ASSERT(node_manager_ != nullptr); |
| return *node_manager_; |
| } |
| GcManager &GetGcManager() const { |
| ZX_DEBUG_ASSERT(gc_manager_ != nullptr); |
| return *gc_manager_; |
| } |
| PlatformVfs *vfs() const { return vfs_; } |
| |
| // For testing Reset() and TakeBc() |
| bool IsValid() const; |
| zx_status_t LoadSuper(std::unique_ptr<Superblock> sb); |
| void Reset(); |
| zx_status_t GrabMetaPage(pgoff_t index, LockedPage *out); |
| zx_status_t GetMetaPage(pgoff_t index, LockedPage *out); |
| bool CanReclaim() const; |
| bool IsTearDown() const; |
| void SetTearDown(); |
| |
| zx_status_t CheckOrphanSpace(); |
| void PurgeOrphanInode(nid_t ino); |
| int PurgeOrphanInodes(); |
| void WriteOrphanInodes(block_t start_blk); |
| zx_status_t GetValidCheckpoint(); |
| zx_status_t ValidateCheckpoint(block_t cp_addr, uint64_t *version, LockedPage *out); |
| uint32_t GetFreeSectionsForDirtyPages(); |
| |
| void PutSuper(); |
| void Sync(SyncCallback closure = nullptr) __TA_EXCLUDES(f2fs::GetGlobalLock()); |
| zx_status_t SyncFs(bool bShutdown = false) __TA_EXCLUDES(f2fs::GetGlobalLock()); |
| zx_status_t DoCheckpoint(bool is_umount) __TA_REQUIRES(f2fs::GetGlobalLock()); |
| zx_status_t WriteCheckpoint(bool blocked, bool is_umount) __TA_REQUIRES(f2fs::GetGlobalLock()); |
| |
| // recovery.cc |
| // For the list of fsync inodes, used only during recovery |
| class FsyncInodeEntry : public fbl::DoublyLinkedListable<std::unique_ptr<FsyncInodeEntry>> { |
| public: |
| explicit FsyncInodeEntry(fbl::RefPtr<VnodeF2fs> vnode_refptr) |
| : vnode_(std::move(vnode_refptr)) {} |
| |
| FsyncInodeEntry() = delete; |
| FsyncInodeEntry(const FsyncInodeEntry &) = delete; |
| FsyncInodeEntry &operator=(const FsyncInodeEntry &) = delete; |
| FsyncInodeEntry(FsyncInodeEntry &&) = delete; |
| FsyncInodeEntry &operator=(FsyncInodeEntry &&) = delete; |
| |
| block_t GetLastDnodeBlkaddr() const { return last_dnode_blkaddr_; } |
| void SetLastDnodeBlkaddr(block_t blkaddr) { last_dnode_blkaddr_ = blkaddr; } |
| VnodeF2fs &GetVnode() const { return *vnode_; } |
| |
| private: |
| fbl::RefPtr<VnodeF2fs> vnode_ = nullptr; // vfs inode pointer |
| block_t last_dnode_blkaddr_ = 0; // block address locating the last dnode |
| }; |
| using FsyncInodeList = fbl::DoublyLinkedList<std::unique_ptr<FsyncInodeEntry>>; |
| |
| FsyncInodeEntry *GetFsyncInode(FsyncInodeList &inode_list, nid_t ino); |
| zx_status_t RecoverDentry(NodePage &ipage, VnodeF2fs &vnode); |
| zx_status_t RecoverInode(VnodeF2fs &vnode, NodePage &node_page); |
| zx_status_t FindFsyncDnodes(FsyncInodeList &inode_list); |
| void DestroyFsyncDnodes(FsyncInodeList &inode_list); |
| void CheckIndexInPrevNodes(block_t blkaddr); |
| void DoRecoverData(VnodeF2fs &vnode, NodePage &page); |
| void RecoverData(FsyncInodeList &inode_list, CursegType type); |
| void RecoverFsyncData(); |
| |
| VnodeF2fs &GetNodeVnode() { return *node_vnode_; } |
| VnodeF2fs &GetMetaVnode() { return *meta_vnode_; } |
| zx::result<fbl::RefPtr<VnodeF2fs>> GetRootVnode() { |
| if (root_vnode_) { |
| return zx::ok(root_vnode_); |
| } |
| return zx::error(ZX_ERR_BAD_STATE); |
| } |
| |
| // For testing |
| void SetVfsForTests(std::unique_ptr<PlatformVfs> vfs) { vfs_for_tests_ = std::move(vfs); } |
| zx::result<std::unique_ptr<PlatformVfs>> TakeVfsForTests() { |
| if (vfs_for_tests_) { |
| return zx::ok(std::move(vfs_for_tests_)); |
| } |
| return zx::error(ZX_ERR_UNAVAILABLE); |
| } |
| |
| zx::result<> MakeReadOperations(std::vector<LockedPage> &pages, std::vector<block_t> &addrs, |
| PageType type, bool is_sync = true); |
| zx::result<> MakeReadOperation(LockedPage &page, block_t blk_addr, PageType type, |
| bool is_sync = true); |
| zx::result<> MakeReadOperations(zx::vmo &vmo, std::vector<block_t> &addrs, PageType type, |
| bool is_sync = true); |
| zx_status_t MakeTrimOperation(block_t blk_addr, block_t nblocks) const; |
| |
| void ScheduleWriter(sync_completion_t *completion = nullptr, PageList pages = {}, |
| bool flush = true) { |
| writer_->ScheduleWriteBlocks(completion, std::move(pages), flush); |
| } |
| void ScheduleWriter(fpromise::promise<> task) { writer_->ScheduleTask(std::move(task)); } |
| |
| void ScheduleWriteback(size_t num_pages = kDefaultBlocksPerSegment); |
| zx::result<> WaitForWriteback() { |
| if (!writeback_flag_.try_acquire_for(std::chrono::seconds(kWriteTimeOut))) { |
| return zx::error(ZX_ERR_TIMED_OUT); |
| } |
| writeback_flag_.release(); |
| return zx::ok(); |
| } |
| std::atomic_flag &GetStopReclaimFlag() { return stop_reclaim_flag_; } |
| |
| bool StopWriteback() { |
| // release-acquire ordering with MemoryPressureWatcher::OnLevelChanged(). |
| auto level = current_memory_pressure_level_.load(std::memory_order_acquire); |
| return level == MemoryPressure::kLow || |
| !superblock_info_->GetPageCount(CountType::kDirtyData) || |
| (level == MemoryPressure::kUnknown && |
| superblock_info_->GetPageCount(CountType::kDirtyData) < kMaxDirtyDataPages / 4); |
| } |
| |
| bool HasNotEnoughMemory() { |
| // release-acquire ordering with MemoryPressureWatcher::OnLevelChanged(). |
| auto level = current_memory_pressure_level_.load(std::memory_order_acquire); |
| return (level > MemoryPressure::kLow && |
| superblock_info_->GetPageCount(CountType::kDirtyData)) || |
| (level == MemoryPressure::kUnknown && |
| superblock_info_->GetPageCount(CountType::kDirtyData) >= kMaxDirtyDataPages); |
| } |
| |
| void WaitForAvailableMemory() { |
| while (HasNotEnoughMemory()) { |
| ScheduleWriteback(); |
| ZX_ASSERT(WaitForWriteback().is_ok()); |
| } |
| } |
| |
| bool IsOnRecovery() const { return on_recovery_; } |
| void SetOnRecovery() { on_recovery_ = true; } |
| void ClearOnRecovery() { on_recovery_ = false; } |
| |
| void AddToVnodeSet(VnodeSet type, nid_t ino) __TA_EXCLUDES(vnode_set_mutex_); |
| void RemoveFromVnodeSet(VnodeSet type, nid_t ino) __TA_EXCLUDES(vnode_set_mutex_); |
| bool FindVnodeSet(VnodeSet type, nid_t ino) __TA_EXCLUDES(vnode_set_mutex_); |
| size_t GetVnodeSetSize(VnodeSet type) __TA_EXCLUDES(vnode_set_mutex_); |
| void ForAllVnodeSet(VnodeSet type, fit::function<void(nid_t)> callback) |
| __TA_EXCLUDES(vnode_set_mutex_); |
| void ClearVnodeSet() __TA_EXCLUDES(vnode_set_mutex_); |
| |
| private: |
| void StartMemoryPressureWatcher(); |
| |
| void FlushDirsAndNodes() __TA_REQUIRES(f2fs::GetGlobalLock()); |
| pgoff_t FlushDirtyMetaPages(bool is_commit) __TA_REQUIRES(f2fs::GetGlobalLock()); |
| pgoff_t FlushDirtyDataPages(WritebackOperation &operation, bool wait_writer = false) |
| __TA_REQUIRES_SHARED(f2fs::GetGlobalLock()); |
| zx::result<pgoff_t> FlushDirtyNodePages(WritebackOperation &operation) |
| __TA_REQUIRES(f2fs::GetGlobalLock()); |
| |
| std::atomic_flag teardown_flag_ = ATOMIC_FLAG_INIT; |
| std::atomic_flag stop_reclaim_flag_ = ATOMIC_FLAG_INIT; |
| std::binary_semaphore writeback_flag_{1}; |
| |
| FuchsiaDispatcher dispatcher_; |
| PlatformVfs *const vfs_ = nullptr; |
| std::unique_ptr<f2fs::BcacheMapper> bc_; |
| // for unittest |
| std::unique_ptr<PlatformVfs> vfs_for_tests_; |
| |
| MountOptions mount_options_; |
| |
| std::unique_ptr<SuperblockInfo> superblock_info_; |
| std::unique_ptr<SegmentManager> segment_manager_; |
| std::unique_ptr<NodeManager> node_manager_; |
| std::unique_ptr<GcManager> gc_manager_; |
| |
| std::unique_ptr<Reader> reader_; |
| std::unique_ptr<Writer> writer_; |
| |
| std::unique_ptr<VnodeF2fs> meta_vnode_; |
| std::unique_ptr<VnodeF2fs> node_vnode_; |
| fbl::RefPtr<VnodeF2fs> root_vnode_; |
| |
| VnodeCache vnode_cache_; |
| DirEntryCache dir_entry_cache_; |
| |
| bool on_recovery_ = false; // recovery is doing or not |
| // for inode number management |
| fs::SharedMutex vnode_set_mutex_; |
| std::map<ino_t, uint32_t> vnode_set_ __TA_GUARDED(vnode_set_mutex_); |
| size_t vnode_set_size_[static_cast<size_t>(VnodeSet::kMax)] __TA_GUARDED(vnode_set_mutex_) = { |
| 0, |
| }; |
| |
| zx::event fs_id_; |
| std::unique_ptr<InspectTree> inspect_tree_; |
| std::atomic<MemoryPressure> current_memory_pressure_level_ = MemoryPressure::kUnknown; |
| std::unique_ptr<MemoryPressureWatcher> memory_pressure_watcher_; |
| }; |
| |
| } // namespace f2fs |
| |
| #endif // SRC_STORAGE_F2FS_F2FS_H_ |