Skip to content

Commit bbc8d79

Browse files
committed
gpu: copy nfdhook functionality to gpu-plugin
In order to support v0.14+ NFD and extended resources, write node labels into a file and store it for NFD's features.d directory. The old functionality still exists in the initcontainer, but if it's used NFD has to be configured to allow it. Signed-off-by: Tuomas Katila <[email protected]>
1 parent 4c58a78 commit bbc8d79

19 files changed

+429
-72
lines changed

cmd/gpu_nfdhook/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Table of Contents
1111

1212
## Introduction
1313

14+
***NOTE:*** NFD's binary hook support will be turned off by default in the 0.14 release. If one desires to use it then, NFD's default configuration has to be modified to allow binary hooks. Due to the change, GPU binary hook's functionality has been moved to GPU plugin.
15+
1416
This is the [Node Feature Discovery](https://github.com/kubernetes-sigs/node-feature-discovery)
1517
binary hook implementation for the Intel GPUs. The intel-gpu-initcontainer (which
1618
is built with the other images) can be used as part of the gpu-plugin deployment

cmd/gpu_nfdhook/main.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
package main
1616

1717
import (
18-
"os"
19-
20-
"k8s.io/klog/v2"
18+
l "github.com/intel/intel-device-plugins-for-kubernetes/cmd/internal/labeler"
2119
)
2220

2321
const (
@@ -27,13 +25,5 @@ const (
2725
)
2826

2927
func main() {
30-
l := newLabeler(sysfsDRMDirectory, debugfsDRIDirectory)
31-
32-
err := l.createLabels()
33-
if err != nil {
34-
klog.Errorf("%+v", err)
35-
os.Exit(1)
36-
}
37-
38-
l.printLabels()
28+
l.CreateAndPrintLabels(sysfsDRMDirectory, debugfsDRIDirectory)
3929
}

cmd/gpu_plugin/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ backend libraries can offload compute operations to GPU.
5252
| -resource-manager | - | disabled | Enable fractional resource management, [see also dependencies](#fractional-resources) |
5353
| -shared-dev-num | int | 1 | Number of containers that can share the same GPU device |
5454
| -allocation-policy | string | none | 3 possible values: balanced, packed, none. For shared-dev-num > 1: _balanced_ mode spreads workloads among GPU devices, _packed_ mode fills one GPU fully before moving to next, and _none_ selects first available device from kubelet. Default is _none_. Allocation policy does not have an effect when resource manager is enabled. |
55+
| -export-resource-lables | bool | true | Export resource labels to NFD via features.d directory. Replaces initcontainer's functionality. |
5556

5657
The plugin also accepts a number of other arguments (common to all plugins) related to logging.
5758
Please use the -h option to see the complete list of logging related options.

cmd/gpu_plugin/gpu_plugin.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017-2022 Intel Corporation. All Rights Reserved.
1+
// Copyright 2017-2023 Intel Corporation. All Rights Reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import (
3131
pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1"
3232

3333
"github.com/intel/intel-device-plugins-for-kubernetes/cmd/gpu_plugin/rm"
34+
l "github.com/intel/intel-device-plugins-for-kubernetes/cmd/internal/labeler"
3435
"github.com/intel/intel-device-plugins-for-kubernetes/cmd/internal/pluginutils"
3536
dpapi "github.com/intel/intel-device-plugins-for-kubernetes/pkg/deviceplugin"
3637
)
@@ -53,13 +54,17 @@ const (
5354

5455
// Period of device scans.
5556
scanPeriod = 5 * time.Second
57+
58+
// Labeler's max update interval, 5min.
59+
labelerMaxInterval = 5 * 60 * time.Second
5660
)
5761

5862
type cliOptions struct {
5963
preferredAllocationPolicy string
6064
sharedDevNum int
6165
enableMonitoring bool
6266
resourceManagement bool
67+
exportResourceLabels bool
6368
}
6469

6570
type preferredAllocationPolicyFunc func(*pluginapi.ContainerPreferredAllocationRequest) []string
@@ -242,8 +247,9 @@ type devicePlugin struct {
242247
controlDeviceReg *regexp.Regexp
243248
pciAddressReg *regexp.Regexp
244249

245-
scanTicker *time.Ticker
246-
scanDone chan bool
250+
scanTicker *time.Ticker
251+
scanDone chan bool
252+
scanResources chan bool
247253

248254
resMan rm.ResourceManager
249255

@@ -270,6 +276,7 @@ func newDevicePlugin(sysfsDir, devfsDir string, options cliOptions) *devicePlugi
270276
scanTicker: time.NewTicker(scanPeriod),
271277
scanDone: make(chan bool, 1), // buffered as we may send to it before Scan starts receiving from it
272278
bypathFound: true,
279+
scanResources: make(chan bool, 1),
273280
}
274281

275282
if options.resourceManagement {
@@ -347,17 +354,26 @@ func (dp *devicePlugin) Scan(notifier dpapi.Notifier) error {
347354
klog.Warning("Failed to scan: ", err)
348355
}
349356

357+
countChanged := false
358+
350359
for name, prev := range previousCount {
351360
count := devTree.DeviceTypeCount(name)
352361
if count != prev {
353362
klog.V(1).Infof("GPU scan update: %d->%d '%s' resources found", prev, count, name)
354363

355364
previousCount[name] = count
365+
366+
countChanged = true
356367
}
357368
}
358369

359370
notifier.Notify(devTree)
360371

372+
// Trigger resource scan if it's enabled.
373+
if dp.options.exportResourceLabels && countChanged {
374+
dp.scanResources <- true
375+
}
376+
361377
select {
362378
case <-dp.scanDone:
363379
return nil
@@ -494,6 +510,7 @@ func main() {
494510
flag.BoolVar(&opts.resourceManagement, "resource-manager", false, "fractional GPU resource management")
495511
flag.IntVar(&opts.sharedDevNum, "shared-dev-num", 1, "number of containers sharing the same GPU device")
496512
flag.StringVar(&opts.preferredAllocationPolicy, "allocation-policy", "none", "modes of allocating GPU devices: balanced, packed and none")
513+
flag.BoolVar(&opts.exportResourceLabels, "export-resource-labels", true, "export labels from node's GPUs and store them for NFD")
497514
flag.Parse()
498515

499516
if opts.sharedDevNum < 1 {
@@ -515,6 +532,12 @@ func main() {
515532
klog.V(1).Infof("GPU device plugin started with %s preferred allocation policy", opts.preferredAllocationPolicy)
516533

517534
plugin := newDevicePlugin(prefix+sysfsDrmDirectory, prefix+devfsDriDirectory, opts)
535+
536+
if opts.exportResourceLabels {
537+
// Start labeler to export labels file for NFD.
538+
go l.Run(prefix, labelerMaxInterval, plugin.scanResources)
539+
}
540+
518541
manager := dpapi.NewManager(namespace, plugin)
519542
manager.Run()
520543
}

cmd/gpu_plugin/gpu_plugin_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017-2021 Intel Corporation. All Rights Reserved.
1+
// Copyright 2017-2023 Intel Corporation. All Rights Reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.

cmd/gpu_plugin/render-device.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/sh
22
#
3-
# Copyright 2021 Intel Corporation.
3+
# Copyright 2021-2023 Intel Corporation.
44
#
55
# SPDX-License-Identifier: Apache-2.0
66
#

cmd/gpu_plugin/rm/gpu_plugin_resource_manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2021 Intel Corporation. All Rights Reserved.
1+
// Copyright 2021-2023 Intel Corporation. All Rights Reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.

cmd/gpu_plugin/rm/gpu_plugin_resource_manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2021 Intel Corporation. All Rights Reserved.
1+
// Copyright 2021-2023 Intel Corporation. All Rights Reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.

cmd/gpu_nfdhook/labeler.go renamed to cmd/internal/labeler/labeler.go

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2020-2021 Intel Corporation. All Rights Reserved.
1+
// Copyright 2020-2023 Intel Corporation. All Rights Reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,18 +12,22 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package main
15+
package labeler
1616

1717
import (
1818
"bufio"
1919
"fmt"
2020
"os"
21+
"os/signal"
2122
"path"
2223
"path/filepath"
24+
"reflect"
2325
"regexp"
2426
"sort"
2527
"strconv"
2628
"strings"
29+
"syscall"
30+
"time"
2731

2832
"github.com/intel/intel-device-plugins-for-kubernetes/cmd/internal/pluginutils"
2933
"github.com/pkg/errors"
@@ -47,6 +51,10 @@ const (
4751
vendorString = "0x8086"
4852
labelMaxLength = 63
4953
labelControlChar = "Z"
54+
55+
sysfsDRMDirectory = "/sys/class/drm"
56+
debugfsDRIDirectory = "/sys/kernel/debug/dri"
57+
nfdFeatureFile = "/etc/kubernetes/node-feature-discovery/features.d/intel-i915-resources.txt"
5058
)
5159

5260
type labelMap map[string]string
@@ -58,6 +66,7 @@ type labeler struct {
5866

5967
sysfsDRMDir string
6068
debugfsDRIDir string
69+
labelsChanged bool
6170
}
6271

6372
func newLabeler(sysfsDRMDir, debugfsDRIDir string) *labeler {
@@ -67,6 +76,7 @@ func newLabeler(sysfsDRMDir, debugfsDRIDir string) *labeler {
6776
gpuDeviceReg: regexp.MustCompile(gpuDeviceRE),
6877
controlDeviceReg: regexp.MustCompile(controlDeviceRE),
6978
labels: labelMap{},
79+
labelsChanged: true,
7080
}
7181
}
7282

@@ -345,6 +355,10 @@ func (l *labeler) createPCIGroupLabel(gpuNumList []string) string {
345355

346356
// createLabels is the main function of plugin labeler, it creates label-value pairs for the gpus.
347357
func (l *labeler) createLabels() error {
358+
prevLabels := l.labels
359+
360+
l.labels = labelMap{}
361+
348362
gpuNameList, err := l.scan()
349363
if err != nil {
350364
return err
@@ -431,6 +445,8 @@ func (l *labeler) createLabels() error {
431445
}
432446
}
433447

448+
l.labelsChanged = !reflect.DeepEqual(prevLabels, l.labels)
449+
434450
return nil
435451
}
436452

@@ -455,8 +471,97 @@ func createNumaNodeMappingLabel(mapping map[int][]string) string {
455471
return strings.Join(parts, "_")
456472
}
457473

458-
func (l *labeler) printLabels() {
474+
func (l *labeler) printLabelsToFile(labelFile string) error {
475+
f, err := os.OpenFile(labelFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
476+
if err != nil {
477+
klog.Warning("Failed to open file:", err)
478+
479+
return err
480+
}
481+
482+
for key, val := range l.labels {
483+
if _, err := f.WriteString(key + "=" + val + "\n"); err != nil {
484+
klog.Warning("Failed to write label to file:", err)
485+
486+
return err
487+
}
488+
}
489+
490+
return nil
491+
}
492+
493+
func CreateAndPrintLabels(sysfsDRMDir, debugfsDRIDir string) {
494+
l := newLabeler(sysfsDRMDir, debugfsDRIDir)
495+
496+
if err := l.createLabels(); err != nil {
497+
klog.Warningf("failed to create labels: %+v", err)
498+
499+
return
500+
}
501+
459502
for key, val := range l.labels {
460503
fmt.Println(key + "=" + val)
461504
}
462505
}
506+
507+
// Gathers node's GPU labels on channel trigger or timeout, and write them to a file.
508+
// The created label file is deleted on exit (process dying).
509+
func Run(pathPrefix string, updateInterval time.Duration, scanResources chan bool) {
510+
l := newLabeler(pathPrefix+sysfsDRMDirectory, pathPrefix+debugfsDRIDirectory)
511+
512+
interruptChan := make(chan os.Signal, 1)
513+
signal.Notify(interruptChan, os.Interrupt)
514+
signal.Notify(interruptChan, syscall.SIGTERM)
515+
516+
klog.V(1).Info("Starting GPU labeler")
517+
518+
running := true
519+
520+
for running {
521+
timeout := time.After(updateInterval)
522+
523+
select {
524+
case <-timeout:
525+
case <-scanResources:
526+
case <-interruptChan:
527+
klog.V(2).Info("Interrupt received")
528+
529+
running = false
530+
531+
continue
532+
}
533+
534+
klog.V(1).Info("Ext resources scanning")
535+
536+
err := l.createLabels()
537+
if err != nil {
538+
klog.Warningf("label creation failed: %+v", err)
539+
540+
continue
541+
}
542+
543+
if l.labelsChanged {
544+
klog.V(1).Info("Writing labels")
545+
546+
if err := l.printLabelsToFile(nfdFeatureFile); err != nil {
547+
klog.Warningf("failed to write labels to file: %+v", err)
548+
}
549+
}
550+
}
551+
552+
signal.Stop(interruptChan)
553+
554+
klog.V(2).Info("Removing label file")
555+
556+
err := os.Remove(nfdFeatureFile)
557+
if err != nil {
558+
klog.Errorf("Failed to cleanup label file: %+v", err)
559+
}
560+
561+
klog.V(1).Info("Stopping GPU labeler")
562+
563+
// Trigger sigterm to cause the app to exit without delays
564+
if err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM); err != nil {
565+
klog.Errorf("Failed to re-send sigterm: %+v", err)
566+
}
567+
}

cmd/gpu_nfdhook/labeler_test.go renamed to cmd/internal/labeler/labeler_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package main
15+
package labeler
1616

1717
import (
1818
"os"
@@ -24,6 +24,10 @@ import (
2424
"github.com/intel/intel-device-plugins-for-kubernetes/cmd/internal/pluginutils"
2525
)
2626

27+
const (
28+
sysfsDirectory = "/sys/"
29+
)
30+
2731
type testcase struct {
2832
capabilityFile map[string][]byte
2933
expectedRetval error

0 commit comments

Comments
 (0)