Skip to content

Commit 1de0751

Browse files
committed
virtiofs: split exit-code reporting and VM-exit request
Split guest exit-code reporting from VM-exit signaling in the virtio-fs path. Guest exit status reporting and VM termination are different operations and should not depend on reboot semantics. Keep KRUN_EXIT_CODE_IOCTL for recording the guest exit status, and add KRUN_EXIT_VM_IOCTL for explicitly requesting VM exit. Update init.krun to call sync() and send the explicit VM-exit request instead of rebooting the guest, and thread the exit request through the virtio-fs device/server/worker path so the worker signals exit_evt only for the explicit VM-exit request. This keeps the change focused on the virtio-fs exit protocol and avoids mixing in unrelated init cleanups. Signed-off-by: Zewei Yang <yangzewei@loongson.cn>
1 parent 788cf91 commit 1de0751

8 files changed

Lines changed: 105 additions & 8 deletions

File tree

init/init.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <net/if.h>
1515
#include <sys/ioctl.h>
1616
#include <sys/mount.h>
17+
#include <sys/reboot.h>
1718
#include <sys/resource.h>
1819
#include <sys/socket.h>
1920
#include <sys/stat.h>
@@ -32,6 +33,7 @@
3233

3334
#define KRUN_EXIT_CODE_IOCTL 0x7602
3435
#define KRUN_REMOVE_ROOT_DIR_IOCTL 0x7603
36+
#define KRUN_EXIT_VM_IOCTL 0x7604
3537

3638
#define KRUN_MAGIC "KRUN"
3739
#define KRUN_FOOTER_LEN 12
@@ -1003,6 +1005,41 @@ void set_exit_code(int code)
10031005
close(fd);
10041006
}
10051007

1008+
static void request_vm_exit(void)
1009+
{
1010+
int fd;
1011+
int ret;
1012+
int virtiofs_check;
1013+
1014+
virtiofs_check = is_virtiofs("/");
1015+
if (virtiofs_check < 0) {
1016+
printf("Warning: Could not determine filesystem type for root\n");
1017+
}
1018+
1019+
if (virtiofs_check == 0) {
1020+
return;
1021+
}
1022+
1023+
fd = open("/", O_RDONLY);
1024+
if (fd < 0) {
1025+
perror("Couldn't open root filesystem to request VM exit");
1026+
return;
1027+
}
1028+
1029+
ret = ioctl(fd, KRUN_EXIT_VM_IOCTL, 0);
1030+
if (ret < 0) {
1031+
perror("Error using the ioctl to request VM exit");
1032+
}
1033+
1034+
close(fd);
1035+
}
1036+
1037+
static void shutdown_vm(void)
1038+
{
1039+
sync();
1040+
request_vm_exit();
1041+
}
1042+
10061043
int try_mount(const char *source, const char *target, const char *fstype,
10071044
unsigned long mountflags, const void *data)
10081045
{
@@ -1241,13 +1278,12 @@ int main(int argc, char **argv)
12411278
// Not the first child, ignore it.
12421279
};
12431280

1244-
// The workload's entrypoint has exited, record its exit code and exit
1245-
// ourselves.
12461281
if (WIFEXITED(status)) {
12471282
set_exit_code(WEXITSTATUS(status));
12481283
} else if (WIFSIGNALED(status)) {
12491284
set_exit_code(WTERMSIG(status) + 128);
12501285
}
1286+
shutdown_vm();
12511287
}
12521288

12531289
return 0;

src/devices/src/virtio/fs/device.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use crossbeam_channel::Sender;
33
use std::cmp;
44
use std::io::Write;
5-
use std::sync::atomic::{AtomicI32, AtomicU64, Ordering};
5+
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering};
66
use std::sync::Arc;
77
use std::thread::JoinHandle;
88

