github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/process_linux_test.go (about)

     1  package libcontainer
     2  
     3  import (
     4  	"io/fs"
     5  	"testing"
     6  	"testing/fstest"
     7  )
     8  
     9  func TestIsolatedCPUAffinityTransition(t *testing.T) {
    10  	const isolatedCPUAffinityTransitionAnnotation = "org.opencontainers.runc.exec.isolated-cpu-affinity-transition"
    11  
    12  	noAffinity := -1
    13  	temporaryTransition := "temporary"
    14  	definitiveTransition := "definitive"
    15  
    16  	tests := []struct {
    17  		name                         string
    18  		testFS                       fs.FS
    19  		cpuset                       string
    20  		expectedErr                  bool
    21  		expectedAffinityCore         int
    22  		expectedDefinitiveTransition bool
    23  		annotations                  map[string]string
    24  	}{
    25  		{
    26  			name:   "no affinity",
    27  			cpuset: "0-15",
    28  			testFS: fstest.MapFS{
    29  				"sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("0-4\n")},
    30  			},
    31  			expectedAffinityCore:         noAffinity,
    32  			expectedDefinitiveTransition: false,
    33  		},
    34  		{
    35  			name:   "affinity match with temporary transition",
    36  			cpuset: "3-4",
    37  			testFS: fstest.MapFS{
    38  				"sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("0-4\n")},
    39  			},
    40  			expectedAffinityCore:         3,
    41  			expectedDefinitiveTransition: false,
    42  			annotations: map[string]string{
    43  				isolatedCPUAffinityTransitionAnnotation: temporaryTransition,
    44  			},
    45  		},
    46  		{
    47  			name:   "affinity match with temporary transition and nohz_full boot param",
    48  			cpuset: "3-4",
    49  			testFS: fstest.MapFS{
    50  				"proc/cmdline": &fstest.MapFile{Data: []byte("nohz_full=0-4\n")},
    51  			},
    52  			expectedAffinityCore:         3,
    53  			expectedDefinitiveTransition: false,
    54  			annotations: map[string]string{
    55  				isolatedCPUAffinityTransitionAnnotation: temporaryTransition,
    56  			},
    57  		},
    58  		{
    59  			name:   "affinity match with definitive transition",
    60  			cpuset: "3-4",
    61  			testFS: fstest.MapFS{
    62  				"sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("0-4\n")},
    63  			},
    64  			expectedAffinityCore:         3,
    65  			expectedDefinitiveTransition: true,
    66  			annotations: map[string]string{
    67  				isolatedCPUAffinityTransitionAnnotation: definitiveTransition,
    68  			},
    69  		},
    70  		{
    71  			name:   "affinity match with definitive transition and nohz_full boot param",
    72  			cpuset: "3-4",
    73  			testFS: fstest.MapFS{
    74  				"proc/cmdline": &fstest.MapFile{Data: []byte("nohz_full=0-4\n")},
    75  			},
    76  			expectedAffinityCore:         3,
    77  			expectedDefinitiveTransition: true,
    78  			annotations: map[string]string{
    79  				isolatedCPUAffinityTransitionAnnotation: definitiveTransition,
    80  			},
    81  		},
    82  		{
    83  			name:   "affinity error with bad isolated set",
    84  			cpuset: "0-15",
    85  			testFS: fstest.MapFS{
    86  				"sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("bad_isolated_set\n")},
    87  			},
    88  			expectedErr:          true,
    89  			expectedAffinityCore: noAffinity,
    90  			annotations: map[string]string{
    91  				isolatedCPUAffinityTransitionAnnotation: temporaryTransition,
    92  			},
    93  		},
    94  		{
    95  			name:   "affinity error with bad isolated set for nohz_full boot param",
    96  			cpuset: "0-15",
    97  			testFS: fstest.MapFS{
    98  				"proc/cmdline": &fstest.MapFile{Data: []byte("nohz_full=bad_isolated_set\n")},
    99  			},
   100  			expectedErr:          true,
   101  			expectedAffinityCore: noAffinity,
   102  			annotations: map[string]string{
   103  				isolatedCPUAffinityTransitionAnnotation: temporaryTransition,
   104  			},
   105  		},
   106  		{
   107  			name:   "no affinity with null isolated set value",
   108  			cpuset: "0-15",
   109  			testFS: fstest.MapFS{
   110  				"sys/devices/system/cpu/nohz_full": &fstest.MapFile{Data: []byte("(null)\n")},
   111  			},
   112  			expectedAffinityCore:         noAffinity,
   113  			expectedDefinitiveTransition: false,
   114  			annotations: map[string]string{
   115  				isolatedCPUAffinityTransitionAnnotation: temporaryTransition,
   116  			},
   117  		},
   118  	}
   119  
   120  	for _, tt := range tests {
   121  		t.Run(tt.name, func(t *testing.T) {
   122  			affinityCore, definitive, err := isolatedCPUAffinityTransition(tt.testFS, tt.cpuset, tt.annotations)
   123  			if err != nil && !tt.expectedErr {
   124  				t.Fatalf("unexpected error: %s", err)
   125  			} else if err == nil && tt.expectedErr {
   126  				t.Fatalf("unexpected success")
   127  			} else if tt.expectedDefinitiveTransition != definitive {
   128  				t.Fatalf("expected reset affinity %t: got %t instead", tt.expectedDefinitiveTransition, definitive)
   129  			} else if tt.expectedAffinityCore != affinityCore {
   130  				t.Fatalf("expected affinity core %d: got %d instead", tt.expectedAffinityCore, affinityCore)
   131  			}
   132  		})
   133  	}
   134  }
   135  
   136  func TestGetEligibleCPU(t *testing.T) {
   137  	tests := []struct {
   138  		name                 string
   139  		cpuset               string
   140  		isolset              string
   141  		expectedErr          bool
   142  		expectedAffinityCore int
   143  		expectedEligible     bool
   144  	}{
   145  		{
   146  			name:             "no cpuset",
   147  			isolset:          "2-15,18-31,34-47",
   148  			expectedEligible: false,
   149  		},
   150  		{
   151  			name:             "no isolated set",
   152  			cpuset:           "0-15",
   153  			expectedEligible: false,
   154  		},
   155  		{
   156  			name:        "bad cpuset format",
   157  			cpuset:      "core0 to core15",
   158  			isolset:     "2-15,18-31,34-47",
   159  			expectedErr: true,
   160  		},
   161  		{
   162  			name:        "bad isolated set format",
   163  			cpuset:      "0-15",
   164  			isolset:     "core0 to core15",
   165  			expectedErr: true,
   166  		},
   167  		{
   168  			name:             "no eligible core",
   169  			cpuset:           "0-1,16-17,32-33",
   170  			isolset:          "2-15,18-31,34-47",
   171  			expectedEligible: false,
   172  		},
   173  		{
   174  			name:             "no eligible core inverted",
   175  			cpuset:           "2-15,18-31,34-47",
   176  			isolset:          "0-1,16-17,32-33",
   177  			expectedEligible: false,
   178  		},
   179  		{
   180  			name:                 "eligible core mixed",
   181  			cpuset:               "8-31",
   182  			isolset:              "2-15,18-31,34-47",
   183  			expectedEligible:     true,
   184  			expectedAffinityCore: 16,
   185  		},
   186  		{
   187  			name:                 "eligible core #4",
   188  			cpuset:               "4-7",
   189  			isolset:              "2-15,18-31,34-47",
   190  			expectedEligible:     true,
   191  			expectedAffinityCore: 4,
   192  		},
   193  		{
   194  			name:                 "eligible core #40",
   195  			cpuset:               "40-47",
   196  			isolset:              "2-15,18-31,34-47",
   197  			expectedEligible:     true,
   198  			expectedAffinityCore: 40,
   199  		},
   200  		{
   201  			name:                 "eligible core #24",
   202  			cpuset:               "24-31",
   203  			isolset:              "2-15,18-31,34-47",
   204  			expectedEligible:     true,
   205  			expectedAffinityCore: 24,
   206  		},
   207  		{
   208  			name:             "no eligible core small isolated set",
   209  			cpuset:           "60-63",
   210  			isolset:          "0-1",
   211  			expectedEligible: false,
   212  		},
   213  	}
   214  
   215  	for _, tt := range tests {
   216  		t.Run(tt.name, func(t *testing.T) {
   217  			affinityCore, err := getEligibleCPU(tt.cpuset, tt.isolset)
   218  			eligible := affinityCore >= 0
   219  			if err != nil && !tt.expectedErr {
   220  				t.Fatalf("unexpected error: %s", err)
   221  			} else if err == nil && tt.expectedErr {
   222  				t.Fatalf("unexpected success")
   223  			} else if tt.expectedEligible && !eligible {
   224  				t.Fatalf("was expecting eligible core but no eligible core returned")
   225  			} else if !tt.expectedEligible && eligible {
   226  				t.Fatalf("was not expecting eligible core but got eligible core")
   227  			} else if tt.expectedEligible && tt.expectedAffinityCore != affinityCore {
   228  				t.Fatalf("expected affinity core %d: got %d instead", tt.expectedAffinityCore, affinityCore)
   229  			}
   230  		})
   231  	}
   232  }