Skip to content

Commit 9039d8a

Browse files
committed
libct/intelrdt: skip reading /proc/cpuinfo
Reading /proc/cpuinfo is a surprisingly expensive operation. Since kernel version 4.12 [1], opening /proc/cpuinfo on an x86 system can block for around 20 milliseconds while the kernel samples the current CPU frequency. There is a very recent patch [2] which gets rid of the delay, but has yet to make it into the mainline kenel. Regardless, kernels for which opening /proc/cpuinfo takes 20ms will continue to be run in production for years to come. libcontainer only opens /proc/cpuinfo to read the processor feature flags so all the delays to get an accurate snapshot of the CPU frequency are just wasted time. If we wanted to, we could interrogate the CPU features directly from userspace using the `CPUID` instruction. However, Intel and AMD CPUs have flags in different positions for their analogous sub-features and there are CPU quirks [3] which would need to be accounted for. Some Haswell server CPUs support RDT/CAT but are missing the `CPUID` flags advertising their support; the kernel checks for support on that processor family by probing the the hardware using privileged RDMSR/WRMSR instructions [4]. This sort of probing could not be implemented in userspace so it would not be possible to check for RDT feature support in userspace without false negatives on some hardware configurations. It looks like libcontainer reads the CPU feature flags as a kind of optimization so that it can skip checking whether the kernel supports an RDT sub-feature if the hardware support is missing. As the kernel only exposes subtrees in the `resctrl` filesystem for RDT sub-features with hardware and kernel support, checking the CPU feature flags is redundant from a correctness point of view. Remove the /proc/cpuinfo check as it is an optimization which actually hurts performance. [1]: https://unix.stackexchange.com/a/526679 [2]: https://lore.kernel.org/all/[email protected]/ [3]: https://github.com/torvalds/linux/blob/7cf6a8a17f5b134b7e783c2d45c53298faef82a7/arch/x86/kernel/cpu/resctrl/core.c#L834-L851 [4]: https://github.com/torvalds/linux/blob/a6b450573b912316ad36262bfc70e7c3870c56d1/arch/x86/kernel/cpu/resctrl/core.c#L111-L153 Signed-off-by: Cory Snider <[email protected]>
1 parent 3f0daac commit 9039d8a

File tree

1 file changed

+19
-84
lines changed

1 file changed

+19
-84
lines changed

libcontainer/intelrdt/intelrdt.go

Lines changed: 19 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package intelrdt
22

33
import (
4-
"bufio"
54
"bytes"
65
"errors"
76
"fmt"
@@ -201,45 +200,33 @@ func featuresInit() {
201200
return
202201
}
203202

204-
// 2. Check if hardware and kernel support Intel RDT sub-features.
205-
flagsSet, err := parseCpuInfoFile("/proc/cpuinfo")
206-
if err != nil {
207-
return
208-
}
209-
210-
// 3. Double check if Intel RDT sub-features are available in
211-
// "resource control" filesystem. Intel RDT sub-features can be
203+
// 2. Check if Intel RDT sub-features are available in "resource
204+
// control" filesystem. Intel RDT sub-features can be
212205
// selectively disabled or enabled by kernel command line
213206
// (e.g., rdt=!l3cat,mba) in 4.14 and newer kernel
214-
if flagsSet.CAT {
215-
if _, err := os.Stat(filepath.Join(root, "info", "L3")); err == nil {
216-
catEnabled = true
217-
}
207+
if _, err := os.Stat(filepath.Join(root, "info", "L3")); err == nil {
208+
catEnabled = true
218209
}
219210
if mbaScEnabled {
220-
// We confirm MBA Software Controller is enabled in step 2,
211+
// We confirm MBA Software Controller is enabled in step 1,
221212
// MBA should be enabled because MBA Software Controller
222213
// depends on MBA
223214
mbaEnabled = true
224-
} else if flagsSet.MBA {
225-
if _, err := os.Stat(filepath.Join(root, "info", "MB")); err == nil {
226-
mbaEnabled = true
227-
}
215+
} else if _, err := os.Stat(filepath.Join(root, "info", "MB")); err == nil {
216+
mbaEnabled = true
228217
}
229-
if flagsSet.MBMTotal || flagsSet.MBMLocal || flagsSet.CMT {
230-
if _, err := os.Stat(filepath.Join(root, "info", "L3_MON")); err != nil {
231-
return
232-
}
233-
enabledMonFeatures, err = getMonFeatures(root)
234-
if err != nil {
235-
return
236-
}
237-
if enabledMonFeatures.mbmTotalBytes || enabledMonFeatures.mbmLocalBytes {
238-
mbmEnabled = true
239-
}
240-
if enabledMonFeatures.llcOccupancy {
241-
cmtEnabled = true
242-
}
218+
if _, err := os.Stat(filepath.Join(root, "info", "L3_MON")); err != nil {
219+
return
220+
}
221+
enabledMonFeatures, err = getMonFeatures(root)
222+
if err != nil {
223+
return
224+
}
225+
if enabledMonFeatures.mbmTotalBytes || enabledMonFeatures.mbmLocalBytes {
226+
mbmEnabled = true
227+
}
228+
if enabledMonFeatures.llcOccupancy {
229+
cmtEnabled = true
243230
}
244231
})
245232
}
@@ -298,58 +285,6 @@ func Root() (string, error) {
298285
return intelRdtRoot, intelRdtRootErr
299286
}
300287

301-
type cpuInfoFlags struct {
302-
CAT bool // Cache Allocation Technology
303-
MBA bool // Memory Bandwidth Allocation
304-
305-
// Memory Bandwidth Monitoring related.
306-
MBMTotal bool
307-
MBMLocal bool
308-
309-
CMT bool // Cache Monitoring Technology
310-
}
311-
312-
func parseCpuInfoFile(path string) (cpuInfoFlags, error) {
313-
infoFlags := cpuInfoFlags{}
314-
315-
f, err := os.Open(path)
316-
if err != nil {
317-
return infoFlags, err
318-
}
319-
defer f.Close()
320-
321-
s := bufio.NewScanner(f)
322-
for s.Scan() {
323-
line := s.Text()
324-
325-
// Search "cat_l3" and "mba" flags in first "flags" line
326-
if strings.HasPrefix(line, "flags") {
327-
flags := strings.Split(line, " ")
328-
// "cat_l3" flag for CAT and "mba" flag for MBA
329-
for _, flag := range flags {
330-
switch flag {
331-
case "cat_l3":
332-
infoFlags.CAT = true
333-
case "mba":
334-
infoFlags.MBA = true
335-
case "cqm_mbm_total":
336-
infoFlags.MBMTotal = true
337-
case "cqm_mbm_local":
338-
infoFlags.MBMLocal = true
339-
case "cqm_occup_llc":
340-
infoFlags.CMT = true
341-
}
342-
}
343-
return infoFlags, nil
344-
}
345-
}
346-
if err := s.Err(); err != nil {
347-
return infoFlags, err
348-
}
349-
350-
return infoFlags, nil
351-
}
352-
353288
// Gets a single uint64 value from the specified file.
354289
func getIntelRdtParamUint(path, file string) (uint64, error) {
355290
fileName := filepath.Join(path, file)

0 commit comments

Comments
 (0)