gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/link/sharedmem/queue/queue_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package queue 16 17 import ( 18 "encoding/binary" 19 "slices" 20 "testing" 21 22 "gvisor.dev/gvisor/pkg/atomicbitops" 23 "gvisor.dev/gvisor/pkg/tcpip/link/sharedmem/pipe" 24 ) 25 26 func TestBasicTxQueue(t *testing.T) { 27 // Tests that a basic transmit on a queue works, and that completion 28 // gets properly reported as well. 29 pb1 := make([]byte, 100) 30 pb2 := make([]byte, 100) 31 32 var rxp pipe.Rx 33 rxp.Init(pb1) 34 35 var txp pipe.Tx 36 txp.Init(pb2) 37 38 var q Tx 39 var state atomicbitops.Uint32 40 q.Init(pb1, pb2, &state) 41 42 // Enqueue two buffers. 43 b := []TxBuffer{ 44 {nil, 100, 60}, 45 {nil, 200, 40}, 46 } 47 48 b[0].Next = &b[1] 49 50 const usedID = 1002 51 const usedTotalSize = 100 52 if !q.Enqueue(usedID, usedTotalSize, 2, &b[0]) { 53 t.Fatalf("Enqueue failed on empty queue") 54 } 55 56 // Check the contents of the pipe. 57 d := rxp.Pull() 58 if d == nil { 59 t.Fatalf("Tx pipe is empty after Enqueue") 60 } 61 62 want := []byte{ 63 234, 3, 0, 0, 0, 0, 0, 0, // id 64 100, 0, 0, 0, // total size 65 0, 0, 0, 0, // reserved 66 100, 0, 0, 0, 0, 0, 0, 0, // offset 1 67 60, 0, 0, 0, // size 1 68 200, 0, 0, 0, 0, 0, 0, 0, // offset 2 69 40, 0, 0, 0, // size 2 70 } 71 72 if !slices.Equal(want, d) { 73 t.Fatalf("Bad posted packet: got %v, want %v", d, want) 74 } 75 76 rxp.Flush() 77 78 // Check that there are no completions yet. 79 if _, ok := q.CompletedPacket(); ok { 80 t.Fatalf("Packet reported as completed too soon") 81 } 82 83 // Post a completion. 84 d = txp.Push(8) 85 if d == nil { 86 t.Fatalf("Unable to push to rx pipe") 87 } 88 binary.LittleEndian.PutUint64(d, usedID) 89 txp.Flush() 90 91 // Check that completion is properly reported. 92 id, ok := q.CompletedPacket() 93 if !ok { 94 t.Fatalf("Completion not reported") 95 } 96 97 if id != usedID { 98 t.Fatalf("Bad completion id: got %v, want %v", id, usedID) 99 } 100 } 101 102 func TestBasicRxQueue(t *testing.T) { 103 // Tests that a basic receive on a queue works. 104 pb1 := make([]byte, 100) 105 pb2 := make([]byte, 100) 106 107 var rxp pipe.Rx 108 rxp.Init(pb1) 109 110 var txp pipe.Tx 111 txp.Init(pb2) 112 113 var q Rx 114 q.Init(pb1, pb2, nil) 115 116 // Post two buffers. 117 b := []RxBuffer{ 118 {100, 60, 1077, 0}, 119 {200, 40, 2123, 0}, 120 } 121 122 if !q.PostBuffers(b) { 123 t.Fatalf("PostBuffers failed on empty queue") 124 } 125 126 // Check the contents of the pipe. 127 want := [][]byte{ 128 { 129 100, 0, 0, 0, 0, 0, 0, 0, // Offset1 130 60, 0, 0, 0, // Size1 131 0, 0, 0, 0, // Remaining in group 1 132 0, 0, 0, 0, 0, 0, 0, 0, // User data 1 133 53, 4, 0, 0, 0, 0, 0, 0, // ID 1 134 }, 135 { 136 200, 0, 0, 0, 0, 0, 0, 0, // Offset2 137 40, 0, 0, 0, // Size2 138 0, 0, 0, 0, // Remaining in group 2 139 0, 0, 0, 0, 0, 0, 0, 0, // User data 2 140 75, 8, 0, 0, 0, 0, 0, 0, // ID 2 141 }, 142 } 143 144 for i := range b { 145 d := rxp.Pull() 146 if d == nil { 147 t.Fatalf("Tx pipe is empty after PostBuffers") 148 } 149 150 if !slices.Equal(want[i], d) { 151 t.Fatalf("Bad posted packet: got %v, want %v", d, want[i]) 152 } 153 154 rxp.Flush() 155 } 156 157 // Check that there are no completions. 158 if _, n := q.Dequeue(nil); n != 0 { 159 t.Fatalf("Packet reported as received too soon") 160 } 161 162 // Post a completion. 163 d := txp.Push(sizeOfConsumedPacketHeader + 2*sizeOfConsumedBuffer) 164 if d == nil { 165 t.Fatalf("Unable to push to rx pipe") 166 } 167 168 copy(d, []byte{ 169 100, 0, 0, 0, // packet size 170 0, 0, 0, 0, // reserved 171 172 100, 0, 0, 0, 0, 0, 0, 0, // offset 1 173 60, 0, 0, 0, // size 1 174 0, 0, 0, 0, 0, 0, 0, 0, // user data 1 175 53, 4, 0, 0, 0, 0, 0, 0, // ID 1 176 177 200, 0, 0, 0, 0, 0, 0, 0, // offset 2 178 40, 0, 0, 0, // size 2 179 0, 0, 0, 0, 0, 0, 0, 0, // user data 2 180 75, 8, 0, 0, 0, 0, 0, 0, // ID 2 181 }) 182 183 txp.Flush() 184 185 // Check that completion is properly reported. 186 bufs, n := q.Dequeue(nil) 187 if n != 100 { 188 t.Fatalf("Bad packet size: got %v, want %v", n, 100) 189 } 190 191 if !slices.Equal(bufs, b) { 192 t.Fatalf("Bad returned buffers: got %v, want %v", bufs, b) 193 } 194 } 195 196 func TestBadTxCompletion(t *testing.T) { 197 // Check that tx completions with bad sizes are properly ignored. 198 pb1 := make([]byte, 100) 199 pb2 := make([]byte, 100) 200 201 var rxp pipe.Rx 202 rxp.Init(pb1) 203 204 var txp pipe.Tx 205 txp.Init(pb2) 206 207 var q Tx 208 var state atomicbitops.Uint32 209 q.Init(pb1, pb2, &state) 210 211 // Post a completion that is too short, and check that it is ignored. 212 if d := txp.Push(7); d == nil { 213 t.Fatalf("Unable to push to rx pipe") 214 } 215 txp.Flush() 216 217 if _, ok := q.CompletedPacket(); ok { 218 t.Fatalf("Bad completion not ignored") 219 } 220 221 // Post a completion that is too long, and check that it is ignored. 222 if d := txp.Push(10); d == nil { 223 t.Fatalf("Unable to push to rx pipe") 224 } 225 txp.Flush() 226 227 if _, ok := q.CompletedPacket(); ok { 228 t.Fatalf("Bad completion not ignored") 229 } 230 } 231 232 func TestBadRxCompletion(t *testing.T) { 233 // Check that bad rx completions are properly ignored. 234 pb1 := make([]byte, 100) 235 pb2 := make([]byte, 100) 236 237 var rxp pipe.Rx 238 rxp.Init(pb1) 239 240 var txp pipe.Tx 241 txp.Init(pb2) 242 243 var q Rx 244 q.Init(pb1, pb2, nil) 245 246 // Post a completion that is too short, and check that it is ignored. 247 if d := txp.Push(7); d == nil { 248 t.Fatalf("Unable to push to rx pipe") 249 } 250 txp.Flush() 251 252 if b, _ := q.Dequeue(nil); b != nil { 253 t.Fatalf("Bad completion not ignored") 254 } 255 256 // Post a completion whose buffer sizes add up to less than the total 257 // size. 258 d := txp.Push(sizeOfConsumedPacketHeader + 2*sizeOfConsumedBuffer) 259 if d == nil { 260 t.Fatalf("Unable to push to rx pipe") 261 } 262 263 copy(d, []byte{ 264 100, 0, 0, 0, // packet size 265 0, 0, 0, 0, // reserved 266 267 100, 0, 0, 0, 0, 0, 0, 0, // offset 1 268 10, 0, 0, 0, // size 1 269 0, 0, 0, 0, 0, 0, 0, 0, // user data 1 270 53, 4, 0, 0, 0, 0, 0, 0, // ID 1 271 272 200, 0, 0, 0, 0, 0, 0, 0, // offset 2 273 10, 0, 0, 0, // size 2 274 0, 0, 0, 0, 0, 0, 0, 0, // user data 2 275 75, 8, 0, 0, 0, 0, 0, 0, // ID 2 276 }) 277 278 txp.Flush() 279 if b, _ := q.Dequeue(nil); b != nil { 280 t.Fatalf("Bad completion not ignored") 281 } 282 283 // Post a completion whose buffer sizes will cause a 32-bit overflow, 284 // but adds up to the right number. 285 d = txp.Push(sizeOfConsumedPacketHeader + 2*sizeOfConsumedBuffer) 286 if d == nil { 287 t.Fatalf("Unable to push to rx pipe") 288 } 289 290 copy(d, []byte{ 291 100, 0, 0, 0, // packet size 292 0, 0, 0, 0, // reserved 293 294 100, 0, 0, 0, 0, 0, 0, 0, // offset 1 295 255, 255, 255, 255, // size 1 296 0, 0, 0, 0, 0, 0, 0, 0, // user data 1 297 53, 4, 0, 0, 0, 0, 0, 0, // ID 1 298 299 200, 0, 0, 0, 0, 0, 0, 0, // offset 2 300 101, 0, 0, 0, // size 2 301 0, 0, 0, 0, 0, 0, 0, 0, // user data 2 302 75, 8, 0, 0, 0, 0, 0, 0, // ID 2 303 }) 304 305 txp.Flush() 306 if b, _ := q.Dequeue(nil); b != nil { 307 t.Fatalf("Bad completion not ignored") 308 } 309 } 310 311 func TestFillTxPipe(t *testing.T) { 312 // Check that transmitting a new buffer when the buffer pipe is full 313 // fails gracefully. 314 pb1 := make([]byte, 104) 315 pb2 := make([]byte, 104) 316 317 var rxp pipe.Rx 318 rxp.Init(pb1) 319 320 var txp pipe.Tx 321 txp.Init(pb2) 322 323 var q Tx 324 var state atomicbitops.Uint32 325 q.Init(pb1, pb2, &state) 326 327 // Transmit twice, which should fill the tx pipe. 328 b := []TxBuffer{ 329 {nil, 100, 60}, 330 {nil, 200, 40}, 331 } 332 333 b[0].Next = &b[1] 334 335 const usedID = 1002 336 const usedTotalSize = 100 337 for i := uint64(0); i < 2; i++ { 338 if !q.Enqueue(usedID+i, usedTotalSize, 2, &b[0]) { 339 t.Fatalf("Failed to transmit buffer") 340 } 341 } 342 343 // Transmit another packet now that the tx pipe is full. 344 if q.Enqueue(usedID+2, usedTotalSize, 2, &b[0]) { 345 t.Fatalf("Enqueue succeeded when tx pipe is full") 346 } 347 } 348 349 func TestFillRxPipe(t *testing.T) { 350 // Check that posting a new buffer when the buffer pipe is full fails 351 // gracefully. 352 pb1 := make([]byte, 100) 353 pb2 := make([]byte, 100) 354 355 var rxp pipe.Rx 356 rxp.Init(pb1) 357 358 var txp pipe.Tx 359 txp.Init(pb2) 360 361 var q Rx 362 q.Init(pb1, pb2, nil) 363 364 // Post a buffer twice, it should fill the tx pipe. 365 b := []RxBuffer{ 366 {100, 60, 1077, 0}, 367 } 368 369 for i := 0; i < 2; i++ { 370 if !q.PostBuffers(b) { 371 t.Fatalf("PostBuffers failed on non-full queue") 372 } 373 } 374 375 // Post another buffer now that the tx pipe is full. 376 if q.PostBuffers(b) { 377 t.Fatalf("PostBuffers succeeded on full queue") 378 } 379 } 380 381 func TestLotsOfTransmissions(t *testing.T) { 382 // Make sure pipes are being properly flushed when transmitting packets. 383 pb1 := make([]byte, 100) 384 pb2 := make([]byte, 100) 385 386 var rxp pipe.Rx 387 rxp.Init(pb1) 388 389 var txp pipe.Tx 390 txp.Init(pb2) 391 392 var q Tx 393 var state atomicbitops.Uint32 394 q.Init(pb1, pb2, &state) 395 396 // Prepare packet with two buffers. 397 b := []TxBuffer{ 398 {nil, 100, 60}, 399 {nil, 200, 40}, 400 } 401 402 b[0].Next = &b[1] 403 404 const usedID = 1002 405 const usedTotalSize = 100 406 407 // Post 100000 packets and completions. 408 for i := 100000; i > 0; i-- { 409 if !q.Enqueue(usedID, usedTotalSize, 2, &b[0]) { 410 t.Fatalf("Enqueue failed on non-full queue") 411 } 412 413 if d := rxp.Pull(); d == nil { 414 t.Fatalf("Tx pipe is empty after Enqueue") 415 } 416 rxp.Flush() 417 418 d := txp.Push(8) 419 if d == nil { 420 t.Fatalf("Unable to write to rx pipe") 421 } 422 binary.LittleEndian.PutUint64(d, usedID) 423 txp.Flush() 424 if _, ok := q.CompletedPacket(); !ok { 425 t.Fatalf("Completion not returned") 426 } 427 } 428 } 429 430 func TestLotsOfReceptions(t *testing.T) { 431 // Make sure pipes are being properly flushed when receiving packets. 432 pb1 := make([]byte, 100) 433 pb2 := make([]byte, 100) 434 435 var rxp pipe.Rx 436 rxp.Init(pb1) 437 438 var txp pipe.Tx 439 txp.Init(pb2) 440 441 var q Rx 442 q.Init(pb1, pb2, nil) 443 444 // Prepare for posting two buffers. 445 b := []RxBuffer{ 446 {100, 60, 1077, 0}, 447 {200, 40, 2123, 0}, 448 } 449 450 // Post 100000 buffers and completions. 451 for i := 100000; i > 0; i-- { 452 if !q.PostBuffers(b) { 453 t.Fatalf("PostBuffers failed on non-full queue") 454 } 455 456 if d := rxp.Pull(); d == nil { 457 t.Fatalf("Tx pipe is empty after PostBuffers") 458 } 459 rxp.Flush() 460 461 if d := rxp.Pull(); d == nil { 462 t.Fatalf("Tx pipe is empty after PostBuffers") 463 } 464 rxp.Flush() 465 466 d := txp.Push(sizeOfConsumedPacketHeader + 2*sizeOfConsumedBuffer) 467 if d == nil { 468 t.Fatalf("Unable to push to rx pipe") 469 } 470 471 copy(d, []byte{ 472 100, 0, 0, 0, // packet size 473 0, 0, 0, 0, // reserved 474 475 100, 0, 0, 0, 0, 0, 0, 0, // offset 1 476 60, 0, 0, 0, // size 1 477 0, 0, 0, 0, 0, 0, 0, 0, // user data 1 478 53, 4, 0, 0, 0, 0, 0, 0, // ID 1 479 480 200, 0, 0, 0, 0, 0, 0, 0, // offset 2 481 40, 0, 0, 0, // size 2 482 0, 0, 0, 0, 0, 0, 0, 0, // user data 2 483 75, 8, 0, 0, 0, 0, 0, 0, // ID 2 484 }) 485 486 txp.Flush() 487 488 if _, n := q.Dequeue(nil); n == 0 { 489 t.Fatalf("Dequeue failed when there is a completion") 490 } 491 } 492 } 493 494 func TestRxEnableNotification(t *testing.T) { 495 // Check that enabling notifications results in properly updated state. 496 pb1 := make([]byte, 100) 497 pb2 := make([]byte, 100) 498 499 var state atomicbitops.Uint32 500 var q Rx 501 q.Init(pb1, pb2, &state) 502 503 q.EnableNotification() 504 if state.Load() != EventFDEnabled { 505 t.Fatalf("Bad value in shared state: got %v, want %v", state.Load(), EventFDEnabled) 506 } 507 } 508 509 func TestRxDisableNotification(t *testing.T) { 510 // Check that disabling notifications results in properly updated state. 511 pb1 := make([]byte, 100) 512 pb2 := make([]byte, 100) 513 514 var state atomicbitops.Uint32 515 var q Rx 516 q.Init(pb1, pb2, &state) 517 518 q.DisableNotification() 519 if state.Load() != EventFDDisabled { 520 t.Fatalf("Bad value in shared state: got %v, want %v", state.Load(), EventFDDisabled) 521 } 522 }