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  }