netlink: read missing attributes from sysfs (#2669)

Read missing dev_id, name_assign_type, and addr_assign_type
from sysfs, since they only take a device-specific lock and
not the whole RTNL lock. This means reading them is much less
impactful on other system processes than many of the other
attributes in sysfs that do take the RTNL lock.

Signed-off-by: Dan Williams <dcbw@redhat.com>
This commit is contained in:
Dan Williams 2023-05-25 08:10:39 -05:00 committed by GitHub
parent eaacb2e3c7
commit 8c5847bd94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -20,12 +20,14 @@ import (
"errors"
"fmt"
"io/fs"
"path/filepath"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log/level"
"github.com/jsimonetti/rtnetlink"
"github.com/mdlayher/ethtool"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs/sysfs"
)
var (
@ -57,14 +59,27 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
}
}
// Get most attributes from Netlink
lMsgs, err := c.getNetClassInfoRTNL()
if err != nil {
return fmt.Errorf("could not get net class info: %w", err)
}
relevantLinks := make([]rtnetlink.LinkMessage, 0, len(lMsgs))
for _, msg := range lMsgs {
if c.ignoredDevicesPattern.MatchString(msg.Attributes.Name) {
continue
if !c.ignoredDevicesPattern.MatchString(msg.Attributes.Name) {
relevantLinks = append(relevantLinks, msg)
}
}
// Read sysfs for attributes that Netlink doesn't expose
sysfsAttrs, err := getSysfsAttributes(relevantLinks)
if err != nil {
return fmt.Errorf("could not get sysfs device info: %w", err)
}
// Parse all the info and update metrics
for _, msg := range relevantLinks {
upDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, c.subsystem, "up"),
"Value is 1 if operstate is 'up', 0 otherwise.",
@ -96,12 +111,16 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
duplex = lm.Duplex.String()
}
ifaceInfo := sysfsAttrs[msg.Attributes.Name]
ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, infoValue, msg.Attributes.Name, msg.Attributes.Address.String(), msg.Attributes.Broadcast.String(), duplex, operstateStr[int(msg.Attributes.OperationalState)], ifalias)
pushMetric(ch, c.getFieldDesc("address_assign_type"), "address_assign_type", ifaceInfo.AddrAssignType, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("carrier"), "carrier", msg.Attributes.Carrier, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("carrier_changes_total"), "carrier_changes_total", msg.Attributes.CarrierChanges, prometheus.CounterValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("carrier_up_changes_total"), "carrier_up_changes_total", msg.Attributes.CarrierUpCount, prometheus.CounterValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("carrier_down_changes_total"), "carrier_down_changes_total", msg.Attributes.CarrierDownCount, prometheus.CounterValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("device_id"), "device_id", ifaceInfo.DevID, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("flags"), "flags", msg.Flags, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("iface_id"), "iface_id", msg.Index, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("iface_link_mode"), "iface_link_mode", msg.Attributes.LinkMode, prometheus.GaugeValue, msg.Attributes.Name)
@ -117,6 +136,7 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
}
pushMetric(ch, c.getFieldDesc("mtu_bytes"), "mtu_bytes", msg.Attributes.MTU, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("name_assign_type"), "name_assign_type", ifaceInfo.NameAssignType, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("net_dev_group"), "net_dev_group", msg.Attributes.NetDevGroup, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("transmit_queue_length"), "transmit_queue_length", msg.Attributes.TxQueueLen, prometheus.GaugeValue, msg.Attributes.Name)
pushMetric(ch, c.getFieldDesc("protocol_type"), "protocol_type", msg.Type, prometheus.GaugeValue, msg.Attributes.Name)
@ -186,3 +206,25 @@ func (c *netClassCollector) getLinkModes() ([]*ethtool.LinkMode, error) {
return lms, err
}
// getSysfsAttributes reads attributes that are absent from netlink but provided
// by sysfs.
func getSysfsAttributes(links []rtnetlink.LinkMessage) (sysfs.NetClass, error) {
netClass := sysfs.NetClass{}
for _, msg := range links {
interfaceClass := sysfs.NetClassIface{}
ifName := msg.Attributes.Name
devPath := filepath.Join("/sys", "class", "net", ifName)
// These three attributes hold a device-specific lock when
// accessed, not the RTNL lock, so they are much less impactful
// than reading most of the other attributes from sysfs.
for _, attr := range []string{"addr_assign_type", "dev_id", "name_assign_type"} {
if err := sysfs.ParseNetClassAttribute(devPath, attr, &interfaceClass); err != nil {
return nil, err
}
}
netClass[ifName] = interfaceClass
}
return netClass, nil
}