blob: dab905407b10bdb92107b038767cea49ba4629b0 [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.
#ifndef SRC_STORAGE_F2FS_VMO_MANAGER_H_
#define SRC_STORAGE_F2FS_VMO_MANAGER_H_
#include <bitset>
#include <safemath/checked_math.h>
namespace f2fs {
// It manages the lifecycle of |vmo_| that Pages use in each vnode.
class VmoNode : public fbl::WAVLTreeContainable<std::unique_ptr<VmoNode>> {
public:
VmoNode() = delete;
VmoNode(const VmoNode &) = delete;
VmoNode &operator=(const VmoNode &) = delete;
VmoNode(const VmoNode &&) = delete;
VmoNode &operator=(const VmoNode &&) = delete;
constexpr VmoNode(pgoff_t index) : index_(index) {}
~VmoNode();
// It ensures that |vmo_| keeps VMO_OP_LOCK as long as any Pages refer to it
// by calling Page::GetPage(). When it needs to reuse |vmo_| that it unlocked due to no
// reference to |vmo_|, it tries VMO_OP_TRY_LOCK to check if kernel has reclaimed any pages
// of |vmo_|. If so, it does VMO_OP_LOCK to check which pages were decommitted by kernel.
zx::status<bool> CreateAndLockVmo(pgoff_t offset);
// It unlocks |vmo_| when there is no Page using it.
zx_status_t UnlockVmo(pgoff_t offset);
zx::status<zx_vaddr_t> GetAddress(pgoff_t offset);
pgoff_t GetKey() const { return index_; }
uint64_t GetActivePages() const { return active_pages_; }
private:
// It indicates the size of |VmoNode::vmo_| in kPageSize units.
// Currently, it is set to the f2fs segment size.
static constexpr size_t kVmoSize = kDefaultBlocksPerSegment;
zx_vaddr_t PageIndexToAddress(pgoff_t page_index);
pgoff_t AddressToPageIndex(zx_vaddr_t address);
// It tracks which Page has been decommitted by kernel during |vmo_| unlocked.
// When a bit is 0, a caller (i.e., Page::GetPage()) clears the kUptodate flag of the
// corresponding Page and fill the Page with data read from disk.
std::bitset<kVmoSize> page_bitmap_;
zx::vmo vmo_;
// A mapping to |vmo_|. It keeps until VmoNode is deleted.
zx_vaddr_t address_ = 0;
// The number of Pages refering to |vmo_|.
uint64_t active_pages_ = 0;
const pgoff_t index_;
};
// It maintains VmoNodes in a WAVL tree. Each vnode has its own VmoManager based
// on which its FileCache runs by getting and putting Pages. It maps kVmoSize Pages
// to a VmoNode to batch VMO_OP_LOCK and UNLOCK operations. Also, a mapping of
// a VmoNode keeps as long as the VmoNode is alive in |vmo_tree_| to reduce the mapping operation.
class VmoManager {
public:
VmoManager() = default;
VmoManager(const VmoManager &) = delete;
VmoManager &operator=(const VmoManager &) = delete;
VmoManager(const VmoManager &&) = delete;
VmoManager &operator=(const VmoManager &&) = delete;
~VmoManager() { Reset(true); }
zx::status<bool> CreateAndLockVmo(const pgoff_t index) __TA_EXCLUDES(tree_lock_);
zx_status_t UnlockVmo(const pgoff_t index, const bool evict) __TA_EXCLUDES(tree_lock_);
zx::status<zx_vaddr_t> GetAddress(pgoff_t index) __TA_EXCLUDES(tree_lock_);
void Reset(bool shutdown = false) __TA_EXCLUDES(tree_lock_);
private:
// It indicates the size of |VmoNode::vmo_| in kPageSize units.
// Currently, it is set to the f2fs segment size.
static constexpr size_t kVmoSize = kDefaultBlocksPerSegment;
pgoff_t GetOffsetInVmoNode(pgoff_t page_index);
pgoff_t GetVmoNodeKey(pgoff_t page_index);
using VmoTreeTraits = fbl::DefaultKeyedObjectTraits<pgoff_t, VmoNode>;
using VmoTree = fbl::WAVLTree<pgoff_t, std::unique_ptr<VmoNode>, VmoTreeTraits>;
zx::status<VmoNode *> FindVmoNodeUnsafe(const pgoff_t index) __TA_REQUIRES_SHARED(tree_lock_);
zx::status<VmoNode *> GetVmoNodeUnsafe(const pgoff_t index) __TA_REQUIRES(tree_lock_);
fs::SharedMutex tree_lock_;
VmoTree vmo_tree_ __TA_GUARDED(tree_lock_);
};
} // namespace f2fs
#endif // SRC_STORAGE_F2FS_VMO_MANAGER_H_