Skip to content

Commit 2740cac

Browse files
committed
Fixup vsock unit tests to work with iovec-based packet handling
IoVecBuffer[Mut] statically guarantee that a read-only descriptor chain cannot be written to, and a write-only descriptor chain cannot be read from. Since RX descriptors are write-only and TX descriptors are read-only, this causes some problems in the vsock unit test code, which has to first emulate the guest putting packets into the TX queue (e.g. write to a read-only queue) and also reading the data that the device wrote into the RX queue to validate its correctness (which is a write-only queue). The unit tests however do not differentiate well between which queue a packet belogns to, using one packet that was initialized from the RX queue to both mock RX and TX operations. This commit fixes this somewhat, by differentiating between RX and TX packets. It does not address the separation between guest- and host-view of the respective queues, and instead uses a trick: Both queues point toward the same memory region, which is then interpreted as RX or TX memory depending on whether the test wants to mock a guest operation, or validate a device operation. Signed-off-by: Patrick Roy <[email protected]>
1 parent 32595c8 commit 2740cac

File tree

5 files changed

+232
-262
lines changed

5 files changed

+232
-262
lines changed

src/vmm/src/devices/virtio/vsock/csm/connection.rs

Lines changed: 71 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ mod tests {
694694
use super::super::super::defs::uapi;
695695
use super::super::defs as csm_defs;
696696
use super::*;
697-
use crate::devices::virtio::vsock::device::RXQ_INDEX;
697+
use crate::devices::virtio::vsock::device::{RXQ_INDEX, TXQ_INDEX};
698698
use crate::devices::virtio::vsock::test_utils;
699699
use crate::devices::virtio::vsock::test_utils::TestContext;
700700
use crate::vstate::memory::BitmapSlice;
@@ -850,7 +850,9 @@ mod tests {
850850
#[derive(Debug)]
851851
struct CsmTestContext {
852852
_vsock_test_ctx: TestContext,
853-
pkt: VsockPacket,
853+
// Two views of the same in-memory packet. rx-view for writing, tx-view for reading
854+
rx_pkt: VsockPacket,
855+
tx_pkt: VsockPacket,
854856
conn: VsockConnection<TestStream>,
855857
}
856858

@@ -863,8 +865,14 @@ mod tests {
863865
let vsock_test_ctx = TestContext::new();
864866
let mut handler_ctx = vsock_test_ctx.create_event_handler_context();
865867
let stream = TestStream::new();
866-
let mut pkt = VsockPacket::from_rx_virtq_head(
867-
&handler_ctx.device.queues[RXQ_INDEX]
868+
let mut rx_pkt = VsockPacket::from_rx_virtq_head(
869+
handler_ctx.device.queues[RXQ_INDEX]
870+
.pop(&vsock_test_ctx.mem)
871+
.unwrap(),
872+
)
873+
.unwrap();
874+
let tx_pkt = VsockPacket::from_tx_virtq_head(
875+
handler_ctx.device.queues[TXQ_INDEX]
868876
.pop(&vsock_test_ctx.mem)
869877
.unwrap(),
870878
)
@@ -891,16 +899,17 @@ mod tests {
891899
PEER_BUF_ALLOC,
892900
);
893901
assert!(conn.has_pending_rx());
894-
conn.recv_pkt(&mut pkt, &vsock_test_ctx.mem).unwrap();
895-
assert_eq!(pkt.op(), uapi::VSOCK_OP_RESPONSE);
902+
conn.recv_pkt(&mut rx_pkt, &vsock_test_ctx.mem).unwrap();
903+
assert_eq!(rx_pkt.op(), uapi::VSOCK_OP_RESPONSE);
896904
conn
897905
}
898906
other => panic!("invalid ctx state: {:?}", other),
899907
};
900908
assert_eq!(conn.state, conn_state);
901909
Self {
902910
_vsock_test_ctx: vsock_test_ctx,
903-
pkt,
911+
rx_pkt,
912+
tx_pkt,
904913
conn,
905914
}
906915
}
@@ -918,13 +927,13 @@ mod tests {
918927

919928
fn send(&mut self) {
920929
self.conn
921-
.send_pkt(&self.pkt, &self._vsock_test_ctx.mem)
930+
.send_pkt(&self.tx_pkt, &self._vsock_test_ctx.mem)
922931
.unwrap();
923932
}
924933

925934
fn recv(&mut self) {
926935
self.conn
927-
.recv_pkt(&mut self.pkt, &self._vsock_test_ctx.mem)
936+
.recv_pkt(&mut self.rx_pkt, &self._vsock_test_ctx.mem)
928937
.unwrap();
929938
}
930939

@@ -937,19 +946,17 @@ mod tests {
937946
self.conn.notify(EventSet::OUT);
938947
}
939948

940-
fn init_pkt(&mut self, op: u16, len: u32) -> &mut VsockPacket {
941-
init_pkt(&mut self.pkt, op, len)
949+
fn init_tx_pkt(&mut self, op: u16, len: u32) -> &mut VsockPacket {
950+
init_pkt(&mut self.tx_pkt, op, len)
942951
}
943952

944-
fn init_data_pkt(&mut self, mut data: &[u8]) -> &VsockPacket {
945-
assert!(data.len() <= self.pkt.buf_size());
946-
self.init_pkt(uapi::VSOCK_OP_RW, u32::try_from(data.len()).unwrap());
953+
fn init_data_tx_pkt(&mut self, mut data: &[u8]) -> &VsockPacket {
954+
assert!(data.len() <= self.tx_pkt.buf_size());
955+
self.init_tx_pkt(uapi::VSOCK_OP_RW, u32::try_from(data.len()).unwrap());
947956

948957
let len = data.len();
949-
self.pkt
950-
.read_at_offset_from(&self._vsock_test_ctx.mem, 0, &mut data, len)
951-
.unwrap();
952-
&self.pkt
958+
self.rx_pkt.read_at_offset_from(&mut data, 0, len).unwrap();
959+
&self.tx_pkt
953960
}
954961
}
955962

@@ -960,13 +967,13 @@ mod tests {
960967
ctx.recv();
961968
// For peer-initiated requests, our connection should always yield a vsock reponse packet,
962969
// in order to establish the connection.
963-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RESPONSE);
964-
assert_eq!(ctx.pkt.src_cid(), LOCAL_CID);
965-
assert_eq!(ctx.pkt.dst_cid(), PEER_CID);
966-
assert_eq!(ctx.pkt.src_port(), LOCAL_PORT);
967-
assert_eq!(ctx.pkt.dst_port(), PEER_PORT);
968-
assert_eq!(ctx.pkt.type_(), uapi::VSOCK_TYPE_STREAM);
969-
assert_eq!(ctx.pkt.len(), 0);
970+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RESPONSE);
971+
assert_eq!(ctx.rx_pkt.src_cid(), LOCAL_CID);
972+
assert_eq!(ctx.rx_pkt.dst_cid(), PEER_CID);
973+
assert_eq!(ctx.rx_pkt.src_port(), LOCAL_PORT);
974+
assert_eq!(ctx.rx_pkt.dst_port(), PEER_PORT);
975+
assert_eq!(ctx.rx_pkt.type_(), uapi::VSOCK_TYPE_STREAM);
976+
assert_eq!(ctx.rx_pkt.len(), 0);
970977
// After yielding the response packet, the connection should have transitioned to the
971978
// established state.
972979
assert_eq!(ctx.conn.state, ConnState::Established);
@@ -981,11 +988,11 @@ mod tests {
981988
// armed.
982989
assert!(!ctx.conn.will_expire());
983990
ctx.recv();
984-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_REQUEST);
991+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_REQUEST);
985992
// Since the request might time-out, the kill timer should now be armed.
986993
assert!(ctx.conn.will_expire());
987994
assert!(!ctx.conn.has_expired());
988-
ctx.init_pkt(uapi::VSOCK_OP_RESPONSE, 0);
995+
ctx.init_tx_pkt(uapi::VSOCK_OP_RESPONSE, 0);
989996
ctx.send();
990997
// Upon receiving a connection response, the connection should have transitioned to the
991998
// established state, and the kill timer should've been disarmed.
@@ -997,7 +1004,7 @@ mod tests {
9971004
fn test_local_request_timeout() {
9981005
let mut ctx = CsmTestContext::new(ConnState::LocalInit);
9991006
ctx.recv();
1000-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_REQUEST);
1007+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_REQUEST);
10011008
assert!(ctx.conn.will_expire());
10021009
assert!(!ctx.conn.has_expired());
10031010
std::thread::sleep(std::time::Duration::from_millis(
@@ -1014,14 +1021,14 @@ mod tests {
10141021
assert_eq!(ctx.conn.as_raw_fd(), ctx.conn.stream.as_raw_fd());
10151022
ctx.notify_epollin();
10161023
ctx.recv();
1017-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RW);
1018-
assert_eq!(ctx.pkt.len() as usize, data.len());
1024+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RW);
1025+
assert_eq!(ctx.rx_pkt.len() as usize, data.len());
10191026

1020-
let buf = test_utils::read_packet_data(&ctx.pkt, &ctx._vsock_test_ctx.mem, 4);
1027+
let buf = test_utils::read_packet_data(&ctx.tx_pkt, 4);
10211028
assert_eq!(&buf, data);
10221029

10231030
// There's no more data in the stream, so `recv_pkt` should yield `VsockError::NoData`.
1024-
match ctx.conn.recv_pkt(&mut ctx.pkt, &ctx._vsock_test_ctx.mem) {
1031+
match ctx.conn.recv_pkt(&mut ctx.tx_pkt, &ctx._vsock_test_ctx.mem) {
10251032
Err(VsockError::NoData) => (),
10261033
other => panic!("{:?}", other),
10271034
}
@@ -1030,7 +1037,7 @@ mod tests {
10301037
ctx.conn.state = ConnState::LocalClosed;
10311038
ctx.notify_epollin();
10321039
ctx.recv();
1033-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RST);
1040+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RST);
10341041
}
10351042

10361043
#[test]
@@ -1044,9 +1051,9 @@ mod tests {
10441051
// When the host-side stream is closed, we can neither send not receive any more data.
10451052
// Therefore, the vsock shutdown packet that we'll deliver to the guest must contain both
10461053
// the no-more-send and the no-more-recv indications.
1047-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_SHUTDOWN);
1048-
assert_ne!(ctx.pkt.flags() & uapi::VSOCK_FLAGS_SHUTDOWN_SEND, 0);
1049-
assert_ne!(ctx.pkt.flags() & uapi::VSOCK_FLAGS_SHUTDOWN_RCV, 0);
1054+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_SHUTDOWN);
1055+
assert_ne!(ctx.rx_pkt.flags() & uapi::VSOCK_FLAGS_SHUTDOWN_SEND, 0);
1056+
assert_ne!(ctx.rx_pkt.flags() & uapi::VSOCK_FLAGS_SHUTDOWN_RCV, 0);
10501057

