Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ethtool-rss-report-which-fields-are-configured-for-hashing'

Jakub Kicinski says:

====================
ethtool: rss: report which fields are configured for hashing

Add support for reading flow hash configuration via Netlink ethtool.

$ ynl --family ethtool --dump rss-get
[{
"header": {
"dev-index": 1,
"dev-name": "enp1s0"
},
"hfunc": 1,
"hkey": b"...",
"indir": [0, 1, ...],
"flow-hash": {
"ether": {"l2da"},
"ah-esp4": {"ip-src", "ip-dst"},
"ah-esp6": {"ip-src", "ip-dst"},
"ah4": {"ip-src", "ip-dst"},
"ah6": {"ip-src", "ip-dst"},
"esp4": {"ip-src", "ip-dst"},
"esp6": {"ip-src", "ip-dst"},
"ip4": {"ip-src", "ip-dst"},
"ip6": {"ip-src", "ip-dst"},
"sctp4": {"ip-src", "ip-dst"},
"sctp6": {"ip-src", "ip-dst"},
"udp4": {"ip-src", "ip-dst"},
"udp6": {"ip-src", "ip-dst"}
"tcp4": {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"},
"tcp6": {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"},
},
}]
====================

Link: https://patch.msgid.link/20250708220640.2738464-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+364 -35
+151
Documentation/netlink/specs/ethtool.yaml
··· 158 158 - 159 159 name: pse-event-sw-pw-control-error 160 160 doc: PSE faced an error managing the power control from software 161 + - 162 + name: rxfh-fields 163 + name-prefix: rxh- 164 + enum-name: 165 + header: linux/ethtool.h 166 + type: flags 167 + entries: 168 + - 169 + name: l2da 170 + value: 1 171 + - 172 + name: vlan 173 + - 174 + name: l3-proto 175 + - 176 + name: ip-src 177 + - 178 + name: ip-dst 179 + - 180 + name: l4-b-0-1 181 + doc: src port in case of TCP/UDP/SCTP 182 + - 183 + name: l4-b-2-3 184 + doc: dst port in case of TCP/UDP/SCTP 185 + - 186 + name: gtp-teid 187 + - 188 + name: discard 189 + value: 31 161 190 162 191 attribute-sets: 163 192 - ··· 1477 1448 type: u32 1478 1449 name-prefix: ethtool-a- 1479 1450 - 1451 + name: flow 1452 + attr-cnt-name: --ethtool-a-flow-cnt 1453 + doc: | 1454 + Flow types, corresponding to those defined in the old 1455 + ethtool header for RXFH and RXNFC as ${PROTO}_FLOW. 1456 + The values are not matching the old ones to avoid carrying 1457 + into Netlink the IP_USER_FLOW vs IPV4_FLOW vs IPV4_USER_FLOW confusion. 1458 + attributes: 1459 + - 1460 + name: ether 1461 + type: uint 1462 + enum: rxfh-fields 1463 + - 1464 + name: ip4 1465 + type: uint 1466 + enum: rxfh-fields 1467 + - 1468 + name: ip6 1469 + type: uint 1470 + enum: rxfh-fields 1471 + - 1472 + name: tcp4 1473 + type: uint 1474 + enum: rxfh-fields 1475 + - 1476 + name: tcp6 1477 + type: uint 1478 + enum: rxfh-fields 1479 + - 1480 + name: udp4 1481 + type: uint 1482 + enum: rxfh-fields 1483 + - 1484 + name: udp6 1485 + type: uint 1486 + enum: rxfh-fields 1487 + - 1488 + name: sctp4 1489 + type: uint 1490 + enum: rxfh-fields 1491 + - 1492 + name: sctp6 1493 + type: uint 1494 + enum: rxfh-fields 1495 + - 1496 + name: ah4 1497 + type: uint 1498 + enum: rxfh-fields 1499 + - 1500 + name: ah6 1501 + type: uint 1502 + enum: rxfh-fields 1503 + - 1504 + name: esp4 1505 + type: uint 1506 + enum: rxfh-fields 1507 + - 1508 + name: esp6 1509 + type: uint 1510 + enum: rxfh-fields 1511 + - 1512 + name: ah-esp4 1513 + type: uint 1514 + enum: rxfh-fields 1515 + - 1516 + name: ah-esp6 1517 + type: uint 1518 + enum: rxfh-fields 1519 + - 1520 + name: gtpu4 1521 + type: uint 1522 + enum: rxfh-fields 1523 + - 1524 + name: gtpu6 1525 + type: uint 1526 + enum: rxfh-fields 1527 + - 1528 + name: gtpc4 1529 + type: uint 1530 + enum: rxfh-fields 1531 + - 1532 + name: gtpc6 1533 + type: uint 1534 + enum: rxfh-fields 1535 + - 1536 + name: gtpc-teid4 1537 + type: uint 1538 + enum: rxfh-fields 1539 + - 1540 + name: gtpc-teid6 1541 + type: uint 1542 + enum: rxfh-fields 1543 + - 1544 + name: gtpu-eh4 1545 + type: uint 1546 + enum: rxfh-fields 1547 + - 1548 + name: gtpu-eh6 1549 + type: uint 1550 + enum: rxfh-fields 1551 + - 1552 + name: gtpu-ul4 1553 + type: uint 1554 + enum: rxfh-fields 1555 + - 1556 + name: gtpu-ul6 1557 + type: uint 1558 + enum: rxfh-fields 1559 + - 1560 + name: gtpu-dl4 1561 + type: uint 1562 + enum: rxfh-fields 1563 + - 1564 + name: gtpu-dl6 1565 + type: uint 1566 + enum: rxfh-fields 1567 + - 1480 1568 name: rss 1481 1569 attr-cnt-name: __ethtool-a-rss-cnt 1482 1570 attributes: ··· 1624 1478 - 1625 1479 name: start-context 1626 1480 type: u32 1481 + - 1482 + name: flow-hash 1483 + type: nest 1484 + nested-attributes: flow 1627 1485 - 1628 1486 name: plca 1629 1487 attr-cnt-name: __ethtool-a-plca-cnt ··· 2457 2307 - indir 2458 2308 - hkey 2459 2309 - input-xfrm 2310 + - flow-hash 2460 2311 dump: 2461 2312 request: 2462 2313 attributes:
+6 -3
Documentation/networking/ethtool-netlink.rst
··· 1969 1969 1970 1970 Kernel response contents: 1971 1971 1972 - ===================================== ====== ========================== 1972 + ===================================== ====== =============================== 1973 1973 ``ETHTOOL_A_RSS_HEADER`` nested reply header 1974 1974 ``ETHTOOL_A_RSS_CONTEXT`` u32 context number 1975 1975 ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func 1976 1976 ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes 1977 1977 ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes 1978 1978 ``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation 1979 - ===================================== ====== ========================== 1979 + ``ETHTOOL_A_RSS_FLOW_HASH`` nested Header fields included in hash 1980 + ===================================== ====== =============================== 1980 1981 1981 1982 ETHTOOL_A_RSS_HFUNC attribute is bitmap indicating the hash function 1982 1983 being used. Current supported options are toeplitz, xor or crc32. ··· 1986 1985 ETHTOOL_A_RSS_INPUT_XFRM attribute is a bitmap indicating the type of 1987 1986 transformation applied to the input protocol fields before given to the RSS 1988 1987 hfunc. Current supported options are symmetric-xor and symmetric-or-xor. 1988 + ETHTOOL_A_RSS_FLOW_HASH carries per-flow type bitmask of which header 1989 + fields are included in the hash calculation. 1989 1990 1990 1991 PLCA_GET_CFG 1991 1992 ============ ··· 2439 2436 ``ETHTOOL_SFLAGS`` ``ETHTOOL_MSG_FEATURES_SET`` 2440 2437 ``ETHTOOL_GPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_GET`` 2441 2438 ``ETHTOOL_SPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_SET`` 2442 - ``ETHTOOL_GRXFH`` n/a 2439 + ``ETHTOOL_GRXFH`` ``ETHTOOL_MSG_RSS_GET`` 2443 2440 ``ETHTOOL_SRXFH`` n/a 2444 2441 ``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET`` 2445 2442 ``ETHTOOL_SGRO`` ``ETHTOOL_MSG_FEATURES_SET``
+2 -2
include/uapi/linux/ethtool.h
··· 2314 2314 IPV6_USER_FLOW = 0x0e, /* spec only (usr_ip6_spec; nfc only) */ 2315 2315 IPV4_FLOW = 0x10, /* hash only */ 2316 2316 IPV6_FLOW = 0x11, /* hash only */ 2317 - ETHER_FLOW = 0x12, /* spec only (ether_spec) */ 2317 + ETHER_FLOW = 0x12, /* hash or spec (ether_spec) */ 2318 2318 2319 2319 /* Used for GTP-U IPv4 and IPv6. 2320 2320 * The format of GTP packets only includes ··· 2371 2371 /* Flag to enable RSS spreading of traffic matching rule (nfc only) */ 2372 2372 #define FLOW_RSS 0x20000000 2373 2373 2374 - /* L3-L4 network traffic flow hash options */ 2374 + /* L2-L4 network traffic flow hash options */ 2375 2375 #define RXH_L2DA (1 << 1) 2376 2376 #define RXH_VLAN (1 << 2) 2377 2377 #define RXH_L3_PROTO (1 << 3)
+6 -1
net/ethtool/ioctl.c
··· 981 981 static bool flow_type_hashable(u32 flow_type) 982 982 { 983 983 switch (flow_type) { 984 + case ETHER_FLOW: 984 985 case TCP_V4_FLOW: 985 986 case UDP_V4_FLOW: 986 987 case SCTP_V4_FLOW: ··· 1101 1100 rc = ops->set_rxfh_fields(dev, &fields, NULL); 1102 1101 exit_unlock: 1103 1102 mutex_unlock(&dev->ethtool->rss_lock); 1104 - return rc; 1103 + if (rc) 1104 + return rc; 1105 + 1106 + ethtool_rss_notify(dev, fields.rss_context); 1107 + return 0; 1105 1108 } 1106 1109 1107 1110 static noinline_for_stack int
+116 -29
net/ethtool/rss.c
··· 12 12 13 13 struct rss_reply_data { 14 14 struct ethnl_reply_data base; 15 + bool has_flow_hash; 15 16 bool no_key_fields; 16 17 u32 indir_size; 17 18 u32 hkey_size; ··· 20 19 u32 input_xfrm; 21 20 u32 *indir_table; 22 21 u8 *hkey; 22 + int flow_hash[__ETHTOOL_A_FLOW_CNT]; 23 + }; 24 + 25 + static const u8 ethtool_rxfh_ft_nl2ioctl[] = { 26 + [ETHTOOL_A_FLOW_ETHER] = ETHER_FLOW, 27 + [ETHTOOL_A_FLOW_IP4] = IPV4_FLOW, 28 + [ETHTOOL_A_FLOW_IP6] = IPV6_FLOW, 29 + [ETHTOOL_A_FLOW_TCP4] = TCP_V4_FLOW, 30 + [ETHTOOL_A_FLOW_UDP4] = UDP_V4_FLOW, 31 + [ETHTOOL_A_FLOW_SCTP4] = SCTP_V4_FLOW, 32 + [ETHTOOL_A_FLOW_AH_ESP4] = AH_ESP_V4_FLOW, 33 + [ETHTOOL_A_FLOW_TCP6] = TCP_V6_FLOW, 34 + [ETHTOOL_A_FLOW_UDP6] = UDP_V6_FLOW, 35 + [ETHTOOL_A_FLOW_SCTP6] = SCTP_V6_FLOW, 36 + [ETHTOOL_A_FLOW_AH_ESP6] = AH_ESP_V6_FLOW, 37 + [ETHTOOL_A_FLOW_AH4] = AH_V4_FLOW, 38 + [ETHTOOL_A_FLOW_ESP4] = ESP_V4_FLOW, 39 + [ETHTOOL_A_FLOW_AH6] = AH_V6_FLOW, 40 + [ETHTOOL_A_FLOW_ESP6] = ESP_V6_FLOW, 41 + [ETHTOOL_A_FLOW_GTPU4] = GTPU_V4_FLOW, 42 + [ETHTOOL_A_FLOW_GTPU6] = GTPU_V6_FLOW, 43 + [ETHTOOL_A_FLOW_GTPC4] = GTPC_V4_FLOW, 44 + [ETHTOOL_A_FLOW_GTPC6] = GTPC_V6_FLOW, 45 + [ETHTOOL_A_FLOW_GTPC_TEID4] = GTPC_TEID_V4_FLOW, 46 + [ETHTOOL_A_FLOW_GTPC_TEID6] = GTPC_TEID_V6_FLOW, 47 + [ETHTOOL_A_FLOW_GTPU_EH4] = GTPU_EH_V4_FLOW, 48 + [ETHTOOL_A_FLOW_GTPU_EH6] = GTPU_EH_V6_FLOW, 49 + [ETHTOOL_A_FLOW_GTPU_UL4] = GTPU_UL_V4_FLOW, 50 + [ETHTOOL_A_FLOW_GTPU_UL6] = GTPU_UL_V6_FLOW, 51 + [ETHTOOL_A_FLOW_GTPU_DL4] = GTPU_DL_V4_FLOW, 52 + [ETHTOOL_A_FLOW_GTPU_DL6] = GTPU_DL_V6_FLOW, 23 53 }; 24 54 25 55 #define RSS_REQINFO(__req_base) \ ··· 81 49 return 0; 82 50 } 83 51 52 + static void 53 + rss_prepare_flow_hash(const struct rss_req_info *req, struct net_device *dev, 54 + struct rss_reply_data *data, const struct genl_info *info) 55 + { 56 + int i; 57 + 58 + data->has_flow_hash = false; 59 + 60 + if (!dev->ethtool_ops->get_rxfh_fields) 61 + return; 62 + if (req->rss_context && !dev->ethtool_ops->rxfh_per_ctx_fields) 63 + return; 64 + 65 + mutex_lock(&dev->ethtool->rss_lock); 66 + for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) { 67 + struct ethtool_rxfh_fields fields = { 68 + .flow_type = ethtool_rxfh_ft_nl2ioctl[i], 69 + .rss_context = req->rss_context, 70 + }; 71 + 72 + if (dev->ethtool_ops->get_rxfh_fields(dev, &fields)) { 73 + data->flow_hash[i] = -1; /* Unsupported */ 74 + continue; 75 + } 76 + 77 + data->flow_hash[i] = fields.data; 78 + data->has_flow_hash = true; 79 + } 80 + mutex_unlock(&dev->ethtool->rss_lock); 81 + } 82 + 84 83 static int 85 84 rss_prepare_get(const struct rss_req_info *request, struct net_device *dev, 86 85 struct rss_reply_data *data, const struct genl_info *info) ··· 127 64 ret = ethnl_ops_begin(dev); 128 65 if (ret < 0) 129 66 return ret; 67 + mutex_lock(&dev->ethtool->rss_lock); 130 68 131 69 data->indir_size = 0; 132 70 data->hkey_size = 0; ··· 141 77 rss_config = kzalloc(total_size, GFP_KERNEL); 142 78 if (!rss_config) { 143 79 ret = -ENOMEM; 144 - goto out_ops; 80 + goto out_unlock; 145 81 } 146 82 147 83 if (data->indir_size) ··· 156 92 157 93 ret = ops->get_rxfh(dev, &rxfh); 158 94 if (ret) 159 - goto out_ops; 95 + goto out_unlock; 160 96 161 97 data->hfunc = rxfh.hfunc; 162 98 data->input_xfrm = rxfh.input_xfrm; 163 - out_ops: 99 + out_unlock: 100 + mutex_unlock(&dev->ethtool->rss_lock); 164 101 ethnl_ops_complete(dev); 165 102 return ret; 166 103 } ··· 173 108 struct ethtool_rxfh_context *ctx; 174 109 u32 total_size, indir_bytes; 175 110 u8 *rss_config; 111 + int ret; 176 112 177 113 data->no_key_fields = !dev->ethtool_ops->rxfh_per_ctx_key; 178 114 115 + mutex_lock(&dev->ethtool->rss_lock); 179 116 ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context); 180 - if (!ctx) 181 - return -ENOENT; 117 + if (!ctx) { 118 + ret = -ENOENT; 119 + goto out_unlock; 120 + } 182 121 183 122 data->indir_size = ctx->indir_size; 184 123 data->hkey_size = ctx->key_size; ··· 192 123 indir_bytes = data->indir_size * sizeof(u32); 193 124 total_size = indir_bytes + data->hkey_size; 194 125 rss_config = kzalloc(total_size, GFP_KERNEL); 195 - if (!rss_config) 196 - return -ENOMEM; 126 + if (!rss_config) { 127 + ret = -ENOMEM; 128 + goto out_unlock; 129 + } 197 130 198 131 data->indir_table = (u32 *)rss_config; 199 132 memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx), indir_bytes); ··· 206 135 data->hkey_size); 207 136 } 208 137 209 - return 0; 138 + ret = 0; 139 + out_unlock: 140 + mutex_unlock(&dev->ethtool->rss_lock); 141 + return ret; 210 142 } 211 143 212 144 static int 213 145 rss_prepare(const struct rss_req_info *request, struct net_device *dev, 214 146 struct rss_reply_data *data, const struct genl_info *info) 215 147 { 148 + rss_prepare_flow_hash(request, dev, data, info); 149 + 216 150 if (request->rss_context) 217 151 return rss_prepare_ctx(request, dev, data, info); 218 152 return rss_prepare_get(request, dev, data, info); ··· 232 156 struct rss_req_info *request = RSS_REQINFO(req_base); 233 157 struct net_device *dev = reply_base->dev; 234 158 const struct ethtool_ops *ops; 235 - int ret; 236 159 237 160 ops = dev->ethtool_ops; 238 161 if (!ops->get_rxfh) ··· 241 166 if (request->rss_context && !ops->create_rxfh_context) 242 167 return -EOPNOTSUPP; 243 168 244 - mutex_lock(&dev->ethtool->rss_lock); 245 - ret = rss_prepare(request, dev, data, info); 246 - mutex_unlock(&dev->ethtool->rss_lock); 247 - 248 - return ret; 169 + return rss_prepare(request, dev, data, info); 249 170 } 250 171 251 172 static int ··· 255 184 nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ 256 185 nla_total_size(sizeof(u32)) + /* _RSS_INPUT_XFRM */ 257 186 nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ 258 - nla_total_size(data->hkey_size); /* _RSS_HKEY */ 187 + nla_total_size(data->hkey_size) + /* _RSS_HKEY */ 188 + nla_total_size(0) + /* _RSS_FLOW_HASH */ 189 + nla_total_size(sizeof(u32)) * ETHTOOL_A_FLOW_MAX + 190 + 0; 259 191 260 192 return len; 261 193 } ··· 279 205 sizeof(u32) * data->indir_size, data->indir_table))) 280 206 return -EMSGSIZE; 281 207 282 - if (data->no_key_fields) 283 - return 0; 284 - 285 - if ((data->hfunc && 286 - nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || 287 - (data->input_xfrm && 288 - nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) || 289 - (data->hkey_size && 290 - nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))) 208 + if (!data->no_key_fields && 209 + ((data->hfunc && 210 + nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || 211 + (data->input_xfrm && 212 + nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) || 213 + (data->hkey_size && 214 + nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey)))) 291 215 return -EMSGSIZE; 216 + 217 + if (data->has_flow_hash) { 218 + struct nlattr *nest; 219 + int i; 220 + 221 + nest = nla_nest_start(skb, ETHTOOL_A_RSS_FLOW_HASH); 222 + if (!nest) 223 + return -EMSGSIZE; 224 + 225 + for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) { 226 + if (data->flow_hash[i] >= 0 && 227 + nla_put_uint(skb, i, data->flow_hash[i])) { 228 + nla_nest_cancel(skb, nest); 229 + return -EMSGSIZE; 230 + } 231 + } 232 + 233 + nla_nest_end(skb, nest); 234 + } 292 235 293 236 return 0; 294 237 } ··· 385 294 if (ret < 0) 386 295 goto err_cancel; 387 296 388 - /* Context 0 is not currently storred or cached in the XArray */ 389 - if (!rss_context) 390 - ret = rss_prepare_get(&req, dev, &data, info); 391 - else 392 - ret = rss_prepare_ctx(&req, dev, &data, info); 297 + ret = rss_prepare(&req, dev, &data, info); 393 298 if (ret) 394 299 goto err_cancel; 395 300
+2
tools/net/ynl/pyynl/lib/ynl.py
··· 762 762 decoded = True 763 763 elif attr_spec.is_auto_scalar: 764 764 decoded = attr.as_auto_scalar(attr_spec['type'], attr_spec.byte_order) 765 + if 'enum' in attr_spec: 766 + decoded = self._decode_enum(decoded, attr_spec) 765 767 elif attr_spec["type"] in NlAttr.type_formats: 766 768 decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order) 767 769 if 'enum' in attr_spec:
+47
tools/testing/selftests/drivers/net/hw/rss_api.py
··· 20 20 return int(output.split()[-1]) 21 21 22 22 23 + def _ethtool_get_cfg(cfg, fl_type, to_nl=False): 24 + descr = ethtool(f"-n {cfg.ifname} rx-flow-hash {fl_type}").stdout 25 + 26 + if to_nl: 27 + converter = { 28 + "IP SA": "ip-src", 29 + "IP DA": "ip-dst", 30 + "L4 bytes 0 & 1 [TCP/UDP src port]": "l4-b-0-1", 31 + "L4 bytes 2 & 3 [TCP/UDP dst port]": "l4-b-2-3", 32 + } 33 + 34 + ret = set() 35 + else: 36 + converter = { 37 + "IP SA": "s", 38 + "IP DA": "d", 39 + "L3 proto": "t", 40 + "L4 bytes 0 & 1 [TCP/UDP src port]": "f", 41 + "L4 bytes 2 & 3 [TCP/UDP dst port]": "n", 42 + } 43 + 44 + ret = "" 45 + 46 + for line in descr.split("\n")[1:-2]: 47 + # if this raises we probably need to add more keys to converter above 48 + if to_nl: 49 + ret.add(converter[line]) 50 + else: 51 + ret += converter[line] 52 + return ret 53 + 54 + 23 55 def test_rxfh_indir_ntf(cfg): 24 56 """ 25 57 Check that Netlink notifications are generated when RSS indirection ··· 107 75 ksft_eq(ntf["name"], "rss-ntf") 108 76 ksft_eq(ntf["msg"].get("context"), ctx_id) 109 77 ksft_eq(set(ntf["msg"]["indir"]), {1}) 78 + 79 + 80 + def test_rxfh_fields(cfg): 81 + """ 82 + Test reading Rx Flow Hash over Netlink. 83 + """ 84 + 85 + flow_types = ["tcp4", "tcp6", "udp4", "udp6"] 86 + ethnl = EthtoolFamily() 87 + 88 + cfg_nl = ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 89 + for fl_type in flow_types: 90 + one = _ethtool_get_cfg(cfg, fl_type, to_nl=True) 91 + ksft_eq(one, cfg_nl["flow-hash"][fl_type], 92 + comment="Config for " + fl_type) 110 93 111 94 112 95 def main() -> None: