blob: e503f6dbdb26319a8ef3c2f6bfb687fc240e3263 [file] [log] [blame]
// 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_LIB_VFS_CPP_PAGED_VFS_H_
#define SRC_STORAGE_LIB_VFS_CPP_PAGED_VFS_H_
#include <lib/async/dispatcher.h>
#include <lib/zx/pager.h>
#include <lib/zx/result.h>
#include <lib/zx/thread.h>
#include <lib/zx/vmo.h>
#include <zircon/types.h>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <vector>
#include "src/storage/lib/vfs/cpp/managed_vfs.h"
#include "src/storage/lib/vfs/cpp/pager_thread_pool.h"
namespace fs {
class PagedVnode;
// A variant of Vfs that supports paging. A PagedVfs supports PagedVnode objects.
class PagedVfs : public ManagedVfs {
public:
// The caller must call Init() which must succeed before using this class.
explicit PagedVfs(async_dispatcher_t* dispatcher, int num_pager_threads = 1);
~PagedVfs() override;
// Creates the pager and worker threads. If any of these fail, this class should not be used.
// After calling Init, TearDown *must* be called before destroying.
zx::result<> Init() __TA_EXCLUDES(vfs_lock_);
bool is_initialized() const { return pager_.is_valid(); }
// TearDown should be called before PagedVfs is destroyed. It can be called from the derived
// classes's destructor, but it should be on the class that is marked as final.
void TearDown();
// Gets the list of pager threads. This is designed to allow callers to set up scheduling profiles
// on their pagers.
std::vector<zx::unowned_thread> GetPagerThreads() const;
// Called in response to a successful PagedVnode::VmoRead() request, this supplies paged data from
// aux_vmo to the PagedVnode's VMO to the kernel. See zx_pager_supply_pages() documentation for
// more.
zx::result<> SupplyPages(const zx::vmo& node_vmo, uint64_t offset, uint64_t length,
const zx::vmo& aux_vmo, uint64_t aux_offset) __TA_EXCLUDES(vfs_lock_);
// Called in response to a successful PagedVnode::VmoDirty() request, this transits page state
// from clean to dirty. See zx_pager_op_range() documentation for more.
zx::result<> DirtyPages(const zx::vmo& node_vmo, uint64_t offset, uint64_t length)
__TA_EXCLUDES(vfs_lock_);
// Called in response to a failed PagedVnode::VmoRead() request, this reports that there was an
// error populating page data. See zx_pager_op_range() documentation for more, only certain
// values are permitted for err.
zx::result<> ReportPagerError(const zx::vmo& node_vmo, uint64_t offset, uint64_t length,
zx_status_t err) __TA_EXCLUDES(vfs_lock_);
// Notifies the kernel that the filesystem has started cleaning the `range` of pages. See
// `ZX_PAGER_OP_WRITEBACK_BEGIN` for more information.
zx::result<> WritebackBegin(const zx::vmo& node_vmo, uint64_t offset, uint64_t length)
__TA_EXCLUDES(vfs_lock_);
// Notifies the kernel that the filesystem has finished cleaning the `range` of pages. See
// `ZX_PAGER_OP_WRITEBACK_END` for more information.
zx::result<> WritebackEnd(const zx::vmo& node_vmo, uint64_t offset, uint64_t length)
__TA_EXCLUDES(vfs_lock_);
// Allocates a VMO of the given size associated with the given PagedVnode. VMOs for use with
// the pager must be allocated by this method so the page requests are routed to the correct
// PagedVnode.
//
// This will register the PagedVnode for pager requests. A non-owning reference will be taken to
// it. The PagedVnode is responsible for notifying this class when it is destroyed via
// FreePagedVmo() to free this reference.
//
// This function is for internal use by PagedVnode. Most callers should use
// PagedVnode::EnsureCreateVmo().
//
// |options| must be zero or a combination of ZX_VMO_RESIZABLE and ZX_VMO_TRAP_DIRTY.
struct VmoCreateInfo {
zx::vmo vmo;
// Unique identifier for the VMO that can be used in FreePagedVmo().
uint64_t id = 0;
};
zx::result<VmoCreateInfo> CreatePagedNodeVmo(PagedVnode* node, uint64_t size,
uint32_t options = 0) __TA_EXCLUDES(vfs_lock_);
// When there is a VMO clone is alive, the PagedVnode should be registered with the VFS to handle
// the paging requests for it.
//
// PagedVnodes should unregister themselves and properly detach from the pager when being freed or
// when their VMO handle is destroyed to prevent leaks or use-after-free errors. Detaching from
// the pager is important because otherwise pager requests from any code that has mappings will
// hang indefinitely.
//
// The unique IDs are the ones generated by CreatePagedNodeVmo(). The vmo information should be
// moved into this function, which will destroy the VMO handle after it's unregistered.
void FreePagedVmo(VmoCreateInfo info) __TA_EXCLUDES(vfs_lock_);
// Callback that the PagerThreadPool uses to notify us of pager events. These calls will get
// issued on arbitrary threads.
void PagerVmoRead(uint64_t node_id, uint64_t offset, uint64_t length) __TA_EXCLUDES(vfs_lock_)
__TA_EXCLUDES(live_nodes_lock_);
void PagerVmoDirty(uint64_t node_id, uint64_t offset, uint64_t length) __TA_EXCLUDES(vfs_lock_)
__TA_EXCLUDES(live_nodes_lock_);
// Returns the number of VMOs registered for notifications. Used for testing.
size_t GetRegisteredPagedVmoCount() const __TA_EXCLUDES(vfs_lock_)
__TA_EXCLUDES(live_nodes_lock_);
protected:
// Provides direct access to the underlying zx::pager. This is only exposed so clients can make
// pager syscalls that haven't been stabilized yet.
const zx::pager& pager_for_next_vdso_syscalls() const { return pager_; }
private:
std::unique_ptr<PagerThreadPool> pager_pool_; // Threadsafe, does not need locking.
zx::pager pager_; // Does not need locking.
// Vnodes with active VMOs from the kernel paging system. These are non-owning references and
// the PagedVnode class is responsible for notifying us when the reference is no longer valid.
//
// It is important for Vnodes to be registered here for as long as the VMO could possibly be used
// to avoid accumulating bad state in the kernel. For example, if there are currently no mappings
// one might think it would be safe to be unregistered. But if new mappings could possibly be
// created in the future, it needs to stay continuously registered in case of race conditions.
// See PagedVno::OnNoPagedVmoClones() for more.
//
// Protected by the registred vnode lock so creating nodes in the main lock doesn't attempt to
// reenter the lock by registering.
uint64_t next_node_id_ __TA_GUARDED(live_nodes_lock_) = 1;
std::map<uint64_t, PagedVnode*> paged_nodes_ __TA_GUARDED(live_nodes_lock_);
};
} // namespace fs
#endif // SRC_STORAGE_LIB_VFS_CPP_PAGED_VFS_H_