Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 85 additions & 5 deletions sysfs/class_nvme.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,35 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"

"github.com/prometheus/procfs/internal/util"
)

const nvmeClassPath = "class/nvme"

// NVMeNamespace contains info from files in /sys/class/nvme/<device>/<namespace>.
type NVMeNamespace struct {
ID string // namespace ID extracted from directory name
UsedBlocks uint64 // from nuse file (blocks used)
SizeBlocks uint64 // from size file (total blocks)
LogicalBlockSize uint64 // from queue/logical_block_size file
ANAState string // from ana_state file
UsedBytes uint64 // calculated: UsedBlocks * LogicalBlockSize
SizeBytes uint64 // calculated: SizeBlocks * LogicalBlockSize
CapacityBytes uint64 // calculated: SizeBlocks * LogicalBlockSize
}

// NVMeDevice contains info from files in /sys/class/nvme for a single NVMe device.
type NVMeDevice struct {
Name string
Serial string // /sys/class/nvme/<Name>/serial
Model string // /sys/class/nvme/<Name>/model
State string // /sys/class/nvme/<Name>/state
FirmwareRevision string // /sys/class/nvme/<Name>/firmware_rev
ControllerID string // /sys/class/nvme/<Name>/cntlid
Serial string // /sys/class/nvme/<Name>/serial
Model string // /sys/class/nvme/<Name>/model
State string // /sys/class/nvme/<Name>/state
FirmwareRevision string // /sys/class/nvme/<Name>/firmware_rev
ControllerID string // /sys/class/nvme/<Name>/cntlid
Namespaces []NVMeNamespace // NVMe namespaces for this device
}

// NVMeClass is a collection of every NVMe device in /sys/class/nvme.
Expand Down Expand Up @@ -67,6 +82,7 @@ func (fs FS) parseNVMeDevice(name string) (*NVMeDevice, error) {
path := fs.sys.Path(nvmeClassPath, name)
device := NVMeDevice{Name: name}

// Parse device-level attributes
for _, f := range [...]string{"firmware_rev", "model", "serial", "state", "cntlid"} {
name := filepath.Join(path, f)
value, err := util.SysReadFile(name)
Expand All @@ -88,5 +104,69 @@ func (fs FS) parseNVMeDevice(name string) (*NVMeDevice, error) {
}
}

// Parse namespaces - read directory and filter using regex
dirs, err := os.ReadDir(path)
if err != nil {
return nil, fmt.Errorf("failed to list NVMe namespaces at %q: %w", path, err)
}

var namespaces []NVMeNamespace
var re = regexp.MustCompile(`nvme\d+c\d+n(\d+)`)

for _, d := range dirs {
// Use regex to identify namespace directories and extract namespace ID
match := re.FindStringSubmatch(d.Name())
if len(match) < 2 {
// Skip if not a namespace directory
continue
}
nsid := match[1]
namespacePath := filepath.Join(path, d.Name())

namespace := NVMeNamespace{
ID: nsid,
ANAState: "unknown", // Default value
}

// Parse namespace attributes using the same approach as device attributes
for _, f := range [...]string{"nuse", "size", "queue/logical_block_size", "ana_state"} {
filePath := filepath.Join(namespacePath, f)
value, err := util.SysReadFile(filePath)
if err != nil {
if f == "ana_state" {
// ana_state may not exist, skip silently
continue
}
return nil, fmt.Errorf("failed to read file %q: %w", filePath, err)
}

switch f {
case "nuse":
if val, parseErr := strconv.ParseUint(value, 10, 64); parseErr == nil {
namespace.UsedBlocks = val
}
case "size":
if val, parseErr := strconv.ParseUint(value, 10, 64); parseErr == nil {
namespace.SizeBlocks = val
}
case "queue/logical_block_size":
if val, parseErr := strconv.ParseUint(value, 10, 64); parseErr == nil {
namespace.LogicalBlockSize = val
}
case "ana_state":
namespace.ANAState = value
}
}

// Calculate derived values
namespace.UsedBytes = namespace.UsedBlocks * namespace.LogicalBlockSize
namespace.SizeBytes = namespace.SizeBlocks * namespace.LogicalBlockSize
namespace.CapacityBytes = namespace.SizeBlocks * namespace.LogicalBlockSize

namespaces = append(namespaces, namespace)
}

device.Namespaces = namespaces

return &device, nil
}
Loading