github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/cmd/ndm_daemonset/probe/udevprobe.go (about) 1 /* 2 Copyright 2018 OpenEBS Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package probe 18 19 import ( 20 "errors" 21 22 "github.com/openebs/node-disk-manager/blockdevice" 23 "github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller" 24 "github.com/openebs/node-disk-manager/pkg/features" 25 "github.com/openebs/node-disk-manager/pkg/sysfs" 26 libudevwrapper "github.com/openebs/node-disk-manager/pkg/udev" 27 "github.com/openebs/node-disk-manager/pkg/udevevent" 28 "github.com/openebs/node-disk-manager/pkg/util" 29 "golang.org/x/sync/semaphore" 30 31 "k8s.io/klog/v2" 32 ) 33 34 const ( 35 udevProbePriority = 1 36 udevConfigKey = "udev-probe" 37 ) 38 39 var ( 40 udevProbeName = "udev probe" 41 udevProbeState = defaultEnabled 42 ) 43 44 // udevProbeRegister contains registration process of udev probe 45 var udevProbeRegister = func() { 46 ctrl := <-controller.ControllerBroadcastChannel 47 if ctrl == nil { 48 klog.Error("unable to configure", udevProbeName) 49 return 50 } 51 if ctrl.NDMConfig != nil { 52 for _, probeConfig := range ctrl.NDMConfig.ProbeConfigs { 53 if probeConfig.Key == udevConfigKey { 54 udevProbeName = probeConfig.Name 55 udevProbeState = util.CheckTruthy(probeConfig.State) 56 break 57 } 58 } 59 } 60 newRegisterProbe := ®isterProbe{ 61 priority: udevProbePriority, 62 name: udevProbeName, 63 state: udevProbeState, 64 pi: newUdevProbe(ctrl), 65 controller: ctrl, 66 } 67 newRegisterProbe.register() 68 } 69 70 // udevProbe contains require variables for scan , populate diskInfo and push 71 // resource in etcd 72 type udevProbe struct { 73 controller *controller.Controller 74 udev *libudevwrapper.Udev 75 udevDevice *libudevwrapper.UdevDevice 76 udevEnumerate *libudevwrapper.UdevEnumerate 77 udeveventSubscription *udevevent.Subscription 78 } 79 80 // newUdevProbe returns udevProbe struct which helps to setup probe listen and scan 81 // system it contains copy of udev, udevEnumerate struct use defer free() in caller 82 // function to free c pointer memory. 83 func newUdevProbe(c *controller.Controller) *udevProbe { 84 udev, err := libudevwrapper.NewUdev() 85 if err != nil { 86 return nil 87 } 88 udevEnumerate, err := udev.NewUdevEnumerate() 89 if err != nil { 90 return nil 91 } 92 udevProbe := &udevProbe{ 93 controller: c, 94 udev: udev, 95 udevEnumerate: udevEnumerate, 96 } 97 return udevProbe 98 } 99 100 // newUdevProbeForFillDiskDetails returns udevProbe struct which helps populate diskInfo struct. 101 // it contains copy of udevDevice struct to populate diskInfo use defer free in caller function 102 // to free c pointer memory 103 func newUdevProbeForFillDiskDetails(sysPath string) (*udevProbe, error) { 104 udev, err := libudevwrapper.NewUdev() 105 if err != nil { 106 return nil, err 107 } 108 udevDevice, err := udev.NewDeviceFromSysPath(sysPath) 109 if err != nil { 110 return nil, err 111 } 112 udevProbe := &udevProbe{ 113 udev: udev, 114 udevDevice: udevDevice, 115 } 116 return udevProbe, nil 117 } 118 119 // Start setup udev probe listener and make a single scan of system 120 func (up *udevProbe) Start() { 121 go up.listen() 122 if features.FeatureGates.IsEnabled(features.ChangeDetection) { 123 up.udeveventSubscription = udevevent.Subscribe( 124 udevevent.EventTypeAdd, 125 udevevent.EventTypeRemove, 126 udevevent.EventTypeChange, 127 ) 128 } else { 129 up.udeveventSubscription = udevevent.Subscribe( 130 udevevent.EventTypeAdd, 131 udevevent.EventTypeRemove, 132 ) 133 } 134 errChan := udevevent.Monitor() 135 go up.listenUdevEventMonitor(errChan) 136 probeEvent := newUdevProbe(up.controller) 137 err := probeEvent.scan() 138 if err != nil { 139 klog.Errorf("error while scanning system for block devices, Error: %v", err) 140 } 141 } 142 143 // Rescan syncs etcd and NDM 144 func Rescan(c *controller.Controller) error { 145 udevProbe := newUdevProbe(c) 146 defer udevProbe.free() 147 err := udevProbe.scan() 148 if err != nil { 149 klog.Error(err) 150 return err 151 } 152 return nil 153 } 154 155 var sem = semaphore.NewWeighted(1) 156 157 // scan scans system for block devices and send add event via channel 158 func (up *udevProbe) scan() error { 159 160 // By using a semaphore, we ensure thread safety. 161 if !sem.TryAcquire(1) { 162 return errors.New("Scan is in progress") 163 } 164 defer sem.Release(1) 165 166 if (up.udev == nil) || (up.udevEnumerate == nil) { 167 return errors.New("unable to scan udev and udev enumerate is nil") 168 } 169 diskInfo := make([]*blockdevice.BlockDevice, 0) 170 disksUid := make([]string, 0) 171 err := up.udevEnumerate.AddSubsystemFilter(libudevwrapper.UDEV_SUBSYSTEM) 172 if err != nil { 173 return err 174 } 175 err = up.udevEnumerate.ScanDevices() 176 if err != nil { 177 return err 178 } 179 // everytime while performing the scan, we are re-initializing the 180 // disk map of the system 181 up.controller.BDHierarchy = make(blockdevice.Hierarchy) 182 for l := up.udevEnumerate.ListEntry(); l != nil; l = l.GetNextEntry() { 183 s := l.GetName() 184 newUdevice, err := up.udev.NewDeviceFromSysPath(s) 185 if err != nil { 186 continue 187 } 188 if newUdevice.IsDisk() || newUdevice.IsParitition() { 189 deviceDetails := &blockdevice.BlockDevice{} 190 if features.FeatureGates.IsEnabled(features.GPTBasedUUID) { 191 // WWN, Serial, PartitionTableUUID/GPTLabel, PartitionUUID, FileSystemUUID and DeviceType 192 // are the fields we use to generate the UUID. These fields will be fetched 193 // from the udev event itself. This is to guarantee that we do not need to rely 194 // on any other probes to fill in those details which are critical for device identification. 195 deviceDetails.DeviceAttributes.WWN = newUdevice.GetPropertyValue(libudevwrapper.UDEV_WWN) 196 deviceDetails.DeviceAttributes.Serial = newUdevice.GetPropertyValue(libudevwrapper.UDEV_SERIAL) 197 deviceDetails.PartitionInfo.PartitionTableUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_PARTITION_TABLE_UUID) 198 deviceDetails.PartitionInfo.PartitionEntryUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_PARTITION_UUID) 199 deviceDetails.FSInfo.FileSystemUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_FS_UUID) 200 deviceDetails.DMInfo.DMUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_DM_UUID) 201 } else { 202 uuid := newUdevice.GetUid() 203 disksUid = append(disksUid, uuid) 204 deviceDetails.UUID = uuid 205 } 206 udevDeviceType := newUdevice.GetPropertyValue(libudevwrapper.UDEV_DEVTYPE) 207 deviceDetails.SysPath = newUdevice.GetSyspath() 208 deviceDetails.DevPath = newUdevice.GetPath() 209 210 // log the details only if present, to avoid log flooding 211 if deviceDetails.DeviceAttributes.WWN != "" { 212 klog.V(4).Infof("device: %s, WWN: %s filled during udev scan", 213 deviceDetails.DevPath, deviceDetails.DeviceAttributes.WWN) 214 } 215 if deviceDetails.DeviceAttributes.Serial != "" { 216 klog.V(4).Infof("device: %s, Serial: %s filled during udev scan", 217 deviceDetails.DevPath, deviceDetails.DeviceAttributes.Serial) 218 } 219 if deviceDetails.PartitionInfo.PartitionTableUUID != "" { 220 klog.V(4).Infof("device: %s, PartitionTableUUID: %s filled during udev scan", 221 deviceDetails.DevPath, deviceDetails.PartitionInfo.PartitionTableUUID) 222 } 223 if deviceDetails.PartitionInfo.PartitionEntryUUID != "" { 224 klog.V(4).Infof("device: %s, PartitionEntryUUID: %s filled during udev scan", 225 deviceDetails.DevPath, deviceDetails.PartitionInfo.PartitionEntryUUID) 226 } 227 if deviceDetails.FSInfo.FileSystemUUID != "" { 228 klog.V(4).Infof("device: %s, FileSystemUUID: %s filled during udev scan", 229 deviceDetails.DevPath, deviceDetails.FSInfo.FileSystemUUID) 230 } 231 232 sysfsDevice, err := sysfs.NewSysFsDeviceFromDevPath(deviceDetails.DevPath) 233 // TODO if error occurs a rescan may be required 234 if err != nil { 235 klog.Errorf("could not get sysfs device for %s, err: %v", deviceDetails.DevPath, err) 236 } else { 237 // get the dependents of the block device 238 // this is done by scanning sysfs 239 dependents, err := sysfsDevice.GetDependents() 240 // TODO if error occurs need to do a scan from the beginning 241 if err != nil { 242 klog.Errorf("error getting dependent devices for %s, err: %v", deviceDetails.DevPath, err) 243 } else { 244 deviceDetails.DependentDevices = dependents 245 klog.Infof("Dependents of %s : %+v", deviceDetails.DevPath, dependents) 246 } 247 // the device type reported by udev will always be disk/partition. Using this info 248 // and the entries from sysfs, the actual device type is found out. 249 deviceType, err := sysfsDevice.GetDeviceType(udevDeviceType) 250 if err != nil { 251 klog.Errorf("could not get device type for %s, falling back to udev reported type: %s", deviceDetails.DevPath, udevDeviceType) 252 deviceType = udevDeviceType 253 } 254 deviceDetails.DeviceAttributes.DeviceType = deviceType 255 klog.Infof("Device: %s is of type: %s", deviceDetails.DevPath, deviceDetails.DeviceAttributes.DeviceType) 256 } 257 258 diskInfo = append(diskInfo, deviceDetails) 259 } 260 newUdevice.UdevDeviceUnref() 261 } 262 263 // when GPTBasedUUID is enabled, all the blockdevices will be made inactive initially. 264 // after that each device that is detected by the probe will be marked as Active. 265 up.controller.DeactivateStaleBlockDeviceResource(disksUid) 266 eventDetails := controller.EventMessage{ 267 Action: libudevwrapper.UDEV_ACTION_ADD, 268 Devices: diskInfo, 269 } 270 controller.EventMessageChannel <- eventDetails 271 return nil 272 } 273 274 // fillDiskDetails fills details in diskInfo struct using probe information 275 func (up *udevProbe) FillBlockDeviceDetails(blockDevice *blockdevice.BlockDevice) { 276 udevDevice, err := newUdevProbeForFillDiskDetails(blockDevice.SysPath) 277 if err != nil { 278 klog.Errorf("%s : %s", blockDevice.SysPath, err) 279 return 280 } 281 udevDiskDetails := udevDevice.udevDevice.DiskInfoFromLibudev() 282 defer udevDevice.free() 283 blockDevice.DevPath = udevDiskDetails.Path 284 blockDevice.DeviceAttributes.Model = udevDiskDetails.Model 285 blockDevice.DeviceAttributes.WWN = udevDiskDetails.WWN 286 blockDevice.DeviceAttributes.Serial = udevDiskDetails.Serial 287 blockDevice.DeviceAttributes.Vendor = udevDiskDetails.Vendor 288 blockDevice.DeviceAttributes.IDType = udevDiskDetails.IDType 289 290 blockDevice.DMInfo.DevMapperPath = udevDiskDetails.DMPath 291 292 // log only if details are present to prevent log flooding 293 if blockDevice.DeviceAttributes.Model != "" { 294 klog.V(4).Infof("device: %s, Model: %s filled by udev probe", 295 blockDevice.DevPath, blockDevice.DeviceAttributes.Model) 296 } 297 if blockDevice.DeviceAttributes.WWN != "" { 298 klog.V(4).Infof("device: %s, WWN: %s filled by udev probe", 299 blockDevice.DevPath, blockDevice.DeviceAttributes.WWN) 300 } 301 if blockDevice.DeviceAttributes.Serial != "" { 302 klog.V(4).Infof("device: %s, Serial: %s filled by udev probe", 303 blockDevice.DevPath, blockDevice.DeviceAttributes.Serial) 304 } 305 if blockDevice.DeviceAttributes.Vendor != "" { 306 klog.V(4).Infof("device: %s, Vendor: %s filled by udev probe", 307 blockDevice.DevPath, blockDevice.DeviceAttributes.Vendor) 308 } 309 if blockDevice.DeviceAttributes.IDType != "" { 310 klog.V(4).Infof("device: %s, IDType: %s filled by udev probe", 311 blockDevice.DevPath, blockDevice.DeviceAttributes.IDType) 312 } 313 314 if len(udevDiskDetails.ByIdDevLinks) != 0 { 315 blockDevice.DevLinks = append(blockDevice.DevLinks, blockdevice.DevLink{ 316 Kind: libudevwrapper.BY_ID_LINK, 317 Links: udevDiskDetails.ByIdDevLinks, 318 }) 319 } 320 321 if len(udevDiskDetails.ByPathDevLinks) != 0 { 322 blockDevice.DevLinks = append(blockDevice.DevLinks, blockdevice.DevLink{ 323 Kind: libudevwrapper.BY_PATH_LINK, 324 Links: udevDiskDetails.ByPathDevLinks, 325 }) 326 } 327 328 if len(udevDiskDetails.SymLinks) != 0 { 329 blockDevice.DevLinks = append(blockDevice.DevLinks, blockdevice.DevLink{ 330 Kind: libudevwrapper.SYMLINK, 331 Links: udevDiskDetails.SymLinks, 332 }) 333 } 334 335 // filesystem info of the attached device. Only filesystem data will be filled in the struct, 336 // as the mountpoint related information will be filled in by the mount probe 337 blockDevice.FSInfo.FileSystem = udevDiskDetails.FileSystem 338 339 blockDevice.PartitionInfo.PartitionTableType = udevDiskDetails.PartitionTableType 340 341 // if this is a partition, partition number and partition UUID need to be filled 342 if udevDiskDetails.DiskType == blockdevice.BlockDeviceTypePartition { 343 blockDevice.PartitionInfo.PartitionNumber = udevDiskDetails.PartitionNumber 344 } 345 } 346 347 // listen listens for event message over UdevEventMessages channel 348 // when it gets event via channel it transfer to event handler 349 // this function is blocking function better to use it in a routine. 350 func (up *udevProbe) listen() { 351 if up.controller == nil { 352 klog.Error("unable to setup udev probe listener controller object is nil") 353 return 354 } 355 probeEvent := ProbeEvent{ 356 Controller: up.controller, 357 } 358 klog.Info("starting udev probe listener") 359 for { 360 msg := <-controller.EventMessageChannel 361 switch msg.Action { 362 case string(AttachEA): 363 probeEvent.addBlockDeviceEvent(msg) 364 case string(DetachEA): 365 probeEvent.deleteBlockDeviceEvent(msg) 366 case string(ChangeEA): 367 probeEvent.changeBlockDeviceEvent(msg) 368 } 369 } 370 } 371 372 // free frees c pointers if it is not null 373 func (up *udevProbe) free() { 374 if up.udev != nil { 375 up.udev.UnrefUdev() 376 } 377 if up.udevDevice != nil { 378 up.udevDevice.UdevDeviceUnref() 379 } 380 if up.udevEnumerate != nil { 381 up.udevEnumerate.UnrefUdevEnumerate() 382 } 383 } 384 385 func (up *udevProbe) listenUdevEventMonitor(errChan <-chan error) { 386 eventChan := up.udeveventSubscription.Events() 387 for { 388 select { 389 case event := <-eventChan: 390 controller.EventMessageChannel <- processUdevEvent(event) 391 case err := <-errChan: 392 klog.Error(err) 393 } 394 } 395 } 396 397 func processUdevEvent(event udevevent.UdevEvent) controller.EventMessage { 398 defer event.UdevDeviceUnref() 399 diskInfo := make([]*blockdevice.BlockDevice, 0) 400 uuid := event.GetUid() 401 path := event.GetPath() 402 action := event.GetAction() 403 klog.Infof("processing new event for (%s) action type %s", path, action) 404 deviceDetails := &blockdevice.BlockDevice{} 405 eventMessage := controller.EventMessage{} 406 407 deviceDetails.DevPath = path 408 // The change event handler discards the devices sent in the event message 409 // and only uses the dev path field to fetch the bd from the controller cache. 410 // Fill only dev path and do not process further in case of change events. 411 if action == udevevent.EventTypeChange { 412 eventMessage.RequestedProbes = []string{udevProbeName, sysfsProbeName} 413 goto event_dispatch 414 } 415 // This is the legacy uuid. It will be overwritten in the event handler. 416 deviceDetails.UUID = uuid 417 deviceDetails.SysPath = event.GetSyspath() 418 419 // fields used for UUID. These fields will be filled always. But used only if the 420 // GPTBasedUUID feature-gate is enabled. 421 deviceDetails.DeviceAttributes.DeviceType = event.GetPropertyValue(libudevwrapper.UDEV_DEVTYPE) 422 deviceDetails.DeviceAttributes.WWN = event.GetPropertyValue(libudevwrapper.UDEV_WWN) 423 deviceDetails.DeviceAttributes.Serial = event.GetPropertyValue(libudevwrapper.UDEV_SERIAL) 424 425 // The below 3 fields are used only for legacy uuid generation. But they are filled in here, 426 // so as to handle upgrade cases from legacy to gpt 427 deviceDetails.DeviceAttributes.Model = event.GetPropertyValue(libudevwrapper.UDEV_MODEL) 428 deviceDetails.DeviceAttributes.Vendor = event.GetPropertyValue(libudevwrapper.UDEV_VENDOR) 429 deviceDetails.DeviceAttributes.IDType = event.GetPropertyValue(libudevwrapper.UDEV_TYPE) 430 431 deviceDetails.PartitionInfo.PartitionTableUUID = event.GetPropertyValue(libudevwrapper.UDEV_PARTITION_TABLE_UUID) 432 deviceDetails.PartitionInfo.PartitionEntryUUID = event.GetPropertyValue(libudevwrapper.UDEV_PARTITION_UUID) 433 deviceDetails.FSInfo.FileSystemUUID = event.GetPropertyValue(libudevwrapper.UDEV_FS_UUID) 434 435 deviceDetails.DMInfo.DMUUID = event.GetPropertyValue(libudevwrapper.UDEV_DM_UUID) 436 437 // fields used for dependents. dependents cannot be obtained while 438 // removing the device since sysfs entry will be absent 439 if action != udevevent.EventTypeRemove { 440 sysfsDevice, err := sysfs.NewSysFsDeviceFromDevPath(deviceDetails.DevPath) 441 if err != nil { 442 klog.Errorf("could not get sysfs device for %s, err: %v", deviceDetails.DevPath, err) 443 } else { 444 dependents, err := sysfsDevice.GetDependents() 445 // TODO if error occurs need to do a scan from the beginning 446 if err != nil { 447 klog.Errorf("could not get dependents for %s, %v", deviceDetails.DevPath, err) 448 } else { 449 deviceDetails.DependentDevices = dependents 450 klog.V(4).Infof("Dependents of %s : %+v", deviceDetails.DevPath, dependents) 451 } 452 udevDeviceType := deviceDetails.DeviceAttributes.DeviceType 453 deviceType, err := sysfsDevice.GetDeviceType(udevDeviceType) 454 if err != nil { 455 klog.Errorf("could not get device type for %s, falling back to udev reported type: %s", deviceDetails.DevPath, udevDeviceType) 456 deviceType = udevDeviceType 457 } 458 deviceDetails.DeviceAttributes.DeviceType = deviceType 459 klog.Infof("Device: %s is of type: %s", deviceDetails.DevPath, deviceDetails.DeviceAttributes.DeviceType) 460 } 461 } 462 463 event_dispatch: 464 diskInfo = append(diskInfo, deviceDetails) 465 switch action { 466 case udevevent.EventTypeAdd: 467 eventMessage.Action = string(AttachEA) 468 case udevevent.EventTypeRemove: 469 eventMessage.Action = string(DetachEA) 470 case udevevent.EventTypeChange: 471 eventMessage.Action = string(ChangeEA) 472 } 473 eventMessage.Devices = diskInfo 474 return eventMessage 475 }