From 74d6993e9bc3e1bce5b6af799decf8ae9e37a6c0 Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Thu, 20 Sep 2018 15:49:45 +0300 Subject: [PATCH] Implement PCI feature source This feature source detects the presence of PCI devices. At the moment, it only advertises GPUs and accelerator cards, i.e. device classes 0x03, 0x0b40 and 0x12. The label format is: node.alpha.kubernetes-incubator.io/nfd-pci-.present where is composed of raw PCI IDs: _ --- README.md | 18 +++++++- main.go | 4 +- main_test.go | 4 +- source/pci/pci.go | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 source/pci/pci.go diff --git a/README.md b/README.md index 04f8b5bc3..987ed4d06 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ node-feature-discovery. will override settings read from the config file. [Default: ] --sources= Comma separated list of feature sources. - [Default: cpuid,iommu,memory,network,pstate,rdt,selinux,storage] + [Default: cpuid,iommu,memory,network,pci,pstate,rdt,selinux,storage] --no-publish Do not publish discovered features to the cluster-local Kubernetes API server. --label-whitelist= Regular expression to filter label names to @@ -115,6 +115,7 @@ the only label value published for features is the string `"true"`._ "node.alpha.kubernetes-incubator.io/nfd-iommu-": "true", "node.alpha.kubernetes-incubator.io/nfd-memory-": "true", "node.alpha.kubernetes-incubator.io/nfd-network-": "true", + "node.alpha.kubernetes-incubator.io/nfd-pci-.present": "true", "node.alpha.kubernetes-incubator.io/nfd-pstate-": "true", "node.alpha.kubernetes-incubator.io/nfd-rdt-": "true", "node.alpha.kubernetes-incubator.io/nfd-selinux-": "true", @@ -176,6 +177,21 @@ such as restricting discovered features with the --label-whitelist option._ | sriov | capable | [Single Root Input/Output Virtualization][sriov] (SR-IOV) enabled Network Interface Card(s) present |
| configured | SR-IOV virtual functions have been configured +### PCI Features + +| Feature | Attribute | Description | +| -------------------- | --------- | ----------------------------------------- | +| <device label> | present | PCI device is detected + +The `` part is composed of raw PCI IDs, and has the following +format: `_`. E.g. +``` +node.alpha.kubernetes-incubator.io/nfd-pci-1200_8086.present=true +``` + +Only device classes (0x)03, (0x)0b40 and (0x)12, i.e. GPUs, co-processors and +accelerator cards are deteted. + ### RDT (Intel Resource Director Technology) Features | Feature name | Description | diff --git a/main.go b/main.go index c893f7f83..174ce97ec 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,7 @@ import ( "github.com/kubernetes-incubator/node-feature-discovery/source/memory" "github.com/kubernetes-incubator/node-feature-discovery/source/network" "github.com/kubernetes-incubator/node-feature-discovery/source/panic_fake" + "github.com/kubernetes-incubator/node-feature-discovery/source/pci" "github.com/kubernetes-incubator/node-feature-discovery/source/pstate" "github.com/kubernetes-incubator/node-feature-discovery/source/rdt" "github.com/kubernetes-incubator/node-feature-discovery/source/selinux" @@ -163,7 +164,7 @@ func argsParse(argv []string) (args Args) { will override settings read from the config file. [Default: ] --sources= Comma separated list of feature sources. - [Default: cpuid,iommu,memory,network,pstate,rdt,selinux,storage] + [Default: cpuid,iommu,memory,network,pci,pstate,rdt,selinux,storage] --no-publish Do not publish discovered features to the cluster-local Kubernetes API server. --label-whitelist= Regular expression to filter label names to @@ -242,6 +243,7 @@ func configureParameters(sourcesWhiteList []string, labelWhiteListStr string) (e memory.Source{}, network.Source{}, panic_fake.Source{}, + pci.Source{}, pstate.Source{}, rdt.Source{}, selinux.Source{}, diff --git a/main_test.go b/main_test.go index 2fb3f83a1..4cd82dd08 100644 --- a/main_test.go +++ b/main_test.go @@ -137,7 +137,7 @@ func TestArgsParse(t *testing.T) { So(args.sleepInterval, ShouldEqual, 60*time.Second) So(args.noPublish, ShouldBeTrue) So(args.oneshot, ShouldBeTrue) - So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pstate", "rdt", "selinux", "storage"}) + So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pci", "pstate", "rdt", "selinux", "storage"}) So(len(args.labelWhiteList), ShouldEqual, 0) }) }) @@ -159,7 +159,7 @@ func TestArgsParse(t *testing.T) { Convey("args.labelWhiteList is set to appropriate value and args.sources is set to default value", func() { So(args.noPublish, ShouldBeFalse) - So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pstate", "rdt", "selinux", "storage"}) + So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pci", "pstate", "rdt", "selinux", "storage"}) So(args.labelWhiteList, ShouldResemble, ".*rdt.*") }) }) diff --git a/source/pci/pci.go b/source/pci/pci.go new file mode 100644 index 000000000..aaa72efae --- /dev/null +++ b/source/pci/pci.go @@ -0,0 +1,111 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pci + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path" + "strings" +) + +type pciDeviceInfo map[string]string + +var logger = log.New(os.Stderr, "", log.LstdFlags) + +var deviceClassWhitelist = []string{"03", "0b40", "12"} + +// Implement FeatureSource interface +type Source struct{} + +// Return name of the feature source +func (s Source) Name() string { return "pci" } + +// Discover features +func (s Source) Discover() ([]string, error) { + features := map[string]bool{} + + devs, err := detectPci() + if err != nil { + return nil, fmt.Errorf("Failed to detect PCI devices: %s", err.Error()) + } + + for class, classDevs := range devs { + for _, white := range deviceClassWhitelist { + if strings.HasPrefix(class, white) { + for _, dev := range classDevs { + features[fmt.Sprintf("%s_%s.present", dev["class"], dev["vendor"])] = true + } + } + } + } + + feature_list := []string{} + for feature := range features { + feature_list = append(feature_list, feature) + } + + return feature_list, nil +} + +// Read information of one PCI device +func readDevInfo(devPath string) (pciDeviceInfo, error) { + info := pciDeviceInfo{} + + attrs := []string{"class", "vendor", "device", "subsystem_vendor", "subsystem_device"} + for _, attr := range attrs { + data, err := ioutil.ReadFile(path.Join(devPath, attr)) + if err != nil { + return info, fmt.Errorf("Failed to read device %s: %s", attr, err) + } + // Strip whitespace and '0x' prefix + info[attr] = strings.TrimSpace(strings.TrimPrefix(string(data), "0x")) + + if attr == "class" && len(info[attr]) > 4 { + // Take four first characters, so that the programming + // interface identifier gets stripped from the raw class code + info[attr] = info[attr][0:4] + } + } + return info, nil +} + +// List available PCI devices +func detectPci() (map[string][]pciDeviceInfo, error) { + const basePath = "/sys/bus/pci/devices/" + devInfo := make(map[string][]pciDeviceInfo) + + devices, err := ioutil.ReadDir(basePath) + if err != nil { + return nil, err + } + + // Iterate over devices + for _, device := range devices { + info, err := readDevInfo(path.Join(basePath, device.Name())) + if err != nil { + log.Print(err) + continue + } + class := info["class"] + devInfo[class] = append(devInfo[class], info) + } + + return devInfo, nil +}