@@ -49,6 +49,8 @@ pub struct Fs {
4949
worker_thread: Option<JoinHandle<()>>,
5050
worker_stopfd: EventFd,
5151
exit_code: Arc<AtomicI32>,
52+
exit_request: Arc<AtomicBool>,
53+
exit_evt: EventFd,
5254
#[cfg(target_os = "macos")]
5355
map_sender: Option<Sender<WorkerMessage>>,
5456
}
@@ -58,7 +60,9 @@ impl Fs {
5860
fs_id: String,
5961
shared_dir: String,
6062
exit_code: Arc<AtomicI32>,
63+
exit_request: Arc<AtomicBool>,
6164
allow_root_dir_delete: bool,
65+
exit_evt: EventFd,
6266
) -> super::Result<Fs> {
6367
let avail_features = (1u64 << VIRTIO_F_VERSION_1) | (1u64 << VIRTIO_RING_F_EVENT_IDX);
6468

@@ -83,6 +87,8 @@ impl Fs {
8387
worker_thread: None,
8488
worker_stopfd: EventFd::new(EFD_NONBLOCK).map_err(FsError::EventFd)?,
8589
exit_code,
90+
exit_request,
91+
exit_evt,
8692
#[cfg(target_os = "macos")]
8793
map_sender: None,
8894
})
@@ -185,6 +191,8 @@ impl VirtioDevice for Fs {
185191
self.passthrough_cfg.clone(),
186192
self.worker_stopfd.try_clone().unwrap(),
187193
self.exit_code.clone(),
194+
self.exit_request.clone(),
195+
self.exit_evt.try_clone().unwrap(),
188196
#[cfg(target_os = "macos")]
189197
self.map_sender.clone(),
190198
);

src/devices/src/virtio/fs/filesystem.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::ffi::{CStr, CString};
1313
use std::fs::File;
1414
use std::io;
1515
use std::mem;
16-
use std::sync::atomic::AtomicI32;
16+
use std::sync::atomic::{AtomicBool, AtomicI32};
1717
use std::sync::{Arc, Mutex};
1818
use std::time::Duration;
1919

@@ -1170,6 +1170,7 @@ pub trait FileSystem {
11701170
in_size: u32,
11711171
out_size: u32,
11721172
exit_code: &Arc<AtomicI32>,
1173+
exit_request: &Arc<AtomicBool>,
11731174
) -> io::Result<Vec<u8>> {
11741175
Err(io::Error::from_raw_os_error(bindings::LINUX_ENOSYS))
11751176
}

