Skip to content

Commit 61f6ccf

Browse files
authored
Merge pull request #49 from ffromani/features-with-state
add a new API to report report feature states
2 parents 46753c7 + 10f3ba2 commit 61f6ccf

2 files changed

Lines changed: 89 additions & 0 deletions

File tree

ethtool.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,22 @@ func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index u
702702
return (blocks)[index/32].active&(1<<(index%32)) != 0
703703
}
704704

705+
type FeatureState struct {
706+
Available bool
707+
Requested bool
708+
Active bool
709+
NeverChanged bool
710+
}
711+
712+
func getFeatureStateBits(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) FeatureState {
713+
return FeatureState{
714+
Available: (blocks)[index/32].available&(1<<(index%32)) != 0,
715+
Requested: (blocks)[index/32].requested&(1<<(index%32)) != 0,
716+
Active: (blocks)[index/32].active&(1<<(index%32)) != 0,
717+
NeverChanged: (blocks)[index/32].never_changed&(1<<(index%32)) != 0,
718+
}
719+
}
720+
705721
func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) {
706722
blockIndex, bitIndex := index/32, index%32
707723

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

809+
// FeaturesWithState retrieves features of the given interface name,
810+
// with extra flags to explain if they can be enabled
811+
func (e *Ethtool) FeaturesWithState(intf string) (map[string]FeatureState, error) {
812+
names, err := e.FeatureNames(intf)
813+
if err != nil {
814+
return nil, err
815+
}
816+
817+
length := uint32(len(names))
818+
if length == 0 {
819+
return map[string]FeatureState{}, nil
820+
}
821+
822+
features := ethtoolGfeatures{
823+
cmd: ETHTOOL_GFEATURES,
824+
size: (length + 32 - 1) / 32,
825+
}
826+
827+
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&features))); err != nil {
828+
return nil, err
829+
}
830+
831+
var result = make(map[string]FeatureState, length)
832+
for key, index := range names {
833+
result[key] = getFeatureStateBits(features.blocks, index)
834+
}
835+
836+
return result, nil
837+
}
838+
793839
// Change requests a change in the given device's features.
794840
func (e *Ethtool) Change(intf string, config map[string]bool) error {
795841
names, err := e.FeatureNames(intf)

ethtool_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,46 @@ func TestSupportedLinkModes(t *testing.T) {
122122
}
123123
}
124124
}
125+
126+
func TestFeatures(t *testing.T) {
127+
et, err := NewEthtool()
128+
if err != nil {
129+
t.Fatal(err)
130+
}
131+
defer et.Close()
132+
133+
feats, err := et.Features("lo")
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
138+
if len(feats) == 0 {
139+
// TOOD: do we have a sane subset of features we should check?
140+
t.Fatalf("expected features for loopback interface")
141+
}
142+
143+
featsWithState, err := et.FeaturesWithState("lo")
144+
if err != nil {
145+
t.Fatal(err)
146+
}
147+
148+
if len(feats) != len(featsWithState) {
149+
t.Fatalf("features mismatch: %d with state %d", len(feats), len(featsWithState))
150+
}
151+
152+
fixed := 0
153+
for key, val := range feats {
154+
state, ok := featsWithState[key]
155+
if !ok || val != state.Active {
156+
t.Errorf("inconsistent feature: %q reported %v active %v", key, val, state.Active)
157+
}
158+
if !state.Available {
159+
fixed++
160+
}
161+
}
162+
163+
if fixed == 0 {
164+
// the lo interface MUST have some non-available features, by design
165+
t.Fatalf("loopback interface reported all features available")
166+
}
167+
}

0 commit comments

Comments
 (0)