Skip to content

Commit ddc18ea

Browse files
committed
e2e: topomgr: autodetect NUMA position of VF devs
Add autodetection code to figure out on which NUMA node are the devices attached to. This autodetection work under the assumption all the VFs in the system must be used for the tests. Should not this be the case, or in general to handle non-trivial configurations, we keep the annotations mechanism added to the SRIOV device plugin config map. Signed-off-by: Francesco Romani <[email protected]>
1 parent 0c2827c commit ddc18ea

File tree

2 files changed

+97
-7
lines changed

2 files changed

+97
-7
lines changed

test/e2e_node/numa_alignment.go

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package e2enode
1919
import (
2020
"fmt"
2121
"io/ioutil"
22+
"os"
23+
"path/filepath"
2224
"sort"
2325
"strconv"
2426
"strings"
@@ -153,12 +155,7 @@ func getPCIDeviceToNumaNodeMapFromEnv(f *framework.Framework, pod *v1.Pod, envir
153155
for _, pciDev := range pciDevs {
154156
pciDevNUMANode := f.ExecCommandInContainer(pod.Name, pod.Spec.Containers[0].Name,
155157
"/bin/cat", fmt.Sprintf("/sys/bus/pci/devices/%s/numa_node", pciDev))
156-
157-
nodeNum, err := strconv.Atoi(pciDevNUMANode)
158-
if err != nil {
159-
return nil, err
160-
}
161-
NUMAPerDev[pciDev] = nodeNum
158+
NUMAPerDev[pciDev] = numaNodeFromSysFsEntry(pciDevNUMANode)
162159
}
163160
}
164161
if len(NUMAPerDev) == 0 {
@@ -209,3 +206,56 @@ func checkNUMAAlignment(f *framework.Framework, pod *v1.Pod, logs string, numaNo
209206
}
210207
return numaRes, nil
211208
}
209+
210+
type pciDeviceInfo struct {
211+
Address string
212+
NUMANode int
213+
IsPhysFn bool
214+
IsVFn bool
215+
}
216+
217+
func getPCIDeviceInfo(sysPCIDir string) ([]pciDeviceInfo, error) {
218+
var pciDevs []pciDeviceInfo
219+
220+
entries, err := ioutil.ReadDir(sysPCIDir)
221+
if err != nil {
222+
return nil, err
223+
}
224+
225+
for _, entry := range entries {
226+
isPhysFn := false
227+
isVFn := false
228+
if _, err := os.Stat(filepath.Join(sysPCIDir, entry.Name(), "sriov_numvfs")); err == nil {
229+
isPhysFn = true
230+
} else if !os.IsNotExist(err) {
231+
// unexpected error. Bail out
232+
return nil, err
233+
}
234+
if _, err := os.Stat(filepath.Join(sysPCIDir, entry.Name(), "physfn")); err == nil {
235+
isVFn = true
236+
} else if !os.IsNotExist(err) {
237+
// unexpected error. Bail out
238+
return nil, err
239+
}
240+
241+
content, err := ioutil.ReadFile(filepath.Join(sysPCIDir, entry.Name(), "numa_node"))
242+
if err != nil {
243+
return nil, err
244+
}
245+
246+
pciDevs = append(pciDevs, pciDeviceInfo{
247+
Address: entry.Name(),
248+
NUMANode: numaNodeFromSysFsEntry(string(content)),
249+
IsPhysFn: isPhysFn,
250+
IsVFn: isVFn,
251+
})
252+
}
253+
254+
return pciDevs, nil
255+
}
256+
257+
func numaNodeFromSysFsEntry(content string) int {
258+
nodeNum, err := strconv.Atoi(strings.TrimSpace(content))
259+
framework.ExpectNoError(err, "error detecting the device numa_node from sysfs: %v", err)
260+
return nodeNum
261+
}

test/e2e_node/topology_manager_test.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func makeTopologyManagerTestPod(podName, podCmd string, tmCtnAttributes []tmCtnA
134134
}
135135
}
136136

137-
func findNUMANodeWithoutSRIOVDevices(configMap *v1.ConfigMap, numaNodes int) (int, bool) {
137+
func findNUMANodeWithoutSRIOVDevicesFromConfigMap(configMap *v1.ConfigMap, numaNodes int) (int, bool) {
138138
for nodeNum := 0; nodeNum < numaNodes; nodeNum++ {
139139
value, ok := configMap.Annotations[fmt.Sprintf("pcidevice_node%d", nodeNum)]
140140
if !ok {
@@ -154,6 +154,46 @@ func findNUMANodeWithoutSRIOVDevices(configMap *v1.ConfigMap, numaNodes int) (in
154154
return -1, false
155155
}
156156

157+
func findNUMANodeWithoutSRIOVDevicesFromSysfs(numaNodes int) (int, bool) {
158+
pciDevs, err := getPCIDeviceInfo("/sys/bus/pci/devices")
159+
if err != nil {
160+
framework.Failf("error detecting the PCI device NUMA node: %v", err)
161+
}
162+
163+
pciPerNuma := make(map[int]int)
164+
for _, pciDev := range pciDevs {
165+
if pciDev.IsVFn {
166+
pciPerNuma[pciDev.NUMANode]++
167+
}
168+
}
169+
170+
if len(pciPerNuma) == 0 {
171+
// if we got this far we already passed a rough check that SRIOV devices
172+
// are available in the box, so something is seriously wrong
173+
framework.Failf("failed to find any VF devices from %v", pciDevs)
174+
}
175+
176+
for nodeNum := 0; nodeNum < numaNodes; nodeNum++ {
177+
v := pciPerNuma[nodeNum]
178+
if v == 0 {
179+
framework.Logf("NUMA node %d has no SRIOV devices attached", nodeNum)
180+
return nodeNum, true
181+
}
182+
framework.Logf("NUMA node %d has %d SRIOV devices attached", nodeNum, v)
183+
}
184+
return -1, false
185+
}
186+
187+
func findNUMANodeWithoutSRIOVDevices(configMap *v1.ConfigMap, numaNodes int) (int, bool) {
188+
// if someone annotated the configMap, let's use this information
189+
if nodeNum, found := findNUMANodeWithoutSRIOVDevicesFromConfigMap(configMap, numaNodes); found {
190+
return nodeNum, found
191+
}
192+
// no annotations, try to autodetect
193+
// NOTE: this assumes all the VFs in the box can be used for the tests.
194+
return findNUMANodeWithoutSRIOVDevicesFromSysfs(numaNodes)
195+
}
196+
157197
func configureTopologyManagerInKubelet(f *framework.Framework, oldCfg *kubeletconfig.KubeletConfiguration, policy string, configMap *v1.ConfigMap, numaNodes int) string {
158198
// Configure Topology Manager in Kubelet with policy.
159199
newCfg := oldCfg.DeepCopy()

0 commit comments

Comments
 (0)