netstack_test.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package amneziawgnet
  2. import (
  3. "testing"
  4. "gvisor.dev/gvisor/pkg/buffer"
  5. )
  6. // TestStackTunReadDrainsBufferedBatch is a regression test for a real
  7. // throughput bug: Read used to always return exactly one packet per call
  8. // no matter how many were already queued, forcing amneziawg-go's TUN
  9. // reader to pay a full peer-lookup+staging+syscall cycle per packet on the
  10. // download path while the upload path (via the UDP bind's own
  11. // recvmmsg/sendmmsg batching) amortized that cost across up to 128
  12. // packets. Confirmed live: this alone took real download throughput from
  13. // 30-40 Mbit/s to 130-250 Mbit/s on a real test connection (see commit
  14. // 6436fd9c's message and internal/amneziawgnet/netstack.go's own comment
  15. // on tunQueueDepth for the full story) -- this test locks in the second,
  16. // finer-grained fix on top of that: Read must actually drain what's
  17. // already buffered instead of returning after the first packet.
  18. func TestStackTunReadDrainsBufferedBatch(t *testing.T) {
  19. t.Parallel()
  20. tun := &stackTun{incomingPacket: make(chan *buffer.View, tunQueueDepth)}
  21. packets := [][]byte{{1, 2, 3}, {4, 5}, {6, 7, 8, 9}}
  22. for _, p := range packets {
  23. tun.incomingPacket <- buffer.NewViewWithData(p)
  24. }
  25. buf := make([][]byte, 8)
  26. sizes := make([]int, 8)
  27. for i := range buf {
  28. buf[i] = make([]byte, 64)
  29. }
  30. n, err := tun.Read(buf, sizes, 0)
  31. if err != nil {
  32. t.Fatalf("Read: %v", err)
  33. }
  34. if n != len(packets) {
  35. t.Fatalf("Read returned %d packets, want %d (all buffered packets in one call)", n, len(packets))
  36. }
  37. for i, want := range packets {
  38. got := buf[i][:sizes[i]]
  39. if string(got) != string(want) {
  40. t.Errorf("packet %d = %v, want %v", i, got, want)
  41. }
  42. }
  43. }
  44. // TestStackTunReadStopsAtBufCapacity confirms Read never returns more
  45. // packets than the caller's buf can hold, and that whatever didn't fit is
  46. // still there (in order) for the next call -- draining must respect the
  47. // caller's batch size, not just gulp everything queued.
  48. func TestStackTunReadStopsAtBufCapacity(t *testing.T) {
  49. t.Parallel()
  50. tun := &stackTun{incomingPacket: make(chan *buffer.View, tunQueueDepth)}
  51. packets := [][]byte{{1}, {2}, {3}}
  52. for _, p := range packets {
  53. tun.incomingPacket <- buffer.NewViewWithData(p)
  54. }
  55. buf := make([][]byte, 2)
  56. sizes := make([]int, 2)
  57. for i := range buf {
  58. buf[i] = make([]byte, 64)
  59. }
  60. n, err := tun.Read(buf, sizes, 0)
  61. if err != nil {
  62. t.Fatalf("first Read: %v", err)
  63. }
  64. if n != 2 {
  65. t.Fatalf("first Read returned %d, want 2 (buf capacity)", n)
  66. }
  67. n, err = tun.Read(buf, sizes, 0)
  68. if err != nil {
  69. t.Fatalf("second Read: %v", err)
  70. }
  71. if n != 1 {
  72. t.Fatalf("second Read returned %d, want 1 (the leftover packet)", n)
  73. }
  74. if got := buf[0][:sizes[0]]; string(got) != "\x03" {
  75. t.Errorf("leftover packet = %v, want [3]", got)
  76. }
  77. }