Skip to content

feat: Refactor VSOCK - #11322

Draft
Bownairo wants to merge 18 commits into
masterfrom
eero/vsock-revamp
Draft

feat: Refactor VSOCK#11322
Bownairo wants to merge 18 commits into
masterfrom
eero/vsock-revamp

Conversation

@Bownairo

@Bownairo Bownairo commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Restructure the VSOCK code to make it easier to test and build off of, for future use in the type 4.X nodes with more than one GuestOS.

@github-actions github-actions Bot added the feat label Aug 26, 2026
Comment thread rs/ic_os/vsock/src/bin/guest.rs
Comment thread rs/ic_os/vsock/src/bin/guest.rs
Comment thread rs/ic_os/vsock/src/bin/guest.rs Outdated
match response {
Ok(Payload::HostOSVsockVersion(version)) => println!("{version}"),
Ok(Payload::HostOSVersion(version)) => println!("{version}"),
Err(error) => bail!("Server responded with error: '{error}'"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When printing anyhow errors. use {:?}, otherwise only the top error is printed, never the cause (in our case "sending command", nothing else)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually annoying to wire this up. Can I argue that this level is only transport errors, so the command itself matters less, and it's in the binary, so the command that was run should be more obvious?

Ok(())
}

// Adapted from rs/http_endpoints/async_utils/src/lib.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is it useful as a comment? If the don't have to be kept in sync, maybe we don't really need to know where it originated from (especially because it's a pretty common pattern).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like it might be nice to re-use the lib at some point, I just didn't want to pull in the logger. How about I turn this into a TODO with that note instead?

}

// Skip logging to host if manual recovery TUI is running to avoid interfering with the display
if procfs::process::all_processes().is_ok_and(|processes| {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, this is a bit ugly 🫣 (but no need to fix it now)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed 🙈.

Comment thread rs/ic_os/vsock/src/server/misc.rs Outdated
Comment thread rs/ic_os/vsock/src/server/mod.rs Outdated
use upgrade::{start_upgrade_guest_vm, upgrade_hostos};

use vsock::{VMADDR_CID_ANY, VsockAddr, VsockListener, VsockStream};
use ic_http_utils::file_downloader::FileDownloadError;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: any reason for splitting the imports into groups?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly follow the groups here, but maybe it's just a (bad) habit at this point 😬.

Err(error) => {
println!("Error converting bytes to string: {error}");
return Err(io::Error::new(io::ErrorKind::InvalidData, error));
drop(self.listener);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it important to control the drop order? If so, could you add a comment why it's important to drop the listener first?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be dropped so that the socket is closed & new tasks are not added to the tracker. Otherwise it might hang forever.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the above loop no longer runs, no more tasks are added to the tracker. The only thing the Drop implementation does is closing the vsock socket fd and thereby rejecting connection requests right away (instead of making the kernel buffer them until the end of the method is reached when the listener is dropped anyway). I don't mind having it but if we actually use it as an optimization, it's worth mentioning it in a comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started basing this off ic-bn-lib so I'm sure it's a little overkill. Still, I've left it with a comment because a few of the timeouts are pretty long.

Comment thread rs/ic_os/vsock/src/server/mod.rs Outdated
}
.map_err(|e| {
let error = e.to_string();
println!("{error}");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this can lead to double logging. Usually it's recommended to do exactly one of logging or returning an error.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any errors from the command execution we don't return up the stack. Here we print for the server, and write the Response back to the client, so both sides can log. Is that fine?

Comment thread rs/ic_os/vsock/src/server/upgrade.rs Outdated
Ok(_) => {} // systemctl failed, fallthrough to error handling below
Err(err) => return Err(format!("Could not start {GUESTOS_UPGRADER_SERVICE}: {err}")),
};
.output()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider mapping this to an UpgraderService error, too (and perhaps add some context string, like could not start upgrader service)

Comment thread rs/ic_os/vsock/src/server/hsm.rs Outdated
.output();

handle_command_output(command_output)
.output()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error context?

Comment thread rs/ic_os/vsock/src/server/hsm.rs Outdated
Comment thread rs/ic_os/vsock/src/server/misc.rs Outdated
}

for handle in handles {
handle.await??;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error context here too?

Err(error) => {
println!("Error converting bytes to string: {error}");
return Err(io::Error::new(io::ErrorKind::InvalidData, error));
drop(self.listener);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be dropped so that the socket is closed & new tasks are not added to the tracker. Otherwise it might hang forever.

Comment thread rs/ic_os/vsock/src/server/mod.rs Outdated
.take(MAX_MESSAGE_SIZE)
.read_to_end(&mut buffer),
)
.await??;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context

Comment thread rs/ic_os/vsock/src/server/mod.rs Outdated
// As a sanity check, we request that the sender adds its own CID to the message, and that CID must match the CID in the stream peer address.
// NOTE: The kernel vhost driver also enforces this. Any packet with a forged source is dropped.
fn verify_sender_cid(stream: &mut VsockStream, guest_cid: u32) -> Result<(), VsockServerError> {
if stream.peer_addr()?.cid() != guest_cid {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context

Comment thread rs/ic_os/vsock/src/server/upgrade.rs Outdated
let command_output = std::process::Command::new(INSTALL_UPGRADE_FILE_PATH)
.arg(UPGRADE_FILE_PATH)
.output();
.output()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context? :)

Comment thread rs/ic_os/vsock/src/client.rs Outdated

let mut stream = VsockStream::connect_with_cid_port(VMADDR_CID_HOST, self.port)
.map_err(|e| e.to_string())?;
let mut stream = VsockStream::connect_with_cid_port(VMADDR_CID_HOST, self.port)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context :) and below too

Comment thread rs/ic_os/vsock/src/protocol.rs Outdated
Comment thread rs/ic_os/vsock/Cargo.toml Outdated
@blind-oracle

Copy link
Copy Markdown
Contributor

Overall, please add as much context to errors as possible - would ease debuggng in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants