blob: dd7ac9beae18494c3ca53e1652feed6a82355050 [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 <string_view>
#include <utility>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/scoped_file.h>
#include <base/strings/string_split.h>
#include <base/time/time.h>
#include <base/values.h>
#include <brillo/files/safe_fd.h>
#include <brillo/process/process.h>
#include <vm_applications/apps.pb.h>
#include "vm_tools/concierge/balloon_policy.h"
#include "vm_tools/concierge/vm_base_impl.h"
namespace base {
class FilePath;
} // namespace base
namespace vm_tools {
namespace apps {
enum VmType : int;
}
namespace concierge {
namespace internal {
std::string GetDevConfPath(apps::VmType type);
int64_t GetVmMemoryMiBInternal(int64_t system, bool is_32bit);
} // namespace internal
enum VmInfo_VmType : int;
// Path to process file descriptors.
constexpr char kProcFileDescriptorsPath[] = "/proc/self/fd/";
// Reserved memory for host when sizing a VM.
constexpr int kHostReservedNumMiB = 1024;
// 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;
};
// 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
// cpu_info_path.
std::optional<int32_t> GetCpuPackageId(int32_t cpu,
const base::FilePath& cpu_info_path);
// Retrieves the CPU capacity property for |cpu| from cpu_info_path.
std::optional<int32_t> GetCpuCapacity(int32_t cpu,
const base::FilePath& cpu_info_path);
// 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(
const std::string& socket_path, std::optional<base::TimeDelta> timeout);
// Returns the guest working set from the virtio-balloon device.
std::optional<BalloonWorkingSet> GetBalloonWorkingSet(
const std::string& socket_path);
// Attach a host network tap device with tap_name.
bool AttachNetDevice(const std::string& socket_path,
const std::string& tap_name,
uint8_t* out_bus);
// Detach a host network tap device on guest attached at bus.
bool DetachNetDevice(const std::string& socket_path, uint8_t bus);
// Attaches an usb device at host |bus|:|addr|, with |vid|, |pid| and an
// opened |fd|.
bool AttachUsbDevice(const std::string& socket_path,
uint8_t bus,
uint8_t addr,
uint16_t vid,
uint16_t pid,
int fd,
uint8_t* out_port,
apps::VmType vm_type);
// Attaches and shares a security key from the given opened |hidraw_fd|.
bool AttachKey(const std::string& socket_path,
int hidraw_fd,
uint8_t* out_port,
apps::VmType vm_type);
// Detaches the usb device at guest |port|.
bool DetachUsbDevice(const std::string& socket_path, uint8_t port);
// Lists all usb devices attached to guest.
bool ListUsbDevice(const std::string& socket_path,
std::vector<UsbDeviceEntry>* devices);
// Resizes the disk identified by |disk_index| to |new_size| in bytes.
bool CrosvmDiskResize(const 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);
// Returns the new vCPU count to use in a Borealis VM
uint32_t GetBorealisCpuCountOverride(uint32_t num_cpus);
// 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 the given |type| to the legacy VM type defined in
// vm_concierge/concierge.pb
VmInfo_VmType ToLegacyVmType(apps::VmType type);
// Convert the given VM |status| to public protobuf variant
// vm_tools::concierge::VmStatus
VmStatus ToVmStatus(VmBaseImpl::Status status);
// Converts VM |info| to public protobuf variant vm_tools::concierge::VmInfo.
// If |fill_sensitive_info| is true then sensitive data, such as permission
// token, will be included in the result. Typically sensitive data is excluded
// when result of the conversion is used in DBus signals, and included in
// results of DBus method calls.
VmInfo ToVmInfo(const VmBaseImpl::Info& info, bool fill_sensitive_info);
class CustomParametersForDev {
public:
// By default this class would do nothing.
CustomParametersForDev() = default;
CustomParametersForDev(const CustomParametersForDev&) = delete;
CustomParametersForDev& operator=(CustomParametersForDev&) = delete;
// 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) const;
std::vector<const std::string> ObtainSpecialParameters(
const std::string& key) const;
// Parameters before crosvm command itself. Useful for adding things like
// strace.
std::vector<std::string> ObtainPrecrosvmParams() const {
return precrosvm_params_;
}
// Append to parameters between crosvm's `crosvm` and `run` command.
void AppendPrerunParams(base::StringPairs& pre_run_args) const;
private:
// Command line parameters before `run` option.
base::StringPairs prerun_params_{};
// Command line before `crosvm`
std::vector<std::string> precrosvm_params_{};
// Command line parameter prefix to 'crosvm run' to remove.
std::vector<std::string> run_prefix_to_remove_{};
// Command line parameters for 'crosvm run' to prepend.
base::StringPairs run_params_to_prepend_{};
// Command line parameters to 'crosvm run' to add.
base::StringPairs run_params_to_add_{};
// Other special handling.
std::map<std::string, std::vector<const std::string>> special_parameters_{};
bool initialized_{false};
};
// Load file for custom parameters for dev systems. Or return nullptr when not
// available.
// |type| Vm type used to specify what file name to use.
// |use_dev_conf| Whether to allow use of dev_conf, sent via request.
std::unique_ptr<CustomParametersForDev> MaybeLoadCustomParametersForDev(
apps::VmType type, bool use_dev_conf = true);
// 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 {
enum class Cache {
kAuto,
kAlways,
kNever,
};
std::string to_string() const;
base::FilePath data_dir;
std::string tag;
std::string uid_map;
std::string gid_map;
Cache enable_caches;
bool rewrite_security_xattrs{true};
bool ascii_casefold;
bool posix_acl;
std::vector<uid_t> privileged_quota_uids;
};
// Creates the font-specific shared data parameter for crosvm.
SharedDataParam 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;
VmStartChecker(const VmStartChecker&) = delete;
VmStartChecker& operator=(const VmStartChecker&) = delete;
// 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_