10511058
// The kill timer should now be armed.
10521059
assert!(ctx.conn.will_expire());
@@ -1063,14 +1070,14 @@ mod tests {
10631070
{
10641071
let mut ctx = CsmTestContext::new_established();
10651072

1066-
ctx.init_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
1073+
ctx.init_tx_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
10671074
.set_flags(uapi::VSOCK_FLAGS_SHUTDOWN_RCV);
10681075
ctx.send();
10691076
assert_eq!(ctx.conn.state, ConnState::PeerClosed(true, false));
10701077

10711078
// Attempting to reset the no-more-recv indication should not work
10721079
// (we are only setting the no-more-send indication here).
1073-
ctx.pkt.set_flags(uapi::VSOCK_FLAGS_SHUTDOWN_SEND);
1080+
ctx.tx_pkt.set_flags(uapi::VSOCK_FLAGS_SHUTDOWN_SEND);
10741081
ctx.send();
10751082
assert_eq!(ctx.conn.state, ConnState::PeerClosed(true, true));
10761083
}
@@ -1082,17 +1089,17 @@ mod tests {
10821089
let data = &[1, 2, 3, 4];
10831090
let mut ctx = CsmTestContext::new_established();
10841091
ctx.set_stream(TestStream::new_with_read_buf(data));
1085-
ctx.init_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
1092+
ctx.init_tx_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
10861093
.set_flags(uapi::VSOCK_FLAGS_SHUTDOWN_SEND);
10871094
ctx.send();
10881095
ctx.notify_epollin();
10891096
ctx.recv();
1090-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RW);
1097+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RW);
10911098

