Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate initial virtio console implementation #217

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

RaduNiculae
Copy link
Contributor

Added a virtio console device to replace the UART serial console.
The implementation is divided between vm-virtio and vmm-reference.

Signed-off-by: Niculae Radu [email protected]

Summary of the PR

Added a virtio console device with similar queue handling as the block and
network devices in vmm-reference. Part of the implementation is found in
the vm-virtio repository. For now it is linked to my personal repo until it
will be merged.

Requirements

Before submitting your PR, please make sure you addressed the following
requirements:

  • All commits in this PR are signed (with git commit -s), and the commit
    message has max 60 characters for the summary and max 75 characters for each
    description line.
  • All added/changed functionality has a corresponding unit/integration
    test.
  • Any newly added unsafe code is properly documented.

@RaduNiculae RaduNiculae force-pushed the virtio-console branch 3 times, most recently from 9fc51ca to 5cb7cef Compare May 17, 2022 17:25
Copy link
Collaborator

@lauralt lauralt left a comment

Choose a reason for hiding this comment

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

Nice work, I'll finish the review tomorrow.

Cargo.lock Outdated Show resolved Hide resolved
src/devices/src/virtio/console/mod.rs Outdated Show resolved Hide resolved
src/devices/src/virtio/console/inorder_handler.rs Outdated Show resolved Hide resolved
src/devices/src/virtio/console/inorder_handler.rs Outdated Show resolved Hide resolved
src/devices/src/virtio/console/inorder_handler.rs Outdated Show resolved Hide resolved
src/devices/src/virtio/console/inorder_handler.rs Outdated Show resolved Hide resolved
src/devices/src/virtio/console/inorder_handler.rs Outdated Show resolved Hide resolved
@lauralt lauralt changed the title Init virtio console. Integrate initial virtio console implementation Jun 15, 2022
@RaduNiculae RaduNiculae force-pushed the virtio-console branch 5 times, most recently from dcf40ba to dbbcf17 Compare June 17, 2022 11:03
src/devices/src/virtio/console/mod.rs Outdated Show resolved Hide resolved
src/devices/src/virtio/console/mod.rs Show resolved Hide resolved
src/devices/src/virtio/console/device.rs Show resolved Hide resolved
src/devices/src/virtio/console/mod.rs Outdated Show resolved Hide resolved
src/vmm/src/config/mod.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@lauralt lauralt left a comment

Choose a reason for hiding this comment

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

Left a few other comments. Will finish the review early next week.

