Skip to content

Commit b285b07

Browse files
committed
sandbox: Optimize sandbox lifecycle management
1. Add many roll back handlers in the process of starting sandbox. 2. Move setup network to Start sandbox as it should be called near the vm startup. 3. Only monit running sandbox and dump it status in time. 4. Sandbox stop should make sure the sandbox is stopped successfully, so wait it to stop for 10s. 5. If the VM process terminated suddenly and the containerd has no idea to stop sandbox, destroy network should be done in monit thread. 6. Forcefully kill vmm process in stopping sandbox. Signed-off-by: Zhang Tianyang <[email protected]>
1 parent 2b34a6e commit b285b07

File tree

3 files changed

+224
-121
lines changed

3 files changed

+224
-121
lines changed

vmm/sandbox/src/cloud_hypervisor/mod.rs

+69-17
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
use std::{os::unix::io::RawFd, process::Stdio};
17+
use std::{os::unix::io::RawFd, process::Stdio, time::Duration};
1818

1919
use anyhow::anyhow;
2020
use async_trait::async_trait;
2121
use containerd_sandbox::error::{Error, Result};
22-
use log::{debug, error, info};
22+
use log::{debug, error, info, warn};
23+
use nix::{errno::Errno::ESRCH, sys::signal, unistd::Pid};
2324
use serde::{Deserialize, Serialize};
2425
use time::OffsetDateTime;
2526
use tokio::{
@@ -41,10 +42,10 @@ use crate::{
4142
},
4243
},
4344
device::{BusType, DeviceInfo},
44-
impl_recoverable, load_config,
45+
load_config,
4546
param::ToCmdLineParams,
4647
sandbox::KuasarSandboxer,
47-
utils::{read_std, set_cmd_fd, set_cmd_netns, wait_pid, write_file_atomic},
48+
utils::{read_std, set_cmd_fd, set_cmd_netns, wait_channel, wait_pid, write_file_atomic},
4849
vm::{Pids, VcpuThreads, VM},
4950
};
5051

@@ -145,13 +146,25 @@ impl CloudHypervisorVM {
145146
self.fds.push(fd);
146147
self.fds.len() - 1 + 3
147148
}
149+
150+
async fn wait_stop(&mut self, t: Duration) -> Result<()> {
151+
if let Some(rx) = self.wait_channel().await {
152+
let (_, ts) = *rx.borrow();
153+
if ts == 0 {
154+
wait_channel(t, rx).await?;
155+
}
156+
}
157+
Ok(())
158+
}
148159
}
149160