1092-
let buf = test_utils::read_packet_data(&ctx.pkt, &ctx._vsock_test_ctx.mem, 4);
1099+
let buf = test_utils::read_packet_data(&ctx.tx_pkt, 4);
10931100
assert_eq!(&buf, data);
10941101

1095-
ctx.init_data_pkt(data);
1102+
ctx.init_data_tx_pkt(data);
10961103
ctx.send();
10971104
assert_eq!(ctx.conn.stream.write_buf.len(), 0);
10981105
assert!(ctx.conn.tx_buf.is_empty());
@@ -1103,29 +1110,29 @@ mod tests {
11031110
// - attempting to read data from it should yield an RST packet.
11041111
{
11051112
let mut ctx = CsmTestContext::new_established();
1106-
ctx.init_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
1113+
ctx.init_tx_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
11071114
.set_flags(uapi::VSOCK_FLAGS_SHUTDOWN_RCV);
11081115
ctx.send();
11091116
let data = &[1, 2, 3, 4];
1110-
ctx.init_data_pkt(data);
1117+
ctx.init_data_tx_pkt(data);
11111118
ctx.send();
11121119
assert_eq!(ctx.conn.stream.write_buf, data.to_vec());
11131120

11141121
ctx.notify_epollin();
11151122
ctx.recv();
1116-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RST);
1123+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RST);
11171124
}
11181125