src/devices/src/virtio/console/inorder_handler.rs Outdated Show resolved Hide resolved
Comment on lines 86 to 106
Ok(used_len) => used_len,
Err(e) => {
self.receiveq.state.go_to_previous_position();
return Err(Error::Console(e));
}
};
if used_len == 0 {
self.receiveq.state.go_to_previous_position();
break;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have to think a bit more about this. I think process_receiveq_chain can fail even if there were some bytes written to the chain, in which case we shouldn't go back one position in the available ring.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this can be achieved by using a write method in process_receiveq_chain that also returns the numbers of bytes successfully written to memory, and return that value even when we fail with an error there (there is something similar done for block https://github.com/rust-vmm/vm-virtio/blob/main/crates/devices/virtio-blk/src/stdio_executor.rs#L339). This is a bit more complicated and requires some dive deep, I think we can postpone it for a future PR of virtio-console.

src/devices/src/virtio/console/inorder_handler.rs Outdated Show resolved Hide resolved
Comment on lines 86 to 106
Ok(used_len) => used_len,
Err(e) => {
self.receiveq.state.go_to_previous_position();
return Err(Error::Console(e));
}
};
if used_len == 0 {
self.receiveq.state.go_to_previous_position();
break;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this can be achieved by using a write method in process_receiveq_chain that also returns the numbers of bytes successfully written to memory, and return that value even when we fail with an error there (there is something similar done for block https://github.com/rust-vmm/vm-virtio/blob/main/crates/devices/virtio-blk/src/stdio_executor.rs#L339). This is a bit more complicated and requires some dive deep, I think we can postpone it for a future PR of virtio-console.

.long("console")
.required(false)
.takes_value(true)
.help("Console configuration. \n\tFormat: \"type=<string>\"")
Copy link
Member

Choose a reason for hiding this comment

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

What are the possible types here? For the other ones it is self explanatory what the value of the configuration is (i.e. a path, kernel_load_addr, etc), but for this one it is not. We should write the possible values in the help here, and also when we are parsing it, we should check that the value is one of the supported ones.

@@ -236,6 +244,7 @@ mod tests {
vcpu_config: VcpuConfig { num: 1 },
block_config: None,
net_config: None,
console_config: None
Copy link
Member

Choose a reason for hiding this comment

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

We should update the test to have a console_config that's being parsed as well. Best would be to have it with all possible values.

];

let config_space = Vec::new();
let virtio_cfg = VirtioConfig::new(device_features, queues, config_space);
Copy link
Member

Choose a reason for hiding this comment

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

nit: too many whitespaces. Why do we need them?

T: Write,
{
pub fn process_transmitq(&mut self) -> result::Result<(), Error> {
// To see why this is done in a loop, please look at the `Queue::enable_notification`
Copy link
Member

Choose a reason for hiding this comment

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

This is very likely to be outdated in the future. Can you add a short description at least for why we do the loop here? You can say that more details can be found in the Queue:enable_notification function, but we should have at least a short description here as well.

src/devices/src/virtio/console/mod.rs Outdated Show resolved Hide resolved
const TRANSMITQ_EVENT: u32 = 1;
const RECEIVEQ_EVENT: u32 = 2;

const STDIN_BUFFER_SIZE: usize = 1024;
Copy link
Member

Choose a reason for hiding this comment

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

Where is this taken from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just a random value. To figure out a good one we should run some tests. I tried with some python tests but I could not measure the time it takes to send data to the driver accurately.

}
}
}
if !failed {
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need this as opposed to just handling the errors as they happen? I think this can be significantly shorten by just returning the error directly.

Comment on lines +76 to +81
if let Err(e) = self.transmitqfd.read() {
error!("Could not read transmitq event fd: {:?}", e);
} else if let Err(e) = self.inner.process_transmitq() {
error!("Transmitq processing failed: {:?}", e);
}
Copy link
Member

Choose a reason for hiding this comment

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

Can you try to use and_then to reduce some of these lines of code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried using and_then/or_else to keep the error messages and it made the code more convoluted. I think this is easier to understand. I will change it if you think it is better that way.

#[derive(Clone, Debug, PartialEq)]
pub struct ConsoleConfig {
/// Type of console.
pub console_type: String,
Copy link
Member

Choose a reason for hiding this comment

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

This should be an enum instead of a String. In the enum we would then have 2 variants: uart and virtio.

Added a virtio console device to replace the UART serial console.
The implementation is divided between vm-virtio and vmm-reference.

Signed-off-by: Niculae Radu <[email protected]>
driver_notify,
receiveq: self.cfg.virtio.queues.remove(0),
transmitq: self.cfg.virtio.queues.remove(0),
console: console::Console::new_with_capacity(console::DEFAULT_CAPACITY, stdout())
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can use here the new constructor or even the Default implementation.

// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause

use crate::virtio::console::CONSOLE_DEVICE_ID;
use crate::virtio::features::{VIRTIO_F_IN_ORDER, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1};
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: can we group together the crate imports, same comment for the external crates (see use virtio_console::console below)?

type Error = ConversionError;

fn try_from(console_cfg_str: &str) -> Result<Self, Self::Error> {
// Supported options: `type=String`
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not correct, we support only type=virtio/console.

@@ -252,6 +306,7 @@ mod tests {
vcpu_config: VcpuConfig { num: 1 },
block_config: None,
net_config: None,
console_config: None
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this be uart?

let console_type_str: String = arg_parser
.value_of("type")
.map_err(ConversionError::new_console)?
.ok_or_else(|| ConversionError::new_console("Missing required argument: type"))?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think type is not required, right? So if the user doesn't pass it, then we set here the default value, or am I missing something?

output_lines = data.split(b'\r\n')
last = output_lines[-1].strip()
if prompt in last:
total_data += data
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be nice to move this fix to a separate commit and explain a bit what is it about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants