mte/unikernel/duniverse/mirage-tcpip/test/test_deadlock.ml

150 lines
4.9 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
open Lwt.Infix
let mtu = 4000
let server_log = Logs.Src.create "test_deadlock_server" ~doc:"tcp deadlock tests: server"
module Server_log = (val Logs.src_log server_log : Logs.LOG)
let client_log = Logs.Src.create "test_deadlock_client" ~doc:"tcp deadlock tests: client"
module Client_log = (val Logs.src_log client_log : Logs.LOG)
module TCPIP =
struct
module RANDOM = Mirage_crypto_rng
module M =
struct
module B = Basic_backend.Make
module NETIF = Vnetif.Make(B)
module ETHIF = Ethernet.Make(NETIF)
module ARPV4 = Arp.Make(ETHIF)
module IPV4 = Static_ipv4.Make(ETHIF)(ARPV4)
module IPV6 = Ipv6.Make(NETIF)(ETHIF)
module IP = Tcpip_stack_direct.IPV4V6(IPV4)(IPV6)
module ICMPV4 = Icmpv4.Make(IPV4)
module UDP = Udp.Make(IP)
module TCP = Tcp.Flow.Make(IP)
module TCPIP = Tcpip_stack_direct.MakeV4V6(NETIF)(ETHIF)(ARPV4)(IP)(ICMPV4)(UDP)(TCP)
end
open M
type stack = TCPIP.t
let server_ip = Ipaddr.V4.of_string_exn "192.168.10.10"
let server_cidr = Ipaddr.V4.Prefix.make 24 server_ip
let client_ip = Ipaddr.V4.of_string_exn "192.168.10.20"
let client_cidr = Ipaddr.V4.Prefix.make 24 client_ip
let make ~cidr ?gateway netif =
ETHIF.connect netif >>= fun ethif ->
ARPV4.connect ethif >>= fun arpv4 ->
IPV4.connect ~cidr ?gateway ethif arpv4 >>= fun ipv4 ->
IPV6.connect netif ethif >>= fun ipv6 ->
IP.connect ~ipv4_only:false ~ipv6_only:false ipv4 ipv6 >>= fun ip ->
ICMPV4.connect ipv4 >>= fun icmpv4 ->
UDP.connect ip >>= fun udp ->
TCP.connect ip >>= fun tcp ->
TCPIP.connect netif ethif arpv4 ip icmpv4 udp tcp >>= fun tcpip ->
Lwt.return tcpip
include TCPIP
let tcpip t = t
let make role netif = match role with
| `Server -> make ~cidr:server_cidr netif
| `Client -> make ~cidr:client_cidr netif
type conn = M.NETIF.t
let get_stats _t =
{ Mirage_net.rx_pkts = 0l; rx_bytes = 0L;
tx_pkts = 0l; tx_bytes = 0L;
}
let reset_stats _t = ()
end
let port = 10000
let test_digest netif1 netif2 =
TCPIP.make `Client netif1 >>= fun client_stack ->
TCPIP.make `Server netif2 >>= fun server_stack ->
let send_data () =
let data = Mirage_crypto_rng.generate 100_000_000 in
let t0 = Unix.gettimeofday () in
TCPIP.TCP.create_connection
TCPIP.(tcp @@ tcpip server_stack) (Ipaddr.V4 TCPIP.client_ip, port) >>= function
| Error _ -> failwith "could not establish tunneled connection"
| Ok flow ->
Server_log.debug (fun f -> f "established conn");
let rec read_digest chunks =
TCPIP.TCP.read flow >>= function
| Error _ -> failwith "read error"
| Ok (`Data data) -> read_digest (data :: chunks)
| Ok `Eof ->
Server_log.debug (fun f -> f "EOF");
let dt = Unix.gettimeofday () -. t0 in
Server_log.warn (fun f -> f "!!!!!!!!!! XXXX needed %.2fs (%.1f MB/s)"
dt (float (String.length data) /. dt /. 1024. ** 2.));
Lwt.return_unit
in
Lwt.pick
[ read_digest [];
begin
let rec send_data data =
if Cstruct.length data < mtu then
(TCPIP.TCP.write flow data >>= fun _ -> Lwt.return_unit)
else
let sub, data = Cstruct.split data mtu in
Lwt.pick
[
(TCPIP.TCP.write flow sub >>= fun _ -> Lwt.return_unit);
(Lwt_unix.sleep 5. >>= fun () ->
Common.failf "=========== DEADLOCK!!! =============");
]
>>= fun () ->
send_data data in
send_data @@ Cstruct.of_string data >>= fun () ->
Server_log.debug (fun f -> f "wrote data");
TCPIP.TCP.close flow
end
]
in
TCPIP.TCP.listen TCPIP.(tcp (tcpip client_stack)) ~port
(fun flow ->
Client_log.debug (fun f -> f "client got conn");
let rec consume () =
TCPIP.TCP.read flow >>= function
| Error _ ->
Client_log.debug (fun f -> f "XXXX client read error");
TCPIP.TCP.close flow
| Ok `Eof ->
TCPIP.TCP.write flow @@ Cstruct.of_string "thanks for all the fish"
>>= fun _ ->
TCPIP.TCP.close flow
| Ok (`Data _data) ->
(if Random.float 1.0 < 0.01 then Lwt_unix.sleep 0.01
else Lwt.return_unit) >>= fun () ->
consume ()
in
consume ());
Lwt.pick
[
send_data ();
TCPIP.listen @@ TCPIP.tcpip server_stack;
TCPIP.listen @@ TCPIP.tcpip client_stack;
]
let run_vnetif () =
let backend = Basic_backend.Make.create
~use_async_readers:true ~yield:Lwt.pause () in
TCPIP.M.NETIF.connect ~size_limit:mtu backend >>= fun c1 ->
TCPIP.M.NETIF.connect ~size_limit:mtu backend >>= fun c2 ->
test_digest c1 c2
let suite = [
"test tcp deadlock with slow receiver", `Slow, run_vnetif
]