xref: /cloud-hypervisor/virtio-devices/src/vsock/csm/mod.rs (revision 20296e909a88ab1dd9d94b6bd2160ca84c430f5f)
1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 //! This module implements our vsock connection state machine. The heavy lifting is done by
5 //! `connection::VsockConnection`, while this file only defines some constants and helper structs.
6 
7 use thiserror::Error;
8 
9 mod connection;
10 mod txbuf;
11 
12 pub use connection::VsockConnection;
13 
14 pub mod defs {
15     /// Vsock connection TX buffer capacity.
16     pub const CONN_TX_BUF_SIZE: u32 = 64 * 1024;
17 
18     /// When the guest thinks we have less than this amount of free buffer space,
19     /// we will send them a credit update packet.
20     pub const CONN_CREDIT_UPDATE_THRESHOLD: u32 = 4 * 1024;
21 
22     /// Connection request timeout, in millis.
23     pub const CONN_REQUEST_TIMEOUT_MS: u64 = 2000;
24 
25     /// Connection graceful shutdown timeout, in millis.
26     pub const CONN_SHUTDOWN_TIMEOUT_MS: u64 = 2000;
27 }
28 
29 #[derive(Debug, Error)]
30 pub enum Error {
31     /// Attempted to push data to a full TX buffer.
32     #[error("TX buffer full")]
33     TxBufFull,
34     /// An I/O error occurred, when attempting to flush the connection TX buffer.
35     #[error("Error flushing TX buffer: {0}")]
36     TxBufFlush(#[source] std::io::Error),
37     /// An I/O error occurred, when attempting to write data to the host-side stream.
38     #[error("Error writing to host side stream: {0}")]
39     StreamWrite(#[source] std::io::Error),
40 }
41 
42 type Result<T> = std::result::Result<T, Error>;
43 
44 /// A vsock connection state.
45 ///
46 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
47 pub enum ConnState {
48     /// The connection has been initiated by the host end, but is yet to be confirmed by the guest.
49     LocalInit,
50     /// The connection has been initiated by the guest, but we are yet to confirm it, by sending
51     /// a response packet (VSOCK_OP_RESPONSE).
52     PeerInit,
53     /// The connection handshake has been performed successfully, and data can now be exchanged.
54     Established,
55     /// The host (AF_UNIX) socket was closed.
56     LocalClosed,
57     /// A VSOCK_OP_SHUTDOWN packet was received from the guest. The tuple represents the guest R/W
58     /// indication: (will_not_recv_anymore_data, will_not_send_anymore_data).
59     PeerClosed(bool, bool),
60     /// The connection is scheduled to be forcefully terminated as soon as possible.
61     Killed,
62 }
63 
64 /// An RX indication, used by `VsockConnection` to schedule future `recv_pkt()` responses.
65 /// For instance, after being notified that there is available data to be read from the host stream
66 /// (via `notify()`), the connection will store a `PendingRx::Rw` to be later inspected by
67 /// `recv_pkt()`.
68 ///
69 #[derive(Clone, Copy, PartialEq, Eq)]
70 enum PendingRx {
71     /// We need to yield a connection request packet (VSOCK_OP_REQUEST).
72     Request = 0,
73     /// We need to yield a connection response packet (VSOCK_OP_RESPONSE).
74     Response = 1,
75     /// We need to yield a forceful connection termination packet (VSOCK_OP_RST).
76     Rst = 2,
77     /// We need to yield a data packet (VSOCK_OP_RW), by reading from the AF_UNIX socket.
78     Rw = 3,
79     /// We need to yield a credit update packet (VSOCK_OP_CREDIT_UPDATE).
80     CreditUpdate = 4,
81 }
82 impl PendingRx {
83     /// Transform the enum value into a bitmask, that can be used for set operations.
84     ///
85     fn into_mask(self) -> u16 {
86         1u16 << (self as u16)
87     }
88 }
89 
90 /// A set of RX indications (`PendingRx` items).
91 ///
92 struct PendingRxSet {
93     data: u16,
94 }
95 
96 impl PendingRxSet {
97     /// Insert an item into the set.
98     ///
99     fn insert(&mut self, it: PendingRx) {
100         self.data |= it.into_mask();
101     }
102 
103     /// Remove an item from the set and return:
104     /// - true, if the item was in the set; or
105     /// - false, if the item wasn't in the set.
106     ///
107     fn remove(&mut self, it: PendingRx) -> bool {
108         let ret = self.contains(it);
109         self.data &= !it.into_mask();
110         ret
111     }
112 
113     /// Check if an item is present in this set.
114     ///
115     fn contains(&self, it: PendingRx) -> bool {
116         self.data & it.into_mask() != 0
117     }
118 
119     /// Check if the set is empty.
120     ///
121     fn is_empty(&self) -> bool {
122         self.data == 0
123     }
124 }
125 
126 /// Create a set containing only one item.
127 ///
128 impl From<PendingRx> for PendingRxSet {
129     fn from(it: PendingRx) -> Self {
130         Self {
131             data: it.into_mask(),
132         }
133     }
134 }
135