blob: bf861b23789832416d8100d7b379d6a36a3d9dd4 [file] [log] [blame]
// Copyright 2020 The Chromium 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 "chrome/browser/performance_manager/mechanisms/userspace_swap_chromeos.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/files/scoped_file.h"
#include "base/posix/safe_strerror.h"
#include "base/task/post_task.h"
#include "base/threading/scoped_blocking_call.h"
#include "chromeos/memory/userspace_swap/swap_storage.h"
#include "chromeos/memory/userspace_swap/userfaultfd.h"
#include "chromeos/memory/userspace_swap/userspace_swap.h"
#include "chromeos/memory/userspace_swap/userspace_swap.mojom.h"
#include "components/performance_manager/graph/node_attached_data.h"
#include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/performance_manager_impl.h"
#include "components/performance_manager/process_node_source.h"
#include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/public/performance_manager.h"
#include "components/performance_manager/public/render_process_host_proxy.h"
#include "components/performance_manager/render_process_user_data.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
namespace performance_manager {
namespace mechanism {
namespace userspace_swap {
namespace {
using chromeos::memory::userspace_swap::SwapFile;
using chromeos::memory::userspace_swap::UserfaultFD;
using chromeos::memory::userspace_swap::UserspaceSwapConfig;
// The RendererSwapData structure contains all the state related to userspace
// swap for an individual renderer.
//
// TODO(bgeffon): This moves to a shared file later when the remainder of the
// code lands.
struct RendererSwapData {
int render_process_host_id;
bool setup_complete = false;
std::unique_ptr<UserfaultFD> uffd;
std::unique_ptr<SwapFile> swap_file;
};
// UserspaceSwapMechanismData contains process node specific details and
// handles.
class UserspaceSwapMechanismData
: public ExternalNodeAttachedDataImpl<UserspaceSwapMechanismData> {
public:
explicit UserspaceSwapMechanismData(const ProcessNode* node)
: swap_data(new RendererSwapData) {}
~UserspaceSwapMechanismData() override = default;
// Note: This is a unique_ptr because it will be used with code that is added
// in a follow up CL.
std::unique_ptr<RendererSwapData> swap_data;
};
void InitializeProcessNodeOnGraph(int render_process_host_id,
base::ScopedFD uffd,
performance_manager::Graph* graph) {
// Now look up the ProcessNode so we can complete initialization.
DCHECK(graph);
DCHECK(uffd.is_valid());
const ProcessNode* process_node = nullptr;
for (const ProcessNode* proc : graph->GetAllProcessNodes()) {
if (proc->GetRenderProcessHostId().GetUnsafeValue() ==
render_process_host_id) {
process_node = proc;
}
}
if (!process_node) {
LOG(ERROR) << "Couldn't find process node for RPH: "
<< render_process_host_id;
return;
}
if (UserspaceSwapMechanismData::Destroy(process_node)) {
LOG(ERROR) << "ProcessNode contained UserspaceSwapMechanismData";
return;
}
auto* data = UserspaceSwapMechanismData::GetOrCreate(process_node);
auto& swap_data = data->swap_data;
swap_data->render_process_host_id = render_process_host_id;
// Finally wrap up the received userfaultfd into a UserfaultFD instance.
swap_data->uffd = UserfaultFD::WrapFD(std::move(uffd));
// The SwapFile is always encrypted but the compression layer is optional.
SwapFile::Type swap_type = SwapFile::Type::kEncrypted;
if (UserspaceSwapConfig::Get().use_compressed_swap_file) {
swap_type =
static_cast<SwapFile::Type>(swap_type | SwapFile::Type::kCompressed);
}
swap_data->swap_file = SwapFile::Create(swap_type);
if (!swap_data->swap_file) {
PLOG(ERROR) << "Unable to complete userspace swap initialization failure "
"creating swap file";
// If we can't create a swap file, then we will bail freeing our resources.
UserspaceSwapMechanismData::Destroy(process_node);
return;
}
swap_data->setup_complete = true;
}
} // namespace
bool IsEligibleToSwap(const ProcessNode* process_node) {
if (!process_node->GetProcess().IsValid()) {
return false;
}
auto* data = UserspaceSwapMechanismData::Get(process_node);
if (!data) {
return false;
}
auto& swap_data = data->swap_data;
if (!swap_data->setup_complete || !swap_data->swap_file || !swap_data->uffd) {
return false;
}
return true;
}
UserspaceSwapInitializationImpl::UserspaceSwapInitializationImpl(
int render_process_host_id)
: render_process_host_id_(render_process_host_id) {
CHECK(UserspaceSwapInitializationImpl::UserspaceSwapSupportedAndEnabled());
}
UserspaceSwapInitializationImpl::~UserspaceSwapInitializationImpl() = default;
// static
bool UserspaceSwapInitializationImpl::UserspaceSwapSupportedAndEnabled() {
return chromeos::memory::userspace_swap::UserspaceSwapSupportedAndEnabled();
}
// static
void UserspaceSwapInitializationImpl::Create(
int render_process_host_id,
mojo::PendingReceiver<::userspace_swap::mojom::UserspaceSwapInitialization>
receiver) {
auto impl =
std::make_unique<UserspaceSwapInitializationImpl>(render_process_host_id);
mojo::MakeSelfOwnedReceiver(std::move(impl), std::move(receiver));
}
void UserspaceSwapInitializationImpl::TransferUserfaultFD(
uint64_t error,
mojo::PlatformHandle uffd_handle,
TransferUserfaultFDCallback cb) {
base::ScopedClosureRunner scr(std::move(cb));
if (received_transfer_cb_) {
return;
}
received_transfer_cb_ = true;
if (error != 0) {
LOG(ERROR) << "Unable to create userfaultfd for renderer: "
<< base::safe_strerror(error);
return;
}
if (!uffd_handle.is_valid()) {
LOG(ERROR) << "FD received is invalid.";
return;
}
// Make sure we're on the graph and complete the initialization.
PerformanceManager::CallOnGraph(
FROM_HERE, base::BindOnce(&InitializeProcessNodeOnGraph,
render_process_host_id_, uffd_handle.TakeFD()));
}
} // namespace userspace_swap
} // namespace mechanism
} // namespace performance_manager