From ee199cfb18e242e8854a3f7c6aa2493bddf0b490 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 6 Sep 2022 12:48:34 -0700 Subject: Use a minimum of 1 thread per core for CPU total calculation (#1756) In some LXD containers running with VT-x hardware virtualization, it appears that threads per core is 0, resulting in a total number of CPUs of 0. Add a test case and handle some quirks with the `lscpu` output. Closes https://github.com/chef/ohai/issues/1755 Signed-off-by: Stan Hu --- lib/ohai/plugins/cpu.rb | 3 +- spec/data/plugins/cpuinfo-x86-guest-vtx.output | 53 +++++++++++++ spec/data/plugins/lscpu-x86-guest-vtx-cores.output | 6 ++ spec/data/plugins/lscpu-x86-guest-vtx.output | 26 +++++++ spec/unit/plugins/linux/cpu_spec.rb | 89 ++++++++++++++++++++-- 5 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 spec/data/plugins/cpuinfo-x86-guest-vtx.output create mode 100644 spec/data/plugins/lscpu-x86-guest-vtx-cores.output create mode 100644 spec/data/plugins/lscpu-x86-guest-vtx.output diff --git a/lib/ohai/plugins/cpu.rb b/lib/ohai/plugins/cpu.rb index 1d37c39f..897f716b 100644 --- a/lib/ohai/plugins/cpu.rb +++ b/lib/ohai/plugins/cpu.rb @@ -193,7 +193,8 @@ Ohai.plugin(:CPU) do lscpu_real = lscpu_info[:sockets] lscpu_cores = lscpu_info[:sockets] * lscpu_info[:cores_per_socket] else - lscpu_total = lscpu_info[:sockets] * lscpu_info[:cores_per_socket] * lscpu_info[:threads_per_core] + threads_per_core = [lscpu_info[:threads_per_core], 1].max + lscpu_total = lscpu_info[:sockets] * lscpu_info[:cores_per_socket] * threads_per_core lscpu_real = lscpu_info[:sockets] lscpu_cores = lscpu_info[:sockets] * lscpu_info[:cores_per_socket] end diff --git a/spec/data/plugins/cpuinfo-x86-guest-vtx.output b/spec/data/plugins/cpuinfo-x86-guest-vtx.output new file mode 100644 index 00000000..645601f0 --- /dev/null +++ b/spec/data/plugins/cpuinfo-x86-guest-vtx.output @@ -0,0 +1,53 @@ +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 26 +model name : Intel(R) Xeon(R) CPU X5570 @ 2.93GHz +stepping : 5 +microcode : 0x1d +cpu MHz : 1703.430 +cache size : 8192 KB +physical id : 1 +siblings : 8 +core id : 2 +cpu cores : 4 +apicid : 21 +initial apicid : 21 +fpu : yes +fpu_exception : yes +cpuid level : 11 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt lahf_lm pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid dtherm ida flush_l1d +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit +bogomips : 5851.81 +clflush size : 64 +cache_alignment : 64 +address sizes : 40 bits physical, 48 bits virtual +power management: + +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 26 +model name : Intel(R) Xeon(R) CPU X5570 @ 2.93GHz +stepping : 5 +microcode : 0x1d +cpu MHz : 1749.755 +cache size : 8192 KB +physical id : 0 +siblings : 8 +core id : 2 +cpu cores : 4 +apicid : 5 +initial apicid : 5 +fpu : yes +fpu_exception : yes +cpuid level : 11 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt lahf_lm pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid dtherm ida flush_l1d +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit +bogomips : 5851.58 +clflush size : 64 +cache_alignment : 64 +address sizes : 40 bits physical, 48 bits virtual +power management: diff --git a/spec/data/plugins/lscpu-x86-guest-vtx-cores.output b/spec/data/plugins/lscpu-x86-guest-vtx-cores.output new file mode 100644 index 00000000..bbce1d8e --- /dev/null +++ b/spec/data/plugins/lscpu-x86-guest-vtx-cores.output @@ -0,0 +1,6 @@ +# The following is the parsable format, which can be fed to other +# programs. Each different item in every column has an unique ID +# starting from zero. +# CPU,Core,Socket +12,4,0 +13,5,1 diff --git a/spec/data/plugins/lscpu-x86-guest-vtx.output b/spec/data/plugins/lscpu-x86-guest-vtx.output new file mode 100644 index 00000000..01bb1137 --- /dev/null +++ b/spec/data/plugins/lscpu-x86-guest-vtx.output @@ -0,0 +1,26 @@ +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Byte Order: Little Endian +Address sizes: 40 bits physical, 48 bits virtual +CPU(s): 16 +On-line CPU(s) list: 0,2,5,9 +Off-line CPU(s) list: 1,3,4,6-8,10-15 +Thread(s) per core: 0 +Core(s) per socket: 4 +Socket(s): 2 +NUMA node(s): 2 +Vendor ID: GenuineIntel +CPU family: 6 +Model: 26 +Model name: Intel(R) Xeon(R) CPU X5570 @ 2.93GHz +Stepping: 5 +CPU MHz: 1596.085 +BogoMIPS: 5852.18 +Virtualization: VT-x +L1d cache: 32K +L1i cache: 32K +L2 cache: 256K +L3 cache: 8192K +NUMA node0 CPU(s): 0,2,4,6,8,10,12,14 +NUMA node1 CPU(s): 1,3,5,7,9,11,13,15 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt lahf_lm pti tpr_shadow vnmi flexpriority ept vpid dtherm ida diff --git a/spec/unit/plugins/linux/cpu_spec.rb b/spec/unit/plugins/linux/cpu_spec.rb index 92acdce6..c011a2bd 100644 --- a/spec/unit/plugins/linux/cpu_spec.rb +++ b/spec/unit/plugins/linux/cpu_spec.rb @@ -18,7 +18,7 @@ require "spec_helper" -shared_examples "Common cpu info" do |total_cpu, real_cpu, ls_cpu, architecture, cpu_opmodes, byte_order, cpus, +shared_examples "Common cpu info" do |default_cpu, total_cpu, real_cpu, ls_cpu, architecture, cpu_opmodes, byte_order, cpus, cpus_online, threads_per_core, cores_per_socket, sockets, numa_nodes, vendor_id, model, model_name, bogomips, l1d_cache, l1i_cache, l2_cache, l3_cache, flags, numa_node_cpus| describe "cpu" do @@ -27,9 +27,9 @@ shared_examples "Common cpu info" do |total_cpu, real_cpu, ls_cpu, architecture, expect(plugin[:cpu][:total]).to eq(total_cpu) end - it "has a cpu 0" do + it "has a default CPU" do plugin.run - expect(plugin[:cpu]).to have_key("0") + expect(plugin[:cpu]).to have_key(default_cpu) end if ls_cpu @@ -84,10 +84,10 @@ shared_examples "Common cpu info" do |total_cpu, real_cpu, ls_cpu, architecture, expect(plugin[:cpu]["cores_per_socket"]).to eq(cores_per_socket) end - it "cpu 0 has cores to #{cores_per_socket}" do + it "default CPU has cores to #{cores_per_socket}" do plugin.run - expect(plugin[:cpu]["0"]).to have_key("cores") - expect(plugin[:cpu]["0"]["cores"]).to eq(cores_per_socket.to_s) + expect(plugin[:cpu][default_cpu]).to have_key("cores") + expect(plugin[:cpu][default_cpu]["cores"]).to eq(cores_per_socket.to_s) end # s390x shows this differently @@ -385,6 +385,7 @@ describe Ohai::System, "General Linux cpu plugin" do let(:cpuinfo_contents) { File.read(File.join(SPEC_PLUGIN_PATH, "cpuinfo-no-cores.output")) } it_behaves_like "Common cpu info", + "0", # default_cpu 1, # total_cpu 1, # real_cpu false # ls_cpu @@ -478,6 +479,7 @@ describe Ohai::System, "General Linux cpu plugin" do numa_node_cpus = { "0" => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] } it_behaves_like "Common cpu info", + "0", # default_cpu 24, # total_cpu 2, # real_cpu true, # ls_cpu @@ -553,6 +555,7 @@ describe Ohai::System, "General Linux cpu plugin" do numa_node_cpus = { "0" => [0, 1, 2, 3] } it_behaves_like "Common cpu info", + "0", # default_cpu 4, # total_cpu 1, # real_cpu true, # ls_cpu @@ -615,6 +618,7 @@ describe Ohai::System, "General Linux cpu plugin" do numa_node_cpus = { "0" => [0, 1, 2, 3] } it_behaves_like "Common cpu info", + "0", # default_cpu 4, # total_cpu 4, # real_cpu true, # ls_cpu @@ -663,6 +667,7 @@ describe Ohai::System, "General Linux cpu plugin" do numa_node_cpus = { "0" => [0] } it_behaves_like "Common cpu info", + "0", # default_cpu 1, # total_cpu 1, # real_cpu true, # ls_cpu @@ -717,6 +722,7 @@ describe Ohai::System, "General Linux cpu plugin" do numa_node_cpus = { "0" => [0, 1, 2, 3] } it_behaves_like "Common cpu info", + "0", # default_cpu 4, # total_cpu 1, # real_cpu true, # ls_cpu @@ -750,6 +756,65 @@ describe Ohai::System, "General Linux cpu plugin" do "Xen" # hypervisor_vendor end + context "x86 guest on VT-x" do + let(:cpuinfo_contents) { File.read(File.join(SPEC_PLUGIN_PATH, "cpuinfo-x86-guest-vtx.output")) } + let(:lscpu) { File.read(File.join(SPEC_PLUGIN_PATH, "lscpu-x86-guest-vtx.output")) } + let(:lscpu_cores) { File.read(File.join(SPEC_PLUGIN_PATH, "lscpu-x86-guest-vtx-cores.output")) } + + before do + allow(File).to receive(:open).with("/proc/cpuinfo").and_return(cpuinfo_contents) + allow(plugin).to receive(:shell_out).with("lscpu").and_return(mock_shell_out(0, lscpu, "")) + allow(plugin).to receive(:shell_out).with("lscpu -p=CPU,CORE,SOCKET").and_return(mock_shell_out(0, lscpu_cores, "")) + end + + flags = %w{acpi aperfmperf apic arch_perfmon bts clflush cmov constant_tsc cpuid cx16 cx8 dca de ds_cpl dtes64 dtherm dts ept est flexpriority fpu fxsr ht ida lahf_lm lm mca mce mmx monitor msr mtrr nonstop_tsc nopl nx pae pat pbe pdcm pebs pge pni popcnt pse pse36 pti rdtscp rep_good sep sse sse2 sse4_1 sse4_2 ssse3 syscall tm tm2 tpr_shadow tsc vme vmx vnmi vpid xtopology xtpr} + numa_node_cpus = { + "0" => [0, 2, 4, 6, 8, 10, 12, 14], + "1" => [1, 3, 5, 7, 9, 11, 13, 15], + } + + it_behaves_like "Common cpu info", + "12", # default_cpu + 8, # total_cpu + 2, # real_cpu + true, # ls_cpu + "x86_64", # architecture + %w{32-bit 64-bit}, # cpu_opmodes + "little endian", # byte_order + 16, # cpus + 4, # cpus_online + 0, # threads_per_core + 4, # cores_per_socket + 2, # sockets + 2, # numa_nodes + "GenuineIntel", # vendor_id + "26", # model + "Intel(R) Xeon(R) CPU X5570 @ 2.93GHz", # model_name + "5852.18", # bogomips + "32K", # l1d_cache + "32K", # l1i_cache + "256K", # l2_cache + "8192K", # l3_cache + flags, # flags + numa_node_cpus # numa_node_cpus + + it "VT-x x86 processor info" do + plugin.run + + expect(plugin[:cpu]).to have_key("family") + expect(plugin[:cpu]["family"]).to eq("6") + + expect(plugin[:cpu]).to have_key("stepping") + expect(plugin[:cpu]["stepping"]).to eq("5") + + expect(plugin[:cpu]).to have_key("mhz") + expect(plugin[:cpu]["mhz"]).to eq("1596.085") + + expect(plugin[:cpu].keys).to include("12", "13") + expect(plugin[:cpu]["12"]).not_to have_key("cache_size") + expect(plugin[:cpu]["13"]).not_to have_key("cache_size") + end + end end describe Ohai::System, "S390 linux cpu plugin" do @@ -764,6 +829,7 @@ describe Ohai::System, "S390 linux cpu plugin" do end it_behaves_like "Common cpu info", + "0", # default_cpu 4, # total_cpu nil, # real_cpu false # ls_cpu @@ -809,6 +875,7 @@ describe Ohai::System, "S390 linux cpu plugin" do numa_node_cpus = { "0" => [0, 1, 2, 3] } it_behaves_like "Common cpu info", + "0", # default_cpu 4, # total_cpu 1, # real_cpu true, # ls_cpu @@ -940,7 +1007,7 @@ describe Ohai::System, "arm64 linux cpu plugin" do allow(plugin).to receive(:shell_out).with("lscpu -p=CPU,CORE,SOCKET").and_return(mock_shell_out(1, "", "")) end - it_behaves_like "Common cpu info", 2, nil, false + it_behaves_like "Common cpu info", "0", 2, nil, false it "has a cpu 1" do plugin.run @@ -971,6 +1038,7 @@ describe Ohai::System, "arm64 linux cpu plugin" do numa_node_cpus = { "0" => Range.new(0, 31).to_a } it_behaves_like "Common cpu info", + "0", # default_cpu 32, # total_cpu 1, # real_cpu true, # ls_cpu @@ -1035,6 +1103,7 @@ describe Ohai::System, "arm64 linux cpu plugin" do numa_node_cpus = { "0" => Range.new(0, 1).to_a } it_behaves_like "Common cpu info", + "0", # default_cpu 2, # total_cpu 1, # real_cpu true, # ls_cpu @@ -1078,6 +1147,7 @@ describe Ohai::System, "arm64 linux cpu plugin" do numa_node_cpus = { "0" => Range.new(0, 1).to_a } it_behaves_like "Common cpu info", + "0", # default_cpu 2, # total_cpu 1, # real_cpu true, # ls_cpu @@ -1125,6 +1195,7 @@ describe Ohai::System, "ppc64le linux cpu plugin (POWER9)" do end it_behaves_like "Common cpu info", + "0", # default_cpu 2, # total_cpu nil, # real_cpu false # ls_cpu @@ -1156,6 +1227,7 @@ describe Ohai::System, "ppc64le linux cpu plugin (POWER9)" do } it_behaves_like "Common cpu info", + "0", # default_cpu 32, # total_cpu 2, # real_cpu true, # ls_cpu @@ -1234,6 +1306,7 @@ describe Ohai::System, "ppc64le linux cpu plugin (POWER9)" do } it_behaves_like "Common cpu info", + "0", # default_cpu 20, # total_cpu 4, # real_cpu true, # ls_cpu @@ -1306,6 +1379,7 @@ describe Ohai::System, "ppc64le linux cpu plugin (POWER9)" do numa_node_cpus = { "0" => Range.new(0, 15).to_a } it_behaves_like "Common cpu info", + "0", # default_cpu 16, # total_cpu 16, # real_cpu true, # ls_cpu @@ -1364,6 +1438,7 @@ describe Ohai::System, "ppc64le linux cpu plugin (POWER9)" do numa_node_cpus = { "0" => Range.new(0, 15).to_a } it_behaves_like "Common cpu info", + "0", # default_cpu 16, # total_cpu 16, # real_cpu true, # ls_cpu -- cgit v1.2.1