Skip to content

Commit 73fbca2

Browse files
authored
Merge pull request #659 from almaslennikov/guid-config
Configure IB VFs' GUIDs using a statically provided GUID pool
2 parents 4dd8dce + f199eb9 commit 73fbca2

25 files changed

+1116
-98
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Infiniband VF GUID Static Configuration
2+
3+
SR-IOV Network Operator is able to use a static configuration file from the host filesystem to assign GUIDs to IB VFs.
4+
5+
## Prerequisites
6+
7+
- Infiniband NICs
8+
- Static GUID configuration file on the host filesystem
9+
10+
### Configuration file
11+
12+
Config file should be stored at `/etc/sriov-operator/infiniband/guids`. This location is writable across most cloud platforms.
13+
14+
VF to GUID assignment, based on this file, is ordered. VF0 takes the GUID0, VF1 takes the GUID1 etc.
15+
16+
Each PF has its own set of GUIDs.
17+
18+
Example of the config file:
19+
20+
```json
21+
[
22+
{
23+
"pci_address": "<pci_address_1>",
24+
"guids": [
25+
"02:00:00:00:00:00:00:00",
26+
"02:00:00:00:00:00:00:01"
27+
]
28+
},
29+
{
30+
"pf_guid": "<pf_guid_2>",
31+
"guidsRange": {
32+
"start": "02:00:00:00:00:aa:00:02",
33+
"end": "02:00:00:00:00:aa:00:0a"
34+
}
35+
}
36+
]
37+
```
38+
39+
Config file parameters:
40+
41+
* `pci_address` is a PCI address of a PF
42+
* `pf_guid` is a GUID of a PF. Can be obtained with `ibstat` command.
43+
* `guids` is an array of VF GUID strings
44+
* `guidsRange` is an object representing the start and end of a VF GUID range. It has two fields:
45+
* `start` is a VF GUID range start
46+
* `end` is a VF GUID range end
47+
48+
Requirements for the config file:
49+
50+
* `pci_address` and `pf_guid` cannot be set at the same time for a single device - should return an error
51+
* if the list contains multiple entries for the same device, the first one shall be taken
52+
* `rangeStart` and `rangeEnd` are both included in the range
53+
* `guids` list and range cannot be both set at the same time for a single device - should return an error
54+
* GUIDs are assigned once and not change throughout the lifecycle of the host
55+
56+
### Deploy SriovNetworkNodePolicy
57+
58+
```yaml
59+
apiVersion: sriovnetwork.openshift.io/v1
60+
kind: SriovNetworkNodePolicy
61+
metadata:
62+
name: ib-policy
63+
namespace: sriov-network-operator
64+
spec:
65+
nodeSelector:
66+
feature.node.kubernetes.io/network-sriov.capable: "true"
67+
resourceName: ib_vfs
68+
priority: 10
69+
numVfs: 2
70+
nicSelector:
71+
vendor: "15b3"
72+
rootDevices:
73+
- 0000:d8.00.0
74+
deviceType: netdevice
75+
```
76+
77+
## Verify assigned GUIDs
78+
79+
Run ip link tool to verify assigned GUIDs:
80+
81+
```bash
82+
ip link
83+
84+
...
85+
6: ibp216s0f0: <BROADCAST,MULTICAST> mtu 4092 qdisc noop state UP mode DEFAULT group default qlen 256
86+
link/infiniband 00:00:05:a9:fe:80:00:00:00:00:00:00:b8:59:9f:03:00:f9:8f:86 brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff
87+
vf 0 link/infiniband ... NODE_GUID 02:00:00:00:00:00:00:00, PORT_GUID 02:00:00:00:00:00:00:00, link-state enable, trust off, query_rss off
88+
vf 1 link/infiniband ... NODE_GUID 02:00:00:00:00:00:00:01, PORT_GUID 02:00:00:00:00:00:00:01, link-state enable, trust off, query_rss off
89+
```
90+

pkg/consts/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ const (
138138

139139
// ManageSoftwareBridgesFeatureGate: enables management of software bridges by the operator
140140
ManageSoftwareBridgesFeatureGate = "manageSoftwareBridges"
141+
142+
// The path to the file on the host filesystem that contains the IB GUID distribution for IB VFs
143+
InfinibandGUIDConfigFilePath = SriovConfBasePath + "/infiniband/guids"
141144
)
142145

