Skip to content

Commit

Permalink
Merge pull request #49 from ffromani/features-with-state
Browse files Browse the repository at this point in the history
add a new API to report report feature states
  • Loading branch information
safchain committed Jun 11, 2024
2 parents 46753c7 + 10f3ba2 commit 61f6ccf
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
46 changes: 46 additions & 0 deletions ethtool.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,22 @@ func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index u
return (blocks)[index/32].active&(1<<(index%32)) != 0
}

type FeatureState struct {
Available bool
Requested bool
Active bool
NeverChanged bool
}

func getFeatureStateBits(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) FeatureState {
return FeatureState{
Available: (blocks)[index/32].available&(1<<(index%32)) != 0,
Requested: (blocks)[index/32].requested&(1<<(index%32)) != 0,
Active: (blocks)[index/32].active&(1<<(index%32)) != 0,
NeverChanged: (blocks)[index/32].never_changed&(1<<(index%32)) != 0,
}
}

func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) {
blockIndex, bitIndex := index/32, index%32

Expand Down Expand Up @@ -790,6 +806,36 @@ func (e *Ethtool) Features(intf string) (map[string]bool, error) {
return result, nil
}

// FeaturesWithState retrieves features of the given interface name,
// with extra flags to explain if they can be enabled
func (e *Ethtool) FeaturesWithState(intf string) (map[string]FeatureState, error) {
names, err := e.FeatureNames(intf)
if err != nil {
return nil, err
}

length := uint32(len(names))
if length == 0 {
return map[string]FeatureState{}, nil
}

features := ethtoolGfeatures{
cmd: ETHTOOL_GFEATURES,
size: (length + 32 - 1) / 32,
}

if err := e.ioctl(intf, uintptr(unsafe.Pointer(&features))); err != nil {
return nil, err
}

var result = make(map[string]FeatureState, length)
for key, index := range names {
result[key] = getFeatureStateBits(features.blocks, index)
}

return result, nil
}

// Change requests a change in the given device's features.
func (e *Ethtool) Change(intf string, config map[string]bool) error {
names, err := e.FeatureNames(intf)
Expand Down
43 changes: 43 additions & 0 deletions ethtool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,46 @@ func TestSupportedLinkModes(t *testing.T) {
}
}
}

func TestFeatures(t *testing.T) {
et, err := NewEthtool()
if err != nil {
t.Fatal(err)
}
defer et.Close()

feats, err := et.Features("lo")
if err != nil {
t.Fatal(err)
}

if len(feats) == 0 {
// TOOD: do we have a sane subset of features we should check?

Check failure on line 139 in ethtool_test.go

View workflow job for this annotation

GitHub Actions / unittests

`TOOD` is a misspelling of `TODO` (misspell)
t.Fatalf("expected features for loopback interface")
}

featsWithState, err := et.FeaturesWithState("lo")
if err != nil {
t.Fatal(err)
}

if len(feats) != len(featsWithState) {
t.Fatalf("features mismatch: %d with state %d", len(feats), len(featsWithState))
}

fixed := 0
for key, val := range feats {
state, ok := featsWithState[key]
if !ok || val != state.Active {
t.Errorf("inconsistent feature: %q reported %v active %v", key, val, state.Active)
}
if !state.Available {
fixed++
}
}

if fixed == 0 {
// the lo interface MUST have some non-available features, by design
t.Fatalf("loopback interface reported all features available")
}
}

0 comments on commit 61f6ccf

Please sign in to comment.