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
|
// 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/<Name>
SASAddress string // /sys/class/sas_phy/<Name>/sas_address
SASPort string // /sys/class/sas_phy/<Name>/device/ports
DeviceType string // /sys/class/sas_phy/<Name>/device_type
InitiatorPortProtocols []string // /sys/class/sas_phy/<Name>/initiator_port_protocols
InvalidDwordCount int // /sys/class/sas_phy/<Name>/invalid_dword_count
LossOfDwordSyncCount int // /sys/class/sas_phy/<Name>/loss_of_dword_sync_count
MaximumLinkrate float64 // /sys/class/sas_phy/<Name>/maximum_linkrate
MaximumLinkrateHW float64 // /sys/class/sas_phy/<Name>/maximum_linkrate_hw
MinimumLinkrate float64 // /sys/class/sas_phy/<Name>/minimum_linkrate
MinimumLinkrateHW float64 // /sys/class/sas_phy/<Name>/minimum_linkrate_hw
NegotiatedLinkrate float64 // /sys/class/sas_phy/<Name>/negotiated_linkrate
PhyIdentifier string // /sys/class/sas_phy/<Name>/phy_identifier
PhyResetProblemCount int // /sys/class/sas_phy/<Name>/phy_reset_problem_count
RunningDisparityErrorCount int // /sys/class/sas_phy/<Name>/running_disparity_error_count
TargetPortProtocols []string // /sys/class/sas_phy/<Name>/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]
}
|