src/devices/src/virtio/fs/linux/passthrough.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,7 @@ impl FileSystem for PassthroughFs {
21792179
_in_size: u32,
21802180
out_size: u32,
21812181
exit_code: &Arc<AtomicI32>,
2182+
exit_request: &Arc<AtomicBool>,
21822183
) -> io::Result<Vec<u8>> {
21832184
const VIRTIO_IOC_MAGIC: u8 = b'v';
21842185

@@ -2198,6 +2199,10 @@ impl FileSystem for PassthroughFs {
21982199
const VIRTIO_IOC_REMOVE_ROOT_DIR_REQ: u32 =
21992200
request_code_none!(VIRTIO_IOC_MAGIC, VIRTIO_IOC_REMOVE_ROOT_DIR_CODE) as u32;
22002201

2202+
const VIRTIO_IOC_TYPE_EXIT_REQUEST: u8 = 4;
2203+
const VIRTIO_IOC_EXIT_REQUEST_REQ: u32 =
2204+
request_code_none!(VIRTIO_IOC_MAGIC, VIRTIO_IOC_TYPE_EXIT_REQUEST) as u32;
2205+
22012206
match cmd {
22022207
VIRTIO_IOC_EXPORT_FD_REQ => {
22032208
if out_size as usize != VIRTIO_IOC_EXPORT_FD_SIZE {
@@ -2229,9 +2234,15 @@ impl FileSystem for PassthroughFs {
22292234
Ok(ret)
22302235
}
22312236
VIRTIO_IOC_EXIT_CODE_REQ => {
2237+
debug!("virtiofs exit-code ioctl received: {}", arg as i32);
22322238
exit_code.store(arg as i32, Ordering::SeqCst);
22332239
Ok(Vec::new())
22342240
}
2241+
VIRTIO_IOC_EXIT_REQUEST_REQ => {
2242+
debug!("virtiofs explicit-exit ioctl received");
2243+
exit_request.store(true, Ordering::SeqCst);
2244+
Ok(Vec::new())
2245+
}
22352246
VIRTIO_IOC_REMOVE_ROOT_DIR_REQ if self.cfg.allow_root_dir_delete => {
22362247
std::fs::remove_dir_all(&self.cfg.root_dir)?;
22372248
Ok(Vec::new())

src/devices/src/virtio/fs/macos/passthrough.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,17 +2435,23 @@ impl FileSystem for PassthroughFs {
24352435
_in_size: u32,
24362436
_out_size: u32,
24372437
exit_code: &Arc<AtomicI32>,
2438+
exit_request: &Arc<AtomicBool>,
24382439
) -> io::Result<Vec<u8>> {
24392440
// We can't use nix::request_code_none here since it's system-dependent
24402441
// and we need the value from Linux.
24412442
const VIRTIO_IOC_EXIT_CODE_REQ: u32 = 0x7602;
24422443
const VIRTIO_IOC_REMOVE_ROOT_DIR_REQ: u32 = 0x7603;
2444+
const VIRTIO_IOC_EXIT_REQUEST_REQ: u32 = 0x7604;
24432445

24442446
match cmd {
24452447
VIRTIO_IOC_EXIT_CODE_REQ => {
24462448
exit_code.store(arg as i32, Ordering::SeqCst);
24472449
Ok(Vec::new())
24482450
}
2451+
VIRTIO_IOC_EXIT_REQUEST_REQ => {
2452+
exit_request.store(true, Ordering::SeqCst);
2453+
Ok(Vec::new())
2454+
}
24492455
VIRTIO_IOC_REMOVE_ROOT_DIR_REQ if self.cfg.allow_root_dir_delete => {
24502456
std::fs::remove_dir_all(&self.cfg.root_dir)?;
24512457
Ok(Vec::new())

src/devices/src/virtio/fs/server.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::ffi::{CStr, CString};
1212
use std::fs::File;
1313
use std::io::{self, Read, Write};
1414
use std::mem::size_of;
15-
use std::sync::atomic::{AtomicI32, AtomicU64, Ordering};
15+
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering};
1616
use std::sync::Arc;
1717

1818
use vm_memory::ByteValued;
@@ -85,6 +85,7 @@ impl<F: FileSystem + Sync> Server<F> {
8585
w: Writer,
8686
shm_region: &Option<VirtioShmRegion>,
8787
exit_code: &Arc<AtomicI32>,
88+
exit_request: &Arc<AtomicBool>,
8889
#[cfg(target_os = "macos")] map_sender: &Option<Sender<WorkerMessage>>,
8990
) -> Result<usize> {
9091
let in_header: InHeader = r.read_obj().map_err(Error::DecodeMessage)?;
@@ -134,7 +135,9 @@ impl<F: FileSystem + Sync> Server<F> {
134135
x if x == Opcode::Interrupt as u32 => self.interrupt(in_header),
135136
x if x == Opcode::Bmap as u32 => self.bmap(in_header, r, w),
136137
x if x == Opcode::Destroy as u32 => self.destroy(),
137-
x if x == Opcode::Ioctl as u32 => self.ioctl(in_header, r, w, exit_code),
138+
x if x == Opcode::Ioctl as u32 => {
139+
self.ioctl(in_header, r, w, exit_code, exit_request)
140+
}
138141
x if x == Opcode::Poll as u32 => self.poll(in_header, r, w),
139142
x if x == Opcode::NotifyReply as u32 => self.notify_reply(in_header, r, w),
140143
x if x == Opcode::BatchForget as u32 => self.batch_forget(in_header, r, w),
@@ -1185,6 +1188,7 @@ impl<F: FileSystem + Sync> Server<F> {
11851188
mut r: Reader,
11861189
w: Writer,
11871190
exit_code: &Arc<AtomicI32>,
1191+
exit_request: &Arc<AtomicBool>,
11881192
) -> Result<usize> {
11891193
let IoctlIn {
11901194
fh,
@@ -1205,6 +1209,7 @@ impl<F: FileSystem + Sync> Server<F> {
12051209
in_size,
12061210
out_size,
12071211
exit_code,
1212+
exit_request,
12081213
) {
12091214
Ok(data) => {
12101215
let out = IoctlOut {

src/devices/src/virtio/fs/worker.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crossbeam_channel::Sender;
44
use utils::worker_message::WorkerMessage;
55

66
use std::os::fd::AsRawFd;
7-
use std::sync::atomic::AtomicI32;
7+
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
88
use std::sync::Arc;
99
use std::thread;
1010

@@ -28,6 +28,8 @@ pub struct FsWorker {
2828
server: Server<PassthroughFs>,
2929
stop_fd: EventFd,
3030
exit_code: Arc<AtomicI32>,
31+
exit_request: Arc<AtomicBool>,
32+
exit_evt: EventFd,
3133
#[cfg(target_os = "macos")]
3234
map_sender: Option<Sender<WorkerMessage>>,
3335
}
@@ -43,6 +45,8 @@ impl FsWorker {
4345
passthrough_cfg: passthrough::Config,
4446
stop_fd: EventFd,
4547
exit_code: Arc<AtomicI32>,
48+
exit_request: Arc<AtomicBool>,
49+
exit_evt: EventFd,
4650
#[cfg(target_os = "macos")] map_sender: Option<Sender<WorkerMessage>>,
4751
) -> Self {
4852
Self {
@@ -54,6 +58,8 @@ impl FsWorker {
5458
server: Server::new(PassthroughFs::new(passthrough_cfg).unwrap()),
5559
stop_fd,
5660
exit_code,
61+
exit_request,
62+
exit_evt,
5763
#[cfg(target_os = "macos")]
5864
map_sender,
5965
}
@@ -160,6 +166,7 @@ impl FsWorker {
160166
writer,
161167
&self.shm_region,
162168
&self.exit_code,
169+
&self.exit_request,
163170
#[cfg(target_os = "macos")]
164171
&self.map_sender,
165172
) {
@@ -173,6 +180,14 @@ impl FsWorker {
173180
if queue.needs_notification(&self.mem).unwrap() {
174181
self.interrupt.signal_used_queue();
175182
}
183+
184+
if self.exit_request.swap(false, Ordering::SeqCst) {
185+
debug!("virtiofs explicit exit request received; signaling VMM exit event");
186+
if let Err(e) = self.exit_evt.write(1) {
187+
error!("failed to signal VMM exit event: {e}");
188+
}
189+
return;
190+
}
176191
}
177192
}
178193
}

src/vmm/src/builder.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::io::{self, IsTerminal, Read};
1515
use std::os::fd::AsRawFd;
1616
use std::os::fd::{BorrowedFd, FromRawFd};
1717
use std::path::PathBuf;
18-
use std::sync::atomic::AtomicI32;
18+
use std::sync::atomic::{AtomicBool, AtomicI32};
1919
use std::sync::{Arc, Mutex};
2020

2121
use super::{Error, Vmm};
@@ -949,6 +949,8 @@ pub fn build_microvm(
949949

950950
// We use this atomic to record the exit code set by init/init.c in the VM.
951951
let exit_code = Arc::new(AtomicI32::new(i32::MAX));
952+
#[cfg(not(any(feature = "tee", feature = "aws-nitro")))]
953+
let exit_request = Arc::new(AtomicBool::new(false));
952954

953955
let mut vmm = Vmm {
954956
guest_memory,
@@ -1030,6 +1032,13 @@ pub fn build_microvm(
10301032
attach_input_devices(&mut vmm, &vm_resources.input_backends, intc.clone())?;
10311033
}
10321034

1035+
#[cfg(not(any(feature = "tee", feature = "aws-nitro")))]
1036+
let fs_exit_evt = vmm
1037+
.exit_evt
1038+
.try_clone()
1039+
.map_err(Error::EventFd)
1040+
.map_err(StartMicrovmError::Internal)?;
1041+
10331042
#[cfg(not(any(feature = "tee", feature = "aws-nitro")))]
10341043
attach_fs_devices(
10351044
&mut vmm,
@@ -1039,6 +1048,8 @@ pub fn build_microvm(
10391048
export_table,
10401049
intc.clone(),
10411050
exit_code,
1051+
exit_request,
1052+
fs_exit_evt,
10421053
#[cfg(target_os = "macos")]
10431054
_sender,
10441055
)?;
@@ -1880,6 +1891,8 @@ fn attach_fs_devices(
18801891
#[cfg(not(feature = "tee"))] export_table: Option<ExportTable>,
18811892
intc: IrqChip,
18821893
exit_code: Arc<AtomicI32>,
1894+
exit_request: Arc<AtomicBool>,
1895+
exit_evt: EventFd,
18831896
#[cfg(target_os = "macos")] map_sender: Sender<WorkerMessage>,
18841897
) -> std::result::Result<(), StartMicrovmError> {
18851898
use self::StartMicrovmError::*;
@@ -1890,7 +1903,9 @@ fn attach_fs_devices(
18901903
config.fs_id.clone(),
18911904
config.shared_dir.clone(),
18921905
exit_code.clone(),
1906+
exit_request.clone(),
18931907
config.allow_root_dir_delete,
1908+
exit_evt.try_clone().map_err(Error::EventFd).map_err(Internal)?,
18941909
)
18951910
.unwrap(),
18961911
));

0 commit comments

Comments
 (0)