Skip to content

Commit 01b40b7

Browse files
committed
Add support for lscpu_check TuneD built-in
This change adds support for lscpu_check TuneD built-in. Other changes * Drop the severity from ERROR to WARNING in execTuneDBuiltin() * Be more specific what is the impact of calling unsupported TuneD built-in function Resolves: OCPBUGS-66214
1 parent dae717e commit 01b40b7

File tree

2 files changed

+175
-4
lines changed

2 files changed

+175
-4
lines changed

pkg/tuned/tuned_parser.go

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io" // io.EOF
77
"os" // os.Stat()
88
"os/exec" // os.Exec()
9+
"regexp" // regexp.Compile()
910
"strings" // strings.Split()
1011
"syscall" // syscall.SIGHUP, ...
1112
"time" // time.Second, ...
@@ -309,6 +310,49 @@ func execCmd(command []string) (string, error) {
309310
return out, nil
310311
}
311312

313+
// lscpu_check checks `lscpuStr` (lscpu output) against regular expressions.
314+
// Accepts args in the form: REGEX1, STR1, REGEX2, STR2, ...[, STR_FALLBACK]
315+
// If REGEX1 matches something in the output it returns STR1,
316+
// if REGEX2 matches it returns STR2, etc.
317+
// It stops on the first match. If no regex matches, it returns STR_FALLBACK
318+
// (or empty string if no fallback is provided).
319+
func lscpu_check(lscpuStr string, args []string) (string, error) {
320+
var fallback string
321+
322+
if len(args) < 2 {
323+
return "", fmt.Errorf("lscpu_check requires at least 2 arguments, got %d", len(args))
324+
}
325+
326+
// See if we have a fallback (odd number of args).
327+
if len(args)%2 == 1 {
328+
fallback = args[len(args)-1]
329+
args = args[:len(args)-1]
330+
}
331+
332+
for i := 0; i < len(args); i += 2 {
333+
regexStr := args[i]
334+
resultStr := args[i+1]
335+
336+
// Enable multi-line matching: (?m) makes ^ and $ match line boundaries.
337+
// Only add (?m) if not already present.
338+
if len(regexStr) < 4 || regexStr[:4] != "(?m)" {
339+
regexStr = "(?m)" + regexStr
340+
}
341+
342+
re, err := regexp.Compile(regexStr)
343+
if err != nil {
344+
return "", fmt.Errorf("invalid regex '%s': %w", regexStr, err)
345+
}
346+
347+
if re.MatchString(lscpuStr) {
348+
return resultStr, nil
349+
}
350+
}
351+
352+
// No match found, return fallback.
353+
return fallback, nil
354+
}
355+
312356
// execTuneDBuiltin executes TuneD built-in function 'function' with
313357
// arguments 'args'. Returns the result/expansion of running the built-in.
314358
// If the execution of the built-in fails, returns the string 'onFail'.
@@ -317,7 +361,7 @@ func execTuneDBuiltin(function string, args []string, onFail string) string {
317361
case "exec":
318362
out, err := execCmd(args)
319363
if err != nil {
320-
klog.Errorf("error calling built-in exec: %v", err)
364+
klog.Warningf("failure calling built-in exec: %v", err)
321365
return onFail
322366
}
323367
return out
@@ -330,21 +374,35 @@ func execTuneDBuiltin(function string, args []string, onFail string) string {
330374
// argument 2. Note the expansion to argument 2 is done also on error
331375
// to match the semantics of the TuneD "virt_check".
332376
if len(args) != 2 {
333-
klog.Errorf("built-in \"virt_check\" requires 2 arguments")
377+
klog.Warningf("built-in \"virt_check\" requires 2 arguments")
334378
return onFail
335379
}
336380
out, err := execCmd([]string{"virt-what"})
337381
if err == nil && len(out) > 0 {
338382
return args[0]
339383
}
340384
if err != nil {
341-
klog.Errorf("failure calling built-in exec: %v", err)
385+
klog.Warningf("failure calling built-in exec: %v", err)
342386
}
343387

344388
return args[1]
345389

390+
case "lscpu_check":
391+
lscpuOut, err := execCmd([]string{"lscpu"})
392+
if err != nil {
393+
klog.Warningf("failure calling built-in lscpu_check: %v", err)
394+
return onFail
395+
}
396+
out, err := lscpu_check(lscpuOut, args)
397+
if err != nil {
398+
klog.Warningf("failure calling built-in lscpu_check: %v", err)
399+
return onFail
400+
}
401+
402+
return out
403+
346404
default:
347-
klog.Errorf("calling unsupported built-in: %v", function)
405+
klog.Warningf("calling unsupported built-in: %q, TuneD reloads on config changes may not work properly", function)
348406
// unsupported built-in
349407
}
350408

pkg/tuned/tuned_parser_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,116 @@ func TestBuiltinExpansion(t *testing.T) {
6767
}
6868
}
6969
}
70+
71+
func TestLscpuCheckARM(t *testing.T) {
72+
lscpuARM := `Architecture: aarch64
73+
CPU op-mode(s): 32-bit, 64-bit
74+
Byte Order: Little Endian
75+
CPU(s): 80
76+
On-line CPU(s) list: 0-79
77+
Vendor ID: ARM
78+
BIOS Vendor ID: Ampere(R)
79+
Model name: Neoverse-N1
80+
BIOS Model name: Ampere(R) Altra(R) Processor Q80-30 CPU @ 3.0GHz
81+
BIOS CPU family: 257
82+
Model: 1
83+
Thread(s) per core: 1
84+
Core(s) per socket: 80
85+
Socket(s): 1
86+
Stepping: r3p1
87+
Frequency boost: disabled
88+
CPU(s) scaling MHz: 100%
89+
CPU max MHz: 3000.0000
90+
CPU min MHz: 1000.0000
91+
BogoMIPS: 50.00
92+
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
93+
L1d cache: 5 MiB (80 instances)
94+
L1i cache: 5 MiB (80 instances)
95+
L2 cache: 80 MiB (80 instances)
96+
NUMA node(s): 1
97+
NUMA node0 CPU(s): 0-79
98+
Vulnerability Gather data sampling: Not affected
99+
Vulnerability Itlb multihit: Not affected
100+
Vulnerability L1tf: Not affected
101+
Vulnerability Mds: Not affected
102+
Vulnerability Meltdown: Not affected
103+
Vulnerability Mmio stale data: Not affected
104+
Vulnerability Reg file data sampling: Not affected
105+
Vulnerability Retbleed: Not affected
106+
Vulnerability Spec rstack overflow: Not affected
107+
Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl
108+
Vulnerability Spectre v1: Mitigation; __user pointer sanitization
109+
Vulnerability Spectre v2: Mitigation; CSV2, BHB
110+
Vulnerability Srbds: Not affected
111+
Vulnerability Tsx async abort: Not affected
112+
`
113+
114+
tests := []struct {
115+
name string
116+
args []string
117+
want string
118+
wantErr bool
119+
}{
120+
{
121+
name: "Match ARM Vendor ID",
122+
args: []string{"Vendor ID\\:\\s*GenuineIntel", "intel", "Vendor ID\\:\\s*AuthenticAMD", "amd", "Architecture\\:\\s*aarch64", "arm", "unknown"},
123+
want: "arm",
124+
wantErr: false,
125+
},
126+
{
127+
name: "Match aarch64 architecture",
128+
args: []string{"Architecture\\:\\s*x86_64", "x86", "Architecture\\:\\s*aarch64", "aarch64"},
129+
want: "aarch64",
130+
wantErr: false,
131+
},
132+
{
133+
name: "No match returns fallback",
134+
args: []string{"NonExistentPattern", "result", "fallback"},
135+
want: "fallback",
136+
wantErr: false,
137+
},
138+
{
139+
name: "No match no fallback returns empty string",
140+
args: []string{"AuthenticAMD", "amd"},
141+
want: "",
142+
wantErr: false,
143+
},
144+
{
145+
name: "First match wins",
146+
args: []string{"^Architecture", "aarch64", "ARM", "arm", "unknown"},
147+
want: "aarch64",
148+
wantErr: false,
149+
},
150+
{
151+
name: "One argument returns error",
152+
args: []string{"fallback"},
153+
want: "",
154+
wantErr: true,
155+
},
156+
{
157+
name: "Zero arguments returns error",
158+
args: []string{},
159+
want: "",
160+
wantErr: true,
161+
},
162+
{
163+
name: "Invalid regex returns error",
164+
args: []string{"[invalid(regex", "result"},
165+
want: "",
166+
wantErr: true,
167+
},
168+
}
169+
170+
for _, tc := range tests {
171+
t.Run(tc.name, func(t *testing.T) {
172+
got, err := lscpu_check(lscpuARM, tc.args)
173+
if (err != nil) != tc.wantErr {
174+
t.Errorf("lscpu_check() error = %v, wantErr %v", err, tc.wantErr)
175+
return
176+
}
177+
if got != tc.want {
178+
t.Errorf("lscpu_check() = %v, want %v", got, tc.want)
179+
}
180+
})
181+
}
182+
}

0 commit comments

Comments
 (0)