Skip to content

Commit 598e890

Browse files
committed
Add AMD64 micro architecture level support
This commit adds the support for AMD64 micro architecture levels on Linux. Signed-off-by: Zhongcheng Lao <[email protected]>
1 parent db76a43 commit 598e890

File tree

7 files changed

+97
-9
lines changed

7 files changed

+97
-9
lines changed

cpuinfo.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import (
2323
"github.com/containerd/log"
2424
)
2525

26-
// Present the ARM instruction set architecture, eg: v7, v8
26+
// Present the instruction set architecture, eg: v7, v8 for ARM CPU,
27+
// v3, v4 for AMD64 CPU.
2728
// Don't use this value directly; call cpuVariant() instead.
2829
var cpuVariantValue string
2930

@@ -33,9 +34,15 @@ func cpuVariant() string {
3334
cpuVariantOnce.Do(func() {
3435
if isArmArch(runtime.GOARCH) {
3536
var err error
36-
cpuVariantValue, err = getCPUVariant()
37+
cpuVariantValue, err = getArmCPUVariant()
3738
if err != nil {
38-
log.L.Errorf("Error getCPUVariant for OS %s: %v", runtime.GOOS, err)
39+
log.L.Errorf("Error getArmCPUVariant for OS %s: %v", runtime.GOOS, err)
40+
}
41+
} else if isAmd64Arch(runtime.GOARCH) {
42+
var err error
43+
cpuVariantValue, err = getAmd64MicroArchLevel()
44+
if err != nil {
45+
log.L.Errorf("Error getAmd64MicroArchLevel for OS %s: %v", runtime.GOOS, err)
3946
}
4047
}
4148
})

cpuinfo_linux.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ func getCPUVariantFromArch(arch string) (string, error) {
106106
return variant, nil
107107
}
108108

109-
// getCPUVariant returns cpu variant for ARM
109+
// getArmCPUVariant returns cpu variant for ARM
110110
// We first try reading "Cpu architecture" field from /proc/cpuinfo
111111
// If we can't find it, then fall back using a system call
112112
// This is to cover running ARM in emulated environment on x86 host as this field in /proc/cpuinfo
113113
// was not present.
114-
func getCPUVariant() (string, error) {
114+
func getArmCPUVariant() (string, error) {
115115
variant, err := getCPUInfo("Cpu architecture")
116116
if err != nil {
117117
if errors.Is(err, errNotFound) {
@@ -158,3 +158,40 @@ func getCPUVariant() (string, error) {
158158

159159
return variant, nil
160160
}
161+
162+
func getAmd64MicroArchLevel() (string, error) {
163+
flags, err := getCPUInfo("flags")
164+
if errors.Is(err, errNotFound) {
165+
return "", fmt.Errorf("failure getting CPU flags: %v", err)
166+
}
167+
168+
containsAll := func(set map[string]interface{}, toMatch []string) bool {
169+
for _, m := range toMatch {
170+
if _, ok := set[m]; !ok {
171+
return false
172+
}
173+
}
174+
return true
175+
}
176+
177+
flagSet := map[string]interface{}{}
178+
for _, flag := range strings.Split(flags, " ") {
179+
flagSet[flag] = true
180+
}
181+
182+
// https://unix.stackexchange.com/questions/631217/how-do-i-check-if-my-cpu-supports-x86-64-v2
183+
level := 1
184+
if containsAll(flagSet, []string{"lm", "cmov", "cx8", "fpu", "fxsr", "mmx", "syscall", "sse2"}) {
185+
level = 1
186+
}
187+
if level == 1 && containsAll(flagSet, []string{"cx16", "lahf_lm", "popcnt", "sse4_1", "sse4_2", "ssse3"}) {
188+
level = 2
189+
}
190+
if level == 2 && containsAll(flagSet, []string{"avx", "avx2", "bmi1", "bmi2", "f16c", "fma", "abm", "movbe", "xsave"}) {
191+
level = 3
192+
}
193+
if level == 3 && containsAll(flagSet, []string{"avx512f", "avx512bw", "avx512cd", "avx512dq", "avx512vl"}) {
194+
level = 4
195+
}
196+
return fmt.Sprintf("v%d", level), nil
197+
}

cpuinfo_linux_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestCPUVariant(t *testing.T) {
2929

3030
variants := []string{"v8", "v7", "v6", "v5", "v4", "v3"}
3131

32-
p, err := getCPUVariant()
32+
p, err := getArmCPUVariant()
3333
if err != nil {
3434
t.Fatalf("Error getting CPU variant: %v", err)
3535
return
@@ -137,3 +137,26 @@ func TestGetCPUVariantFromArch(t *testing.T) {
137137

138138
}
139139
}
140+
141+
func TestGetAmd64MicroArchLevel(t *testing.T) {
142+
if !isAmd64Arch(runtime.GOARCH) {
143+
t.Skip("only relevant on linux/amd64")
144+
}
145+
146+
supportedLevels := []string{"v1", "v2", "v3", "v4"}
147+
148+
actualLevel, err := getAmd64MicroArchLevel()
149+
if err != nil {
150+
t.Fatalf("Error getting AMD64 micro architecture level: %v", err)
151+
return
152+
}
153+
154+
for _, level := range supportedLevels {
155+
if actualLevel == level {
156+
t.Logf("got valid micro architecture level as expected: %#v = %#v", actualLevel, level)
157+
return
158+
}
159+
}
160+
161+
t.Fatalf("could not get valid micro architecture levels as expected: %v", supportedLevels)
162+
}

cpuinfo_other.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"runtime"
2424
)
2525

26-
func getCPUVariant() (string, error) {
26+
func getArmCPUVariant() (string, error) {
2727

2828
var variant string
2929

@@ -48,8 +48,13 @@ func getCPUVariant() (string, error) {
4848
variant = "unknown"
4949
}
5050
} else {
51-
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
51+
return "", fmt.Errorf("getArmCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
5252
}
5353

5454
return variant, nil
5555
}
56+
57+
func getAmd64MicroArchLevel() (string, error) {
58+
// return v1 on non-Linux platforms
59+
return "v1", nil
60+
}

database.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ func isArmArch(arch string) bool {
4848
return false
4949
}
5050

51+
// isAmd64Arch returns true if the architecture is AMD64.
52+
//
53+
// The arch value should be normalized before being passed to this function.
54+
func isAmd64Arch(arch string) bool {
55+
return arch == "amd64"
56+
}
57+
5158
// isKnownArch returns true if we know about the architecture.
5259
//
5360
// The arch value should be normalized before being passed to this function.

defaults_unix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func DefaultSpec() specs.Platform {
2929
return specs.Platform{
3030
OS: runtime.GOOS,
3131
Architecture: runtime.GOARCH,
32-
// The Variant field will be empty if arch != ARM.
32+
// The Variant field will be empty if arch != ARM and AMD64.
3333
Variant: cpuVariant(),
3434
}
3535
}

platforms_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,15 @@ func TestParseSelector(t *testing.T) {
248248
},
249249
formatted: "linux/amd64",
250250
},
251+
{
252+
input: "Linux/x86_64/v2",
253+
expected: specs.Platform{
254+
OS: "linux",
255+
Architecture: "amd64",
256+
Variant: "v2",
257+
},
258+
formatted: "linux/amd64/v2",
259+
},
251260
{
252261
input: "i386",
253262
expected: specs.Platform{

0 commit comments

Comments
 (0)