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]
}
|