github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/pkg/host/internal/kernel/kernel.go (about) 1 package kernel 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "sigs.k8s.io/controller-runtime/pkg/log" 11 12 sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" 13 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" 14 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal" 15 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" 16 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" 17 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" 18 ) 19 20 type kernel struct { 21 utilsHelper utils.CmdInterface 22 } 23 24 func New(utilsHelper utils.CmdInterface) types.KernelInterface { 25 return &kernel{utilsHelper: utilsHelper} 26 } 27 28 func (k *kernel) LoadKernelModule(name string, args ...string) error { 29 log.Log.Info("LoadKernelModule(): try to load kernel module", "name", name, "args", args) 30 chrootDefinition := utils.GetChrootExtension() 31 cmdArgs := strings.Join(args, " ") 32 33 // check if the driver is already loaded in to the system 34 isLoaded, err := k.IsKernelModuleLoaded(name) 35 if err != nil { 36 log.Log.Error(err, "LoadKernelModule(): failed to check if kernel module is already loaded", "name", name) 37 } 38 if isLoaded { 39 log.Log.Info("LoadKernelModule(): kernel module already loaded", "name", name) 40 return nil 41 } 42 43 _, _, err = k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s modprobe %s %s", chrootDefinition, name, cmdArgs)) 44 if err != nil { 45 log.Log.Error(err, "LoadKernelModule(): failed to load kernel module with arguments", "name", name, "args", args) 46 return err 47 } 48 return nil 49 } 50 51 func (k *kernel) IsKernelModuleLoaded(kernelModuleName string) (bool, error) { 52 log.Log.Info("IsKernelModuleLoaded(): check if kernel module is loaded", "name", kernelModuleName) 53 chrootDefinition := utils.GetChrootExtension() 54 55 stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s lsmod | grep \"^%s\"", chrootDefinition, kernelModuleName)) 56 if err != nil && len(stderr) != 0 { 57 log.Log.Error(err, "IsKernelModuleLoaded(): failed to check if kernel module is loaded", 58 "name", kernelModuleName, "stderr", stderr) 59 return false, err 60 } 61 log.Log.V(2).Info("IsKernelModuleLoaded():", "stdout", stdout) 62 if len(stderr) != 0 { 63 log.Log.Error(err, "IsKernelModuleLoaded(): failed to check if kernel module is loaded", "name", kernelModuleName, "stderr", stderr) 64 return false, fmt.Errorf(stderr) 65 } 66 67 if len(stdout) != 0 { 68 log.Log.Info("IsKernelModuleLoaded(): kernel module already loaded", "name", kernelModuleName) 69 return true, nil 70 } 71 72 return false, nil 73 } 74 75 func (k *kernel) TryEnableTun() { 76 if err := k.LoadKernelModule("tun"); err != nil { 77 log.Log.Error(err, "tryEnableTun(): TUN kernel module not loaded") 78 } 79 } 80 81 func (k *kernel) TryEnableVhostNet() { 82 if err := k.LoadKernelModule("vhost_net"); err != nil { 83 log.Log.Error(err, "tryEnableVhostNet(): VHOST_NET kernel module not loaded") 84 } 85 } 86 87 // GetCurrentKernelArgs This retrieves the kernel cmd line arguments 88 func (k *kernel) GetCurrentKernelArgs() (string, error) { 89 path := consts.ProcKernelCmdLine 90 if !vars.UsingSystemdMode { 91 path = filepath.Join(consts.Host, path) 92 } 93 94 path = filepath.Join(vars.FilesystemRoot, path) 95 cmdLine, err := os.ReadFile(path) 96 if err != nil { 97 return "", fmt.Errorf("GetCurrentKernelArgs(): Error reading %s: %v", path, err) 98 } 99 return string(cmdLine), nil 100 } 101 102 // IsKernelArgsSet This checks if the kernel cmd line is set properly. Please note that the same key could be repeated 103 // several times in the kernel cmd line. We can only ensure that the kernel cmd line has the key/val kernel arg that we set. 104 func (k *kernel) IsKernelArgsSet(cmdLine string, karg string) bool { 105 elements := strings.Fields(cmdLine) 106 for _, element := range elements { 107 if element == karg { 108 return true 109 } 110 } 111 return false 112 } 113 114 // Unbind unbind driver for one device 115 func (k *kernel) Unbind(pciAddr string) error { 116 log.Log.V(2).Info("Unbind(): unbind device driver for device", "device", pciAddr) 117 return k.UnbindDriverByBusAndDevice(consts.BusPci, pciAddr) 118 } 119 120 // BindDpdkDriver bind dpdk driver for one device 121 // Bind the device given by "pciAddr" to the driver "driver" 122 func (k *kernel) BindDpdkDriver(pciAddr, driver string) error { 123 log.Log.V(2).Info("BindDpdkDriver(): bind device to driver", 124 "device", pciAddr, "driver", driver) 125 if err := k.BindDriverByBusAndDevice(consts.BusPci, pciAddr, driver); err != nil { 126 _, innerErr := os.Readlink(filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "iommu_group")) 127 if innerErr != nil { 128 log.Log.Error(err, "Could not read IOMMU group for device", "device", pciAddr) 129 return fmt.Errorf( 130 "cannot bind driver %s to device %s, make sure IOMMU is enabled in BIOS. %w", driver, pciAddr, innerErr) 131 } 132 return err 133 } 134 return nil 135 } 136 137 // BindDefaultDriver bind driver for one device 138 // Bind the device given by "pciAddr" to the default driver 139 func (k *kernel) BindDefaultDriver(pciAddr string) error { 140 log.Log.V(2).Info("BindDefaultDriver(): bind device to default driver", "device", pciAddr) 141 142 curDriver, err := getDriverByBusAndDevice(consts.BusPci, pciAddr) 143 if err != nil { 144 return err 145 } 146 if curDriver != "" { 147 if !sriovnetworkv1.StringInArray(curDriver, vars.DpdkDrivers) { 148 log.Log.V(2).Info("BindDefaultDriver(): device already bound to default driver", 149 "device", pciAddr, "driver", curDriver) 150 return nil 151 } 152 if err := k.UnbindDriverByBusAndDevice(consts.BusPci, pciAddr); err != nil { 153 return err 154 } 155 } 156 if err := setDriverOverride(consts.BusPci, pciAddr, ""); err != nil { 157 return err 158 } 159 if err := probeDriver(consts.BusPci, pciAddr); err != nil { 160 return err 161 } 162 return nil 163 } 164 165 // BindDriverByBusAndDevice binds device to the provided driver 166 // bus - the bus path in the sysfs, e.g. "pci" or "vdpa" 167 // device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA 168 // driver - the name of the driver, e.g. vfio-pci or vhost_vdpa. 169 func (k *kernel) BindDriverByBusAndDevice(bus, device, driver string) error { 170 log.Log.V(2).Info("BindDriverByBusAndDevice(): bind device to driver", 171 "bus", bus, "device", device, "driver", driver) 172 173 curDriver, err := getDriverByBusAndDevice(bus, device) 174 if err != nil { 175 return err 176 } 177 if curDriver != "" { 178 if curDriver == driver { 179 log.Log.V(2).Info("BindDriverByBusAndDevice(): device already bound to driver", 180 "bus", bus, "device", device, "driver", driver) 181 return nil 182 } 183 if err := k.UnbindDriverByBusAndDevice(bus, device); err != nil { 184 return err 185 } 186 } 187 if err := setDriverOverride(bus, device, driver); err != nil { 188 return err 189 } 190 if err := bindDriver(bus, device, driver); err != nil { 191 return err 192 } 193 return setDriverOverride(bus, device, "") 194 } 195 196 // Workaround function to handle a case where the vf default driver is stuck and not able to create the vf kernel interface. 197 // This function unbind the VF from the default driver and try to bind it again 198 // bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2045087 199 func (k *kernel) RebindVfToDefaultDriver(vfAddr string) error { 200 log.Log.Info("RebindVfToDefaultDriver()", "vf", vfAddr) 201 if err := k.Unbind(vfAddr); err != nil { 202 return err 203 } 204 if err := k.BindDefaultDriver(vfAddr); err != nil { 205 log.Log.Error(err, "RebindVfToDefaultDriver(): fail to bind default driver", "device", vfAddr) 206 return err 207 } 208 209 log.Log.Info("RebindVfToDefaultDriver(): workaround implemented", "vf", vfAddr) 210 return nil 211 } 212 213 func (k *kernel) UnbindDriverIfNeeded(vfAddr string, isRdma bool) error { 214 if isRdma { 215 log.Log.Info("UnbindDriverIfNeeded(): unbinding driver", "device", vfAddr) 216 if err := k.Unbind(vfAddr); err != nil { 217 return err 218 } 219 log.Log.Info("UnbindDriverIfNeeded(): unbounded driver", "device", vfAddr) 220 } 221 return nil 222 } 223 224 // UnbindDriverByBusAndDevice unbind device identified by bus and device ID from the driver 225 // bus - the bus path in the sysfs, e.g. "pci" or "vdpa" 226 // device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA 227 func (k *kernel) UnbindDriverByBusAndDevice(bus, device string) error { 228 log.Log.V(2).Info("UnbindDriverByBusAndDevice(): unbind device driver for device", "bus", bus, "device", device) 229 driver, err := getDriverByBusAndDevice(bus, device) 230 if err != nil { 231 return err 232 } 233 if driver == "" { 234 log.Log.V(2).Info("UnbindDriverByBusAndDevice(): device has no driver", "bus", bus, "device", device) 235 return nil 236 } 237 return unbindDriver(bus, device, driver) 238 } 239 240 func (k *kernel) HasDriver(pciAddr string) (bool, string) { 241 driver, err := getDriverByBusAndDevice(consts.BusPci, pciAddr) 242 if err != nil { 243 log.Log.V(2).Info("HasDriver(): device driver is empty for device", "device", pciAddr) 244 return false, "" 245 } 246 if driver != "" { 247 log.Log.V(2).Info("HasDriver(): device driver for device", "device", pciAddr, "driver", driver) 248 return true, driver 249 } 250 return false, "" 251 } 252 253 // GetDriverByBusAndDevice returns driver for the device or error. 254 // returns "", nil if the device has no driver. 255 // bus - the bus path in the sysfs, e.g. "pci" or "vdpa" 256 // device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA 257 func (k *kernel) GetDriverByBusAndDevice(bus, device string) (string, error) { 258 log.Log.V(2).Info("GetDriverByBusAndDevice(): get driver for device", "bus", bus, "device", device) 259 return getDriverByBusAndDevice(bus, device) 260 } 261 262 func (k *kernel) TryEnableRdma() (bool, error) { 263 log.Log.V(2).Info("tryEnableRdma()") 264 chrootDefinition := utils.GetChrootExtension() 265 266 // check if the driver is already loaded in to the system 267 _, stderr, mlx4Err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep --quiet 'mlx4_en' <(%s lsmod)", chrootDefinition)) 268 if mlx4Err != nil && len(stderr) != 0 { 269 log.Log.Error(mlx4Err, "tryEnableRdma(): failed to check for kernel module 'mlx4_en'", "stderr", stderr) 270 return false, fmt.Errorf(stderr) 271 } 272 273 _, stderr, mlx5Err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep --quiet 'mlx5_core' <(%s lsmod)", chrootDefinition)) 274 if mlx5Err != nil && len(stderr) != 0 { 275 log.Log.Error(mlx5Err, "tryEnableRdma(): failed to check for kernel module 'mlx5_core'", "stderr", stderr) 276 return false, fmt.Errorf(stderr) 277 } 278 279 if mlx4Err != nil && mlx5Err != nil { 280 log.Log.Error(nil, "tryEnableRdma(): no RDMA capable devices") 281 return false, nil 282 } 283 284 isRhelSystem, err := k.IsRHELSystem() 285 if err != nil { 286 log.Log.Error(err, "tryEnableRdma(): failed to check if the machine is base on RHEL") 287 return false, err 288 } 289 290 // RHEL check 291 if isRhelSystem { 292 return k.EnableRDMAOnRHELMachine() 293 } 294 295 isUbuntuSystem, err := k.IsUbuntuSystem() 296 if err != nil { 297 log.Log.Error(err, "tryEnableRdma(): failed to check if the machine is base on Ubuntu") 298 return false, err 299 } 300 301 if isUbuntuSystem { 302 return k.EnableRDMAOnUbuntuMachine() 303 } 304 305 osName, err := k.GetOSPrettyName() 306 if err != nil { 307 log.Log.Error(err, "tryEnableRdma(): failed to check OS name") 308 return false, err 309 } 310 311 log.Log.Error(nil, "tryEnableRdma(): Unsupported OS", "name", osName) 312 return false, fmt.Errorf("unable to load RDMA unsupported OS: %s", osName) 313 } 314 315 func (k *kernel) EnableRDMAOnRHELMachine() (bool, error) { 316 log.Log.Info("EnableRDMAOnRHELMachine()") 317 isCoreOsSystem, err := k.IsCoreOS() 318 if err != nil { 319 log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if the machine runs CoreOS") 320 return false, err 321 } 322 323 // CoreOS check 324 if isCoreOsSystem { 325 isRDMALoaded, err := k.RdmaIsLoaded() 326 if err != nil { 327 log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if RDMA kernel modules are loaded") 328 return false, err 329 } 330 331 return isRDMALoaded, nil 332 } 333 334 // RHEL 335 log.Log.Info("EnableRDMAOnRHELMachine(): enabling RDMA on RHEL machine") 336 isRDMAEnable, err := k.EnableRDMA(internal.RhelRDMAConditionFile, internal.RhelRDMAServiceName, internal.RhelPackageManager) 337 if err != nil { 338 log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to enable RDMA on RHEL machine") 339 return false, err 340 } 341 342 // check if we need to install rdma-core package 343 if isRDMAEnable { 344 isRDMALoaded, err := k.RdmaIsLoaded() 345 if err != nil { 346 log.Log.Error(err, "EnableRDMAOnRHELMachine(): failed to check if RDMA kernel modules are loaded") 347 return false, err 348 } 349 350 // if ib kernel module is not loaded trigger a loading 351 if isRDMALoaded { 352 err = k.TriggerUdevEvent() 353 if err != nil { 354 log.Log.Error(err, "EnableRDMAOnRHELMachine() failed to trigger udev event") 355 return false, err 356 } 357 } 358 } 359 360 return true, nil 361 } 362 363 func (k *kernel) EnableRDMAOnUbuntuMachine() (bool, error) { 364 log.Log.Info("EnableRDMAOnUbuntuMachine(): enabling RDMA on RHEL machine") 365 isRDMAEnable, err := k.EnableRDMA(internal.UbuntuRDMAConditionFile, internal.UbuntuRDMAServiceName, internal.UbuntuPackageManager) 366 if err != nil { 367 log.Log.Error(err, "EnableRDMAOnUbuntuMachine(): failed to enable RDMA on Ubuntu machine") 368 return false, err 369 } 370 371 // check if we need to install rdma-core package 372 if isRDMAEnable { 373 isRDMALoaded, err := k.RdmaIsLoaded() 374 if err != nil { 375 log.Log.Error(err, "EnableRDMAOnUbuntuMachine(): failed to check if RDMA kernel modules are loaded") 376 return false, err 377 } 378 379 // if ib kernel module is not loaded trigger a loading 380 if isRDMALoaded { 381 err = k.TriggerUdevEvent() 382 if err != nil { 383 log.Log.Error(err, "EnableRDMAOnUbuntuMachine() failed to trigger udev event") 384 return false, err 385 } 386 } 387 } 388 389 return true, nil 390 } 391 392 func (k *kernel) IsRHELSystem() (bool, error) { 393 log.Log.Info("IsRHELSystem(): checking for RHEL machine") 394 path := internal.RedhatReleaseFile 395 if !vars.UsingSystemdMode { 396 path = filepath.Join(internal.HostPathFromDaemon, path) 397 } 398 if _, err := os.Stat(path); err != nil { 399 if os.IsNotExist(err) { 400 log.Log.V(2).Info("IsRHELSystem() not a RHEL machine") 401 return false, nil 402 } 403 404 log.Log.Error(err, "IsRHELSystem() failed to check for os release file", "path", path) 405 return false, err 406 } 407 408 return true, nil 409 } 410 411 func (k *kernel) IsCoreOS() (bool, error) { 412 log.Log.Info("IsCoreOS(): checking for CoreOS machine") 413 path := internal.RedhatReleaseFile 414 if !vars.UsingSystemdMode { 415 path = filepath.Join(internal.HostPathFromDaemon, path) 416 } 417 418 data, err := os.ReadFile(path) 419 if err != nil { 420 log.Log.Error(err, "IsCoreOS(): failed to read RHEL release file on path", "path", path) 421 return false, err 422 } 423 424 if strings.Contains(string(data), "CoreOS") { 425 return true, nil 426 } 427 428 return false, nil 429 } 430 431 func (k *kernel) IsUbuntuSystem() (bool, error) { 432 log.Log.Info("IsUbuntuSystem(): checking for Ubuntu machine") 433 path := internal.GenericOSReleaseFile 434 if !vars.UsingSystemdMode { 435 path = filepath.Join(internal.HostPathFromDaemon, path) 436 } 437 438 if _, err := os.Stat(path); err != nil { 439 if os.IsNotExist(err) { 440 log.Log.Error(nil, "IsUbuntuSystem() os-release on path doesn't exist", "path", path) 441 return false, err 442 } 443 444 log.Log.Error(err, "IsUbuntuSystem() failed to check for os release file", "path", path) 445 return false, err 446 } 447 448 stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep -i --quiet 'ubuntu' %s", path)) 449 if err != nil && len(stderr) != 0 { 450 log.Log.Error(err, "IsUbuntuSystem(): failed to check for ubuntu operating system name in os-releasae file", "stderr", stderr) 451 return false, fmt.Errorf(stderr) 452 } 453 454 if len(stdout) > 0 { 455 return true, nil 456 } 457 458 return false, nil 459 } 460 461 func (k *kernel) RdmaIsLoaded() (bool, error) { 462 log.Log.V(2).Info("RdmaIsLoaded()") 463 chrootDefinition := utils.GetChrootExtension() 464 465 // check if the driver is already loaded in to the system 466 _, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("grep --quiet '\\(^ib\\|^rdma\\)' <(%s lsmod)", chrootDefinition)) 467 if err != nil && len(stderr) != 0 { 468 log.Log.Error(err, "RdmaIsLoaded(): fail to check if ib and rdma kernel modules are loaded", "stderr", stderr) 469 return false, fmt.Errorf(stderr) 470 } 471 472 if err != nil { 473 return false, nil 474 } 475 476 return true, nil 477 } 478 479 func (k *kernel) EnableRDMA(conditionFilePath, serviceName, packageManager string) (bool, error) { 480 path := conditionFilePath 481 if !vars.UsingSystemdMode { 482 path = filepath.Join(internal.HostPathFromDaemon, path) 483 } 484 log.Log.Info("EnableRDMA(): checking for service file", "path", path) 485 486 if _, err := os.Stat(path); err != nil { 487 if os.IsNotExist(err) { 488 log.Log.V(2).Info("EnableRDMA(): RDMA server doesn't exist") 489 err = k.InstallRDMA(packageManager) 490 if err != nil { 491 log.Log.Error(err, "EnableRDMA() failed to install RDMA package") 492 return false, err 493 } 494 495 err = k.TriggerUdevEvent() 496 if err != nil { 497 log.Log.Error(err, "EnableRDMA() failed to trigger udev event") 498 return false, err 499 } 500 501 return false, nil 502 } 503 504 log.Log.Error(err, "EnableRDMA() failed to check for os release file", "path", path) 505 return false, err 506 } 507 508 log.Log.Info("EnableRDMA(): service installed", "name", serviceName) 509 return true, nil 510 } 511 512 func (k *kernel) InstallRDMA(packageManager string) error { 513 log.Log.Info("InstallRDMA(): installing RDMA") 514 chrootDefinition := utils.GetChrootExtension() 515 516 stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s %s install -y rdma-core", chrootDefinition, packageManager)) 517 if err != nil && len(stderr) != 0 { 518 log.Log.Error(err, "InstallRDMA(): failed to install RDMA package", "stdout", stdout, "stderr", stderr) 519 return err 520 } 521 522 return nil 523 } 524 525 func (k *kernel) TriggerUdevEvent() error { 526 log.Log.Info("TriggerUdevEvent(): installing RDMA") 527 528 err := k.ReloadDriver("mlx4_en") 529 if err != nil { 530 return err 531 } 532 533 err = k.ReloadDriver("mlx5_core") 534 if err != nil { 535 return err 536 } 537 538 return nil 539 } 540 541 func (k *kernel) ReloadDriver(driverName string) error { 542 log.Log.Info("ReloadDriver(): reload driver", "name", driverName) 543 chrootDefinition := utils.GetChrootExtension() 544 545 _, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("%s modprobe -r %s && %s modprobe %s", chrootDefinition, driverName, chrootDefinition, driverName)) 546 if err != nil && len(stderr) != 0 { 547 log.Log.Error(err, "ReloadDriver(): failed to reload kernel module", 548 "name", driverName, "stderr", stderr) 549 return err 550 } 551 552 return nil 553 } 554 555 func (k *kernel) GetOSPrettyName() (string, error) { 556 path := internal.GenericOSReleaseFile 557 if !vars.UsingSystemdMode { 558 path = filepath.Join(internal.HostPathFromDaemon, path) 559 } 560 561 log.Log.Info("GetOSPrettyName(): getting os name from os-release file") 562 563 stdout, stderr, err := k.utilsHelper.RunCommand("/bin/sh", "-c", fmt.Sprintf("cat %s | grep PRETTY_NAME | cut -c 13-", path)) 564 if err != nil && len(stderr) != 0 { 565 log.Log.Error(err, "GetOSPrettyName(): failed to check for operating system name in os-release file", "stderr", stderr) 566 return "", fmt.Errorf(stderr) 567 } 568 569 if len(stdout) > 0 { 570 return stdout, nil 571 } 572 573 return "", fmt.Errorf("failed to find pretty operating system name") 574 } 575 576 // IsKernelLockdownMode returns true when kernel lockdown mode is enabled 577 // TODO: change this to return error 578 func (k *kernel) IsKernelLockdownMode() bool { 579 path := utils.GetHostExtension() 580 path = filepath.Join(path, "/sys/kernel/security/lockdown") 581 582 stdout, stderr, err := k.utilsHelper.RunCommand("cat", path) 583 log.Log.V(2).Info("IsKernelLockdownMode()", "output", stdout, "error", err) 584 if err != nil { 585 log.Log.Error(err, "IsKernelLockdownMode(): failed to check for lockdown file", "stderr", stderr) 586 return false 587 } 588 return strings.Contains(stdout, "[integrity]") || strings.Contains(stdout, "[confidentiality]") 589 } 590 591 // returns driver for device on the bus 592 func getDriverByBusAndDevice(bus, device string) (string, error) { 593 driverLink := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "devices", device, "driver") 594 driverInfo, err := os.Readlink(driverLink) 595 if err != nil { 596 if errors.Is(err, os.ErrNotExist) { 597 log.Log.V(2).Info("getDriverByBusAndDevice(): driver path for device not exist", "bus", bus, "device", device, "driver", driverInfo) 598 return "", nil 599 } 600 log.Log.Error(err, "getDriverByBusAndDevice(): error getting driver info for device", "bus", bus, "device", device) 601 return "", err 602 } 603 log.Log.V(2).Info("getDriverByBusAndDevice(): driver for device", "bus", bus, "device", device, "driver", driverInfo) 604 return filepath.Base(driverInfo), nil 605 } 606 607 // binds device to the provide driver 608 func bindDriver(bus, device, driver string) error { 609 log.Log.V(2).Info("bindDriver(): bind to driver", "bus", bus, "device", device, "driver", driver) 610 bindPath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers", driver, "bind") 611 err := os.WriteFile(bindPath, []byte(device), os.ModeAppend) 612 if err != nil { 613 log.Log.Error(err, "bindDriver(): failed to bind driver", "bus", bus, "device", device, "driver", driver) 614 return err 615 } 616 return nil 617 } 618 619 // unbind device from the driver 620 func unbindDriver(bus, device, driver string) error { 621 log.Log.V(2).Info("unbindDriver(): unbind from driver", "bus", bus, "device", device, "driver", driver) 622 unbindPath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers", driver, "unbind") 623 err := os.WriteFile(unbindPath, []byte(device), os.ModeAppend) 624 if err != nil { 625 log.Log.Error(err, "unbindDriver(): failed to unbind driver", "bus", bus, "device", device, "driver", driver) 626 return err 627 } 628 return nil 629 } 630 631 // probes driver for device on the bus 632 func probeDriver(bus, device string) error { 633 log.Log.V(2).Info("probeDriver(): drivers probe", "bus", bus, "device", device) 634 probePath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers_probe") 635 err := os.WriteFile(probePath, []byte(device), os.ModeAppend) 636 if err != nil { 637 log.Log.Error(err, "probeDriver(): failed to trigger driver probe", "bus", bus, "device", device) 638 return err 639 } 640 return nil 641 } 642 643 // set driver override for the bus/device, 644 // resets override if override arg is "", 645 // if device doesn't support overriding (has no driver_override path), does nothing 646 func setDriverOverride(bus, device, override string) error { 647 driverOverridePath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "devices", device, "driver_override") 648 if _, err := os.Stat(driverOverridePath); err != nil { 649 if os.IsNotExist(err) { 650 log.Log.V(2).Info("setDriverOverride(): device doesn't support driver override, skip", "bus", bus, "device", device) 651 return nil 652 } 653 return err 654 } 655 var overrideData []byte 656 if override != "" { 657 log.Log.V(2).Info("setDriverOverride(): configure driver override for device", "bus", bus, "device", device, "driver", override) 658 overrideData = []byte(override) 659 } else { 660 log.Log.V(2).Info("setDriverOverride(): reset driver override for device", "bus", bus, "device", device) 661 overrideData = []byte("\x00") 662 } 663 err := os.WriteFile(driverOverridePath, overrideData, os.ModeAppend) 664 if err != nil { 665 log.Log.Error(err, "setDriverOverride(): fail to write driver_override for device", 666 "bus", bus, "device", device, "driver", override) 667 return err 668 } 669 return nil 670 }