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
|
// 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 (
"os"
"path/filepath"
"regexp"
)
const sasPortClassPath = "class/sas_port"
type SASPort struct {
Name string // /sys/class/sas_device/<Name>
SASPhys []string // /sys/class/sas_device/<Name>/device/phy-*
Expanders []string // /sys/class/sas_port/<Name>/device/expander-*
EndDevices []string // /sys/class/sas_port/<Name>/device/end_device-*
}
type SASPortClass map[string]*SASPort
var (
sasExpanderDeviceRegexp = regexp.MustCompile(`^expander-[0-9:]+$`)
sasEndDeviceRegexp = regexp.MustCompile(`^end_device-[0-9:]+$`)
)
// SASPortClass parses ports in /sys/class/sas_port.
//
// A SAS port in this context is a collection of SAS PHYs operating
// together. For example, it's common to have 8-lane SAS cards that
// have 2 external connectors, each of which carries 4 SAS lanes over
// a SFF-8088 or SFF-8644 connector. While it's possible to split
// those 4 lanes into 4 different cables wired directly into
// individual drives, it's more common to connect them all to a SAS
// expander. This gives you 4x the bandwidth between the expander and
// the SAS host, and is represented by a sas-port object which
// contains 4 sas-phy objects.
func (fs FS) SASPortClass() (SASPortClass, error) {
path := fs.sys.Path(sasPortClassPath)
dirs, err := os.ReadDir(path)
if err != nil {
return nil, err
}
spc := make(SASPortClass, len(dirs))
for _, d := range dirs {
port, err := fs.parseSASPort(d.Name())
if err != nil {
return nil, err
}
spc[port.Name] = port
}
return spc, nil
}
// Parse a single sas_port.
func (fs FS) parseSASPort(name string) (*SASPort, error) {
port := SASPort{Name: name}
portpath := fs.sys.Path(filepath.Join(sasPortClassPath, name, "device"))
dirs, err := os.ReadDir(portpath)
if err != nil {
return nil, err
}
for _, d := range dirs {
if sasPhyDeviceRegexp.MatchString(d.Name()) {
port.SASPhys = append(port.SASPhys, d.Name())
}
if sasExpanderDeviceRegexp.MatchString(d.Name()) {
port.Expanders = append(port.Expanders, d.Name())
}
if sasEndDeviceRegexp.MatchString(d.Name()) {
port.EndDevices = append(port.EndDevices, d.Name())
}
}
return &port, nil
}
// GetByName returns the SASPort with the provided name.
func (spc *SASPortClass) GetByName(name string) *SASPort {
return (*spc)[name]
}
// GetByPhy finds the SASPort that contains the provided PHY name.
func (spc *SASPortClass) GetByPhy(name string) *SASPort {
for _, d := range *spc {
for _, p := range d.SASPhys {
if p == name {
return d
}
}
}
return nil
}
// GetByExpander finds the SASPort that contains the provided SAS expander name.
func (spc *SASPortClass) GetByExpander(name string) *SASPort {
for _, d := range *spc {
for _, e := range d.Expanders {
if e == name {
return d
}
}
}
return nil
}
// GetByEndDevice finds the SASPort that contains the provided SAS end device name.
func (spc *SASPortClass) GetByEndDevice(name string) *SASPort {
for _, d := range *spc {
for _, e := range d.EndDevices {
if e == name {
return d
}
}
}
return nil
}
|