diff --git a/blockdevice/stats.go b/blockdevice/stats.go index 52139d3e8..56e9a475f 100644 --- a/blockdevice/stats.go +++ b/blockdevice/stats.go @@ -16,13 +16,13 @@ package blockdevice import ( "bufio" "fmt" - "github.com/prometheus/procfs/internal/util" "io" "io/ioutil" "os" "strings" "github.com/prometheus/procfs/internal/fs" + "github.com/prometheus/procfs/internal/util" ) // Info contains identifying information for a block device such as a disk drive @@ -178,12 +178,37 @@ type BlockQueueStats struct { WriteZeroesMaxBytes uint64 } +// DeviceMapperInfo models the devicemapper files that are located in the sysfs tree for each block device +// and described in the kernel documentation: +// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm +type DeviceMapperInfo struct { + // Name is the string containing mapped device name. + Name string + // RqBasedSeqIOMergeDeadline determines how long (in microseconds) a request that is a reasonable merge + // candidate can be queued on the request queue. + RqBasedSeqIOMergeDeadline uint64 + // Suspended indicates if the device is suspended (1 is on, 0 is off). + Suspended uint64 + // UseBlkMQ indicates if the device is using the request-based blk-mq I/O path mode (1 is on, 0 is off). + UseBlkMQ uint64 + // UUID is the DM-UUID string or empty string if DM-UUID is not set. + UUID string +} + +// UnderlyingDevices models the list of devices that this device is built from. +type UnderlyingDeviceInfo struct { + // DeviceNames is the list of devices names + DeviceNames []string +} + const ( procDiskstatsPath = "diskstats" procDiskstatsFormat = "%d %d %s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d" sysBlockPath = "block" sysBlockStatFormat = "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d" sysBlockQueue = "queue" + sysBlockDM = "dm" + sysUnderlyingDev = "slaves" ) // FS represents the pseudo-filesystems proc and sys, which provides an @@ -317,7 +342,7 @@ func (fs FS) SysBlockDeviceStat(device string) (IOStats, int, error) { // SysBlockDeviceQueueStats returns stats for /sys/block/xxx/queue where xxx is a device name. func (fs FS) SysBlockDeviceQueueStats(device string) (BlockQueueStats, error) { stat := BlockQueueStats{} - // files with uint64 fields + // Files with uint64 fields for file, p := range map[string]*uint64{ "add_random": &stat.AddRandom, "dax": &stat.DAX, @@ -355,7 +380,7 @@ func (fs FS) SysBlockDeviceQueueStats(device string) (BlockQueueStats, error) { } *p = val } - // files with int64 fields + // Files with int64 fields for file, p := range map[string]*int64{ "io_poll_delay": &stat.IOPollDelay, "wbt_lat_usec": &stat.WBTLatUSec, @@ -366,7 +391,7 @@ func (fs FS) SysBlockDeviceQueueStats(device string) (BlockQueueStats, error) { } *p = val } - // files with string fields + // Files with string fields for file, p := range map[string]*string{ "write_cache": &stat.WriteCache, "zoned": &stat.Zoned, @@ -398,3 +423,44 @@ func (fs FS) SysBlockDeviceQueueStats(device string) (BlockQueueStats, error) { } return stat, nil } + +func (fs FS) SysBlockDeviceMapperInfo(device string) (DeviceMapperInfo, error) { + info := DeviceMapperInfo{} + // Files with uint64 fields + for file, p := range map[string]*uint64{ + "rq_based_seq_io_merge_deadline": &info.RqBasedSeqIOMergeDeadline, + "suspended": &info.Suspended, + "use_blk_mq": &info.UseBlkMQ, + } { + val, err := util.ReadUintFromFile(fs.sys.Path(sysBlockPath, device, sysBlockDM, file)) + if err != nil { + return DeviceMapperInfo{}, err + } + *p = val + } + // Files with string fields + for file, p := range map[string]*string{ + "name": &info.Name, + "uuid": &info.UUID, + } { + val, err := util.SysReadFile(fs.sys.Path(sysBlockPath, device, sysBlockDM, file)) + if err != nil { + return DeviceMapperInfo{}, err + } + *p = val + } + return info, nil +} + +func (fs FS) SysBlockDeviceUnderlyingDevices(device string) (UnderlyingDeviceInfo, error) { + underlyingDir, err := os.Open(fs.sys.Path(sysBlockPath, device, sysUnderlyingDev)) + if err != nil { + return UnderlyingDeviceInfo{}, err + } + underlying, err := underlyingDir.Readdirnames(0) + if err != nil { + return UnderlyingDeviceInfo{}, err + } + return UnderlyingDeviceInfo{DeviceNames: underlying}, nil + +} diff --git a/blockdevice/stats_test.go b/blockdevice/stats_test.go index c0a5768a4..a701bcd2a 100644 --- a/blockdevice/stats_test.go +++ b/blockdevice/stats_test.go @@ -14,6 +14,7 @@ package blockdevice import ( + "os" "reflect" "testing" ) @@ -152,3 +153,69 @@ func TestBlockDevice(t *testing.T) { t.Errorf("Incorrect BlockQueueStat, expected: \n%+v, got: \n%+v", blockQueueStatExpected, blockQueueStat) } } + +func TestBlockDmInfo(t *testing.T) { + blockdevice, err := NewFS("../fixtures/proc", "../fixtures/sys") + if err != nil { + t.Fatalf("failed to access blockdevice fs: %v", err) + } + devices, err := blockdevice.SysBlockDevices() + if err != nil { + t.Fatal(err) + } + dm0Info, err := blockdevice.SysBlockDeviceMapperInfo(devices[0]) + if err != nil { + t.Fatal(err) + } + + dm0InfoExpected := DeviceMapperInfo{ + Name: "vg0--lv_root", + RqBasedSeqIOMergeDeadline: 0, + Suspended: 0, + UseBlkMQ: 0, + UUID: "LVM-3zSHSR5Nbf4j7g6auAAefWY2CMaX01theZYEvQyecVsm2WtX3iY5q51qq5dWWOq7", + } + if !reflect.DeepEqual(dm0Info, dm0InfoExpected) { + t.Errorf("Incorrect BlockQueueStat, expected: \n%+v, got: \n%+v", dm0InfoExpected, dm0Info) + } + + dm1Info, err := blockdevice.SysBlockDeviceMapperInfo(devices[1]) + if err != nil { + if _, ok := err.(*os.PathError); ok { + // Fail the test if there's an error other than PathError. + if !os.IsNotExist(err) { + t.Fatal(err) + } + } else { + t.Fatal(err) + } + } else { + t.Fatal("SysBlockDeviceMapperInfo on sda was supposed to fail.") + } + dm1InfoExpected := DeviceMapperInfo{} + if !reflect.DeepEqual(dm1Info, dm1InfoExpected) { + t.Errorf("Incorrect BlockQueueStat, expected: \n%+v, got: \n%+v", dm0InfoExpected, dm0Info) + } +} + +func TestSysBlockDeviceUnderlyingDevices(t *testing.T) { + blockdevice, err := NewFS("../fixtures/proc", "../fixtures/sys") + if err != nil { + t.Fatalf("failed to access blockdevice fs: %v", err) + } + devices, err := blockdevice.SysBlockDevices() + if err != nil { + t.Fatal(err) + } + + underlying0, err := blockdevice.SysBlockDeviceUnderlyingDevices(devices[0]) + if err != nil { + t.Fatal(err) + } + underlying0Expected := UnderlyingDeviceInfo{ + DeviceNames: []string{"sda"}, + } + if !reflect.DeepEqual(underlying0, underlying0Expected) { + t.Errorf("Incorrect BlockQueueStat, expected: \n%+v, got: \n%+v", underlying0Expected, underlying0) + } +} diff --git a/fixtures.ttar b/fixtures.ttar index e005ee94a..a920770c7 100644 --- a/fixtures.ttar +++ b/fixtures.ttar @@ -3231,6 +3231,41 @@ Mode: 775 Directory: fixtures/sys/block/dm-0 Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/block/dm-0/dm +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/block/dm-0/dm/name +Lines: 1 +vg0--lv_rootEOF +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/block/dm-0/dm/rq_based_seq_io_merge_deadline +Lines: 1 +0EOF +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/block/dm-0/dm/suspended +Lines: 1 +0EOF +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/block/dm-0/dm/use_blk_mq +Lines: 1 +0EOF +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/block/dm-0/dm/uuid +Lines: 1 +LVM-3zSHSR5Nbf4j7g6auAAefWY2CMaX01theZYEvQyecVsm2WtX3iY5q51qq5dWWOq7EOF +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/block/dm-0/slaves +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/block/dm-0/slaves/sda +Lines: 0 +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/block/dm-0/stat Lines: 1 6447303 0 710266738 1529043 953216 0 31201176 4557464 0 796160 6088971 @@ -3478,6 +3513,27 @@ Mode: 664 Directory: fixtures/sys/class Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/block +Mode: 775 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/block/dm-0 +SymlinkTo: ../../devices/virtual/block/dm-0 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/block/dm-1 +SymlinkTo: ../../devices/virtual/block/dm-1 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/block/dm-2 +SymlinkTo: ../../devices/virtual/block/dm-2 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/block/dm-3 +SymlinkTo: ../../devices/virtual/block/dm-3 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/block/dm-4 +SymlinkTo: ../../devices/virtual/block/dm-4 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/block/dm-5 +SymlinkTo: ../../devices/virtual/block/dm-5 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/class/dmi Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6491,6 +6547,15 @@ nr_zone_active_file 11 nr_zone_unevictable 12 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/virtual +Mode: 775 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/virtual/block +Mode: 775 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/virtual/block/dm-0 +Mode: 775 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/fs Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -