diff --git a/src/agent/protocols/protos/agent.proto b/src/agent/protocols/protos/agent.proto index 6cbf5a28ca..c29ef7ebc4 100644 --- a/src/agent/protocols/protos/agent.proto +++ b/src/agent/protocols/protos/agent.proto @@ -66,6 +66,7 @@ service AgentService { rpc SetGuestDateTime(SetGuestDateTimeRequest) returns (google.protobuf.Empty); rpc CopyFile(CopyFileRequest) returns (google.protobuf.Empty); rpc GetOOMEvent(GetOOMEventRequest) returns (OOMEvent); + rpc AddSwap(AddSwapRequest) returns (google.protobuf.Empty); } message CreateContainerRequest { @@ -503,6 +504,10 @@ message OOMEvent { string container_id = 1; } +message AddSwapRequest { + repeated uint32 PCIPath = 1; +} + message GetMetricsRequest {} message Metrics { diff --git a/src/agent/src/device.rs b/src/agent/src/device.rs index 60a02147a0..5fd85d4bb0 100644 --- a/src/agent/src/device.rs +++ b/src/agent/src/device.rs @@ -62,7 +62,7 @@ pub fn online_device(path: &str) -> Result<()> { // the sysfs path for the PCI host bridge, based on the PCI path // provided. #[instrument] -fn pcipath_to_sysfs(root_bus_sysfs: &str, pcipath: &pci::Path) -> Result { +pub fn pcipath_to_sysfs(root_bus_sysfs: &str, pcipath: &pci::Path) -> Result { let mut bus = "0000:00".to_string(); let mut relpath = String::new(); diff --git a/src/agent/src/rpc.rs b/src/agent/src/rpc.rs index 518597f5c8..ba08c8cf5d 100644 --- a/src/agent/src/rpc.rs +++ b/src/agent/src/rpc.rs @@ -3,11 +3,14 @@ // SPDX-License-Identifier: Apache-2.0 // +use crate::pci; use async_trait::async_trait; use rustjail::{pipestream::PipeStream, process::StreamType}; use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf}; use tokio::sync::Mutex; +use std::ffi::CString; +use std::io; use std::path::Path; use std::sync::Arc; use ttrpc::{ @@ -20,8 +23,9 @@ use anyhow::{anyhow, Context, Result}; use oci::{LinuxNamespace, Root, Spec}; use protobuf::{RepeatedField, SingularPtrField}; use protocols::agent::{ - AgentDetails, CopyFileRequest, GuestDetailsResponse, Interfaces, Metrics, OOMEvent, - ReadStreamResponse, Routes, StatsContainerResponse, WaitProcessResponse, WriteStreamResponse, + AddSwapRequest, AgentDetails, CopyFileRequest, GuestDetailsResponse, Interfaces, Metrics, + OOMEvent, ReadStreamResponse, Routes, StatsContainerResponse, WaitProcessResponse, + WriteStreamResponse, }; use protocols::empty::Empty; use protocols::health::{ @@ -40,7 +44,7 @@ use nix::sys::stat; use nix::unistd::{self, Pid}; use rustjail::process::ProcessOperations; -use crate::device::{add_devices, rescan_pci_bus, update_device_cgroup}; +use crate::device::{add_devices, pcipath_to_sysfs, rescan_pci_bus, update_device_cgroup}; use crate::linux_abi::*; use crate::metrics::get_metrics; use crate::mount::{add_storages, remove_mounts, BareMount, STORAGE_HANDLER_LIST}; @@ -59,7 +63,7 @@ use tracing_opentelemetry::OpenTelemetrySpanExt; use tracing::instrument; -use libc::{self, c_ushort, pid_t, winsize, TIOCSWINSZ}; +use libc::{self, c_char, c_ushort, pid_t, winsize, TIOCSWINSZ}; use std::convert::TryFrom; use std::fs; use std::os::unix::prelude::PermissionsExt; @@ -1192,6 +1196,18 @@ impl protocols::agent_ttrpc::AgentService for AgentService { Err(ttrpc_error(ttrpc::Code::INTERNAL, "")) } + + async fn add_swap( + &self, + ctx: &TtrpcContext, + req: protocols::agent::AddSwapRequest, + ) -> ttrpc::Result { + trace_rpc_call!(ctx, "add_swap", req); + + do_add_swap(&req).map_err(|e| ttrpc_error(ttrpc::Code::INTERNAL, e.to_string()))?; + + Ok(Empty::new()) + } } #[derive(Clone)] @@ -1541,6 +1557,56 @@ fn do_copy_file(req: &CopyFileRequest) -> Result<()> { Ok(()) } +pub fn path_name_lookup + std::fmt::Debug>( + path: P, + lookup: &str, +) -> Result<(PathBuf, String)> { + for entry in fs::read_dir(path.clone())? { + let entry = entry?; + if let Some(name) = entry.path().file_name() { + if let Some(name) = name.to_str() { + if Some(0) == name.find(lookup) { + return Ok((entry.path(), name.to_string())); + } + } + } + } + Err(anyhow!("cannot get {} dir in {:?}", lookup, path)) +} + +fn do_add_swap(req: &AddSwapRequest) -> Result<()> { + // re-scan PCI bus + // looking for hidden devices + rescan_pci_bus().context("Could not rescan PCI bus")?; + + let mut slots = Vec::new(); + for slot in &req.PCIPath { + slots.push(pci::Slot::new(*slot as u8)?); + } + let pcipath = pci::Path::new(slots)?; + let root_bus_sysfs = format!("{}{}", SYSFS_DIR, create_pci_root_bus_path()); + let sysfs_rel_path = format!( + "{}{}", + root_bus_sysfs, + pcipath_to_sysfs(&root_bus_sysfs, &pcipath)? + ); + let (mut virtio_path, _) = path_name_lookup(sysfs_rel_path, "virtio")?; + virtio_path.push("block"); + let (_, dev_name) = path_name_lookup(virtio_path, "vd")?; + let dev_name = format!("/dev/{}", dev_name); + + let c_str = CString::new(dev_name)?; + let ret = unsafe { libc::swapon(c_str.as_ptr() as *const c_char, 0) }; + if ret != 0 { + return Err(anyhow!( + "libc::swapon get error {}", + io::Error::last_os_error() + )); + } + + Ok(()) +} + // Setup container bundle under CONTAINER_BASE, which is cleaned up // before removing a container. // - bundle path is ///