gpu: collect reset-required/row-remap status and classify Xid codes by severity
nvidia-smi exposes reset_status.reset_required and remapped_rows.* only on newer drivers for Ampere+ GPUs; queried via a separate exec call since an unrecognized field name fails the whole --query-gpu command and would have wiped out unrelated telemetry (temp/ECC/power) on older drivers otherwise. Also refines Xid severity in both the ingest dmesg collector and the always-on kmsg watcher: Xid 64 (row-remap InfoROM write failure) now escalates to Critical instead of the generic warning every other Xid got, and fixes the SAT-window flush path which previously hardcoded "Warning" and ignored pattern severity entirely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d850d4fc3a
commit
1175e6ccd8
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"bee/audit/internal/app"
|
||||
"bee/audit/internal/collector"
|
||||
"bee/audit/internal/platform"
|
||||
)
|
||||
|
||||
@@ -40,6 +41,7 @@ type kmsgEvent struct {
|
||||
raw string
|
||||
ids []string // BDF addresses or device names extracted
|
||||
category string
|
||||
severity string // "warning" or "critical", resolved once at parse time
|
||||
}
|
||||
|
||||
func newKmsgWatcher(statusDB *app.ComponentStatusDB) *kmsgWatcher {
|
||||
@@ -148,8 +150,18 @@ func (w *kmsgWatcher) flushWindow(window *kmsgWindow) {
|
||||
return
|
||||
}
|
||||
source := "watchdog:kmsg"
|
||||
// Collect unique component keys from events.
|
||||
seen := map[string]string{} // componentKey → first raw line
|
||||
// Collect unique component keys from events, escalating to Critical if any
|
||||
// matching event for that component was critical (e.g. Xid 64 remap failure).
|
||||
seen := map[string]string{} // componentKey → first raw line
|
||||
severities := map[string]string{} // componentKey → worst severity seen
|
||||
record := func(key string, evt kmsgEvent) {
|
||||
if _, exists := seen[key]; !exists {
|
||||
seen[key] = evt.raw
|
||||
severities[key] = capitalizeSeverity(evt.severity)
|
||||
} else if evt.severity == "critical" {
|
||||
severities[key] = "Critical"
|
||||
}
|
||||
}
|
||||
for _, evt := range window.events {
|
||||
if len(evt.ids) == 0 {
|
||||
// MCE or un-identified error.
|
||||
@@ -157,9 +169,7 @@ func (w *kmsgWatcher) flushWindow(window *kmsgWindow) {
|
||||
if evt.category == "memory" {
|
||||
key = "memory:all"
|
||||
}
|
||||
if _, exists := seen[key]; !exists {
|
||||
seen[key] = evt.raw
|
||||
}
|
||||
record(key, evt)
|
||||
continue
|
||||
}
|
||||
for _, id := range evt.ids {
|
||||
@@ -174,14 +184,12 @@ func (w *kmsgWatcher) flushWindow(window *kmsgWindow) {
|
||||
default:
|
||||
key = "pcie:" + normalizeBDF(id)
|
||||
}
|
||||
if _, exists := seen[key]; !exists {
|
||||
seen[key] = evt.raw
|
||||
}
|
||||
record(key, evt)
|
||||
}
|
||||
}
|
||||
for key, detail := range seen {
|
||||
detail = "kernel error during SAT (" + strings.Join(window.targets, ",") + "): " + truncate(detail, 120)
|
||||
w.statusDB.Record(key, source, "Warning", detail)
|
||||
w.statusDB.Record(key, source, severities[key], detail)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,21 +201,7 @@ func (w *kmsgWatcher) flushImmediate(evt kmsgEvent) {
|
||||
}
|
||||
const source = "watchdog:kmsg"
|
||||
detail := "kernel: " + truncate(evt.raw, 120)
|
||||
|
||||
var severity string
|
||||
for _, p := range platform.HardwareErrorPatterns {
|
||||
if p.Re.MatchString(evt.raw) {
|
||||
if p.Severity == "critical" {
|
||||
severity = "Critical"
|
||||
} else {
|
||||
severity = "Warning"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if severity == "" {
|
||||
severity = "Warning"
|
||||
}
|
||||
severity := capitalizeSeverity(evt.severity)
|
||||
|
||||
if len(evt.ids) == 0 {
|
||||
key := "cpu:all"
|
||||
@@ -250,10 +244,15 @@ func parseKmsgLine(raw string) (kmsgEvent, bool) {
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
severity := p.Severity
|
||||
if sev, ok := collector.XidSeverity(msg); ok {
|
||||
severity = sev
|
||||
}
|
||||
evt := kmsgEvent{
|
||||
timestamp: time.Now(),
|
||||
raw: msg,
|
||||
category: p.Category,
|
||||
severity: severity,
|
||||
}
|
||||
if p.BDFGroup > 0 && p.BDFGroup < len(m) {
|
||||
evt.ids = append(evt.ids, normalizeBDF(m[p.BDFGroup]))
|
||||
@@ -266,6 +265,15 @@ func parseKmsgLine(raw string) (kmsgEvent, bool) {
|
||||
return kmsgEvent{}, false
|
||||
}
|
||||
|
||||
// capitalizeSeverity converts an ErrorPattern severity ("warning"/"critical")
|
||||
// to the Title-case convention ComponentStatusDB.Record expects.
|
||||
func capitalizeSeverity(s string) string {
|
||||
if s == "critical" {
|
||||
return "Critical"
|
||||
}
|
||||
return "Warning"
|
||||
}
|
||||
|
||||
// normalizeBDF normalizes a PCIe BDF to the 4-part form "0000:c8:00.0".
|
||||
func normalizeBDF(bdf string) string {
|
||||
bdf = strings.ToLower(strings.TrimSpace(bdf))
|
||||
|
||||
Reference in New Issue
Block a user