blob: 422ea671165ea6b813bb951bd4a796437578ddd8 [file] [log] [blame]
// Copyright 2019 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 ZIRCON_SYSTEM_ULIB_BLOBFS_PAGER_USER_PAGER_H_
#define ZIRCON_SYSTEM_ULIB_BLOBFS_PAGER_USER_PAGER_H_
#ifndef __Fuchsia__
#error Fuchsia-only Header
#endif
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/dispatcher.h>
#include <lib/zx/pager.h>
#include <blobfs/compression-algorithm.h>
#include "../blob-verifier.h"
namespace blobfs {
// Info required by the user pager to read in and verify pages.
// Initialized by the PageWatcher and passed on to the UserPager.
struct UserPagerInfo {
// Unique identifier used by UserPager to identify the data source on the underlying block
// device.
uint32_t identifier = 0;
// Block offset (in bytes) the data starts at. Used to inform the UserPager of the offset it
// should start issuing reads from.
uint64_t data_start_bytes = 0;
// Total length of the data. The |verifier| must be set up to verify this length.
uint64_t data_length_bytes = 0;
// Used to verify the pages as they are read in.
// TODO(44742): Make BlobVerifier movable, unwrap from unique_ptr.
std::unique_ptr<BlobVerifier> verifier;
CompressionAlgorithm compression_algorithm;
};
// The size of a transfer buffer for reading from storage.
//
// The decision to use a single global transfer buffer is arbitrary; a pool of them could also be
// available in the future for more fine-grained access. Moreover, the blobfs pager uses a single
// thread at the moment, so a global buffer should be sufficient.
//
// 256 MB; but the size is arbitrary, since pages will become decommitted as they are moved to
// destination VMOS.
constexpr uint64_t kTransferBufferSize = 256 * (1 << 20);
// Abstract class that encapsulates a user pager, its associated thread and transfer buffer. The
// child class will need to define the interface required to populate the transfer buffer with
// blocks read from storage.
class UserPager {
public:
UserPager() = default;
virtual ~UserPager() = default;
// Returns the pager handle.
const zx::pager& Pager() const { return pager_; }
// Returns the pager dispatcher.
async_dispatcher_t* Dispatcher() const { return pager_loop_.dispatcher(); }
// Invoked by the |PageWatcher| on a read request. Reads in the requested byte range
// [|offset|, |offset| + |length|) for the inode associated with |info->identifier| into the
// |transfer_buffer_|, and then moves those pages to the destination |vmo|. If |verifier_info| is
// not null, uses it to verify the pages prior to transferring them to the destination vmo.
zx_status_t TransferPagesToVmo(uint64_t offset, uint64_t length, const zx::vmo& vmo,
UserPagerInfo* info);
protected:
// Sets up the transfer buffer, creates the pager and starts the pager thread.
zx_status_t InitPager();
// Protected for unit test access.
zx::pager pager_;
// Scratch buffer for pager transfers.
// NOTE: Per the constraints imposed by |zx_pager_supply_pages|, this needs to be unmapped before
// calling |zx_pager_supply_pages|. Map this only when an explicit address is required, e.g. for
// verification, and unmap it immediately after.
zx::vmo transfer_buffer_;
private:
struct ReadRange {
uint64_t offset;
uint64_t length;
};
// Returns a range which covers [offset, offset+length), adjusted for read-ahead and alignment.
//
// The returned range will have the following guarantees:
// - The range will contain [offset, offset+length).
// - The returned offset will be block-aligned.
// - The end of the returned range is *either* block-aligned or is the end of the file.
// - The range will be adjusted for verification (see |BlobVerifier::Align|).
//
// The range needs to be extended before actually populating the transfer buffer with pages, as
// absent pages will cause page faults during verification on the userpager thread, causing it to
// block against itself indefinitely.
//
// For example:
// |...input_range...|
// |..data_block..|..data_block..|..data_block..|
// |........output_range.........|
ReadRange ExtendReadRange(UserPagerInfo* info, uint64_t offset, uint64_t length);
// Attaches the transfer buffer to the underlying block device, so that blocks can be read into it
// from storage.
virtual zx_status_t AttachTransferVmo(const zx::vmo& transfer_vmo) = 0;
// Reads data for the inode corresponding to |info->identifier| into the transfer buffer for the
// byte range specified by [|offset|, |offset| + |length|).
virtual zx_status_t PopulateTransferVmo(uint64_t offset, uint64_t length,
UserPagerInfo* info) = 0;
// Verifies the data read in to |transfer_vmo| (i.e. the transfer buffer) via
// |PopulateTransferVmo|. Data in the range [|offset|, |offset| + |length|) is verified using the
// |info| provided.
virtual zx_status_t VerifyTransferVmo(uint64_t offset, uint64_t length,
const zx::vmo& transfer_vmo, UserPagerInfo* info) = 0;
// Async loop for pager requests.
async::Loop pager_loop_ = async::Loop(&kAsyncLoopConfigNoAttachToCurrentThread);
};
} // namespace blobfs
#endif // ZIRCON_SYSTEM_ULIB_BLOBFS_PAGER_USER_PAGER_H_