1use std::collections::{HashMap, VecDeque};
14use std::num::Wrapping;
15#[cfg(test)]
16use std::os::unix::io::FromRawFd;
17use std::os::unix::io::{AsRawFd, OwnedFd, RawFd};
18use std::sync::Arc;
19use std::sync::atomic::{AtomicU32, Ordering};
20
21use crate::VsockHostConnections;
22
23pub type VsockDoorbell = Arc<dyn Fn() + Send + Sync>;
30
31#[derive(Debug, Clone, Copy, Default)]
40pub struct RxOps(u8);
41
42impl RxOps {
43 pub const REQUEST: u8 = 0x01;
45 pub const RW: u8 = 0x02;
46 pub const RESPONSE: u8 = 0x04;
47 pub const CREDIT_UPDATE: u8 = 0x08;
48 pub const RESET: u8 = 0x10;
49 pub const CREDIT_REQUEST: u8 = 0x20;
50
51 pub fn pending(&self) -> bool {
53 self.0 != 0
54 }
55
56 pub fn enqueue(&mut self, op: u8) {
58 self.0 |= op;
59 }
60
61 pub fn dequeue(&mut self) -> u8 {
64 if self.0 == 0 {
65 return 0;
66 }
67 let op = self.0 & self.0.wrapping_neg();
69 self.0 &= !op;
70 op
71 }
72
73 pub fn peek(&self) -> u8 {
75 if self.0 == 0 {
76 return 0;
77 }
78 self.0 & self.0.wrapping_neg()
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92pub struct VsockConnectionId {
93 pub host_port: u32,
94 pub guest_port: u32,
95}
96
97pub const TX_BUFFER_SIZE: u32 = 64 * 1024;
103
104pub const CREDIT_UPDATE_THRESHOLD: u32 = 4096;
115
116pub const VSOCK_SHUTDOWN_F_RECEIVE: u32 = 1 << 0;
118
119pub const VSOCK_SHUTDOWN_F_SEND: u32 = 1 << 1;
121
122pub const VSOCK_SHUTDOWN_F_BOTH: u32 = VSOCK_SHUTDOWN_F_RECEIVE | VSOCK_SHUTDOWN_F_SEND;
124
125pub struct VsockConnection {
135 pub id: VsockConnectionId,
136 pub internal_fd: OwnedFd,
137 pub injected_notify: Option<std::sync::mpsc::Sender<()>>,
142 pub guest_cid: u64,
143
144 pub connect: bool,
146
147 pub rx_queue: RxOps,
149
150 pub fwd_cnt: Wrapping<u32>,
154
155 last_fwd_cnt: Wrapping<u32>,
158
159 pub peer_buf_alloc: u32,
161
162 pub peer_fwd_cnt: Wrapping<u32>,
164
165 pub rx_cnt: Wrapping<u32>,
167
168 credit_request_pending: bool,
173
174 peer_no_recv: bool,
178}
179
180impl VsockConnection {
181 pub fn new_local_init(
183 id: VsockConnectionId,
184 guest_cid: u64,
185 fd: OwnedFd,
186 injected_notify: std::sync::mpsc::Sender<()>,
187 ) -> Self {
188 let mut conn = Self {
189 id,
190 internal_fd: fd,
191 guest_cid,
192 connect: false,
193 injected_notify: Some(injected_notify),
194 rx_queue: RxOps::default(),
195 fwd_cnt: Wrapping(0),
196 last_fwd_cnt: Wrapping(0),
197 peer_buf_alloc: 0,
198 peer_fwd_cnt: Wrapping(0),
199 rx_cnt: Wrapping(0),
200 credit_request_pending: false,
201 peer_no_recv: false,
202 };
203 conn.rx_queue.enqueue(RxOps::REQUEST);
205 conn
206 }
207
208 pub fn peer_avail_credit(&self) -> usize {
213 (Wrapping(self.peer_buf_alloc) - (self.rx_cnt - self.peer_fwd_cnt)).0 as usize
214 }
215
216 pub fn update_peer_credit(&mut self, buf_alloc: u32, fwd_cnt: u32) {
220 self.peer_buf_alloc = buf_alloc;
221 self.peer_fwd_cnt = Wrapping(fwd_cnt);
222 self.credit_request_pending = false;
223 }
224
225 pub fn maybe_request_credit(&mut self) {
233 if self.credit_request_pending || self.peer_buf_alloc == 0 {
234 return;
235 }
236 let half = (self.peer_buf_alloc / 2) as usize;
237 if self.peer_avail_credit() < half {
238 self.rx_queue.enqueue(RxOps::CREDIT_REQUEST);
239 self.credit_request_pending = true;
240 }
241 }
242
243 pub fn note_credit_request_sent(&mut self) {
248 self.credit_request_pending = true;
249 }
250
251 #[must_use]
253 pub fn credit_request_pending(&self) -> bool {
254 self.credit_request_pending
255 }
256
257 pub fn mark_peer_no_recv(&mut self) {
260 self.peer_no_recv = true;
261 }
262
263 #[must_use]
265 pub const fn peer_no_recv(&self) -> bool {
266 self.peer_no_recv
267 }
268
269 #[must_use]
272 pub const fn accepts_data(&self) -> bool {
273 self.connect && !self.peer_no_recv
274 }
275
276 pub fn advance_fwd_cnt(&mut self, bytes: u32) {
279 self.fwd_cnt += Wrapping(bytes);
280
281 let consumed = (self.fwd_cnt - self.last_fwd_cnt).0;
284 if consumed >= CREDIT_UPDATE_THRESHOLD {
285 self.rx_queue.enqueue(RxOps::CREDIT_UPDATE);
286 }
287 }
288
289 pub fn record_rx(&mut self, bytes: u32) {
291 self.rx_cnt += Wrapping(bytes);
292 }
293
294 pub fn mark_credit_sent(&mut self) {
296 self.last_fwd_cnt = self.fwd_cnt;
297 }
298}
299
300pub struct VsockConnectionManager {
310 connections: HashMap<VsockConnectionId, VsockConnection>,
311 pub backend_rxq: VecDeque<VsockConnectionId>,
314 next_host_port: AtomicU32,
316 doorbell: Option<VsockDoorbell>,
322}
323
324impl VsockConnectionManager {
325 const EPHEMERAL_PORT_BASE: u32 = 50_000;
327
328 pub fn new() -> Self {
330 Self {
331 connections: HashMap::new(),
332 backend_rxq: VecDeque::new(),
333 next_host_port: AtomicU32::new(Self::EPHEMERAL_PORT_BASE),
334 doorbell: None,
335 }
336 }
337
338 pub fn set_doorbell(&mut self, doorbell: VsockDoorbell) {
340 self.doorbell = Some(doorbell);
341 }
342
343 fn ring_doorbell(&self) {
344 if let Some(doorbell) = &self.doorbell {
345 doorbell();
346 }
347 }
348
349 pub fn allocate(
366 &mut self,
367 guest_port: u32,
368 guest_cid: u64,
369 internal_fd: OwnedFd,
370 ) -> (VsockConnectionId, std::sync::mpsc::Receiver<()>) {
371 let host_port = self.next_host_port.fetch_add(1, Ordering::Relaxed);
372 let id = VsockConnectionId {
373 host_port,
374 guest_port,
375 };
376 let (tx, rx) = std::sync::mpsc::channel();
377 let conn = VsockConnection::new_local_init(id, guest_cid, internal_fd, tx);
378 self.connections.insert(id, conn);
379 self.backend_rxq.push_back(id);
381 self.ring_doorbell();
382 tracing::info!(
383 "VsockConnectionManager: allocated connection guest_port={} host_port={} — \
384 OP_REQUEST enqueued",
385 guest_port,
386 host_port,
387 );
388 (id, rx)
389 }
390
391 pub fn connected_fds(&self) -> Vec<(VsockConnectionId, RawFd)> {
396 self.connections
397 .values()
398 .filter(|c| c.connect)
399 .map(|c| (c.id, c.internal_fd.as_raw_fd()))
400 .collect()
401 }
402
403 pub fn get_mut(&mut self, id: &VsockConnectionId) -> Option<&mut VsockConnection> {
405 self.connections.get_mut(id)
406 }
407
408 pub fn get(&self, id: &VsockConnectionId) -> Option<&VsockConnection> {
410 self.connections.get(id)
411 }
412
413 pub fn enqueue_rw(&mut self, id: VsockConnectionId) {
415 if let Some(conn) = self.connections.get_mut(&id) {
416 conn.rx_queue.enqueue(RxOps::RW);
417 self.backend_rxq.push_back(id);
418 }
419 }
420
421 pub fn enqueue_reset(&mut self, id: VsockConnectionId) {
423 if let Some(conn) = self.connections.get_mut(&id) {
424 conn.rx_queue.enqueue(RxOps::RESET);
425 self.backend_rxq.push_back(id);
426 }
427 }
428
429 pub fn remove(&mut self, id: &VsockConnectionId) {
431 if let Some(mut conn) = self.connections.remove(id) {
432 if let Some(tx) = conn.injected_notify.take() {
435 let _ = tx.send(());
436 }
437 self.backend_rxq.retain(|qid| qid != id);
440 tracing::info!(
441 "VsockConnectionManager: removed connection guest_port={} host_port={} — fd closed",
442 id.guest_port,
443 id.host_port,
444 );
445 }
446 }
447
448 pub fn connections_with_pending_rx(&self) -> Vec<VsockConnectionId> {
452 let in_queue: std::collections::HashSet<_> = self.backend_rxq.iter().copied().collect();
453 self.connections
454 .values()
455 .filter(|c| c.rx_queue.pending() && !in_queue.contains(&c.id))
456 .map(|c| c.id)
457 .collect()
458 }
459
460 #[cfg(test)]
462 pub fn len(&self) -> usize {
463 self.connections.len()
464 }
465
466 #[cfg(test)]
468 pub fn is_empty(&self) -> bool {
469 self.connections.is_empty()
470 }
471}
472
473impl VsockHostConnections for VsockConnectionManager {
474 fn fd_for(&self, guest_port: u32, host_port: u32) -> Option<RawFd> {
475 let id = VsockConnectionId {
476 host_port,
477 guest_port,
478 };
479 self.connections
480 .get(&id)
481 .filter(|c| c.connect)
482 .map(|c| c.internal_fd.as_raw_fd())
483 }
484
485 fn mark_connected(&mut self, guest_port: u32, host_port: u32) {
486 let id = VsockConnectionId {
487 host_port,
488 guest_port,
489 };
490 if let Some(conn) = self.connections.get_mut(&id) {
491 conn.connect = true;
492 self.ring_doorbell();
496 tracing::info!("VsockConnectionManager: connection {:?} now Connected", id,);
497 } else {
498 tracing::warn!(
499 "VsockConnectionManager: mark_connected for unknown connection \
500 guest_port={} host_port={}",
501 guest_port,
502 host_port,
503 );
504 }
505 }
506
507 fn remove_connection(&mut self, guest_port: u32, host_port: u32) {
508 let id = VsockConnectionId {
509 host_port,
510 guest_port,
511 };
512 self.remove(&id);
513 }
514
515 fn update_peer_credit(
516 &mut self,
517 guest_port: u32,
518 host_port: u32,
519 buf_alloc: u32,
520 fwd_cnt: u32,
521 ) {
522 let id = VsockConnectionId {
523 host_port,
524 guest_port,
525 };
526 if let Some(conn) = self.connections.get_mut(&id) {
527 conn.update_peer_credit(buf_alloc, fwd_cnt);
528 }
529 }
530
531 fn advance_fwd_cnt(&mut self, guest_port: u32, host_port: u32, bytes: u32) -> bool {
532 let id = VsockConnectionId {
533 host_port,
534 guest_port,
535 };
536 if let Some(conn) = self.connections.get_mut(&id) {
537 conn.advance_fwd_cnt(bytes);
538 if conn.rx_queue.pending() {
539 self.backend_rxq.push_back(id);
540 self.ring_doorbell();
541 return true;
542 }
543 }
544 false
545 }
546
547 fn enqueue_credit_update(&mut self, guest_port: u32, host_port: u32) {
548 let id = VsockConnectionId {
549 host_port,
550 guest_port,
551 };
552 if let Some(conn) = self.connections.get_mut(&id) {
553 conn.rx_queue.enqueue(RxOps::CREDIT_UPDATE);
554 self.backend_rxq.push_back(id);
555 self.ring_doorbell();
556 }
557 }
558
559 fn handle_shutdown(&mut self, guest_port: u32, host_port: u32, flags: u32) {
560 if flags == 0 || flags & VSOCK_SHUTDOWN_F_BOTH == VSOCK_SHUTDOWN_F_BOTH {
563 self.remove_connection(guest_port, host_port);
564 return;
565 }
566
567 let id = VsockConnectionId {
568 host_port,
569 guest_port,
570 };
571 if flags & VSOCK_SHUTDOWN_F_RECEIVE != 0 {
572 if let Some(conn) = self.connections.get_mut(&id) {
573 conn.mark_peer_no_recv();
574 }
575 }
576 if flags & VSOCK_SHUTDOWN_F_SEND != 0 {
589 if let Some(conn) = self.connections.get(&id) {
590 let fd = conn.internal_fd.as_raw_fd();
591 let r = unsafe { libc::shutdown(fd, libc::SHUT_WR) };
595 if r != 0 {
596 let err = std::io::Error::last_os_error();
597 if !matches!(err.raw_os_error(), Some(libc::ENOTCONN | libc::EINVAL)) {
602 tracing::warn!(
603 guest_port,
604 host_port,
605 "shutdown(internal_fd, SHUT_WR) for F_SEND failed: {}",
606 err,
607 );
608 }
609 }
610 }
611 }
612 }
613}
614
615impl Default for VsockConnectionManager {
616 fn default() -> Self {
617 Self::new()
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624
625 fn make_socketpair() -> (OwnedFd, OwnedFd) {
626 let mut fds: [libc::c_int; 2] = [0; 2];
627 let ret =
628 unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
629 assert_eq!(ret, 0);
630 unsafe { (OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])) }
631 }
632
633 #[test]
634 fn rx_ops_priority_order() {
635 let mut ops = RxOps::default();
636 ops.enqueue(RxOps::RESET);
637 ops.enqueue(RxOps::REQUEST);
638 ops.enqueue(RxOps::RW);
639 ops.enqueue(RxOps::CREDIT_UPDATE);
640
641 assert_eq!(ops.dequeue(), RxOps::REQUEST);
643 assert_eq!(ops.dequeue(), RxOps::RW);
644 assert_eq!(ops.dequeue(), RxOps::CREDIT_UPDATE);
645 assert_eq!(ops.dequeue(), RxOps::RESET);
646 assert_eq!(ops.dequeue(), 0);
647 }
648
649 #[test]
650 fn rx_ops_dedup() {
651 let mut ops = RxOps::default();
652 ops.enqueue(RxOps::RW);
653 ops.enqueue(RxOps::RW);
654 ops.enqueue(RxOps::RW);
655
656 assert_eq!(ops.dequeue(), RxOps::RW);
657 assert_eq!(ops.dequeue(), 0); }
659
660 #[test]
661 fn allocate_unique_host_ports() {
662 let mut mgr = VsockConnectionManager::new();
663 let (_, internal1) = make_socketpair();
664 let (_, internal2) = make_socketpair();
665
666 let (id1, _rx1) = mgr.allocate(1024, 3, internal1);
667 let (id2, _rx2) = mgr.allocate(1024, 3, internal2);
668
669 assert_ne!(id1.host_port, id2.host_port);
670 assert_eq!(id1.guest_port, 1024);
671 assert_eq!(id2.guest_port, 1024);
672 assert_eq!(mgr.len(), 2);
673 }
674
675 #[test]
676 fn allocate_enqueues_request() {
677 let mut mgr = VsockConnectionManager::new();
678 let (_, internal) = make_socketpair();
679 let (id, _rx) = mgr.allocate(1024, 3, internal);
680
681 assert_eq!(mgr.backend_rxq.len(), 1);
683 assert_eq!(mgr.backend_rxq[0], id);
684
685 let conn = mgr.get(&id).unwrap();
687 assert_eq!(conn.rx_queue.peek(), RxOps::REQUEST);
688 assert!(!conn.connect);
689 }
690
691 #[test]
692 fn connected_fds_only_returns_connected() {
693 let mut mgr = VsockConnectionManager::new();
694 let (_, internal1) = make_socketpair();
695 let (_, internal2) = make_socketpair();
696
697 let (id1, _rx1) = mgr.allocate(1024, 3, internal1);
698 let (_id2, _rx2) = mgr.allocate(1024, 3, internal2);
699
700 assert!(mgr.connected_fds().is_empty());
701
702 mgr.mark_connected(id1.guest_port, id1.host_port);
703 let fds = mgr.connected_fds();
704 assert_eq!(fds.len(), 1);
705 assert_eq!(fds[0].0, id1);
706 }
707
708 #[test]
709 fn remove_closes_fd() {
710 let mut mgr = VsockConnectionManager::new();
711 let (_, internal) = make_socketpair();
712 let fd_raw = internal.as_raw_fd();
713 let (id, _rx) = mgr.allocate(1024, 3, internal);
714
715 mgr.mark_connected(id.guest_port, id.host_port);
716 assert!(mgr.fd_for(1024, id.host_port).is_some());
717
718 mgr.remove_connection(id.guest_port, id.host_port);
719 assert!(mgr.fd_for(1024, id.host_port).is_none());
720 assert_eq!(mgr.len(), 0);
721
722 let ret = unsafe { libc::fcntl(fd_raw, libc::F_GETFD) };
724 assert_eq!(ret, -1);
725 }
726
727 #[test]
728 fn credit_flow_control() {
729 let mut mgr = VsockConnectionManager::new();
730 let (_, internal) = make_socketpair();
731 let (id, _rx) = mgr.allocate(1024, 3, internal);
732
733 let conn = mgr.get_mut(&id).unwrap();
735 conn.update_peer_credit(128 * 1024, 0);
736 assert_eq!(conn.peer_avail_credit(), 128 * 1024);
737
738 conn.record_rx(64 * 1024);
740 assert_eq!(conn.peer_avail_credit(), 64 * 1024);
741
742 conn.update_peer_credit(128 * 1024, 32 * 1024);
744 assert_eq!(conn.peer_avail_credit(), 96 * 1024);
745 }
746
747 #[test]
748 fn fwd_cnt_triggers_credit_update() {
749 let mut mgr = VsockConnectionManager::new();
750 let (_, internal) = make_socketpair();
751 let (id, _rx) = mgr.allocate(1024, 3, internal);
752
753 let conn = mgr.get_mut(&id).unwrap();
755 conn.rx_queue.dequeue();
756
757 conn.advance_fwd_cnt(CREDIT_UPDATE_THRESHOLD);
759 assert_eq!(conn.rx_queue.peek(), RxOps::CREDIT_UPDATE);
760 }
761
762 #[test]
763 fn fwd_cnt_below_threshold_does_not_trigger_credit_update() {
764 let mut mgr = VsockConnectionManager::new();
765 let (_, internal) = make_socketpair();
766 let (id, _rx) = mgr.allocate(1024, 3, internal);
767 let conn = mgr.get_mut(&id).unwrap();
768 conn.rx_queue.dequeue();
769
770 conn.advance_fwd_cnt(CREDIT_UPDATE_THRESHOLD - 1);
772 assert!(!conn.rx_queue.pending());
773 }
774
775 #[test]
776 fn maybe_request_credit_fires_below_half_window() {
777 let mut mgr = VsockConnectionManager::new();
778 let (_, internal) = make_socketpair();
779 let (id, _rx) = mgr.allocate(1024, 3, internal);
780 let conn = mgr.get_mut(&id).unwrap();
781 conn.rx_queue.dequeue(); conn.update_peer_credit(8192, 0);
784 conn.record_rx(5000); conn.maybe_request_credit();
786
787 assert_eq!(conn.rx_queue.peek(), RxOps::CREDIT_REQUEST);
788 assert!(conn.credit_request_pending());
789 }
790
791 #[test]
792 fn maybe_request_credit_noop_above_half_window() {
793 let mut mgr = VsockConnectionManager::new();
794 let (_, internal) = make_socketpair();
795 let (id, _rx) = mgr.allocate(1024, 3, internal);
796 let conn = mgr.get_mut(&id).unwrap();
797 conn.rx_queue.dequeue();
798
799 conn.update_peer_credit(8192, 0);
800 conn.record_rx(3000); conn.maybe_request_credit();
802
803 assert!(!conn.rx_queue.pending());
804 assert!(!conn.credit_request_pending());
805 }
806
807 #[test]
808 fn maybe_request_credit_dedupes_while_pending() {
809 let mut mgr = VsockConnectionManager::new();
810 let (_, internal) = make_socketpair();
811 let (id, _rx) = mgr.allocate(1024, 3, internal);
812 let conn = mgr.get_mut(&id).unwrap();
813 conn.rx_queue.dequeue();
814
815 conn.update_peer_credit(8192, 0);
816 conn.record_rx(5000);
817 conn.maybe_request_credit();
818 conn.rx_queue.dequeue();
820
821 conn.record_rx(100); conn.maybe_request_credit();
823
824 assert!(!conn.rx_queue.pending(), "second request would be a dup");
825 }
826
827 #[test]
828 fn update_peer_credit_clears_pending_flag() {
829 let mut mgr = VsockConnectionManager::new();
830 let (_, internal) = make_socketpair();
831 let (id, _rx) = mgr.allocate(1024, 3, internal);
832 let conn = mgr.get_mut(&id).unwrap();
833 conn.rx_queue.dequeue();
834
835 conn.update_peer_credit(8192, 0);
836 conn.record_rx(5000);
837 conn.maybe_request_credit();
838 assert!(conn.credit_request_pending());
839
840 conn.update_peer_credit(8192, 5000);
845 assert!(!conn.credit_request_pending());
846
847 assert_eq!(conn.rx_queue.dequeue(), RxOps::CREDIT_REQUEST);
849
850 conn.maybe_request_credit();
852 assert!(!conn.rx_queue.pending());
853 assert!(!conn.credit_request_pending());
854 }
855
856 #[test]
857 fn shutdown_both_bits_removes_connection() {
858 let mut mgr = VsockConnectionManager::new();
859 let (_, internal) = make_socketpair();
860 let (id, _rx) = mgr.allocate(1024, 3, internal);
861 assert!(mgr.get(&id).is_some());
862
863 mgr.handle_shutdown(id.guest_port, id.host_port, VSOCK_SHUTDOWN_F_BOTH);
864 assert!(mgr.get(&id).is_none());
865 }
866
867 #[test]
868 fn shutdown_receive_bit_marks_half_close() {
869 let mut mgr = VsockConnectionManager::new();
870 let (_, internal) = make_socketpair();
871 let (id, _rx) = mgr.allocate(1024, 3, internal);
872
873 mgr.handle_shutdown(id.guest_port, id.host_port, VSOCK_SHUTDOWN_F_RECEIVE);
874 let conn = mgr.get(&id).expect("conn must survive half-close");
875 assert!(conn.peer_no_recv());
876 assert!(!conn.accepts_data() || !conn.connect); }
878
879 #[test]
880 fn shutdown_send_bit_only_is_informational() {
881 let mut mgr = VsockConnectionManager::new();
882 let (_, internal) = make_socketpair();
883 let (id, _rx) = mgr.allocate(1024, 3, internal);
884
885 mgr.handle_shutdown(id.guest_port, id.host_port, VSOCK_SHUTDOWN_F_SEND);
886 let conn = mgr.get(&id).expect("conn must survive");
887 assert!(
888 !conn.peer_no_recv(),
889 "F_SEND alone does not block host→peer RW"
890 );
891 }
892
893 #[test]
894 fn shutdown_send_bit_propagates_eof_to_daemon_fd() {
895 use std::io::Read;
900 use std::os::fd::IntoRawFd;
901
902 let mut mgr = VsockConnectionManager::new();
903 let (daemon_end, internal) = make_socketpair();
904 let (id, _rx) = mgr.allocate(1024, 3, internal);
905
906 let mut daemon_stream =
908 unsafe { std::os::unix::net::UnixStream::from_raw_fd(daemon_end.into_raw_fd()) };
909 daemon_stream
911 .set_read_timeout(Some(std::time::Duration::from_secs(2)))
912 .unwrap();
913
914 mgr.handle_shutdown(id.guest_port, id.host_port, VSOCK_SHUTDOWN_F_SEND);
917
918 let mut buf = [0u8; 8];
919 let n = daemon_stream
920 .read(&mut buf)
921 .expect("read on daemon fd should not error");
922 assert_eq!(n, 0, "daemon fd must read EOF after F_SEND propagation");
923
924 use std::io::Write;
926 daemon_stream
927 .write_all(b"still-alive")
928 .expect("daemon→internal write should still succeed");
929 }
930
931 #[test]
932 fn doorbell_rings_on_producer_paths() {
933 use std::sync::atomic::AtomicUsize;
934
935 let rings = Arc::new(AtomicUsize::new(0));
936 let mut mgr = VsockConnectionManager::new();
937 let rings_cb = Arc::clone(&rings);
938 mgr.set_doorbell(Arc::new(move || {
939 rings_cb.fetch_add(1, Ordering::SeqCst);
940 }));
941
942 let (_, internal) = make_socketpair();
943 let (id, _rx) = mgr.allocate(1024, 3, internal);
944 assert_eq!(rings.load(Ordering::SeqCst), 1, "allocate rings");
945
946 mgr.mark_connected(id.guest_port, id.host_port);
947 assert_eq!(rings.load(Ordering::SeqCst), 2, "mark_connected rings");
948
949 mgr.enqueue_credit_update(id.guest_port, id.host_port);
950 assert_eq!(
951 rings.load(Ordering::SeqCst),
952 3,
953 "enqueue_credit_update rings"
954 );
955
956 assert!(mgr.advance_fwd_cnt(id.guest_port, id.host_port, CREDIT_UPDATE_THRESHOLD));
958 assert_eq!(
959 rings.load(Ordering::SeqCst),
960 4,
961 "advance_fwd_cnt rings on push"
962 );
963 }
964
965 #[test]
966 fn doorbell_silent_on_injection_driver_paths() {
967 use std::sync::atomic::AtomicUsize;
968
969 let rings = Arc::new(AtomicUsize::new(0));
970 let mut mgr = VsockConnectionManager::new();
971
972 let (_, internal) = make_socketpair();
973 let (id, _rx) = mgr.allocate(1024, 3, internal);
974 mgr.get_mut(&id).unwrap().rx_queue.dequeue();
976
977 let rings_cb = Arc::clone(&rings);
979 mgr.set_doorbell(Arc::new(move || {
980 rings_cb.fetch_add(1, Ordering::SeqCst);
981 }));
982
983 assert!(!mgr.advance_fwd_cnt(id.guest_port, id.host_port, 1));
985 assert_eq!(rings.load(Ordering::SeqCst), 0);
986
987 mgr.enqueue_rw(id);
990 mgr.enqueue_reset(id);
991 assert_eq!(rings.load(Ordering::SeqCst), 0);
992 }
993
994 #[test]
995 fn shutdown_flags_zero_removes_connection_conservatively() {
996 let mut mgr = VsockConnectionManager::new();
998 let (_, internal) = make_socketpair();
999 let (id, _rx) = mgr.allocate(1024, 3, internal);
1000
1001 mgr.handle_shutdown(id.guest_port, id.host_port, 0);
1002 assert!(mgr.get(&id).is_none());
1003 }
1004}