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-support-rss_set-via-netlink'

Jakub Kicinski says:

====================
ethtool: rss: support RSS_SET via Netlink

Support configuring RSS settings via Netlink.
Creating and removing contexts remains for the following series.

v2: https://lore.kernel.org/20250714222729.743282-1-kuba@kernel.org
v1: https://lore.kernel.org/20250711015303.3688717-1-kuba@kernel.org
====================

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

+767 -20
+37
Documentation/netlink/specs/ethtool.yaml
··· 159 159 name: pse-event-sw-pw-control-error 160 160 doc: PSE faced an error managing the power control from software 161 161 - 162 + name: input-xfrm 163 + doc: RSS hash function transformations. 164 + type: flags 165 + enum-name: 166 + name-prefix: rxh-xfrm- 167 + header: linux/ethtool.h 168 + entries: 169 + - 170 + name: sym-xor 171 + doc: >- 172 + XOR the corresponding source and destination fields of each specified 173 + protocol. Both copies of the XOR'ed fields are fed into the RSS and 174 + RXHASH calculation. Note that this XORing reduces the input set 175 + entropy and could be exploited to reduce the RSS queue spread. 176 + - 177 + name: sym-or-xor 178 + doc: >- 179 + Similar to SYM_XOR, except that one copy of the XOR'ed fields is 180 + replaced by an OR of the same fields. 181 + - 162 182 name: rxfh-fields 163 183 name-prefix: rxh- 164 184 enum-name: ··· 1641 1621 - 1642 1622 name: input-xfrm 1643 1623 type: u32 1624 + enum: input-xfrm 1644 1625 - 1645 1626 name: start-context 1646 1627 type: u32 ··· 2664 2643 attributes: 2665 2644 - header 2666 2645 - events 2646 + - 2647 + name: rss-set 2648 + doc: Set RSS params. 2649 + 2650 + attribute-set: rss 2651 + 2652 + do: 2653 + request: 2654 + attributes: 2655 + - header 2656 + - context 2657 + - hfunc 2658 + - indir 2659 + - hkey 2660 + - input-xfrm 2661 + - flow-hash 2667 2662 - 2668 2663 name: rss-ntf 2669 2664 doc: |
+27 -2
Documentation/networking/ethtool-netlink.rst
··· 239 239 ``ETHTOOL_MSG_PHY_GET`` get Ethernet PHY information 240 240 ``ETHTOOL_MSG_TSCONFIG_GET`` get hw timestamping configuration 241 241 ``ETHTOOL_MSG_TSCONFIG_SET`` set hw timestamping configuration 242 + ``ETHTOOL_MSG_RSS_SET`` set RSS settings 242 243 ===================================== ================================= 243 244 244 245 Kernel to userspace: ··· 293 292 ``ETHTOOL_MSG_TSCONFIG_GET_REPLY`` hw timestamping configuration 294 293 ``ETHTOOL_MSG_TSCONFIG_SET_REPLY`` new hw timestamping configuration 295 294 ``ETHTOOL_MSG_PSE_NTF`` PSE events notification 295 + ``ETHTOOL_MSG_RSS_NTF`` RSS settings notification 296 296 ======================================== ================================= 297 297 298 298 ``GET`` requests are sent by userspace applications to retrieve device ··· 1991 1989 ETHTOOL_A_RSS_FLOW_HASH carries per-flow type bitmask of which header 1992 1990 fields are included in the hash calculation. 1993 1991 1992 + RSS_SET 1993 + ======= 1994 + 1995 + Request contents: 1996 + 1997 + ===================================== ====== ============================== 1998 + ``ETHTOOL_A_RSS_HEADER`` nested request header 1999 + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number 2000 + ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func 2001 + ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes 2002 + ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes 2003 + ``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation 2004 + ``ETHTOOL_A_RSS_FLOW_HASH`` nested Header fields included in hash 2005 + ===================================== ====== ============================== 2006 + 2007 + ``ETHTOOL_A_RSS_INDIR`` is the minimal RSS table the user expects. Kernel and 2008 + the device driver may replicate the table if its smaller than smallest table 2009 + size supported by the device. For example if user requests ``[0, 1]`` but the 2010 + device needs at least 8 entries - the real table in use will end up being 2011 + ``[0, 1, 0, 1, 0, 1, 0, 1]``. Most devices require the table size to be power 2012 + of 2, so tables which size is not a power of 2 will likely be rejected. 2013 + Using table of size 0 will reset the indirection table to the default. 2014 + 1994 2015 PLCA_GET_CFG 1995 2016 ============ 1996 2017 ··· 2465 2440 ``ETHTOOL_GPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_GET`` 2466 2441 ``ETHTOOL_SPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_SET`` 2467 2442 ``ETHTOOL_GRXFH`` ``ETHTOOL_MSG_RSS_GET`` 2468 - ``ETHTOOL_SRXFH`` n/a 2443 + ``ETHTOOL_SRXFH`` ``ETHTOOL_MSG_RSS_SET`` 2469 2444 ``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET`` 2470 2445 ``ETHTOOL_SGRO`` ``ETHTOOL_MSG_FEATURES_SET`` 2471 2446 ``ETHTOOL_GRXRINGS`` n/a ··· 2480 2455 ``ETHTOOL_GRXNTUPLE`` n/a 2481 2456 ``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET`` 2482 2457 ``ETHTOOL_GRXFHINDIR`` ``ETHTOOL_MSG_RSS_GET`` 2483 - ``ETHTOOL_SRXFHINDIR`` n/a 2458 + ``ETHTOOL_SRXFHINDIR`` ``ETHTOOL_MSG_RSS_SET`` 2484 2459 ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET`` 2485 2460 ``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET`` 2486 2461 ``ETHTOOL_GCHANNELS`` ``ETHTOOL_MSG_CHANNELS_GET``
+15
net/ethtool/common.c
··· 806 806 return rc; 807 807 } 808 808 809 + /* Check if fields configured for flow hash are symmetric - if src is included 810 + * so is dst and vice versa. 811 + */ 812 + int ethtool_rxfh_config_is_sym(u64 rxfh) 813 + { 814 + bool sym; 815 + 816 + sym = rxfh == (rxfh & (RXH_IP_SRC | RXH_IP_DST | 817 + RXH_L4_B_0_1 | RXH_L4_B_2_3)); 818 + sym &= !!(rxfh & RXH_IP_SRC) == !!(rxfh & RXH_IP_DST); 819 + sym &= !!(rxfh & RXH_L4_B_0_1) == !!(rxfh & RXH_L4_B_2_3); 820 + 821 + return sym; 822 + } 823 + 809 824 int ethtool_check_ops(const struct ethtool_ops *ops) 810 825 { 811 826 if (WARN_ON(ops->set_coalesce && !ops->supported_coalesce_params))
+1
net/ethtool/common.h
··· 44 44 struct ethtool_channels channels, 45 45 struct genl_info *info); 46 46 int ethtool_check_rss_ctx_busy(struct net_device *dev, u32 rss_context); 47 + int ethtool_rxfh_config_is_sym(u64 rxfh); 47 48 48 49 void ethtool_ringparam_get_cfg(struct net_device *dev, 49 50 struct ethtool_ringparam *param,
+1 -3
net/ethtool/ioctl.c
··· 1027 1027 */ 1028 1028 if ((input_xfrm != RXH_XFRM_NO_CHANGE && 1029 1029 input_xfrm & (RXH_XFRM_SYM_XOR | RXH_XFRM_SYM_OR_XOR)) && 1030 - ((rxfh & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) || 1031 - (!!(rxfh & RXH_IP_SRC) ^ !!(rxfh & RXH_IP_DST)) || 1032 - (!!(rxfh & RXH_L4_B_0_1) ^ !!(rxfh & RXH_L4_B_2_3)))) 1030 + !ethtool_rxfh_config_is_sym(rxfh)) 1033 1031 return -EINVAL; 1034 1032 1035 1033 return 0;
+8
net/ethtool/netlink.c
··· 405 405 [ETHTOOL_MSG_PSE_GET] = &ethnl_pse_request_ops, 406 406 [ETHTOOL_MSG_PSE_SET] = &ethnl_pse_request_ops, 407 407 [ETHTOOL_MSG_RSS_GET] = &ethnl_rss_request_ops, 408 + [ETHTOOL_MSG_RSS_SET] = &ethnl_rss_request_ops, 408 409 [ETHTOOL_MSG_PLCA_GET_CFG] = &ethnl_plca_cfg_request_ops, 409 410 [ETHTOOL_MSG_PLCA_SET_CFG] = &ethnl_plca_cfg_request_ops, 410 411 [ETHTOOL_MSG_PLCA_GET_STATUS] = &ethnl_plca_status_request_ops, ··· 1504 1503 .doit = ethnl_default_set_doit, 1505 1504 .policy = ethnl_tsconfig_set_policy, 1506 1505 .maxattr = ARRAY_SIZE(ethnl_tsconfig_set_policy) - 1, 1506 + }, 1507 + { 1508 + .cmd = ETHTOOL_MSG_RSS_SET, 1509 + .flags = GENL_UNS_ADMIN_PERM, 1510 + .doit = ethnl_default_set_doit, 1511 + .policy = ethnl_rss_set_policy, 1512 + .maxattr = ARRAY_SIZE(ethnl_rss_set_policy) - 1, 1507 1513 }, 1508 1514 }; 1509 1515
+1
net/ethtool/netlink.h
··· 484 484 extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1]; 485 485 extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1]; 486 486 extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_START_CONTEXT + 1]; 487 + extern const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1]; 487 488 extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1]; 488 489 extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1]; 489 490 extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1];
+389
net/ethtool/rss.c
··· 218 218 { 219 219 rss_prepare_flow_hash(request, dev, data, info); 220 220 221 + /* Coming from RSS_SET, driver may only have flow_hash_fields ops */ 222 + if (!dev->ethtool_ops->get_rxfh) 223 + return 0; 224 + 221 225 if (request->rss_context) 222 226 return rss_prepare_ctx(request, dev, data, info); 223 227 return rss_prepare_get(request, dev, data, info); ··· 470 466 ethnl_notify(dev, ETHTOOL_MSG_RSS_NTF, &req_info.base); 471 467 } 472 468 469 + /* RSS_SET */ 470 + 471 + #define RFH_MASK (RXH_L2DA | RXH_VLAN | RXH_IP_SRC | RXH_IP_DST | \ 472 + RXH_L3_PROTO | RXH_L4_B_0_1 | RXH_L4_B_2_3 | \ 473 + RXH_GTP_TEID | RXH_DISCARD) 474 + 475 + static const struct nla_policy ethnl_rss_flows_policy[] = { 476 + [ETHTOOL_A_FLOW_ETHER] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 477 + [ETHTOOL_A_FLOW_IP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 478 + [ETHTOOL_A_FLOW_IP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 479 + [ETHTOOL_A_FLOW_TCP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 480 + [ETHTOOL_A_FLOW_UDP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 481 + [ETHTOOL_A_FLOW_SCTP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 482 + [ETHTOOL_A_FLOW_AH_ESP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 483 + [ETHTOOL_A_FLOW_TCP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 484 + [ETHTOOL_A_FLOW_UDP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 485 + [ETHTOOL_A_FLOW_SCTP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 486 + [ETHTOOL_A_FLOW_AH_ESP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 487 + [ETHTOOL_A_FLOW_AH4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 488 + [ETHTOOL_A_FLOW_ESP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 489 + [ETHTOOL_A_FLOW_AH6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 490 + [ETHTOOL_A_FLOW_ESP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 491 + [ETHTOOL_A_FLOW_GTPU4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 492 + [ETHTOOL_A_FLOW_GTPU6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 493 + [ETHTOOL_A_FLOW_GTPC4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 494 + [ETHTOOL_A_FLOW_GTPC6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 495 + [ETHTOOL_A_FLOW_GTPC_TEID4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 496 + [ETHTOOL_A_FLOW_GTPC_TEID6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 497 + [ETHTOOL_A_FLOW_GTPU_EH4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 498 + [ETHTOOL_A_FLOW_GTPU_EH6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 499 + [ETHTOOL_A_FLOW_GTPU_UL4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 500 + [ETHTOOL_A_FLOW_GTPU_UL6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 501 + [ETHTOOL_A_FLOW_GTPU_DL4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 502 + [ETHTOOL_A_FLOW_GTPU_DL6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK), 503 + }; 504 + 505 + const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = { 506 + [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 507 + [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, }, 508 + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), 509 + [ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, }, 510 + [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), 511 + [ETHTOOL_A_RSS_INPUT_XFRM] = 512 + NLA_POLICY_MAX(NLA_U32, RXH_XFRM_SYM_OR_XOR), 513 + [ETHTOOL_A_RSS_FLOW_HASH] = NLA_POLICY_NESTED(ethnl_rss_flows_policy), 514 + }; 515 + 516 + static int 517 + ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info) 518 + { 519 + const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 520 + struct rss_req_info *request = RSS_REQINFO(req_info); 521 + struct nlattr **tb = info->attrs; 522 + struct nlattr *bad_attr = NULL; 523 + u32 input_xfrm; 524 + 525 + if (request->rss_context && !ops->create_rxfh_context) 526 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_CONTEXT]; 527 + 528 + if (request->rss_context && !ops->rxfh_per_ctx_key) { 529 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC]; 530 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HKEY]; 531 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM]; 532 + } 533 + 534 + input_xfrm = nla_get_u32_default(tb[ETHTOOL_A_RSS_INPUT_XFRM], 0); 535 + if (input_xfrm & ~ops->supported_input_xfrm) 536 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM]; 537 + 538 + if (tb[ETHTOOL_A_RSS_FLOW_HASH] && !ops->set_rxfh_fields) 539 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_FLOW_HASH]; 540 + if (request->rss_context && 541 + tb[ETHTOOL_A_RSS_FLOW_HASH] && !ops->rxfh_per_ctx_fields) 542 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_FLOW_HASH]; 543 + 544 + if (bad_attr) { 545 + NL_SET_BAD_ATTR(info->extack, bad_attr); 546 + return -EOPNOTSUPP; 547 + } 548 + 549 + return 1; 550 + } 551 + 552 + static int 553 + rss_set_prep_indir(struct net_device *dev, struct genl_info *info, 554 + struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh, 555 + bool *reset, bool *mod) 556 + { 557 + const struct ethtool_ops *ops = dev->ethtool_ops; 558 + struct netlink_ext_ack *extack = info->extack; 559 + struct nlattr **tb = info->attrs; 560 + struct ethtool_rxnfc rx_rings; 561 + size_t alloc_size; 562 + u32 user_size; 563 + int i, err; 564 + 565 + if (!tb[ETHTOOL_A_RSS_INDIR]) 566 + return 0; 567 + if (!data->indir_size || !ops->get_rxnfc) 568 + return -EOPNOTSUPP; 569 + 570 + rx_rings.cmd = ETHTOOL_GRXRINGS; 571 + err = ops->get_rxnfc(dev, &rx_rings, NULL); 572 + if (err) 573 + return err; 574 + 575 + if (nla_len(tb[ETHTOOL_A_RSS_INDIR]) % 4) { 576 + NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_INDIR]); 577 + return -EINVAL; 578 + } 579 + user_size = nla_len(tb[ETHTOOL_A_RSS_INDIR]) / 4; 580 + if (!user_size) { 581 + if (rxfh->rss_context) { 582 + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_RSS_INDIR], 583 + "can't reset table for a context"); 584 + return -EINVAL; 585 + } 586 + *reset = true; 587 + } else if (data->indir_size % user_size) { 588 + NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR], 589 + "size (%d) mismatch with device indir table (%d)", 590 + user_size, data->indir_size); 591 + return -EINVAL; 592 + } 593 + 594 + rxfh->indir_size = data->indir_size; 595 + alloc_size = array_size(data->indir_size, sizeof(rxfh->indir[0])); 596 + rxfh->indir = kzalloc(alloc_size, GFP_KERNEL); 597 + if (!rxfh->indir) 598 + return -ENOMEM; 599 + 600 + nla_memcpy(rxfh->indir, tb[ETHTOOL_A_RSS_INDIR], alloc_size); 601 + for (i = 0; i < user_size; i++) { 602 + if (rxfh->indir[i] < rx_rings.data) 603 + continue; 604 + 605 + NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR], 606 + "entry %d: queue out of range (%d)", 607 + i, rxfh->indir[i]); 608 + err = -EINVAL; 609 + goto err_free; 610 + } 611 + 612 + if (user_size) { 613 + /* Replicate the user-provided table to fill the device table */ 614 + for (i = user_size; i < data->indir_size; i++) 615 + rxfh->indir[i] = rxfh->indir[i % user_size]; 616 + } else { 617 + for (i = 0; i < data->indir_size; i++) 618 + rxfh->indir[i] = 619 + ethtool_rxfh_indir_default(i, rx_rings.data); 620 + } 621 + 622 + *mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size); 623 + 624 + return 0; 625 + 626 + err_free: 627 + kfree(rxfh->indir); 628 + rxfh->indir = NULL; 629 + return err; 630 + } 631 + 632 + static int 633 + rss_set_prep_hkey(struct net_device *dev, struct genl_info *info, 634 + struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh, 635 + bool *mod) 636 + { 637 + struct nlattr **tb = info->attrs; 638 + 639 + if (!tb[ETHTOOL_A_RSS_HKEY]) 640 + return 0; 641 + 642 + if (nla_len(tb[ETHTOOL_A_RSS_HKEY]) != data->hkey_size) { 643 + NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_HKEY]); 644 + return -EINVAL; 645 + } 646 + 647 + rxfh->key_size = data->hkey_size; 648 + rxfh->key = kmemdup(data->hkey, data->hkey_size, GFP_KERNEL); 649 + if (!rxfh->key) 650 + return -ENOMEM; 651 + 652 + ethnl_update_binary(rxfh->key, rxfh->key_size, tb[ETHTOOL_A_RSS_HKEY], 653 + mod); 654 + return 0; 655 + } 656 + 657 + static int 658 + rss_check_rxfh_fields_sym(struct net_device *dev, struct genl_info *info, 659 + struct rss_reply_data *data, bool xfrm_sym) 660 + { 661 + struct nlattr **tb = info->attrs; 662 + int i; 663 + 664 + if (!xfrm_sym) 665 + return 0; 666 + if (!data->has_flow_hash) { 667 + NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_RSS_INPUT_XFRM], 668 + "hash field config not reported"); 669 + return -EINVAL; 670 + } 671 + 672 + for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) 673 + if (data->flow_hash[i] >= 0 && 674 + !ethtool_rxfh_config_is_sym(data->flow_hash[i])) { 675 + NL_SET_ERR_MSG_ATTR(info->extack, 676 + tb[ETHTOOL_A_RSS_INPUT_XFRM], 677 + "hash field config is not symmetric"); 678 + return -EINVAL; 679 + } 680 + 681 + return 0; 682 + } 683 + 684 + static int 685 + ethnl_set_rss_fields(struct net_device *dev, struct genl_info *info, 686 + u32 rss_context, struct rss_reply_data *data, 687 + bool xfrm_sym, bool *mod) 688 + { 689 + struct nlattr *flow_nest = info->attrs[ETHTOOL_A_RSS_FLOW_HASH]; 690 + struct nlattr *flows[ETHTOOL_A_FLOW_MAX + 1]; 691 + const struct ethtool_ops *ops; 692 + int i, ret; 693 + 694 + ops = dev->ethtool_ops; 695 + 696 + ret = rss_check_rxfh_fields_sym(dev, info, data, xfrm_sym); 697 + if (ret) 698 + return ret; 699 + 700 + if (!flow_nest) 701 + return 0; 702 + 703 + ret = nla_parse_nested(flows, ARRAY_SIZE(ethnl_rss_flows_policy) - 1, 704 + flow_nest, ethnl_rss_flows_policy, info->extack); 705 + if (ret < 0) 706 + return ret; 707 + 708 + for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) { 709 + struct ethtool_rxfh_fields fields = { 710 + .flow_type = ethtool_rxfh_ft_nl2ioctl[i], 711 + .rss_context = rss_context, 712 + }; 713 + 714 + if (!flows[i]) 715 + continue; 716 + 717 + fields.data = nla_get_u32(flows[i]); 718 + if (data->has_flow_hash && data->flow_hash[i] == fields.data) 719 + continue; 720 + 721 + if (xfrm_sym && !ethtool_rxfh_config_is_sym(fields.data)) { 722 + NL_SET_ERR_MSG_ATTR(info->extack, flows[i], 723 + "conflict with xfrm-input"); 724 + return -EINVAL; 725 + } 726 + 727 + ret = ops->set_rxfh_fields(dev, &fields, info->extack); 728 + if (ret) 729 + return ret; 730 + 731 + *mod = true; 732 + } 733 + 734 + return 0; 735 + } 736 + 737 + static void 738 + rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb, 739 + struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh) 740 + { 741 + int i; 742 + 743 + if (rxfh->indir) { 744 + for (i = 0; i < data->indir_size; i++) 745 + ethtool_rxfh_context_indir(ctx)[i] = rxfh->indir[i]; 746 + ctx->indir_configured = !!nla_len(tb[ETHTOOL_A_RSS_INDIR]); 747 + } 748 + if (rxfh->key) { 749 + memcpy(ethtool_rxfh_context_key(ctx), rxfh->key, 750 + data->hkey_size); 751 + ctx->key_configured = !!rxfh->key_size; 752 + } 753 + if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE) 754 + ctx->hfunc = rxfh->hfunc; 755 + if (rxfh->input_xfrm != RXH_XFRM_NO_CHANGE) 756 + ctx->input_xfrm = rxfh->input_xfrm; 757 + } 758 + 759 + static int 760 + ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info) 761 + { 762 + bool indir_reset = false, indir_mod, xfrm_sym = false; 763 + struct rss_req_info *request = RSS_REQINFO(req_info); 764 + struct ethtool_rxfh_context *ctx = NULL; 765 + struct net_device *dev = req_info->dev; 766 + bool mod = false, fields_mod = false; 767 + struct ethtool_rxfh_param rxfh = {}; 768 + struct nlattr **tb = info->attrs; 769 + struct rss_reply_data data = {}; 770 + const struct ethtool_ops *ops; 771 + int ret; 772 + 773 + ops = dev->ethtool_ops; 774 + data.base.dev = dev; 775 + 776 + ret = rss_prepare(request, dev, &data, info); 777 + if (ret) 778 + return ret; 779 + 780 + rxfh.rss_context = request->rss_context; 781 + 782 + ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_reset, &mod); 783 + if (ret) 784 + goto exit_clean_data; 785 + indir_mod = !!tb[ETHTOOL_A_RSS_INDIR]; 786 + 787 + rxfh.hfunc = data.hfunc; 788 + ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); 789 + if (rxfh.hfunc == data.hfunc) 790 + rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE; 791 + 792 + ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod); 793 + if (ret) 794 + goto exit_free_indir; 795 + 796 + rxfh.input_xfrm = data.input_xfrm; 797 + ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); 798 + /* For drivers which don't support input_xfrm it will be set to 0xff 799 + * in the RSS context info. In all other case input_xfrm != 0 means 800 + * symmetric hashing is requested. 801 + */ 802 + if (!request->rss_context || ops->rxfh_per_ctx_key) 803 + xfrm_sym = rxfh.input_xfrm || data.input_xfrm; 804 + if (rxfh.input_xfrm == data.input_xfrm) 805 + rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; 806 + 807 + mutex_lock(&dev->ethtool->rss_lock); 808 + if (request->rss_context) { 809 + ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context); 810 + if (!ctx) { 811 + ret = -ENOENT; 812 + goto exit_unlock; 813 + } 814 + } 815 + 816 + ret = ethnl_set_rss_fields(dev, info, request->rss_context, 817 + &data, xfrm_sym, &fields_mod); 818 + if (ret) 819 + goto exit_unlock; 820 + 821 + if (!mod) 822 + ret = 0; /* nothing to tell the driver */ 823 + else if (!ops->set_rxfh) 824 + ret = -EOPNOTSUPP; 825 + else if (!rxfh.rss_context) 826 + ret = ops->set_rxfh(dev, &rxfh, info->extack); 827 + else 828 + ret = ops->modify_rxfh_context(dev, ctx, &rxfh, info->extack); 829 + if (ret) 830 + goto exit_unlock; 831 + 832 + if (ctx) 833 + rss_set_ctx_update(ctx, tb, &data, &rxfh); 834 + else if (indir_reset) 835 + dev->priv_flags &= ~IFF_RXFH_CONFIGURED; 836 + else if (indir_mod) 837 + dev->priv_flags |= IFF_RXFH_CONFIGURED; 838 + 839 + exit_unlock: 840 + mutex_unlock(&dev->ethtool->rss_lock); 841 + kfree(rxfh.key); 842 + exit_free_indir: 843 + kfree(rxfh.indir); 844 + exit_clean_data: 845 + rss_cleanup_data(&data.base); 846 + 847 + return ret ?: mod || fields_mod; 848 + } 849 + 473 850 const struct ethnl_request_ops ethnl_rss_request_ops = { 474 851 .request_cmd = ETHTOOL_MSG_RSS_GET, 475 852 .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY, ··· 863 478 .reply_size = rss_reply_size, 864 479 .fill_reply = rss_fill_reply, 865 480 .cleanup_data = rss_cleanup_data, 481 + 482 + .set_validate = ethnl_rss_set_validate, 483 + .set = ethnl_rss_set, 484 + .set_ntf_cmd = ETHTOOL_MSG_RSS_NTF, 866 485 };
+6 -1
tools/net/ynl/pyynl/lib/ynl.py
··· 575 575 elif attr["type"] == 'string': 576 576 attr_payload = str(value).encode('ascii') + b'\x00' 577 577 elif attr["type"] == 'binary': 578 - if isinstance(value, bytes): 578 + if value is None: 579 + attr_payload = b'' 580 + elif isinstance(value, bytes): 579 581 attr_payload = value 580 582 elif isinstance(value, str): 581 583 if attr.display_hint: ··· 586 584 attr_payload = bytes.fromhex(value) 587 585 elif isinstance(value, dict) and attr.struct_name: 588 586 attr_payload = self._encode_struct(attr.struct_name, value) 587 + elif isinstance(value, list) and attr.sub_type in NlAttr.type_formats: 588 + format = NlAttr.get_format(attr.sub_type) 589 + attr_payload = b''.join([format.pack(x) for x in value]) 589 590 else: 590 591 raise Exception(f'Unknown type for binary attribute, value: {value}') 591 592 elif attr['type'] in NlAttr.type_formats or attr.is_auto_scalar:
+278 -11
tools/testing/selftests/drivers/net/hw/rss_api.py
··· 6 6 """ 7 7 8 8 import glob 9 - from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne 9 + import random 10 + from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne, ksft_raises 10 11 from lib.py import KsftSkipEx, KsftFailEx 11 - from lib.py import defer, ethtool 12 - from lib.py import EthtoolFamily 12 + from lib.py import defer, ethtool, CmdExitFailure 13 + from lib.py import EthtoolFamily, NlError 13 14 from lib.py import NetDrvEnv 15 + 16 + 17 + def _require_2qs(cfg): 18 + qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) 19 + if qcnt < 2: 20 + raise KsftSkipEx(f"Local has only {qcnt} queues") 21 + return qcnt 14 22 15 23 16 24 def _ethtool_create(cfg, act, opts): ··· 60 52 return ret 61 53 62 54 55 + def test_rxfh_nl_set_fail(cfg): 56 + """ 57 + Test error path of Netlink SET. 58 + """ 59 + _require_2qs(cfg) 60 + 61 + ethnl = EthtoolFamily() 62 + ethnl.ntf_subscribe("monitor") 63 + 64 + with ksft_raises(NlError): 65 + ethnl.rss_set({"header": {"dev-name": "lo"}, 66 + "indir": None}) 67 + 68 + with ksft_raises(NlError): 69 + ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 70 + "indir": [100000]}) 71 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 72 + ksft_is(ntf, None) 73 + 74 + 75 + def test_rxfh_nl_set_indir(cfg): 76 + """ 77 + Test setting indirection table via Netlink. 78 + """ 79 + qcnt = _require_2qs(cfg) 80 + 81 + # Test some SETs with a value 82 + reset = defer(cfg.ethnl.rss_set, 83 + {"header": {"dev-index": cfg.ifindex}, "indir": None}) 84 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 85 + "indir": [1]}) 86 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 87 + ksft_eq(set(rss.get("indir", [-1])), {1}) 88 + 89 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 90 + "indir": [0, 1]}) 91 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 92 + ksft_eq(set(rss.get("indir", [-1])), {0, 1}) 93 + 94 + # Make sure we can't set the queue count below max queue used 95 + with ksft_raises(CmdExitFailure): 96 + ethtool(f"-L {cfg.ifname} combined 0 rx 1") 97 + with ksft_raises(CmdExitFailure): 98 + ethtool(f"-L {cfg.ifname} combined 1 rx 0") 99 + 100 + # Test reset back to default 101 + reset.exec() 102 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 103 + ksft_eq(set(rss.get("indir", [-1])), set(range(qcnt))) 104 + 105 + 106 + def test_rxfh_nl_set_indir_ctx(cfg): 107 + """ 108 + Test setting indirection table for a custom context via Netlink. 109 + """ 110 + _require_2qs(cfg) 111 + 112 + # Get setting for ctx 0, we'll make sure they don't get clobbered 113 + dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 114 + 115 + # Create context 116 + ctx_id = _ethtool_create(cfg, "-X", "context new") 117 + defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") 118 + 119 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 120 + "context": ctx_id, "indir": [1]}) 121 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}, 122 + "context": ctx_id}) 123 + ksft_eq(set(rss.get("indir", [-1])), {1}) 124 + 125 + ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 126 + ksft_eq(ctx0, dflt) 127 + 128 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 129 + "context": ctx_id, "indir": [0, 1]}) 130 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}, 131 + "context": ctx_id}) 132 + ksft_eq(set(rss.get("indir", [-1])), {0, 1}) 133 + 134 + ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 135 + ksft_eq(ctx0, dflt) 136 + 137 + # Make sure we can't set the queue count below max queue used 138 + with ksft_raises(CmdExitFailure): 139 + ethtool(f"-L {cfg.ifname} combined 0 rx 1") 140 + with ksft_raises(CmdExitFailure): 141 + ethtool(f"-L {cfg.ifname} combined 1 rx 0") 142 + 143 + 63 144 def test_rxfh_indir_ntf(cfg): 64 145 """ 65 146 Check that Netlink notifications are generated when RSS indirection 66 147 table was modified. 67 148 """ 68 - 69 - qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) 70 - if qcnt < 2: 71 - raise KsftSkipEx(f"Local has only {qcnt} queues") 149 + _require_2qs(cfg) 72 150 73 151 ethnl = EthtoolFamily() 74 152 ethnl.ntf_subscribe("monitor") ··· 182 88 Check that Netlink notifications are generated when RSS indirection 183 89 table was modified on an additional RSS context. 184 90 """ 185 - 186 - qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) 187 - if qcnt < 2: 188 - raise KsftSkipEx(f"Local has only {qcnt} queues") 91 + _require_2qs(cfg) 189 92 190 93 ctx_id = _ethtool_create(cfg, "-X", "context new") 191 94 defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") ··· 200 109 ksft_eq(set(ntf["msg"]["indir"]), {1}) 201 110 202 111 112 + def test_rxfh_nl_set_key(cfg): 113 + """ 114 + Test setting hashing key via Netlink. 115 + """ 116 + 117 + dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 118 + defer(cfg.ethnl.rss_set, 119 + {"header": {"dev-index": cfg.ifindex}, 120 + "hkey": dflt["hkey"], "indir": None}) 121 + 122 + # Empty key should error out 123 + with ksft_raises(NlError) as cm: 124 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 125 + "hkey": None}) 126 + ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.hkey') 127 + 128 + # Set key to random 129 + mod = random.randbytes(len(dflt["hkey"])) 130 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 131 + "hkey": mod}) 132 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 133 + ksft_eq(rss.get("hkey", [-1]), mod) 134 + 135 + # Set key to random and indir tbl to something at once 136 + mod = random.randbytes(len(dflt["hkey"])) 137 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 138 + "indir": [0, 1], "hkey": mod}) 139 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 140 + ksft_eq(rss.get("hkey", [-1]), mod) 141 + ksft_eq(set(rss.get("indir", [-1])), {0, 1}) 142 + 143 + 203 144 def test_rxfh_fields(cfg): 204 145 """ 205 146 Test reading Rx Flow Hash over Netlink. ··· 247 124 comment="Config for " + fl_type) 248 125 249 126 127 + def test_rxfh_fields_set(cfg): 128 + """ Test configuring Rx Flow Hash over Netlink. """ 129 + 130 + flow_types = ["tcp4", "tcp6", "udp4", "udp6"] 131 + 132 + # Collect current settings 133 + cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 134 + # symmetric hashing is config-order-sensitive make sure we leave 135 + # symmetric mode, or make the flow-hash sym-compatible first 136 + changes = [{"flow-hash": cfg_old["flow-hash"],}, 137 + {"input-xfrm": cfg_old.get("input-xfrm", {}),}] 138 + if cfg_old.get("input-xfrm"): 139 + changes = list(reversed(changes)) 140 + for old in changes: 141 + defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old) 142 + 143 + # symmetric hashing prevents some of the configs below 144 + if cfg_old.get("input-xfrm"): 145 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 146 + "input-xfrm": {}}) 147 + 148 + for fl_type in flow_types: 149 + cur = _ethtool_get_cfg(cfg, fl_type) 150 + if cur == "sdfn": 151 + change_nl = {"ip-src", "ip-dst"} 152 + change_ic = "sd" 153 + else: 154 + change_nl = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"} 155 + change_ic = "sdfn" 156 + 157 + cfg.ethnl.rss_set({ 158 + "header": {"dev-index": cfg.ifindex}, 159 + "flow-hash": {fl_type: change_nl} 160 + }) 161 + reset = defer(ethtool, f"--disable-netlink -N {cfg.ifname} " 162 + f"rx-flow-hash {fl_type} {cur}") 163 + 164 + cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 165 + ksft_eq(change_nl, cfg_nl["flow-hash"][fl_type], 166 + comment=f"Config for {fl_type} over Netlink") 167 + cfg_ic = _ethtool_get_cfg(cfg, fl_type) 168 + ksft_eq(change_ic, cfg_ic, 169 + comment=f"Config for {fl_type} over IOCTL") 170 + 171 + reset.exec() 172 + cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 173 + ksft_eq(cfg_old["flow-hash"][fl_type], cfg_nl["flow-hash"][fl_type], 174 + comment=f"Un-config for {fl_type} over Netlink") 175 + cfg_ic = _ethtool_get_cfg(cfg, fl_type) 176 + ksft_eq(cur, cfg_ic, comment=f"Un-config for {fl_type} over IOCTL") 177 + 178 + # Try to set multiple at once, the defer was already installed at the start 179 + change = {"ip-src"} 180 + if change == cfg_old["flow-hash"]["tcp4"]: 181 + change = {"ip-dst"} 182 + cfg.ethnl.rss_set({ 183 + "header": {"dev-index": cfg.ifindex}, 184 + "flow-hash": {x: change for x in flow_types} 185 + }) 186 + 187 + cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 188 + for fl_type in flow_types: 189 + ksft_eq(change, cfg_nl["flow-hash"][fl_type], 190 + comment=f"multi-config for {fl_type} over Netlink") 191 + 192 + 193 + def test_rxfh_fields_set_xfrm(cfg): 194 + """ Test changing Rx Flow Hash vs xfrm_input at once. """ 195 + 196 + def set_rss(cfg, xfrm, fh): 197 + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 198 + "input-xfrm": xfrm, "flow-hash": fh}) 199 + 200 + # Install the reset handler 201 + cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 202 + # symmetric hashing is config-order-sensitive make sure we leave 203 + # symmetric mode, or make the flow-hash sym-compatible first 204 + changes = [{"flow-hash": cfg_old["flow-hash"],}, 205 + {"input-xfrm": cfg_old.get("input-xfrm", {}),}] 206 + if cfg_old.get("input-xfrm"): 207 + changes = list(reversed(changes)) 208 + for old in changes: 209 + defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old) 210 + 211 + # Make sure we start with input-xfrm off, and tcp4 config non-sym 212 + set_rss(cfg, {}, {}) 213 + set_rss(cfg, {}, {"tcp4": {"ip-src"}}) 214 + 215 + # Setting sym and fixing tcp4 config not expected to pass right now 216 + with ksft_raises(NlError): 217 + set_rss(cfg, {"sym-xor"}, {"tcp4": {"ip-src", "ip-dst"}}) 218 + # One at a time should work, hopefully 219 + set_rss(cfg, 0, {"tcp4": {"ip-src", "ip-dst"}}) 220 + no_support = False 221 + try: 222 + set_rss(cfg, {"sym-xor"}, {}) 223 + except NlError: 224 + try: 225 + set_rss(cfg, {"sym-or-xor"}, {}) 226 + except NlError: 227 + no_support = True 228 + if no_support: 229 + raise KsftSkipEx("no input-xfrm supported") 230 + # Disabling two at once should not work either without kernel changes 231 + with ksft_raises(NlError): 232 + set_rss(cfg, {}, {"tcp4": {"ip-src"}}) 233 + 234 + 235 + def test_rxfh_fields_ntf(cfg): 236 + """ Test Rx Flow Hash notifications. """ 237 + 238 + cur = _ethtool_get_cfg(cfg, "tcp4") 239 + if cur == "sdfn": 240 + change = {"ip-src", "ip-dst"} 241 + else: 242 + change = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"} 243 + 244 + ethnl = EthtoolFamily() 245 + ethnl.ntf_subscribe("monitor") 246 + 247 + ethnl.rss_set({ 248 + "header": {"dev-index": cfg.ifindex}, 249 + "flow-hash": {"tcp4": change} 250 + }) 251 + reset = defer(ethtool, 252 + f"--disable-netlink -N {cfg.ifname} rx-flow-hash tcp4 {cur}") 253 + 254 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 255 + if ntf is None: 256 + raise KsftFailEx("No notification received after IOCTL change") 257 + ksft_eq(ntf["name"], "rss-ntf") 258 + ksft_eq(ntf["msg"]["flow-hash"]["tcp4"], change) 259 + ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None) 260 + 261 + reset.exec() 262 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 263 + if ntf is None: 264 + raise KsftFailEx("No notification received after Netlink change") 265 + ksft_eq(ntf["name"], "rss-ntf") 266 + ksft_ne(ntf["msg"]["flow-hash"]["tcp4"], change) 267 + ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None) 268 + 269 + 250 270 def main() -> None: 251 271 """ Ksft boiler plate main """ 252 272 253 273 with NetDrvEnv(__file__, nsim_test=False) as cfg: 274 + cfg.ethnl = EthtoolFamily() 254 275 ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, )) 255 276 ksft_exit() 256 277
+3 -3
tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py
··· 37 37 if not hasattr(socket, "SO_INCOMING_CPU"): 38 38 raise KsftSkipEx("socket.SO_INCOMING_CPU was added in Python 3.11") 39 39 40 - input_xfrm = cfg.ethnl.rss_get( 41 - {'header': {'dev-name': cfg.ifname}}).get('input-xfrm') 40 + rss = cfg.ethnl.rss_get({'header': {'dev-name': cfg.ifname}}) 41 + input_xfrm = set(filter(lambda x: 'sym' in x, rss.get('input-xfrm', {}))) 42 42 43 43 # Check for symmetric xor/or-xor 44 - if not input_xfrm or (input_xfrm != 1 and input_xfrm != 2): 44 + if not input_xfrm: 45 45 raise KsftSkipEx("Symmetric RSS hash not requested") 46 46 47 47 cpus = set()