From de5f32e2b6301c1f6780b3ae9c2740a5e422e2da Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 29 Jun 2020 14:33:44 +0100 Subject: sfc: don't try to create more channels than we can have VIs Calculate efx->max_vis at probe time, and check against it in efx_allocate_msix_channels() when considering whether to create XDP TX channels. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/net_driver.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 1afb58feb9ab..7bc4d1cbb398 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1022,6 +1022,7 @@ struct efx_nic { unsigned next_buffer_table; unsigned int max_channels; + unsigned int max_vis; unsigned int max_tx_channels; unsigned n_channels; unsigned n_rx_channels; -- cgit v1.2.3 From 66a65128d4a585aff4baf123d710107cbd31c3a7 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 29 Jun 2020 14:35:33 +0100 Subject: sfc: track which BAR is mapped EF100 needs to map multiple BARs (sequentially, not concurrently) in order to read the Function Control Window during probe. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 4 ++-- drivers/net/ethernet/sfc/efx_common.c | 19 ++++++++++++------- drivers/net/ethernet/sfc/efx_common.h | 2 +- drivers/net/ethernet/sfc/net_driver.h | 3 +++ 4 files changed, 18 insertions(+), 10 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 474cfce5c042..86639b1e4e5c 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -1074,7 +1074,7 @@ static void efx_pci_remove(struct pci_dev *pci_dev) efx_pci_remove_main(efx); - efx_fini_io(efx, efx->type->mem_bar(efx)); + efx_fini_io(efx); netif_dbg(efx, drv, efx->net_dev, "shutdown successful\n"); efx_fini_struct(efx); @@ -1342,7 +1342,7 @@ static int efx_pci_probe(struct pci_dev *pci_dev, return 0; fail3: - efx_fini_io(efx, efx->type->mem_bar(efx)); + efx_fini_io(efx); fail2: efx_fini_struct(efx); fail1: diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index 02459d90afb0..36c0ab57d3bd 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -953,6 +953,8 @@ int efx_init_struct(struct efx_nic *efx, INIT_WORK(&efx->mac_work, efx_mac_work); init_waitqueue_head(&efx->flush_wq); + efx->mem_bar = UINT_MAX; + rc = efx_init_channels(efx); if (rc) goto fail; @@ -996,7 +998,9 @@ int efx_init_io(struct efx_nic *efx, int bar, dma_addr_t dma_mask, struct pci_dev *pci_dev = efx->pci_dev; int rc; - netif_dbg(efx, probe, efx->net_dev, "initialising I/O\n"); + efx->mem_bar = UINT_MAX; + + netif_dbg(efx, probe, efx->net_dev, "initialising I/O bar=%d\n", bar); rc = pci_enable_device(pci_dev); if (rc) { @@ -1038,21 +1042,21 @@ int efx_init_io(struct efx_nic *efx, int bar, dma_addr_t dma_mask, rc = pci_request_region(pci_dev, bar, "sfc"); if (rc) { netif_err(efx, probe, efx->net_dev, - "request for memory BAR failed\n"); + "request for memory BAR[%d] failed\n", bar); rc = -EIO; goto fail3; } - + efx->mem_bar = bar; efx->membase = ioremap(efx->membase_phys, mem_map_size); if (!efx->membase) { netif_err(efx, probe, efx->net_dev, - "could not map memory BAR at %llx+%x\n", + "could not map memory BAR[%d] at %llx+%x\n", bar, (unsigned long long)efx->membase_phys, mem_map_size); rc = -ENOMEM; goto fail4; } netif_dbg(efx, probe, efx->net_dev, - "memory BAR at %llx+%x (virtual %p)\n", + "memory BAR[%d] at %llx+%x (virtual %p)\n", bar, (unsigned long long)efx->membase_phys, mem_map_size, efx->membase); @@ -1068,7 +1072,7 @@ fail1: return rc; } -void efx_fini_io(struct efx_nic *efx, int bar) +void efx_fini_io(struct efx_nic *efx) { netif_dbg(efx, drv, efx->net_dev, "shutting down I/O\n"); @@ -1078,8 +1082,9 @@ void efx_fini_io(struct efx_nic *efx, int bar) } if (efx->membase_phys) { - pci_release_region(efx->pci_dev, bar); + pci_release_region(efx->pci_dev, efx->mem_bar); efx->membase_phys = 0; + efx->mem_bar = UINT_MAX; } /* Don't disable bus-mastering if VFs are assigned */ diff --git a/drivers/net/ethernet/sfc/efx_common.h b/drivers/net/ethernet/sfc/efx_common.h index c522a5be43d2..93a017aafb9f 100644 --- a/drivers/net/ethernet/sfc/efx_common.h +++ b/drivers/net/ethernet/sfc/efx_common.h @@ -13,7 +13,7 @@ int efx_init_io(struct efx_nic *efx, int bar, dma_addr_t dma_mask, unsigned int mem_map_size); -void efx_fini_io(struct efx_nic *efx, int bar); +void efx_fini_io(struct efx_nic *efx); int efx_init_struct(struct efx_nic *efx, struct pci_dev *pci_dev, struct net_device *net_dev); void efx_fini_struct(struct efx_nic *efx); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 7bc4d1cbb398..e0b84b2e3bd2 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -961,6 +961,7 @@ struct efx_async_filter_insertion { * @vpd_sn: Serial number read from VPD * @xdp_rxq_info_failed: Have any of the rx queues failed to initialise their * xdp_rxq_info structures? + * @mem_bar: The BAR that is mapped into membase. * @monitor_work: Hardware monitor workitem * @biu_lock: BIU (bus interface unit) lock * @last_irq_cpu: Last CPU to handle a possible test interrupt. This @@ -1137,6 +1138,8 @@ struct efx_nic { char *vpd_sn; bool xdp_rxq_info_failed; + unsigned int mem_bar; + /* The following fields may be written more often */ struct delayed_work monitor_work ____cacheline_aligned_in_smp; -- cgit v1.2.3 From af3c38d3fb7c3003842974b1de454ecebbd8bc83 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 2 Jul 2020 17:27:35 +0100 Subject: sfc: support setting MTU even if not privileged to configure MAC fully Unprivileged functions (such as VFs) may set their MTU by use of the 'control' field of MC_CMD_SET_MAC_EXT, as used in efx_mcdi_set_mtu(). If calling efx_ef10_mac_reconfigure() from efx_change_mtu(), and the NIC supports the above (SET_MAC_ENHANCED capability), use it rather than efx_mcdi_set_mac(). Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 15 ++++++--------- drivers/net/ethernet/sfc/efx.c | 4 ---- drivers/net/ethernet/sfc/efx_common.c | 12 ++++++------ drivers/net/ethernet/sfc/efx_common.h | 2 +- drivers/net/ethernet/sfc/ethtool_common.c | 2 +- drivers/net/ethernet/sfc/net_driver.h | 2 +- drivers/net/ethernet/sfc/siena.c | 2 +- 7 files changed, 16 insertions(+), 23 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 5faf2f0e4d62..a51925b45fc7 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -3306,18 +3306,15 @@ static int efx_ef10_set_mac_address(struct efx_nic *efx) return rc; } -static int efx_ef10_mac_reconfigure(struct efx_nic *efx) +static int efx_ef10_mac_reconfigure(struct efx_nic *efx, bool mtu_only) { - efx_mcdi_filter_sync_rx_mode(efx); - - return efx_mcdi_set_mac(efx); -} + WARN_ON(!mutex_is_locked(&efx->mac_lock)); -static int efx_ef10_mac_reconfigure_vf(struct efx_nic *efx) -{ efx_mcdi_filter_sync_rx_mode(efx); - return 0; + if (mtu_only && efx_has_cap(efx, SET_MAC_ENHANCED)) + return efx_mcdi_set_mtu(efx); + return efx_mcdi_set_mac(efx); } static int efx_ef10_start_bist(struct efx_nic *efx, u32 bist_type) @@ -4038,7 +4035,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .stop_stats = efx_port_dummy_op_void, .set_id_led = efx_mcdi_set_id_led, .push_irq_moderation = efx_ef10_push_irq_moderation, - .reconfigure_mac = efx_ef10_mac_reconfigure_vf, + .reconfigure_mac = efx_ef10_mac_reconfigure, .check_mac_fault = efx_mcdi_mac_check_fault, .reconfigure_port = efx_mcdi_port_reconfigure, .get_wol = efx_ef10_get_wol_vf, diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 028d826ab147..418676aba43a 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -169,10 +169,6 @@ static int efx_init_port(struct efx_nic *efx) efx->port_initialized = true; - /* Reconfigure the MAC before creating dma queues (required for - * Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */ - efx_mac_reconfigure(efx); - /* Ensure the PHY advertises the correct flow control settings */ rc = efx->phy_op->reconfigure(efx); if (rc && rc != -EPERM) diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index a2f744377aaa..26dca2f9a363 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -139,11 +139,11 @@ void efx_destroy_reset_workqueue(void) /* We assume that efx->type->reconfigure_mac will always try to sync RX * filters and therefore needs to read-lock the filter table against freeing */ -void efx_mac_reconfigure(struct efx_nic *efx) +void efx_mac_reconfigure(struct efx_nic *efx, bool mtu_only) { if (efx->type->reconfigure_mac) { down_read(&efx->filter_sem); - efx->type->reconfigure_mac(efx); + efx->type->reconfigure_mac(efx, mtu_only); up_read(&efx->filter_sem); } } @@ -158,7 +158,7 @@ static void efx_mac_work(struct work_struct *data) mutex_lock(&efx->mac_lock); if (efx->port_enabled) - efx_mac_reconfigure(efx); + efx_mac_reconfigure(efx, false); mutex_unlock(&efx->mac_lock); } @@ -190,7 +190,7 @@ int efx_set_mac_address(struct net_device *net_dev, void *data) /* Reconfigure the MAC */ mutex_lock(&efx->mac_lock); - efx_mac_reconfigure(efx); + efx_mac_reconfigure(efx, false); mutex_unlock(&efx->mac_lock); return 0; @@ -304,7 +304,7 @@ int efx_change_mtu(struct net_device *net_dev, int new_mtu) mutex_lock(&efx->mac_lock); net_dev->mtu = new_mtu; - efx_mac_reconfigure(efx); + efx_mac_reconfigure(efx, true); mutex_unlock(&efx->mac_lock); efx_start_all(efx); @@ -486,7 +486,7 @@ static void efx_start_port(struct efx_nic *efx) efx->port_enabled = true; /* Ensure MAC ingress/egress is enabled */ - efx_mac_reconfigure(efx); + efx_mac_reconfigure(efx, false); mutex_unlock(&efx->mac_lock); } diff --git a/drivers/net/ethernet/sfc/efx_common.h b/drivers/net/ethernet/sfc/efx_common.h index 73c355fc2590..4056f68f04e5 100644 --- a/drivers/net/ethernet/sfc/efx_common.h +++ b/drivers/net/ethernet/sfc/efx_common.h @@ -95,7 +95,7 @@ static inline void efx_init_mcdi_logging(struct efx_nic *efx) {} static inline void efx_fini_mcdi_logging(struct efx_nic *efx) {} #endif -void efx_mac_reconfigure(struct efx_nic *efx); +void efx_mac_reconfigure(struct efx_nic *efx, bool mtu_only); int efx_set_mac_address(struct net_device *net_dev, void *data); void efx_set_rx_mode(struct net_device *net_dev); int efx_set_features(struct net_device *net_dev, netdev_features_t data); diff --git a/drivers/net/ethernet/sfc/ethtool_common.c b/drivers/net/ethernet/sfc/ethtool_common.c index c96595e50234..738d9be86899 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.c +++ b/drivers/net/ethernet/sfc/ethtool_common.c @@ -241,7 +241,7 @@ 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_reconfigure(efx); + efx_mac_reconfigure(efx, false); out: mutex_unlock(&efx->mac_lock); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index e0b84b2e3bd2..65a106878186 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1356,7 +1356,7 @@ struct efx_nic_type { void (*push_irq_moderation)(struct efx_channel *channel); int (*reconfigure_port)(struct efx_nic *efx); void (*prepare_enable_fc_tx)(struct efx_nic *efx); - int (*reconfigure_mac)(struct efx_nic *efx); + int (*reconfigure_mac)(struct efx_nic *efx, bool mtu_only); bool (*check_mac_fault)(struct efx_nic *efx); void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol); int (*set_wol)(struct efx_nic *efx, u32 type); diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index ffe193f03352..a7dcd2d3c09b 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -633,7 +633,7 @@ static size_t siena_update_nic_stats(struct efx_nic *efx, u64 *full_stats, return SIENA_STAT_COUNT; } -static int siena_mac_reconfigure(struct efx_nic *efx) +static int siena_mac_reconfigure(struct efx_nic *efx, bool mtu_only __always_unused) { MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_MCAST_HASH_IN_LEN); int rc; -- cgit v1.2.3 From bc32442176d7289a71a2e940e5e8ea63cd0a23bb Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 2 Jul 2020 17:27:51 +0100 Subject: sfc: remove max_interrupt_mode All NICs supported by this driver are capable of MSI-X interrupts (only Falcon A1 wasn't, and that's now hived off into its own driver), so no need for a nic-type parameter. Besides, the code that checked it was buggy anyway (the following assignment that checked min_interrupt_mode overrode it). Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 2 -- drivers/net/ethernet/sfc/efx_channels.c | 6 ------ drivers/net/ethernet/sfc/net_driver.h | 3 --- drivers/net/ethernet/sfc/siena.c | 1 - 4 files changed, 12 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index a51925b45fc7..88522b683cc7 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -4108,7 +4108,6 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .can_rx_scatter = true, .always_rx_scatter = true, .min_interrupt_mode = EFX_INT_MODE_MSIX, - .max_interrupt_mode = EFX_INT_MODE_MSIX, .timer_period_max = 1 << ERF_DD_EVQ_IND_TIMER_VAL_WIDTH, .offload_features = EF10_OFFLOAD_FEATURES, .mcdi_max_ver = 2, @@ -4245,7 +4244,6 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .always_rx_scatter = true, .option_descriptors = true, .min_interrupt_mode = EFX_INT_MODE_LEGACY, - .max_interrupt_mode = EFX_INT_MODE_MSIX, .timer_period_max = 1 << ERF_DD_EVQ_IND_TIMER_VAL_WIDTH, .offload_features = EF10_OFFLOAD_FEATURES, .mcdi_max_ver = 2, diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index 2f9db219513a..2220b9507336 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -557,12 +557,6 @@ int efx_init_channels(struct efx_nic *efx) } /* Higher numbered interrupt modes are less capable! */ - if (WARN_ON_ONCE(efx->type->max_interrupt_mode > - efx->type->min_interrupt_mode)) { - return -EIO; - } - efx->interrupt_mode = max(efx->type->max_interrupt_mode, - interrupt_mode); efx->interrupt_mode = min(efx->type->min_interrupt_mode, interrupt_mode); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 65a106878186..e536c1e12f86 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1317,8 +1317,6 @@ struct efx_udp_tunnel { * @option_descriptors: NIC supports TX option descriptors * @min_interrupt_mode: Lowest capability interrupt mode supported * from &enum efx_int_mode. - * @max_interrupt_mode: Highest capability interrupt mode supported - * from &enum efx_int_mode. * @timer_period_max: Maximum period of interrupt timer (in ticks) * @offload_features: net_device feature flags for protocol offload * features implemented in hardware @@ -1492,7 +1490,6 @@ struct efx_nic_type { bool always_rx_scatter; bool option_descriptors; unsigned int min_interrupt_mode; - unsigned int max_interrupt_mode; unsigned int timer_period_max; netdev_features_t offload_features; int mcdi_max_ver; diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index a7dcd2d3c09b..e438853f64a3 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -1085,7 +1085,6 @@ const struct efx_nic_type siena_a0_nic_type = { .can_rx_scatter = true, .option_descriptors = false, .min_interrupt_mode = EFX_INT_MODE_LEGACY, - .max_interrupt_mode = EFX_INT_MODE_MSIX, .timer_period_max = 1 << FRF_CZ_TC_TIMER_VAL_WIDTH, .offload_features = (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXHASH | NETIF_F_NTUPLE), -- cgit v1.2.3 From f9cac93e5b3eefc5c6340ba4efd3628f2347e1ef Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 2 Jul 2020 17:29:24 +0100 Subject: sfc: make tx_queues_per_channel variable at runtime Siena needs four TX queues (csum * highpri), EF10 needs two (csum), and EF100 only needs one (as checksumming is controlled entirely by the transmit descriptor). Rather than having various bits of ad-hoc code to decide which queues to set up etc., put the knowledge of how many TXQs a channel has in one place. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 13 +++++++------ drivers/net/ethernet/sfc/efx_channels.c | 4 ++-- drivers/net/ethernet/sfc/efx_common.c | 1 + drivers/net/ethernet/sfc/net_driver.h | 34 +++++++++++++-------------------- drivers/net/ethernet/sfc/siena.c | 1 + drivers/net/ethernet/sfc/tx.c | 8 ++++++-- 6 files changed, 30 insertions(+), 31 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 88522b683cc7..be15640c160a 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -600,6 +600,7 @@ static int efx_ef10_probe(struct efx_nic *efx) * However, until we use TX option descriptors we need two TX queues * per channel. */ + efx->tx_queues_per_channel = 2; efx->max_vis = efx_ef10_mem_map_size(efx) / efx->vi_stride; if (!efx->max_vis) { netif_err(efx, drv, efx->net_dev, "error determining max VIs\n"); @@ -607,7 +608,7 @@ static int efx_ef10_probe(struct efx_nic *efx) goto fail5; } efx->max_channels = min_t(unsigned int, EFX_MAX_CHANNELS, - efx->max_vis / EFX_TXQ_TYPES); + efx->max_vis / efx->tx_queues_per_channel); efx->max_tx_channels = efx->max_channels; if (WARN_ON(efx->max_channels == 0)) { rc = -EIO; @@ -1120,17 +1121,17 @@ static int efx_ef10_alloc_vis(struct efx_nic *efx, */ static int efx_ef10_dimension_resources(struct efx_nic *efx) { + unsigned int min_vis = max_t(unsigned int, efx->tx_queues_per_channel, + efx_separate_tx_channels ? 2 : 1); + unsigned int channel_vis, pio_write_vi_base, max_vis; struct efx_ef10_nic_data *nic_data = efx->nic_data; unsigned int uc_mem_map_size, wc_mem_map_size; - unsigned int min_vis = max(EFX_TXQ_TYPES, - efx_separate_tx_channels ? 2 : 1); - unsigned int channel_vis, pio_write_vi_base, max_vis; void __iomem *membase; int rc; channel_vis = max(efx->n_channels, ((efx->n_tx_channels + efx->n_extra_tx_channels) * - EFX_TXQ_TYPES) + + efx->tx_queues_per_channel) + efx->n_xdp_channels * efx->xdp_tx_per_channel); if (efx->max_vis && efx->max_vis < channel_vis) { netif_dbg(efx, drv, efx->net_dev, @@ -1219,7 +1220,7 @@ static int efx_ef10_dimension_resources(struct efx_nic *efx) */ efx->max_channels = nic_data->n_allocated_vis; efx->max_tx_channels = - nic_data->n_allocated_vis / EFX_TXQ_TYPES; + nic_data->n_allocated_vis / efx->tx_queues_per_channel; efx_mcdi_free_vis(efx); return -EAGAIN; diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index 466257b9abbf..c3edebf523b6 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -726,7 +726,7 @@ void efx_remove_channel(struct efx_channel *channel) efx_for_each_channel_rx_queue(rx_queue, channel) efx_remove_rx_queue(rx_queue); - efx_for_each_possible_channel_tx_queue(tx_queue, channel) + efx_for_each_channel_tx_queue(tx_queue, channel) efx_remove_tx_queue(tx_queue); efx_remove_eventq(channel); channel->type->post_remove(channel); @@ -1090,7 +1090,7 @@ void efx_stop_channels(struct efx_nic *efx) efx_for_each_channel(channel, efx) { efx_for_each_channel_rx_queue(rx_queue, channel) efx_fini_rx_queue(rx_queue); - efx_for_each_possible_channel_tx_queue(tx_queue, channel) + efx_for_each_channel_tx_queue(tx_queue, channel) efx_fini_tx_queue(tx_queue); } } diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index 26dca2f9a363..c84123456c01 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -1036,6 +1036,7 @@ int efx_init_struct(struct efx_nic *efx, INIT_WORK(&efx->mac_work, efx_mac_work); init_waitqueue_head(&efx->flush_wq); + efx->tx_queues_per_channel = 1; efx->rxq_entries = EFX_DEFAULT_DMAQ_SIZE; efx->txq_entries = EFX_DEFAULT_DMAQ_SIZE; diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index e536c1e12f86..4ded155b12e9 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -867,6 +867,7 @@ struct efx_async_filter_insertion { * @n_rx_channels: Number of channels used for RX (= number of RX queues) * @n_tx_channels: Number of channels used for TX * @n_extra_tx_channels: Number of extra channels with TX queues + * @tx_queues_per_channel: number of TX queues probed on each channel * @n_xdp_channels: Number of channels used for XDP TX * @xdp_channel_offset: Offset of zeroth channel used for XPD TX. * @xdp_tx_per_channel: Max number of TX queues on an XDP TX channel. @@ -1031,6 +1032,7 @@ struct efx_nic { unsigned tx_channel_offset; unsigned n_tx_channels; unsigned n_extra_tx_channels; + unsigned int tx_queues_per_channel; unsigned int n_xdp_channels; unsigned int xdp_channel_offset; unsigned int xdp_tx_per_channel; @@ -1529,7 +1531,7 @@ static inline struct efx_tx_queue * efx_get_tx_queue(struct efx_nic *efx, unsigned index, unsigned type) { EFX_WARN_ON_ONCE_PARANOID(index >= efx->n_tx_channels || - type >= EFX_TXQ_TYPES); + type >= efx->tx_queues_per_channel); return &efx->channel[efx->tx_channel_offset + index]->tx_queue[type]; } @@ -1551,18 +1553,18 @@ static inline bool efx_channel_has_tx_queues(struct efx_channel *channel) return true; } -static inline struct efx_tx_queue * -efx_channel_get_tx_queue(struct efx_channel *channel, unsigned type) +static inline unsigned int efx_channel_num_tx_queues(struct efx_channel *channel) { - EFX_WARN_ON_ONCE_PARANOID(!efx_channel_has_tx_queues(channel) || - type >= EFX_TXQ_TYPES); - return &channel->tx_queue[type]; + if (efx_channel_is_xdp_tx(channel)) + return channel->efx->xdp_tx_per_channel; + return channel->efx->tx_queues_per_channel; } -static inline bool efx_tx_queue_used(struct efx_tx_queue *tx_queue) +static inline struct efx_tx_queue * +efx_channel_get_tx_queue(struct efx_channel *channel, unsigned type) { - return !(tx_queue->efx->net_dev->num_tc < 2 && - tx_queue->queue & EFX_TXQ_TYPE_HIGHPRI); + EFX_WARN_ON_ONCE_PARANOID(type >= efx_channel_num_tx_queues(channel)); + return &channel->tx_queue[type]; } /* Iterate over all TX queues belonging to a channel */ @@ -1571,18 +1573,8 @@ static inline bool efx_tx_queue_used(struct efx_tx_queue *tx_queue) ; \ else \ for (_tx_queue = (_channel)->tx_queue; \ - _tx_queue < (_channel)->tx_queue + EFX_TXQ_TYPES && \ - (efx_tx_queue_used(_tx_queue) || \ - efx_channel_is_xdp_tx(_channel)); \ - _tx_queue++) - -/* Iterate over all possible TX queues belonging to a channel */ -#define efx_for_each_possible_channel_tx_queue(_tx_queue, _channel) \ - if (!efx_channel_has_tx_queues(_channel)) \ - ; \ - else \ - for (_tx_queue = (_channel)->tx_queue; \ - _tx_queue < (_channel)->tx_queue + EFX_TXQ_TYPES; \ + _tx_queue < (_channel)->tx_queue + \ + efx_channel_num_tx_queues(_channel); \ _tx_queue++) static inline bool efx_channel_has_rx_queue(struct efx_channel *channel) diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index e438853f64a3..4c5881a3bfe4 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -279,6 +279,7 @@ static int siena_probe_nic(struct efx_nic *efx) efx->max_channels = EFX_MAX_CHANNELS; efx->max_vis = EFX_MAX_CHANNELS; efx->max_tx_channels = EFX_MAX_CHANNELS; + efx->tx_queues_per_channel = 2; efx_reado(efx, ®, FR_AZ_CS_DEBUG); efx->port_num = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index ed20f6aef435..76ff394f5b58 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -569,6 +569,10 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; + /* Only Siena supported highpri queues */ + if (efx_nic_rev(efx) > EFX_REV_SIENA_A0) + return -EOPNOTSUPP; + num_tc = mqprio->num_tc; if (num_tc > EFX_MAX_TX_TC) @@ -585,10 +589,10 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, } if (num_tc > net_dev->num_tc) { + efx->tx_queues_per_channel = 4; /* Initialise high-priority queues as necessary */ efx_for_each_channel(channel, efx) { - efx_for_each_possible_channel_tx_queue(tx_queue, - channel) { + efx_for_each_channel_tx_queue(tx_queue, channel) { if (!(tx_queue->queue & EFX_TXQ_TYPE_HIGHPRI)) continue; if (!tx_queue->buffer) { -- cgit v1.2.3 From a81dcd85a7c1bc548ce8fb635623970fdee5887d Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 2 Jul 2020 17:29:58 +0100 Subject: sfc: assign TXQs without gaps Since we only allocate VIs for the number of TXQs we actually need, we cannot naively use "channel * TXQ_TYPES + txq" for the TXQ number, as this has gaps (when efx->tx_queues_per_channel < EFX_TXQ_TYPES) and thus overruns the driver's VI allocations, causing the firmware to reject the MC_CMD_INIT_TXQ based on INSTANCE. Thus, we distinguish INSTANCE (stored in tx_queue->queue) from LABEL (tx_queue->label); the former is allocated starting from 0 in efx_set_channels(), while the latter is simply the txq type (index in channel->tx_queue array). To simplify things, rather than changing tx_queues_per_channel after setting up TXQs, make Siena always probe its HIGHPRI queues at start of day, rather than deferring it until tc mqprio enables them. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 2 +- drivers/net/ethernet/sfc/efx_channels.c | 38 ++++++++++++++++++------- drivers/net/ethernet/sfc/ethtool_common.c | 7 ++--- drivers/net/ethernet/sfc/farch.c | 6 ++-- drivers/net/ethernet/sfc/mcdi_functions.c | 4 +-- drivers/net/ethernet/sfc/net_driver.h | 5 +++- drivers/net/ethernet/sfc/nic_common.h | 2 +- drivers/net/ethernet/sfc/selftest.c | 18 ++++++------ drivers/net/ethernet/sfc/siena.c | 2 +- drivers/net/ethernet/sfc/tx.c | 46 ++++--------------------------- 10 files changed, 58 insertions(+), 72 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index be15640c160a..311a2d2a906d 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2244,7 +2244,7 @@ static u32 efx_ef10_tso_versions(struct efx_nic *efx) static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) { - bool csum_offload = tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD; + bool csum_offload = tx_queue->label & EFX_TXQ_TYPE_OFFLOAD; struct efx_channel *channel = tx_queue->channel; struct efx_nic *efx = tx_queue->efx; struct efx_ef10_nic_data *nic_data; diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index 30358f3f48ca..dd4f30ea48a8 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -524,7 +524,8 @@ efx_alloc_channel(struct efx_nic *efx, int i, struct efx_channel *old_channel) for (j = 0; j < EFX_TXQ_TYPES; j++) { tx_queue = &channel->tx_queue[j]; tx_queue->efx = efx; - tx_queue->queue = i * EFX_TXQ_TYPES + j; + tx_queue->queue = -1; + tx_queue->label = j; tx_queue->channel = channel; } @@ -853,8 +854,9 @@ rollback: int efx_set_channels(struct efx_nic *efx) { - struct efx_channel *channel; struct efx_tx_queue *tx_queue; + struct efx_channel *channel; + unsigned int next_queue = 0; int xdp_queue_number; int rc; @@ -884,14 +886,30 @@ int efx_set_channels(struct efx_nic *efx) else channel->rx_queue.core_index = -1; - efx_for_each_channel_tx_queue(tx_queue, channel) { - tx_queue->queue -= (efx->tx_channel_offset * - EFX_TXQ_TYPES); - - if (efx_channel_is_xdp_tx(channel) && - xdp_queue_number < efx->xdp_tx_queue_count) { - efx->xdp_tx_queues[xdp_queue_number] = tx_queue; - xdp_queue_number++; + if (channel->channel >= efx->tx_channel_offset) { + if (efx_channel_is_xdp_tx(channel)) { + efx_for_each_channel_tx_queue(tx_queue, channel) { + tx_queue->queue = next_queue++; + netif_dbg(efx, drv, efx->net_dev, "Channel %u TXQ %u is XDP %u, HW %u\n", + channel->channel, tx_queue->label, + xdp_queue_number, tx_queue->queue); + /* We may have a few left-over XDP TX + * queues owing to xdp_tx_queue_count + * not dividing evenly by EFX_TXQ_TYPES. + * We still allocate and probe those + * TXQs, but never use them. + */ + if (xdp_queue_number < efx->xdp_tx_queue_count) + efx->xdp_tx_queues[xdp_queue_number] = tx_queue; + xdp_queue_number++; + } + } else { + efx_for_each_channel_tx_queue(tx_queue, channel) { + tx_queue->queue = next_queue++; + netif_dbg(efx, drv, efx->net_dev, "Channel %u TXQ %u is HW %u\n", + channel->channel, tx_queue->label, + tx_queue->queue); + } } } } diff --git a/drivers/net/ethernet/sfc/ethtool_common.c b/drivers/net/ethernet/sfc/ethtool_common.c index 738d9be86899..37a4409e759e 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.c +++ b/drivers/net/ethernet/sfc/ethtool_common.c @@ -287,8 +287,7 @@ static void efx_fill_test(unsigned int test_index, u8 *strings, u64 *data, } #define EFX_CHANNEL_NAME(_channel) "chan%d", _channel->channel -#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue -#define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue +#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->label #define EFX_LOOPBACK_NAME(_mode, _counter) \ "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, efx_loopback_mode) @@ -316,11 +315,11 @@ static int efx_fill_loopback_test(struct efx_nic *efx, efx_for_each_channel_tx_queue(tx_queue, channel) { efx_fill_test(test_index++, strings, data, - &lb_tests->tx_sent[tx_queue->queue], + &lb_tests->tx_sent[tx_queue->label], EFX_TX_QUEUE_NAME(tx_queue), EFX_LOOPBACK_NAME(mode, "tx_sent")); efx_fill_test(test_index++, strings, data, - &lb_tests->tx_done[tx_queue->queue], + &lb_tests->tx_done[tx_queue->label], EFX_TX_QUEUE_NAME(tx_queue), EFX_LOOPBACK_NAME(mode, "tx_done")); } diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c index dbbb898adddb..d07eeaad9bdf 100644 --- a/drivers/net/ethernet/sfc/farch.c +++ b/drivers/net/ethernet/sfc/farch.c @@ -379,7 +379,7 @@ int efx_farch_tx_probe(struct efx_tx_queue *tx_queue) void efx_farch_tx_init(struct efx_tx_queue *tx_queue) { - int csum = tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD; + int csum = tx_queue->label & EFX_TXQ_TYPE_OFFLOAD; struct efx_nic *efx = tx_queue->efx; efx_oword_t reg; @@ -395,7 +395,7 @@ void efx_farch_tx_init(struct efx_tx_queue *tx_queue) FRF_AZ_TX_DESCQ_EVQ_ID, tx_queue->channel->channel, FRF_AZ_TX_DESCQ_OWNER_ID, 0, - FRF_AZ_TX_DESCQ_LABEL, tx_queue->queue, + FRF_AZ_TX_DESCQ_LABEL, tx_queue->label, FRF_AZ_TX_DESCQ_SIZE, __ffs(tx_queue->txd.entries), FRF_AZ_TX_DESCQ_TYPE, 0, @@ -409,7 +409,7 @@ void efx_farch_tx_init(struct efx_tx_queue *tx_queue) EFX_POPULATE_OWORD_1(reg, FRF_BZ_TX_PACE, - (tx_queue->queue & EFX_TXQ_TYPE_HIGHPRI) ? + (tx_queue->label & EFX_TXQ_TYPE_HIGHPRI) ? FFE_BZ_TX_PACE_OFF : FFE_BZ_TX_PACE_RESERVED); efx_writeo_table(efx, ®, FR_BZ_TX_PACE_TBL, tx_queue->queue); diff --git a/drivers/net/ethernet/sfc/mcdi_functions.c b/drivers/net/ethernet/sfc/mcdi_functions.c index 962d8395d958..b3a8aa88db06 100644 --- a/drivers/net/ethernet/sfc/mcdi_functions.c +++ b/drivers/net/ethernet/sfc/mcdi_functions.c @@ -164,7 +164,7 @@ int efx_mcdi_tx_init(struct efx_tx_queue *tx_queue, bool tso_v2) { MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_TXQ_IN_LEN(EFX_MAX_DMAQ_SIZE * 8 / EFX_BUF_SIZE)); - bool csum_offload = tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD; + bool csum_offload = tx_queue->label & EFX_TXQ_TYPE_OFFLOAD; size_t entries = tx_queue->txd.buf.len / EFX_BUF_SIZE; struct efx_channel *channel = tx_queue->channel; struct efx_nic *efx = tx_queue->efx; @@ -176,7 +176,7 @@ int efx_mcdi_tx_init(struct efx_tx_queue *tx_queue, bool tso_v2) MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_SIZE, tx_queue->ptr_mask + 1); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_TARGET_EVQ, channel->channel); - MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_LABEL, tx_queue->queue); + MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_LABEL, tx_queue->label); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_INSTANCE, tx_queue->queue); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_OWNER_ID, 0); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_PORT_ID, efx->vport_id); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 4ded155b12e9..0bf11ebb03cf 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -189,6 +189,8 @@ struct efx_tx_buffer { * * @efx: The associated Efx NIC * @queue: DMA queue number + * @label: Label for TX completion events. + * Is our index within @channel->tx_queue array. * @tso_version: Version of TSO in use for this queue. * @channel: The associated channel * @core_txq: The networking core TX queue structure @@ -250,7 +252,8 @@ struct efx_tx_buffer { struct efx_tx_queue { /* Members which don't change on the fast path */ struct efx_nic *efx ____cacheline_aligned_in_smp; - unsigned queue; + unsigned int queue; + unsigned int label; unsigned int tso_version; struct efx_channel *channel; struct netdev_queue *core_txq; diff --git a/drivers/net/ethernet/sfc/nic_common.h b/drivers/net/ethernet/sfc/nic_common.h index fd474d9e55e4..813f288ab3fe 100644 --- a/drivers/net/ethernet/sfc/nic_common.h +++ b/drivers/net/ethernet/sfc/nic_common.h @@ -90,7 +90,7 @@ static inline bool efx_nic_tx_is_empty(struct efx_tx_queue *tx_queue) /* XXX is this a thing on EF100? */ static inline struct efx_tx_queue *efx_tx_queue_partner(struct efx_tx_queue *tx_queue) { - if (tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD) + if (tx_queue->label & EFX_TXQ_TYPE_OFFLOAD) return tx_queue - EFX_TXQ_TYPE_OFFLOAD; else return tx_queue + EFX_TXQ_TYPE_OFFLOAD; diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index 1ae369022d7d..e71d6d37a317 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -445,7 +445,7 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue) if (rc != NETDEV_TX_OK) { netif_err(efx, drv, efx->net_dev, "TX queue %d could not transmit packet %d of " - "%d in %s loopback test\n", tx_queue->queue, + "%d in %s loopback test\n", tx_queue->label, i + 1, state->packet_count, LOOPBACK_MODE(efx)); @@ -497,7 +497,7 @@ static int efx_end_loopback(struct efx_tx_queue *tx_queue, netif_err(efx, drv, efx->net_dev, "TX queue %d saw only %d out of an expected %d " "TX completion events in %s loopback test\n", - tx_queue->queue, tx_done, state->packet_count, + tx_queue->label, tx_done, state->packet_count, LOOPBACK_MODE(efx)); rc = -ETIMEDOUT; /* Allow to fall through so we see the RX errors as well */ @@ -508,15 +508,15 @@ static int efx_end_loopback(struct efx_tx_queue *tx_queue, netif_dbg(efx, drv, efx->net_dev, "TX queue %d saw only %d out of an expected %d " "received packets in %s loopback test\n", - tx_queue->queue, rx_good, state->packet_count, + tx_queue->label, rx_good, state->packet_count, LOOPBACK_MODE(efx)); rc = -ETIMEDOUT; /* Fall through */ } /* Update loopback test structure */ - lb_tests->tx_sent[tx_queue->queue] += state->packet_count; - lb_tests->tx_done[tx_queue->queue] += tx_done; + lb_tests->tx_sent[tx_queue->label] += state->packet_count; + lb_tests->tx_done[tx_queue->label] += tx_done; lb_tests->rx_good += rx_good; lb_tests->rx_bad += rx_bad; @@ -542,8 +542,8 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, state->flush = false; netif_dbg(efx, drv, efx->net_dev, - "TX queue %d testing %s loopback with %d packets\n", - tx_queue->queue, LOOPBACK_MODE(efx), + "TX queue %d (hw %d) testing %s loopback with %d packets\n", + tx_queue->label, tx_queue->queue, LOOPBACK_MODE(efx), state->packet_count); efx_iterate_state(efx); @@ -570,7 +570,7 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, netif_dbg(efx, drv, efx->net_dev, "TX queue %d passed %s loopback test with a burst length " - "of %d packets\n", tx_queue->queue, LOOPBACK_MODE(efx), + "of %d packets\n", tx_queue->label, LOOPBACK_MODE(efx), state->packet_count); return 0; @@ -660,7 +660,7 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, /* Test all enabled types of TX queue */ efx_for_each_channel_tx_queue(tx_queue, channel) { - state->offload_csum = (tx_queue->queue & + state->offload_csum = (tx_queue->label & EFX_TXQ_TYPE_OFFLOAD); rc = efx_test_loopback(tx_queue, &tests->loopback[mode]); diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 4c5881a3bfe4..219fb3a0c9d0 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -279,7 +279,7 @@ static int siena_probe_nic(struct efx_nic *efx) efx->max_channels = EFX_MAX_CHANNELS; efx->max_vis = EFX_MAX_CHANNELS; efx->max_tx_channels = EFX_MAX_CHANNELS; - efx->tx_queues_per_channel = 2; + efx->tx_queues_per_channel = 4; efx_reado(efx, ®, FR_AZ_CS_DEBUG); efx->port_num = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 76ff394f5b58..1bcf50ab95d9 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -551,8 +551,8 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) /* Must be inverse of queue lookup in efx_hard_start_xmit() */ tx_queue->core_txq = netdev_get_tx_queue(efx->net_dev, - tx_queue->queue / EFX_TXQ_TYPES + - ((tx_queue->queue & EFX_TXQ_TYPE_HIGHPRI) ? + tx_queue->channel->channel + + ((tx_queue->label & EFX_TXQ_TYPE_HIGHPRI) ? efx->n_tx_channels : 0)); } @@ -561,10 +561,7 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, { struct efx_nic *efx = netdev_priv(net_dev); struct tc_mqprio_qopt *mqprio = type_data; - struct efx_channel *channel; - struct efx_tx_queue *tx_queue; unsigned tc, num_tc; - int rc; if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; @@ -588,40 +585,9 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, net_dev->tc_to_txq[tc].count = efx->n_tx_channels; } - if (num_tc > net_dev->num_tc) { - efx->tx_queues_per_channel = 4; - /* Initialise high-priority queues as necessary */ - efx_for_each_channel(channel, efx) { - efx_for_each_channel_tx_queue(tx_queue, channel) { - if (!(tx_queue->queue & EFX_TXQ_TYPE_HIGHPRI)) - continue; - if (!tx_queue->buffer) { - rc = efx_probe_tx_queue(tx_queue); - if (rc) - return rc; - } - if (!tx_queue->initialised) - efx_init_tx_queue(tx_queue); - efx_init_tx_queue_core_txq(tx_queue); - } - } - } else { - /* Reduce number of classes before number of queues */ - net_dev->num_tc = num_tc; - } - - rc = netif_set_real_num_tx_queues(net_dev, - max_t(int, num_tc, 1) * - efx->n_tx_channels); - if (rc) - return rc; - - /* Do not destroy high-priority queues when they become - * unused. We would have to flush them first, and it is - * fairly difficult to flush a subset of TX queues. Leave - * it to efx_fini_channels(). - */ - net_dev->num_tc = num_tc; - return 0; + + return netif_set_real_num_tx_queues(net_dev, + max_t(int, num_tc, 1) * + efx->n_tx_channels); } -- cgit v1.2.3 From 205a55f4e65353dd4846547d376a6f85cdda3d04 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 22 Jul 2020 12:05:10 -0700 Subject: sfc: convert to new udp_tunnel infrastructure Check MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED, before setting the info, which will hopefully protect us from -EPERM errors the previous code was gracefully ignoring. Ed reports this is not the 100% correct bit, but it's the best approximation we have. Shared code reports the port information back to user space, so we really want to know what was added and what failed. Ignoring -EPERM is not an option. The driver does not call udp_tunnel_get_rx_info(), so its own management of table state is not really all that problematic, we can leave it be. This allows the driver to continue with its copious table syncing, and matching the ports to TX frames, which it will reportedly do one day. Leave the feature checking in the callbacks, as the device may remove the capabilities on reset. Inline the loop from __efx_ef10_udp_tnl_lookup_port() into efx_ef10_udp_tnl_has_port(), since it's the only caller now. With new infra this driver gains port replace - when space frees up in a full table a new port will be selected for offload. Plus efx will no longer sleep in an atomic context. v2: - amend the commit message about TRUSTED not being 100% - add TUNNEL_ENCAP_UDP_PORT_ENTRY_INVALID to mark unsed entries Signed-off-by: Jakub Kicinski Acked-By: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 162 +++++++++++++--------------------- drivers/net/ethernet/sfc/efx.c | 72 +-------------- drivers/net/ethernet/sfc/net_driver.h | 11 +-- 3 files changed, 63 insertions(+), 182 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index cb7b634a1150..fa7229fff2ff 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -22,6 +22,7 @@ #include #include #include +#include /* Hardware control for EF10 architecture including 'Huntington'. */ @@ -38,6 +39,7 @@ struct efx_ef10_vlan { }; static int efx_ef10_set_udp_tnl_ports(struct efx_nic *efx, bool unloading); +static const struct udp_tunnel_nic_info efx_ef10_udp_tunnels; static int efx_ef10_get_warm_boot_count(struct efx_nic *efx) { @@ -564,6 +566,9 @@ static int efx_ef10_probe(struct efx_nic *efx) goto fail2; mutex_init(&nic_data->udp_tunnels_lock); + for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i) + nic_data->udp_tunnels[i].type = + TUNNEL_ENCAP_UDP_PORT_ENTRY_INVALID; /* Reset (most) configuration for this function */ rc = efx_mcdi_reset(efx, RESET_TYPE_ALL); @@ -665,6 +670,12 @@ static int efx_ef10_probe(struct efx_nic *efx) if (rc) goto fail_add_vid_0; + if (nic_data->datapath_caps & + (1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN) && + efx->mcdi->fn_flags & + (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED)) + efx->net_dev->udp_tunnel_nic_info = &efx_ef10_udp_tunnels; + return 0; fail_add_vid_0: @@ -3702,8 +3713,8 @@ static int efx_ef10_set_udp_tnl_ports(struct efx_nic *efx, bool unloading) MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MAXNUM); for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i) { - if (nic_data->udp_tunnels[i].count && - nic_data->udp_tunnels[i].port) { + if (nic_data->udp_tunnels[i].type != + TUNNEL_ENCAP_UDP_PORT_ENTRY_INVALID) { efx_dword_t entry; EFX_POPULATE_DWORD_2(entry, @@ -3789,79 +3800,34 @@ static int efx_ef10_udp_tnl_push_ports(struct efx_nic *efx) return rc; } -static struct efx_udp_tunnel *__efx_ef10_udp_tnl_lookup_port(struct efx_nic *efx, - __be16 port) +static int efx_ef10_udp_tnl_set_port(struct net_device *dev, + unsigned int table, unsigned int entry, + struct udp_tunnel_info *ti) { - struct efx_ef10_nic_data *nic_data = efx->nic_data; - size_t i; + struct efx_nic *efx = netdev_priv(dev); + struct efx_ef10_nic_data *nic_data; + int efx_tunnel_type, rc; - for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i) { - if (!nic_data->udp_tunnels[i].count) - continue; - if (nic_data->udp_tunnels[i].port == port) - return &nic_data->udp_tunnels[i]; - } - return NULL; -} - -static int efx_ef10_udp_tnl_add_port(struct efx_nic *efx, - struct efx_udp_tunnel tnl) -{ - struct efx_ef10_nic_data *nic_data = efx->nic_data; - struct efx_udp_tunnel *match; - char typebuf[8]; - size_t i; - int rc; + if (ti->type == UDP_TUNNEL_TYPE_VXLAN) + efx_tunnel_type = TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN; + else + efx_tunnel_type = TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE; + nic_data = efx->nic_data; if (!(nic_data->datapath_caps & (1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN))) - return 0; - - efx_get_udp_tunnel_type_name(tnl.type, typebuf, sizeof(typebuf)); - netif_dbg(efx, drv, efx->net_dev, "Adding UDP tunnel (%s) port %d\n", - typebuf, ntohs(tnl.port)); + return -EOPNOTSUPP; mutex_lock(&nic_data->udp_tunnels_lock); /* Make sure all TX are stopped while we add to the table, else we * might race against an efx_features_check(). */ efx_device_detach_sync(efx); - - match = __efx_ef10_udp_tnl_lookup_port(efx, tnl.port); - if (match != NULL) { - if (match->type == tnl.type) { - netif_dbg(efx, drv, efx->net_dev, - "Referencing existing tunnel entry\n"); - match->count++; - /* No need to cause an MCDI update */ - rc = 0; - goto unlock_out; - } - efx_get_udp_tunnel_type_name(match->type, - typebuf, sizeof(typebuf)); - netif_dbg(efx, drv, efx->net_dev, - "UDP port %d is already in use by %s\n", - ntohs(tnl.port), typebuf); - rc = -EEXIST; - goto unlock_out; - } - - for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i) - if (!nic_data->udp_tunnels[i].count) { - nic_data->udp_tunnels[i] = tnl; - nic_data->udp_tunnels[i].count = 1; - rc = efx_ef10_set_udp_tnl_ports(efx, false); - goto unlock_out; - } - - netif_dbg(efx, drv, efx->net_dev, - "Unable to add UDP tunnel (%s) port %d; insufficient resources.\n", - typebuf, ntohs(tnl.port)); - - rc = -ENOMEM; - -unlock_out: + nic_data->udp_tunnels[entry].type = efx_tunnel_type; + nic_data->udp_tunnels[entry].port = ti->port; + rc = efx_ef10_set_udp_tnl_ports(efx, false); mutex_unlock(&nic_data->udp_tunnels_lock); + return rc; } @@ -3873,6 +3839,7 @@ unlock_out: static bool efx_ef10_udp_tnl_has_port(struct efx_nic *efx, __be16 port) { struct efx_ef10_nic_data *nic_data = efx->nic_data; + size_t i; if (!(nic_data->datapath_caps & (1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN))) @@ -3884,58 +3851,51 @@ static bool efx_ef10_udp_tnl_has_port(struct efx_nic *efx, __be16 port) */ return false; - return __efx_ef10_udp_tnl_lookup_port(efx, port) != NULL; + for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i) + if (nic_data->udp_tunnels[i].type != + TUNNEL_ENCAP_UDP_PORT_ENTRY_INVALID && + nic_data->udp_tunnels[i].port == port) + return true; + + return false; } -static int efx_ef10_udp_tnl_del_port(struct efx_nic *efx, - struct efx_udp_tunnel tnl) +static int efx_ef10_udp_tnl_unset_port(struct net_device *dev, + unsigned int table, unsigned int entry, + struct udp_tunnel_info *ti) { - struct efx_ef10_nic_data *nic_data = efx->nic_data; - struct efx_udp_tunnel *match; - char typebuf[8]; + struct efx_nic *efx = netdev_priv(dev); + struct efx_ef10_nic_data *nic_data; int rc; - if (!(nic_data->datapath_caps & - (1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN))) - return 0; - - efx_get_udp_tunnel_type_name(tnl.type, typebuf, sizeof(typebuf)); - netif_dbg(efx, drv, efx->net_dev, "Removing UDP tunnel (%s) port %d\n", - typebuf, ntohs(tnl.port)); + nic_data = efx->nic_data; mutex_lock(&nic_data->udp_tunnels_lock); /* Make sure all TX are stopped while we remove from the table, else we * might race against an efx_features_check(). */ efx_device_detach_sync(efx); - - match = __efx_ef10_udp_tnl_lookup_port(efx, tnl.port); - if (match != NULL) { - if (match->type == tnl.type) { - if (--match->count) { - /* Port is still in use, so nothing to do */ - netif_dbg(efx, drv, efx->net_dev, - "UDP tunnel port %d remains active\n", - ntohs(tnl.port)); - rc = 0; - goto out_unlock; - } - rc = efx_ef10_set_udp_tnl_ports(efx, false); - goto out_unlock; - } - efx_get_udp_tunnel_type_name(match->type, - typebuf, sizeof(typebuf)); - netif_warn(efx, drv, efx->net_dev, - "UDP port %d is actually in use by %s, not removing\n", - ntohs(tnl.port), typebuf); - } - rc = -ENOENT; - -out_unlock: + nic_data->udp_tunnels[entry].type = TUNNEL_ENCAP_UDP_PORT_ENTRY_INVALID; + nic_data->udp_tunnels[entry].port = 0; + rc = efx_ef10_set_udp_tnl_ports(efx, false); mutex_unlock(&nic_data->udp_tunnels_lock); + return rc; } +static const struct udp_tunnel_nic_info efx_ef10_udp_tunnels = { + .set_port = efx_ef10_udp_tnl_set_port, + .unset_port = efx_ef10_udp_tnl_unset_port, + .flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP, + .tables = { + { + .n_entries = 16, + .tunnel_types = UDP_TUNNEL_TYPE_VXLAN | + UDP_TUNNEL_TYPE_GENEVE, + }, + }, +}; + /* EF10 may have multiple datapath firmware variants within a * single version. Report which variants are running. */ @@ -4172,9 +4132,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .vlan_rx_add_vid = efx_ef10_vlan_rx_add_vid, .vlan_rx_kill_vid = efx_ef10_vlan_rx_kill_vid, .udp_tnl_push_ports = efx_ef10_udp_tnl_push_ports, - .udp_tnl_add_port = efx_ef10_udp_tnl_add_port, .udp_tnl_has_port = efx_ef10_udp_tnl_has_port, - .udp_tnl_del_port = efx_ef10_udp_tnl_del_port, #ifdef CONFIG_SFC_SRIOV .sriov_configure = efx_ef10_sriov_configure, .sriov_init = efx_ef10_sriov_init, diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index befd253af918..f16b4f236031 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -36,28 +36,6 @@ #include "mcdi_pcol.h" #include "workarounds.h" -/************************************************************************** - * - * Type name strings - * - ************************************************************************** - */ - -/* UDP tunnel type names */ -static const char *const efx_udp_tunnel_type_names[] = { - [TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN] = "vxlan", - [TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE] = "geneve", -}; - -void efx_get_udp_tunnel_type_name(u16 type, char *buf, size_t buflen) -{ - if (type < ARRAY_SIZE(efx_udp_tunnel_type_names) && - efx_udp_tunnel_type_names[type] != NULL) - snprintf(buf, buflen, "%s", efx_udp_tunnel_type_names[type]); - else - snprintf(buf, buflen, "type %d", type); -} - /************************************************************************** * * Configurable values @@ -612,52 +590,6 @@ static int efx_vlan_rx_kill_vid(struct net_device *net_dev, __be16 proto, u16 vi return -EOPNOTSUPP; } -static int efx_udp_tunnel_type_map(enum udp_parsable_tunnel_type in) -{ - switch (in) { - case UDP_TUNNEL_TYPE_VXLAN: - return TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN; - case UDP_TUNNEL_TYPE_GENEVE: - return TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE; - default: - return -1; - } -} - -static void efx_udp_tunnel_add(struct net_device *dev, struct udp_tunnel_info *ti) -{ - struct efx_nic *efx = netdev_priv(dev); - struct efx_udp_tunnel tnl; - int efx_tunnel_type; - - efx_tunnel_type = efx_udp_tunnel_type_map(ti->type); - if (efx_tunnel_type < 0) - return; - - tnl.type = (u16)efx_tunnel_type; - tnl.port = ti->port; - - if (efx->type->udp_tnl_add_port) - (void)efx->type->udp_tnl_add_port(efx, tnl); -} - -static void efx_udp_tunnel_del(struct net_device *dev, struct udp_tunnel_info *ti) -{ - struct efx_nic *efx = netdev_priv(dev); - struct efx_udp_tunnel tnl; - int efx_tunnel_type; - - efx_tunnel_type = efx_udp_tunnel_type_map(ti->type); - if (efx_tunnel_type < 0) - return; - - tnl.type = (u16)efx_tunnel_type; - tnl.port = ti->port; - - if (efx->type->udp_tnl_del_port) - (void)efx->type->udp_tnl_del_port(efx, tnl); -} - static const struct net_device_ops efx_netdev_ops = { .ndo_open = efx_net_open, .ndo_stop = efx_net_stop, @@ -685,8 +617,8 @@ static const struct net_device_ops efx_netdev_ops = { #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = efx_filter_rfs, #endif - .ndo_udp_tunnel_add = efx_udp_tunnel_add, - .ndo_udp_tunnel_del = efx_udp_tunnel_del, + .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, + .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, .ndo_xdp_xmit = efx_xdp_xmit, .ndo_bpf = efx_xdp }; diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 0bf11ebb03cf..786fda559976 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -609,8 +609,6 @@ extern const unsigned int efx_reset_type_max; #define RESET_TYPE(type) \ STRING_TABLE_LOOKUP(type, efx_reset_type) -void efx_get_udp_tunnel_type_name(u16 type, char *buf, size_t buflen); - enum efx_int_mode { /* Be careful if altering to correct macro below */ EFX_INT_MODE_MSIX = 0, @@ -1173,12 +1171,9 @@ struct efx_mtd_partition { }; struct efx_udp_tunnel { +#define TUNNEL_ENCAP_UDP_PORT_ENTRY_INVALID 0xffff u16 type; /* TUNNEL_ENCAP_UDP_PORT_ENTRY_foo, see mcdi_pcol.h */ __be16 port; - /* Count of repeated adds of the same port. Used only inside the list, - * not in request arguments. - */ - u16 count; }; /** @@ -1302,9 +1297,7 @@ struct efx_udp_tunnel { * @tso_versions: Returns mask of firmware-assisted TSO versions supported. * If %NULL, then device does not support any TSO version. * @udp_tnl_push_ports: Push the list of UDP tunnel ports to the NIC if required. - * @udp_tnl_add_port: Add a UDP tunnel port * @udp_tnl_has_port: Check if a port has been added as UDP tunnel - * @udp_tnl_del_port: Remove a UDP tunnel port * @print_additional_fwver: Dump NIC-specific additional FW version info * @revision: Hardware architecture revision * @txd_ptr_tbl_base: TX descriptor ring base address @@ -1474,9 +1467,7 @@ struct efx_nic_type { int (*set_mac_address)(struct efx_nic *efx); u32 (*tso_versions)(struct efx_nic *efx); int (*udp_tnl_push_ports)(struct efx_nic *efx); - int (*udp_tnl_add_port)(struct efx_nic *efx, struct efx_udp_tunnel tnl); bool (*udp_tnl_has_port)(struct efx_nic *efx, __be16 port); - int (*udp_tnl_del_port)(struct efx_nic *efx, struct efx_udp_tunnel tnl); size_t (*print_additional_fwver)(struct efx_nic *efx, char *buf, size_t len); -- cgit v1.2.3 From 61060c5dc5c5734942528f31c094606539fffb8b Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 27 Jul 2020 12:55:41 +0100 Subject: sfc_ef100: register accesses on EF100 EF100 adds a few new valid addresses for efx_writed_page(), as well as a Function Control Window in the BAR whose location is variable. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/io.h | 16 +++++++++++++--- drivers/net/ethernet/sfc/net_driver.h | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/io.h b/drivers/net/ethernet/sfc/io.h index c3c011bc6a68..30439cc83a89 100644 --- a/drivers/net/ethernet/sfc/io.h +++ b/drivers/net/ethernet/sfc/io.h @@ -75,6 +75,11 @@ #endif #endif +static inline u32 efx_reg(struct efx_nic *efx, unsigned int reg) +{ + return efx->reg_base + reg; +} + #ifdef EFX_USE_QWORD_IO static inline void _efx_writeq(struct efx_nic *efx, __le64 value, unsigned int reg) @@ -217,8 +222,11 @@ static inline void efx_reado_table(struct efx_nic *efx, efx_oword_t *value, efx_reado(efx, value, reg + index * sizeof(efx_oword_t)); } -/* default VI stride (step between per-VI registers) is 8K */ -#define EFX_DEFAULT_VI_STRIDE 0x2000 +/* default VI stride (step between per-VI registers) is 8K on EF10 and + * 64K on EF100 + */ +#define EFX_DEFAULT_VI_STRIDE 0x2000 +#define EF100_DEFAULT_VI_STRIDE 0x10000 /* Calculate offset to page-mapped register */ static inline unsigned int efx_paged_reg(struct efx_nic *efx, unsigned int page, @@ -265,7 +273,9 @@ _efx_writed_page(struct efx_nic *efx, const efx_dword_t *value, #define efx_writed_page(efx, value, reg, page) \ _efx_writed_page(efx, value, \ reg + \ - BUILD_BUG_ON_ZERO((reg) != 0x400 && \ + BUILD_BUG_ON_ZERO((reg) != 0x180 && \ + (reg) != 0x200 && \ + (reg) != 0x400 && \ (reg) != 0x420 && \ (reg) != 0x830 && \ (reg) != 0x83c && \ diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 786fda559976..db35beabdcff 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -964,6 +964,7 @@ struct efx_async_filter_insertion { * @xdp_rxq_info_failed: Have any of the rx queues failed to initialise their * xdp_rxq_info structures? * @mem_bar: The BAR that is mapped into membase. + * @reg_base: Offset from the start of the bar to the function control window. * @monitor_work: Hardware monitor workitem * @biu_lock: BIU (bus interface unit) lock * @last_irq_cpu: Last CPU to handle a possible test interrupt. This @@ -1142,6 +1143,7 @@ struct efx_nic { bool xdp_rxq_info_failed; unsigned int mem_bar; + u32 reg_base; /* The following fields may be written more often */ -- cgit v1.2.3 From 51b35a454efdcd86f578e61ec8bf7596299c5f80 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 27 Jul 2020 12:55:55 +0100 Subject: sfc: skeleton EF100 PF driver No TX or RX path, no MCDI, not even an ifup/down handler. Besides stubs, the bulk of the patch deals with reading the Xilinx extended PCIe capability, which tells us where to find our BAR. Though in the same module, EF100 has its own struct pci_driver, which is named sfc_ef100. A small number of additional nic_type methods are added; those in the TX (tx_enqueue) and RX (rx_packet) paths are called through indirect call wrappers to minimise the performance impact. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/Kconfig | 5 +- drivers/net/ethernet/sfc/Makefile | 4 +- drivers/net/ethernet/sfc/ef10.c | 7 + drivers/net/ethernet/sfc/ef100.c | 541 ++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/ef100.h | 12 + drivers/net/ethernet/sfc/ef100_ethtool.c | 24 ++ drivers/net/ethernet/sfc/ef100_ethtool.h | 12 + drivers/net/ethernet/sfc/ef100_netdev.c | 129 +++++++ drivers/net/ethernet/sfc/ef100_netdev.h | 17 + drivers/net/ethernet/sfc/ef100_nic.c | 174 ++++++++++ drivers/net/ethernet/sfc/ef100_nic.h | 26 ++ drivers/net/ethernet/sfc/ef100_rx.c | 25 ++ drivers/net/ethernet/sfc/ef100_rx.h | 19 ++ drivers/net/ethernet/sfc/ef100_tx.c | 32 ++ drivers/net/ethernet/sfc/ef100_tx.h | 18 + drivers/net/ethernet/sfc/efx.c | 8 + drivers/net/ethernet/sfc/efx.h | 16 +- drivers/net/ethernet/sfc/ethtool.c | 2 - drivers/net/ethernet/sfc/ethtool_common.c | 2 +- drivers/net/ethernet/sfc/ethtool_common.h | 2 - drivers/net/ethernet/sfc/mcdi.c | 2 +- drivers/net/ethernet/sfc/net_driver.h | 16 + drivers/net/ethernet/sfc/nic_common.h | 6 + drivers/net/ethernet/sfc/siena.c | 3 + drivers/net/ethernet/sfc/tx.c | 4 +- drivers/net/ethernet/sfc/tx_common.h | 2 + 26 files changed, 1095 insertions(+), 13 deletions(-) create mode 100644 drivers/net/ethernet/sfc/ef100.c create mode 100644 drivers/net/ethernet/sfc/ef100.h create mode 100644 drivers/net/ethernet/sfc/ef100_ethtool.c create mode 100644 drivers/net/ethernet/sfc/ef100_ethtool.h create mode 100644 drivers/net/ethernet/sfc/ef100_netdev.c create mode 100644 drivers/net/ethernet/sfc/ef100_netdev.h create mode 100644 drivers/net/ethernet/sfc/ef100_nic.c create mode 100644 drivers/net/ethernet/sfc/ef100_nic.h create mode 100644 drivers/net/ethernet/sfc/ef100_rx.c create mode 100644 drivers/net/ethernet/sfc/ef100_rx.h create mode 100644 drivers/net/ethernet/sfc/ef100_tx.c create mode 100644 drivers/net/ethernet/sfc/ef100_tx.h (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig index 81b0f7d3a025..5e37c8313725 100644 --- a/drivers/net/ethernet/sfc/Kconfig +++ b/drivers/net/ethernet/sfc/Kconfig @@ -17,7 +17,7 @@ config NET_VENDOR_SOLARFLARE if NET_VENDOR_SOLARFLARE config SFC - tristate "Solarflare SFC9000/SFC9100-family support" + tristate "Solarflare SFC9000/SFC9100/EF100-family support" depends on PCI select MDIO select CRC32 @@ -26,6 +26,9 @@ config SFC This driver supports 10/40-gigabit Ethernet cards based on the Solarflare SFC9000-family and SFC9100-family controllers. + It also supports 10/25/40/100-gigabit Ethernet cards based + on the Solarflare EF100 networking IP in Xilinx FPGAs. + To compile this driver as a module, choose M here. The module will be called sfc. config SFC_MTD diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index 87d093da22ca..8bd01c429f91 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -4,7 +4,9 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ tx.o tx_common.o tx_tso.o rx.o rx_common.o \ selftest.o ethtool.o ethtool_common.o ptp.o \ mcdi.o mcdi_port.o mcdi_port_common.o \ - mcdi_functions.o mcdi_filters.o mcdi_mon.o + mcdi_functions.o mcdi_filters.o mcdi_mon.o \ + ef100.o ef100_nic.o ef100_netdev.o \ + ef100_ethtool.o ef100_rx.o ef100_tx.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o siena_sriov.o ef10_sriov.o diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index fa7229fff2ff..4b0b2cf026a5 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -6,6 +6,7 @@ #include "net_driver.h" #include "rx_common.h" +#include "tx_common.h" #include "ef10_regs.h" #include "io.h" #include "mcdi.h" @@ -3977,6 +3978,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .tx_remove = efx_mcdi_tx_remove, .tx_write = efx_ef10_tx_write, .tx_limit_len = efx_ef10_tx_limit_len, + .tx_enqueue = __efx_enqueue_skb, .rx_push_rss_config = efx_mcdi_vf_rx_push_rss_config, .rx_pull_rss_config = efx_mcdi_rx_pull_rss_config, .rx_probe = efx_mcdi_rx_probe, @@ -3984,6 +3986,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .rx_remove = efx_mcdi_rx_remove, .rx_write = efx_ef10_rx_write, .rx_defer_refill = efx_ef10_rx_defer_refill, + .rx_packet = __efx_rx_packet, .ev_probe = efx_mcdi_ev_probe, .ev_init = efx_ef10_ev_init, .ev_fini = efx_mcdi_ev_fini, @@ -4038,6 +4041,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .rx_hash_key_size = 40, .check_caps = ef10_check_caps, .print_additional_fwver = efx_ef10_print_additional_fwver, + .sensor_event = efx_mcdi_sensor_event, }; const struct efx_nic_type efx_hunt_a0_nic_type = { @@ -4087,6 +4091,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .tx_remove = efx_mcdi_tx_remove, .tx_write = efx_ef10_tx_write, .tx_limit_len = efx_ef10_tx_limit_len, + .tx_enqueue = __efx_enqueue_skb, .rx_push_rss_config = efx_mcdi_pf_rx_push_rss_config, .rx_pull_rss_config = efx_mcdi_rx_pull_rss_config, .rx_push_rss_context_config = efx_mcdi_rx_push_rss_context_config, @@ -4097,6 +4102,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .rx_remove = efx_mcdi_rx_remove, .rx_write = efx_ef10_rx_write, .rx_defer_refill = efx_ef10_rx_defer_refill, + .rx_packet = __efx_rx_packet, .ev_probe = efx_mcdi_ev_probe, .ev_init = efx_ef10_ev_init, .ev_fini = efx_mcdi_ev_fini, @@ -4172,4 +4178,5 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .rx_hash_key_size = 40, .check_caps = ef10_check_caps, .print_additional_fwver = efx_ef10_print_additional_fwver, + .sensor_event = efx_mcdi_sensor_event, }; diff --git a/drivers/net/ethernet/sfc/ef100.c b/drivers/net/ethernet/sfc/ef100.c new file mode 100644 index 000000000000..de611c0f94e7 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100.c @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include +#include +#include "efx_common.h" +#include "efx_channels.h" +#include "io.h" +#include "ef100_nic.h" +#include "ef100_netdev.h" +#include "ef100_regs.h" +#include "ef100.h" + +#define EFX_EF100_PCI_DEFAULT_BAR 2 + +/* Number of bytes at start of vendor specified extended capability that indicate + * that the capability is vendor specified. i.e. offset from value returned by + * pci_find_next_ext_capability() to beginning of vendor specified capability + * header. + */ +#define PCI_EXT_CAP_HDR_LENGTH 4 + +/* Expected size of a Xilinx continuation address table entry. */ +#define ESE_GZ_CFGBAR_CONT_CAP_MIN_LENGTH 16 + +struct ef100_func_ctl_window { + bool valid; + unsigned int bar; + u64 offset; +}; + +static int ef100_pci_walk_xilinx_table(struct efx_nic *efx, u64 offset, + struct ef100_func_ctl_window *result); + +/* Number of bytes to offset when reading bit position x with dword accessors. */ +#define ROUND_DOWN_TO_DWORD(x) (((x) & (~31)) >> 3) + +#define EXTRACT_BITS(x, lbn, width) \ + (((x) >> ((lbn) & 31)) & ((1ull << (width)) - 1)) + +static u32 _ef100_pci_get_bar_bits_with_width(struct efx_nic *efx, + int structure_start, + int lbn, int width) +{ + efx_dword_t dword; + + efx_readd(efx, &dword, structure_start + ROUND_DOWN_TO_DWORD(lbn)); + + return EXTRACT_BITS(le32_to_cpu(dword.u32[0]), lbn, width); +} + +#define ef100_pci_get_bar_bits(efx, entry_location, bitdef) \ + _ef100_pci_get_bar_bits_with_width(efx, entry_location, \ + ESF_GZ_CFGBAR_ ## bitdef ## _LBN, \ + ESF_GZ_CFGBAR_ ## bitdef ## _WIDTH) + +static int ef100_pci_parse_ef100_entry(struct efx_nic *efx, int entry_location, + struct ef100_func_ctl_window *result) +{ + u64 offset = ef100_pci_get_bar_bits(efx, entry_location, EF100_FUNC_CTL_WIN_OFF) << + ESE_GZ_EF100_FUNC_CTL_WIN_OFF_SHIFT; + u32 bar = ef100_pci_get_bar_bits(efx, entry_location, EF100_BAR); + + netif_dbg(efx, probe, efx->net_dev, + "Found EF100 function control window bar=%d offset=0x%llx\n", + bar, offset); + + if (result->valid) { + netif_err(efx, probe, efx->net_dev, + "Duplicated EF100 table entry.\n"); + return -EINVAL; + } + + if (bar == ESE_GZ_CFGBAR_EF100_BAR_NUM_EXPANSION_ROM || + bar == ESE_GZ_CFGBAR_EF100_BAR_NUM_INVALID) { + netif_err(efx, probe, efx->net_dev, + "Bad BAR value of %d in Xilinx capabilities EF100 entry.\n", + bar); + return -EINVAL; + } + + result->bar = bar; + result->offset = offset; + result->valid = true; + return 0; +} + +static bool ef100_pci_does_bar_overflow(struct efx_nic *efx, int bar, + u64 next_entry) +{ + return next_entry + ESE_GZ_CFGBAR_ENTRY_HEADER_SIZE > + pci_resource_len(efx->pci_dev, bar); +} + +/* Parse a Xilinx capabilities table entry describing a continuation to a new + * sub-table. + */ +static int ef100_pci_parse_continue_entry(struct efx_nic *efx, int entry_location, + struct ef100_func_ctl_window *result) +{ + unsigned int previous_bar; + efx_oword_t entry; + u64 offset; + int rc = 0; + u32 bar; + + efx_reado(efx, &entry, entry_location); + + bar = EFX_OWORD_FIELD32(entry, ESF_GZ_CFGBAR_CONT_CAP_BAR); + + offset = EFX_OWORD_FIELD64(entry, ESF_GZ_CFGBAR_CONT_CAP_OFFSET) << + ESE_GZ_CONT_CAP_OFFSET_BYTES_SHIFT; + + previous_bar = efx->mem_bar; + + if (bar == ESE_GZ_VSEC_BAR_NUM_EXPANSION_ROM || + bar == ESE_GZ_VSEC_BAR_NUM_INVALID) { + netif_err(efx, probe, efx->net_dev, + "Bad BAR value of %d in Xilinx capabilities sub-table.\n", + bar); + return -EINVAL; + } + + if (bar != previous_bar) { + efx_fini_io(efx); + + if (ef100_pci_does_bar_overflow(efx, bar, offset)) { + netif_err(efx, probe, efx->net_dev, + "Xilinx table will overrun BAR[%d] offset=0x%llx\n", + bar, offset); + return -EINVAL; + } + + /* Temporarily map new BAR. */ + rc = efx_init_io(efx, bar, + DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH), + pci_resource_len(efx->pci_dev, bar)); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Mapping new BAR for Xilinx table failed, rc=%d\n", rc); + return rc; + } + } + + rc = ef100_pci_walk_xilinx_table(efx, offset, result); + if (rc) + return rc; + + if (bar != previous_bar) { + efx_fini_io(efx); + + /* Put old BAR back. */ + rc = efx_init_io(efx, previous_bar, + DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH), + pci_resource_len(efx->pci_dev, previous_bar)); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Putting old BAR back failed, rc=%d\n", rc); + return rc; + } + } + + return 0; +} + +/* Iterate over the Xilinx capabilities table in the currently mapped BAR and + * call ef100_pci_parse_ef100_entry() on any EF100 entries and + * ef100_pci_parse_continue_entry() on any table continuations. + */ +static int ef100_pci_walk_xilinx_table(struct efx_nic *efx, u64 offset, + struct ef100_func_ctl_window *result) +{ + u64 current_entry = offset; + int rc = 0; + + while (true) { + u32 id = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_FORMAT); + u32 last = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_LAST); + u32 rev = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_REV); + u32 entry_size; + + if (id == ESE_GZ_CFGBAR_ENTRY_LAST) + return 0; + + entry_size = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_SIZE); + + netif_dbg(efx, probe, efx->net_dev, + "Seen Xilinx table entry 0x%x size 0x%x at 0x%llx in BAR[%d]\n", + id, entry_size, current_entry, efx->mem_bar); + + if (entry_size < sizeof(u32) * 2) { + netif_err(efx, probe, efx->net_dev, + "Xilinx table entry too short len=0x%x\n", entry_size); + return -EINVAL; + } + + switch (id) { + case ESE_GZ_CFGBAR_ENTRY_EF100: + if (rev != ESE_GZ_CFGBAR_ENTRY_REV_EF100 || + entry_size < ESE_GZ_CFGBAR_ENTRY_SIZE_EF100) { + netif_err(efx, probe, efx->net_dev, + "Bad length or rev for EF100 entry in Xilinx capabilities table. entry_size=%d rev=%d.\n", + entry_size, rev); + return -EINVAL; + } + + rc = ef100_pci_parse_ef100_entry(efx, current_entry, + result); + if (rc) + return rc; + break; + case ESE_GZ_CFGBAR_ENTRY_CONT_CAP_ADDR: + if (rev != 0 || entry_size < ESE_GZ_CFGBAR_CONT_CAP_MIN_LENGTH) { + netif_err(efx, probe, efx->net_dev, + "Bad length or rev for continue entry in Xilinx capabilities table. entry_size=%d rev=%d.\n", + entry_size, rev); + return -EINVAL; + } + + rc = ef100_pci_parse_continue_entry(efx, current_entry, result); + if (rc) + return rc; + break; + default: + /* Ignore unknown table entries. */ + break; + } + + if (last) + return 0; + + current_entry += entry_size; + + if (ef100_pci_does_bar_overflow(efx, efx->mem_bar, current_entry)) { + netif_err(efx, probe, efx->net_dev, + "Xilinx table overrun at position=0x%llx.\n", + current_entry); + return -EINVAL; + } + } +} + +static int _ef100_pci_get_config_bits_with_width(struct efx_nic *efx, + int structure_start, int lbn, + int width, u32 *result) +{ + int rc, pos = structure_start + ROUND_DOWN_TO_DWORD(lbn); + u32 temp; + + rc = pci_read_config_dword(efx->pci_dev, pos, &temp); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to read PCI config dword at %d\n", + pos); + return rc; + } + + *result = EXTRACT_BITS(temp, lbn, width); + + return 0; +} + +#define ef100_pci_get_config_bits(efx, entry_location, bitdef, result) \ + _ef100_pci_get_config_bits_with_width(efx, entry_location, \ + ESF_GZ_VSEC_ ## bitdef ## _LBN, \ + ESF_GZ_VSEC_ ## bitdef ## _WIDTH, result) + +/* Call ef100_pci_walk_xilinx_table() for the Xilinx capabilities table pointed + * to by this PCI_EXT_CAP_ID_VNDR. + */ +static int ef100_pci_parse_xilinx_cap(struct efx_nic *efx, int vndr_cap, + bool has_offset_hi, + struct ef100_func_ctl_window *result) +{ + u32 offset_high = 0; + u32 offset_lo = 0; + u64 offset = 0; + u32 bar = 0; + int rc = 0; + + rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_BAR, &bar); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to read ESF_GZ_VSEC_TBL_BAR, rc=%d\n", + rc); + return rc; + } + + if (bar == ESE_GZ_CFGBAR_CONT_CAP_BAR_NUM_EXPANSION_ROM || + bar == ESE_GZ_CFGBAR_CONT_CAP_BAR_NUM_INVALID) { + netif_err(efx, probe, efx->net_dev, + "Bad BAR value of %d in Xilinx capabilities sub-table.\n", + bar); + return -EINVAL; + } + + rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_OFF_LO, &offset_lo); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to read ESF_GZ_VSEC_TBL_OFF_LO, rc=%d\n", + rc); + return rc; + } + + /* Get optional extension to 64bit offset. */ + if (has_offset_hi) { + rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_OFF_HI, &offset_high); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to read ESF_GZ_VSEC_TBL_OFF_HI, rc=%d\n", + rc); + return rc; + } + } + + offset = (((u64)offset_lo) << ESE_GZ_VSEC_TBL_OFF_LO_BYTES_SHIFT) | + (((u64)offset_high) << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT); + + if (offset > pci_resource_len(efx->pci_dev, bar) - sizeof(u32) * 2) { + netif_err(efx, probe, efx->net_dev, + "Xilinx table will overrun BAR[%d] offset=0x%llx\n", + bar, offset); + return -EINVAL; + } + + /* Temporarily map BAR. */ + rc = efx_init_io(efx, bar, + DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH), + pci_resource_len(efx->pci_dev, bar)); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "efx_init_io failed, rc=%d\n", rc); + return rc; + } + + rc = ef100_pci_walk_xilinx_table(efx, offset, result); + + /* Unmap temporarily mapped BAR. */ + efx_fini_io(efx); + return rc; +} + +/* Call ef100_pci_parse_ef100_entry() for each Xilinx PCI_EXT_CAP_ID_VNDR + * capability. + */ +static int ef100_pci_find_func_ctrl_window(struct efx_nic *efx, + struct ef100_func_ctl_window *result) +{ + int num_xilinx_caps = 0; + int cap = 0; + + result->valid = false; + + while ((cap = pci_find_next_ext_capability(efx->pci_dev, cap, PCI_EXT_CAP_ID_VNDR)) != 0) { + int vndr_cap = cap + PCI_EXT_CAP_HDR_LENGTH; + u32 vsec_ver = 0; + u32 vsec_len = 0; + u32 vsec_id = 0; + int rc = 0; + + num_xilinx_caps++; + + rc = ef100_pci_get_config_bits(efx, vndr_cap, ID, &vsec_id); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to read ESF_GZ_VSEC_ID, rc=%d\n", + rc); + return rc; + } + + rc = ef100_pci_get_config_bits(efx, vndr_cap, VER, &vsec_ver); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to read ESF_GZ_VSEC_VER, rc=%d\n", + rc); + return rc; + } + + /* Get length of whole capability - i.e. starting at cap */ + rc = ef100_pci_get_config_bits(efx, vndr_cap, LEN, &vsec_len); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to read ESF_GZ_VSEC_LEN, rc=%d\n", + rc); + return rc; + } + + if (vsec_id == ESE_GZ_XILINX_VSEC_ID && + vsec_ver == ESE_GZ_VSEC_VER_XIL_CFGBAR && + vsec_len >= ESE_GZ_VSEC_LEN_MIN) { + bool has_offset_hi = (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT); + + rc = ef100_pci_parse_xilinx_cap(efx, vndr_cap, + has_offset_hi, result); + if (rc) + return rc; + } + } + + if (num_xilinx_caps && !result->valid) { + netif_err(efx, probe, efx->net_dev, + "Seen %d Xilinx tables, but no EF100 entry.\n", + num_xilinx_caps); + return -EINVAL; + } + + return 0; +} + +/* Final NIC shutdown + * This is called only at module unload (or hotplug removal). A PF can call + * this on its VFs to ensure they are unbound first. + */ +static void ef100_pci_remove(struct pci_dev *pci_dev) +{ + struct efx_nic *efx; + + efx = pci_get_drvdata(pci_dev); + if (!efx) + return; + + rtnl_lock(); + dev_close(efx->net_dev); + rtnl_unlock(); + + /* Unregistering our netdev notifier triggers unbinding of TC indirect + * blocks, so we have to do it before PCI removal. + */ + unregister_netdevice_notifier(&efx->netdev_notifier); + ef100_remove(efx); + efx_fini_io(efx); + netif_dbg(efx, drv, efx->net_dev, "shutdown successful\n"); + + pci_set_drvdata(pci_dev, NULL); + efx_fini_struct(efx); + free_netdev(efx->net_dev); + + pci_disable_pcie_error_reporting(pci_dev); +}; + +static int ef100_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *entry) +{ + struct ef100_func_ctl_window fcw = { 0 }; + struct net_device *net_dev; + struct efx_nic *efx; + int rc; + + /* Allocate and initialise a struct net_device and struct efx_nic */ + net_dev = alloc_etherdev_mq(sizeof(*efx), EFX_MAX_CORE_TX_QUEUES); + if (!net_dev) + return -ENOMEM; + efx = netdev_priv(net_dev); + efx->type = (const struct efx_nic_type *)entry->driver_data; + + pci_set_drvdata(pci_dev, efx); + SET_NETDEV_DEV(net_dev, &pci_dev->dev); + rc = efx_init_struct(efx, pci_dev, net_dev); + if (rc) + goto fail; + + efx->vi_stride = EF100_DEFAULT_VI_STRIDE; + netif_info(efx, probe, efx->net_dev, + "Solarflare EF100 NIC detected\n"); + + rc = ef100_pci_find_func_ctrl_window(efx, &fcw); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Error looking for ef100 function control window, rc=%d\n", + rc); + goto fail; + } + + if (!fcw.valid) { + /* Extended capability not found - use defaults. */ + fcw.bar = EFX_EF100_PCI_DEFAULT_BAR; + fcw.offset = 0; + fcw.valid = true; + } + + if (fcw.offset > pci_resource_len(efx->pci_dev, fcw.bar) - ESE_GZ_FCW_LEN) { + netif_err(efx, probe, efx->net_dev, + "Func control window overruns BAR\n"); + goto fail; + } + + /* Set up basic I/O (BAR mappings etc) */ + rc = efx_init_io(efx, fcw.bar, + DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH), + pci_resource_len(efx->pci_dev, fcw.bar)); + if (rc) + goto fail; + + efx->reg_base = fcw.offset; + + efx->netdev_notifier.notifier_call = ef100_netdev_event; + rc = register_netdevice_notifier(&efx->netdev_notifier); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "Failed to register netdevice notifier, rc=%d\n", rc); + goto fail; + } + + rc = efx->type->probe(efx); + if (rc) + goto fail; + + netif_dbg(efx, probe, efx->net_dev, "initialisation successful\n"); + + return 0; + +fail: + ef100_pci_remove(pci_dev); + return rc; +} + +/* PCI device ID table */ +static const struct pci_device_id ef100_pci_table[] = { + {PCI_DEVICE(PCI_VENDOR_ID_XILINX, 0x0100), /* Riverhead PF */ + .driver_data = (unsigned long) &ef100_pf_nic_type }, + {0} /* end of list */ +}; + +struct pci_driver ef100_pci_driver = { + .name = "sfc_ef100", + .id_table = ef100_pci_table, + .probe = ef100_pci_probe, + .remove = ef100_pci_remove, + .err_handler = &efx_err_handlers, +}; + +MODULE_DEVICE_TABLE(pci, ef100_pci_table); diff --git a/drivers/net/ethernet/sfc/ef100.h b/drivers/net/ethernet/sfc/ef100.h new file mode 100644 index 000000000000..a63f0ff6641b --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +extern struct pci_driver ef100_pci_driver; diff --git a/drivers/net/ethernet/sfc/ef100_ethtool.c b/drivers/net/ethernet/sfc/ef100_ethtool.c new file mode 100644 index 000000000000..729c425d0f78 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_ethtool.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ +#include +#include +#include "net_driver.h" +#include "efx.h" +#include "mcdi_port_common.h" +#include "ethtool_common.h" +#include "ef100_ethtool.h" +#include "mcdi_functions.h" + +/* Ethtool options available + */ +const struct ethtool_ops ef100_ethtool_ops = { + .get_drvinfo = efx_ethtool_get_drvinfo, +}; diff --git a/drivers/net/ethernet/sfc/ef100_ethtool.h b/drivers/net/ethernet/sfc/ef100_ethtool.h new file mode 100644 index 000000000000..6efda72dfc6c --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_ethtool.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +extern const struct ethtool_ops ef100_ethtool_ops; diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c new file mode 100644 index 000000000000..f900b375d9b1 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_netdev.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ +#include "net_driver.h" +#include "mcdi_port_common.h" +#include "mcdi_functions.h" +#include "efx_common.h" +#include "efx_channels.h" +#include "tx_common.h" +#include "ef100_netdev.h" +#include "ef100_ethtool.h" +#include "efx_common.h" +#include "nic_common.h" +#include "ef100_nic.h" +#include "ef100_tx.h" +#include "ef100_regs.h" +#include "mcdi_filters.h" +#include "rx_common.h" + +static void ef100_update_name(struct efx_nic *efx) +{ + strcpy(efx->name, efx->net_dev->name); +} + +/* Initiate a packet transmission. We use one channel per CPU + * (sharing when we have more CPUs than channels). + * + * Context: non-blocking. + * Note that returning anything other than NETDEV_TX_OK will cause the + * OS to free the skb. + */ +static netdev_tx_t ef100_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev) +{ + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_tx_queue *tx_queue; + struct efx_channel *channel; + int rc; + + channel = efx_get_tx_channel(efx, skb_get_queue_mapping(skb)); + netif_vdbg(efx, tx_queued, efx->net_dev, + "%s len %d data %d channel %d\n", __func__, + skb->len, skb->data_len, channel->channel); + if (!efx->n_channels || !efx->n_tx_channels || !channel) { + netif_stop_queue(net_dev); + goto err; + } + + tx_queue = &channel->tx_queue[0]; + rc = ef100_enqueue_skb(tx_queue, skb); + if (rc == 0) + return NETDEV_TX_OK; + +err: + net_dev->stats.tx_dropped++; + return NETDEV_TX_OK; +} + +static const struct net_device_ops ef100_netdev_ops = { + .ndo_start_xmit = ef100_hard_start_xmit, +}; + +/* Netdev registration + */ +int ef100_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct efx_nic *efx = container_of(this, struct efx_nic, netdev_notifier); + struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); + + if (netdev_priv(net_dev) == efx && event == NETDEV_CHANGENAME) + ef100_update_name(efx); + + return NOTIFY_DONE; +} + +int ef100_register_netdev(struct efx_nic *efx) +{ + struct net_device *net_dev = efx->net_dev; + int rc; + + net_dev->watchdog_timeo = 5 * HZ; + net_dev->irq = efx->pci_dev->irq; + net_dev->netdev_ops = &ef100_netdev_ops; + net_dev->min_mtu = EFX_MIN_MTU; + net_dev->max_mtu = EFX_MAX_MTU; + net_dev->ethtool_ops = &ef100_ethtool_ops; + + rtnl_lock(); + + rc = dev_alloc_name(net_dev, net_dev->name); + if (rc < 0) + goto fail_locked; + ef100_update_name(efx); + + rc = register_netdevice(net_dev); + if (rc) + goto fail_locked; + + /* Always start with carrier off; PHY events will detect the link */ + netif_carrier_off(net_dev); + + efx->state = STATE_READY; + rtnl_unlock(); + efx_init_mcdi_logging(efx); + + return 0; + +fail_locked: + rtnl_unlock(); + netif_err(efx, drv, efx->net_dev, "could not register net dev\n"); + return rc; +} + +void ef100_unregister_netdev(struct efx_nic *efx) +{ + if (efx_dev_registered(efx)) { + efx_fini_mcdi_logging(efx); + efx->state = STATE_UNINIT; + unregister_netdev(efx->net_dev); + } +} diff --git a/drivers/net/ethernet/sfc/ef100_netdev.h b/drivers/net/ethernet/sfc/ef100_netdev.h new file mode 100644 index 000000000000..d40abb7cc086 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_netdev.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include + +int ef100_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr); +int ef100_register_netdev(struct efx_nic *efx); +void ef100_unregister_netdev(struct efx_nic *efx); diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c new file mode 100644 index 000000000000..7bb304bc148e --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "ef100_nic.h" +#include "efx_common.h" +#include "efx_channels.h" +#include "io.h" +#include "selftest.h" +#include "ef100_regs.h" +#include "mcdi.h" +#include "mcdi_pcol.h" +#include "mcdi_port_common.h" +#include "mcdi_functions.h" +#include "mcdi_filters.h" +#include "ef100_rx.h" +#include "ef100_tx.h" +#include "ef100_netdev.h" + +#define EF100_MAX_VIS 4096 + +/* MCDI + */ +static int ef100_get_warm_boot_count(struct efx_nic *efx) +{ + efx_dword_t reg; + + efx_readd(efx, ®, efx_reg(efx, ER_GZ_MC_SFT_STATUS)); + + if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) == 0xffffffff) { + netif_err(efx, hw, efx->net_dev, "Hardware unavailable\n"); + efx->state = STATE_DISABLED; + return -ENETDOWN; + } else { + return EFX_DWORD_FIELD(reg, EFX_WORD_1) == 0xb007 ? + EFX_DWORD_FIELD(reg, EFX_WORD_0) : -EIO; + } +} + +/* Event handling + */ +static int ef100_ev_probe(struct efx_channel *channel) +{ + /* Allocate an extra descriptor for the QMDA status completion entry */ + return efx_nic_alloc_buffer(channel->efx, &channel->eventq.buf, + (channel->eventq_mask + 2) * + sizeof(efx_qword_t), + GFP_KERNEL); +} + +static irqreturn_t ef100_msi_interrupt(int irq, void *dev_id) +{ + struct efx_msi_context *context = dev_id; + struct efx_nic *efx = context->efx; + + netif_vdbg(efx, intr, efx->net_dev, + "IRQ %d on CPU %d\n", irq, raw_smp_processor_id()); + + if (likely(READ_ONCE(efx->irq_soft_enabled))) { + /* Note test interrupts */ + if (context->index == efx->irq_level) + efx->last_irq_cpu = raw_smp_processor_id(); + + /* Schedule processing of the channel */ + efx_schedule_channel_irq(efx->channel[context->index]); + } + + return IRQ_HANDLED; +} + +/* NIC level access functions + */ +const struct efx_nic_type ef100_pf_nic_type = { + .revision = EFX_REV_EF100, + .is_vf = false, + .probe = ef100_probe_pf, + .mcdi_max_ver = 2, + .irq_enable_master = efx_port_dummy_op_void, + .irq_disable_non_ev = efx_port_dummy_op_void, + .push_irq_moderation = efx_channel_dummy_op_void, + .min_interrupt_mode = EFX_INT_MODE_MSIX, + + .ev_probe = ef100_ev_probe, + .irq_handle_msi = ef100_msi_interrupt, + + /* Per-type bar/size configuration not used on ef100. Location of + * registers is defined by extended capabilities. + */ + .mem_bar = NULL, + .mem_map_size = NULL, + +}; + +/* NIC probe and remove + */ +static int ef100_probe_main(struct efx_nic *efx) +{ + unsigned int bar_size = resource_size(&efx->pci_dev->resource[efx->mem_bar]); + struct net_device *net_dev = efx->net_dev; + struct ef100_nic_data *nic_data; + int i, rc; + + if (WARN_ON(bar_size == 0)) + return -EIO; + + nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL); + if (!nic_data) + return -ENOMEM; + efx->nic_data = nic_data; + nic_data->efx = efx; + net_dev->features |= efx->type->offload_features; + net_dev->hw_features |= efx->type->offload_features; + + /* Get the MC's warm boot count. In case it's rebooting right + * now, be prepared to retry. + */ + i = 0; + for (;;) { + rc = ef100_get_warm_boot_count(efx); + if (rc >= 0) + break; + if (++i == 5) + goto fail; + ssleep(1); + } + nic_data->warm_boot_count = rc; + + /* In case we're recovering from a crash (kexec), we want to + * cancel any outstanding request by the previous user of this + * function. We send a special message using the least + * significant bits of the 'high' (doorbell) register. + */ + _efx_writed(efx, cpu_to_le32(1), efx_reg(efx, ER_GZ_MC_DB_HWRD)); + + /* Post-IO section. */ + + efx->max_vis = EF100_MAX_VIS; + + rc = efx_init_channels(efx); + if (rc) + goto fail; + + rc = ef100_register_netdev(efx); + if (rc) + goto fail; + + return 0; +fail: + return rc; +} + +int ef100_probe_pf(struct efx_nic *efx) +{ + return ef100_probe_main(efx); +} + +void ef100_remove(struct efx_nic *efx) +{ + struct ef100_nic_data *nic_data = efx->nic_data; + + ef100_unregister_netdev(efx); + efx_fini_channels(efx); + kfree(efx->phy_data); + efx->phy_data = NULL; + kfree(nic_data); + efx->nic_data = NULL; +} diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h new file mode 100644 index 000000000000..643111aebba5 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_nic.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "nic_common.h" + +extern const struct efx_nic_type ef100_pf_nic_type; + +int ef100_probe_pf(struct efx_nic *efx); +void ef100_remove(struct efx_nic *efx); + +struct ef100_nic_data { + struct efx_nic *efx; + u16 warm_boot_count; +}; + +#define efx_ef100_has_cap(caps, flag) \ + (!!((caps) & BIT_ULL(MC_CMD_GET_CAPABILITIES_V4_OUT_ ## flag ## _LBN))) diff --git a/drivers/net/ethernet/sfc/ef100_rx.c b/drivers/net/ethernet/sfc/ef100_rx.c new file mode 100644 index 000000000000..d6b62f980463 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_rx.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2019 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "ef100_rx.h" +#include "rx_common.h" +#include "efx.h" + +void __ef100_rx_packet(struct efx_channel *channel) +{ + /* Stub. No RX path yet. Discard the buffer. */ + struct efx_rx_buffer *rx_buf = efx_rx_buffer(&channel->rx_queue, + channel->rx_pkt_index); + struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); + + efx_free_rx_buffers(rx_queue, rx_buf, 1); + channel->rx_pkt_n_frags = 0; +} diff --git a/drivers/net/ethernet/sfc/ef100_rx.h b/drivers/net/ethernet/sfc/ef100_rx.h new file mode 100644 index 000000000000..a6083a98f362 --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_rx.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2019 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_EF100_RX_H +#define EFX_EF100_RX_H + +#include "net_driver.h" + +void __ef100_rx_packet(struct efx_channel *channel); + +#endif diff --git a/drivers/net/ethernet/sfc/ef100_tx.c b/drivers/net/ethernet/sfc/ef100_tx.c new file mode 100644 index 000000000000..b12295413c0d --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_tx.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2018 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "tx_common.h" +#include "nic_common.h" +#include "ef100_tx.h" + +/* Add a socket buffer to a TX queue + * + * You must hold netif_tx_lock() to call this function. + * + * Returns 0 on success, error code otherwise. In case of an error this + * function will free the SKB. + */ +int ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + /* Stub. No TX path yet. */ + struct efx_nic *efx = tx_queue->efx; + + netif_stop_queue(efx->net_dev); + dev_kfree_skb_any(skb); + return -ENODEV; +} diff --git a/drivers/net/ethernet/sfc/ef100_tx.h b/drivers/net/ethernet/sfc/ef100_tx.h new file mode 100644 index 000000000000..a7895efca4ff --- /dev/null +++ b/drivers/net/ethernet/sfc/ef100_tx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2019 Solarflare Communications Inc. + * Copyright 2019-2020 Xilinx 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_EF100_TX_H +#define EFX_EF100_TX_H + +#include "net_driver.h" + +netdev_tx_t ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +#endif diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index f16b4f236031..f5aa1bd02f19 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -25,6 +25,7 @@ #include "efx.h" #include "efx_common.h" #include "efx_channels.h" +#include "ef100.h" #include "rx_common.h" #include "tx_common.h" #include "nic.h" @@ -1360,8 +1361,14 @@ static int __init efx_init_module(void) if (rc < 0) goto err_pci; + rc = pci_register_driver(&ef100_pci_driver); + if (rc < 0) + goto err_pci_ef100; + return 0; + err_pci_ef100: + pci_unregister_driver(&efx_pci_driver); err_pci: efx_destroy_reset_workqueue(); err_reset: @@ -1378,6 +1385,7 @@ static void __exit efx_exit_module(void) { printk(KERN_INFO "Solarflare NET driver unloading\n"); + pci_unregister_driver(&ef100_pci_driver); pci_unregister_driver(&efx_pci_driver); efx_destroy_reset_workqueue(); #ifdef CONFIG_SFC_SRIOV diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index e7e7d8d1a07b..a9808e86068d 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -8,7 +8,10 @@ #ifndef EFX_EFX_H #define EFX_EFX_H +#include #include "net_driver.h" +#include "ef100_rx.h" +#include "ef100_tx.h" #include "filter.h" int efx_net_open(struct net_device *net_dev); @@ -18,13 +21,18 @@ int efx_net_stop(struct net_device *net_dev); void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue); netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); -netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +netdev_tx_t __efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +static inline netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + return INDIRECT_CALL_2(tx_queue->efx->type->tx_enqueue, + ef100_enqueue_skb, __efx_enqueue_skb, + tx_queue, skb); +} void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); void efx_xmit_done_single(struct efx_tx_queue *tx_queue); int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, void *type_data); extern unsigned int efx_piobuf_size; -extern bool efx_separate_tx_channels; /* RX */ void __efx_rx_packet(struct efx_channel *channel); @@ -33,7 +41,9 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, static inline void efx_rx_flush_packet(struct efx_channel *channel) { if (channel->rx_pkt_n_frags) - __efx_rx_packet(channel); + INDIRECT_CALL_2(channel->efx->type->rx_packet, + __ef100_rx_packet, __efx_rx_packet, + channel); } /* Maximum number of TCP segments we support for soft-TSO */ diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 038c08d2d7aa..4ffda7782f68 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -221,8 +221,6 @@ static int efx_ethtool_get_ts_info(struct net_device *net_dev, return 0; } -const char *efx_driver_name = KBUILD_MODNAME; - const struct ethtool_ops efx_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_USECS_IRQ | diff --git a/drivers/net/ethernet/sfc/ethtool_common.c b/drivers/net/ethernet/sfc/ethtool_common.c index fb06097b70d8..05ac87807929 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.c +++ b/drivers/net/ethernet/sfc/ethtool_common.c @@ -104,7 +104,7 @@ void efx_ethtool_get_drvinfo(struct net_device *net_dev, { struct efx_nic *efx = netdev_priv(net_dev); - strlcpy(info->driver, efx_driver_name, sizeof(info->driver)); + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); efx_mcdi_print_fwver(efx, info->fw_version, sizeof(info->fw_version)); diff --git a/drivers/net/ethernet/sfc/ethtool_common.h b/drivers/net/ethernet/sfc/ethtool_common.h index 0c0ea9ac4d08..659491932101 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.h +++ b/drivers/net/ethernet/sfc/ethtool_common.h @@ -11,8 +11,6 @@ #ifndef EFX_ETHTOOL_COMMON_H #define EFX_ETHTOOL_COMMON_H -extern const char *efx_driver_name; - void efx_ethtool_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *info); u32 efx_ethtool_get_msglevel(struct net_device *net_dev); diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 6c49740a178e..5467819aef6e 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -1337,7 +1337,7 @@ void efx_mcdi_process_event(struct efx_channel *channel, efx_mcdi_process_link_change(efx, event); break; case MCDI_EVENT_CODE_SENSOREVT: - efx_mcdi_sensor_event(efx, event); + efx_sensor_event(efx, event); break; case MCDI_EVENT_CODE_SCHEDERR: netif_dbg(efx, hw, efx->net_dev, diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index db35beabdcff..9791fac0b649 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -963,6 +963,7 @@ struct efx_async_filter_insertion { * @vpd_sn: Serial number read from VPD * @xdp_rxq_info_failed: Have any of the rx queues failed to initialise their * xdp_rxq_info structures? + * @netdev_notifier: Netdevice notifier. * @mem_bar: The BAR that is mapped into membase. * @reg_base: Offset from the start of the bar to the function control window. * @monitor_work: Hardware monitor workitem @@ -1142,6 +1143,8 @@ struct efx_nic { char *vpd_sn; bool xdp_rxq_info_failed; + struct notifier_block netdev_notifier; + unsigned int mem_bar; u32 reg_base; @@ -1246,6 +1249,7 @@ struct efx_udp_tunnel { * @tx_init: Initialise TX queue on the NIC * @tx_remove: Free resources for TX queue * @tx_write: Write TX descriptors and doorbell + * @tx_enqueue: Add an SKB to TX queue * @rx_push_rss_config: Write RSS hash key and indirection table to the NIC * @rx_pull_rss_config: Read RSS hash key and indirection table back from the NIC * @rx_push_rss_context_config: Write RSS hash key and indirection table for @@ -1257,6 +1261,7 @@ struct efx_udp_tunnel { * @rx_remove: Free resources for RX queue * @rx_write: Write RX descriptors and doorbell * @rx_defer_refill: Generate a refill reminder event + * @rx_packet: Receive the queued RX buffer on a channel * @ev_probe: Allocate resources for event queue * @ev_init: Initialise event queue on the NIC * @ev_fini: Deinitialise event queue on the NIC @@ -1301,6 +1306,7 @@ struct efx_udp_tunnel { * @udp_tnl_push_ports: Push the list of UDP tunnel ports to the NIC if required. * @udp_tnl_has_port: Check if a port has been added as UDP tunnel * @print_additional_fwver: Dump NIC-specific additional FW version info + * @sensor_event: Handle a sensor event from MCDI * @revision: Hardware architecture revision * @txd_ptr_tbl_base: TX descriptor ring base address * @rxd_ptr_tbl_base: RX descriptor ring base address @@ -1381,6 +1387,7 @@ struct efx_nic_type { void (*tx_init)(struct efx_tx_queue *tx_queue); void (*tx_remove)(struct efx_tx_queue *tx_queue); void (*tx_write)(struct efx_tx_queue *tx_queue); + netdev_tx_t (*tx_enqueue)(struct efx_tx_queue *tx_queue, struct sk_buff *skb); unsigned int (*tx_limit_len)(struct efx_tx_queue *tx_queue, dma_addr_t dma_addr, unsigned int len); int (*rx_push_rss_config)(struct efx_nic *efx, bool user, @@ -1398,6 +1405,7 @@ struct efx_nic_type { void (*rx_remove)(struct efx_rx_queue *rx_queue); void (*rx_write)(struct efx_rx_queue *rx_queue); void (*rx_defer_refill)(struct efx_rx_queue *rx_queue); + void (*rx_packet)(struct efx_channel *channel); int (*ev_probe)(struct efx_channel *channel); int (*ev_init)(struct efx_channel *channel); void (*ev_fini)(struct efx_channel *channel); @@ -1472,6 +1480,7 @@ struct efx_nic_type { bool (*udp_tnl_has_port)(struct efx_nic *efx, __be16 port); size_t (*print_additional_fwver)(struct efx_nic *efx, char *buf, size_t len); + void (*sensor_event)(struct efx_nic *efx, efx_qword_t *ev); int revision; unsigned int txd_ptr_tbl_base; @@ -1523,6 +1532,13 @@ efx_get_channel(struct efx_nic *efx, unsigned index) _channel = _channel->channel ? \ (_efx)->channel[_channel->channel - 1] : NULL) +static inline struct efx_channel * +efx_get_tx_channel(struct efx_nic *efx, unsigned int index) +{ + EFX_WARN_ON_ONCE_PARANOID(index >= efx->n_tx_channels); + return efx->channel[efx->tx_channel_offset + index]; +} + static inline struct efx_tx_queue * efx_get_tx_queue(struct efx_nic *efx, unsigned index, unsigned type) { diff --git a/drivers/net/ethernet/sfc/nic_common.h b/drivers/net/ethernet/sfc/nic_common.h index e04b6817cde3..974107354087 100644 --- a/drivers/net/ethernet/sfc/nic_common.h +++ b/drivers/net/ethernet/sfc/nic_common.h @@ -225,6 +225,12 @@ void efx_nic_event_test_start(struct efx_channel *channel); bool efx_nic_event_present(struct efx_channel *channel); +static inline void efx_sensor_event(struct efx_nic *efx, efx_qword_t *ev) +{ + if (efx->type->sensor_event) + efx->type->sensor_event(efx, ev); +} + /* Some statistics are computed as A - B where A and B each increase * linearly with some hardware counter(s) and the counters are read * asynchronously. If the counters contributing to B are always read diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 219fb3a0c9d0..a7ea630bb5e6 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -1018,6 +1018,7 @@ const struct efx_nic_type siena_a0_nic_type = { .tx_remove = efx_farch_tx_remove, .tx_write = efx_farch_tx_write, .tx_limit_len = efx_farch_tx_limit_len, + .tx_enqueue = __efx_enqueue_skb, .rx_push_rss_config = siena_rx_push_rss_config, .rx_pull_rss_config = siena_rx_pull_rss_config, .rx_probe = efx_farch_rx_probe, @@ -1025,6 +1026,7 @@ const struct efx_nic_type siena_a0_nic_type = { .rx_remove = efx_farch_rx_remove, .rx_write = efx_farch_rx_write, .rx_defer_refill = efx_farch_rx_defer_refill, + .rx_packet = __efx_rx_packet, .ev_probe = efx_farch_ev_probe, .ev_init = efx_farch_ev_init, .ev_fini = efx_farch_ev_fini, @@ -1096,4 +1098,5 @@ const struct efx_nic_type siena_a0_nic_type = { 1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT), .rx_hash_key_size = 16, .check_caps = siena_check_caps, + .sensor_event = efx_mcdi_sensor_event, }; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 1bcf50ab95d9..727201d5eb24 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -284,7 +284,7 @@ static int efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, * Returns NETDEV_TX_OK. * You must hold netif_tx_lock() to call this function. */ -netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +netdev_tx_t __efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) { unsigned int old_insert_count = tx_queue->insert_count; bool xmit_more = netdev_xmit_more(); @@ -503,7 +503,7 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, } tx_queue = efx_get_tx_queue(efx, index, type); - return efx_enqueue_skb(tx_queue, skb); + return __efx_enqueue_skb(tx_queue, skb); } void efx_xmit_done_single(struct efx_tx_queue *tx_queue) diff --git a/drivers/net/ethernet/sfc/tx_common.h b/drivers/net/ethernet/sfc/tx_common.h index cbe995b024a6..bbab7f248250 100644 --- a/drivers/net/ethernet/sfc/tx_common.h +++ b/drivers/net/ethernet/sfc/tx_common.h @@ -40,4 +40,6 @@ int efx_tx_map_data(struct efx_tx_queue *tx_queue, struct sk_buff *skb, unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); int efx_tx_tso_fallback(struct efx_tx_queue *tx_queue, struct sk_buff *skb); + +extern bool efx_separate_tx_channels; #endif -- cgit v1.2.3 From d19a5372186336df8a90391c1ae2011e03310dca Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 3 Aug 2020 21:34:00 +0100 Subject: sfc_ef100: TX path for EF100 NICs Includes checksum offload and TSO, so declare those in our netdev features. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_nic.c | 8 + drivers/net/ethernet/sfc/ef100_tx.c | 367 +++++++++++++++++++++++++++++++++- drivers/net/ethernet/sfc/ef100_tx.h | 4 + drivers/net/ethernet/sfc/net_driver.h | 21 ++ drivers/net/ethernet/sfc/tx_common.c | 1 + 5 files changed, 396 insertions(+), 5 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index 9b5e4b42fe51..fcc5b0de76d7 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -263,6 +263,9 @@ static int ef100_ev_process(struct efx_channel *channel, int quota) case ESE_GZ_EF100_EV_MCDI: efx_mcdi_process_event(channel, p_event); break; + case ESE_GZ_EF100_EV_TX_COMPLETION: + ef100_ev_tx(channel, p_event); + break; case ESE_GZ_EF100_EV_DRIVER: netif_info(efx, drv, efx->net_dev, "Driver initiated event " EFX_QWORD_FMT "\n", @@ -436,10 +439,15 @@ static unsigned int ef100_check_caps(const struct efx_nic *efx, /* NIC level access functions */ +#define EF100_OFFLOAD_FEATURES (NETIF_F_HW_CSUM | \ + NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_FRAGLIST | \ + NETIF_F_TSO_ECN | NETIF_F_TSO_MANGLEID | NETIF_F_HW_VLAN_CTAG_TX) + const struct efx_nic_type ef100_pf_nic_type = { .revision = EFX_REV_EF100, .is_vf = false, .probe = ef100_probe_pf, + .offload_features = EF100_OFFLOAD_FEATURES, .mcdi_max_ver = 2, .mcdi_request = ef100_mcdi_request, .mcdi_poll_response = ef100_mcdi_poll_response, diff --git a/drivers/net/ethernet/sfc/ef100_tx.c b/drivers/net/ethernet/sfc/ef100_tx.c index 15e646f8c3e0..a09546e43408 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.c +++ b/drivers/net/ethernet/sfc/ef100_tx.c @@ -9,14 +9,24 @@ * by the Free Software Foundation, incorporated herein by reference. */ +#include + #include "net_driver.h" #include "tx_common.h" #include "nic_common.h" +#include "mcdi_functions.h" +#include "ef100_regs.h" +#include "io.h" #include "ef100_tx.h" +#include "ef100_nic.h" -/* TX queue stubs */ int ef100_tx_probe(struct efx_tx_queue *tx_queue) { + /* Allocate an extra descriptor for the QMDA status completion entry */ + return efx_nic_alloc_buffer(tx_queue->efx, &tx_queue->txd.buf, + (tx_queue->ptr_mask + 2) * + sizeof(efx_oword_t), + GFP_KERNEL); return 0; } @@ -27,10 +37,286 @@ void ef100_tx_init(struct efx_tx_queue *tx_queue) netdev_get_tx_queue(tx_queue->efx->net_dev, tx_queue->channel->channel - tx_queue->efx->tx_channel_offset); + + if (efx_mcdi_tx_init(tx_queue, false)) + netdev_WARN(tx_queue->efx->net_dev, + "failed to initialise TXQ %d\n", tx_queue->queue); +} + +static bool ef100_tx_can_tso(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + struct efx_nic *efx = tx_queue->efx; + struct ef100_nic_data *nic_data; + struct efx_tx_buffer *buffer; + struct tcphdr *tcphdr; + struct iphdr *iphdr; + size_t header_len; + u32 mss; + + nic_data = efx->nic_data; + + if (!skb_is_gso_tcp(skb)) + return false; + if (!(efx->net_dev->features & NETIF_F_TSO)) + return false; + + mss = skb_shinfo(skb)->gso_size; + if (unlikely(mss < 4)) { + WARN_ONCE(1, "MSS of %u is too small for TSO\n", mss); + return false; + } + + header_len = efx_tx_tso_header_length(skb); + if (header_len > nic_data->tso_max_hdr_len) + return false; + + if (skb_shinfo(skb)->gso_segs > nic_data->tso_max_payload_num_segs) { + /* net_dev->gso_max_segs should've caught this */ + WARN_ON_ONCE(1); + return false; + } + + if (skb->data_len / mss > nic_data->tso_max_frames) + return false; + + /* net_dev->gso_max_size should've caught this */ + if (WARN_ON_ONCE(skb->data_len > nic_data->tso_max_payload_len)) + return false; + + /* Reserve an empty buffer for the TSO V3 descriptor. + * Convey the length of the header since we already know it. + */ + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + buffer->flags = EFX_TX_BUF_TSO_V3 | EFX_TX_BUF_CONT; + buffer->len = header_len; + buffer->unmap_len = 0; + buffer->skb = skb; + ++tx_queue->insert_count; + + /* Adjust the TCP checksum to exclude the total length, since we set + * ED_INNER_IP_LEN in the descriptor. + */ + tcphdr = tcp_hdr(skb); + if (skb_is_gso_v6(skb)) { + tcphdr->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + } else { + iphdr = ip_hdr(skb); + tcphdr->check = ~csum_tcpudp_magic(iphdr->saddr, iphdr->daddr, + 0, IPPROTO_TCP, 0); + } + return true; +} + +static efx_oword_t *ef100_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index) +{ + if (likely(tx_queue->txd.buf.addr)) + return ((efx_oword_t *)tx_queue->txd.buf.addr) + index; + else + return NULL; +} + +void ef100_notify_tx_desc(struct efx_tx_queue *tx_queue) +{ + unsigned int write_ptr; + efx_dword_t reg; + + if (unlikely(tx_queue->notify_count == tx_queue->write_count)) + return; + + write_ptr = tx_queue->write_count & tx_queue->ptr_mask; + /* The write pointer goes into the high word */ + EFX_POPULATE_DWORD_1(reg, ERF_GZ_TX_RING_PIDX, write_ptr); + efx_writed_page(tx_queue->efx, ®, + ER_GZ_TX_RING_DOORBELL, tx_queue->queue); + tx_queue->notify_count = tx_queue->write_count; + tx_queue->xmit_more_available = false; +} + +static void ef100_tx_push_buffers(struct efx_tx_queue *tx_queue) +{ + ef100_notify_tx_desc(tx_queue); + ++tx_queue->pushes; +} + +static void ef100_set_tx_csum_partial(const struct sk_buff *skb, + struct efx_tx_buffer *buffer, efx_oword_t *txd) +{ + efx_oword_t csum; + int csum_start; + + if (!skb || skb->ip_summed != CHECKSUM_PARTIAL) + return; + + /* skb->csum_start has the offset from head, but we need the offset + * from data. + */ + csum_start = skb_checksum_start_offset(skb); + EFX_POPULATE_OWORD_3(csum, + ESF_GZ_TX_SEND_CSO_PARTIAL_EN, 1, + ESF_GZ_TX_SEND_CSO_PARTIAL_START_W, + csum_start >> 1, + ESF_GZ_TX_SEND_CSO_PARTIAL_CSUM_W, + skb->csum_offset >> 1); + EFX_OR_OWORD(*txd, *txd, csum); +} + +static void ef100_set_tx_hw_vlan(const struct sk_buff *skb, efx_oword_t *txd) +{ + u16 vlan_tci = skb_vlan_tag_get(skb); + efx_oword_t vlan; + + EFX_POPULATE_OWORD_2(vlan, + ESF_GZ_TX_SEND_VLAN_INSERT_EN, 1, + ESF_GZ_TX_SEND_VLAN_INSERT_TCI, vlan_tci); + EFX_OR_OWORD(*txd, *txd, vlan); +} + +static void ef100_make_send_desc(struct efx_nic *efx, + const struct sk_buff *skb, + struct efx_tx_buffer *buffer, efx_oword_t *txd, + unsigned int segment_count) +{ + /* TX send descriptor */ + EFX_POPULATE_OWORD_3(*txd, + ESF_GZ_TX_SEND_NUM_SEGS, segment_count, + ESF_GZ_TX_SEND_LEN, buffer->len, + ESF_GZ_TX_SEND_ADDR, buffer->dma_addr); + + if (likely(efx->net_dev->features & NETIF_F_HW_CSUM)) + ef100_set_tx_csum_partial(skb, buffer, txd); + if (efx->net_dev->features & NETIF_F_HW_VLAN_CTAG_TX && + skb && skb_vlan_tag_present(skb)) + ef100_set_tx_hw_vlan(skb, txd); +} + +static void ef100_make_tso_desc(struct efx_nic *efx, + const struct sk_buff *skb, + struct efx_tx_buffer *buffer, efx_oword_t *txd, + unsigned int segment_count) +{ + u32 mangleid = (efx->net_dev->features & NETIF_F_TSO_MANGLEID) || + skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID ? + ESE_GZ_TX_DESC_IP4_ID_NO_OP : + ESE_GZ_TX_DESC_IP4_ID_INC_MOD16; + u16 vlan_enable = efx->net_dev->features & NETIF_F_HW_VLAN_CTAG_TX ? + skb_vlan_tag_present(skb) : 0; + unsigned int len, ip_offset, tcp_offset, payload_segs; + u16 vlan_tci = skb_vlan_tag_get(skb); + u32 mss = skb_shinfo(skb)->gso_size; + + len = skb->len - buffer->len; + /* We use 1 for the TSO descriptor and 1 for the header */ + payload_segs = segment_count - 2; + ip_offset = skb_network_offset(skb); + tcp_offset = skb_transport_offset(skb); + + EFX_POPULATE_OWORD_13(*txd, + ESF_GZ_TX_DESC_TYPE, ESE_GZ_TX_DESC_TYPE_TSO, + ESF_GZ_TX_TSO_MSS, mss, + ESF_GZ_TX_TSO_HDR_NUM_SEGS, 1, + ESF_GZ_TX_TSO_PAYLOAD_NUM_SEGS, payload_segs, + ESF_GZ_TX_TSO_HDR_LEN_W, buffer->len >> 1, + ESF_GZ_TX_TSO_PAYLOAD_LEN, len, + ESF_GZ_TX_TSO_CSO_INNER_L4, 1, + ESF_GZ_TX_TSO_INNER_L3_OFF_W, ip_offset >> 1, + ESF_GZ_TX_TSO_INNER_L4_OFF_W, tcp_offset >> 1, + ESF_GZ_TX_TSO_ED_INNER_IP4_ID, mangleid, + ESF_GZ_TX_TSO_ED_INNER_IP_LEN, 1, + ESF_GZ_TX_TSO_VLAN_INSERT_EN, vlan_enable, + ESF_GZ_TX_TSO_VLAN_INSERT_TCI, vlan_tci + ); +} + +static void ef100_tx_make_descriptors(struct efx_tx_queue *tx_queue, + const struct sk_buff *skb, + unsigned int segment_count) +{ + unsigned int old_write_count = tx_queue->write_count; + unsigned int new_write_count = old_write_count; + struct efx_tx_buffer *buffer; + unsigned int next_desc_type; + unsigned int write_ptr; + efx_oword_t *txd; + unsigned int nr_descs = tx_queue->insert_count - old_write_count; + + if (unlikely(nr_descs == 0)) + return; + + if (segment_count) + next_desc_type = ESE_GZ_TX_DESC_TYPE_TSO; + else + next_desc_type = ESE_GZ_TX_DESC_TYPE_SEND; + + /* if it's a raw write (such as XDP) then always SEND single frames */ + if (!skb) + nr_descs = 1; + + do { + write_ptr = new_write_count & tx_queue->ptr_mask; + buffer = &tx_queue->buffer[write_ptr]; + txd = ef100_tx_desc(tx_queue, write_ptr); + ++new_write_count; + + /* Create TX descriptor ring entry */ + tx_queue->packet_write_count = new_write_count; + + switch (next_desc_type) { + case ESE_GZ_TX_DESC_TYPE_SEND: + ef100_make_send_desc(tx_queue->efx, skb, + buffer, txd, nr_descs); + break; + case ESE_GZ_TX_DESC_TYPE_TSO: + /* TX TSO descriptor */ + WARN_ON_ONCE(!(buffer->flags & EFX_TX_BUF_TSO_V3)); + ef100_make_tso_desc(tx_queue->efx, skb, + buffer, txd, nr_descs); + break; + default: + /* TX segment descriptor */ + EFX_POPULATE_OWORD_3(*txd, + ESF_GZ_TX_DESC_TYPE, ESE_GZ_TX_DESC_TYPE_SEG, + ESF_GZ_TX_SEG_LEN, buffer->len, + ESF_GZ_TX_SEG_ADDR, buffer->dma_addr); + } + /* if it's a raw write (such as XDP) then always SEND */ + next_desc_type = skb ? ESE_GZ_TX_DESC_TYPE_SEG : + ESE_GZ_TX_DESC_TYPE_SEND; + + } while (new_write_count != tx_queue->insert_count); + + wmb(); /* Ensure descriptors are written before they are fetched */ + + tx_queue->write_count = new_write_count; + + /* The write_count above must be updated before reading + * channel->holdoff_doorbell to avoid a race with the + * completion path, so ensure these operations are not + * re-ordered. This also flushes the update of write_count + * back into the cache. + */ + smp_mb(); } void ef100_tx_write(struct efx_tx_queue *tx_queue) { + ef100_tx_make_descriptors(tx_queue, NULL, 0); + ef100_tx_push_buffers(tx_queue); +} + +void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) +{ + unsigned int tx_done = + EFX_QWORD_FIELD(*p_event, ESF_GZ_EV_TXCMPL_NUM_DESC); + unsigned int qlabel = + EFX_QWORD_FIELD(*p_event, ESF_GZ_EV_TXCMPL_Q_LABEL); + struct efx_tx_queue *tx_queue = + efx_channel_get_tx_queue(channel, qlabel); + unsigned int tx_index = (tx_queue->read_count + tx_done - 1) & + tx_queue->ptr_mask; + + efx_xmit_done(tx_queue, tx_index); } /* Add a socket buffer to a TX queue @@ -42,10 +328,81 @@ void ef100_tx_write(struct efx_tx_queue *tx_queue) */ int ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) { - /* Stub. No TX path yet. */ + unsigned int old_insert_count = tx_queue->insert_count; struct efx_nic *efx = tx_queue->efx; + bool xmit_more = netdev_xmit_more(); + unsigned int fill_level; + unsigned int segments; + int rc; + + if (!tx_queue->buffer || !tx_queue->ptr_mask) { + netif_stop_queue(efx->net_dev); + dev_kfree_skb_any(skb); + return -ENODEV; + } + + segments = skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 0; + if (segments == 1) + segments = 0; /* Don't use TSO/GSO for a single segment. */ + if (segments && !ef100_tx_can_tso(tx_queue, skb)) { + rc = efx_tx_tso_fallback(tx_queue, skb); + tx_queue->tso_fallbacks++; + if (rc) + goto err; + else + return 0; + } + + /* Map for DMA and create descriptors */ + rc = efx_tx_map_data(tx_queue, skb, segments); + if (rc) + goto err; + ef100_tx_make_descriptors(tx_queue, skb, segments); + + fill_level = efx_channel_tx_fill_level(tx_queue->channel); + if (fill_level > efx->txq_stop_thresh) { + netif_tx_stop_queue(tx_queue->core_txq); + /* Re-read after a memory barrier in case we've raced with + * the completion path. Otherwise there's a danger we'll never + * restart the queue if all completions have just happened. + */ + smp_mb(); + fill_level = efx_channel_tx_fill_level(tx_queue->channel); + if (fill_level < efx->txq_stop_thresh) + netif_tx_start_queue(tx_queue->core_txq); + } + + if (__netdev_tx_sent_queue(tx_queue->core_txq, skb->len, xmit_more)) + tx_queue->xmit_more_available = false; /* push doorbell */ + else if (tx_queue->write_count - tx_queue->notify_count > 255) + /* Ensure we never push more than 256 packets at once */ + tx_queue->xmit_more_available = false; /* push */ + else + tx_queue->xmit_more_available = true; /* don't push yet */ + + if (!tx_queue->xmit_more_available) + ef100_tx_push_buffers(tx_queue); + + if (segments) { + tx_queue->tso_bursts++; + tx_queue->tso_packets += segments; + tx_queue->tx_packets += segments; + } else { + tx_queue->tx_packets++; + } + return 0; + +err: + efx_enqueue_unwind(tx_queue, old_insert_count); + if (!IS_ERR_OR_NULL(skb)) + dev_kfree_skb_any(skb); - netif_stop_queue(efx->net_dev); - dev_kfree_skb_any(skb); - return -ENODEV; + /* If we're not expecting another transmit and we had something to push + * on this queue then we need to push here to get the previous packets + * out. We only enter this branch from before the 'Update BQL' section + * above, so xmit_more_available still refers to the old state. + */ + if (tx_queue->xmit_more_available && !xmit_more) + ef100_tx_push_buffers(tx_queue); + return rc; } diff --git a/drivers/net/ethernet/sfc/ef100_tx.h b/drivers/net/ethernet/sfc/ef100_tx.h index 9a472f7aff43..fa23e430bdd7 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.h +++ b/drivers/net/ethernet/sfc/ef100_tx.h @@ -17,6 +17,10 @@ int ef100_tx_probe(struct efx_tx_queue *tx_queue); void ef100_tx_init(struct efx_tx_queue *tx_queue); void ef100_tx_write(struct efx_tx_queue *tx_queue); +void ef100_notify_tx_desc(struct efx_tx_queue *tx_queue); +unsigned int ef100_tx_max_skb_descs(struct efx_nic *efx); + +void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event); netdev_tx_t ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); #endif diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 9791fac0b649..7bb7ecb480ae 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -173,6 +173,7 @@ struct efx_tx_buffer { #define EFX_TX_BUF_MAP_SINGLE 8 /* buffer was mapped with dma_map_single() */ #define EFX_TX_BUF_OPTION 0x10 /* empty buffer for option descriptor */ #define EFX_TX_BUF_XDP 0x20 /* buffer was sent with XDP */ +#define EFX_TX_BUF_TSO_V3 0x40 /* empty buffer for a TSO_V3 descriptor */ /** * struct efx_tx_queue - An Efx TX queue @@ -245,6 +246,7 @@ struct efx_tx_buffer { * @pio_packets: Number of times the TX PIO feature has been used * @xmit_more_available: Are any packets waiting to be pushed to the NIC * @cb_packets: Number of times the TX copybreak feature has been used + * @notify_count: Count of notified descriptors to the NIC * @empty_read_count: If the completion path has seen the queue as empty * and the transmission path has not yet checked this, the value of * @read_count bitwise-added to %EFX_EMPTY_COUNT_VALID; otherwise 0. @@ -292,6 +294,7 @@ struct efx_tx_queue { unsigned int pio_packets; bool xmit_more_available; unsigned int cb_packets; + unsigned int notify_count; /* Statistics to supplement MAC stats */ unsigned long tx_packets; @@ -1669,6 +1672,24 @@ static inline void efx_xmit_hwtstamp_pending(struct sk_buff *skb) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; } +/* Get the max fill level of the TX queues on this channel */ +static inline unsigned int +efx_channel_tx_fill_level(struct efx_channel *channel) +{ + struct efx_tx_queue *tx_queue; + unsigned int fill_level = 0; + + /* This function is currently only used by EF100, which maybe + * could do something simpler and just compute the fill level + * of the single TXQ that's really in use. + */ + efx_for_each_channel_tx_queue(tx_queue, channel) + fill_level = max(fill_level, + tx_queue->insert_count - tx_queue->read_count); + + return fill_level; +} + /* Get all supported features. * If a feature is not fixed, it is present in hw_features. * If a feature is fixed, it does not present in hw_features, but diff --git a/drivers/net/ethernet/sfc/tx_common.c b/drivers/net/ethernet/sfc/tx_common.c index 11b64c609550..793e234819a8 100644 --- a/drivers/net/ethernet/sfc/tx_common.c +++ b/drivers/net/ethernet/sfc/tx_common.c @@ -71,6 +71,7 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue) "initialising TX queue %d\n", tx_queue->queue); tx_queue->insert_count = 0; + tx_queue->notify_count = 0; tx_queue->write_count = 0; tx_queue->packet_write_count = 0; tx_queue->old_write_count = 0; -- cgit v1.2.3