storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/smart/smart.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5   * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc.
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   */
    19  
    20  package smart
    21  
    22  import (
    23  	"bytes"
    24  	"encoding/binary"
    25  	"fmt"
    26  	"regexp"
    27  	"strings"
    28  	"unsafe"
    29  
    30  	"github.com/dswarbrick/smart/drivedb"
    31  	"github.com/dswarbrick/smart/ioctl"
    32  	"github.com/dswarbrick/smart/scsi"
    33  	"github.com/dswarbrick/smart/utils"
    34  
    35  	"gopkg.in/yaml.v2"
    36  )
    37  
    38  // GetInfo - gets info about device
    39  func GetInfo(device string) (Info, error) {
    40  	info := Info{
    41  		Device: device,
    42  	}
    43  
    44  	var db drivedb.DriveDb
    45  	dec := yaml.NewDecoder(bytes.NewBuffer(MustAsset("drivedb.yaml")))
    46  
    47  	err := dec.Decode(&db)
    48  	if err != nil {
    49  		return info, err
    50  	}
    51  
    52  	for i, d := range db.Drives {
    53  		db.Drives[i].CompiledRegexp, _ = regexp.Compile(d.ModelRegex)
    54  	}
    55  
    56  	if strings.HasPrefix(device, "/dev/nvme") {
    57  		d := NewNVMeDevice(device)
    58  		if err := d.Open(); err != nil {
    59  			return info, err
    60  		}
    61  		nvmeInfo, err := getNvmeInfo(d)
    62  		if err != nil {
    63  			return info, err
    64  		}
    65  		info.Nvme = nvmeInfo
    66  		return info, nil
    67  	}
    68  
    69  	d, err := scsi.OpenSCSIAutodetect(device)
    70  	if err != nil {
    71  		return info, err
    72  	}
    73  
    74  	switch dev := d.(type) {
    75  	case *scsi.SCSIDevice:
    76  		scsiInfo, err := getScsiInfo(dev)
    77  		if err != nil {
    78  			return info, err
    79  		}
    80  		info.Scsi = scsiInfo
    81  	case *scsi.SATDevice:
    82  		ataInfo, err := getAtaInfo(dev)
    83  		if err != nil {
    84  			return info, err
    85  		}
    86  		info.Ata = ataInfo
    87  	}
    88  
    89  	return info, nil
    90  }
    91  
    92  func getNvmeInfo(d *NVMeDevice) (*NvmeInfo, error) {
    93  	buf := make([]byte, 4096)
    94  	nvmeInfo := &NvmeInfo{}
    95  
    96  	cmd := nvmePassthruCommand{
    97  		opcode:  NvmeAdminIdentify,
    98  		nsid:    0, // Namespace 0, since we are identifying the controller
    99  		addr:    uint64(uintptr(unsafe.Pointer(&buf[0]))),
   100  		dataLen: uint32(len(buf)),
   101  		cdw10:   1, // Identify controller
   102  	}
   103  
   104  	if err := ioctl.Ioctl(uintptr(d.fd), nvmeIoctlAdminCmd, uintptr(unsafe.Pointer(&cmd))); err != nil {
   105  		return nvmeInfo, err
   106  	}
   107  
   108  	var controller nvmeIdentController
   109  	binary.Read(bytes.NewBuffer(buf[:]), utils.NativeEndian, &controller)
   110  
   111  	nvmeInfo.VendorID = strings.TrimSpace(fmt.Sprintf("%#04x", controller.VendorID))
   112  	nvmeInfo.ModelNum = strings.TrimSpace(fmt.Sprintf("%s", controller.ModelNumber))
   113  	nvmeInfo.SerialNum = strings.TrimSpace(fmt.Sprintf("%s", controller.SerialNumber))
   114  	nvmeInfo.FirmwareVersion = strings.TrimSpace(fmt.Sprintf("%s", controller.Firmware))
   115  	nvmeInfo.MaxDataTransferPages = 1 << controller.Mdts
   116  
   117  	buf2 := make([]byte, 512)
   118  
   119  	// Read SMART log
   120  	if err := d.readLogPage(0x02, &buf2); err != nil {
   121  		return nvmeInfo, err
   122  	}
   123  
   124  	var sl nvmeSMARTLog
   125  	binary.Read(bytes.NewBuffer(buf2[:]), utils.NativeEndian, &sl)
   126  
   127  	unitsRead := le128ToBigInt(sl.DataUnitsRead)
   128  	unitsWritten := le128ToBigInt(sl.DataUnitsWritten)
   129  
   130  	nvmeInfo.CriticalWarning = fmt.Sprintf("%x", sl.CritWarning)
   131  	nvmeInfo.Temperature = fmt.Sprintf("%d Celsius",
   132  		((uint16(sl.Temperature[1])<<8)|uint16(sl.Temperature[0]))-273) // Kelvin to degrees Celsius
   133  	nvmeInfo.SpareAvailable = fmt.Sprintf("%d%%", sl.AvailSpare)
   134  	nvmeInfo.SpareThreshold = fmt.Sprintf("%d%%", sl.SpareThresh)
   135  	nvmeInfo.DataUnitsReadBytes = unitsRead
   136  	nvmeInfo.DataUnitsWrittenBytes = unitsWritten
   137  	nvmeInfo.HostReadCommands = le128ToBigInt(sl.HostReads)
   138  	nvmeInfo.HostWriteCommands = le128ToBigInt(sl.HostWrites)
   139  	nvmeInfo.ControllerBusyTime = le128ToBigInt(sl.CtrlBusyTime)
   140  	nvmeInfo.PowerCycles = le128ToBigInt(sl.PowerCycles)
   141  	nvmeInfo.PowerOnHours = le128ToBigInt(sl.PowerOnHours)
   142  	nvmeInfo.UnsafeShutdowns = le128ToBigInt(sl.UnsafeShutdowns)
   143  	nvmeInfo.MediaAndDataIntegrityErrors = le128ToBigInt(sl.MediaErrors)
   144  
   145  	return nvmeInfo, nil
   146  }
   147  
   148  func getScsiInfo(d *scsi.SCSIDevice) (*ScsiInfo, error) {
   149  	return &ScsiInfo{}, nil
   150  }
   151  
   152  func getAtaInfo(d *scsi.SATDevice) (*AtaInfo, error) {
   153  	return &AtaInfo{}, nil
   154  }