Skip to content

Commit 1f2f059

Browse files
lauraltalxiord
authored andcommitted
check the available space before enqueuing bytes
Check that there is still space in the `in_buffer` before enqueuing bytes. The maximum number of bytes allowed at a time should be equal to `FIFO_SIZE`. Since no bytes are written and an error is returned when the buffer is full, also add a function `fifo_capacity` to query how many bytes can still be written. Signed-off-by: Andreea Florescu <[email protected]> Signed-off-by: Laura Loghin <[email protected]>
1 parent f823520 commit 1f2f059

File tree

3 files changed

+75
-9
lines changed

3 files changed

+75
-9
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
for both serial console and i8042
1414
([#7](https://github.com/rust-vmm/vm-superio/issues/7)).
1515

16+
## Fixed
17+
18+
- Limited the maximum number of bytes allowed at a time, when enqueuing input
19+
for serial, to 64 (FIFO_SIZE) to avoid memory pressure
20+
([#17](https://github.com/rust-vmm/vm-superio/issues/17)).
21+
1622
# v0.1.0
1723

1824
This is the first vm-superio release.

coverage_config_x86_64.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 97.6,
2+
"coverage_score": 97.7,
33
"exclude_path": "",
44
"crate_features": ""
55
}

src/serial.rs

+68-8
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,12 @@ const DEFAULT_SCRATCH: u8 = 0x00;
152152
/// let value = serial.read(0);
153153
///
154154
/// // Send more bytes to the guest in one shot.
155-
/// serial.enqueue_raw_bytes(&[b'a', b'b', b'c']).unwrap();
155+
/// let input = &[b'a', b'b', b'c'];
156+
/// // Before enqueuing bytes we first check if there is enough free space
157+
/// // in the FIFO.
158+
/// if serial.fifo_capacity() >= input.len() {
159+
/// serial.enqueue_raw_bytes(input).unwrap();
160+
/// }
156161
/// ```
157162
pub struct Serial<T: Trigger, W: Write> {
158163
// Some UART registers.
@@ -183,6 +188,8 @@ pub enum Error<E> {
183188
Trigger(E),
184189
/// Couldn't write/flush to the given destination.
185190
IOError(io::Error),
191+
/// No space left in FIFO.
192+
FullFifo,
186193
}
187194

188195
impl<T: Trigger, W: Write> Serial<T, W> {
@@ -407,6 +414,17 @@ impl<T: Trigger, W: Write> Serial<T, W> {
407414
}
408415
}
409416

417+
/// Returns how much space is still available in the FIFO.
418+
///
419+
/// # Example
420+
///
421+
/// You can see an example of how to use this function in the
422+
/// [`Example` section from `Serial`](struct.Serial.html#example).
423+
#[inline]
424+
pub fn fifo_capacity(&self) -> usize {
425+
FIFO_SIZE - self.in_buffer.len()
426+
}
427+
410428
/// Helps in sending more bytes to the guest in one shot, by storing
411429
/// `input` bytes in UART buffer and letting the driver know there is
412430
/// some pending data to be read by setting RDA bit and its corresponding
@@ -415,31 +433,45 @@ impl<T: Trigger, W: Write> Serial<T, W> {
415433
/// # Arguments
416434
/// * `input` - The data to be sent to the guest.
417435
///
436+
/// # Returns
437+
///
438+
/// The function returns the number of bytes it was able to write to the fifo,
439+
/// or `FullFifo` error when the fifo is full. Users can use
440+
/// [`fifo_capacity`](#method.fifo_capacity) before calling this function
441+
/// to check the available space.
442+
///
418443
/// # Example
419444
///
420445
/// You can see an example of how to use this function in the
421446
/// [`Example` section from `Serial`](struct.Serial.html#example).
422-
pub fn enqueue_raw_bytes(&mut self, input: &[u8]) -> Result<(), Error<T::E>> {
447+
pub fn enqueue_raw_bytes(&mut self, input: &[u8]) -> Result<usize, Error<T::E>> {
448+
let mut write_count = 0;
423449
if !self.is_in_loop_mode() {
424-
self.in_buffer.extend(input);
425-
self.set_lsr_rda_bit();
426-
self.received_data_interrupt().map_err(Error::Trigger)?;
450+
if self.fifo_capacity() == 0 {
451+
return Err(Error::FullFifo);
452+
}
453+
write_count = std::cmp::min(self.fifo_capacity(), input.len());
454+
if write_count > 0 {
455+
self.in_buffer.extend(&input[0..write_count]);
456+
self.set_lsr_rda_bit();
457+
self.received_data_interrupt().map_err(Error::Trigger)?;
458+
}
427459
}
428-
Ok(())
460+
Ok(write_count)
429461
}
430462
}
431463

432464
#[cfg(test)]
433465
mod tests {
434466
use super::*;
435-
use std::io::{sink, Error, Result};
467+
use std::io::{sink, Result};
436468

437469
use vmm_sys_util::eventfd::EventFd;
438470

439471
const RAW_INPUT_BUF: [u8; 3] = [b'a', b'b', b'c'];
440472

441473
impl Trigger for EventFd {
442-
type E = Error;
474+
type E = io::Error;
443475

444476
fn trigger(&self) -> Result<()> {
445477
self.write(1)
@@ -638,4 +670,32 @@ mod tests {
638670
// have the same value).
639671
assert_eq!(serial.read(MSR_OFFSET), MSR_DSR_BIT | MSR_CTS_BIT);
640672
}
673+
674+
#[test]
675+
fn test_fifo_max_size() {
676+
let event_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
677+
let mut serial = Serial::new(event_fd, sink());
678+
679+
// Test case: trying to write too many bytes in an empty fifo will just write
680+
// `FIFO_SIZE`. Any other subsequent writes, will return a `FullFifo` error.
681+
let too_many_bytes = vec![1u8; FIFO_SIZE + 1];
682+
let written_bytes = serial.enqueue_raw_bytes(&too_many_bytes).unwrap();
683+
assert_eq!(written_bytes, FIFO_SIZE);
684+
assert_eq!(serial.in_buffer.len(), FIFO_SIZE);
685+
686+
// A subsequent call to `enqueue_raw_bytes` fails because the fifo is
687+
// now full.
688+
let one_byte_input = [1u8];
689+
match serial.enqueue_raw_bytes(&one_byte_input) {
690+
Err(Error::FullFifo) => (),
691+
_ => unreachable!(),
692+
}
693+
694+
// Test case: consuming one byte from a full fifo does not allow writes
695+
// bigger than one byte.
696+
let _ = serial.read(DATA_OFFSET);
697+
let written_bytes = serial.enqueue_raw_bytes(&too_many_bytes[..2]).unwrap();
698+
assert_eq!(written_bytes, 1);
699+
assert_eq!(serial.in_buffer.len(), FIFO_SIZE);
700+
}
641701
}

0 commit comments

Comments
 (0)