blob: 386afd4f1d02101376a2243866f791b6185ccae7 [file] [log] [blame] [edit]
// 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.
#include "page-watcher.h"
#include <zircon/status.h>
#include <blobfs/format.h>
#include <fbl/auto_lock.h>
#include <fs/trace.h>
namespace blobfs {
// Called from the blobfs main thread.
zx_status_t PageWatcher::CreatePagedVmo(size_t vmo_size, zx::vmo* vmo_out) {
TRACE_DURATION("blobfs", "PageWatcher::CreatePagedVmo", "vmo_size", vmo_size);
uint32_t vmo_options = 0;
zx::vmo vmo;
zx_status_t status = page_request_handler_.CreateVmo(
user_pager_->Dispatcher(), zx::unowned_pager(user_pager_->Pager().get()), vmo_options,
vmo_size, &vmo);
if (status != ZX_OK) {
FS_TRACE_ERROR("blobfs: Failed to create paged VMO: %s\n", zx_status_get_string(status));
return status;
}
{
// The call to |CreateVmo| succeeded. The VMO is now attached to the pager, so we need to
// wait for a detach before we can destroy the PageWatcher cleanly - |vmo_attached_to_pager_|
// tracks that and is set to false on receiving a ZX_PAGER_VMO_COMPLETE packet (on the pager
// detach path).
fbl::AutoLock guard(&vmo_attached_mutex_);
vmo_attached_to_pager_ = true;
}
vmo_ = zx::unowned_vmo(vmo);
*vmo_out = std::move(vmo);
return ZX_OK;
}
// Called from the blobfs main thread.
void PageWatcher::DetachPagedVmoSync() {
TRACE_DURATION("blobfs", "PageWatcher::DetachPagedVmoSync");
page_request_handler_.Detach();
// Wait on signal from the page request handler.
fbl::AutoLock guard(&vmo_attached_mutex_);
while (vmo_attached_to_pager_) {
vmo_attached_condvar_.Wait(&vmo_attached_mutex_);
}
}
// Called from the singleton userpager thread.
void PageWatcher::HandlePageRequest(async_dispatcher_t* dispatcher, async::PagedVmoBase* paged_vmo,
zx_status_t status, const zx_packet_page_request_t* request) {
TRACE_DURATION("blobfs", "PageWatcher::HandlePageRequest", "command", request->command, "offset",
request->offset, "length", request->length);
// The async loop is shutting down. The VMO has been detached from the pager, mark it safe to
// destroy.
if (status == ZX_ERR_CANCELED) {
// Signal here without waiting for a ZX_PAGER_VMO_COMPLETE packet, to prevent holding up
// destruction indefinitely. The pager async loop is shutting down, so we won't receive any more
// packets on its port.
SignalPagerDetach();
return;
}
// The only other |status| we expect is ZX_OK.
ZX_DEBUG_ASSERT(status == ZX_OK);
ZX_DEBUG_ASSERT(request->flags == 0);
switch (request->command) {
case ZX_PAGER_VMO_READ: {
PopulateAndVerifyPagesInRange(request->offset, request->length);
return;
}
case ZX_PAGER_VMO_COMPLETE: {
SignalPagerDetach();
return;
}
default:
return;
}
}
// TODO(rashaeqbal): fxb/40207
// Propagate errors better. Right now we simply return after an FS_TRACE_ERROR.
// Ideally we want to signal the waiting event associated with the corresponding page request, but
// we don't have access to that here. We probably need a syscall to signal failure so the thread
// waiting on the page request can handle it. With the current design, a failure will cause it to
// block indefinitely waiting on the page request event. The only two spots this event is signaled
// are:
// 1. when the |supply_pages| syscall succeeds (look at PageSource::OnPagesSupplied()).
// 2. when the page source is detached from the VMO.
//
// Called from the singleton userpager thread.
void PageWatcher::PopulateAndVerifyPagesInRange(uint64_t offset, uint64_t length) {
TRACE_DURATION("blobfs", "PageWatcher::PopulateAndVerifyPagesInRange", "offset", offset, "length",
length);
if (!vmo_->is_valid()) {
FS_TRACE_ERROR("blobfs pager: VMO is not valid.\n");
return;
}
zx_status_t status = user_pager_->TransferPagesToVmo(offset, length, *vmo_, &userpager_info_);
if (status != ZX_OK) {
FS_TRACE_ERROR("blobfs pager: Failed to transfer pages to the blob, error: %s\n",
zx_status_get_string(status));
return;
}
}
// Called from the singleton userpager thread.
void PageWatcher::SignalPagerDetach() {
TRACE_DURATION("blobfs", "PageWatcher::SignalPagerDetach");
// Reset the mapping so that future read requests on this VMO will be ignored.
vmo_ = zx::unowned_vmo(ZX_HANDLE_INVALID);
// Complete the paged vmo detach. Any in-flight read requests that arrive after this will be
// ignored.
fbl::AutoLock guard(&vmo_attached_mutex_);
vmo_attached_to_pager_ = false;
vmo_attached_condvar_.Signal();
}
} // namespace blobfs