1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
|
// Copyright 2024 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
// +build linux
package sysfs
import (
"fmt"
"os"
"path/filepath"
"strconv"
"github.com/prometheus/procfs/internal/util"
)
// Documentation of the sysfs path
// https://docs.nvidia.com/networking/display/mlnxofedv571020/explicit+congestion+notification+(ecn)
// https://enterprise-support.nvidia.com/s/article/dcqcn-parameters
// Ecn contains values from /sys/class/net/<iface>/ecn/roce_np/
// for single interface (iface).
type RoceNpEcn struct {
// A map from a priority to it's enabled status
Ecn map[uint8]bool
// Minimum time between sending CNPs from the port, in microseconds.
// Range: 0-4095, Default: 4
MinTimeBetweenCnps uint64
// The DSCP value for CNPs.
// Range: 0-63, Default: 48
CnpDscp uint64
// The PCP value for CNPs.
// Range: 0-7, Default: 6
Cnp802pPriority uint64
}
// Ecn contains values from /sys/class/net/<iface>/ecn/roce_rp/
// for single interface (iface).
type RoceRpEcn struct {
// A map from a priority to it's enabled status
Ecn map[uint8]bool
// Alpha Update
// Every Alpha Update Period alpha is updated.
// If the CNP is received during this period, alpha is incremented.
// Otherwise, it is decremented.
// Range: 0-1023, Default: 1019
DceTCPG uint64
// The Alpha Update Period used in the formula for DceTCPG. Unit is microseconds.
// Range: 1-131071, Default: 1
DceTCPRtt uint64
// This parameter sets the initial value of alpha that should be used when receiving
// the first CNP for a flow. Fixed point with 10 bits in the fraction part.
// Range: 1-1023, Default: 1023
InitialAlphaValue uint64
// Rate Decrease
// Rates (current, target) on first CNP (0 – 85% of line rate) in Mbps.
// Range: 0, 1-line rate, Default: 0
RateToSetOnFirstCnp uint64
// This parameter defines the maximal ratio of rate decrease in a single event.
// Range: 0-100, Default: 50
RpgMinDecFac uint64
// This parameter defines the minimal rate limit of the QP in Mbps.
// Range: 1-line rate, Default: 1
RpgMinRate uint64
// The coefficient between alpha and the rate reduction factor.
// Range: 10-11, Default: 11
RpgGd uint64
// The time period between rate reductions in microseconds.
// Range: 0-UINT32, Default: 4
RateReduceMonitorPeriod uint64
// Rate Increase
// If set, every rate decreases. The target rate is updated to the current rate.
// Otherwise, the target rate is updated to the current rate only on the first
// decrement after the increment event.
ClampTgtRate bool
// The time period between rate increase events in microseconds.
// Range: 1-131071, Default: 300
RpgTimeReset uint64
// The sent bytes counter between rate increase events.
// Range: 1-32767, Default: 32767
RpgByteReset uint64
// The threshold of rate increase events for moving to next rate increase phase.
// Range: 1-31, Default: 1
RpgThreshold uint64
// The rate increase value in the Additive Increase phase in Mbps.
// Range: 1-line rate, Default: 5
RpgAiRate uint64
// The rate increase value in the Hyper Increase phase in Mbps.
// Range: 1-line rate, Default: 1
RpgHaiRate uint64
}
// EcnIface contains Ecn info from files in /sys/class/net/<iface>/ecn/
// for single interface (iface).
type EcnIface struct {
Name string // Interface name
// protocols
RoceNpEcn RoceNpEcn // Notification point
RoceRpEcn RoceRpEcn // Reaction point
}
// AllEcnIface is collection of Ecn info for every interface (iface) in /sys/class/net.
// The map keys are interface (iface) names.
type AllEcnIface map[string]EcnIface
// EcnByIface returns info for a single net interfaces (iface).
func (fs FS) EcnByIface(devicePath string) (*EcnIface, error) {
_, err := fs.NetClassByIface(devicePath)
if err != nil {
return nil, err
}
path := fs.sys.Path(netclassPath)
ecnPath := filepath.Join(path, devicePath, "ecn")
validPath, err := PathExistsAndIsDir(ecnPath)
if err != nil {
return nil, err
}
if !validPath {
// this device doesn't have ECN values at this path
return nil, fmt.Errorf("does not have ECN values: %q", devicePath)
}
ecnIface, err := ParseEcnIfaceInfo(ecnPath)
if err != nil {
return nil, err
}
ecnIface.Name = devicePath
return ecnIface, nil
}
// EcnDevices returns EcnIface for all net interfaces (iface) read from /sys/class/net/<iface>/ecn.
func (fs FS) EcnDevices() (AllEcnIface, error) {
devices, err := fs.NetClassDevices()
if err != nil {
return nil, err
}
path := fs.sys.Path(netclassPath)
allEcnIface := AllEcnIface{}
for _, devicePath := range devices {
ecnPath := filepath.Join(path, devicePath, "ecn")
validPath, err := PathExistsAndIsDir(ecnPath)
if err != nil {
return nil, err
}
if !validPath {
// this device doesn't have ECN values at this path
continue
}
ecnIface, err := ParseEcnIfaceInfo(ecnPath)
if err != nil {
return nil, err
}
ecnIface.Name = devicePath
allEcnIface[devicePath] = *ecnIface
}
return allEcnIface, nil
}
// ParseEcnIfaceInfo scans predefined files in /sys/class/net/<iface>/ecn
// directory and gets their contents.
func ParseEcnIfaceInfo(ecnPath string) (*EcnIface, error) {
ecnIface := EcnIface{}
err := ParseRoceNpEcnInfo(filepath.Join(ecnPath, "roce_np"), &ecnIface.RoceNpEcn)
if err != nil {
return nil, err
}
err = ParseRoceRpEcnInfo(filepath.Join(ecnPath, "roce_rp"), &ecnIface.RoceRpEcn)
if err != nil {
return nil, err
}
return &ecnIface, nil
}
// ParseEcnIfaceInfo scans predefined files in /sys/class/net/<iface>/ecn/roce_np/
// directory and gets their contents.
func ParseRoceNpEcnInfo(ecnPath string, ecn *RoceNpEcn) error {
value, err := ParseEcnEnable(filepath.Join(ecnPath, "enable"))
if err != nil {
return err
}
ecn.Ecn = value
files, err := os.ReadDir(ecnPath)
if err != nil {
return err
}
for _, f := range files {
if !f.Type().IsRegular() {
continue
}
if err := ParseRoceNpEcnAttribute(ecnPath, f.Name(), ecn); err != nil {
return err
}
}
return nil
}
// Parses all of the attributes in for ROCE NP protocol.
func ParseRoceNpEcnAttribute(ecnPath string, attrName string, ecn *RoceNpEcn) error {
attrPath := filepath.Join(ecnPath, attrName)
value, err := util.SysReadFile(attrPath)
if err != nil {
if canIgnoreError(err) {
return nil
}
return fmt.Errorf("failed to read file %q: %w", attrPath, err)
}
vp := util.NewValueParser(value)
switch attrName {
case "min_time_between_cnps":
ecn.MinTimeBetweenCnps = *vp.PUInt64()
case "cnp_802p_prio":
ecn.Cnp802pPriority = *vp.PUInt64()
case "cnp_dscp":
ecn.CnpDscp = *vp.PUInt64()
default:
return nil
}
return nil
}
// ParseRoceRpEcnInfo scans predefined files in /sys/class/net/<iface>/ecn/roce_rp/
// directory and gets their contents.
func ParseRoceRpEcnInfo(ecnPath string, ecn *RoceRpEcn) error {
value, err := ParseEcnEnable(filepath.Join(ecnPath, "enable"))
if err != nil {
return err
}
ecn.Ecn = value
files, err := os.ReadDir(ecnPath)
if err != nil {
return err
}
for _, f := range files {
if !f.Type().IsRegular() {
continue
}
if err := ParseRoceRpEcnAttribute(ecnPath, f.Name(), ecn); err != nil {
return err
}
}
return nil
}
// Parses all of the attributes in for ROCE RP protocol.
func ParseRoceRpEcnAttribute(ecnPath string, attrName string, ecn *RoceRpEcn) error {
attrPath := filepath.Join(ecnPath, attrName)
value, err := util.SysReadFile(attrPath)
if err != nil {
if canIgnoreError(err) {
return nil
}
return fmt.Errorf("failed to read file %q: %w", attrPath, err)
}
vp := util.NewValueParser(value)
switch attrName {
case "clamp_tgt_rate":
if *vp.PUInt64() == 0 {
ecn.ClampTgtRate = false
} else if *vp.PUInt64() == 1 {
ecn.ClampTgtRate = true
} else {
return fmt.Errorf("failed to parse file %q: %w", attrPath, err)
}
case "dce_tcp_g":
ecn.DceTCPG = *vp.PUInt64()
case "dce_tcp_rtt":
ecn.DceTCPRtt = *vp.PUInt64()
case "initial_alpha_value":
ecn.InitialAlphaValue = *vp.PUInt64()
case "rate_reduce_monitor_period":
ecn.RateReduceMonitorPeriod = *vp.PUInt64()
case "rate_to_set_on_first_cnp":
ecn.RateToSetOnFirstCnp = *vp.PUInt64()
case "rpg_ai_rate":
ecn.RpgAiRate = *vp.PUInt64()
case "rpg_byte_reset":
ecn.RpgByteReset = *vp.PUInt64()
case "rpg_gd":
ecn.RpgGd = *vp.PUInt64()
case "rpg_hai_rate":
ecn.RpgHaiRate = *vp.PUInt64()
case "rpg_min_dec_fac":
ecn.RpgMinDecFac = *vp.PUInt64()
case "rpg_min_rate":
ecn.RpgMinRate = *vp.PUInt64()
case "rpg_threshold":
ecn.RpgThreshold = *vp.PUInt64()
case "rpg_time_reset":
ecn.RpgTimeReset = *vp.PUInt64()
default:
return nil
}
return nil
}
// parses the ECN enable directory. It takes a path which should be a directory.
// This directory should have filenames that are uint8 and the content of the file is
// either 0 or 1.
func ParseEcnEnable(path string) (map[uint8]bool, error) {
// Read the files in the directory
files, err := os.ReadDir(path)
if err != nil {
return nil, err
}
ecn := make(map[uint8]bool)
// Iterate through each file in the directory
for _, file := range files {
// Only process files (skip directories)
if file.IsDir() {
continue
}
// Extract the file name (which should be the integer key)
filename := file.Name()
// Attempt to convert the file name to an integer
filenameInt, err := strconv.ParseUint(filename, 10, 8)
if err != nil {
// Skip the file if the name cannot be converted to an integer
continue
}
value, err := util.SysReadFile(filepath.Join(path, filename))
if err != nil {
if canIgnoreError(err) {
return nil, err
}
return nil, fmt.Errorf("failed to read file %q: %w", filename, err)
}
vp := util.NewValueParser(value)
fileValue := *vp.PUInt64()
switch fileValue {
case 0:
ecn[uint8(filenameInt)] = false
case 1:
ecn[uint8(filenameInt)] = true
default:
return nil, fmt.Errorf("failed to parse file %q: %q", filename, value)
}
}
return ecn, nil
}
// Utility function that given a path will return if the path is a dir or not.
func PathExistsAndIsDir(path string) (bool, error) {
info, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false, nil // Path does not exist
}
return false, err // Some other error occurred
}
return info.IsDir(), nil // Check if the path is a directory
}
|