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 := &registerProbe{
    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  }