// Copyright 2021 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" "strings" "github.com/prometheus/procfs/internal/util" ) const sasPhyClassPath = "class/sas_phy" type SASPhy struct { Name string // /sys/class/sas_phy/ SASAddress string // /sys/class/sas_phy//sas_address SASPort string // /sys/class/sas_phy//device/ports DeviceType string // /sys/class/sas_phy//device_type InitiatorPortProtocols []string // /sys/class/sas_phy//initiator_port_protocols InvalidDwordCount int // /sys/class/sas_phy//invalid_dword_count LossOfDwordSyncCount int // /sys/class/sas_phy//loss_of_dword_sync_count MaximumLinkrate float64 // /sys/class/sas_phy//maximum_linkrate MaximumLinkrateHW float64 // /sys/class/sas_phy//maximum_linkrate_hw MinimumLinkrate float64 // /sys/class/sas_phy//minimum_linkrate MinimumLinkrateHW float64 // /sys/class/sas_phy//minimum_linkrate_hw NegotiatedLinkrate float64 // /sys/class/sas_phy//negotiated_linkrate PhyIdentifier string // /sys/class/sas_phy//phy_identifier PhyResetProblemCount int // /sys/class/sas_phy//phy_reset_problem_count RunningDisparityErrorCount int // /sys/class/sas_phy//running_disparity_error_count TargetPortProtocols []string // /sys/class/sas_phy//target_port_protocols } type SASPhyClass map[string]*SASPhy // SASPhyClass parses entries in /sys/class/sas_phy. func (fs FS) SASPhyClass() (SASPhyClass, error) { path := fs.sys.Path(sasPhyClassPath) dirs, err := os.ReadDir(path) if err != nil { return nil, err } spc := make(SASPhyClass, len(dirs)) for _, d := range dirs { phy, err := fs.parseSASPhy(d.Name()) if err != nil { return nil, err } spc[phy.Name] = phy } return spc, nil } // Parse a single sas_phy. func (fs FS) parseSASPhy(name string) (*SASPhy, error) { phy := SASPhy{Name: name} phypath := fs.sys.Path(filepath.Join(sasPhyClassPath, name)) phydevicepath := filepath.Join(phypath, "device") link, err := os.Readlink(filepath.Join(phydevicepath, "port")) if err == nil { if sasPortDeviceRegexp.MatchString(filepath.Base(link)) { phy.SASPort = filepath.Base(link) } } files, err := os.ReadDir(phypath) if err != nil { return nil, err } for _, f := range files { name := filepath.Join(phypath, f.Name()) fileinfo, _ := os.Stat(name) if fileinfo.Mode().IsRegular() { value, err := util.SysReadFile(name) if err != nil { if os.IsPermission(err) { continue } return nil, fmt.Errorf("failed to read file %q: %w", name, err) } vp := util.NewValueParser(value) switch f.Name() { case "sas_address": phy.SASAddress = value case "device_type": phy.DeviceType = value case "initiator_port_protocols": phy.InitiatorPortProtocols = strings.Split(value, ", ") case "invalid_dword_count": phy.InvalidDwordCount = vp.Int() case "loss_of_dword_sync_count": phy.LossOfDwordSyncCount = vp.Int() case "maximum_linkrate": phy.MaximumLinkrate = parseLinkrate(value) case "maximum_linkrate_hw": phy.MaximumLinkrateHW = parseLinkrate(value) case "minimum_linkrate": phy.MinimumLinkrate = parseLinkrate(value) case "minimum_linkrate_hw": phy.MinimumLinkrateHW = parseLinkrate(value) case "negotiated_linkrate": phy.NegotiatedLinkrate = parseLinkrate(value) case "phy_identifier": phy.PhyIdentifier = value case "phy_reset_problem_count": phy.PhyResetProblemCount = vp.Int() case "running_disparity_error_count": phy.RunningDisparityErrorCount = vp.Int() case "target_port_protocols": phy.TargetPortProtocols = strings.Split(value, ", ") } if err := vp.Err(); err != nil { return nil, err } } } return &phy, nil } // parseLinkRate turns the kernel's SAS linkrate values into floats. // The kernel returns values like "12.0 Gbit". Valid speeds are // currently 1.5, 3.0, 6.0, 12.0, and up. This is a float to cover // the 1.5 Gbps case. A value of 0 is returned if the speed can't be // parsed. func parseLinkrate(value string) float64 { f := strings.Split(value, " ")[0] gb, err := strconv.ParseFloat(f, 64) if err != nil { return 0 } return gb } // GetByName returns the SASPhy with the provided name. func (spc *SASPhyClass) GetByName(name string) *SASPhy { return (*spc)[name] }