11191126
// Test case: setting both no-more-send and no-more-recv indications should have the
11201127
// connection confirm termination (i.e. yield an RST).
11211128
{
11221129
let mut ctx = CsmTestContext::new_established();
1123-
ctx.init_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
1130+
ctx.init_tx_pkt(uapi::VSOCK_OP_SHUTDOWN, 0)
11241131
.set_flags(uapi::VSOCK_FLAGS_SHUTDOWN_RCV | uapi::VSOCK_FLAGS_SHUTDOWN_SEND);
11251132
ctx.send();
11261133
assert!(ctx.conn.has_pending_rx());
11271134
ctx.recv();
1128-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RST);
1135+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RST);
11291136
}
11301137
}
11311138

@@ -1137,7 +1144,7 @@ mod tests {
11371144
ctx.set_stream(stream);
11381145
ctx.notify_epollin();
11391146
ctx.recv();
1140-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RST);
1147+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RST);
11411148
}
11421149

11431150
#[test]
@@ -1146,19 +1153,19 @@ mod tests {
11461153
ctx.set_peer_credit(0);
11471154
ctx.notify_epollin();
11481155
ctx.recv();
1149-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_CREDIT_REQUEST);
1156+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_CREDIT_REQUEST);
11501157
}
11511158

11521159
#[test]
11531160
fn test_credit_request_from_peer() {
11541161
let mut ctx = CsmTestContext::new_established();
1155-
ctx.init_pkt(uapi::VSOCK_OP_CREDIT_REQUEST, 0);
1162+
ctx.init_tx_pkt(uapi::VSOCK_OP_CREDIT_REQUEST, 0);
11561163
ctx.send();
11571164
assert!(ctx.conn.has_pending_rx());
11581165
ctx.recv();
1159-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_CREDIT_UPDATE);
1160-
assert_eq!(ctx.pkt.buf_alloc(), csm_defs::CONN_TX_BUF_SIZE);
1161-
assert_eq!(ctx.pkt.fwd_cnt(), ctx.conn.fwd_cnt.0);
1166+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_CREDIT_UPDATE);
1167+
assert_eq!(ctx.rx_pkt.buf_alloc(), csm_defs::CONN_TX_BUF_SIZE);
1168+
assert_eq!(ctx.rx_pkt.fwd_cnt(), ctx.conn.fwd_cnt.0);
11621169
}
11631170

