Skip to content

Commit 4172095

Browse files
committed
fix: fallback to /proc/meminfo for memory modules
If SMBIOS does not report memory information, fall back to /proc/meminfo and expose a dummy memory module as a best-effort approximation. Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
1 parent 7f1147b commit 4172095

File tree

2 files changed

+135
-26
lines changed

2 files changed

+135
-26
lines changed

internal/app/machined/pkg/controllers/hardware/system.go

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import (
1010
"strings"
1111

1212
"github.com/cosi-project/runtime/pkg/controller"
13+
"github.com/cosi-project/runtime/pkg/resource"
1314
"github.com/cosi-project/runtime/pkg/safe"
1415
"github.com/cosi-project/runtime/pkg/state"
16+
"github.com/prometheus/procfs"
1517
"github.com/siderolabs/gen/optional"
1618
"github.com/siderolabs/go-smbios/smbios"
1719
"go.uber.org/zap"
@@ -70,6 +72,8 @@ func (ctrl *SystemInfoController) Outputs() []controller.Output {
7072
}
7173
}
7274

75+
const memoryModuleUnknown = "UNKNOWN"
76+
7377
// Run implements controller.Controller interface.
7478
//
7579
//nolint:gocyclo
@@ -86,6 +90,8 @@ func (ctrl *SystemInfoController) Run(ctx context.Context, r controller.Runtime,
8690
case <-r.EventCh():
8791
}
8892

