From 4355ba2f83411e2ff3869334ce1342a2c45cdec3 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Sat, 30 Sep 2017 14:21:23 +0800 Subject: [PATCH 1/5] ctr: Add helpers to database to check Linux and ARM arch On ARM platforms, we have to prepare the variant for spec.platform. Currently, this action would only work on Linux/ARM system. We introduce these two helpers to check running system's OS and Arch. Change-Id: Iff14087699219413779dd6caf1bf9524db1cc19e Signed-off-by: Wei Chen --- platforms/database.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/platforms/database.go b/platforms/database.go index bd66e2517..362b005a6 100644 --- a/platforms/database.go +++ b/platforms/database.go @@ -5,6 +5,13 @@ import ( "strings" ) +// isLinuxOS returns true if the operating system is Linux. +// +// The OS value should be normalized before calling this function. +func isLinuxOS(os string) bool { + return os == "linux" +} + // These function are generated from from https://golang.org/src/go/build/syslist.go. // // We use switch statements because they are slightly faster than map lookups @@ -21,6 +28,17 @@ func isKnownOS(os string) bool { return false } +// isArmArch returns true if the architecture is ARM. +// +// The arch value should be normalized before being passed to this function. +func isArmArch(arch string) bool { + switch arch { + case "arm", "arm64": + return true + } + return false +} + // isKnownArch returns true if we know about the architecture. // // The arch value should be normalized before being passed to this function. From a047abb1f633867a593b8490173d03743bbcebbf Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Fri, 29 Sep 2017 16:36:12 +0800 Subject: [PATCH 2/5] ctr: Identify the platform.variant for ARM platforms In the commit "26329b2b8d7fd4e290b2b0f0163547f2d79bb817", dmcgowan/fix-pull-multi-arch, PR#1535. The containerd has enabled the pull multi-arch image support. But the platform.variant field of OCI for ARM hadn't been ready at that time, so all ARM images could not pull successfully. " docker.io/library/hello-world:latest: resolved |++++++++++++++++++++++++++++++++++++++| index-sha256:3644c0788e3d3823f9e97f757f01d2ddc6eb5458df9d801: done |++++++++++++++++++++++++++++++++++++++| elapsed: 5.1 s total: 2.7 Ki (533.0 B/s) unpacking sha256:3d3823f9e97f757f01d2ddc6eb5458df9d801... ctr: : manifest not found: not found " In this patch we'll detect the ARM variants from /proc/cpuinfo. Because Linux kernel has already detected the ABI, ISA and Features for us. We don't need to parse them from registers again. Change-Id: I479b34bf3f52df9f7a6b3c77718b7d316dbf7f69 Signed-off-by: Wei Chen Signed-off-by: Penny Zheng --- platforms/cpuinfo.go | 85 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 platforms/cpuinfo.go diff --git a/platforms/cpuinfo.go b/platforms/cpuinfo.go new file mode 100644 index 000000000..b7c23cc19 --- /dev/null +++ b/platforms/cpuinfo.go @@ -0,0 +1,85 @@ +package platforms + +import ( + "bufio" + "os" + "runtime" + "strings" + + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + "github.com/pkg/errors" +) + +// Present the ARM instruction set architecture, eg: v7, v8 +var cpuVariant string + +func init() { + if isArmArch(runtime.GOARCH) { + cpuVariant = getCPUVariant() + } else { + cpuVariant = "" + } +} + +// For Linux, the kernel has already detected the ABI, ISA and Features. +// So we don't need to access the ARM registers to detect platform information +// by ourselves. We can just parse these information from /proc/cpuinfo +func getCPUInfo(pattern string) (info string, err error) { + if !isLinuxOS(runtime.GOOS) { + return "", errors.Wrapf(errdefs.ErrNotImplemented, "getCPUInfo for OS %s", runtime.GOOS) + } + + cpuinfo, err := os.Open("/proc/cpuinfo") + if err != nil { + return "", err + } + defer cpuinfo.Close() + + // Start to Parse the Cpuinfo line by line. For SMP SoC, we parse + // the first core is enough. + scanner := bufio.NewScanner(cpuinfo) + for scanner.Scan() { + newline := scanner.Text() + list := strings.Split(newline, ":") + + if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) { + return strings.TrimSpace(list[1]), nil + } + } + + // Check whether the scanner encountered errors + err = scanner.Err() + if err != nil { + return "", err + } + + return "", errors.Wrapf(errdefs.ErrNotFound, "getCPUInfo for pattern: %s", pattern) +} + +func getCPUVariant() string { + variant, err := getCPUInfo("Cpu architecture") + if err != nil { + log.L.WithError(err).Error("failure getting variant") + return "" + } + + switch variant { + case "8": + variant = "v8" + case "7", "7M", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": + variant = "v7" + case "6", "6TEJ": + variant = "v6" + case "5", "5T", "5TE", "5TEJ": + variant = "v5" + case "4", "4T": + variant = "v4" + case "3": + variant = "v3" + default: + variant = "unknown" + } + + return variant +} From 3192b36ab90f35dbd51918b72d0ecbe7b7c46a1b Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Sat, 30 Sep 2017 14:31:45 +0800 Subject: [PATCH 3/5] ctr: Add variant to ARM's DefaultSpec string The variant is required for platform match while pulling images for ARM platforms. Currently, the cpuVariant only would be assigned on linux/arm|arm64 platforms. Other platforms this variable would be empty. So we can use this cpuVariant to initialize the Variant field. Change-Id: Ic065be9b502f1e662445daa61a0973bf56385b37 Signed-off-by: Wei Chen Signed-off-by: Penny Zheng --- platforms/defaults.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platforms/defaults.go b/platforms/defaults.go index 2b57b4979..ed493ab2c 100644 --- a/platforms/defaults.go +++ b/platforms/defaults.go @@ -16,6 +16,7 @@ func DefaultSpec() specs.Platform { return specs.Platform{ OS: runtime.GOOS, Architecture: runtime.GOARCH, - // TODO(stevvooe): Need to resolve GOARM for arm hosts. + // The Variant field will be empty if arch != ARM. + Variant: cpuVariant, } } From 2517e13a5e7231029477bbfd02835e490c7b4a48 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Mon, 16 Oct 2017 15:43:53 +0800 Subject: [PATCH 4/5] ctr: Add Variant field to default test expected result This test case should cover the variant field now, as we have add Variant to default value of spec.platform. Change-Id: I8359007d40a4b8f6a072510fff2ba604a062afa1 Signed-off-by: Wei Chen Signed-off-by: Penny Zheng --- platforms/defaults_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/platforms/defaults_test.go b/platforms/defaults_test.go index 39f2e485d..db8e88ff3 100644 --- a/platforms/defaults_test.go +++ b/platforms/defaults_test.go @@ -12,6 +12,7 @@ func TestDefault(t *testing.T) { expected := specs.Platform{ OS: runtime.GOOS, Architecture: runtime.GOARCH, + Variant: cpuVariant, } p := DefaultSpec() if !reflect.DeepEqual(p, expected) { From cbc33018b3aa70a741565cc8066e4f0690e6409d Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Mon, 16 Oct 2017 16:23:36 +0800 Subject: [PATCH 5/5] ctr: Add a test file to test cpu getCpuVariant Change-Id: Id9558de2e41b08c41cf7d4b458774e99e24515a0 Signed-off-by: Wei Chen Signed-off-by: Penny Zheng --- platforms/cpuinfo_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 platforms/cpuinfo_test.go diff --git a/platforms/cpuinfo_test.go b/platforms/cpuinfo_test.go new file mode 100644 index 000000000..95911efe0 --- /dev/null +++ b/platforms/cpuinfo_test.go @@ -0,0 +1,24 @@ +package platforms + +import ( + "runtime" + "testing" +) + +func TestCPUVariant(t *testing.T) { + if !isArmArch(runtime.GOARCH) || !isLinuxOS(runtime.GOOS) { + t.Skip("only relevant on linux/arm") + } + + variants := []string{"v8", "v7", "v6", "v5", "v4", "v3"} + + p := getCPUVariant() + for _, variant := range variants { + if p == variant { + t.Logf("got valid variant as expected: %#v = %#v\n", p, variant) + return + } + } + + t.Fatalf("could not get valid variant as expected: %v\n", variants) +}