Skip to content

Commit e6f3599

Browse files
Tobi Lehmantlehman
Tobi Lehman
authored andcommitted
Add custom PCI DevicePlugin
> Co-authored-by: Gaurav Mehta <[email protected]>
1 parent 2fc0f8b commit e6f3599

File tree

12 files changed

+1035
-226
lines changed

12 files changed

+1035
-226
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
pcidevices
33
bin/
44
dist/
5-
.idea
5+
.idea
6+
TAGS

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
TARGETS := $(shell ls scripts)
22

3+
tags:
4+
gotags -R pkg > TAGS
5+
36
.dapper:
47
@echo Downloading dapper
58
@curl -sL https://releases.rancher.com/dapper/latest/dapper-`uname -s`-`uname -m` > .dapper.tmp

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ This operator introduces these CRDs:
1414
- PCIDevice
1515
- PCIDeviceClaim
1616

17+
It also introduces a custom PCIDevicePlugin. The way the deviceplugin works is by storing all
18+
PCIDevices with the same resourceName. Then when one is claimed, the deviceplugin marks that device state as "healthy".
19+
1720
## PCIDevice
1821

1922
This custom resource represents PCI Devices on the host.
@@ -131,7 +134,7 @@ This only detects the presence or absence of device, not the number of them.
131134
Another reason not to use these simple labels is that we want to be able to allow our customers to set custom RBAC rules that restrict who can use which device in the cluster. We can do that with a custom `PCIDevice` CRD, but it's not clear how to do that with node labels.
132135