143146
const (

pkg/helper/host.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ func NewHostHelpers(utilsHelper utils.CmdInterface,
3535
func NewDefaultHostHelpers() (HostHelpersInterface, error) {
3636
utilsHelper := utils.New()
3737
mlxHelper := mlx.New(utilsHelper)
38-
hostManager := host.NewHostManager(utilsHelper)
38+
hostManager, err := host.NewHostManager(utilsHelper)
39+
if err != nil {
40+
log.Log.Error(err, "failed to create host manager")
41+
return nil, err
42+
}
3943
storeManager, err := store.NewManager()
4044
if err != nil {
4145
log.Log.Error(err, "failed to create store manager")

pkg/helper/mock/mock_helper.go

Lines changed: 29 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package infiniband
2+
3+
import (
4+
"fmt"
5+
"math/rand"
6+
"net"
7+
)
8+
9+
// GUID address is an uint64 encapsulation for network hardware address
10+
type GUID uint64
11+
12+
const (
13+
guidLength = 8
14+
byteBitLen = 8
15+
byteMask = 0xff
16+
)
17+
18+
// ParseGUID parses string only as GUID 64 bit
19+
func ParseGUID(s string) (GUID, error) {
20+
ha, err := net.ParseMAC(s)
21+
if err != nil {
22+
return 0, err
23+
}
24+
if len(ha) != guidLength {
25+
return 0, fmt.Errorf("invalid GUID address %s", s)
26+
}
27+
var guid uint64
28+
for idx, octet := range ha {
29+
guid |= uint64(octet) << uint(byteBitLen*(guidLength-1-idx))
30+
}
31+
return GUID(guid), nil
32+
}
33+
34+
// String returns the string representation of GUID
35+
func (g GUID) String() string {
36+
return g.HardwareAddr().String()
37+
}
38+
39+
// HardwareAddr returns GUID representation as net.HardwareAddr
40+
func (g GUID) HardwareAddr() net.HardwareAddr {
41+
value := uint64(g)
42+
ha := make(net.HardwareAddr, guidLength)
43+
for idx := guidLength - 1; idx >= 0; idx-- {
44+
ha[idx] = byte(value & byteMask)
45+
value >>= byteBitLen
46+
}
47+
48+
return ha
49+
}
50+
51+
func generateRandomGUID() net.HardwareAddr {
52+
guid := make(net.HardwareAddr, 8)
53+
54+
// First field is 0x01 - xfe to avoid all zero and all F invalid guids
55+
guid[0] = byte(1 + rand.Intn(0xfe))
56+
57+
for i := 1; i < len(guid); i++ {
58+
guid[i] = byte(rand.Intn(0x100))
59+
}
60+
61+
return guid
62+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package infiniband
2+
3+
import (
4+
"net"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
var _ = Describe("GUID", func() {
11+
It("should parse and process GUIDs correctly", func() {
12+
guidStr := "00:01:02:03:04:05:06:08"
13+
nextGuidStr := "00:01:02:03:04:05:06:09"
14+
15+
guid, err := ParseGUID(guidStr)
16+
Expect(err).NotTo(HaveOccurred())
17+
18+
Expect(guid.String()).To(Equal(guidStr))
19+
Expect((guid + 1).String()).To(Equal(nextGuidStr))
20+
})
21+
It("should represent GUID as HW address", func() {
22+
guidStr := "00:01:02:03:04:05:06:08"
23+
24+
guid, err := ParseGUID(guidStr)
25+
Expect(err).NotTo(HaveOccurred())
26+
27+
Expect(guid.HardwareAddr()).To(Equal(net.HardwareAddr{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08}))
28+
})
29+
})
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package infiniband
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
8+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink"
9+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
10+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
11+
)
12+
13+
type ibPfGUIDJSONConfig struct {
14+
PciAddress string `json:"pciAddress,omitempty"`
15+
PfGUID string `json:"pfGuid,omitempty"`
16+
GUIDs []string `json:"guids,omitempty"`
17+
GUIDsRange *GUIDRangeJSON `json:"guidsRange,omitempty"`
18+
}
19+
20+
type GUIDRangeJSON struct {
21+
Start string `json:"start,omitempty"`
22+
End string `json:"end,omitempty"`
23+
}
24+
25+
type ibPfGUIDConfig struct {
26+
GUIDs []GUID
27+
GUIDRange *GUIDRange
28+
}
29+
30+
type GUIDRange struct {
31+
Start GUID
32+
End GUID
33+
}
34+
35+
func getIbGUIDConfig(configPath string, netlinkLib netlink.NetlinkLib, networkHelper types.NetworkInterface) (map[string]ibPfGUIDConfig, error) {
36+
links, err := netlinkLib.LinkList()
37+
if err != nil {
38+
return nil, err
39+
}
40+
rawConfigs, err := readJSONConfig(configPath)
41+
if err != nil {
42+
return nil, err
43+
}
44+
resultConfigs := map[string]ibPfGUIDConfig{}
45+
// Parse JSON config into an internal struct
46+
for _, rawConfig := range rawConfigs {
47+
pciAddress, err := getPfPciAddressFromRawConfig(rawConfig, links, networkHelper)
48+
if err != nil {
49+
return nil, fmt.Errorf("failed to extract pci address from ib guid config: %w", err)
50+
}
51+
if len(rawConfig.GUIDs) == 0 && (rawConfig.GUIDsRange == nil || (rawConfig.GUIDsRange.Start == "" || rawConfig.GUIDsRange.End == "")) {
52+
return nil, fmt.Errorf("either guid list or guid range should be provided, got none")
53+
}
54+
if len(rawConfig.GUIDs) != 0 && rawConfig.GUIDsRange != nil {
55+
return nil, fmt.Errorf("either guid list or guid range should be provided, got both")
56+
}
57+
if rawConfig.GUIDsRange != nil && ((rawConfig.GUIDsRange.Start != "" && rawConfig.GUIDsRange.End == "") || (rawConfig.GUIDsRange.Start == "" && rawConfig.GUIDsRange.End != "")) {
58+
return nil, fmt.Errorf("both guid rangeStart and rangeEnd should be provided, got one")
59+
}
60+
if len(rawConfig.GUIDs) != 0 {
61+
var guids []GUID
62+
for _, guidStr := range rawConfig.GUIDs {
63+
guid, err := ParseGUID(guidStr)
64+
if err != nil {
65+
return nil, fmt.Errorf("failed to parse ib guid %s: %w", guidStr, err)
66+
}
67+
guids = append(guids, guid)
68+
}
69+
resultConfigs[pciAddress] = ibPfGUIDConfig{
70+
GUIDs: guids,
71+
}
72+
continue
73+
}
74+
75+
rangeStart, err := ParseGUID(rawConfig.GUIDsRange.Start)
76+
if err != nil {
77+
return nil, fmt.Errorf("failed to parse ib guid range start: %w", err)
78+
}
79+
rangeEnd, err := ParseGUID(rawConfig.GUIDsRange.End)
80+
if err != nil {
81+
return nil, fmt.Errorf("failed to parse ib guid range end: %w", err)
82+
}
83+
if rangeEnd < rangeStart {
84+
return nil, fmt.Errorf("range end cannot be less then range start")
85+
}
86+
resultConfigs[pciAddress] = ibPfGUIDConfig{
87+
GUIDRange: &GUIDRange{
88+
Start: rangeStart,
89+
End: rangeEnd,
90+
},
91+
}
92+
}
93+
return resultConfigs, nil
94+
}
95+
96+
// readJSONConfig reads the file at the given path and unmarshals the contents into an array of ibPfGUIDJSONConfig structs
97+
func readJSONConfig(configPath string) ([]ibPfGUIDJSONConfig, error) {
98+
data, err := os.ReadFile(utils.GetHostExtensionPath(configPath))
99+
if err != nil {
100+
return nil, fmt.Errorf("failed to read ib guid config: %w", err)
101+
}
102+
var configs []ibPfGUIDJSONConfig
103+
if err := json.Unmarshal(data, &configs); err != nil {
104+
return nil, fmt.Errorf("failed to unmarshal content of ib guid config: %w", err)
105+
}
106+
return configs, nil
107+
}
108+
109+
func getPfPciAddressFromRawConfig(pfRawConfig ibPfGUIDJSONConfig, links []netlink.Link, networkHelper types.NetworkInterface) (string, error) {
110+
if pfRawConfig.PciAddress != "" && pfRawConfig.PfGUID != "" {
111+
return "", fmt.Errorf("either PCI address or PF GUID required to describe an interface, both provided")
112+
}
113+
if pfRawConfig.PciAddress == "" && pfRawConfig.PfGUID == "" {
114+
return "", fmt.Errorf("either PCI address or PF GUID required to describe an interface, none provided")
115+
}
116+
if pfRawConfig.PciAddress != "" {
117+
return pfRawConfig.PciAddress, nil
118+
}
119+
// PfGUID is provided, need to resolve the pci address
120+
for _, link := range links {
121+
if link.Attrs().HardwareAddr.String() == pfRawConfig.PfGUID {
122+
return networkHelper.GetPciAddressFromInterfaceName(link.Attrs().Name)
123+
}
124+
}
125+
return "", fmt.Errorf("no matching link found for pf guid: %s", pfRawConfig.PfGUID)
126+
}

0 commit comments

Comments
 (0)