Skip to content

Commit 8c5847b

Browse files
authored
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 <[email protected]>
1 parent eaacb2e commit 8c5847b

File tree

1 file changed

+44
-2
lines changed

1 file changed

+44
-2
lines changed

collector/netclass_rtnl_linux.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import (
2020
"errors"
2121
"fmt"
2222
"io/fs"
23+
"path/filepath"
2324

2425
"github.com/alecthomas/kingpin/v2"
2526
"github.com/go-kit/log/level"
2627
"github.com/jsimonetti/rtnetlink"
2728
"github.com/mdlayher/ethtool"
2829
"github.com/prometheus/client_golang/prometheus"
30+
"github.com/prometheus/procfs/sysfs"
2931
)
3032

3133
var (
@@ -57,14 +59,27 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
5759
}
5860
}
5961

62+
// Get most attributes from Netlink
6063
lMsgs, err := c.getNetClassInfoRTNL()
6164
if err != nil {
6265
return fmt.Errorf("could not get net class info: %w", err)
6366
}
67+
68+
relevantLinks := make([]rtnetlink.LinkMessage, 0, len(lMsgs))
6469
for _, msg := range lMsgs {
65-
if c.ignoredDevicesPattern.MatchString(msg.Attributes.Name) {
66-
continue
70+
if !c.ignoredDevicesPattern.MatchString(msg.Attributes.Name) {
71+
relevantLinks = append(relevantLinks, msg)
6772
}
73+
}
74+
75+
// Read sysfs for attributes that Netlink doesn't expose
76+
sysfsAttrs, err := getSysfsAttributes(relevantLinks)
77+
if err != nil {
78+
return fmt.Errorf("could not get sysfs device info: %w", err)
79+
}
80+
81+
// Parse all the info and update metrics
82+
for _, msg := range relevantLinks {
6883
upDesc := prometheus.NewDesc(
6984
prometheus.BuildFQName(namespace, c.subsystem, "up"),
7085
"Value is 1 if operstate is 'up', 0 otherwise.",
@@ -96,12 +111,16 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
96111
duplex = lm.Duplex.String()
97112
}
98113

114+
ifaceInfo := sysfsAttrs[msg.Attributes.Name]
115+
99116
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)
100117

118+
pushMetric(ch, c.getFieldDesc("address_assign_type"), "address_assign_type", ifaceInfo.AddrAssignType, prometheus.GaugeValue, msg.Attributes.Name)
101119
pushMetric(ch, c.getFieldDesc("carrier"), "carrier", msg.Attributes.Carrier, prometheus.GaugeValue, msg.Attributes.Name)
102120
pushMetric(ch, c.getFieldDesc("carrier_changes_total"), "carrier_changes_total", msg.Attributes.CarrierChanges, prometheus.CounterValue, msg.Attributes.Name)
103121
pushMetric(ch, c.getFieldDesc("carrier_up_changes_total"), "carrier_up_changes_total", msg.Attributes.CarrierUpCount, prometheus.CounterValue, msg.Attributes.Name)
104122
pushMetric(ch, c.getFieldDesc("carrier_down_changes_total"), "carrier_down_changes_total", msg.Attributes.CarrierDownCount, prometheus.CounterValue, msg.Attributes.Name)
123+
pushMetric(ch, c.getFieldDesc("device_id"), "device_id", ifaceInfo.DevID, prometheus.GaugeValue, msg.Attributes.Name)
105124
pushMetric(ch, c.getFieldDesc("flags"), "flags", msg.Flags, prometheus.GaugeValue, msg.Attributes.Name)
106125
pushMetric(ch, c.getFieldDesc("iface_id"), "iface_id", msg.Index, prometheus.GaugeValue, msg.Attributes.Name)
107126
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
117136
}
118137

119138
pushMetric(ch, c.getFieldDesc("mtu_bytes"), "mtu_bytes", msg.Attributes.MTU, prometheus.GaugeValue, msg.Attributes.Name)
139+
pushMetric(ch, c.getFieldDesc("name_assign_type"), "name_assign_type", ifaceInfo.NameAssignType, prometheus.GaugeValue, msg.Attributes.Name)
120140
pushMetric(ch, c.getFieldDesc("net_dev_group"), "net_dev_group", msg.Attributes.NetDevGroup, prometheus.GaugeValue, msg.Attributes.Name)
121141
pushMetric(ch, c.getFieldDesc("transmit_queue_length"), "transmit_queue_length", msg.Attributes.TxQueueLen, prometheus.GaugeValue, msg.Attributes.Name)
122142
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) {
186206

187207
return lms, err
188208
}
209+
210+
// getSysfsAttributes reads attributes that are absent from netlink but provided
211+
// by sysfs.
212+
func getSysfsAttributes(links []rtnetlink.LinkMessage) (sysfs.NetClass, error) {
213+
netClass := sysfs.NetClass{}
214+
for _, msg := range links {
215+
interfaceClass := sysfs.NetClassIface{}
216+
ifName := msg.Attributes.Name
217+
devPath := filepath.Join("/sys", "class", "net", ifName)
218+
219+
// These three attributes hold a device-specific lock when
220+
// accessed, not the RTNL lock, so they are much less impactful
221+
// than reading most of the other attributes from sysfs.
222+
for _, attr := range []string{"addr_assign_type", "dev_id", "name_assign_type"} {
223+
if err := sysfs.ParseNetClassAttribute(devPath, attr, &interfaceClass); err != nil {
224+
return nil, err
225+
}
226+
}
227+
netClass[ifName] = interfaceClass
228+
}
229+
return netClass, nil
230+
}

0 commit comments

Comments
 (0)