93+
r.StartTrackingOutputs()
94+
8995
_, err := safe.ReaderGetByID[*runtime.MetaLoaded](ctx, r, runtime.MetaLoadedID)
9096
if err != nil {
9197
if state.IsNotFoundError(err) {
@@ -106,51 +112,108 @@ func (ctrl *SystemInfoController) Run(ctx context.Context, r controller.Runtime,
106112
ctrl.SMBIOS = s
107113
}
108114

109-
uuidRewriteRes, err := safe.ReaderGetByID[*runtime.MetaKey](ctx, r, runtime.MetaKeyTagToID(meta.UUIDOverride))
110-
if err != nil && !state.IsNotFoundError(err) {
111-
return fmt.Errorf("error getting meta key resource: %w", err)
115+
if err := ctrl.reconcileSystemInformation(ctx, r, logger); err != nil {
116+
return err
112117
}
113118

114-
var uuidRewrite string
119+
if err := ctrl.reconcileProcessors(ctx, r); err != nil {
120+
return err
121+
}
115122

116-
if uuidRewriteRes != nil && uuidRewriteRes.TypedSpec().Value != "" {
117-
uuidRewrite = uuidRewriteRes.TypedSpec().Value
123+
if err := ctrl.reconcileMemoryModules(ctx, r, logger); err != nil {
124+
return err
125+
}
118126

119-
logger.Info("using UUID rewrite", zap.String("uuid", uuidRewrite))
127+
if err := r.CleanupOutputs(ctx,
128+
resource.NewMetadata(hardware.NamespaceName, hardware.SystemInformationType, hardware.SystemInformationID, resource.VersionUndefined),
129+
resource.NewMetadata(hardware.NamespaceName, hardware.ProcessorType, "", resource.VersionUndefined),
130+
resource.NewMetadata(hardware.NamespaceName, hardware.MemoryModuleType, "", resource.VersionUndefined),
131+
); err != nil {
132+
return fmt.Errorf("failed to cleanup outputs: %w", err)
120133
}
134+
}
135+
}
136+
137+
func (ctrl *SystemInfoController) reconcileSystemInformation(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
138+
uuidRewriteRes, err := safe.ReaderGetByID[*runtime.MetaKey](ctx, r, runtime.MetaKeyTagToID(meta.UUIDOverride))
139+
if err != nil && !state.IsNotFoundError(err) {
140+
return fmt.Errorf("error getting meta key resource: %w", err)
141+
}
142+
143+
var uuidRewrite string
144+
145+
if uuidRewriteRes != nil && uuidRewriteRes.TypedSpec().Value != "" {
146+
uuidRewrite = uuidRewriteRes.TypedSpec().Value
147+
148+
logger.Info("using UUID rewrite", zap.String("uuid", uuidRewrite))
149+
}
150+
151+
if err := safe.WriterModify(ctx, r, hardware.NewSystemInformation(hardware.SystemInformationID), func(res *hardware.SystemInformation) error {
152+
hwadapter.SystemInformation(res).Update(&ctrl.SMBIOS.SystemInformation, uuidRewrite)
153+
154+
return nil
155+
}); err != nil {
156+
return fmt.Errorf("error updating objects: %w", err)
157+
}
158+
159+
return nil
160+
}
161+
162+
func (ctrl *SystemInfoController) reconcileProcessors(ctx context.Context, r controller.Runtime) error {
163+
for _, p := range ctrl.SMBIOS.ProcessorInformation {
164+
// replaces `CPU 0` with `CPU-0`
165+
id := strings.ReplaceAll(p.SocketDesignation, " ", "-")
121166

122-
if err := safe.WriterModify(ctx, r, hardware.NewSystemInformation(hardware.SystemInformationID), func(res *hardware.SystemInformation) error {
123-
hwadapter.SystemInformation(res).Update(&ctrl.SMBIOS.SystemInformation, uuidRewrite)
167+
if err := safe.WriterModify(ctx, r, hardware.NewProcessorInfo(id), func(res *hardware.Processor) error {
168+
hwadapter.Processor(res).Update(&p)
124169

125170
return nil
126171
}); err != nil {
127172
return fmt.Errorf("error updating objects: %w", err)
128173
}
174+
}
129175

130-
for _, p := range ctrl.SMBIOS.ProcessorInformation {
131-
// replaces `CPU 0` with `CPU-0`
132-
id := strings.ReplaceAll(p.SocketDesignation, " ", "-")
176+
return nil
177+
}
133178

134-
if err := safe.WriterModify(ctx, r, hardware.NewProcessorInfo(id), func(res *hardware.Processor) error {
135-
hwadapter.Processor(res).Update(&p)
179+
func (ctrl *SystemInfoController) reconcileMemoryModules(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
180+
for _, m := range ctrl.SMBIOS.MemoryDevices {
181+
// replaces `SIMM 0` with `SIMM-0`
182+
id := strings.ReplaceAll(m.DeviceLocator, " ", "-")
136183

137-
return nil
138-
}); err != nil {
139-
return fmt.Errorf("error updating objects: %w", err)
140-
}
184+
if err := safe.WriterModify(ctx, r, hardware.NewMemoryModuleInfo(id), func(res *hardware.MemoryModule) error {
185+
hwadapter.MemoryModule(res).Update(&m)
186+
187+
return nil
188+
}); err != nil {
189+
return fmt.Errorf("error updating objects: %w", err)
141190
}
191+
}
192+
193+
if len(ctrl.SMBIOS.MemoryDevices) == 0 {
194+
logger.Debug("no memory devices found, attempting to retrieve memory information from procfs")
142195

143-
for _, m := range ctrl.SMBIOS.MemoryDevices {
144-
// replaces `SIMM 0` with `SIMM-0`
145-
id := strings.ReplaceAll(m.DeviceLocator, " ", "-")
196+
proc, err := procfs.NewDefaultFS()
197+
if err != nil {
198+
return err
199+
}
146200

147-
if err := safe.WriterModify(ctx, r, hardware.NewMemoryModuleInfo(id), func(res *hardware.MemoryModule) error {
148-
hwadapter.MemoryModule(res).Update(&m)
201+
info, err := proc.Meminfo()
202+
if err != nil {
203+
return err
204+
}
149205

150-
return nil
151-
}); err != nil {
152-
return fmt.Errorf("error updating objects: %w", err)
206+
if err := safe.WriterModify(ctx, r, hardware.NewMemoryModuleInfo(memoryModuleUnknown), func(res *hardware.MemoryModule) error {
207+
if info.MemTotalBytes != nil {
208+
hwadapter.MemoryModule(res).TypedSpec().Size = uint32(*info.MemTotal / 1024)
153209
}
210+
hwadapter.MemoryModule(res).TypedSpec().Manufacturer = memoryModuleUnknown
211+
212+
return nil
213+
}); err != nil {
214+
return fmt.Errorf("error updating objects: %w", err)
154215
}
155216
}
217+
218+
return nil
156219
}

internal/app/machined/pkg/controllers/hardware/system_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package hardware_test
66

77
import (
8+
"io"
89
"os"
910
"testing"
1011
"time"
@@ -48,6 +49,16 @@ func (suite *SystemInfoSuite) TestPopulateSystemInformation() {
4849

4950
suite.Require().NoError(suite.state.Create(suite.ctx, runtime.NewMetaLoaded()))
5051

52+
systemInformation := hardware.SystemInformationSpec{
53+
Manufacturer: "Supermicro",
54+
ProductName: "SYS-1027R-WRF",
55+
Version: "0123456789",
56+
SerialNumber: "E09626824801435",
57+
UUID: "00000000-0000-0000-0000-002590eb9628",
58+
WakeUpType: "Power Switch",
59+
SKUNumber: "",
60+
}
61+
5162
cpuSpecs := map[string]hardware.ProcessorSpec{
5263
"CPU-1": {
5364
Socket: "CPU 1",
@@ -97,6 +108,10 @@ func (suite *SystemInfoSuite) TestPopulateSystemInformation() {
97108
},
98109
}
99110

111+
ctest.AssertResource(suite, hardware.SystemInformationID, func(r *hardware.SystemInformation, assertions *assert.Assertions) {
112+
assertions.Equal(systemInformation, *r.TypedSpec())
113+
})
114+
100115
for k, v := range cpuSpecs {
101116
ctest.AssertResource(suite, k, func(r *hardware.Processor, assertions *assert.Assertions) {
102117
assertions.Equal(v, *r.TypedSpec())
@@ -110,6 +125,37 @@ func (suite *SystemInfoSuite) TestPopulateSystemInformation() {
110125
}
111126
}
112127

128+
func (suite *SystemInfoSuite) TestPopulateSystemInformationEmpty() {
129+
version := smbios.Version{Major: 3, Minor: 3, Revision: 0} // dummy version
130+
s, err := smbios.Decode(io.NewSectionReader(nil, 0, 0), version)
131+
suite.Require().NoError(err)
132+
133+
suite.Require().NoError(
134+
suite.runtime.RegisterController(
135+
&hardwarectrl.SystemInfoController{
136+
SMBIOS: s,
137+
},
138+
),
139+
)
140+
141+
suite.startRuntime()
142+
143+
suite.Require().NoError(suite.state.Create(suite.ctx, runtime.NewMetaLoaded()))
144+
145+
memorySpecs := map[string]hardware.MemoryModuleSpec{
146+
"UNKNOWN": {
147+
Manufacturer: "UNKNOWN",
148+
},
149+
}
150+
151+
for k, v := range memorySpecs {
152+
ctest.AssertResource(suite, k, func(r *hardware.MemoryModule, assertions *assert.Assertions) {
153+
assertions.Equal(v.DeviceLocator, r.TypedSpec().DeviceLocator)
154+
assertions.NotZero(r.TypedSpec().Size)
155+
})
156+
}
157+
}
158+
113159
func (suite *SystemInfoSuite) TestUUIDOverwrite() {
114160
stream, err := os.Open("testdata/SuperMicro-Dual-Xeon.dmi")
115161
suite.Require().NoError(err)

0 commit comments

Comments
 (0)