blob: dcac410dff681e153c407516b101c971b2c7a513 [file] [log] [blame] [edit]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef VM_TOOLS_CONCIERGE_VM_UTIL_H_
#define VM_TOOLS_CONCIERGE_VM_UTIL_H_
#include <sys/types.h>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "base/files/scoped_file.h"
#include <base/files/file_path.h>
#include <base/strings/string_split.h>
#include <base/strings/string_piece.h>
#include <base/time/time.h>
#include <base/values.h>
#include <brillo/files/safe_fd.h>
#include <brillo/process/process.h>
#include <vm_tools/concierge/balloon_policy.h>
namespace base {
class FilePath;
} // namespace base
namespace vm_tools {
namespace apps {
enum VmType : int;
}
namespace concierge {
enum VmInfo_VmType : int;
// Path to process file descriptors.
constexpr char kProcFileDescriptorsPath[] = "/proc/self/fd/";
// Describes key components of a VM.
struct VMImageSpec {
base::FilePath kernel;
base::FilePath initrd;
base::FilePath rootfs;
base::FilePath bios;
base::FilePath pflash;
base::FilePath tools_disk;
bool is_trusted_image;
};
// Describe the values for --async-executor options passed to crosvm
enum class AsyncExecutor {
kUring,
kEpoll,
};
struct Disk {
// Gets the command line argument that needs to be passed to crosvm
// corresponding to this disk.
base::StringPairs GetCrosvmArgs() const;
// Path to the disk image on the host.
base::FilePath path;
// Whether the disk should be writable by the VM.
bool writable;
// Whether the disk should allow sparse file operations (discard) by the VM.
std::optional<bool> sparse;
// Whether the disk access should be done with O_DIRECT by the VM.
std::optional<bool> o_direct;
// Whether to enable multiple workers
std::optional<bool> multiple_workers;
// Async executor crosvm should use to run the disk devices.
std::optional<AsyncExecutor> async_executor;
// Block size.
std::optional<size_t> block_size;
};
// Path to the crosvm binary.
extern const char kCrosvmBin[];
// Calculates the amount of memory to give the virtual machine, in MiB.
// Currently configured to provide 75% of system memory. This is deliberately
// over provisioned with the expectation that we will use the balloon driver to
// reduce the actual memory footprint.
int64_t GetVmMemoryMiB();
// Retrieves the physical package ID for |cpu| from the topology information in
// sysfs.
std::optional<int32_t> GetCpuPackageId(int32_t cpu);
// Retrieves the CPU capacity property for |cpu| from sysfs.
std::optional<int32_t> GetCpuCapacity(int32_t cpu);
// Calculate an appropriate CPU affinity setting based on the host system's
// CPU clusters and capacity. CPUs will be grouped based on cluster if multiple
// clusters exist, or based on groupings of equal CPU capacity if more than one
// such grouping exists. Otherwise, |nullopt| will be returned.
std::optional<std::string> GetCpuAffinityFromClusters(
const std::vector<std::vector<std::string>>& cpu_clusters,
const std::map<int32_t, std::vector<std::string>>& cpu_capacity_groups);
// Puts the current process in a CPU cgroup specificed by |cpu_cgroup|, and
// then calls SetPgid(). This function can be called as brillo::ProcessImpl's
// PreExecCallback.
bool SetUpCrosvmProcess(const base::FilePath& cpu_cgroup);
// Sets the pgid of the current process to its pid. This is needed because
// crosvm assumes that only it and its children are in the same process group
// and indiscriminately sends a SIGKILL if it needs to shut them down. This
// function can be called as brillo::ProcessImpl's PreExecCallback.
bool SetPgid();
// Waits for the |pid| to exit. Returns true if |pid| successfully exited and
// false if it did not exit in time.
bool WaitForChild(pid_t child, base::TimeDelta timeout);
// Returns true if a process with |pid| exists.
bool CheckProcessExists(pid_t pid);
// Returns balloon stats info retrieved from virtio-balloon device.
std::optional<BalloonStats> GetBalloonStats(std::string socket_path);
// Parses balloon stats info from a JSON value.
std::optional<BalloonStats> ParseBalloonStats(
const base::Value::Dict& balloon_stats);
// Attaches an usb device at host |bus|:|addr|, with |vid|, |pid| and an
// opened |fd|.
bool AttachUsbDevice(std::string socket_path,
uint8_t bus,
uint8_t addr,
uint16_t vid,
uint16_t pid,
int fd,
uint8_t* out_port);
// Detaches the usb device at guest |port|.
bool DetachUsbDevice(std::string socket_path, uint8_t port);
// Lists all usb devices attached to guest.
bool ListUsbDevice(std::string socket_path,
std::vector<UsbDeviceEntry>* devices);
// Resizes the disk identified by |disk_index| to |new_size| in bytes.
bool CrosvmDiskResize(std::string socket_path,
int disk_index,
uint64_t new_size);
// Updates |cpu_cgroup|'s cpu.shares to |cpu_shares|.
bool UpdateCpuShares(const base::FilePath& cpu_cgroup, int cpu_shares);
// Updates |cpu_cgroup|'s cpu.cfs_quota_us and cpu.cfs_period_us
// based on |percent|.
bool UpdateCpuQuota(const base::FilePath& cpu_cgroup, int percent);
// Updates |cpu_cgroup|'s cpu.uclamp.latency_sensitive to |enable|.
bool UpdateCpuLatencySensitive(const base::FilePath& cpu_cgroup, bool enable);
// Updates |cpu_cgroup|'s cpu.uclamp.min based on |percent|.
bool UpdateCpuUclampMin(const base::FilePath& cpu_cgroup, double percent);
// Convert file path into fd path
// This will open the file and append SafeFD into provided container
std::string ConvertToFdBasedPath(brillo::SafeFD& parent_fd,
base::FilePath* in_out_path,
int flags,
std::vector<brillo::SafeFD>& fd_storage);
// Convert a string to the corresponding AsyncExecutor. This returns nullopt if
// the given string is unknown.
std::optional<AsyncExecutor> StringToAsyncExecutor(
const std::string& async_executor);
// Convert the given |type| to the legacy VM type defined in
// vm_concierge/concierge.pb
VmInfo_VmType ToLegacyVmType(apps::VmType type);
class CustomParametersForDev {
public:
// By default this class would do nothing.
CustomParametersForDev() = default;
// Allow custom parameters on development devices with arcvm_dev.conf.
// Loads custom parameters from a string. Please check
// vm_tools/init/arcvm_dev.conf for the list of supported directives.
explicit CustomParametersForDev(const std::string& data);
// Apply the parsed result of configuration files to |args| as a vector of
// string pairs.
void Apply(base::StringPairs* args);
std::optional<const std::string> ObtainSpecialParameter(
const std::string& key);
private:
// Command line parameter prefix to 'crosvm run' to remove.
std::vector<std::string> prefix_to_remove_{};
// Command line parameters for 'crosvm run' to prepend.
base::StringPairs params_to_prepend_{};
// Command line parameters to 'crosvm run' to add.
base::StringPairs params_to_add_{};
// Other special handling.
std::map<std::string, std::string> special_parameters_{};
bool initialized_{false};
};
// Uid and gid mappings for the android data directory. This is a
// comma-separated list of 3 values: <start of range inside the user namespace>
// <start of range outside the user namespace> <count>. The values are taken
// from platform2/arc/container-bundle/pi/config.json.
constexpr char kAndroidUidMap[] =
"0 655360 5000,5000 600 50,5050 660410 1994950";
constexpr char kAndroidGidMap[] =
"0 655360 1065,1065 20119 1,1066 656426 3934,5000 600 50,5050 660410 "
"1994950";
// Shared data parameter for crosvm.
struct SharedDataParam {
std::string to_string() const;
base::FilePath data_dir;
std::string tag;
std::string uid_map;
std::string gid_map;
bool enable_caches;
bool ascii_casefold;
bool posix_acl;
std::vector<uid_t> privileged_quota_uids;
};
// Creates the font-specific shared data parameter for crosvm.
std::string CreateFontsSharedDataParam();
class ArcVmCPUTopology {
public:
ArcVmCPUTopology(uint32_t num_cpus, uint32_t num_rt_cpus);
~ArcVmCPUTopology() = default;
ArcVmCPUTopology(const ArcVmCPUTopology&) = delete;
ArcVmCPUTopology& operator=(const ArcVmCPUTopology&) = delete;
void CreateCPUAffinity();
bool IsSymmetricCPU();
uint32_t NumCPUs();
uint32_t NumRTCPUs();
void SetNumRTCPUs(uint32_t num_rt_cpus);
const std::string& AffinityMask();
const std::string& RTCPUMask();
const std::string& NonRTCPUMask();
const std::string& CapacityMask();
const std::vector<std::string>& PackageMask();
int TopAppUclampMin();
double GlobalVMBoost();
// Unit Testing crud
void AddCpuToCapacityGroupForTesting(uint32_t cpu, uint32_t capacity);
void AddCpuToPackageGroupForTesting(uint32_t cpu, uint32_t package);
void CreateCPUAffinityForTesting();
private:
void CreateTopology();
void CreateAffinity();
// Total number of CPUs VM will be configured with
uint32_t num_cpus_;
// Number of RT CPUs
uint32_t num_rt_cpus_;
// CPU mask for RT CPUs
std::string rt_cpu_mask_;
// CPU mask for non RT CPUs
std::string non_rt_cpu_mask_;
// CPU affinity
std::string affinity_mask_;
// A set of RT CPUs
std::set<uint32_t> rt_cpus_;
// CPU capacity grouping
std::map<uint32_t, std::vector<uint32_t>> capacity_;
// CPU package grouping
std::map<uint32_t, std::vector<uint32_t>> package_;
// CPU capacity mask
std::string capacity_mask_;
// CPU package mask
std::vector<std::string> package_mask_;
// Default uclamp.min for performance tasks based on capacity
int top_app_uclamp_min_;
// Amount of the global VM boost, which should be applied to the host cgroups
double global_vm_boost_;
};
class VmStartChecker {
public:
// The return type when we call |Wait|.
enum Status {
// VM is ready.
READY = 0,
// Invalid event received while epoll-ing.
EPOLL_INVALID_EVENT,
// Invalid fd received while epoll-ing.
EPOLL_INVALID_FD,
// Timed out waiting for the VM to start i.e. no event or signal received.
TIMEOUT,
// Invalid signal info.
INVALID_SIGNAL_INFO,
// Signal received while waiting.
SIGNAL_RECEIVED
};
// Create an instance of |VmStartChecker|. |signal_fd| is owned by the client.
static std::unique_ptr<VmStartChecker> Create(int32_t signal_fd);
~VmStartChecker() = default;
// Wait for the VM to start with |timeout|.
Status Wait(base::TimeDelta timeout);
int32_t GetEventFd() const;
private:
VmStartChecker(int32_t signal_fd,
base::ScopedFD event_fd,
base::ScopedFD epoll_fd);
// Signal fd associated with the client that constructs this object.
int32_t signal_fd_;
// Event fd created to monitor VM start up.
base::ScopedFD event_fd_;
// Epoll fd that will wait on both |event_fd_| and |signal_fd_|.
base::ScopedFD epoll_fd_;
};
} // namespace concierge
} // namespace vm_tools
#endif // VM_TOOLS_CONCIERGE_VM_UTIL_H_