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 }