diff options
Diffstat (limited to 'drivers/net/ethernet/sfc/ethtool.c')
| -rw-r--r-- | drivers/net/ethernet/sfc/ethtool.c | 630 |
1 files changed, 366 insertions, 264 deletions
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 90158c913fb..74739c4b999 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -1,7 +1,7 @@ /**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards + * Driver for Solarflare network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2010 Solarflare Communications Inc. + * Copyright 2006-2013 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -19,14 +19,9 @@ #include "filter.h" #include "nic.h" -struct ethtool_string { - char name[ETH_GSTRING_LEN]; -}; - -struct efx_ethtool_stat { +struct efx_sw_stat_desc { const char *name; enum { - EFX_ETHTOOL_STAT_SOURCE_mac_stats, EFX_ETHTOOL_STAT_SOURCE_nic, EFX_ETHTOOL_STAT_SOURCE_channel, EFX_ETHTOOL_STAT_SOURCE_tx_queue @@ -35,7 +30,7 @@ struct efx_ethtool_stat { u64(*get_stat) (void *field); /* Reader function */ }; -/* Initialiser for a struct #efx_ethtool_stat with type-checking */ +/* Initialiser for a struct efx_sw_stat_desc with type-checking */ #define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \ get_stat_function) { \ .name = #stat_name, \ @@ -52,33 +47,11 @@ static u64 efx_get_uint_stat(void *field) return *(unsigned int *)field; } -static u64 efx_get_ulong_stat(void *field) -{ - return *(unsigned long *)field; -} - -static u64 efx_get_u64_stat(void *field) -{ - return *(u64 *) field; -} - static u64 efx_get_atomic_stat(void *field) { return atomic_read((atomic_t *) field); } -#define EFX_ETHTOOL_ULONG_MAC_STAT(field) \ - EFX_ETHTOOL_STAT(field, mac_stats, field, \ - unsigned long, efx_get_ulong_stat) - -#define EFX_ETHTOOL_U64_MAC_STAT(field) \ - EFX_ETHTOOL_STAT(field, mac_stats, field, \ - u64, efx_get_u64_stat) - -#define EFX_ETHTOOL_UINT_NIC_STAT(name) \ - EFX_ETHTOOL_STAT(name, nic, n_##name, \ - unsigned int, efx_get_uint_stat) - #define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \ EFX_ETHTOOL_STAT(field, nic, field, \ atomic_t, efx_get_atomic_stat) @@ -91,82 +64,25 @@ static u64 efx_get_atomic_stat(void *field) EFX_ETHTOOL_STAT(tx_##field, tx_queue, field, \ unsigned int, efx_get_uint_stat) -static struct efx_ethtool_stat efx_ethtool_stats[] = { - EFX_ETHTOOL_U64_MAC_STAT(tx_bytes), - EFX_ETHTOOL_U64_MAC_STAT(tx_good_bytes), - EFX_ETHTOOL_U64_MAC_STAT(tx_bad_bytes), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_packets), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_bad), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_pause), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_control), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_unicast), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_multicast), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_broadcast), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_lt64), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_64), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_65_to_127), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_128_to_255), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_256_to_511), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_512_to_1023), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_1024_to_15xx), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_15xx_to_jumbo), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_gtjumbo), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_collision), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_single_collision), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_multiple_collision), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_collision), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_deferred), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_late_collision), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_deferred), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_non_tcpudp), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_mac_src_error), - EFX_ETHTOOL_ULONG_MAC_STAT(tx_ip_src_error), +static const struct efx_sw_stat_desc efx_sw_stat_desc[] = { + EFX_ETHTOOL_UINT_TXQ_STAT(merge_events), EFX_ETHTOOL_UINT_TXQ_STAT(tso_bursts), EFX_ETHTOOL_UINT_TXQ_STAT(tso_long_headers), EFX_ETHTOOL_UINT_TXQ_STAT(tso_packets), EFX_ETHTOOL_UINT_TXQ_STAT(pushes), - EFX_ETHTOOL_U64_MAC_STAT(rx_bytes), - EFX_ETHTOOL_U64_MAC_STAT(rx_good_bytes), - EFX_ETHTOOL_U64_MAC_STAT(rx_bad_bytes), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_packets), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_good), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_pause), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_control), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_unicast), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_multicast), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_broadcast), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_lt64), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_64), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_65_to_127), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_128_to_255), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_256_to_511), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_512_to_1023), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_1024_to_15xx), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_15xx_to_jumbo), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_gtjumbo), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_lt64), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_64_to_15xx), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_15xx_to_jumbo), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_gtjumbo), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_overflow), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_missed), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_false_carrier), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_symbol_error), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_align_error), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_length_error), - EFX_ETHTOOL_ULONG_MAC_STAT(rx_internal_error), - EFX_ETHTOOL_UINT_NIC_STAT(rx_nodesc_drop_cnt), + EFX_ETHTOOL_UINT_TXQ_STAT(pio_packets), EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_nodesc_trunc), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_merge_events), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_merge_packets), }; -/* Number of ethtool statistics */ -#define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats) +#define EFX_ETHTOOL_SW_STAT_COUNT ARRAY_SIZE(efx_sw_stat_desc) #define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB @@ -213,8 +129,6 @@ static int efx_ethtool_get_settings(struct net_device *net_dev, efx->phy_op->get_settings(efx, ecmd); mutex_unlock(&efx->mac_lock); - /* GMAC does not support 1000Mbps HD */ - ecmd->supported &= ~SUPPORTED_1000baseT_Half; /* Both MACs support pause frames (bidirectional and respond-only) */ ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; @@ -299,12 +213,11 @@ static void efx_ethtool_set_msglevel(struct net_device *net_dev, u32 msg_enable) * * Fill in an individual self-test entry. */ -static void efx_fill_test(unsigned int test_index, - struct ethtool_string *strings, u64 *data, +static void efx_fill_test(unsigned int test_index, u8 *strings, u64 *data, int *test, const char *unit_format, int unit_id, const char *test_format, const char *test_id) { - struct ethtool_string unit_str, test_str; + char unit_str[ETH_GSTRING_LEN], test_str[ETH_GSTRING_LEN]; /* Fill data value, if applicable */ if (data) @@ -313,15 +226,14 @@ static void efx_fill_test(unsigned int test_index, /* Fill string, if applicable */ if (strings) { if (strchr(unit_format, '%')) - snprintf(unit_str.name, sizeof(unit_str.name), + snprintf(unit_str, sizeof(unit_str), unit_format, unit_id); else - strcpy(unit_str.name, unit_format); - snprintf(test_str.name, sizeof(test_str.name), - test_format, test_id); - snprintf(strings[test_index].name, - sizeof(strings[test_index].name), - "%-6s %-24s", unit_str.name, test_str.name); + strcpy(unit_str, unit_format); + snprintf(test_str, sizeof(test_str), test_format, test_id); + snprintf(strings + test_index * ETH_GSTRING_LEN, + ETH_GSTRING_LEN, + "%-6s %-24s", unit_str, test_str); } } @@ -339,14 +251,18 @@ static void efx_fill_test(unsigned int test_index, * @test_index: Starting index of the test * @strings: Ethtool strings, or %NULL * @data: Ethtool test results, or %NULL + * + * Fill in a block of loopback self-test entries. Return new test + * index. */ static int efx_fill_loopback_test(struct efx_nic *efx, struct efx_loopback_self_tests *lb_tests, enum efx_loopback_mode mode, unsigned int test_index, - struct ethtool_string *strings, u64 *data) + u8 *strings, u64 *data) { - struct efx_channel *channel = efx_get_channel(efx, 0); + struct efx_channel *channel = + efx_get_channel(efx, efx->tx_channel_offset); struct efx_tx_queue *tx_queue; efx_for_each_channel_tx_queue(tx_queue, channel) { @@ -377,11 +293,16 @@ static int efx_fill_loopback_test(struct efx_nic *efx, * @tests: Efx self-test results structure, or %NULL * @strings: Ethtool strings, or %NULL * @data: Ethtool test results, or %NULL + * + * Get self-test number of strings, strings, and/or test results. + * Return number of strings (== number of test results). + * + * The reason for merging these three functions is to make sure that + * they can never be inconsistent. */ static int efx_ethtool_fill_self_tests(struct efx_nic *efx, struct efx_self_tests *tests, - struct ethtool_string *strings, - u64 *data) + u8 *strings, u64 *data) { struct efx_channel *channel; unsigned int n = 0, i; @@ -404,12 +325,10 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx, &tests->eventq_int[channel->channel], EFX_CHANNEL_NAME(channel), "eventq.int", NULL); - efx_fill_test(n++, strings, data, - &tests->eventq_poll[channel->channel], - EFX_CHANNEL_NAME(channel), - "eventq.poll", NULL); } + efx_fill_test(n++, strings, data, &tests->memory, + "core", 0, "memory", NULL); efx_fill_test(n++, strings, data, &tests->registers, "core", 0, "registers", NULL); @@ -444,12 +363,15 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx, static int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set) { + struct efx_nic *efx = netdev_priv(net_dev); + switch (string_set) { case ETH_SS_STATS: - return EFX_ETHTOOL_NUM_STATS; + return efx->type->describe_stats(efx, NULL) + + EFX_ETHTOOL_SW_STAT_COUNT + + efx_ptp_describe_stats(efx, NULL); case ETH_SS_TEST: - return efx_ethtool_fill_self_tests(netdev_priv(net_dev), - NULL, NULL, NULL); + return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); default: return -EINVAL; } @@ -459,20 +381,20 @@ static void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set, u8 *strings) { struct efx_nic *efx = netdev_priv(net_dev); - struct ethtool_string *ethtool_strings = - (struct ethtool_string *)strings; int i; switch (string_set) { case ETH_SS_STATS: - for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) - strncpy(ethtool_strings[i].name, - efx_ethtool_stats[i].name, - sizeof(ethtool_strings[i].name)); + strings += (efx->type->describe_stats(efx, strings) * + ETH_GSTRING_LEN); + for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) + strlcpy(strings + i * ETH_GSTRING_LEN, + efx_sw_stat_desc[i].name, ETH_GSTRING_LEN); + strings += EFX_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN; + efx_ptp_describe_stats(efx, strings); break; case ETH_SS_TEST: - efx_ethtool_fill_self_tests(efx, NULL, - ethtool_strings, NULL); + efx_ethtool_fill_self_tests(efx, NULL, strings, NULL); break; default: /* No other string sets */ @@ -485,26 +407,20 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, u64 *data) { struct efx_nic *efx = netdev_priv(net_dev); - struct efx_mac_stats *mac_stats = &efx->mac_stats; - struct efx_ethtool_stat *stat; + const struct efx_sw_stat_desc *stat; struct efx_channel *channel; struct efx_tx_queue *tx_queue; - struct rtnl_link_stats64 temp; int i; - EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); + spin_lock_bh(&efx->stats_lock); - /* Update MAC and NIC statistics */ - dev_get_stats(net_dev, &temp); + /* Get NIC statistics */ + data += efx->type->update_stats(efx, data, NULL); - /* Fill detailed statistics buffer */ - for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { - stat = &efx_ethtool_stats[i]; + /* Get software statistics */ + for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) { + stat = &efx_sw_stat_desc[i]; switch (stat->source) { - case EFX_ETHTOOL_STAT_SOURCE_mac_stats: - data[i] = stat->get_stat((void *)mac_stats + - stat->offset); - break; case EFX_ETHTOOL_STAT_SOURCE_nic: data[i] = stat->get_stat((void *)efx + stat->offset); break; @@ -525,6 +441,11 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, break; } } + data += EFX_ETHTOOL_SW_STAT_COUNT; + + spin_unlock_bh(&efx->stats_lock); + + efx_ptp_update_stats(efx, data); } static void efx_ethtool_self_test(struct net_device *net_dev, @@ -532,18 +453,16 @@ static void efx_ethtool_self_test(struct net_device *net_dev, { struct efx_nic *efx = netdev_priv(net_dev); struct efx_self_tests *efx_tests; - int already_up; + bool already_up; int rc = -ENOMEM; efx_tests = kzalloc(sizeof(*efx_tests), GFP_KERNEL); if (!efx_tests) goto fail; - - ASSERT_RTNL(); - if (efx->state != STATE_RUNNING) { - rc = -EIO; - goto fail1; + if (efx->state != STATE_READY) { + rc = -EBUSY; + goto out; } netif_info(efx, drv, efx->net_dev, "starting %sline testing\n", @@ -556,7 +475,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev, if (rc) { netif_err(efx, drv, efx->net_dev, "failed opening device.\n"); - goto fail1; + goto out; } } @@ -569,8 +488,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev, rc == 0 ? "passed" : "failed", (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); -fail1: - /* Fill ethtool results structures */ +out: efx_ethtool_fill_self_tests(efx, efx_tests, NULL, data); kfree(efx_tests); fail: @@ -681,7 +599,7 @@ static void efx_ethtool_get_ringparam(struct net_device *net_dev, struct efx_nic *efx = netdev_priv(net_dev); ring->rx_max_pending = EFX_MAX_DMAQ_SIZE; - ring->tx_max_pending = EFX_MAX_DMAQ_SIZE; + ring->tx_max_pending = EFX_TXQ_MAX_ENT(efx); ring->rx_pending = efx->rxq_entries; ring->tx_pending = efx->txq_entries; } @@ -694,7 +612,7 @@ static int efx_ethtool_set_ringparam(struct net_device *net_dev, if (ring->rx_mini_pending || ring->rx_jumbo_pending || ring->rx_pending > EFX_MAX_DMAQ_SIZE || - ring->tx_pending > EFX_MAX_DMAQ_SIZE) + ring->tx_pending > EFX_TXQ_MAX_ENT(efx)) return -EINVAL; if (ring->rx_pending < EFX_RXQ_MIN_ENT) { @@ -719,7 +637,6 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, struct efx_nic *efx = netdev_priv(net_dev); u8 wanted_fc, old_fc; u32 old_adv; - bool reset; int rc = 0; mutex_lock(&efx->mac_lock); @@ -742,24 +659,10 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, goto out; } - /* TX flow control may automatically turn itself off if the - * link partner (intermittently) stops responding to pause - * frames. There isn't any indication that this has happened, - * so the best we do is leave it up to the user to spot this - * and fix it be cycling transmit flow control on this end. */ - reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX); - if (EFX_WORKAROUND_11482(efx) && reset) { - if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { - /* Recover by resetting the EM block */ - falcon_stop_nic_stats(efx); - falcon_drain_tx_fifo(efx); - efx->mac_op->reconfigure(efx); - falcon_start_nic_stats(efx); - } else { - /* Schedule a reset to recover */ - efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); - } - } + /* Hook for Falcon bug 11482 workaround */ + if (efx->type->prepare_enable_fc_tx && + (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX)) + efx->type->prepare_enable_fc_tx(efx); old_adv = efx->link_advertising; old_fc = efx->wanted_fc; @@ -778,7 +681,7 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, /* Reconfigure the MAC. The PHY *may* generate a link state change event * if the user just changed the advertised capabilities, but there's no * harm doing this twice */ - efx->mac_op->reconfigure(efx); + efx->type->reconfigure_mac(efx); out: mutex_unlock(&efx->mac_lock); @@ -796,7 +699,6 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev, pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO); } - static void efx_ethtool_get_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol) { @@ -824,9 +726,100 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) return efx_reset(efx, rc); } +/* MAC address mask including only I/G bit */ +static const u8 mac_addr_ig_mask[ETH_ALEN] __aligned(2) = {0x01, 0, 0, 0, 0, 0}; + +#define IP4_ADDR_FULL_MASK ((__force __be32)~0) +#define PORT_FULL_MASK ((__force __be16)~0) +#define ETHER_TYPE_FULL_MASK ((__force __be16)~0) + +static int efx_ethtool_get_class_rule(struct efx_nic *efx, + struct ethtool_rx_flow_spec *rule) +{ + struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; + struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; + struct ethhdr *mac_entry = &rule->h_u.ether_spec; + struct ethhdr *mac_mask = &rule->m_u.ether_spec; + struct efx_filter_spec spec; + int rc; + + rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL, + rule->location, &spec); + if (rc) + return rc; + + if (spec.dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP) + rule->ring_cookie = RX_CLS_FLOW_DISC; + else + rule->ring_cookie = spec.dmaq_id; + + if ((spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) && + spec.ether_type == htons(ETH_P_IP) && + (spec.match_flags & EFX_FILTER_MATCH_IP_PROTO) && + (spec.ip_proto == IPPROTO_TCP || spec.ip_proto == IPPROTO_UDP) && + !(spec.match_flags & + ~(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID | + EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST | + EFX_FILTER_MATCH_IP_PROTO | + EFX_FILTER_MATCH_LOC_PORT | EFX_FILTER_MATCH_REM_PORT))) { + rule->flow_type = ((spec.ip_proto == IPPROTO_TCP) ? + TCP_V4_FLOW : UDP_V4_FLOW); + if (spec.match_flags & EFX_FILTER_MATCH_LOC_HOST) { + ip_entry->ip4dst = spec.loc_host[0]; + ip_mask->ip4dst = IP4_ADDR_FULL_MASK; + } + if (spec.match_flags & EFX_FILTER_MATCH_REM_HOST) { + ip_entry->ip4src = spec.rem_host[0]; + ip_mask->ip4src = IP4_ADDR_FULL_MASK; + } + if (spec.match_flags & EFX_FILTER_MATCH_LOC_PORT) { + ip_entry->pdst = spec.loc_port; + ip_mask->pdst = PORT_FULL_MASK; + } + if (spec.match_flags & EFX_FILTER_MATCH_REM_PORT) { + ip_entry->psrc = spec.rem_port; + ip_mask->psrc = PORT_FULL_MASK; + } + } else if (!(spec.match_flags & + ~(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG | + EFX_FILTER_MATCH_REM_MAC | EFX_FILTER_MATCH_ETHER_TYPE | + EFX_FILTER_MATCH_OUTER_VID))) { + rule->flow_type = ETHER_FLOW; + if (spec.match_flags & + (EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG)) { + ether_addr_copy(mac_entry->h_dest, spec.loc_mac); + if (spec.match_flags & EFX_FILTER_MATCH_LOC_MAC) + eth_broadcast_addr(mac_mask->h_dest); + else + ether_addr_copy(mac_mask->h_dest, + mac_addr_ig_mask); + } + if (spec.match_flags & EFX_FILTER_MATCH_REM_MAC) { + ether_addr_copy(mac_entry->h_source, spec.rem_mac); + eth_broadcast_addr(mac_mask->h_source); + } + if (spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) { + mac_entry->h_proto = spec.ether_type; + mac_mask->h_proto = ETHER_TYPE_FULL_MASK; + } + } else { + /* The above should handle all filters that we insert */ + WARN_ON(1); + return -EINVAL; + } + + if (spec.match_flags & EFX_FILTER_MATCH_OUTER_VID) { + rule->flow_type |= FLOW_EXT; + rule->h_ext.vlan_tci = spec.outer_vid; + rule->m_ext.vlan_tci = htons(0xfff); + } + + return rc; +} + static int efx_ethtool_get_rxnfc(struct net_device *net_dev, - struct ethtool_rxnfc *info, u32 *rules __always_unused) + struct ethtool_rxnfc *info, u32 *rule_locs) { struct efx_nic *efx = netdev_priv(net_dev); @@ -868,138 +861,243 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, return 0; } + case ETHTOOL_GRXCLSRLCNT: + info->data = efx_filter_get_rx_id_limit(efx); + if (info->data == 0) + return -EOPNOTSUPP; + info->data |= RX_CLS_LOC_SPECIAL; + info->rule_cnt = + efx_filter_count_rx_used(efx, EFX_FILTER_PRI_MANUAL); + return 0; + + case ETHTOOL_GRXCLSRULE: + if (efx_filter_get_rx_id_limit(efx) == 0) + return -EOPNOTSUPP; + return efx_ethtool_get_class_rule(efx, &info->fs); + + case ETHTOOL_GRXCLSRLALL: { + s32 rc; + info->data = efx_filter_get_rx_id_limit(efx); + if (info->data == 0) + return -EOPNOTSUPP; + rc = efx_filter_get_rx_ids(efx, EFX_FILTER_PRI_MANUAL, + rule_locs, info->rule_cnt); + if (rc < 0) + return rc; + info->rule_cnt = rc; + return 0; + } + default: return -EOPNOTSUPP; } } -static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, - struct ethtool_rx_ntuple *ntuple) +static int efx_ethtool_set_class_rule(struct efx_nic *efx, + struct ethtool_rx_flow_spec *rule) { - struct efx_nic *efx = netdev_priv(net_dev); - struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec; - struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec; - struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec; - struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec; - struct efx_filter_spec filter; + struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; + struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; + struct ethhdr *mac_entry = &rule->h_u.ether_spec; + struct ethhdr *mac_mask = &rule->m_u.ether_spec; + struct efx_filter_spec spec; int rc; - /* Range-check action */ - if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR || - ntuple->fs.action >= (s32)efx->n_rx_channels) + /* Check that user wants us to choose the location */ + if (rule->location != RX_CLS_LOC_ANY) + return -EINVAL; + + /* Range-check ring_cookie */ + if (rule->ring_cookie >= efx->n_rx_channels && + rule->ring_cookie != RX_CLS_FLOW_DISC) return -EINVAL; - if (~ntuple->fs.data_mask) + /* Check for unsupported extensions */ + if ((rule->flow_type & FLOW_EXT) && + (rule->m_ext.vlan_etype || rule->m_ext.data[0] || + rule->m_ext.data[1])) return -EINVAL; - efx_filter_init_rx(&filter, EFX_FILTER_PRI_MANUAL, 0, - (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) ? - 0xfff : ntuple->fs.action); + efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, + efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0, + (rule->ring_cookie == RX_CLS_FLOW_DISC) ? + EFX_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie); - switch (ntuple->fs.flow_type) { + switch (rule->flow_type & ~FLOW_EXT) { case TCP_V4_FLOW: - case UDP_V4_FLOW: { - u8 proto = (ntuple->fs.flow_type == TCP_V4_FLOW ? - IPPROTO_TCP : IPPROTO_UDP); - - /* Must match all of destination, */ - if (ip_mask->ip4dst | ip_mask->pdst) - return -EINVAL; - /* all or none of source, */ - if ((ip_mask->ip4src | ip_mask->psrc) && - ((__force u32)~ip_mask->ip4src | - (__force u16)~ip_mask->psrc)) - return -EINVAL; - /* and nothing else */ - if ((u8)~ip_mask->tos | (u16)~ntuple->fs.vlan_tag_mask) + case UDP_V4_FLOW: + spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE | + EFX_FILTER_MATCH_IP_PROTO); + spec.ether_type = htons(ETH_P_IP); + spec.ip_proto = ((rule->flow_type & ~FLOW_EXT) == TCP_V4_FLOW ? + IPPROTO_TCP : IPPROTO_UDP); + if (ip_mask->ip4dst) { + if (ip_mask->ip4dst != IP4_ADDR_FULL_MASK) + return -EINVAL; + spec.match_flags |= EFX_FILTER_MATCH_LOC_HOST; + spec.loc_host[0] = ip_entry->ip4dst; + } + if (ip_mask->ip4src) { + if (ip_mask->ip4src != IP4_ADDR_FULL_MASK) + return -EINVAL; + spec.match_flags |= EFX_FILTER_MATCH_REM_HOST; + spec.rem_host[0] = ip_entry->ip4src; + } + if (ip_mask->pdst) { + if (ip_mask->pdst != PORT_FULL_MASK) + return -EINVAL; + spec.match_flags |= EFX_FILTER_MATCH_LOC_PORT; + spec.loc_port = ip_entry->pdst; + } + if (ip_mask->psrc) { + if (ip_mask->psrc != PORT_FULL_MASK) + return -EINVAL; + spec.match_flags |= EFX_FILTER_MATCH_REM_PORT; + spec.rem_port = ip_entry->psrc; + } + if (ip_mask->tos) return -EINVAL; - - if (!ip_mask->ip4src) - rc = efx_filter_set_ipv4_full(&filter, proto, - ip_entry->ip4dst, - ip_entry->pdst, - ip_entry->ip4src, - ip_entry->psrc); - else - rc = efx_filter_set_ipv4_local(&filter, proto, - ip_entry->ip4dst, - ip_entry->pdst); - if (rc) - return rc; break; - } case ETHER_FLOW: - /* Must match all of destination, */ - if (!is_zero_ether_addr(mac_mask->h_dest)) - return -EINVAL; - /* all or none of VID, */ - if (ntuple->fs.vlan_tag_mask != 0xf000 && - ntuple->fs.vlan_tag_mask != 0xffff) - return -EINVAL; - /* and nothing else */ - if (!is_broadcast_ether_addr(mac_mask->h_source) || - mac_mask->h_proto != htons(0xffff)) - return -EINVAL; - - rc = efx_filter_set_eth_local( - &filter, - (ntuple->fs.vlan_tag_mask == 0xf000) ? - ntuple->fs.vlan_tag : EFX_FILTER_VID_UNSPEC, - mac_entry->h_dest); - if (rc) - return rc; + if (!is_zero_ether_addr(mac_mask->h_dest)) { + if (ether_addr_equal(mac_mask->h_dest, + mac_addr_ig_mask)) + spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG; + else if (is_broadcast_ether_addr(mac_mask->h_dest)) + spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC; + else + return -EINVAL; + ether_addr_copy(spec.loc_mac, mac_entry->h_dest); + } + if (!is_zero_ether_addr(mac_mask->h_source)) { + if (!is_broadcast_ether_addr(mac_mask->h_source)) + return -EINVAL; + spec.match_flags |= EFX_FILTER_MATCH_REM_MAC; + ether_addr_copy(spec.rem_mac, mac_entry->h_source); + } + if (mac_mask->h_proto) { + if (mac_mask->h_proto != ETHER_TYPE_FULL_MASK) + return -EINVAL; + spec.match_flags |= EFX_FILTER_MATCH_ETHER_TYPE; + spec.ether_type = mac_entry->h_proto; + } break; default: return -EINVAL; } - if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR) - return efx_filter_remove_filter(efx, &filter); + if ((rule->flow_type & FLOW_EXT) && rule->m_ext.vlan_tci) { + if (rule->m_ext.vlan_tci != htons(0xfff)) + return -EINVAL; + spec.match_flags |= EFX_FILTER_MATCH_OUTER_VID; + spec.outer_vid = rule->h_ext.vlan_tci; + } + + rc = efx_filter_insert_filter(efx, &spec, true); + if (rc < 0) + return rc; - rc = efx_filter_insert_filter(efx, &filter, true); - return rc < 0 ? rc : 0; + rule->location = rc; + return 0; } -static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, - struct ethtool_rxfh_indir *indir) +static int efx_ethtool_set_rxnfc(struct net_device *net_dev, + struct ethtool_rxnfc *info) { struct efx_nic *efx = netdev_priv(net_dev); - size_t copy_size = - min_t(size_t, indir->size, ARRAY_SIZE(efx->rx_indir_table)); - if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) + if (efx_filter_get_rx_id_limit(efx) == 0) + return -EOPNOTSUPP; + + switch (info->cmd) { + case ETHTOOL_SRXCLSRLINS: + return efx_ethtool_set_class_rule(efx, &info->fs); + + case ETHTOOL_SRXCLSRLDEL: + return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL, + info->fs.location); + + default: return -EOPNOTSUPP; + } +} + +static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev) +{ + struct efx_nic *efx = netdev_priv(net_dev); - indir->size = ARRAY_SIZE(efx->rx_indir_table); - memcpy(indir->ring_index, efx->rx_indir_table, - copy_size * sizeof(indir->ring_index[0])); + return ((efx_nic_rev(efx) < EFX_REV_FALCON_B0 || + efx->n_rx_channels == 1) ? + 0 : ARRAY_SIZE(efx->rx_indir_table)); +} + +static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key) +{ + struct efx_nic *efx = netdev_priv(net_dev); + + memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table)); return 0; } -static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev, - const struct ethtool_rxfh_indir *indir) +static int efx_ethtool_set_rxfh(struct net_device *net_dev, + const u32 *indir, const u8 *key) { struct efx_nic *efx = netdev_priv(net_dev); - size_t i; - if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) - return -EOPNOTSUPP; + memcpy(efx->rx_indir_table, indir, sizeof(efx->rx_indir_table)); + efx->type->rx_push_rss_config(efx); + return 0; +} - /* Validate size and indices */ - if (indir->size != ARRAY_SIZE(efx->rx_indir_table)) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++) - if (indir->ring_index[i] >= efx->n_rx_channels) - return -EINVAL; +static int efx_ethtool_get_ts_info(struct net_device *net_dev, + struct ethtool_ts_info *ts_info) +{ + struct efx_nic *efx = netdev_priv(net_dev); + + /* Software capabilities */ + ts_info->so_timestamping = (SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE); + ts_info->phc_index = -1; - memcpy(efx->rx_indir_table, indir->ring_index, - sizeof(efx->rx_indir_table)); - efx_nic_push_rx_indir_table(efx); + efx_ptp_get_ts_info(efx, ts_info); return 0; } +static int efx_ethtool_get_module_eeprom(struct net_device *net_dev, + struct ethtool_eeprom *ee, + u8 *data) +{ + struct efx_nic *efx = netdev_priv(net_dev); + int ret; + + if (!efx->phy_op || !efx->phy_op->get_module_eeprom) + return -EOPNOTSUPP; + + mutex_lock(&efx->mac_lock); + ret = efx->phy_op->get_module_eeprom(efx, ee, data); + mutex_unlock(&efx->mac_lock); + + return ret; +} + +static int efx_ethtool_get_module_info(struct net_device *net_dev, + struct ethtool_modinfo *modinfo) +{ + struct efx_nic *efx = netdev_priv(net_dev); + int ret; + + if (!efx->phy_op || !efx->phy_op->get_module_info) + return -EOPNOTSUPP; + + mutex_lock(&efx->mac_lock); + ret = efx->phy_op->get_module_info(efx, modinfo); + mutex_unlock(&efx->mac_lock); + + return ret; +} + const struct ethtool_ops efx_ethtool_ops = { .get_settings = efx_ethtool_get_settings, .set_settings = efx_ethtool_set_settings, @@ -1025,7 +1123,11 @@ const struct ethtool_ops efx_ethtool_ops = { .set_wol = efx_ethtool_set_wol, .reset = efx_ethtool_reset, .get_rxnfc = efx_ethtool_get_rxnfc, - .set_rx_ntuple = efx_ethtool_set_rx_ntuple, - .get_rxfh_indir = efx_ethtool_get_rxfh_indir, - .set_rxfh_indir = efx_ethtool_set_rxfh_indir, + .set_rxnfc = efx_ethtool_set_rxnfc, + .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, + .get_rxfh = efx_ethtool_get_rxfh, + .set_rxfh = efx_ethtool_set_rxfh, + .get_ts_info = efx_ethtool_get_ts_info, + .get_module_info = efx_ethtool_get_module_info, + .get_module_eeprom = efx_ethtool_get_module_eeprom, }; |
