mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
This patch has been cherry-picked from the Firecracker tree. The reference commit is 1cc8b8a678eb28b20f5843556bdb7fbb2dfa6284. Fixed a logical error in the vsock flow control, that would cause credit update packets to not be sent at the right time. Signed-off-by: Dan Horobeanu <dhr@amazon.com> Signed-off-by: Gabriel Ionescu <gbi@amazon.com> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
130 lines
4.0 KiB
Rust
130 lines
4.0 KiB
Rust
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
/// This module implements our vsock connection state machine. The heavy lifting is done by
|
|
/// `connection::VsockConnection`, while this file only defines some constants and helper structs.
|
|
///
|
|
mod connection;
|
|
mod txbuf;
|
|
|
|
pub use connection::VsockConnection;
|
|
|
|
pub mod defs {
|
|
/// Vsock connection TX buffer capacity.
|
|
pub const CONN_TX_BUF_SIZE: u32 = 64 * 1024;
|
|
|
|
/// When the guest thinks we have less than this amount of free buffer space,
|
|
/// we will send them a credit update packet.
|
|
pub const CONN_CREDIT_UPDATE_THRESHOLD: u32 = 4 * 1024;
|
|
|
|
/// Connection request timeout, in millis.
|
|
pub const CONN_REQUEST_TIMEOUT_MS: u64 = 2000;
|
|
|
|
/// Connection graceful shutdown timeout, in millis.
|
|
pub const CONN_SHUTDOWN_TIMEOUT_MS: u64 = 2000;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
/// Attempted to push data to a full TX buffer.
|
|
TxBufFull,
|
|
/// An I/O error occurred, when attempting to flush the connection TX buffer.
|
|
TxBufFlush(std::io::Error),
|
|
/// An I/O error occurred, when attempting to write data to the host-side stream.
|
|
StreamWrite(std::io::Error),
|
|
}
|
|
|
|
type Result<T> = std::result::Result<T, Error>;
|
|
|
|
/// A vsock connection state.
|
|
///
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub enum ConnState {
|
|
/// The connection has been initiated by the host end, but is yet to be confirmed by the guest.
|
|
LocalInit,
|
|
/// The connection has been initiated by the guest, but we are yet to confirm it, by sending
|
|
/// a response packet (VSOCK_OP_RESPONSE).
|
|
PeerInit,
|
|
/// The connection handshake has been performed successfully, and data can now be exchanged.
|
|
Established,
|
|
/// The host (AF_UNIX) socket was closed.
|
|
LocalClosed,
|
|
/// A VSOCK_OP_SHUTDOWN packet was received from the guest. The tuple represents the guest R/W
|
|
/// indication: (will_not_recv_anymore_data, will_not_send_anymore_data).
|
|
PeerClosed(bool, bool),
|
|
/// The connection is scheduled to be forcefully terminated as soon as possible.
|
|
Killed,
|
|
}
|
|
|
|
/// An RX indication, used by `VsockConnection` to schedule future `recv_pkt()` responses.
|
|
/// For instance, after being notified that there is available data to be read from the host stream
|
|
/// (via `notify()`), the connection will store a `PendingRx::Rw` to be later inspected by
|
|
/// `recv_pkt()`.
|
|
///
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
enum PendingRx {
|
|
/// We need to yield a connection request packet (VSOCK_OP_REQUEST).
|
|
Request = 0,
|
|
/// We need to yield a connection response packet (VSOCK_OP_RESPONSE).
|
|
Response = 1,
|
|
/// We need to yield a forceful connection termination packet (VSOCK_OP_RST).
|
|
Rst = 2,
|
|
/// We need to yield a data packet (VSOCK_OP_RW), by reading from the AF_UNIX socket.
|
|
Rw = 3,
|
|
/// We need to yield a credit update packet (VSOCK_OP_CREDIT_UPDATE).
|
|
CreditUpdate = 4,
|
|
}
|
|
impl PendingRx {
|
|
/// Transform the enum value into a bitmask, that can be used for set operations.
|
|
///
|
|
fn into_mask(self) -> u16 {
|
|
1u16 << (self as u16)
|
|
}
|
|
}
|
|
|
|
/// A set of RX indications (`PendingRx` items).
|
|
///
|
|
struct PendingRxSet {
|
|
data: u16,
|
|
}
|
|
|
|
impl PendingRxSet {
|
|
/// Insert an item into the set.
|
|
///
|
|
fn insert(&mut self, it: PendingRx) {
|
|
self.data |= it.into_mask();
|
|
}
|
|
|
|
/// Remove an item from the set and return:
|
|
/// - true, if the item was in the set; or
|
|
/// - false, if the item wasn't in the set.
|
|
///
|
|
fn remove(&mut self, it: PendingRx) -> bool {
|
|
let ret = self.contains(it);
|
|
self.data &= !it.into_mask();
|
|
ret
|
|
}
|
|
|
|
/// Check if an item is present in this set.
|
|
///
|
|
fn contains(&self, it: PendingRx) -> bool {
|
|
self.data & it.into_mask() != 0
|
|
}
|
|
|
|
/// Check if the set is empty.
|
|
///
|
|
fn is_empty(&self) -> bool {
|
|
self.data == 0
|
|
}
|
|
}
|
|
|
|
/// Create a set containing only one item.
|
|
///
|
|
impl From<PendingRx> for PendingRxSet {
|
|
fn from(it: PendingRx) -> Self {
|
|
Self {
|
|
data: it.into_mask(),
|
|
}
|
|
}
|
|
}
|