11641171
#[test]
@@ -1179,20 +1186,20 @@ mod tests {
11791186
let data = &[1, 2, 3, 4];
11801187

11811188
// Check that there is no pending RX.
1182-
ctx.init_data_pkt(data);
1189+
ctx.init_data_tx_pkt(data);
11831190
ctx.send();
11841191
assert!(!ctx.conn.has_pending_rx());
11851192

11861193
// Send a packet again.
1187-
ctx.init_data_pkt(data);
1194+
ctx.init_data_tx_pkt(data);
11881195
ctx.send();
11891196

11901197
// The CSM should now have a credit update available for the peer.
11911198
assert!(ctx.conn.has_pending_rx());
11921199
ctx.recv();
1193-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_CREDIT_UPDATE);
1200+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_CREDIT_UPDATE);
11941201
assert_eq!(
1195-
ctx.pkt.fwd_cnt() as usize,
1202+
ctx.rx_pkt.fwd_cnt() as usize,
11961203
initial_fwd_cnt as usize + data.len() * 2,
11971204
);
11981205
assert_eq!(ctx.conn.fwd_cnt, ctx.conn.last_fwd_cnt_to_peer);
@@ -1214,7 +1221,7 @@ mod tests {
12141221
// Send some data through the connection. The backing stream is set to reject writes,
12151222
// so the data should end up in the TX buffer.
12161223
let data = &[1, 2, 3, 4];
1217-
ctx.init_data_pkt(data);
1224+
ctx.init_data_tx_pkt(data);
12181225
ctx.send();
12191226

12201227
// When there's data in the TX buffer, the connection should ask to be notified when it
@@ -1241,13 +1248,13 @@ mod tests {
12411248
ctx.set_stream(stream);
12421249

12431250
let data = &[1, 2, 3, 4];
1244-
ctx.init_data_pkt(data);
1251+
ctx.init_data_tx_pkt(data);
12451252
ctx.send();
12461253

12471254
assert_eq!(ctx.conn.state, ConnState::Killed);
12481255
assert!(ctx.conn.has_pending_rx());
12491256
ctx.recv();
1250-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RST);
1257+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RST);
12511258
}
12521259

12531260
// Test case: notifying a connection that it can flush its TX buffer to a broken stream
@@ -1262,7 +1269,7 @@ mod tests {
12621269
// Send some data through the connection. The backing stream is set to reject writes,
12631270
// so the data should end up in the TX buffer.
12641271
let data = &[1, 2, 3, 4];
1265-
ctx.init_data_pkt(data);
1272+
ctx.init_data_tx_pkt(data);
12661273
ctx.send();
12671274

12681275
// Set the backing stream to error out on write.
@@ -1285,8 +1292,8 @@ mod tests {
12851292
ctx.set_stream(stream);
12861293

12871294
// Fill up the TX buffer.
1288-
let data = vec![0u8; ctx.pkt.buf_size()];
1289-
ctx.init_data_pkt(data.as_slice());
1295+
let data = vec![0u8; ctx.tx_pkt.buf_size()];
1296+
ctx.init_data_tx_pkt(data.as_slice());
12901297
for _i in 0..(csm_defs::CONN_TX_BUF_SIZE as usize / data.len()) {
12911298
ctx.send();
12921299
}
@@ -1298,6 +1305,6 @@ mod tests {
12981305
assert_eq!(ctx.conn.state, ConnState::Killed);
12991306
assert!(ctx.conn.has_pending_rx());
13001307
ctx.recv();
1301-
assert_eq!(ctx.pkt.op(), uapi::VSOCK_OP_RST);
1308+
assert_eq!(ctx.rx_pkt.op(), uapi::VSOCK_OP_RST);
13021309
}
13031310
}

0 commit comments

Comments
 (0)