aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/prometheus/procfs/sysfs/class_sas_phy.go
blob: 67bd7a68a31c7f26d959ae7b28a2e3e9a566178e (plain) (blame)
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]
}