150161
#[async_trait]
151162
impl VM for CloudHypervisorVM {
152163
async fn start(&mut self) -> Result<u32> {
153164
create_dir_all(&self.base_dir).await?;
154165
let virtiofsd_pid = self.start_virtiofsd().await?;
166+
// TODO: add child virtiofsd process
167+
self.pids.affiliated_pids.push(virtiofsd_pid);
155168
let mut params = self.config.to_cmdline_params("--");
156169
for d in self.devices.iter() {
157170
params.extend(d.to_cmdline_params("--"));
@@ -174,31 +187,57 @@ impl VM for CloudHypervisorVM {
174187
.spawn()
175188
.map_err(|e| anyhow!("failed to spawn cloud hypervisor command: {}", e))?;
176189
let pid = child.id();
190+
self.pids.vmm_pid = pid;
177191
let pid_file = format!("{}/pid", self.base_dir);
178-
let (tx, rx) = tokio::sync::watch::channel((0u32, 0i128));
192+
let (tx, rx) = channel((0u32, 0i128));
193+
self.wait_chan = Some(rx);
179194
spawn_wait(
180195
child,
181196
format!("cloud-hypervisor {}", self.id),
182197
Some(pid_file),
183198
Some(tx),
184199
);
185-
self.client = Some(self.create_client().await?);
186-
self.wait_chan = Some(rx);
187200

188-
// update vmm related pids
189-
self.pids.vmm_pid = pid;
190-
self.pids.affiliated_pids.push(virtiofsd_pid);
191-
// TODO: add child virtiofsd process
201+
match self.create_client().await {
202+
Ok(client) => self.client = Some(client),
203+
Err(e) => {
204+
if let Err(re) = self.stop(true).await {
205+
warn!("roll back in create clh api client: {}", re);
206+
return Err(e);
207+
}
208+
return Err(e);
209+
}
210+
};
192211
Ok(pid.unwrap_or_default())
193212
}
194213

195214
async fn stop(&mut self, force: bool) -> Result<()> {
196-
let pid = self.pid()?;
197-
if pid == 0 {
198-
return Ok(());
215+
let signal = if force {
216+
signal::SIGKILL
217+
} else {
218+
signal::SIGTERM
219+
};
220+
221+
let pids = self.pids();
222+
if let Some(vmm_pid) = pids.vmm_pid {
223+
if vmm_pid > 0 {
224+
// TODO: Consider pid reused
225+
match signal::kill(Pid::from_raw(vmm_pid as i32), signal) {
226+
Err(e) => {
227+
if e != ESRCH {
228+
return Err(anyhow!("kill vmm process {}: {}", vmm_pid, e).into());
229+
}
230+
}
231+
Ok(_) => self.wait_stop(Duration::from_secs(10)).await?,
232+
}
233+
}
234+
}
235+
for affiliated_pid in pids.affiliated_pids {
236+
if affiliated_pid > 0 {
237+
// affiliated process may exits automatically, so it's ok not handle error
238+
signal::kill(Pid::from_raw(affiliated_pid as i32), signal).unwrap_or_default();
239+
}
199240
}
200-
let signal = if force { 9 } else { 15 };
201-
unsafe { nix::libc::kill(pid as i32, signal) };
202241

203242
Ok(())
204243
}
@@ -288,7 +327,20 @@ impl VM for CloudHypervisorVM {
288327
}
289328
}
290329

291-
impl_recoverable!(CloudHypervisorVM);
330+
#[async_trait]
331+
impl crate::vm::Recoverable for CloudHypervisorVM {
332+
async fn recover(&mut self) -> Result<()> {
333+
self.client = Some(self.create_client().await?);
334+
let pid = self.pid()?;
335+
let (tx, rx) = channel((0u32, 0i128));
336+
tokio::spawn(async move {
337+
let wait_result = wait_pid(pid as i32).await;
338+
tx.send(wait_result).unwrap_or_default();
339+
});
340+
self.wait_chan = Some(rx);
341+
Ok(())
342+
}
343+
}
292344

293345
macro_rules! read_stdio {
294346
($stdio:expr, $cmd_name:ident) => {

vmm/sandbox/src/network/link.rs

+9-14
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,11 @@ async fn get_pci_driver(bdf: &str) -> Result<String> {
574574
}
575575

576576
async fn bind_device_to_driver(driver: &str, bdf: &str) -> Result<()> {
577+
// 0. Check the current driver
578+
if get_pci_driver(bdf).await? == driver {
579+
return Ok(());
580+
}
581+
577582
// 1. Switch the device driver
578583
let driver_override_path = format!("/sys/bus/pci/devices/{}/driver_override", bdf);
579584
write_file_async(&driver_override_path, driver).await?;
@@ -589,23 +594,13 @@ async fn bind_device_to_driver(driver: &str, bdf: &str) -> Result<()> {
589594
write_file_async(probe_path, bdf).await?;
590595

591596
// 4. Check the result
592-
let driver_link = format!("/sys/bus/pci/devices/{}/driver", bdf);
593-
let driver_path = tokio::fs::read_link(&*driver_link).await?;
594-
595-
let result_driver = driver_path.file_name().ok_or(anyhow!(
596-
"failed to get driver name from {}",
597-
driver_path.display()
598-
))?;
599-
let result_driver = result_driver.to_str().ok_or(anyhow!(
600-
"failed to convert the driver {} to str",
601-
result_driver.to_string_lossy()
602-
))?;
597+
let result_driver = get_pci_driver(bdf).await?;
603598
if result_driver != driver {
604599
return Err(anyhow!(
605-
"device {} driver is {} after executing bind to {}",
600+
"device {} driver is expected to {} but got to {}",
606601
bdf,
607-
result_driver,
608-
driver
602+
driver,
603+
result_driver
609604
)
610605
.into());
611606
}

0 commit comments

Comments
 (0)