Skip to content

Commit 56add94

Browse files
nvme: Parse NVMe namespace details (#765)
* add nvme namespace details to NVMe devices. --------- Signed-off-by: Shashwat Hiregoudar <shashwathiregoudar@gmail.com>
1 parent 8fce2bd commit 56add94

File tree

3 files changed

+477
-5
lines changed

3 files changed

+477
-5
lines changed

sysfs/class_nvme.go

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,37 @@ import (
1919
"fmt"
2020
"os"
2121
"path/filepath"
22+
"regexp"
23+
"strconv"
2224

2325
"github.com/prometheus/procfs/internal/util"
2426
)
2527

2628
const nvmeClassPath = "class/nvme"
2729

30+
var nvmeNamespacePattern = regexp.MustCompile(`nvme\d+c\d+n(\d+)`)
31+
32+
// NVMeNamespace contains info from files in /sys/class/nvme/<device>/<namespace>.
33+
type NVMeNamespace struct {
34+
ID string // namespace ID extracted from directory name
35+
UsedBlocks uint64 // from nuse file (blocks used)
36+
SizeBlocks uint64 // from size file (total blocks)
37+
LogicalBlockSize uint64 // from queue/logical_block_size file
38+
ANAState string // from ana_state file
39+
UsedBytes uint64 // calculated: UsedBlocks * LogicalBlockSize
40+
SizeBytes uint64 // calculated: SizeBlocks * LogicalBlockSize
41+
CapacityBytes uint64 // calculated: SizeBlocks * LogicalBlockSize
42+
}
43+
2844
// NVMeDevice contains info from files in /sys/class/nvme for a single NVMe device.
2945
type NVMeDevice struct {
3046
Name string
31-
Serial string // /sys/class/nvme/<Name>/serial
32-
Model string // /sys/class/nvme/<Name>/model
33-
State string // /sys/class/nvme/<Name>/state
34-
FirmwareRevision string // /sys/class/nvme/<Name>/firmware_rev
35-
ControllerID string // /sys/class/nvme/<Name>/cntlid
47+
Serial string // /sys/class/nvme/<Name>/serial
48+
Model string // /sys/class/nvme/<Name>/model
49+
State string // /sys/class/nvme/<Name>/state
50+
FirmwareRevision string // /sys/class/nvme/<Name>/firmware_rev
51+
ControllerID string // /sys/class/nvme/<Name>/cntlid
52+
Namespaces []NVMeNamespace // NVMe namespaces for this device
3653
}
3754

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

87+
// Parse device-level attributes
7088
for _, f := range [...]string{"firmware_rev", "model", "serial", "state", "cntlid"} {
7189
name := filepath.Join(path, f)
7290
value, err := util.SysReadFile(name)
@@ -88,5 +106,68 @@ func (fs FS) parseNVMeDevice(name string) (*NVMeDevice, error) {
88106
}
89107
}
90108

109+
// Parse namespaces - read directory and filter using regex
110+
dirs, err := os.ReadDir(path)
111+
if err != nil {
112+
return nil, fmt.Errorf("failed to list NVMe namespaces at %q: %w", path, err)
113+
}
114+
115+
var namespaces []NVMeNamespace
116+
117+
for _, d := range dirs {
118+
// Use regex to identify namespace directories and extract namespace ID
119+
match := nvmeNamespacePattern.FindStringSubmatch(d.Name())
120+
if len(match) < 2 {
121+
// Skip if not a namespace directory
122+
continue
123+
}
124+
nsid := match[1]
125+
namespacePath := filepath.Join(path, d.Name())
126+
127+
namespace := NVMeNamespace{
128+
ID: nsid,
129+
ANAState: "unknown", // Default value
130+
}
131+
132+
// Parse namespace attributes using the same approach as device attributes
133+
for _, f := range [...]string{"nuse", "size", "queue/logical_block_size", "ana_state"} {
134+
filePath := filepath.Join(namespacePath, f)
135+
value, err := util.SysReadFile(filePath)
136+
if err != nil {
137+
if f == "ana_state" {
138+
// ana_state may not exist, skip silently
139+
continue
140+
}
141+
return nil, fmt.Errorf("failed to read file %q: %w", filePath, err)
142+
}
143+
144+
switch f {
145+
case "nuse":
146+
if val, parseErr := strconv.ParseUint(value, 10, 64); parseErr == nil {
147+
namespace.UsedBlocks = val
148+
}
149+
case "size":
150+
if val, parseErr := strconv.ParseUint(value, 10, 64); parseErr == nil {
151+
namespace.SizeBlocks = val
152+
}
153+
case "queue/logical_block_size":
154+
if val, parseErr := strconv.ParseUint(value, 10, 64); parseErr == nil {
155+
namespace.LogicalBlockSize = val
156+
}
157+
case "ana_state":
158+
namespace.ANAState = value
159+
}
160+
}
161+
162+
// Calculate derived values
163+
namespace.UsedBytes = namespace.UsedBlocks * namespace.LogicalBlockSize
164+
namespace.SizeBytes = namespace.SizeBlocks * namespace.LogicalBlockSize
165+
namespace.CapacityBytes = namespace.SizeBlocks * namespace.LogicalBlockSize
166+
167+
namespaces = append(namespaces, namespace)
168+
}
169+
170+
device.Namespaces = namespaces
171+
91172
return &device, nil
92173
}

0 commit comments

Comments
 (0)