18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/**************************************************************************** 38c2ecf20Sopenharmony_ci * Driver for Solarflare network controllers and boards 48c2ecf20Sopenharmony_ci * Copyright 2019 Solarflare Communications Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License version 2 as published 88c2ecf20Sopenharmony_ci * by the Free Software Foundation, incorporated herein by reference. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include "net_driver.h" 138c2ecf20Sopenharmony_ci#include "mcdi.h" 148c2ecf20Sopenharmony_ci#include "nic.h" 158c2ecf20Sopenharmony_ci#include "selftest.h" 168c2ecf20Sopenharmony_ci#include "rx_common.h" 178c2ecf20Sopenharmony_ci#include "ethtool_common.h" 188c2ecf20Sopenharmony_ci#include "mcdi_port_common.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct efx_sw_stat_desc { 218c2ecf20Sopenharmony_ci const char *name; 228c2ecf20Sopenharmony_ci enum { 238c2ecf20Sopenharmony_ci EFX_ETHTOOL_STAT_SOURCE_nic, 248c2ecf20Sopenharmony_ci EFX_ETHTOOL_STAT_SOURCE_channel, 258c2ecf20Sopenharmony_ci EFX_ETHTOOL_STAT_SOURCE_tx_queue 268c2ecf20Sopenharmony_ci } source; 278c2ecf20Sopenharmony_ci unsigned int offset; 288c2ecf20Sopenharmony_ci u64 (*get_stat)(void *field); /* Reader function */ 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Initialiser for a struct efx_sw_stat_desc with type-checking */ 328c2ecf20Sopenharmony_ci#define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \ 338c2ecf20Sopenharmony_ci get_stat_function) { \ 348c2ecf20Sopenharmony_ci .name = #stat_name, \ 358c2ecf20Sopenharmony_ci .source = EFX_ETHTOOL_STAT_SOURCE_##source_name, \ 368c2ecf20Sopenharmony_ci .offset = ((((field_type *) 0) == \ 378c2ecf20Sopenharmony_ci &((struct efx_##source_name *)0)->field) ? \ 388c2ecf20Sopenharmony_ci offsetof(struct efx_##source_name, field) : \ 398c2ecf20Sopenharmony_ci offsetof(struct efx_##source_name, field)), \ 408c2ecf20Sopenharmony_ci .get_stat = get_stat_function, \ 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic u64 efx_get_uint_stat(void *field) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return *(unsigned int *)field; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic u64 efx_get_atomic_stat(void *field) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return atomic_read((atomic_t *) field); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \ 548c2ecf20Sopenharmony_ci EFX_ETHTOOL_STAT(field, nic, field, \ 558c2ecf20Sopenharmony_ci atomic_t, efx_get_atomic_stat) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define EFX_ETHTOOL_UINT_CHANNEL_STAT(field) \ 588c2ecf20Sopenharmony_ci EFX_ETHTOOL_STAT(field, channel, n_##field, \ 598c2ecf20Sopenharmony_ci unsigned int, efx_get_uint_stat) 608c2ecf20Sopenharmony_ci#define EFX_ETHTOOL_UINT_CHANNEL_STAT_NO_N(field) \ 618c2ecf20Sopenharmony_ci EFX_ETHTOOL_STAT(field, channel, field, \ 628c2ecf20Sopenharmony_ci unsigned int, efx_get_uint_stat) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define EFX_ETHTOOL_UINT_TXQ_STAT(field) \ 658c2ecf20Sopenharmony_ci EFX_ETHTOOL_STAT(tx_##field, tx_queue, field, \ 668c2ecf20Sopenharmony_ci unsigned int, efx_get_uint_stat) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const struct efx_sw_stat_desc efx_sw_stat_desc[] = { 698c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(merge_events), 708c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(tso_bursts), 718c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(tso_long_headers), 728c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(tso_packets), 738c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(tso_fallbacks), 748c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(pushes), 758c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(pio_packets), 768c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_TXQ_STAT(cb_packets), 778c2ecf20Sopenharmony_ci EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), 788c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), 798c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), 808c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err), 818c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_inner_ip_hdr_chksum_err), 828c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_inner_tcp_udp_chksum_err), 838c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_outer_ip_hdr_chksum_err), 848c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_outer_tcp_udp_chksum_err), 858c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_eth_crc_err), 868c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch), 878c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), 888c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_merge_events), 898c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_merge_packets), 908c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_xdp_drops), 918c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_xdp_bad_drops), 928c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_xdp_tx), 938c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_xdp_redirect), 948c2ecf20Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 958c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT_NO_N(rfs_filter_count), 968c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rfs_succeeded), 978c2ecf20Sopenharmony_ci EFX_ETHTOOL_UINT_CHANNEL_STAT(rfs_failed), 988c2ecf20Sopenharmony_ci#endif 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define EFX_ETHTOOL_SW_STAT_COUNT ARRAY_SIZE(efx_sw_stat_desc) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_civoid efx_ethtool_get_drvinfo(struct net_device *net_dev, 1048c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); 1098c2ecf20Sopenharmony_ci efx_mcdi_print_fwver(efx, info->fw_version, 1108c2ecf20Sopenharmony_ci sizeof(info->fw_version)); 1118c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciu32 efx_ethtool_get_msglevel(struct net_device *net_dev) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return efx->msg_enable; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_civoid efx_ethtool_set_msglevel(struct net_device *net_dev, u32 msg_enable) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci efx->msg_enable = msg_enable; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_civoid efx_ethtool_self_test(struct net_device *net_dev, 1298c2ecf20Sopenharmony_ci struct ethtool_test *test, u64 *data) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 1328c2ecf20Sopenharmony_ci struct efx_self_tests *efx_tests; 1338c2ecf20Sopenharmony_ci bool already_up; 1348c2ecf20Sopenharmony_ci int rc = -ENOMEM; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci efx_tests = kzalloc(sizeof(*efx_tests), GFP_KERNEL); 1378c2ecf20Sopenharmony_ci if (!efx_tests) 1388c2ecf20Sopenharmony_ci goto fail; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!efx_net_active(efx->state)) { 1418c2ecf20Sopenharmony_ci rc = -EBUSY; 1428c2ecf20Sopenharmony_ci goto out; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci netif_info(efx, drv, efx->net_dev, "starting %sline testing\n", 1468c2ecf20Sopenharmony_ci (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* We need rx buffers and interrupts. */ 1498c2ecf20Sopenharmony_ci already_up = (efx->net_dev->flags & IFF_UP); 1508c2ecf20Sopenharmony_ci if (!already_up) { 1518c2ecf20Sopenharmony_ci rc = dev_open(efx->net_dev, NULL); 1528c2ecf20Sopenharmony_ci if (rc) { 1538c2ecf20Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 1548c2ecf20Sopenharmony_ci "failed opening device.\n"); 1558c2ecf20Sopenharmony_ci goto out; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci rc = efx_selftest(efx, efx_tests, test->flags); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!already_up) 1628c2ecf20Sopenharmony_ci dev_close(efx->net_dev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci netif_info(efx, drv, efx->net_dev, "%s %sline self-tests\n", 1658c2ecf20Sopenharmony_ci rc == 0 ? "passed" : "failed", 1668c2ecf20Sopenharmony_ci (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciout: 1698c2ecf20Sopenharmony_ci efx_ethtool_fill_self_tests(efx, efx_tests, NULL, data); 1708c2ecf20Sopenharmony_ci kfree(efx_tests); 1718c2ecf20Sopenharmony_cifail: 1728c2ecf20Sopenharmony_ci if (rc) 1738c2ecf20Sopenharmony_ci test->flags |= ETH_TEST_FL_FAILED; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_civoid efx_ethtool_get_pauseparam(struct net_device *net_dev, 1778c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX); 1828c2ecf20Sopenharmony_ci pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX); 1838c2ecf20Sopenharmony_ci pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciint efx_ethtool_set_pauseparam(struct net_device *net_dev, 1878c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 1908c2ecf20Sopenharmony_ci u8 wanted_fc, old_fc; 1918c2ecf20Sopenharmony_ci u32 old_adv; 1928c2ecf20Sopenharmony_ci int rc = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci mutex_lock(&efx->mac_lock); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | 1978c2ecf20Sopenharmony_ci (pause->tx_pause ? EFX_FC_TX : 0) | 1988c2ecf20Sopenharmony_ci (pause->autoneg ? EFX_FC_AUTO : 0)); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { 2018c2ecf20Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 2028c2ecf20Sopenharmony_ci "Flow control unsupported: tx ON rx OFF\n"); 2038c2ecf20Sopenharmony_ci rc = -EINVAL; 2048c2ecf20Sopenharmony_ci goto out; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising[0]) { 2088c2ecf20Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 2098c2ecf20Sopenharmony_ci "Autonegotiation is disabled\n"); 2108c2ecf20Sopenharmony_ci rc = -EINVAL; 2118c2ecf20Sopenharmony_ci goto out; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Hook for Falcon bug 11482 workaround */ 2158c2ecf20Sopenharmony_ci if (efx->type->prepare_enable_fc_tx && 2168c2ecf20Sopenharmony_ci (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX)) 2178c2ecf20Sopenharmony_ci efx->type->prepare_enable_fc_tx(efx); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci old_adv = efx->link_advertising[0]; 2208c2ecf20Sopenharmony_ci old_fc = efx->wanted_fc; 2218c2ecf20Sopenharmony_ci efx_link_set_wanted_fc(efx, wanted_fc); 2228c2ecf20Sopenharmony_ci if (efx->link_advertising[0] != old_adv || 2238c2ecf20Sopenharmony_ci (efx->wanted_fc ^ old_fc) & EFX_FC_AUTO) { 2248c2ecf20Sopenharmony_ci rc = efx_mcdi_port_reconfigure(efx); 2258c2ecf20Sopenharmony_ci if (rc) { 2268c2ecf20Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 2278c2ecf20Sopenharmony_ci "Unable to advertise requested flow " 2288c2ecf20Sopenharmony_ci "control setting\n"); 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Reconfigure the MAC. The PHY *may* generate a link state change event 2348c2ecf20Sopenharmony_ci * if the user just changed the advertised capabilities, but there's no 2358c2ecf20Sopenharmony_ci * harm doing this twice */ 2368c2ecf20Sopenharmony_ci efx_mac_reconfigure(efx, false); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciout: 2398c2ecf20Sopenharmony_ci mutex_unlock(&efx->mac_lock); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return rc; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/** 2458c2ecf20Sopenharmony_ci * efx_fill_test - fill in an individual self-test entry 2468c2ecf20Sopenharmony_ci * @test_index: Index of the test 2478c2ecf20Sopenharmony_ci * @strings: Ethtool strings, or %NULL 2488c2ecf20Sopenharmony_ci * @data: Ethtool test results, or %NULL 2498c2ecf20Sopenharmony_ci * @test: Pointer to test result (used only if data != %NULL) 2508c2ecf20Sopenharmony_ci * @unit_format: Unit name format (e.g. "chan\%d") 2518c2ecf20Sopenharmony_ci * @unit_id: Unit id (e.g. 0 for "chan0") 2528c2ecf20Sopenharmony_ci * @test_format: Test name format (e.g. "loopback.\%s.tx.sent") 2538c2ecf20Sopenharmony_ci * @test_id: Test id (e.g. "PHYXS" for "loopback.PHYXS.tx_sent") 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * Fill in an individual self-test entry. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistatic void efx_fill_test(unsigned int test_index, u8 *strings, u64 *data, 2588c2ecf20Sopenharmony_ci int *test, const char *unit_format, int unit_id, 2598c2ecf20Sopenharmony_ci const char *test_format, const char *test_id) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci char unit_str[ETH_GSTRING_LEN], test_str[ETH_GSTRING_LEN]; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Fill data value, if applicable */ 2648c2ecf20Sopenharmony_ci if (data) 2658c2ecf20Sopenharmony_ci data[test_index] = *test; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Fill string, if applicable */ 2688c2ecf20Sopenharmony_ci if (strings) { 2698c2ecf20Sopenharmony_ci if (strchr(unit_format, '%')) 2708c2ecf20Sopenharmony_ci snprintf(unit_str, sizeof(unit_str), 2718c2ecf20Sopenharmony_ci unit_format, unit_id); 2728c2ecf20Sopenharmony_ci else 2738c2ecf20Sopenharmony_ci strcpy(unit_str, unit_format); 2748c2ecf20Sopenharmony_ci snprintf(test_str, sizeof(test_str), test_format, test_id); 2758c2ecf20Sopenharmony_ci snprintf(strings + test_index * ETH_GSTRING_LEN, 2768c2ecf20Sopenharmony_ci ETH_GSTRING_LEN, 2778c2ecf20Sopenharmony_ci "%-6s %-24s", unit_str, test_str); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci#define EFX_CHANNEL_NAME(_channel) "chan%d", _channel->channel 2828c2ecf20Sopenharmony_ci#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->label 2838c2ecf20Sopenharmony_ci#define EFX_LOOPBACK_NAME(_mode, _counter) \ 2848c2ecf20Sopenharmony_ci "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, efx_loopback_mode) 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/** 2878c2ecf20Sopenharmony_ci * efx_fill_loopback_test - fill in a block of loopback self-test entries 2888c2ecf20Sopenharmony_ci * @efx: Efx NIC 2898c2ecf20Sopenharmony_ci * @lb_tests: Efx loopback self-test results structure 2908c2ecf20Sopenharmony_ci * @mode: Loopback test mode 2918c2ecf20Sopenharmony_ci * @test_index: Starting index of the test 2928c2ecf20Sopenharmony_ci * @strings: Ethtool strings, or %NULL 2938c2ecf20Sopenharmony_ci * @data: Ethtool test results, or %NULL 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * Fill in a block of loopback self-test entries. Return new test 2968c2ecf20Sopenharmony_ci * index. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_cistatic int efx_fill_loopback_test(struct efx_nic *efx, 2998c2ecf20Sopenharmony_ci struct efx_loopback_self_tests *lb_tests, 3008c2ecf20Sopenharmony_ci enum efx_loopback_mode mode, 3018c2ecf20Sopenharmony_ci unsigned int test_index, 3028c2ecf20Sopenharmony_ci u8 *strings, u64 *data) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct efx_channel *channel = 3058c2ecf20Sopenharmony_ci efx_get_channel(efx, efx->tx_channel_offset); 3068c2ecf20Sopenharmony_ci struct efx_tx_queue *tx_queue; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 3098c2ecf20Sopenharmony_ci efx_fill_test(test_index++, strings, data, 3108c2ecf20Sopenharmony_ci &lb_tests->tx_sent[tx_queue->label], 3118c2ecf20Sopenharmony_ci EFX_TX_QUEUE_NAME(tx_queue), 3128c2ecf20Sopenharmony_ci EFX_LOOPBACK_NAME(mode, "tx_sent")); 3138c2ecf20Sopenharmony_ci efx_fill_test(test_index++, strings, data, 3148c2ecf20Sopenharmony_ci &lb_tests->tx_done[tx_queue->label], 3158c2ecf20Sopenharmony_ci EFX_TX_QUEUE_NAME(tx_queue), 3168c2ecf20Sopenharmony_ci EFX_LOOPBACK_NAME(mode, "tx_done")); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci efx_fill_test(test_index++, strings, data, 3198c2ecf20Sopenharmony_ci &lb_tests->rx_good, 3208c2ecf20Sopenharmony_ci "rx", 0, 3218c2ecf20Sopenharmony_ci EFX_LOOPBACK_NAME(mode, "rx_good")); 3228c2ecf20Sopenharmony_ci efx_fill_test(test_index++, strings, data, 3238c2ecf20Sopenharmony_ci &lb_tests->rx_bad, 3248c2ecf20Sopenharmony_ci "rx", 0, 3258c2ecf20Sopenharmony_ci EFX_LOOPBACK_NAME(mode, "rx_bad")); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return test_index; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/** 3318c2ecf20Sopenharmony_ci * efx_ethtool_fill_self_tests - get self-test details 3328c2ecf20Sopenharmony_ci * @efx: Efx NIC 3338c2ecf20Sopenharmony_ci * @tests: Efx self-test results structure, or %NULL 3348c2ecf20Sopenharmony_ci * @strings: Ethtool strings, or %NULL 3358c2ecf20Sopenharmony_ci * @data: Ethtool test results, or %NULL 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * Get self-test number of strings, strings, and/or test results. 3388c2ecf20Sopenharmony_ci * Return number of strings (== number of test results). 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * The reason for merging these three functions is to make sure that 3418c2ecf20Sopenharmony_ci * they can never be inconsistent. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ciint efx_ethtool_fill_self_tests(struct efx_nic *efx, 3448c2ecf20Sopenharmony_ci struct efx_self_tests *tests, 3458c2ecf20Sopenharmony_ci u8 *strings, u64 *data) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct efx_channel *channel; 3488c2ecf20Sopenharmony_ci unsigned int n = 0, i; 3498c2ecf20Sopenharmony_ci enum efx_loopback_mode mode; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, &tests->phy_alive, 3528c2ecf20Sopenharmony_ci "phy", 0, "alive", NULL); 3538c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, &tests->nvram, 3548c2ecf20Sopenharmony_ci "core", 0, "nvram", NULL); 3558c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, &tests->interrupt, 3568c2ecf20Sopenharmony_ci "core", 0, "interrupt", NULL); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Event queues */ 3598c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) { 3608c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, 3618c2ecf20Sopenharmony_ci &tests->eventq_dma[channel->channel], 3628c2ecf20Sopenharmony_ci EFX_CHANNEL_NAME(channel), 3638c2ecf20Sopenharmony_ci "eventq.dma", NULL); 3648c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, 3658c2ecf20Sopenharmony_ci &tests->eventq_int[channel->channel], 3668c2ecf20Sopenharmony_ci EFX_CHANNEL_NAME(channel), 3678c2ecf20Sopenharmony_ci "eventq.int", NULL); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, &tests->memory, 3718c2ecf20Sopenharmony_ci "core", 0, "memory", NULL); 3728c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, &tests->registers, 3738c2ecf20Sopenharmony_ci "core", 0, "registers", NULL); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci for (i = 0; true; ++i) { 3768c2ecf20Sopenharmony_ci const char *name; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci EFX_WARN_ON_PARANOID(i >= EFX_MAX_PHY_TESTS); 3798c2ecf20Sopenharmony_ci name = efx_mcdi_phy_test_name(efx, i); 3808c2ecf20Sopenharmony_ci if (name == NULL) 3818c2ecf20Sopenharmony_ci break; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci efx_fill_test(n++, strings, data, &tests->phy_ext[i], "phy", 0, name, NULL); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Loopback tests */ 3878c2ecf20Sopenharmony_ci for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { 3888c2ecf20Sopenharmony_ci if (!(efx->loopback_modes & (1 << mode))) 3898c2ecf20Sopenharmony_ci continue; 3908c2ecf20Sopenharmony_ci n = efx_fill_loopback_test(efx, 3918c2ecf20Sopenharmony_ci &tests->loopback[mode], mode, n, 3928c2ecf20Sopenharmony_ci strings, data); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return n; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic size_t efx_describe_per_queue_stats(struct efx_nic *efx, u8 *strings) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci size_t n_stats = 0; 4018c2ecf20Sopenharmony_ci struct efx_channel *channel; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) { 4048c2ecf20Sopenharmony_ci if (efx_channel_has_tx_queues(channel)) { 4058c2ecf20Sopenharmony_ci n_stats++; 4068c2ecf20Sopenharmony_ci if (strings != NULL) { 4078c2ecf20Sopenharmony_ci snprintf(strings, ETH_GSTRING_LEN, 4088c2ecf20Sopenharmony_ci "tx-%u.tx_packets", 4098c2ecf20Sopenharmony_ci channel->tx_queue[0].queue / 4108c2ecf20Sopenharmony_ci EFX_MAX_TXQ_PER_CHANNEL); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci strings += ETH_GSTRING_LEN; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) { 4178c2ecf20Sopenharmony_ci if (efx_channel_has_rx_queue(channel)) { 4188c2ecf20Sopenharmony_ci n_stats++; 4198c2ecf20Sopenharmony_ci if (strings != NULL) { 4208c2ecf20Sopenharmony_ci snprintf(strings, ETH_GSTRING_LEN, 4218c2ecf20Sopenharmony_ci "rx-%d.rx_packets", channel->channel); 4228c2ecf20Sopenharmony_ci strings += ETH_GSTRING_LEN; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci if (efx->xdp_tx_queue_count && efx->xdp_tx_queues) { 4278c2ecf20Sopenharmony_ci unsigned short xdp; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci for (xdp = 0; xdp < efx->xdp_tx_queue_count; xdp++) { 4308c2ecf20Sopenharmony_ci n_stats++; 4318c2ecf20Sopenharmony_ci if (strings) { 4328c2ecf20Sopenharmony_ci snprintf(strings, ETH_GSTRING_LEN, 4338c2ecf20Sopenharmony_ci "tx-xdp-cpu-%hu.tx_packets", xdp); 4348c2ecf20Sopenharmony_ci strings += ETH_GSTRING_LEN; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return n_stats; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ciint efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci switch (string_set) { 4478c2ecf20Sopenharmony_ci case ETH_SS_STATS: 4488c2ecf20Sopenharmony_ci return efx->type->describe_stats(efx, NULL) + 4498c2ecf20Sopenharmony_ci EFX_ETHTOOL_SW_STAT_COUNT + 4508c2ecf20Sopenharmony_ci efx_describe_per_queue_stats(efx, NULL) + 4518c2ecf20Sopenharmony_ci efx_ptp_describe_stats(efx, NULL); 4528c2ecf20Sopenharmony_ci case ETH_SS_TEST: 4538c2ecf20Sopenharmony_ci return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); 4548c2ecf20Sopenharmony_ci default: 4558c2ecf20Sopenharmony_ci return -EINVAL; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_civoid efx_ethtool_get_strings(struct net_device *net_dev, 4608c2ecf20Sopenharmony_ci u32 string_set, u8 *strings) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 4638c2ecf20Sopenharmony_ci int i; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci switch (string_set) { 4668c2ecf20Sopenharmony_ci case ETH_SS_STATS: 4678c2ecf20Sopenharmony_ci strings += (efx->type->describe_stats(efx, strings) * 4688c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 4698c2ecf20Sopenharmony_ci for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) 4708c2ecf20Sopenharmony_ci strlcpy(strings + i * ETH_GSTRING_LEN, 4718c2ecf20Sopenharmony_ci efx_sw_stat_desc[i].name, ETH_GSTRING_LEN); 4728c2ecf20Sopenharmony_ci strings += EFX_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN; 4738c2ecf20Sopenharmony_ci strings += (efx_describe_per_queue_stats(efx, strings) * 4748c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 4758c2ecf20Sopenharmony_ci efx_ptp_describe_stats(efx, strings); 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci case ETH_SS_TEST: 4788c2ecf20Sopenharmony_ci efx_ethtool_fill_self_tests(efx, NULL, strings, NULL); 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci default: 4818c2ecf20Sopenharmony_ci /* No other string sets */ 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_civoid efx_ethtool_get_stats(struct net_device *net_dev, 4878c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 4888c2ecf20Sopenharmony_ci u64 *data) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 4918c2ecf20Sopenharmony_ci const struct efx_sw_stat_desc *stat; 4928c2ecf20Sopenharmony_ci struct efx_channel *channel; 4938c2ecf20Sopenharmony_ci struct efx_tx_queue *tx_queue; 4948c2ecf20Sopenharmony_ci struct efx_rx_queue *rx_queue; 4958c2ecf20Sopenharmony_ci int i; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci spin_lock_bh(&efx->stats_lock); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* Get NIC statistics */ 5008c2ecf20Sopenharmony_ci data += efx->type->update_stats(efx, data, NULL); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Get software statistics */ 5038c2ecf20Sopenharmony_ci for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) { 5048c2ecf20Sopenharmony_ci stat = &efx_sw_stat_desc[i]; 5058c2ecf20Sopenharmony_ci switch (stat->source) { 5068c2ecf20Sopenharmony_ci case EFX_ETHTOOL_STAT_SOURCE_nic: 5078c2ecf20Sopenharmony_ci data[i] = stat->get_stat((void *)efx + stat->offset); 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci case EFX_ETHTOOL_STAT_SOURCE_channel: 5108c2ecf20Sopenharmony_ci data[i] = 0; 5118c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) 5128c2ecf20Sopenharmony_ci data[i] += stat->get_stat((void *)channel + 5138c2ecf20Sopenharmony_ci stat->offset); 5148c2ecf20Sopenharmony_ci break; 5158c2ecf20Sopenharmony_ci case EFX_ETHTOOL_STAT_SOURCE_tx_queue: 5168c2ecf20Sopenharmony_ci data[i] = 0; 5178c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) { 5188c2ecf20Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) 5198c2ecf20Sopenharmony_ci data[i] += 5208c2ecf20Sopenharmony_ci stat->get_stat((void *)tx_queue 5218c2ecf20Sopenharmony_ci + stat->offset); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci data += EFX_ETHTOOL_SW_STAT_COUNT; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci spin_unlock_bh(&efx->stats_lock); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) { 5318c2ecf20Sopenharmony_ci if (efx_channel_has_tx_queues(channel)) { 5328c2ecf20Sopenharmony_ci *data = 0; 5338c2ecf20Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 5348c2ecf20Sopenharmony_ci *data += tx_queue->tx_packets; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci data++; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) { 5408c2ecf20Sopenharmony_ci if (efx_channel_has_rx_queue(channel)) { 5418c2ecf20Sopenharmony_ci *data = 0; 5428c2ecf20Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) { 5438c2ecf20Sopenharmony_ci *data += rx_queue->rx_packets; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci data++; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci if (efx->xdp_tx_queue_count && efx->xdp_tx_queues) { 5498c2ecf20Sopenharmony_ci int xdp; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci for (xdp = 0; xdp < efx->xdp_tx_queue_count; xdp++) { 5528c2ecf20Sopenharmony_ci data[0] = efx->xdp_tx_queues[xdp]->tx_packets; 5538c2ecf20Sopenharmony_ci data++; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci efx_ptp_update_stats(efx, data); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* This must be called with rtnl_lock held. */ 5618c2ecf20Sopenharmony_ciint efx_ethtool_get_link_ksettings(struct net_device *net_dev, 5628c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 5658c2ecf20Sopenharmony_ci struct efx_link_state *link_state = &efx->link_state; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci mutex_lock(&efx->mac_lock); 5688c2ecf20Sopenharmony_ci efx_mcdi_phy_get_link_ksettings(efx, cmd); 5698c2ecf20Sopenharmony_ci mutex_unlock(&efx->mac_lock); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Both MACs support pause frames (bidirectional and respond-only) */ 5728c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); 5738c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (LOOPBACK_INTERNAL(efx)) { 5768c2ecf20Sopenharmony_ci cmd->base.speed = link_state->speed; 5778c2ecf20Sopenharmony_ci cmd->base.duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci/* This must be called with rtnl_lock held. */ 5848c2ecf20Sopenharmony_ciint efx_ethtool_set_link_ksettings(struct net_device *net_dev, 5858c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 5888c2ecf20Sopenharmony_ci int rc; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* GMAC does not support 1000Mbps HD */ 5918c2ecf20Sopenharmony_ci if ((cmd->base.speed == SPEED_1000) && 5928c2ecf20Sopenharmony_ci (cmd->base.duplex != DUPLEX_FULL)) { 5938c2ecf20Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 5948c2ecf20Sopenharmony_ci "rejecting unsupported 1000Mbps HD setting\n"); 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci mutex_lock(&efx->mac_lock); 5998c2ecf20Sopenharmony_ci rc = efx_mcdi_phy_set_link_ksettings(efx, cmd); 6008c2ecf20Sopenharmony_ci mutex_unlock(&efx->mac_lock); 6018c2ecf20Sopenharmony_ci return rc; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ciint efx_ethtool_get_fecparam(struct net_device *net_dev, 6058c2ecf20Sopenharmony_ci struct ethtool_fecparam *fecparam) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 6088c2ecf20Sopenharmony_ci int rc; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci mutex_lock(&efx->mac_lock); 6118c2ecf20Sopenharmony_ci rc = efx_mcdi_phy_get_fecparam(efx, fecparam); 6128c2ecf20Sopenharmony_ci mutex_unlock(&efx->mac_lock); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return rc; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ciint efx_ethtool_set_fecparam(struct net_device *net_dev, 6188c2ecf20Sopenharmony_ci struct ethtool_fecparam *fecparam) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 6218c2ecf20Sopenharmony_ci int rc; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci mutex_lock(&efx->mac_lock); 6248c2ecf20Sopenharmony_ci rc = efx_mcdi_phy_set_fecparam(efx, fecparam); 6258c2ecf20Sopenharmony_ci mutex_unlock(&efx->mac_lock); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return rc; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci/* MAC address mask including only I/G bit */ 6318c2ecf20Sopenharmony_cistatic const u8 mac_addr_ig_mask[ETH_ALEN] __aligned(2) = {0x01, 0, 0, 0, 0, 0}; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci#define IP4_ADDR_FULL_MASK ((__force __be32)~0) 6348c2ecf20Sopenharmony_ci#define IP_PROTO_FULL_MASK 0xFF 6358c2ecf20Sopenharmony_ci#define PORT_FULL_MASK ((__force __be16)~0) 6368c2ecf20Sopenharmony_ci#define ETHER_TYPE_FULL_MASK ((__force __be16)~0) 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic inline void ip6_fill_mask(__be32 *mask) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci mask[0] = mask[1] = mask[2] = mask[3] = ~(__be32)0; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int efx_ethtool_get_class_rule(struct efx_nic *efx, 6448c2ecf20Sopenharmony_ci struct ethtool_rx_flow_spec *rule, 6458c2ecf20Sopenharmony_ci u32 *rss_context) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; 6488c2ecf20Sopenharmony_ci struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; 6498c2ecf20Sopenharmony_ci struct ethtool_usrip4_spec *uip_entry = &rule->h_u.usr_ip4_spec; 6508c2ecf20Sopenharmony_ci struct ethtool_usrip4_spec *uip_mask = &rule->m_u.usr_ip4_spec; 6518c2ecf20Sopenharmony_ci struct ethtool_tcpip6_spec *ip6_entry = &rule->h_u.tcp_ip6_spec; 6528c2ecf20Sopenharmony_ci struct ethtool_tcpip6_spec *ip6_mask = &rule->m_u.tcp_ip6_spec; 6538c2ecf20Sopenharmony_ci struct ethtool_usrip6_spec *uip6_entry = &rule->h_u.usr_ip6_spec; 6548c2ecf20Sopenharmony_ci struct ethtool_usrip6_spec *uip6_mask = &rule->m_u.usr_ip6_spec; 6558c2ecf20Sopenharmony_ci struct ethhdr *mac_entry = &rule->h_u.ether_spec; 6568c2ecf20Sopenharmony_ci struct ethhdr *mac_mask = &rule->m_u.ether_spec; 6578c2ecf20Sopenharmony_ci struct efx_filter_spec spec; 6588c2ecf20Sopenharmony_ci int rc; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL, 6618c2ecf20Sopenharmony_ci rule->location, &spec); 6628c2ecf20Sopenharmony_ci if (rc) 6638c2ecf20Sopenharmony_ci return rc; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (spec.dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP) 6668c2ecf20Sopenharmony_ci rule->ring_cookie = RX_CLS_FLOW_DISC; 6678c2ecf20Sopenharmony_ci else 6688c2ecf20Sopenharmony_ci rule->ring_cookie = spec.dmaq_id; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if ((spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) && 6718c2ecf20Sopenharmony_ci spec.ether_type == htons(ETH_P_IP) && 6728c2ecf20Sopenharmony_ci (spec.match_flags & EFX_FILTER_MATCH_IP_PROTO) && 6738c2ecf20Sopenharmony_ci (spec.ip_proto == IPPROTO_TCP || spec.ip_proto == IPPROTO_UDP) && 6748c2ecf20Sopenharmony_ci !(spec.match_flags & 6758c2ecf20Sopenharmony_ci ~(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID | 6768c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST | 6778c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_IP_PROTO | 6788c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_LOC_PORT | EFX_FILTER_MATCH_REM_PORT))) { 6798c2ecf20Sopenharmony_ci rule->flow_type = ((spec.ip_proto == IPPROTO_TCP) ? 6808c2ecf20Sopenharmony_ci TCP_V4_FLOW : UDP_V4_FLOW); 6818c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_LOC_HOST) { 6828c2ecf20Sopenharmony_ci ip_entry->ip4dst = spec.loc_host[0]; 6838c2ecf20Sopenharmony_ci ip_mask->ip4dst = IP4_ADDR_FULL_MASK; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_REM_HOST) { 6868c2ecf20Sopenharmony_ci ip_entry->ip4src = spec.rem_host[0]; 6878c2ecf20Sopenharmony_ci ip_mask->ip4src = IP4_ADDR_FULL_MASK; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_LOC_PORT) { 6908c2ecf20Sopenharmony_ci ip_entry->pdst = spec.loc_port; 6918c2ecf20Sopenharmony_ci ip_mask->pdst = PORT_FULL_MASK; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_REM_PORT) { 6948c2ecf20Sopenharmony_ci ip_entry->psrc = spec.rem_port; 6958c2ecf20Sopenharmony_ci ip_mask->psrc = PORT_FULL_MASK; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci } else if ((spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) && 6988c2ecf20Sopenharmony_ci spec.ether_type == htons(ETH_P_IPV6) && 6998c2ecf20Sopenharmony_ci (spec.match_flags & EFX_FILTER_MATCH_IP_PROTO) && 7008c2ecf20Sopenharmony_ci (spec.ip_proto == IPPROTO_TCP || spec.ip_proto == IPPROTO_UDP) && 7018c2ecf20Sopenharmony_ci !(spec.match_flags & 7028c2ecf20Sopenharmony_ci ~(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID | 7038c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST | 7048c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_IP_PROTO | 7058c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_LOC_PORT | EFX_FILTER_MATCH_REM_PORT))) { 7068c2ecf20Sopenharmony_ci rule->flow_type = ((spec.ip_proto == IPPROTO_TCP) ? 7078c2ecf20Sopenharmony_ci TCP_V6_FLOW : UDP_V6_FLOW); 7088c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_LOC_HOST) { 7098c2ecf20Sopenharmony_ci memcpy(ip6_entry->ip6dst, spec.loc_host, 7108c2ecf20Sopenharmony_ci sizeof(ip6_entry->ip6dst)); 7118c2ecf20Sopenharmony_ci ip6_fill_mask(ip6_mask->ip6dst); 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_REM_HOST) { 7148c2ecf20Sopenharmony_ci memcpy(ip6_entry->ip6src, spec.rem_host, 7158c2ecf20Sopenharmony_ci sizeof(ip6_entry->ip6src)); 7168c2ecf20Sopenharmony_ci ip6_fill_mask(ip6_mask->ip6src); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_LOC_PORT) { 7198c2ecf20Sopenharmony_ci ip6_entry->pdst = spec.loc_port; 7208c2ecf20Sopenharmony_ci ip6_mask->pdst = PORT_FULL_MASK; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_REM_PORT) { 7238c2ecf20Sopenharmony_ci ip6_entry->psrc = spec.rem_port; 7248c2ecf20Sopenharmony_ci ip6_mask->psrc = PORT_FULL_MASK; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci } else if (!(spec.match_flags & 7278c2ecf20Sopenharmony_ci ~(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG | 7288c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_REM_MAC | EFX_FILTER_MATCH_ETHER_TYPE | 7298c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_OUTER_VID))) { 7308c2ecf20Sopenharmony_ci rule->flow_type = ETHER_FLOW; 7318c2ecf20Sopenharmony_ci if (spec.match_flags & 7328c2ecf20Sopenharmony_ci (EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG)) { 7338c2ecf20Sopenharmony_ci ether_addr_copy(mac_entry->h_dest, spec.loc_mac); 7348c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_LOC_MAC) 7358c2ecf20Sopenharmony_ci eth_broadcast_addr(mac_mask->h_dest); 7368c2ecf20Sopenharmony_ci else 7378c2ecf20Sopenharmony_ci ether_addr_copy(mac_mask->h_dest, 7388c2ecf20Sopenharmony_ci mac_addr_ig_mask); 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_REM_MAC) { 7418c2ecf20Sopenharmony_ci ether_addr_copy(mac_entry->h_source, spec.rem_mac); 7428c2ecf20Sopenharmony_ci eth_broadcast_addr(mac_mask->h_source); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) { 7458c2ecf20Sopenharmony_ci mac_entry->h_proto = spec.ether_type; 7468c2ecf20Sopenharmony_ci mac_mask->h_proto = ETHER_TYPE_FULL_MASK; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci } else if (spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE && 7498c2ecf20Sopenharmony_ci spec.ether_type == htons(ETH_P_IP) && 7508c2ecf20Sopenharmony_ci !(spec.match_flags & 7518c2ecf20Sopenharmony_ci ~(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID | 7528c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST | 7538c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_IP_PROTO))) { 7548c2ecf20Sopenharmony_ci rule->flow_type = IPV4_USER_FLOW; 7558c2ecf20Sopenharmony_ci uip_entry->ip_ver = ETH_RX_NFC_IP4; 7568c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_IP_PROTO) { 7578c2ecf20Sopenharmony_ci uip_mask->proto = IP_PROTO_FULL_MASK; 7588c2ecf20Sopenharmony_ci uip_entry->proto = spec.ip_proto; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_LOC_HOST) { 7618c2ecf20Sopenharmony_ci uip_entry->ip4dst = spec.loc_host[0]; 7628c2ecf20Sopenharmony_ci uip_mask->ip4dst = IP4_ADDR_FULL_MASK; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_REM_HOST) { 7658c2ecf20Sopenharmony_ci uip_entry->ip4src = spec.rem_host[0]; 7668c2ecf20Sopenharmony_ci uip_mask->ip4src = IP4_ADDR_FULL_MASK; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci } else if (spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE && 7698c2ecf20Sopenharmony_ci spec.ether_type == htons(ETH_P_IPV6) && 7708c2ecf20Sopenharmony_ci !(spec.match_flags & 7718c2ecf20Sopenharmony_ci ~(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID | 7728c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST | 7738c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_IP_PROTO))) { 7748c2ecf20Sopenharmony_ci rule->flow_type = IPV6_USER_FLOW; 7758c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_IP_PROTO) { 7768c2ecf20Sopenharmony_ci uip6_mask->l4_proto = IP_PROTO_FULL_MASK; 7778c2ecf20Sopenharmony_ci uip6_entry->l4_proto = spec.ip_proto; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_LOC_HOST) { 7808c2ecf20Sopenharmony_ci memcpy(uip6_entry->ip6dst, spec.loc_host, 7818c2ecf20Sopenharmony_ci sizeof(uip6_entry->ip6dst)); 7828c2ecf20Sopenharmony_ci ip6_fill_mask(uip6_mask->ip6dst); 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_REM_HOST) { 7858c2ecf20Sopenharmony_ci memcpy(uip6_entry->ip6src, spec.rem_host, 7868c2ecf20Sopenharmony_ci sizeof(uip6_entry->ip6src)); 7878c2ecf20Sopenharmony_ci ip6_fill_mask(uip6_mask->ip6src); 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci } else { 7908c2ecf20Sopenharmony_ci /* The above should handle all filters that we insert */ 7918c2ecf20Sopenharmony_ci WARN_ON(1); 7928c2ecf20Sopenharmony_ci return -EINVAL; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (spec.match_flags & EFX_FILTER_MATCH_OUTER_VID) { 7968c2ecf20Sopenharmony_ci rule->flow_type |= FLOW_EXT; 7978c2ecf20Sopenharmony_ci rule->h_ext.vlan_tci = spec.outer_vid; 7988c2ecf20Sopenharmony_ci rule->m_ext.vlan_tci = htons(0xfff); 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (spec.flags & EFX_FILTER_FLAG_RX_RSS) { 8028c2ecf20Sopenharmony_ci rule->flow_type |= FLOW_RSS; 8038c2ecf20Sopenharmony_ci *rss_context = spec.rss_context; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci return rc; 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ciint efx_ethtool_get_rxnfc(struct net_device *net_dev, 8108c2ecf20Sopenharmony_ci struct ethtool_rxnfc *info, u32 *rule_locs) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 8138c2ecf20Sopenharmony_ci u32 rss_context = 0; 8148c2ecf20Sopenharmony_ci s32 rc = 0; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci switch (info->cmd) { 8178c2ecf20Sopenharmony_ci case ETHTOOL_GRXRINGS: 8188c2ecf20Sopenharmony_ci info->data = efx->n_rx_channels; 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci case ETHTOOL_GRXFH: { 8228c2ecf20Sopenharmony_ci struct efx_rss_context *ctx = &efx->rss_context; 8238c2ecf20Sopenharmony_ci __u64 data; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci mutex_lock(&efx->rss_lock); 8268c2ecf20Sopenharmony_ci if (info->flow_type & FLOW_RSS && info->rss_context) { 8278c2ecf20Sopenharmony_ci ctx = efx_find_rss_context_entry(efx, info->rss_context); 8288c2ecf20Sopenharmony_ci if (!ctx) { 8298c2ecf20Sopenharmony_ci rc = -ENOENT; 8308c2ecf20Sopenharmony_ci goto out_unlock; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci data = 0; 8358c2ecf20Sopenharmony_ci if (!efx_rss_active(ctx)) /* No RSS */ 8368c2ecf20Sopenharmony_ci goto out_setdata_unlock; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci switch (info->flow_type & ~FLOW_RSS) { 8398c2ecf20Sopenharmony_ci case UDP_V4_FLOW: 8408c2ecf20Sopenharmony_ci case UDP_V6_FLOW: 8418c2ecf20Sopenharmony_ci if (ctx->rx_hash_udp_4tuple) 8428c2ecf20Sopenharmony_ci data = (RXH_L4_B_0_1 | RXH_L4_B_2_3 | 8438c2ecf20Sopenharmony_ci RXH_IP_SRC | RXH_IP_DST); 8448c2ecf20Sopenharmony_ci else 8458c2ecf20Sopenharmony_ci data = RXH_IP_SRC | RXH_IP_DST; 8468c2ecf20Sopenharmony_ci break; 8478c2ecf20Sopenharmony_ci case TCP_V4_FLOW: 8488c2ecf20Sopenharmony_ci case TCP_V6_FLOW: 8498c2ecf20Sopenharmony_ci data = (RXH_L4_B_0_1 | RXH_L4_B_2_3 | 8508c2ecf20Sopenharmony_ci RXH_IP_SRC | RXH_IP_DST); 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci case SCTP_V4_FLOW: 8538c2ecf20Sopenharmony_ci case SCTP_V6_FLOW: 8548c2ecf20Sopenharmony_ci case AH_ESP_V4_FLOW: 8558c2ecf20Sopenharmony_ci case AH_ESP_V6_FLOW: 8568c2ecf20Sopenharmony_ci case IPV4_FLOW: 8578c2ecf20Sopenharmony_ci case IPV6_FLOW: 8588c2ecf20Sopenharmony_ci data = RXH_IP_SRC | RXH_IP_DST; 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci default: 8618c2ecf20Sopenharmony_ci break; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ciout_setdata_unlock: 8648c2ecf20Sopenharmony_ci info->data = data; 8658c2ecf20Sopenharmony_ciout_unlock: 8668c2ecf20Sopenharmony_ci mutex_unlock(&efx->rss_lock); 8678c2ecf20Sopenharmony_ci return rc; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci case ETHTOOL_GRXCLSRLCNT: 8718c2ecf20Sopenharmony_ci info->data = efx_filter_get_rx_id_limit(efx); 8728c2ecf20Sopenharmony_ci if (info->data == 0) 8738c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8748c2ecf20Sopenharmony_ci info->data |= RX_CLS_LOC_SPECIAL; 8758c2ecf20Sopenharmony_ci info->rule_cnt = 8768c2ecf20Sopenharmony_ci efx_filter_count_rx_used(efx, EFX_FILTER_PRI_MANUAL); 8778c2ecf20Sopenharmony_ci return 0; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci case ETHTOOL_GRXCLSRULE: 8808c2ecf20Sopenharmony_ci if (efx_filter_get_rx_id_limit(efx) == 0) 8818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8828c2ecf20Sopenharmony_ci rc = efx_ethtool_get_class_rule(efx, &info->fs, &rss_context); 8838c2ecf20Sopenharmony_ci if (rc < 0) 8848c2ecf20Sopenharmony_ci return rc; 8858c2ecf20Sopenharmony_ci if (info->fs.flow_type & FLOW_RSS) 8868c2ecf20Sopenharmony_ci info->rss_context = rss_context; 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci case ETHTOOL_GRXCLSRLALL: 8908c2ecf20Sopenharmony_ci info->data = efx_filter_get_rx_id_limit(efx); 8918c2ecf20Sopenharmony_ci if (info->data == 0) 8928c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8938c2ecf20Sopenharmony_ci rc = efx_filter_get_rx_ids(efx, EFX_FILTER_PRI_MANUAL, 8948c2ecf20Sopenharmony_ci rule_locs, info->rule_cnt); 8958c2ecf20Sopenharmony_ci if (rc < 0) 8968c2ecf20Sopenharmony_ci return rc; 8978c2ecf20Sopenharmony_ci info->rule_cnt = rc; 8988c2ecf20Sopenharmony_ci return 0; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci default: 9018c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic inline bool ip6_mask_is_full(__be32 mask[4]) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci return !~(mask[0] & mask[1] & mask[2] & mask[3]); 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic inline bool ip6_mask_is_empty(__be32 mask[4]) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci return !(mask[0] | mask[1] | mask[2] | mask[3]); 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic int efx_ethtool_set_class_rule(struct efx_nic *efx, 9168c2ecf20Sopenharmony_ci struct ethtool_rx_flow_spec *rule, 9178c2ecf20Sopenharmony_ci u32 rss_context) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; 9208c2ecf20Sopenharmony_ci struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; 9218c2ecf20Sopenharmony_ci struct ethtool_usrip4_spec *uip_entry = &rule->h_u.usr_ip4_spec; 9228c2ecf20Sopenharmony_ci struct ethtool_usrip4_spec *uip_mask = &rule->m_u.usr_ip4_spec; 9238c2ecf20Sopenharmony_ci struct ethtool_tcpip6_spec *ip6_entry = &rule->h_u.tcp_ip6_spec; 9248c2ecf20Sopenharmony_ci struct ethtool_tcpip6_spec *ip6_mask = &rule->m_u.tcp_ip6_spec; 9258c2ecf20Sopenharmony_ci struct ethtool_usrip6_spec *uip6_entry = &rule->h_u.usr_ip6_spec; 9268c2ecf20Sopenharmony_ci struct ethtool_usrip6_spec *uip6_mask = &rule->m_u.usr_ip6_spec; 9278c2ecf20Sopenharmony_ci u32 flow_type = rule->flow_type & ~(FLOW_EXT | FLOW_RSS); 9288c2ecf20Sopenharmony_ci struct ethhdr *mac_entry = &rule->h_u.ether_spec; 9298c2ecf20Sopenharmony_ci struct ethhdr *mac_mask = &rule->m_u.ether_spec; 9308c2ecf20Sopenharmony_ci enum efx_filter_flags flags = 0; 9318c2ecf20Sopenharmony_ci struct efx_filter_spec spec; 9328c2ecf20Sopenharmony_ci int rc; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* Check that user wants us to choose the location */ 9358c2ecf20Sopenharmony_ci if (rule->location != RX_CLS_LOC_ANY) 9368c2ecf20Sopenharmony_ci return -EINVAL; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* Range-check ring_cookie */ 9398c2ecf20Sopenharmony_ci if (rule->ring_cookie >= efx->n_rx_channels && 9408c2ecf20Sopenharmony_ci rule->ring_cookie != RX_CLS_FLOW_DISC) 9418c2ecf20Sopenharmony_ci return -EINVAL; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* Check for unsupported extensions */ 9448c2ecf20Sopenharmony_ci if ((rule->flow_type & FLOW_EXT) && 9458c2ecf20Sopenharmony_ci (rule->m_ext.vlan_etype || rule->m_ext.data[0] || 9468c2ecf20Sopenharmony_ci rule->m_ext.data[1])) 9478c2ecf20Sopenharmony_ci return -EINVAL; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (efx->rx_scatter) 9508c2ecf20Sopenharmony_ci flags |= EFX_FILTER_FLAG_RX_SCATTER; 9518c2ecf20Sopenharmony_ci if (rule->flow_type & FLOW_RSS) 9528c2ecf20Sopenharmony_ci flags |= EFX_FILTER_FLAG_RX_RSS; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, flags, 9558c2ecf20Sopenharmony_ci (rule->ring_cookie == RX_CLS_FLOW_DISC) ? 9568c2ecf20Sopenharmony_ci EFX_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (rule->flow_type & FLOW_RSS) 9598c2ecf20Sopenharmony_ci spec.rss_context = rss_context; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci switch (flow_type) { 9628c2ecf20Sopenharmony_ci case TCP_V4_FLOW: 9638c2ecf20Sopenharmony_ci case UDP_V4_FLOW: 9648c2ecf20Sopenharmony_ci spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE | 9658c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_IP_PROTO); 9668c2ecf20Sopenharmony_ci spec.ether_type = htons(ETH_P_IP); 9678c2ecf20Sopenharmony_ci spec.ip_proto = flow_type == TCP_V4_FLOW ? IPPROTO_TCP 9688c2ecf20Sopenharmony_ci : IPPROTO_UDP; 9698c2ecf20Sopenharmony_ci if (ip_mask->ip4dst) { 9708c2ecf20Sopenharmony_ci if (ip_mask->ip4dst != IP4_ADDR_FULL_MASK) 9718c2ecf20Sopenharmony_ci return -EINVAL; 9728c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_HOST; 9738c2ecf20Sopenharmony_ci spec.loc_host[0] = ip_entry->ip4dst; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci if (ip_mask->ip4src) { 9768c2ecf20Sopenharmony_ci if (ip_mask->ip4src != IP4_ADDR_FULL_MASK) 9778c2ecf20Sopenharmony_ci return -EINVAL; 9788c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_REM_HOST; 9798c2ecf20Sopenharmony_ci spec.rem_host[0] = ip_entry->ip4src; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci if (ip_mask->pdst) { 9828c2ecf20Sopenharmony_ci if (ip_mask->pdst != PORT_FULL_MASK) 9838c2ecf20Sopenharmony_ci return -EINVAL; 9848c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_PORT; 9858c2ecf20Sopenharmony_ci spec.loc_port = ip_entry->pdst; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci if (ip_mask->psrc) { 9888c2ecf20Sopenharmony_ci if (ip_mask->psrc != PORT_FULL_MASK) 9898c2ecf20Sopenharmony_ci return -EINVAL; 9908c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_REM_PORT; 9918c2ecf20Sopenharmony_ci spec.rem_port = ip_entry->psrc; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci if (ip_mask->tos) 9948c2ecf20Sopenharmony_ci return -EINVAL; 9958c2ecf20Sopenharmony_ci break; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci case TCP_V6_FLOW: 9988c2ecf20Sopenharmony_ci case UDP_V6_FLOW: 9998c2ecf20Sopenharmony_ci spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE | 10008c2ecf20Sopenharmony_ci EFX_FILTER_MATCH_IP_PROTO); 10018c2ecf20Sopenharmony_ci spec.ether_type = htons(ETH_P_IPV6); 10028c2ecf20Sopenharmony_ci spec.ip_proto = flow_type == TCP_V6_FLOW ? IPPROTO_TCP 10038c2ecf20Sopenharmony_ci : IPPROTO_UDP; 10048c2ecf20Sopenharmony_ci if (!ip6_mask_is_empty(ip6_mask->ip6dst)) { 10058c2ecf20Sopenharmony_ci if (!ip6_mask_is_full(ip6_mask->ip6dst)) 10068c2ecf20Sopenharmony_ci return -EINVAL; 10078c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_HOST; 10088c2ecf20Sopenharmony_ci memcpy(spec.loc_host, ip6_entry->ip6dst, sizeof(spec.loc_host)); 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci if (!ip6_mask_is_empty(ip6_mask->ip6src)) { 10118c2ecf20Sopenharmony_ci if (!ip6_mask_is_full(ip6_mask->ip6src)) 10128c2ecf20Sopenharmony_ci return -EINVAL; 10138c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_REM_HOST; 10148c2ecf20Sopenharmony_ci memcpy(spec.rem_host, ip6_entry->ip6src, sizeof(spec.rem_host)); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci if (ip6_mask->pdst) { 10178c2ecf20Sopenharmony_ci if (ip6_mask->pdst != PORT_FULL_MASK) 10188c2ecf20Sopenharmony_ci return -EINVAL; 10198c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_PORT; 10208c2ecf20Sopenharmony_ci spec.loc_port = ip6_entry->pdst; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci if (ip6_mask->psrc) { 10238c2ecf20Sopenharmony_ci if (ip6_mask->psrc != PORT_FULL_MASK) 10248c2ecf20Sopenharmony_ci return -EINVAL; 10258c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_REM_PORT; 10268c2ecf20Sopenharmony_ci spec.rem_port = ip6_entry->psrc; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci if (ip6_mask->tclass) 10298c2ecf20Sopenharmony_ci return -EINVAL; 10308c2ecf20Sopenharmony_ci break; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci case IPV4_USER_FLOW: 10338c2ecf20Sopenharmony_ci if (uip_mask->l4_4_bytes || uip_mask->tos || uip_mask->ip_ver || 10348c2ecf20Sopenharmony_ci uip_entry->ip_ver != ETH_RX_NFC_IP4) 10358c2ecf20Sopenharmony_ci return -EINVAL; 10368c2ecf20Sopenharmony_ci spec.match_flags = EFX_FILTER_MATCH_ETHER_TYPE; 10378c2ecf20Sopenharmony_ci spec.ether_type = htons(ETH_P_IP); 10388c2ecf20Sopenharmony_ci if (uip_mask->ip4dst) { 10398c2ecf20Sopenharmony_ci if (uip_mask->ip4dst != IP4_ADDR_FULL_MASK) 10408c2ecf20Sopenharmony_ci return -EINVAL; 10418c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_HOST; 10428c2ecf20Sopenharmony_ci spec.loc_host[0] = uip_entry->ip4dst; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci if (uip_mask->ip4src) { 10458c2ecf20Sopenharmony_ci if (uip_mask->ip4src != IP4_ADDR_FULL_MASK) 10468c2ecf20Sopenharmony_ci return -EINVAL; 10478c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_REM_HOST; 10488c2ecf20Sopenharmony_ci spec.rem_host[0] = uip_entry->ip4src; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci if (uip_mask->proto) { 10518c2ecf20Sopenharmony_ci if (uip_mask->proto != IP_PROTO_FULL_MASK) 10528c2ecf20Sopenharmony_ci return -EINVAL; 10538c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_IP_PROTO; 10548c2ecf20Sopenharmony_ci spec.ip_proto = uip_entry->proto; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci break; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci case IPV6_USER_FLOW: 10598c2ecf20Sopenharmony_ci if (uip6_mask->l4_4_bytes || uip6_mask->tclass) 10608c2ecf20Sopenharmony_ci return -EINVAL; 10618c2ecf20Sopenharmony_ci spec.match_flags = EFX_FILTER_MATCH_ETHER_TYPE; 10628c2ecf20Sopenharmony_ci spec.ether_type = htons(ETH_P_IPV6); 10638c2ecf20Sopenharmony_ci if (!ip6_mask_is_empty(uip6_mask->ip6dst)) { 10648c2ecf20Sopenharmony_ci if (!ip6_mask_is_full(uip6_mask->ip6dst)) 10658c2ecf20Sopenharmony_ci return -EINVAL; 10668c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_HOST; 10678c2ecf20Sopenharmony_ci memcpy(spec.loc_host, uip6_entry->ip6dst, sizeof(spec.loc_host)); 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci if (!ip6_mask_is_empty(uip6_mask->ip6src)) { 10708c2ecf20Sopenharmony_ci if (!ip6_mask_is_full(uip6_mask->ip6src)) 10718c2ecf20Sopenharmony_ci return -EINVAL; 10728c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_REM_HOST; 10738c2ecf20Sopenharmony_ci memcpy(spec.rem_host, uip6_entry->ip6src, sizeof(spec.rem_host)); 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci if (uip6_mask->l4_proto) { 10768c2ecf20Sopenharmony_ci if (uip6_mask->l4_proto != IP_PROTO_FULL_MASK) 10778c2ecf20Sopenharmony_ci return -EINVAL; 10788c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_IP_PROTO; 10798c2ecf20Sopenharmony_ci spec.ip_proto = uip6_entry->l4_proto; 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci break; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci case ETHER_FLOW: 10848c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(mac_mask->h_dest)) { 10858c2ecf20Sopenharmony_ci if (ether_addr_equal(mac_mask->h_dest, 10868c2ecf20Sopenharmony_ci mac_addr_ig_mask)) 10878c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG; 10888c2ecf20Sopenharmony_ci else if (is_broadcast_ether_addr(mac_mask->h_dest)) 10898c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC; 10908c2ecf20Sopenharmony_ci else 10918c2ecf20Sopenharmony_ci return -EINVAL; 10928c2ecf20Sopenharmony_ci ether_addr_copy(spec.loc_mac, mac_entry->h_dest); 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(mac_mask->h_source)) { 10958c2ecf20Sopenharmony_ci if (!is_broadcast_ether_addr(mac_mask->h_source)) 10968c2ecf20Sopenharmony_ci return -EINVAL; 10978c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_REM_MAC; 10988c2ecf20Sopenharmony_ci ether_addr_copy(spec.rem_mac, mac_entry->h_source); 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci if (mac_mask->h_proto) { 11018c2ecf20Sopenharmony_ci if (mac_mask->h_proto != ETHER_TYPE_FULL_MASK) 11028c2ecf20Sopenharmony_ci return -EINVAL; 11038c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_ETHER_TYPE; 11048c2ecf20Sopenharmony_ci spec.ether_type = mac_entry->h_proto; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci break; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci default: 11098c2ecf20Sopenharmony_ci return -EINVAL; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if ((rule->flow_type & FLOW_EXT) && rule->m_ext.vlan_tci) { 11138c2ecf20Sopenharmony_ci if (rule->m_ext.vlan_tci != htons(0xfff)) 11148c2ecf20Sopenharmony_ci return -EINVAL; 11158c2ecf20Sopenharmony_ci spec.match_flags |= EFX_FILTER_MATCH_OUTER_VID; 11168c2ecf20Sopenharmony_ci spec.outer_vid = rule->h_ext.vlan_tci; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci rc = efx_filter_insert_filter(efx, &spec, true); 11208c2ecf20Sopenharmony_ci if (rc < 0) 11218c2ecf20Sopenharmony_ci return rc; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci rule->location = rc; 11248c2ecf20Sopenharmony_ci return 0; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ciint efx_ethtool_set_rxnfc(struct net_device *net_dev, 11288c2ecf20Sopenharmony_ci struct ethtool_rxnfc *info) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (efx_filter_get_rx_id_limit(efx) == 0) 11338c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci switch (info->cmd) { 11368c2ecf20Sopenharmony_ci case ETHTOOL_SRXCLSRLINS: 11378c2ecf20Sopenharmony_ci return efx_ethtool_set_class_rule(efx, &info->fs, 11388c2ecf20Sopenharmony_ci info->rss_context); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci case ETHTOOL_SRXCLSRLDEL: 11418c2ecf20Sopenharmony_ci return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL, 11428c2ecf20Sopenharmony_ci info->fs.location); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci default: 11458c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ciu32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (efx->n_rx_channels == 1) 11548c2ecf20Sopenharmony_ci return 0; 11558c2ecf20Sopenharmony_ci return ARRAY_SIZE(efx->rss_context.rx_indir_table); 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ciu32 efx_ethtool_get_rxfh_key_size(struct net_device *net_dev) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci return efx->type->rx_hash_key_size; 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ciint efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key, 11668c2ecf20Sopenharmony_ci u8 *hfunc) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 11698c2ecf20Sopenharmony_ci int rc; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci rc = efx->type->rx_pull_rss_config(efx); 11728c2ecf20Sopenharmony_ci if (rc) 11738c2ecf20Sopenharmony_ci return rc; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (hfunc) 11768c2ecf20Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 11778c2ecf20Sopenharmony_ci if (indir) 11788c2ecf20Sopenharmony_ci memcpy(indir, efx->rss_context.rx_indir_table, 11798c2ecf20Sopenharmony_ci sizeof(efx->rss_context.rx_indir_table)); 11808c2ecf20Sopenharmony_ci if (key) 11818c2ecf20Sopenharmony_ci memcpy(key, efx->rss_context.rx_hash_key, 11828c2ecf20Sopenharmony_ci efx->type->rx_hash_key_size); 11838c2ecf20Sopenharmony_ci return 0; 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ciint efx_ethtool_set_rxfh(struct net_device *net_dev, const u32 *indir, 11878c2ecf20Sopenharmony_ci const u8 *key, const u8 hfunc) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* Hash function is Toeplitz, cannot be changed */ 11928c2ecf20Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) 11938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11948c2ecf20Sopenharmony_ci if (!indir && !key) 11958c2ecf20Sopenharmony_ci return 0; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (!key) 11988c2ecf20Sopenharmony_ci key = efx->rss_context.rx_hash_key; 11998c2ecf20Sopenharmony_ci if (!indir) 12008c2ecf20Sopenharmony_ci indir = efx->rss_context.rx_indir_table; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci return efx->type->rx_push_rss_config(efx, true, indir, key); 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ciint efx_ethtool_get_rxfh_context(struct net_device *net_dev, u32 *indir, 12068c2ecf20Sopenharmony_ci u8 *key, u8 *hfunc, u32 rss_context) 12078c2ecf20Sopenharmony_ci{ 12088c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 12098c2ecf20Sopenharmony_ci struct efx_rss_context *ctx; 12108c2ecf20Sopenharmony_ci int rc = 0; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (!efx->type->rx_pull_rss_context_config) 12138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci mutex_lock(&efx->rss_lock); 12168c2ecf20Sopenharmony_ci ctx = efx_find_rss_context_entry(efx, rss_context); 12178c2ecf20Sopenharmony_ci if (!ctx) { 12188c2ecf20Sopenharmony_ci rc = -ENOENT; 12198c2ecf20Sopenharmony_ci goto out_unlock; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci rc = efx->type->rx_pull_rss_context_config(efx, ctx); 12228c2ecf20Sopenharmony_ci if (rc) 12238c2ecf20Sopenharmony_ci goto out_unlock; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (hfunc) 12268c2ecf20Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 12278c2ecf20Sopenharmony_ci if (indir) 12288c2ecf20Sopenharmony_ci memcpy(indir, ctx->rx_indir_table, sizeof(ctx->rx_indir_table)); 12298c2ecf20Sopenharmony_ci if (key) 12308c2ecf20Sopenharmony_ci memcpy(key, ctx->rx_hash_key, efx->type->rx_hash_key_size); 12318c2ecf20Sopenharmony_ciout_unlock: 12328c2ecf20Sopenharmony_ci mutex_unlock(&efx->rss_lock); 12338c2ecf20Sopenharmony_ci return rc; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ciint efx_ethtool_set_rxfh_context(struct net_device *net_dev, 12378c2ecf20Sopenharmony_ci const u32 *indir, const u8 *key, 12388c2ecf20Sopenharmony_ci const u8 hfunc, u32 *rss_context, 12398c2ecf20Sopenharmony_ci bool delete) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 12428c2ecf20Sopenharmony_ci struct efx_rss_context *ctx; 12438c2ecf20Sopenharmony_ci bool allocated = false; 12448c2ecf20Sopenharmony_ci int rc; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (!efx->type->rx_push_rss_context_config) 12478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12488c2ecf20Sopenharmony_ci /* Hash function is Toeplitz, cannot be changed */ 12498c2ecf20Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) 12508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci mutex_lock(&efx->rss_lock); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) { 12558c2ecf20Sopenharmony_ci if (delete) { 12568c2ecf20Sopenharmony_ci /* alloc + delete == Nothing to do */ 12578c2ecf20Sopenharmony_ci rc = -EINVAL; 12588c2ecf20Sopenharmony_ci goto out_unlock; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci ctx = efx_alloc_rss_context_entry(efx); 12618c2ecf20Sopenharmony_ci if (!ctx) { 12628c2ecf20Sopenharmony_ci rc = -ENOMEM; 12638c2ecf20Sopenharmony_ci goto out_unlock; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci ctx->context_id = EFX_MCDI_RSS_CONTEXT_INVALID; 12668c2ecf20Sopenharmony_ci /* Initialise indir table and key to defaults */ 12678c2ecf20Sopenharmony_ci efx_set_default_rx_indir_table(efx, ctx); 12688c2ecf20Sopenharmony_ci netdev_rss_key_fill(ctx->rx_hash_key, sizeof(ctx->rx_hash_key)); 12698c2ecf20Sopenharmony_ci allocated = true; 12708c2ecf20Sopenharmony_ci } else { 12718c2ecf20Sopenharmony_ci ctx = efx_find_rss_context_entry(efx, *rss_context); 12728c2ecf20Sopenharmony_ci if (!ctx) { 12738c2ecf20Sopenharmony_ci rc = -ENOENT; 12748c2ecf20Sopenharmony_ci goto out_unlock; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (delete) { 12798c2ecf20Sopenharmony_ci /* delete this context */ 12808c2ecf20Sopenharmony_ci rc = efx->type->rx_push_rss_context_config(efx, ctx, NULL, NULL); 12818c2ecf20Sopenharmony_ci if (!rc) 12828c2ecf20Sopenharmony_ci efx_free_rss_context_entry(ctx); 12838c2ecf20Sopenharmony_ci goto out_unlock; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (!key) 12878c2ecf20Sopenharmony_ci key = ctx->rx_hash_key; 12888c2ecf20Sopenharmony_ci if (!indir) 12898c2ecf20Sopenharmony_ci indir = ctx->rx_indir_table; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci rc = efx->type->rx_push_rss_context_config(efx, ctx, indir, key); 12928c2ecf20Sopenharmony_ci if (rc && allocated) 12938c2ecf20Sopenharmony_ci efx_free_rss_context_entry(ctx); 12948c2ecf20Sopenharmony_ci else 12958c2ecf20Sopenharmony_ci *rss_context = ctx->user_id; 12968c2ecf20Sopenharmony_ciout_unlock: 12978c2ecf20Sopenharmony_ci mutex_unlock(&efx->rss_lock); 12988c2ecf20Sopenharmony_ci return rc; 12998c2ecf20Sopenharmony_ci} 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ciint efx_ethtool_reset(struct net_device *net_dev, u32 *flags) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 13048c2ecf20Sopenharmony_ci int rc; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci rc = efx->type->map_reset_flags(flags); 13078c2ecf20Sopenharmony_ci if (rc < 0) 13088c2ecf20Sopenharmony_ci return rc; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci return efx_reset(efx, rc); 13118c2ecf20Sopenharmony_ci} 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ciint efx_ethtool_get_module_eeprom(struct net_device *net_dev, 13148c2ecf20Sopenharmony_ci struct ethtool_eeprom *ee, 13158c2ecf20Sopenharmony_ci u8 *data) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 13188c2ecf20Sopenharmony_ci int ret; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci mutex_lock(&efx->mac_lock); 13218c2ecf20Sopenharmony_ci ret = efx_mcdi_phy_get_module_eeprom(efx, ee, data); 13228c2ecf20Sopenharmony_ci mutex_unlock(&efx->mac_lock); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci return ret; 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ciint efx_ethtool_get_module_info(struct net_device *net_dev, 13288c2ecf20Sopenharmony_ci struct ethtool_modinfo *modinfo) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 13318c2ecf20Sopenharmony_ci int ret; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci mutex_lock(&efx->mac_lock); 13348c2ecf20Sopenharmony_ci ret = efx_mcdi_phy_get_module_info(efx, modinfo); 13358c2ecf20Sopenharmony_ci mutex_unlock(&efx->mac_lock); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci return ret; 13388c2ecf20Sopenharmony_ci} 1339