Skip to content

Commit 5961889

Browse files
committed
Integrate T-BC on Perla2 platform
Assisted by Cursor AI Add Perla2 vendor definitions Integrate on the target Signed-off-by: Vitaly Grinberg <[email protected]>
1 parent 7c137f9 commit 5961889

39 files changed

+3500
-377
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ RUN make clean && make
55

66
FROM quay.io/centos/centos:stream9
77

8-
RUN yum -y update && yum -y update glibc && yum --setopt=skip_missing_names_on_install=False -y install linuxptp ethtool hwdata synce4l && yum clean all
8+
RUN yum -y update && yum -y update glibc && yum --setopt=skip_missing_names_on_install=False -y install pciutils linuxptp ethtool hwdata synce4l && yum clean all
99

1010

1111
RUN yum install -y procps-ng

addons/intel/clock-chain.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,6 @@ func InitClockChain(opts PhaseInputsProvider, nodeProfile *ptpv1.PtpProfile) (*C
249249
if err != nil {
250250
return chain, fmt.Errorf("failed to initialize pins for T-BC operation: %s", err.Error())
251251
}
252-
glog.Info("about to enter TBC Normal mode")
253-
err = chain.EnterNormalTBC()
254-
if err != nil {
255-
return chain, fmt.Errorf("failed to enter T-BC normal mode: %s", err.Error())
256-
}
257252
}
258253
return chain, err
259254
}
@@ -420,7 +415,7 @@ func (c *ClockChain) InitPinsTBC() error {
420415
// (To synchronize the DPLL1 to the E810 PHC synced by ptp4l):
421416
err := c.EnableE810Outputs()
422417
if err != nil {
423-
glog.Error("failed to enable E810 outputs: ", err)
418+
return fmt.Errorf("failed to enable E810 outputs: %w", err)
424419
}
425420
// Disable GNSS-1PPS (all cards), SDP20 and SDP21
426421
commandsGnss, err := c.SetPinsControlForAllNICs([]PinControl{

addons/intel/intel_test.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,33 @@ func Test_ProcessProfileTGMNew(t *testing.T) {
168168
// Test that the profile with no phase inputs is processed correctly
169169
func Test_ProcessProfileTBCNoPhaseInputs(t *testing.T) {
170170
unitTest = true
171-
mockPinSet, restorePinSet := setupBatchPinSetMock()
172-
defer restorePinSet()
171+
172+
// Setup filesystem mock for TBC profile - EnableE810Outputs needs this
173+
mockFS := &MockFileSystem{}
174+
phcEntries := []os.DirEntry{MockDirEntry{name: "ptp0", isDir: true}}
175+
176+
// EnableE810Outputs reads the ptp directory and writes to SMA2 and period
177+
mockFS.ExpectReadDir("/sys/class/net/ens4f0/device/ptp/", phcEntries, nil)
178+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/pins/SMA2", []byte("2 2"), os.FileMode(0666), nil)
179+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/period", []byte("2 0 0 1 0"), os.FileMode(0666), nil)
180+
181+
// Replace global filesystem with mock
182+
originalFS := filesystem
183+
filesystem = mockFS
184+
defer func() { filesystem = originalFS }()
185+
173186
profile, err := loadProfile("./testdata/profile-tbc-no-input-delays.yaml")
174187
assert.NoError(t, err)
175188
err = OnPTPConfigChangeE810(nil, profile)
176189
assert.NoError(t, err)
177-
assert.NotNil(t, mockPinSet.commands, "Ensure clockChain.SetPinDefaults was called")
190+
191+
// Verify that clockChain was initialized (SetPinDefaults is called as part of InitClockChain)
192+
// If SetPinDefaults wasn't called, InitClockChain would have failed
193+
assert.NotNil(t, clockChain, "clockChain should be initialized")
194+
assert.Equal(t, ClockTypeTBC, clockChain.Type, "clockChain should be T-BC type")
195+
196+
// Verify all expected filesystem calls were made
197+
mockFS.VerifyAllCalls(t)
178198
}
179199

180200
func Test_ProcessProfileTbc(t *testing.T) {
@@ -184,6 +204,11 @@ func Test_ProcessProfileTbc(t *testing.T) {
184204
mockFS := &MockFileSystem{}
185205
phcEntries := []os.DirEntry{MockDirEntry{name: "ptp0", isDir: true}}
186206

207+
// EnableE810Outputs is called for the leading NIC (ens4f0) - needs specific paths
208+
mockFS.ExpectReadDir("/sys/class/net/ens4f0/device/ptp/", phcEntries, nil)
209+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/pins/SMA2", []byte("2 2"), os.FileMode(0666), nil)
210+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/period", []byte("2 0 0 1 0"), os.FileMode(0666), nil)
211+
187212
// profile-tbc.yaml has pins for ens4f0, ens5f0, ens8f0 (3 devices)
188213
for i := 0; i < 3; i++ {
189214
// Each device needs ReadDir + 4 pin writes (SMA1, SMA2, U.FL1, U.FL2)
@@ -193,7 +218,7 @@ func Test_ProcessProfileTbc(t *testing.T) {
193218
}
194219
}
195220

196-
// Add extra operations for EnableE810Outputs and other calls
221+
// Add extra operations for other calls
197222
for i := 0; i < 10; i++ {
198223
mockFS.ExpectReadDir("", phcEntries, nil) // Extra ReadDir calls
199224
mockFS.ExpectWriteFile("", []byte(""), os.FileMode(0666), nil) // Extra WriteFile calls
@@ -248,13 +273,18 @@ func Test_ProcessProfileTtsc(t *testing.T) {
248273
mockFS := &MockFileSystem{}
249274
phcEntries := []os.DirEntry{MockDirEntry{name: "ptp0", isDir: true}}
250275

276+
// EnableE810Outputs is called for the leading NIC (ens4f0) - needs specific paths
277+
mockFS.ExpectReadDir("/sys/class/net/ens4f0/device/ptp/", phcEntries, nil)
278+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/pins/SMA2", []byte("2 2"), os.FileMode(0666), nil)
279+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/period", []byte("2 0 0 1 0"), os.FileMode(0666), nil)
280+
251281
// profile-t-tsc.yaml has pins for ens4f0 only
252282
mockFS.ExpectReadDir("", phcEntries, nil) // One ReadDir
253283
for i := 0; i < 4; i++ { // 4 pin writes
254284
mockFS.ExpectWriteFile("", []byte(""), os.FileMode(0666), nil)
255285
}
256286

257-
// Add extra operations for EnableE810Outputs and other calls
287+
// Add extra operations for other calls
258288
for i := 0; i < 10; i++ {
259289
mockFS.ExpectReadDir("", phcEntries, nil) // Extra ReadDir calls
260290
mockFS.ExpectWriteFile("", []byte(""), os.FileMode(0666), nil) // Extra WriteFile calls
@@ -311,6 +341,18 @@ func Test_SetPinDefaults_AllNICs(t *testing.T) {
311341
defer restorePinSet()
312342
unitTest = true
313343

344+
// Setup filesystem mock for EnableE810Outputs
345+
mockFS := &MockFileSystem{}
346+
phcEntries := []os.DirEntry{MockDirEntry{name: "ptp0", isDir: true}}
347+
mockFS.ExpectReadDir("/sys/class/net/ens4f0/device/ptp/", phcEntries, nil)
348+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/pins/SMA2", []byte("2 2"), os.FileMode(0666), nil)
349+
mockFS.ExpectWriteFile("/sys/class/net/ens4f0/device/ptp/ptp0/period", []byte("2 0 0 1 0"), os.FileMode(0666), nil)
350+
351+
// Replace global filesystem with mock
352+
originalFS := filesystem
353+
filesystem = mockFS
354+
defer func() { filesystem = originalFS }()
355+
314356
// Load a profile with multiple NICs (leading + other NICs)
315357
profile, err := loadProfile("./testdata/profile-tbc.yaml")
316358
assert.NoError(t, err)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/golang/glog v1.2.4
1313
github.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f
1414
github.com/jaypipes/ghw v0.12.0
15-
github.com/k8snetworkplumbingwg/ptp-operator v0.0.0-20251105200506-7495c02a5a34
15+
github.com/k8snetworkplumbingwg/ptp-operator v0.0.0-20251204124210-1fe2e26893f5
1616
github.com/mdlayher/genetlink v1.3.2
1717
github.com/mdlayher/netlink v1.7.2
1818
github.com/prometheus/client_golang v1.16.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
9494
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
9595
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
9696
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
97-
github.com/k8snetworkplumbingwg/ptp-operator v0.0.0-20251105200506-7495c02a5a34 h1:wgQR7ZC6EcPjUNGkmAwoT89Hff1PbitpPe8KDjl+lVM=
98-
github.com/k8snetworkplumbingwg/ptp-operator v0.0.0-20251105200506-7495c02a5a34/go.mod h1:sZdvVrCIjLF8TF7D1Jd1C+Awv+qZ8oY1W9/lliM1p78=
97+
github.com/k8snetworkplumbingwg/ptp-operator v0.0.0-20251204124210-1fe2e26893f5 h1:nE9BDzM+55YROTu6tYGwroNJ3yPTNmUKg1ABxZdBIdM=
98+
github.com/k8snetworkplumbingwg/ptp-operator v0.0.0-20251204124210-1fe2e26893f5/go.mod h1:sZdvVrCIjLF8TF7D1Jd1C+Awv+qZ8oY1W9/lliM1p78=
9999
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
100100
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
101101
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=

pkg/controller/hardwareconfig_controller.go

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import (
1717
ptpv2alpha1 "github.com/k8snetworkplumbingwg/ptp-operator/api/v2alpha1"
1818
)
1919

20+
const (
21+
backoffTime = 15 * time.Second
22+
)
23+
2024
// HardwareConfigUpdateHandler defines the interface implemented by the daemon (or a test double)
2125
// to receive effective hardware configuration updates computed by this controller.
2226
//
@@ -90,8 +94,7 @@ func (r *HardwareConfigReconciler) Reconcile(ctx context.Context, req ctrl.Reque
9094
glog.Infof("HardwareConfig deleted, recalculating hardware configurations name=%s", req.Name)
9195
return r.reconcileAllConfigs(ctx)
9296
}
93-
glog.Errorf("Failed to get HardwareConfig: %v", err)
94-
return ctrl.Result{}, err
97+
return ctrl.Result{}, fmt.Errorf("failed to get HardwareConfig: %w", err)
9598
}
9699

97100
// Recalculate and apply hardware configuration for this node
@@ -147,51 +150,51 @@ func (r *HardwareConfigReconciler) reconcileAllConfigs(ctx context.Context) (ctr
147150
// List all HardwareConfigs in the cluster
148151
hwConfigList := &ptpv2alpha1.HardwareConfigList{}
149152
if err := r.List(ctx, hwConfigList); err != nil {
150-
glog.Errorf("Failed to list HardwareConfigs: %v", err)
151-
return ctrl.Result{}, err
153+
return ctrl.Result{}, fmt.Errorf("failed to list HardwareConfigs: %w", err)
152154
}
153155

154156
// Validate hardware config separation before proceeding
155157
if err := r.validateHardwareConfigSeparation(hwConfigList.Items); err != nil {
156158
glog.Errorf("Hardware config validation failed: %v", err)
157159
// Don't return error to avoid controller crash, but log the issue
158-
// In production, you might want to set a status condition instead
160+
// TODO: Set a status condition instead
161+
// add a status condition to the HardwareConfig CRD to enable that
162+
// type HardwareConfigStatus struct {
163+
// MatchedNodes []MatchedNode `json:"matchedNodes,omitempty" yaml:"matchedNodes,omitempty"`
164+
// Conditions []metav1.Condition `json:"conditions,omitempty"
159165
}
160166

161167
// Check if any hardware configs are associated with currently active PTP profiles
162168
// If so, trigger a PTP restart to ensure hardware and PTP configurations are synchronized
163169
needsPTPRestart := r.checkIfActiveProfilesAffected(ctx, hwConfigList.Items)
164170

165171
// Calculate the applicable hardware configurations for this node
166-
applicableConfigs, err := r.calculateNodeHardwareConfigs(ctx, hwConfigList.Items)
167-
if err != nil {
168-
glog.Errorf("Failed to calculate node hardware configurations: %v", err)
169-
return ctrl.Result{}, err
170-
}
172+
applicableConfigs := r.calculateNodeHardwareConfigs(ctx, hwConfigList.Items)
171173

172174
// Apply hardware configurations via the handler (the daemon instance in production)
173175
if len(applicableConfigs) > 0 {
174176
glog.Infof("Updating daemon hardware configuration with %d hardware configs for node %s", len(applicableConfigs), r.NodeName)
175177

176178
// Send hardware configuration update to daemon (HardwareConfigHandler)
177179
if r.HardwareConfigHandler != nil {
178-
err = r.HardwareConfigHandler.UpdateHardwareConfig(applicableConfigs)
180+
err := r.HardwareConfigHandler.UpdateHardwareConfig(applicableConfigs)
179181
if err != nil {
180-
glog.Errorf("Failed to update daemon hardware configuration: %v", err)
181-
return ctrl.Result{}, err
182+
glog.Infof("could not update daemon hardware configuration: %v (will retry after backoff)", err)
183+
// Requeue after a short delay to avoid immediate retry storms
184+
return ctrl.Result{RequeueAfter: backoffTime}, nil
182185
}
183186
}
184-
185187
glog.Infof("Successfully updated daemon hardware configuration configs=%d", len(applicableConfigs))
186188
} else {
187189
glog.Infof("No applicable hardware configurations found for node %s", r.NodeName)
188190

189191
// Clear hardware configurations if needed
190192
if r.HardwareConfigHandler != nil {
191-
err = r.HardwareConfigHandler.UpdateHardwareConfig([]ptpv2alpha1.HardwareConfig{})
193+
err := r.HardwareConfigHandler.UpdateHardwareConfig([]ptpv2alpha1.HardwareConfig{})
192194
if err != nil {
193-
glog.Errorf("Failed to clear daemon hardware configuration: %v", err)
194-
return ctrl.Result{}, err
195+
glog.Errorf("Failed to clear daemon hardware configuration: %v (will retry after backoff)", err)
196+
// Requeue after a short delay to avoid immediate retry storms
197+
return ctrl.Result{RequeueAfter: backoffTime}, nil
195198
}
196199
}
197200
}
@@ -243,24 +246,21 @@ func (r *HardwareConfigReconciler) scheduleDeferredRestart(_ context.Context) {
243246
}
244247

245248
// calculateNodeHardwareConfigs determines which hardware configurations should be applied to this node
246-
//
247-
//nolint:unparam // error return is kept for future node matching logic
248-
func (r *HardwareConfigReconciler) calculateNodeHardwareConfigs(_ context.Context, hwConfigs []ptpv2alpha1.HardwareConfig) ([]ptpv2alpha1.HardwareConfig, error) {
249+
func (r *HardwareConfigReconciler) calculateNodeHardwareConfigs(_ context.Context, hwConfigs []ptpv2alpha1.HardwareConfig) []ptpv2alpha1.HardwareConfig {
249250
var applicableConfigs []ptpv2alpha1.HardwareConfig
250251

251252
// For now, we apply all hardware configurations to all nodes
252253
// This can be enhanced later with node matching logic similar to PtpConfig
253254
for _, hwConfig := range hwConfigs {
254-
glog.Infof("Processing HardwareConfig name=%s profile=%s", hwConfig.Name, getProfileName(hwConfig.Spec.Profile))
255+
glog.Infof("Processing HardwareConfig name=%s profile=%s", hwConfig.Name, hwConfig.Spec.Profile.Name)
255256

256257
// TODO: Add node-specific filtering logic here
257258
// For now, we include all hardware configs
258259
applicableConfigs = append(applicableConfigs, hwConfig)
259-
glog.Infof("Added hardware config profileName=%s relatedPtpProfile=%s", getProfileName(hwConfig.Spec.Profile), hwConfig.Spec.RelatedPtpProfileName)
260+
glog.Infof("Added hardware config profileName=%s relatedPtpProfile=%s", hwConfig.Spec.Profile.Name, hwConfig.Spec.RelatedPtpProfileName)
260261
}
261-
262262
glog.Infof("Calculated hardware configurations for node node=%s totalConfigs=%d", r.NodeName, len(applicableConfigs))
263-
return applicableConfigs, nil
263+
return applicableConfigs
264264
}
265265

266266
// checkIfActiveProfilesAffected determines if hardware config changes should trigger PTP restart
@@ -304,14 +304,6 @@ func (r *HardwareConfigReconciler) checkIfActiveProfilesAffected(_ context.Conte
304304
return false
305305
}
306306

307-
// getProfileName safely extracts the profile name from a HardwareProfile
308-
func getProfileName(profile ptpv2alpha1.HardwareProfile) string {
309-
if profile.Name != nil {
310-
return *profile.Name
311-
}
312-
return "unnamed"
313-
}
314-
315307
// SetupWithManager sets up the controller with the Manager
316308
func (r *HardwareConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
317309
// Watch HardwareConfig resources

pkg/controller/hardwareconfig_controller_test.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,7 @@ func TestCalculateNodeHardwareConfigs(t *testing.T) {
149149
}
150150

151151
// Call the method under test
152-
result, err := reconciler.calculateNodeHardwareConfigs(context.TODO(), tc.hwConfigs)
153-
154-
// Verify no error occurred
155-
assert.NoError(t, err)
152+
result := reconciler.calculateNodeHardwareConfigs(context.TODO(), tc.hwConfigs)
156153

157154
// Verify the number of hardware configs
158155
assert.Len(t, result, tc.expectedConfigsCount,
@@ -161,11 +158,7 @@ func TestCalculateNodeHardwareConfigs(t *testing.T) {
161158
// Verify config names match expected (based on profile names within configs)
162159
var actualConfigNames []string
163160
for _, hwConfig := range result {
164-
if hwConfig.Spec.Profile.Name != nil {
165-
actualConfigNames = append(actualConfigNames, *hwConfig.Spec.Profile.Name)
166-
} else {
167-
actualConfigNames = append(actualConfigNames, "unnamed")
168-
}
161+
actualConfigNames = append(actualConfigNames, *hwConfig.Spec.Profile.Name)
169162
}
170163
assert.ElementsMatch(t, tc.expectedConfigNames, actualConfigNames,
171164
"Expected config names %v, got %v", tc.expectedConfigNames, actualConfigNames)

pkg/controller/ptpconfig_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func anyRuleMatchesNode(matchRules []ptpv1.MatchRule, nodeName string, nodeLabel
203203
// TODO: Add support for "key=value" format matching
204204
}
205205
}
206+
206207
return false
207208
}
208209

pkg/daemon/config.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ func NewLinuxPTPConfUpdate() (*LinuxPTPConfUpdate, error) {
162162
if err != nil {
163163
return nil, fmt.Errorf("failed to read %s: %v", PTP4L_CONF_FILE_PATH, err)
164164
}
165-
166-
return &LinuxPTPConfUpdate{UpdateCh: make(chan bool), defaultPTP4lConfig: defaultPTP4lConfig}, nil
165+
return &LinuxPTPConfUpdate{UpdateCh: make(chan bool, 10), defaultPTP4lConfig: defaultPTP4lConfig}, nil
167166
}
168167

169168
// UpdateConfig updates the PTP configuration from the provided JSON.

0 commit comments

Comments
 (0)