133136
## License
134-
Copyright (c) 2022 [Rancher Labs, Inc.](http://rancher.com)
137+
Copyright (c) 2023 [Rancher Labs, Inc.](http://rancher.com)
135138

136139
Licensed under the Apache License, Version 2.0 (the "License");
137140
you may not use this file except in compliance with the License.
@@ -143,4 +146,4 @@ Unless required by applicable law or agreed to in writing, software
143146
distributed under the License is distributed on an "AS IS" BASIS,
144147
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
145148
See the License for the specific language governing permissions and
146-
limitations under the License.
149+
limitations under the License.

go.mod

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.18
44

55
require (
66
github.com/evanphx/json-patch v5.6.0+incompatible
7+
github.com/fsnotify/fsnotify v1.5.1
78
github.com/gorilla/mux v1.8.0
89
github.com/harvester/harvester v0.0.2-0.20220916012220-3bcba9d3747f
910
github.com/jaypipes/ghw v0.9.0
@@ -18,6 +19,7 @@ require (
1819
github.com/u-root/u-root v7.0.0+incompatible
1920
github.com/urfave/cli/v2 v2.11.1
2021
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
22+
google.golang.org/grpc v1.47.0
2123
k8s.io/api v0.24.3
2224
k8s.io/apimachinery v0.24.3
2325
k8s.io/client-go v12.0.0+incompatible
@@ -29,7 +31,6 @@ require (
2931

3032
require (
3133
github.com/coreos/prometheus-operator v0.38.1-0.20200424145508-7e176fda06cc // indirect
32-
github.com/fsnotify/fsnotify v1.5.1 // indirect
3334
github.com/go-kit/kit v0.9.0 // indirect
3435
github.com/go-logfmt/logfmt v0.5.0 // indirect
3536
github.com/go-ole/go-ole v1.2.6 // indirect
@@ -44,7 +45,6 @@ require (
4445
github.com/openshift/client-go v0.0.0 // indirect
4546
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
4647
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
47-
google.golang.org/grpc v1.47.0 // indirect
4848
howett.net/plist v1.0.0 // indirect
4949
)
5050

@@ -54,7 +54,7 @@ require (
5454
github.com/cespare/xxhash/v2 v2.1.2 // indirect
5555
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
5656
github.com/davecgh/go-spew v1.1.1 // indirect
57-
github.com/emicklei/go-restful v2.15.0+incompatible // indirect
57+
github.com/emicklei/go-restful v2.16.0+incompatible // indirect
5858
github.com/ghodss/yaml v1.0.0 // indirect
5959
github.com/go-logr/logr v1.2.3 // indirect
6060
github.com/go-logr/zapr v1.2.3 // indirect
@@ -92,9 +92,9 @@ require (
9292
go.uber.org/atomic v1.7.0 // indirect
9393
go.uber.org/multierr v1.6.0 // indirect
9494
go.uber.org/zap v1.19.1 // indirect
95-
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
95+
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
9696
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
97-
golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced // indirect
97+
golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced
9898
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 // indirect
9999
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
100100
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect
@@ -116,7 +116,7 @@ require (
116116
k8s.io/kubernetes v1.25.1
117117
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
118118
kubevirt.io/api v0.54.0
119-
kubevirt.io/containerized-data-importer-api v1.47.0 // indirect
119+
kubevirt.io/containerized-data-importer-api v1.50.0 // indirect
120120
kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect
121121
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
122122
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -932,8 +932,8 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh
932932
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
933933
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
934934
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
935-
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
936-
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
935+
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
936+
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
937937
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
938938
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
939939
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=

manifests/pcidevices-operator.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,19 @@ spec:
9090
name: modules
9191
- mountPath: /sys
9292
name: sys
93+
- mountPath: /var/lib/kubelet/device-plugins
94+
name: device-plugins
9395
priorityClassName: system-node-critical
9496
serviceAccountName: pcidevices
9597
volumes:
9698
- hostPath:
9799
path: /lib/modules
98100
type: Directory
99101
name: modules
102+
- hostPath:
103+
path: /var/lib/kubelet/device-plugins
104+
type: Directory
105+
name: device-plugins
100106
- hostPath:
101107
path: /sys
102108
type: Directory

pkg/apis/devices.harvesterhci.io/v1beta1/pcidevice.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@ package v1beta1
22

33
import (
44
"fmt"
5+
"strconv"
56
"strings"
67

78
"regexp"
89

910
"github.com/jaypipes/ghw/pkg/pci"
1011
"github.com/jaypipes/ghw/pkg/util"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12-
device_manager "kubevirt.io/kubevirt/pkg/virt-handler/device-manager"
1313
)
1414

15-
const pciBasePath = "/sys/bus/pci/devices/"
16-
1715
// +genclient
1816
// +genclient:nonNamespaced
1917
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -112,16 +110,17 @@ func resourceName(dev *pci.Device) string {
112110
return fmt.Sprintf("%s/%s", vendorCleaned, dev.Product.ID)
113111
}
114112

115-
func (status *PCIDeviceStatus) Update(dev *pci.Device, hostname string) {
113+
func (status *PCIDeviceStatus) Update(dev *pci.Device, hostname string, iommuGroups map[string]int) {
116114
status.Address = dev.Address
117115
status.VendorId = dev.Vendor.ID
118116
status.DeviceId = dev.Product.ID
119117
status.ClassId = fmt.Sprintf("%s%s", dev.Class.ID, dev.Subclass.ID)
120118
// Generate the ResourceName field, this is used by KubeVirt to schedule the VM to the node
121119
status.ResourceName = resourceName(dev)
122120
status.Description = description(dev)
123-
if iommuGroup, err := device_manager.Handler.GetDeviceIOMMUGroup(pciBasePath, dev.Address); err == nil {
124-
status.IOMMUGroup = iommuGroup
121+
group, ok := iommuGroups[dev.Address]
122+
if ok {
123+
status.IOMMUGroup = strconv.Itoa(group)
125124
}
126125
status.KernelDriverInUse = dev.Driver
127126
status.NodeName = hostname

pkg/controller/pcidevice/pcidevice_controller.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os"
77
"time"
88

9+
"github.com/harvester/pcidevices/pkg/iommu"
10+
911
v1beta1 "github.com/harvester/pcidevices/pkg/apis/devices.harvesterhci.io/v1beta1"
1012
ctl "github.com/harvester/pcidevices/pkg/generated/controllers/devices.harvesterhci.io/v1beta1"
1113
"github.com/harvester/pcidevices/pkg/util/nichelper"
@@ -60,7 +62,12 @@ func Register(
6062
}
6163

6264
func (h Handler) reconcilePCIDevices(nodename string) error {
63-
// List all PCI Devices on host
65+
// Build up the IOMMU group map
66+
iommuGroupPaths, err := iommu.GroupPaths()
67+
if err != nil {
68+
return err
69+
}
70+
iommuGroupMap := iommu.GroupMapForPCIDevices(iommuGroupPaths)
6471

6572
commonLabels := map[string]string{"nodename": nodename} // label
6673
var setOfRealPCIAddrs map[string]bool = make(map[string]bool)
@@ -69,9 +76,7 @@ func (h Handler) reconcilePCIDevices(nodename string) error {
6976
setOfRealPCIAddrs[dev.Address] = true
7077
name := v1beta1.PCIDeviceNameForHostname(dev, nodename)
7178
// Check if device is stored
72-
var err error
73-
var devCR *v1beta1.PCIDevice
74-
devCR, err = h.client.Get(name, metav1.GetOptions{})
79+
devCR, err := h.client.Get(name, metav1.GetOptions{})
7580

7681
if err != nil {
7782
if apierrors.IsNotFound(err) {
@@ -95,13 +100,14 @@ func (h Handler) reconcilePCIDevices(nodename string) error {
95100

96101
devCopy := devCR.DeepCopy()
97102
// Update only modifies the status, no need to update the main object
98-
devCopy.Status.Update(dev, nodename) // update the in-memory CR with the current PCI info
103+
devCopy.Status.Update(dev, nodename, iommuGroupMap) // update the in-memory CR with the current PCI info
99104
_, err = h.client.UpdateStatus(devCopy)
100105
if err != nil {
101106
logrus.Errorf("[PCIDeviceController] Failed to update status sub-resource: %v", err)
102107
return err
103108
}
104109
}
110+
105111
}
106112

107113
// remove non-existent devices

0 commit comments

Comments
 (0)