Skip to content
Draft
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
7 changes: 7 additions & 0 deletions disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ package disko

import (
"encoding/json"
"errors"
"fmt"
"sort"
"strings"

"machinerun.io/disko/partid"
)

// ErrNotVirtualDrive is returned by RAIDController.GetDiskType when the
// device path does not match any virtual/logical drive on the controller.
// This typically indicates a JBOD/passthrough disk that the OS sees through
// the RAID HBA sysfs tree but which has no corresponding VD/LD entry.
var ErrNotVirtualDrive = errors.New("device is not a virtual/logical drive on the RAID controller")

// DiskType enumerates supported disk types.
type DiskType int

Expand Down
64 changes: 64 additions & 0 deletions linux/sysfs/sysfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package sysfs

import (
"fmt"
"path/filepath"
"strings"
)

// IsSysPathRAID checks whether syspath (udevadm DEVPATH) belongs to a RAID
// controller whose PCI driver is registered at driverSysPath.
//
// syspath will look something like
// /devices/pci0000:3a/0000:3a:02.0/0000:3c:00.0/host0/target0:2:2/0:2:2:0/block/sdc
func IsSysPathRAID(syspath string, driverSysPath string) bool {
if !strings.HasPrefix(syspath, "/sys") {
syspath = "/sys" + syspath
}

if !strings.Contains(syspath, "/host") {
return false
}

fp, err := filepath.EvalSymlinks(syspath)
if err != nil {
fmt.Printf("seriously? %s\n", err)
return false
}

for _, path := range GetSysPaths(driverSysPath) {
if strings.HasPrefix(fp, path) {
return true
}
}

return false
}

// GetSysPaths returns the resolved PCI device paths for a RAID driver.
func GetSysPaths(driverSysPath string) []string {
paths := []string{}
// a raid driver has directory entries for each of the scsi hosts on that controller.
// $cd /sys/bus/pci/drivers/<driver name>
// $ for d in *; do [ -d "$d" ] || continue; echo "$d -> $( cd "$d" && pwd -P )"; done
// 0000:3c:00.0 -> /sys/devices/pci0000:3a/0000:3a:02.0/0000:3c:00.0
// module -> /sys/module/<driver module name>

// We take a hack path and consider anything with a ":" in that dir as a host path.
matches, err := filepath.Glob(driverSysPath + "/*:*")

if err != nil {
fmt.Printf("errors: %s\n", err)
return paths
}

for _, p := range matches {
fp, err := filepath.EvalSymlinks(p)

if err == nil {
paths = append(paths, fp)
}
}

return paths
}
56 changes: 37 additions & 19 deletions linux/system.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package linux

import (
"errors"
"fmt"
"log"
"os"
Expand Down Expand Up @@ -161,23 +162,16 @@ func (ls *linuxSystem) ScanDisk(devicePath string) (disko.Disk, error) {

attachType = getAttachType(udInfo)

for _, ctrl := range ls.raidctrls {
if IsSysPathRAID(udInfo.Properties["DEVPATH"], ctrl.DriverSysfsPath()) {
// we know this is device is part of a raid, so if we cannot get
// disk type we must return an error
dType, err := ctrl.GetDiskType(devicePath)
if err != nil {
return disko.Disk{}, fmt.Errorf("failed to get diskType of %q from RAID controller: %s", devicePath, err)
}

attachType = disko.RAID
diskType = dType
raidType, onRAID, raidErr := ls.resolveRAIDDiskType(devicePath, udInfo.Properties["DEVPATH"])
if raidErr != nil {
return disko.Disk{}, raidErr
}

break
}
if onRAID {
attachType = disko.RAID
diskType = raidType
}

// check disk type if it wasn't on raid
if attachType != disko.RAID {
diskType, err = getDiskType(udInfo)
if err != nil {
Expand Down Expand Up @@ -298,15 +292,39 @@ func (ls *linuxSystem) Wipe(d disko.Disk) error {
}

func (ls *linuxSystem) GetDiskType(path string, udInfo disko.UdevInfo) (disko.DiskType, error) {
dType, found, err := ls.resolveRAIDDiskType(path, udInfo.Properties["DEVPATH"])
if err != nil {
return disko.HDD, err
}

if found {
return dType, nil
}

return getDiskType(udInfo)
}

// resolveRAIDDiskType checks whether devicePath sits behind a RAID controller
// and returns the disk type reported by that controller. When the device is on a
// RAID sysfs path but has no virtual-drive entry (JBOD/passthrough) the sentinel
// is caught and (_, false, nil) is returned so the caller falls through to
// generic detection.
func (ls *linuxSystem) resolveRAIDDiskType(devicePath, devpath string) (disko.DiskType, bool, error) {
for _, ctrl := range ls.raidctrls {
if IsSysPathRAID(udInfo.Properties["DEVPATH"], ctrl.DriverSysfsPath()) {
dType, err := ctrl.GetDiskType(path)
if ctrl.IsSysPathRAID(devpath) {
dType, err := ctrl.GetDiskType(devicePath)
if err != nil {
return disko.HDD, fmt.Errorf("failed to get diskType of %q from RAID controller: %s", path, err)
if errors.Is(err, disko.ErrNotVirtualDrive) {
log.Printf("device %q on RAID sysfs path has no virtual drive (JBOD?), using generic detection", devicePath)
return disko.HDD, false, nil
}

return disko.HDD, false, fmt.Errorf("failed to get diskType of %q from RAID controller: %s", devicePath, err)
}

return dType, nil
return dType, true, nil
}
}
return getDiskType(udInfo)

return disko.HDD, false, nil
}
Loading
Loading