From 9f7cc57fe5508c495d3a75efd7f942aeec0013e0 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 29 Mar 2023 15:16:52 -0700 Subject: [PATCH 1/4] tools: ynl: support byte-order in cli Used by ethtool spec. Signed-off-by: Stanislav Fomichev Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/nlspec.py | 1 + tools/net/ynl/lib/ynl.py | 35 +++++++++++++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/tools/net/ynl/lib/nlspec.py b/tools/net/ynl/lib/nlspec.py index 06a906d74f0e..0c43022ff7e6 100644 --- a/tools/net/ynl/lib/nlspec.py +++ b/tools/net/ynl/lib/nlspec.py @@ -163,6 +163,7 @@ class SpecAttr(SpecElement): self.is_multi = yaml.get('multi-attr', False) self.struct_name = yaml.get('struct') self.sub_type = yaml.get('sub-type') + self.byte_order = yaml.get('byte-order') class SpecAttrSet(SpecElement): diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index ec40918152e1..8778994d40c0 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -80,17 +80,25 @@ class NlAttr: self.full_len = (self.payload_len + 3) & ~3 self.raw = raw[offset + 4:offset + self.payload_len] + def format_byte_order(byte_order): + if byte_order: + return ">" if byte_order == "big-endian" else "<" + return "" + def as_u8(self): return struct.unpack("B", self.raw)[0] - def as_u16(self): - return struct.unpack("H", self.raw)[0] + def as_u16(self, byte_order=None): + endian = NlAttr.format_byte_order(byte_order) + return struct.unpack(f"{endian}H", self.raw)[0] - def as_u32(self): - return struct.unpack("I", self.raw)[0] + def as_u32(self, byte_order=None): + endian = NlAttr.format_byte_order(byte_order) + return struct.unpack(f"{endian}I", self.raw)[0] - def as_u64(self): - return struct.unpack("Q", self.raw)[0] + def as_u64(self, byte_order=None): + endian = NlAttr.format_byte_order(byte_order) + return struct.unpack(f"{endian}Q", self.raw)[0] def as_strz(self): return self.raw.decode('ascii')[:-1] @@ -365,11 +373,14 @@ class YnlFamily(SpecFamily): elif attr["type"] == 'u8': attr_payload = struct.pack("B", int(value)) elif attr["type"] == 'u16': - attr_payload = struct.pack("H", int(value)) + endian = NlAttr.format_byte_order(attr.byte_order) + attr_payload = struct.pack(f"{endian}H", int(value)) elif attr["type"] == 'u32': - attr_payload = struct.pack("I", int(value)) + endian = NlAttr.format_byte_order(attr.byte_order) + attr_payload = struct.pack(f"{endian}I", int(value)) elif attr["type"] == 'u64': - attr_payload = struct.pack("Q", int(value)) + endian = NlAttr.format_byte_order(attr.byte_order) + attr_payload = struct.pack(f"{endian}Q", int(value)) elif attr["type"] == 'string': attr_payload = str(value).encode('ascii') + b'\x00' elif attr["type"] == 'binary': @@ -415,11 +426,11 @@ class YnlFamily(SpecFamily): elif attr_spec['type'] == 'u8': decoded = attr.as_u8() elif attr_spec['type'] == 'u16': - decoded = attr.as_u16() + decoded = attr.as_u16(attr_spec.byte_order) elif attr_spec['type'] == 'u32': - decoded = attr.as_u32() + decoded = attr.as_u32(attr_spec.byte_order) elif attr_spec['type'] == 'u64': - decoded = attr.as_u64() + decoded = attr.as_u64(attr_spec.byte_order) elif attr_spec["type"] == 'string': decoded = attr.as_strz() elif attr_spec["type"] == 'binary': From a353318ebf24100bcc7254d293cd9f041f4075dd Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 29 Mar 2023 15:16:53 -0700 Subject: [PATCH 2/4] tools: ynl: populate most of the ethtool spec Things that are not implemented: - cable tests - bitmaks in the requests don't work (needs multi-attr support in ynl.py) - stats-get seems to return nonsense (not passing a bitmask properly?) - notifications are not tested Signed-off-by: Stanislav Fomichev Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/ethtool.yaml | 1298 +++++++++++++++++++++- 1 file changed, 1276 insertions(+), 22 deletions(-) diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml index 6d8ae3d9a680..129f413ea349 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -6,6 +6,12 @@ protocol: genetlink-legacy doc: Partial family for Ethtool Netlink. +definitions: + - + name: udp-tunnel-type + type: enum + entries: [ vxlan, geneve, vxlan-gpe ] + attribute-sets: - name: header @@ -38,6 +44,7 @@ attribute-sets: - name: bit type: nest + multi-attr: true nested-attributes: bitset-bit - name: bitset @@ -53,6 +60,22 @@ attribute-sets: type: nest nested-attributes: bitset-bits + - + name: u64-array + attributes: + - + name: u64 + type: nest + multi-attr: true + nested-attributes: u64 + - + name: s32-array + attributes: + - + name: s32 + type: nest + multi-attr: true + nested-attributes: s32 - name: string attributes: @@ -234,6 +257,657 @@ attribute-sets: name: stats type: nest nested-attributes: mm-stat + - + name: linkinfo + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: port + type: u8 + - + name: phyaddr + type: u8 + - + name: tp-mdix + type: u8 + - + name: tp-mdix-ctrl + type: u8 + - + name: transceiver + type: u8 + - + name: linkmodes + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: autoneg + type: u8 + - + name: ours + type: nest + nested-attributes: bitset + - + name: peer + type: nest + nested-attributes: bitset + - + name: speed + type: u32 + - + name: duplex + type: u8 + - + name: master-slave-cfg + type: u8 + - + name: master-slave-state + type: u8 + - + name: master-slave-lanes + type: u32 + - + name: rate-matching + type: u8 + - + name: linkstate + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: link + type: u8 + - + name: sqi + type: u32 + - + name: sqi-max + type: u32 + - + name: ext-state + type: u8 + - + name: ext-substate + type: u8 + - + name: down-cnt + type: u32 + - + name: debug + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: msgmask + type: nest + nested-attributes: bitset + - + name: wol + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: modes + type: nest + nested-attributes: bitset + - + name: sopass + type: binary + - + name: features + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: hw + type: nest + nested-attributes: bitset + - + name: wanted + type: nest + nested-attributes: bitset + - + name: active + type: nest + nested-attributes: bitset + - + name: nochange + type: nest + nested-attributes: bitset + - + name: channels + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: rx-max + type: u32 + - + name: tx-max + type: u32 + - + name: other-max + type: u32 + - + name: combined-max + type: u32 + - + name: rx-count + type: u32 + - + name: tx-count + type: u32 + - + name: other-count + type: u32 + - + name: combined-count + type: u32 + + - + name: coalesce + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: rx-usecs + type: u32 + - + name: rx-max-frames + type: u32 + - + name: rx-usecs-irq + type: u32 + - + name: rx-max-frames-irq + type: u32 + - + name: tx-usecs + type: u32 + - + name: tx-max-frames + type: u32 + - + name: tx-usecs-irq + type: u32 + - + name: tx-max-frames-irq + type: u32 + - + name: stats-block-usecs + type: u32 + - + name: use-adaptive-rx + type: u8 + - + name: use-adaptive-tx + type: u8 + - + name: pkt-rate-low + type: u32 + - + name: rx-usecs-low + type: u32 + - + name: rx-max-frames-low + type: u32 + - + name: tx-usecs-low + type: u32 + - + name: tx-max-frames-low + type: u32 + - + name: pkt-rate-high + type: u32 + - + name: rx-usecs-high + type: u32 + - + name: rx-max-frames-high + type: u32 + - + name: tx-usecs-high + type: u32 + - + name: tx-max-frames-high + type: u32 + - + name: rate-sample-interval + type: u32 + - + name: use-cqe-mode-tx + type: u8 + - + name: use-cqe-mode-rx + type: u8 + - + name: tx-aggr-max-bytes + type: u32 + - + name: tx-aggr-max-frames + type: u32 + - + name: tx-aggr-time-usecs + type: u32 + - + name: pause-stat + attributes: + - + name: pad + type: u32 + - + name: tx-frames + type: u64 + - + name: rx-frames + type: u64 + - + name: pause + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: autoneg + type: u8 + - + name: rx + type: u8 + - + name: tx + type: u8 + - + name: stats + type: nest + nested-attributes: pause-stat + - + name: stats-src + type: u32 + - + name: eee + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: modes-ours + type: nest + nested-attributes: bitset + - + name: modes-peer + type: nest + nested-attributes: bitset + - + name: active + type: u8 + - + name: enabled + type: u8 + - + name: tx-lpi-enabled + type: u8 + - + name: tx-lpi-timer + type: u32 + - + name: tsinfo + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: timestamping + type: nest + nested-attributes: bitset + - + name: tx-types + type: nest + nested-attributes: bitset + - + name: rx-filters + type: nest + nested-attributes: bitset + - + name: phc-index + type: u32 + - + name: cable-test-nft-nest-result + attributes: + - + name: pair + type: u8 + - + name: code + type: u8 + - + name: cable-test-nft-nest-fault-length + attributes: + - + name: pair + type: u8 + - + name: cm + type: u32 + - + name: cable-test-nft-nest + attributes: + - + name: result + type: nest + nested-attributes: cable-test-nft-nest-result + - + name: fault-length + type: nest + nested-attributes: cable-test-nft-nest-fault-length + - + name: cable-test + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: status + type: u8 + - + name: nest + type: nest + nested-attributes: cable-test-nft-nest + - + name: cable-test-tdr-cfg + attributes: + - + name: first + type: u32 + - + name: last + type: u32 + - + name: step + type: u32 + - + name: pari + type: u8 + - + name: cable-test-tdr + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: cfg + type: nest + nested-attributes: cable-test-tdr-cfg + - + name: tunnel-info-udp-entry + attributes: + - + name: port + type: u16 + byte-order: big-endian + - + name: type + type: u32 + enum: udp-tunnel-type + - + name: tunnel-info-udp-table + attributes: + - + name: size + type: u32 + - + name: types + type: nest + nested-attributes: bitset + - + name: udp-ports + type: nest + nested-attributes: tunnel-info-udp-entry + - + name: tunnel-info + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: udp-ports + type: nest + nested-attributes: tunnel-info-udp-table + - + name: fec-stat + attributes: + - + name: pad + type: u8 + - + name: corrected + type: nest + nested-attributes: u64-array + - + name: uncorr + type: nest + nested-attributes: u64-array + - + name: corr-bits + type: nest + nested-attributes: u64-array + - + name: fec + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: modes + type: nest + nested-attributes: bitset + - + name: auto + type: u8 + - + name: active + type: u32 + - + name: stats + type: nest + nested-attributes: fec-stat + - + name: module-eeprom + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: offset + type: u32 + - + name: length + type: u32 + - + name: page + type: u8 + - + name: bank + type: u8 + - + name: i2c-address + type: u8 + - + name: data + type: binary + - + name: stats-grp + attributes: + - + name: pad + type: u32 + - + name: id + type: u32 + - + name: ss-id + type: u32 + - + name: stat + type: nest + nested-attributes: u64 + - + name: hist-rx + type: nest + nested-attributes: u64 + - + name: hist-tx + type: nest + nested-attributes: u64 + - + name: hist-bkt-low + type: u32 + - + name: hist-bkt-hi + type: u32 + - + name: hist-bkt-val + type: u64 + - + name: stats + attributes: + - + name: pad + type: u32 + - + name: header + type: nest + nested-attributes: header + - + name: groups + type: nest + nested-attributes: bitset + - + name: grp + type: nest + nested-attributes: stats-grp + - + name: src + type: u32 + - + name: phc-vclocks + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: num + type: u32 + - + name: index + type: nest + nested-attributes: s32-array + - + name: module + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: power-mode-policy + type: u8 + - + name: power-mode + type: u8 + - + name: pse + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: admin-state + type: u32 + - + name: admin-control + type: u32 + - + name: pw-d-status + type: u32 + - + name: rss + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: context + type: u32 + - + name: hfunc + type: u32 + - + name: indir + type: binary + - + name: hkey + type: binary + - + name: plca + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: version + type: u16 + - + name: enabled + type: u8 + - + name: status + type: u8 + - + name: node-cnt + type: u32 + - + name: node-id + type: u32 + - + name: to-tmr + type: u32 + - + name: burst-cnt + type: u32 + - + name: burst-tmr + type: u32 operations: enum-model: directional @@ -255,9 +929,188 @@ operations: - header - stringsets dump: *strset-get-op + - + name: linkinfo-get + doc: Get link info. - # TODO: fill in the requests in between + attribute-set: linkinfo + do: &linkinfo-get-op + request: + attributes: + - header + reply: + attributes: &linkinfo + - header + - port + - phyaddr + - tp-mdix + - tp-mdix-ctrl + - transceiver + dump: *linkinfo-get-op + - + name: linkinfo-set + doc: Set link info. + + attribute-set: linkinfo + + do: + request: + attributes: *linkinfo + - + name: linkinfo-ntf + doc: Notification for change in link info. + notify: linkinfo-get + - + name: linkmodes-get + doc: Get link modes. + + attribute-set: linkmodes + + do: &linkmodes-get-op + request: + attributes: + - header + reply: + attributes: &linkmodes + - header + - autoneg + - ours + - peer + - speed + - duplex + - master-slave-cfg + - master-slave-state + - master-slave-lanes + - rate-matching + dump: *linkmodes-get-op + - + name: linkmodes-set + doc: Set link modes. + + attribute-set: linkmodes + + do: + request: + attributes: *linkmodes + - + name: linkmodes-ntf + doc: Notification for change in link modes. + notify: linkmodes-get + - + name: linkstate-get + doc: Get link state. + + attribute-set: linkstate + + do: &linkstate-get-op + request: + attributes: + - header + reply: + attributes: + - header + - link + - sqi + - sqi-max + - ext-state + - ext-substate + - down-cnt + dump: *linkstate-get-op + - + name: debug-get + doc: Get debug message mask. + + attribute-set: debug + + do: &debug-get-op + request: + attributes: + - header + reply: + attributes: &debug + - header + - msgmask + dump: *debug-get-op + - + name: debug-set + doc: Set debug message mask. + + attribute-set: debug + + do: + request: + attributes: *debug + - + name: debug-ntf + doc: Notification for change in debug message mask. + notify: debug-get + - + name: wol-get + doc: Get WOL params. + + attribute-set: wol + + do: &wol-get-op + request: + attributes: + - header + reply: + attributes: &wol + - header + - modes + - sopass + dump: *wol-get-op + - + name: wol-set + doc: Set WOL params. + + attribute-set: wol + + do: + request: + attributes: *wol + - + name: wol-ntf + doc: Notification for change in WOL params. + notify: wol-get + - + name: features-get + doc: Get features. + + attribute-set: features + + do: &feature-get-op + request: + attributes: + - header + reply: + attributes: &feature + - header + # User-changeable features. + - hw + # User-requested features. + - wanted + # Currently active features. + - active + # Unchangeable features. + - nochange + dump: *feature-get-op + - + name: features-set + doc: Set features. + + attribute-set: features + + do: &feature-set-op + request: + attributes: *feature + reply: + attributes: *feature + - + name: features-ntf + doc: Notification for change in features. + notify: features-get - name: privflags-get doc: Get device private flags. @@ -266,12 +1119,10 @@ operations: do: &privflag-get-op request: - value: 13 attributes: - header reply: - value: 14 - attributes: + attributes: &privflag - header - flags dump: *privflag-get-op @@ -283,9 +1134,7 @@ operations: do: request: - attributes: - - header - - flags + attributes: *privflag - name: privflags-ntf doc: Notification for change in device private flags. @@ -302,7 +1151,7 @@ operations: attributes: - header reply: - attributes: + attributes: &ring - header - rx-max - rx-mini-max @@ -328,24 +1177,431 @@ operations: do: request: - attributes: - - header - - rx - - rx-mini - - rx-jumbo - - tx - - rx-buf-len - - tcp-data-split - - cqe-size - - tx-push - - rx-push + attributes: *ring - name: rings-ntf doc: Notification for change in ring params. notify: rings-get + - + name: channels-get + doc: Get channel params. - # TODO: fill in the requests in between + attribute-set: channels + do: &channel-get-op + request: + attributes: + - header + reply: + attributes: &channel + - header + - rx-max + - tx-max + - other-max + - combined-max + - rx-count + - tx-count + - other-count + - combined-count + dump: *channel-get-op + - + name: channels-set + doc: Set channel params. + + attribute-set: channels + + do: + request: + attributes: *channel + - + name: channels-ntf + doc: Notification for change in channel params. + notify: channels-get + - + name: coalesce-get + doc: Get coalesce params. + + attribute-set: coalesce + + do: &coalesce-get-op + request: + attributes: + - header + reply: + attributes: &coalesce + - header + - rx-usecs + - rx-max-frames + - rx-usecs-irq + - rx-max-frames-irq + - tx-usecs + - tx-max-frames + - tx-usecs-irq + - tx-max-frames-irq + - stats-block-usecs + - use-adaptive-rx + - use-adaptive-tx + - pkt-rate-low + - rx-usecs-low + - rx-max-frames-low + - tx-usecs-low + - tx-max-frames-low + - pkt-rate-high + - rx-usecs-high + - rx-max-frames-high + - tx-usecs-high + - tx-max-frames-high + - rate-sample-interval + - use-cqe-mode-tx + - use-cqe-mode-rx + - tx-aggr-max-bytes + - tx-aggr-max-frames + - tx-aggr-time-usecs + dump: *coalesce-get-op + - + name: coalesce-set + doc: Set coalesce params. + + attribute-set: coalesce + + do: + request: + attributes: *coalesce + - + name: coalesce-ntf + doc: Notification for change in coalesce params. + notify: coalesce-get + - + name: pause-get + doc: Get pause params. + + attribute-set: pause + + do: &pause-get-op + request: + attributes: + - header + reply: + attributes: &pause + - header + - autoneg + - rx + - tx + - stats + - stats-src + dump: *pause-get-op + - + name: pause-set + doc: Set pause params. + + attribute-set: pause + + do: + request: + attributes: *pause + - + name: pause-ntf + doc: Notification for change in pause params. + notify: pause-get + - + name: eee-get + doc: Get eee params. + + attribute-set: eee + + do: &eee-get-op + request: + attributes: + - header + reply: + attributes: &eee + - header + - modes-ours + - modes-peer + - active + - enabled + - tx-lpi-enabled + - tx-lpi-timer + dump: *eee-get-op + - + name: eee-set + doc: Set eee params. + + attribute-set: eee + + do: + request: + attributes: *eee + - + name: eee-ntf + doc: Notification for change in eee params. + notify: eee-get + - + name: tsinfo-get + doc: Get tsinfo params. + + attribute-set: tsinfo + + do: &tsinfo-get-op + request: + attributes: + - header + reply: + attributes: + - header + - timestamping + - tx-types + - rx-filters + - phc-index + dump: *tsinfo-get-op + - + name: cable-test-act + doc: Cable test. + + attribute-set: cable-test + + do: + request: + attributes: + - header + reply: + attributes: + - header + - cable-test-nft-nest + - + name: cable-test-tdr-act + doc: Cable test TDR. + + attribute-set: cable-test-tdr + + do: + request: + attributes: + - header + reply: + attributes: + - header + - cable-test-tdr-cfg + - + name: tunnel-info-get + doc: Get tsinfo params. + + attribute-set: tunnel-info + + do: &tunnel-info-get-op + request: + attributes: + - header + reply: + attributes: + - header + - udp-ports + dump: *tunnel-info-get-op + - + name: fec-get + doc: Get FEC params. + + attribute-set: fec + + do: &fec-get-op + request: + attributes: + - header + reply: + attributes: &fec + - header + - modes + - auto + - active + - stats + dump: *fec-get-op + - + name: fec-set + doc: Set FEC params. + + attribute-set: fec + + do: + request: + attributes: *fec + - + name: fec-ntf + doc: Notification for change in FEC params. + notify: fec-get + - + name: module-eeprom-get + doc: Get module EEPROM params. + + attribute-set: module-eeprom + + do: &module-eeprom-get-op + request: + attributes: + - header + reply: + attributes: + - header + - offset + - length + - page + - bank + - i2c-address + - data + dump: *module-eeprom-get-op + - + name: stats-get + doc: Get statistics. + + attribute-set: stats + + do: &stats-get-op + request: + attributes: + - header + - groups + reply: + attributes: + - header + - groups + - grp + - src + dump: *stats-get-op + - + name: phc-vclocks-get + doc: Get PHC VCLOCKs. + + attribute-set: phc-vclocks + + do: &phc-vclocks-get-op + request: + attributes: + - header + reply: + attributes: + - header + - num + dump: *phc-vclocks-get-op + - + name: module-get + doc: Get module params. + + attribute-set: module + + do: &module-get-op + request: + attributes: + - header + reply: + attributes: &module + - header + - power-mode-policy + - power-mode + dump: *module-get-op + - + name: module-set + doc: Set module params. + + attribute-set: module + + do: + request: + attributes: *module + - + name: module-ntf + doc: Notification for change in module params. + notify: module-get + - + name: pse-get + doc: Get Power Sourcing Equipment params. + + attribute-set: pse + + do: &pse-get-op + request: + attributes: + - header + reply: + attributes: &pse + - header + - admin-state + - admin-control + - pw-d-status + dump: *pse-get-op + - + name: pse-set + doc: Set Power Sourcing Equipment params. + + attribute-set: pse + + do: + request: + attributes: *pse + - + name: rss-get + doc: Get RSS params. + + attribute-set: rss + + do: &rss-get-op + request: + attributes: + - header + reply: + attributes: + - header + - context + - hfunc + - indir + - hkey + dump: *rss-get-op + - + name: plca-get + doc: Get PLCA params. + + attribute-set: plca + + do: &plca-get-op + request: + attributes: + - header + reply: + attributes: &plca + - header + - version + - enabled + - status + - node-cnt + - node-id + - to-tmr + - burst-cnt + - burst-tmr + dump: *plca-get-op + - + name: plca-set + doc: Set PLCA params. + + attribute-set: plca + + do: + request: + attributes: *plca + - + name: plca-get-status + doc: Get PLCA status params. + + attribute-set: plca + + do: &plca-get-status-op + request: + attributes: + - header + reply: + attributes: *plca + dump: *plca-get-status-op + - + name: plca-ntf + doc: Notification for change in PLCA params. + notify: plca-get - name: mm-get doc: Get MAC Merge configuration and state @@ -354,11 +1610,9 @@ operations: do: &mm-get-op request: - value: 42 attributes: - header reply: - value: 42 attributes: - header - pmac-enabled From 48993e22d23ae1bda1db3616f5d9baa4e7b18d35 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 29 Mar 2023 15:16:54 -0700 Subject: [PATCH 3/4] tools: ynl: replace print with NlError Instead of dumping the error on the stdout, make the callee and opportunity to decide what to do with it. This is mostly for the ethtool testing. Signed-off-by: Stanislav Fomichev Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 8778994d40c0..373c0edb5f83 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -67,6 +67,14 @@ class Netlink: NLMSGERR_ATTR_MISS_NEST = 6 +class NlError(Exception): + def __init__(self, nl_msg): + self.nl_msg = nl_msg + + def __str__(self): + return f"Netlink error: {os.strerror(-self.nl_msg.error)}\n{self.nl_msg}" + + class NlAttr: type_formats = { 'u8' : ('B', 1), 's8' : ('b', 1), 'u16': ('H', 2), 's16': ('h', 2), @@ -551,9 +559,7 @@ class YnlFamily(SpecFamily): self._decode_extack(msg, op.attr_set, nl_msg.extack) if nl_msg.error: - print("Netlink error:", os.strerror(-nl_msg.error)) - print(nl_msg) - return + raise NlError(nl_msg) if nl_msg.done: if nl_msg.extack: print("Netlink warning:") From f3d07b02b2b8eba5b0e168405614e15cd6617a43 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 29 Mar 2023 15:16:55 -0700 Subject: [PATCH 4/4] tools: ynl: ethtool testing tool This is what I've been using to see whether the spec makes sense. A small subset of getters (mostly the unprivileged ones) is implemented. Some setters (channels) also work. Setters for messages with bitmasks are not implemented. Initially I was trying to make this tool look 1:1 like real ethtool, but eventually gave up :-) Sample output: $ ./tools/net/ynl/ethtool enp0s31f6 Settings for enp0s31f6: Supported ports: [ TP ] Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supported pause frame use: no Supports auto-negotiation: yes Supported FEC modes: Not reported Speed: Unknown! Duplex: Unknown! (255) Auto-negotiation: on Port: Twisted Pair PHYAD: 2 Transceiver: Internal MDI-X: Unknown (auto) Current message level: drv probe link Link detected: no Signed-off-by: Stanislav Fomichev Signed-off-by: Jakub Kicinski --- tools/net/ynl/ethtool | 424 ++++++++++++++++++++++++++++++++++++ tools/net/ynl/lib/nlspec.py | 9 + tools/net/ynl/lib/ynl.py | 11 + 3 files changed, 444 insertions(+) create mode 100755 tools/net/ynl/ethtool diff --git a/tools/net/ynl/ethtool b/tools/net/ynl/ethtool new file mode 100755 index 000000000000..5fb1d670693a --- /dev/null +++ b/tools/net/ynl/ethtool @@ -0,0 +1,424 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +import argparse +import json +import pprint +import sys +import re + +from lib import YnlFamily + +def args_to_req(ynl, op_name, args, req): + """ + Verify and convert command-line arguments to the ynl-compatible request. + """ + valid_attrs = ynl.operation_do_attributes(op_name) + valid_attrs.remove('header') # not user-provided + + if len(args) == 0: + print(f'no attributes, expected: {valid_attrs}') + sys.exit(1) + + i = 0 + while i < len(args): + attr = args[i] + if i + 1 >= len(args): + print(f'expected value for \'{attr}\'') + sys.exit(1) + + if attr not in valid_attrs: + print(f'invalid attribute \'{attr}\', expected: {valid_attrs}') + sys.exit(1) + + val = args[i+1] + i += 2 + + req[attr] = val + +def print_field(reply, *desc): + """ + Pretty-print a set of fields from the reply. desc specifies the + fields and the optional type (bool/yn). + """ + if len(desc) == 0: + return print_field(reply, *zip(reply.keys(), reply.keys())) + + for spec in desc: + try: + field, name, tp = spec + except: + field, name = spec + tp = 'int' + + value = reply.get(field, None) + if tp == 'yn': + value = 'yes' if value else 'no' + elif tp == 'bool' or isinstance(value, bool): + value = 'on' if value else 'off' + else: + value = 'n/a' if value is None else value + + print(f'{name}: {value}') + +def print_speed(name, value): + """ + Print out the speed-like strings from the value dict. + """ + speed_re = re.compile(r'[0-9]+base[^/]+/.+') + speed = [ k for k, v in value.items() if v and speed_re.match(k) ] + print(f'{name}: {" ".join(speed)}') + +def doit(ynl, args, op_name): + """ + Prepare request header, parse arguments and doit. + """ + req = { + 'header': { + 'dev-name': args.device, + }, + } + + args_to_req(ynl, op_name, args.args, req) + ynl.do(op_name, req) + +def dumpit(ynl, args, op_name, extra = {}): + """ + Prepare request header, parse arguments and dumpit (filtering out the + devices we're not interested in). + """ + reply = ynl.dump(op_name, { 'header': {} } | extra) + if not reply: + return {} + + for msg in reply: + if msg['header']['dev-name'] == args.device: + if args.json: + pprint.PrettyPrinter().pprint(msg) + sys.exit(0) + msg.pop('header', None) + return msg + + print(f"Not supported for device {args.device}") + sys.exit(1) + +def bits_to_dict(attr): + """ + Convert ynl-formatted bitmask to a dict of bit=value. + """ + ret = {} + if 'bits' not in attr: + return dict() + if 'bit' not in attr['bits']: + return dict() + for bit in attr['bits']['bit']: + if bit['name'] == '': + continue + name = bit['name'] + value = bit.get('value', False) + ret[name] = value + return ret + +def main(): + parser = argparse.ArgumentParser(description='ethtool wannabe') + parser.add_argument('--json', action=argparse.BooleanOptionalAction) + parser.add_argument('--show-priv-flags', action=argparse.BooleanOptionalAction) + parser.add_argument('--set-priv-flags', action=argparse.BooleanOptionalAction) + parser.add_argument('--show-eee', action=argparse.BooleanOptionalAction) + parser.add_argument('--set-eee', action=argparse.BooleanOptionalAction) + parser.add_argument('-a', '--show-pause', action=argparse.BooleanOptionalAction) + parser.add_argument('-A', '--set-pause', action=argparse.BooleanOptionalAction) + parser.add_argument('-c', '--show-coalesce', action=argparse.BooleanOptionalAction) + parser.add_argument('-C', '--set-coalesce', action=argparse.BooleanOptionalAction) + parser.add_argument('-g', '--show-ring', action=argparse.BooleanOptionalAction) + parser.add_argument('-G', '--set-ring', action=argparse.BooleanOptionalAction) + parser.add_argument('-k', '--show-features', action=argparse.BooleanOptionalAction) + parser.add_argument('-K', '--set-features', action=argparse.BooleanOptionalAction) + parser.add_argument('-l', '--show-channels', action=argparse.BooleanOptionalAction) + parser.add_argument('-L', '--set-channels', action=argparse.BooleanOptionalAction) + parser.add_argument('-T', '--show-time-stamping', action=argparse.BooleanOptionalAction) + parser.add_argument('-S', '--statistics', action=argparse.BooleanOptionalAction) + # TODO: --show-tunnels tunnel-info-get + # TODO: --show-module module-get + # TODO: --get-plca-cfg plca-get + # TODO: --get-plca-status plca-get-status + # TODO: --show-mm mm-get + # TODO: --show-fec fec-get + # TODO: --dump-module-eerpom module-eeprom-get + # TODO: pse-get + # TODO: rss-get + parser.add_argument('device', metavar='device', type=str) + parser.add_argument('args', metavar='args', type=str, nargs='*') + global args + args = parser.parse_args() + + spec = '/usr/local/google/home/sdf/src/linux/Documentation/netlink/specs/ethtool.yaml' + schema = '/usr/local/google/home/sdf/src/linux/Documentation/netlink/genetlink-legacy.yaml' + + ynl = YnlFamily(spec, schema) + + if args.set_priv_flags: + # TODO: parse the bitmask + print("not implemented") + return + + if args.set_eee: + return doit(ynl, args, 'eee-set') + + if args.set_pause: + return doit(ynl, args, 'pause-set') + + if args.set_coalesce: + return doit(ynl, args, 'coalesce-set') + + if args.set_features: + # TODO: parse the bitmask + print("not implemented") + return + + if args.set_channels: + return doit(ynl, args, 'channels-set') + + if args.set_ring: + return doit(ynl, args, 'rings-set') + + if args.show_priv_flags: + flags = bits_to_dict(dumpit(ynl, args, 'privflags-get')['flags']) + print_field(flags) + return + + if args.show_eee: + eee = dumpit(ynl, args, 'eee-get') + ours = bits_to_dict(eee['modes-ours']) + peer = bits_to_dict(eee['modes-peer']) + + if 'enabled' in eee: + status = 'enabled' if eee['enabled'] else 'disabled' + if 'active' in eee and eee['active']: + status = status + ' - active' + else: + status = status + ' - inactive' + else: + status = 'not supported' + + print(f'EEE status: {status}') + print_field(eee, ('tx-lpi-timer', 'Tx LPI')) + print_speed('Advertised EEE link modes', ours) + print_speed('Link partner advertised EEE link modes', peer) + + return + + if args.show_pause: + print_field(dumpit(ynl, args, 'pause-get'), + ('autoneg', 'Autonegotiate', 'bool'), + ('rx', 'RX', 'bool'), + ('tx', 'TX', 'bool')) + return + + if args.show_coalesce: + print_field(dumpit(ynl, args, 'coalesce-get')) + return + + if args.show_features: + reply = dumpit(ynl, args, 'features-get') + available = bits_to_dict(reply['hw']) + requested = bits_to_dict(reply['wanted']).keys() + active = bits_to_dict(reply['active']).keys() + never_changed = bits_to_dict(reply['nochange']).keys() + + for f in sorted(available): + value = "off" + if f in active: + value = "on" + + fixed = "" + if f not in available or f in never_changed: + fixed = " [fixed]" + + req = "" + if f in requested: + if f in active: + req = " [requested on]" + else: + req = " [requested off]" + + print(f'{f}: {value}{fixed}{req}') + + return + + if args.show_channels: + reply = dumpit(ynl, args, 'channels-get') + print(f'Channel parameters for {args.device}:') + + print(f'Pre-set maximums:') + print_field(reply, + ('rx-max', 'RX'), + ('tx-max', 'TX'), + ('other-max', 'Other'), + ('combined-max', 'Combined')) + + print(f'Current hardware settings:') + print_field(reply, + ('rx-count', 'RX'), + ('tx-count', 'TX'), + ('other-count', 'Other'), + ('combined-count', 'Combined')) + + return + + if args.show_ring: + reply = dumpit(ynl, args, 'channels-get') + + print(f'Ring parameters for {args.device}:') + + print(f'Pre-set maximums:') + print_field(reply, + ('rx-max', 'RX'), + ('rx-mini-max', 'RX Mini'), + ('rx-jumbo-max', 'RX Jumbo'), + ('tx-max', 'TX')) + + print(f'Current hardware settings:') + print_field(reply, + ('rx', 'RX'), + ('rx-mini', 'RX Mini'), + ('rx-jumbo', 'RX Jumbo'), + ('tx', 'TX')) + + print_field(reply, + ('rx-buf-len', 'RX Buf Len'), + ('cqe-size', 'CQE Size'), + ('tx-push', 'TX Push', 'bool')) + + return + + if args.statistics: + print(f'NIC statistics:') + + # TODO: pass id? + strset = dumpit(ynl, args, 'strset-get') + pprint.PrettyPrinter().pprint(strset) + + req = { + 'groups': { + 'size': 1, + 'bits': { + 'bit': + # TODO: support passing the bitmask + #[ + #{ 'name': 'eth-phy', 'value': True }, + { 'name': 'eth-mac', 'value': True }, + #{ 'name': 'eth-ctrl', 'value': True }, + #{ 'name': 'rmon', 'value': True }, + #], + }, + }, + } + + rsp = dumpit(ynl, args, 'stats-get', req) + pprint.PrettyPrinter().pprint(rsp) + return + + if args.show_time_stamping: + tsinfo = dumpit(ynl, args, 'tsinfo-get') + + print(f'Time stamping parameters for {args.device}:') + + print('Capabilities:') + [print(f'\t{v}') for v in bits_to_dict(tsinfo['timestamping'])] + + print(f'PTP Hardware Clock: {tsinfo["phc-index"]}') + + print('Hardware Transmit Timestamp Modes:') + [print(f'\t{v}') for v in bits_to_dict(tsinfo['tx-types'])] + + print('Hardware Receive Filter Modes:') + [print(f'\t{v}') for v in bits_to_dict(tsinfo['rx-filters'])] + return + + print(f'Settings for {args.device}:') + linkmodes = dumpit(ynl, args, 'linkmodes-get') + ours = bits_to_dict(linkmodes['ours']) + + supported_ports = ('TP', 'AUI', 'BNC', 'MII', 'FIBRE', 'Backplane') + ports = [ p for p in supported_ports if ours.get(p, False)] + print(f'Supported ports: [ {" ".join(ports)} ]') + + print_speed('Supported link modes', ours) + + print_field(ours, ('Pause', 'Supported pause frame use', 'yn')) + print_field(ours, ('Autoneg', 'Supports auto-negotiation', 'yn')) + + supported_fec = ('None', 'PS', 'BASER', 'LLRS') + fec = [ p for p in supported_fec if ours.get(p, False)] + fec_str = " ".join(fec) + if len(fec) == 0: + fec_str = "Not reported" + + print(f'Supported FEC modes: {fec_str}') + + speed = 'Unknown!' + if linkmodes['speed'] > 0 and linkmodes['speed'] < 0xffffffff: + speed = f'{linkmodes["speed"]}Mb/s' + print(f'Speed: {speed}') + + duplex_modes = { + 0: 'Half', + 1: 'Full', + } + duplex = duplex_modes.get(linkmodes["duplex"], None) + if not duplex: + duplex = f'Unknown! ({linkmodes["duplex"]})' + print(f'Duplex: {duplex}') + + autoneg = "off" + if linkmodes.get("autoneg", 0) != 0: + autoneg = "on" + print(f'Auto-negotiation: {autoneg}') + + ports = { + 0: 'Twisted Pair', + 1: 'AUI', + 2: 'MII', + 3: 'FIBRE', + 4: 'BNC', + 5: 'Directly Attached Copper', + 0xef: 'None', + } + linkinfo = dumpit(ynl, args, 'linkinfo-get') + print(f'Port: {ports.get(linkinfo["port"], "Other")}') + + print_field(linkinfo, ('phyaddr', 'PHYAD')) + + transceiver = { + 0: 'Internal', + 1: 'External', + } + print(f'Transceiver: {transceiver.get(linkinfo["transceiver"], "Unknown")}') + + mdix_ctrl = { + 1: 'off', + 2: 'on', + } + mdix = mdix_ctrl.get(linkinfo['tp-mdix-ctrl'], None) + if mdix: + mdix = mdix + ' (forced)' + else: + mdix = mdix_ctrl.get(linkinfo['tp-mdix'], 'Unknown (auto)') + print(f'MDI-X: {mdix}') + + debug = dumpit(ynl, args, 'debug-get') + msgmask = bits_to_dict(debug.get("msgmask", [])).keys() + print(f'Current message level: {" ".join(msgmask)}') + + linkstate = dumpit(ynl, args, 'linkstate-get') + detected_states = { + 0: 'no', + 1: 'yes', + } + # TODO: wol-get + detected = detected_states.get(linkstate['link'], 'unknown') + print(f'Link detected: {detected}') + +if __name__ == '__main__': + main() diff --git a/tools/net/ynl/lib/nlspec.py b/tools/net/ynl/lib/nlspec.py index 0c43022ff7e6..a0241add3839 100644 --- a/tools/net/ynl/lib/nlspec.py +++ b/tools/net/ynl/lib/nlspec.py @@ -444,6 +444,15 @@ class SpecFamily(SpecElement): self.msgs[op.name] = op + def find_operation(self, name): + """ + For a given operation name, find and return operation spec. + """ + for op in self.yaml['operations']['list']: + if name == op['name']: + return op + return None + def resolve(self): self.resolve_up(super()) diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 373c0edb5f83..7690e0b0cb3f 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -527,6 +527,17 @@ class YnlFamily(SpecFamily): self.handle_ntf(nl_msg, gm) + def operation_do_attributes(self, name): + """ + For a given operation name, find and return a supported + set of attributes (as a dict). + """ + op = self.find_operation(name) + if not op: + return None + + return op['do']['request']['attributes'].copy() + def _op(self, method, vals, dump=False): op = self.ops[method]