blob: 8bca6e7ac7bf46f75c720e99241b638d06b49da3 [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.
#include "src/lib/storage/vfs/cpp/paged_vnode.h"
#include "src/lib/storage/vfs/cpp/paged_vfs.h"
namespace fs {
PagedVnode::PagedVnode(PagedVfs* vfs) : Vnode(vfs), clone_watcher_(this) {}
PagedVnode::~PagedVnode() {}
zx::status<> PagedVnode::EnsureCreatePagedVmo(uint64_t size) {
if (paged_vmo()) {
// The derived class can choose to cache the VMO even if it has no clones, so just because the
// VMO exists doesn't mean it has clones. As a result, we may need to arm the watcher.
WatchForZeroVmoClones();
return zx::ok();
}
if (!paged_vfs())
return zx::error(ZX_ERR_BAD_STATE); // Currently shutting down.
auto info_or = paged_vfs()->CreatePagedNodeVmo(fbl::RefPtr<PagedVnode>(this), size);
if (info_or.is_error())
return info_or.take_error();
paged_vmo_info_ = std::move(info_or).value();
// Watch the VMO for the presence of no children. The VMO currently has no children because we
// just created it, but the signal will be edge-triggered.
WatchForZeroVmoClones();
return zx::ok();
}
void PagedVnode::FreePagedVmo() {
if (!paged_vmo_info_.vmo.is_valid())
return;
if (paged_vfs())
paged_vfs()->UnregisterPagedVmo(paged_vmo_info_.id);
paged_vmo_info_.vmo.reset();
paged_vmo_info_.id = 0;
}
void PagedVnode::OnNoPagedVmoClones() {
// It is now save to release the VMO. Since we know there are no clones, we don't have to
// call zx_pager_detach_vmo() to stop delivery of requests.
FreePagedVmo();
}
void PagedVnode::OnNoPagedVmoClonesMessage(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
std::lock_guard lock(mutex_);
ZX_DEBUG_ASSERT(has_clones_);
if (!paged_vfs())
return; // Called during tear-down.
// The kernel signal delivery could have raced with us creating a new clone. Validate that there
// are still no clones before tearing down.
zx_info_vmo_t info;
if (paged_vmo().get_info(ZX_INFO_VMO, &info, sizeof(info), nullptr, nullptr) != ZX_OK)
return; // Something wrong with the VMO, don't try to tear down.
if (info.num_children > 0) {
// Race with new VMO. Re-arm the clone watcher and continue as if the signal was not sent.
WatchForZeroVmoClones();
return;
}
clone_watcher_.set_object(ZX_HANDLE_INVALID);
has_clones_ = false;
OnNoPagedVmoClones();
}
void PagedVnode::WatchForZeroVmoClones() {
has_clones_ = true;
clone_watcher_.set_object(paged_vmo().get());
clone_watcher_.set_trigger(ZX_VMO_ZERO_CHILDREN);
clone_watcher_.Begin(paged_vfs()->dispatcher());
}
} // namespace fs