* [PATCH v5 06/13] net: ethernet: aquantia: Atlantic A0 and B0 specific functions.
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel Belous, Dmitry Bezrukov
In-Reply-To: <5e87af3b0effb96698d0c085366b1ba9320a6305.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Add Atlantic A0 and B0 specific functions.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.c | 907 +++++++++++++++++++
drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.h | 34 +
.../ethernet/aquantia/hw_atl/hw_atl_a0_internal.h | 152 ++++
drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.c | 960 +++++++++++++++++++++
drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.h | 34 +
.../ethernet/aquantia/hw_atl/hw_atl_b0_internal.h | 205 +++++
6 files changed, 2292 insertions(+)
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.c
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.h
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0_internal.h
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.c
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.h
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0_internal.h
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.c b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.c
new file mode 100644
index 0000000..fafdc9c
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.c
@@ -0,0 +1,907 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_a0.c: Definition of Atlantic hardware specific functions. */
+
+#include "../aq_hw.h"
+#include "../aq_hw_utils.h"
+#include "../aq_ring.h"
+#include "hw_atl_a0.h"
+#include "hw_atl_utils.h"
+#include "hw_atl_llh.h"
+#include "hw_atl_a0_internal.h"
+
+static int hw_atl_a0_get_hw_caps(struct aq_hw_s *self,
+ struct aq_hw_caps_s *aq_hw_caps)
+{
+ memcpy(aq_hw_caps, &hw_atl_a0_hw_caps_, sizeof(*aq_hw_caps));
+ return 0;
+}
+
+static struct aq_hw_s *hw_atl_a0_create(struct aq_pci_func_s *aq_pci_func,
+ unsigned int port,
+ struct aq_hw_ops *ops)
+{
+ struct hw_atl_s *self = NULL;
+ int err = 0;
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ err = -ENOMEM;
+ goto err_exit;
+ }
+ self->base.aq_pci_func = aq_pci_func;
+
+ self->base.not_ff_addr = 0x10U;
+
+err_exit:
+ return (struct aq_hw_s *)self;
+}
+
+static void hw_atl_a0_destroy(struct aq_hw_s *self)
+{
+ kfree(self);
+}
+
+static int hw_atl_a0_hw_reset(struct aq_hw_s *self)
+{
+ int err = 0;
+
+ glb_glb_reg_res_dis_set(self, 1U);
+ pci_pci_reg_res_dis_set(self, 0U);
+ rx_rx_reg_res_dis_set(self, 0U);
+ tx_tx_reg_res_dis_set(self, 0U);
+
+ HW_ATL_FLUSH();
+ glb_soft_res_set(self, 1);
+
+ /* check 10 times by 1ms */
+ AQ_HW_WAIT_FOR(glb_soft_res_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+
+ itr_irq_reg_res_dis_set(self, 0U);
+ itr_res_irq_set(self, 1U);
+
+ /* check 10 times by 1ms */
+ AQ_HW_WAIT_FOR(itr_res_irq_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+
+ hw_atl_utils_mpi_set(self, MPI_RESET, 0x0U);
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_qos_set(struct aq_hw_s *self)
+{
+ u32 tc = 0U;
+ u32 buff_size = 0U;
+ unsigned int i_priority = 0U;
+ bool is_rx_flow_control = false;
+
+ /* TPS Descriptor rate init */
+ tps_tx_pkt_shed_desc_rate_curr_time_res_set(self, 0x0U);
+ tps_tx_pkt_shed_desc_rate_lim_set(self, 0xA);
+
+ /* TPS VM init */
+ tps_tx_pkt_shed_desc_vm_arb_mode_set(self, 0U);
+
+ /* TPS TC credits init */
+ tps_tx_pkt_shed_desc_tc_arb_mode_set(self, 0U);
+ tps_tx_pkt_shed_data_arb_mode_set(self, 0U);
+
+ tps_tx_pkt_shed_tc_data_max_credit_set(self, 0xFFF, 0U);
+ tps_tx_pkt_shed_tc_data_weight_set(self, 0x64, 0U);
+ tps_tx_pkt_shed_desc_tc_max_credit_set(self, 0x50, 0U);
+ tps_tx_pkt_shed_desc_tc_weight_set(self, 0x1E, 0U);
+
+ /* Tx buf size */
+ buff_size = HW_ATL_A0_TXBUF_MAX;
+
+ tpb_tx_pkt_buff_size_per_tc_set(self, buff_size, tc);
+ tpb_tx_buff_hi_threshold_per_tc_set(self,
+ (buff_size * (1024 / 32U) * 66U) /
+ 100U, tc);
+ tpb_tx_buff_lo_threshold_per_tc_set(self,
+ (buff_size * (1024 / 32U) * 50U) /
+ 100U, tc);
+
+ /* QoS Rx buf size per TC */
+ tc = 0;
+ is_rx_flow_control = (AQ_NIC_FC_RX & self->aq_nic_cfg->flow_control);
+ buff_size = HW_ATL_A0_RXBUF_MAX;
+
+ rpb_rx_pkt_buff_size_per_tc_set(self, buff_size, tc);
+ rpb_rx_buff_hi_threshold_per_tc_set(self,
+ (buff_size *
+ (1024U / 32U) * 66U) /
+ 100U, tc);
+ rpb_rx_buff_lo_threshold_per_tc_set(self,
+ (buff_size *
+ (1024U / 32U) * 50U) /
+ 100U, tc);
+ rpb_rx_xoff_en_per_tc_set(self, is_rx_flow_control ? 1U : 0U, tc);
+
+ /* QoS 802.1p priority -> TC mapping */
+ for (i_priority = 8U; i_priority--;)
+ rpf_rpb_user_priority_tc_map_set(self, i_priority, 0U);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_rss_hash_set(struct aq_hw_s *self,
+ struct aq_rss_parameters *rss_params)
+{
+ struct aq_nic_cfg_s *cfg = NULL;
+ int err = 0;
+ unsigned int i = 0U;
+ unsigned int addr = 0U;
+
+ cfg = self->aq_nic_cfg;
+
+ for (i = 10, addr = 0U; i--; ++addr) {
+ u32 key_data = cfg->is_rss ?
+ __swab32(rss_params->hash_secret_key[i]) : 0U;
+ rpf_rss_key_wr_data_set(self, key_data);
+ rpf_rss_key_addr_set(self, addr);
+ rpf_rss_key_wr_en_set(self, 1U);
+ AQ_HW_WAIT_FOR(rpf_rss_key_wr_en_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+ }
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_rss_set(struct aq_hw_s *self,
+ struct aq_rss_parameters *rss_params)
+{
+ u8 *indirection_table = rss_params->indirection_table;
+ u32 i = 0U;
+ u32 num_rss_queues = max(1U, self->aq_nic_cfg->num_rss_queues);
+ int err = 0;
+ u16 bitary[(HW_ATL_A0_RSS_REDIRECTION_MAX *
+ HW_ATL_A0_RSS_REDIRECTION_BITS / 16U)];
+
+ memset(bitary, 0, sizeof(bitary));
+
+ for (i = HW_ATL_A0_RSS_REDIRECTION_MAX; i--; ) {
+ (*(u32 *)(bitary + ((i * 3U) / 16U))) |=
+ ((indirection_table[i] % num_rss_queues) <<
+ ((i * 3U) & 0xFU));
+ }
+
+ for (i = AQ_DIMOF(bitary); i--;) {
+ rpf_rss_redir_tbl_wr_data_set(self, bitary[i]);
+ rpf_rss_redir_tbl_addr_set(self, i);
+ rpf_rss_redir_wr_en_set(self, 1U);
+ AQ_HW_WAIT_FOR(rpf_rss_redir_wr_en_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+ }
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_offload_set(struct aq_hw_s *self,
+ struct aq_nic_cfg_s *aq_nic_cfg)
+{
+ int err = 0;
+
+ /* TX checksums offloads*/
+ tpo_ipv4header_crc_offload_en_set(self, 1);
+ tpo_tcp_udp_crc_offload_en_set(self, 1);
+ if (err < 0)
+ goto err_exit;
+
+ /* RX checksums offloads*/
+ rpo_ipv4header_crc_offload_en_set(self, 1);
+ rpo_tcp_udp_crc_offload_en_set(self, 1);
+ if (err < 0)
+ goto err_exit;
+
+ /* LSO offloads*/
+ tdm_large_send_offload_en_set(self, 0xFFFFFFFFU);
+ if (err < 0)
+ goto err_exit;
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_init_tx_path(struct aq_hw_s *self)
+{
+ thm_lso_tcp_flag_of_first_pkt_set(self, 0x0FF6U);
+ thm_lso_tcp_flag_of_middle_pkt_set(self, 0x0FF6U);
+ thm_lso_tcp_flag_of_last_pkt_set(self, 0x0F7FU);
+
+ /* Tx interrupts */
+ tdm_tx_desc_wr_wb_irq_en_set(self, 1U);
+
+ /* misc */
+ aq_hw_write_reg(self, 0x00007040U, IS_CHIP_FEATURE(TPO2) ?
+ 0x00010000U : 0x00000000U);
+ tdm_tx_dca_en_set(self, 0U);
+ tdm_tx_dca_mode_set(self, 0U);
+
+ tpb_tx_path_scp_ins_en_set(self, 1U);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_init_rx_path(struct aq_hw_s *self)
+{
+ struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
+ int i;
+
+ /* Rx TC/RSS number config */
+ rpb_rpf_rx_traf_class_mode_set(self, 1U);
+
+ /* Rx flow control */
+ rpb_rx_flow_ctl_mode_set(self, 1U);
+
+ /* RSS Ring selection */
+ reg_rx_flr_rss_control1set(self, cfg->is_rss ?
+ 0xB3333333U : 0x00000000U);
+
+ /* Multicast filters */
+ for (i = HW_ATL_A0_MAC_MAX; i--;) {
+ rpfl2_uc_flr_en_set(self, (i == 0U) ? 1U : 0U, i);
+ rpfl2unicast_flr_act_set(self, 1U, i);
+ }
+
+ reg_rx_flr_mcst_flr_msk_set(self, 0x00000000U);
+ reg_rx_flr_mcst_flr_set(self, 0x00010FFFU, 0U);
+
+ /* Vlan filters */
+ rpf_vlan_outer_etht_set(self, 0x88A8U);
+ rpf_vlan_inner_etht_set(self, 0x8100U);
+ rpf_vlan_prom_mode_en_set(self, 1);
+
+ /* Rx Interrupts */
+ rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
+
+ /* misc */
+ rpfl2broadcast_flr_act_set(self, 1U);
+ rpfl2broadcast_count_threshold_set(self, 0xFFFFU & (~0U / 256U));
+
+ rdm_rx_dca_en_set(self, 0U);
+ rdm_rx_dca_mode_set(self, 0U);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr)
+{
+ int err = 0;
+ unsigned int h = 0U;
+ unsigned int l = 0U;
+
+ if (!mac_addr) {
+ err = -EINVAL;
+ goto err_exit;
+ }
+ h = (mac_addr[0] << 8) | (mac_addr[1]);
+ l = (mac_addr[2] << 24) | (mac_addr[3] << 16) |
+ (mac_addr[4] << 8) | mac_addr[5];
+
+ rpfl2_uc_flr_en_set(self, 0U, HW_ATL_A0_MAC);
+ rpfl2unicast_dest_addresslsw_set(self, l, HW_ATL_A0_MAC);
+ rpfl2unicast_dest_addressmsw_set(self, h, HW_ATL_A0_MAC);
+ rpfl2_uc_flr_en_set(self, 1U, HW_ATL_A0_MAC);
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_init(struct aq_hw_s *self,
+ struct aq_nic_cfg_s *aq_nic_cfg,
+ u8 *mac_addr)
+{
+ static u32 aq_hw_atl_igcr_table_[4][2] = {
+ { 0x20000000U, 0x20000000U }, /* AQ_IRQ_INVALID */
+ { 0x20000080U, 0x20000080U }, /* AQ_IRQ_LEGACY */
+ { 0x20000021U, 0x20000025U }, /* AQ_IRQ_MSI */
+ { 0x20000022U, 0x20000026U } /* AQ_IRQ_MSIX */
+ };
+
+ int err = 0;
+
+ self->aq_nic_cfg = aq_nic_cfg;
+
+ hw_atl_utils_hw_chip_features_init(self,
+ &PHAL_ATLANTIC_A0->chip_features);
+
+ hw_atl_a0_hw_init_tx_path(self);
+ hw_atl_a0_hw_init_rx_path(self);
+
+ hw_atl_a0_hw_mac_addr_set(self, mac_addr);
+
+ hw_atl_utils_mpi_set(self, MPI_INIT, aq_nic_cfg->link_speed_msk);
+
+ reg_tx_dma_debug_ctl_set(self, 0x800000b8U);
+ reg_tx_dma_debug_ctl_set(self, 0x000000b8U);
+
+ hw_atl_a0_hw_qos_set(self);
+ hw_atl_a0_hw_rss_set(self, &aq_nic_cfg->aq_rss);
+ hw_atl_a0_hw_rss_hash_set(self, &aq_nic_cfg->aq_rss);
+
+ err = aq_hw_err_from_flags(self);
+ if (err < 0)
+ goto err_exit;
+
+ /* Interrupts */
+ reg_irq_glb_ctl_set(self,
+ aq_hw_atl_igcr_table_[aq_nic_cfg->irq_type]
+ [(aq_nic_cfg->vecs > 1U) ?
+ 1 : 0]);
+
+ itr_irq_auto_masklsw_set(self, aq_nic_cfg->aq_hw_caps->irq_mask);
+
+ /* Interrupts */
+ reg_gen_irq_map_set(self,
+ ((HW_ATL_A0_ERR_INT << 0x18) | (1U << 0x1F)) |
+ ((HW_ATL_A0_ERR_INT << 0x10) | (1U << 0x17)) |
+ ((HW_ATL_A0_ERR_INT << 8) | (1U << 0xF)) |
+ ((HW_ATL_A0_ERR_INT) | (1U << 0x7)), 0U);
+
+ hw_atl_a0_hw_offload_set(self, aq_nic_cfg);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_ring_tx_start(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ tdm_tx_desc_en_set(self, 1, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_ring_rx_start(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ rdm_rx_desc_en_set(self, 1, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_start(struct aq_hw_s *self)
+{
+ tpb_tx_buff_en_set(self, 1);
+ rpb_rx_buff_en_set(self, 1);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_tx_ring_tail_update(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ reg_tx_dma_desc_tail_ptr_set(self, ring->sw_tail, ring->idx);
+ return 0;
+}
+
+static int hw_atl_a0_hw_ring_tx_xmit(struct aq_hw_s *self,
+ struct aq_ring_s *ring,
+ unsigned int frags)
+{
+ struct aq_ring_buff_s *buff = NULL;
+ struct hw_atl_txd_s *txd = NULL;
+ unsigned int buff_pa_len = 0U;
+ unsigned int pkt_len = 0U;
+ unsigned int frag_count = 0U;
+ bool is_gso = false;
+
+ buff = &ring->buff_ring[ring->sw_tail];
+ pkt_len = (buff->is_eop && buff->is_sop) ? buff->len : buff->len_pkt;
+
+ for (frag_count = 0; frag_count < frags; frag_count++) {
+ txd = (struct hw_atl_txd_s *)&ring->dx_ring[ring->sw_tail *
+ HW_ATL_A0_TXD_SIZE];
+ txd->ctl = 0;
+ txd->ctl2 = 0;
+ txd->buf_addr = 0;
+
+ buff = &ring->buff_ring[ring->sw_tail];
+
+ if (buff->is_txc) {
+ txd->ctl |= (buff->len_l3 << 31) |
+ (buff->len_l2 << 24) |
+ HW_ATL_A0_TXD_CTL_CMD_TCP |
+ HW_ATL_A0_TXD_CTL_DESC_TYPE_TXC;
+ txd->ctl2 |= (buff->mss << 16) |
+ (buff->len_l4 << 8) |
+ (buff->len_l3 >> 1);
+
+ pkt_len -= (buff->len_l4 +
+ buff->len_l3 +
+ buff->len_l2);
+ is_gso = true;
+ } else {
+ buff_pa_len = buff->len;
+
+ txd->buf_addr = buff->pa;
+ txd->ctl |= (HW_ATL_A0_TXD_CTL_BLEN &
+ ((u32)buff_pa_len << 4));
+ txd->ctl |= HW_ATL_A0_TXD_CTL_DESC_TYPE_TXD;
+ /* PAY_LEN */
+ txd->ctl2 |= HW_ATL_A0_TXD_CTL2_LEN & (pkt_len << 14);
+
+ if (is_gso) {
+ txd->ctl |= HW_ATL_A0_TXD_CTL_CMD_LSO;
+ txd->ctl2 |= HW_ATL_A0_TXD_CTL2_CTX_EN;
+ }
+
+ /* Tx checksum offloads */
+ if (buff->is_ip_cso)
+ txd->ctl |= HW_ATL_A0_TXD_CTL_CMD_IPCSO;
+
+ if (buff->is_udp_cso || buff->is_tcp_cso)
+ txd->ctl |= HW_ATL_A0_TXD_CTL_CMD_TUCSO;
+
+ if (unlikely(buff->is_eop)) {
+ txd->ctl |= HW_ATL_A0_TXD_CTL_EOP;
+ txd->ctl |= HW_ATL_A0_TXD_CTL_CMD_WB;
+ }
+ }
+
+ ring->sw_tail = aq_ring_next_dx(ring, ring->sw_tail);
+ }
+
+ hw_atl_a0_hw_tx_ring_tail_update(self, ring);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_ring_rx_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring,
+ struct aq_ring_param_s *aq_ring_param)
+{
+ u32 dma_desc_addr_lsw = (u32)aq_ring->dx_ring_pa;
+ u32 dma_desc_addr_msw = (u32)(((u64)aq_ring->dx_ring_pa) >> 32);
+
+ rdm_rx_desc_en_set(self, false, aq_ring->idx);
+
+ rdm_rx_desc_head_splitting_set(self, 0U, aq_ring->idx);
+
+ reg_rx_dma_desc_base_addresslswset(self, dma_desc_addr_lsw,
+ aq_ring->idx);
+
+ reg_rx_dma_desc_base_addressmswset(self,
+ dma_desc_addr_msw, aq_ring->idx);
+
+ rdm_rx_desc_len_set(self, aq_ring->size / 8U, aq_ring->idx);
+
+ rdm_rx_desc_data_buff_size_set(self,
+ AQ_CFG_RX_FRAME_MAX / 1024U,
+ aq_ring->idx);
+
+ rdm_rx_desc_head_buff_size_set(self, 0U, aq_ring->idx);
+ rdm_rx_desc_head_splitting_set(self, 0U, aq_ring->idx);
+ rpo_rx_desc_vlan_stripping_set(self, 0U, aq_ring->idx);
+
+ /* Rx ring set mode */
+
+ /* Mapping interrupt vector */
+ itr_irq_map_rx_set(self, aq_ring_param->vec_idx, aq_ring->idx);
+ itr_irq_map_en_rx_set(self, true, aq_ring->idx);
+
+ rdm_cpu_id_set(self, aq_ring_param->cpu, aq_ring->idx);
+ rdm_rx_desc_dca_en_set(self, 0U, aq_ring->idx);
+ rdm_rx_head_dca_en_set(self, 0U, aq_ring->idx);
+ rdm_rx_pld_dca_en_set(self, 0U, aq_ring->idx);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_ring_tx_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring,
+ struct aq_ring_param_s *aq_ring_param)
+{
+ u32 dma_desc_lsw_addr = (u32)aq_ring->dx_ring_pa;
+ u32 dma_desc_msw_addr = (u32)(((u64)aq_ring->dx_ring_pa) >> 32);
+
+ reg_tx_dma_desc_base_addresslswset(self, dma_desc_lsw_addr,
+ aq_ring->idx);
+
+ reg_tx_dma_desc_base_addressmswset(self, dma_desc_msw_addr,
+ aq_ring->idx);
+
+ tdm_tx_desc_len_set(self, aq_ring->size / 8U, aq_ring->idx);
+
+ hw_atl_a0_hw_tx_ring_tail_update(self, aq_ring);
+
+ /* Set Tx threshold */
+ tdm_tx_desc_wr_wb_threshold_set(self, 0U, aq_ring->idx);
+
+ /* Mapping interrupt vector */
+ itr_irq_map_tx_set(self, aq_ring_param->vec_idx, aq_ring->idx);
+ itr_irq_map_en_tx_set(self, true, aq_ring->idx);
+
+ tdm_cpu_id_set(self, aq_ring_param->cpu, aq_ring->idx);
+ tdm_tx_desc_dca_en_set(self, 0U, aq_ring->idx);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_ring_rx_fill(struct aq_hw_s *self,
+ struct aq_ring_s *ring,
+ unsigned int sw_tail_old)
+{
+ for (; sw_tail_old != ring->sw_tail;
+ sw_tail_old = aq_ring_next_dx(ring, sw_tail_old)) {
+ struct hw_atl_rxd_s *rxd =
+ (struct hw_atl_rxd_s *)&ring->dx_ring[sw_tail_old *
+ HW_ATL_A0_RXD_SIZE];
+
+ struct aq_ring_buff_s *buff = &ring->buff_ring[sw_tail_old];
+
+ rxd->buf_addr = buff->pa;
+ rxd->hdr_addr = 0U;
+ }
+
+ reg_rx_dma_desc_tail_ptr_set(self, sw_tail_old, ring->idx);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_ring_tx_head_update(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ int err = 0;
+ unsigned int hw_head_ = tdm_tx_desc_head_ptr_get(self, ring->idx);
+
+ if (AQ_OBJ_TST(self, AQ_HW_FLAG_ERR_UNPLUG)) {
+ err = -ENXIO;
+ goto err_exit;
+ }
+ ring->hw_head = hw_head_;
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_ring_rx_receive(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ struct device *ndev = aq_nic_get_dev(ring->aq_nic);
+
+ for (; ring->hw_head != ring->sw_tail;
+ ring->hw_head = aq_ring_next_dx(ring, ring->hw_head)) {
+ struct aq_ring_buff_s *buff = NULL;
+ struct hw_atl_rxd_wb_s *rxd_wb = (struct hw_atl_rxd_wb_s *)
+ &ring->dx_ring[ring->hw_head * HW_ATL_A0_RXD_SIZE];
+
+ unsigned int is_err = 1U;
+ unsigned int is_rx_check_sum_enabled = 0U;
+ unsigned int pkt_type = 0U;
+
+ if (!(rxd_wb->status & 0x5U)) { /* RxD is not done */
+ if ((1U << 4) &
+ reg_rx_dma_desc_status_get(self, ring->idx)) {
+ rdm_rx_desc_en_set(self, false, ring->idx);
+ rdm_rx_desc_res_set(self, true, ring->idx);
+ rdm_rx_desc_res_set(self, false, ring->idx);
+ rdm_rx_desc_en_set(self, true, ring->idx);
+ }
+
+ if (ring->hw_head ||
+ (rdm_rx_desc_head_ptr_get(self, ring->idx) < 2U)) {
+ break;
+ } else if (!(rxd_wb->status & 0x1U)) {
+ struct hw_atl_rxd_wb_s *rxd_wb1 =
+ (struct hw_atl_rxd_wb_s *)
+ (&ring->dx_ring[(1U) *
+ HW_ATL_A0_RXD_SIZE]);
+
+ if ((rxd_wb1->status & 0x1U)) {
+ rxd_wb->pkt_len = 1514U;
+ rxd_wb->status = 3U;
+ } else {
+ break;
+ }
+ }
+ }
+
+ buff = &ring->buff_ring[ring->hw_head];
+
+ if (0x3U != (rxd_wb->status & 0x3U))
+ rxd_wb->status |= 4;
+
+ is_err = (0x0000001CU & rxd_wb->status);
+ is_rx_check_sum_enabled = (rxd_wb->type) & (0x3U << 19);
+ pkt_type = 0xFFU & (rxd_wb->type >> 4);
+
+ if (is_rx_check_sum_enabled) {
+ if (0x0U == (pkt_type & 0x3U))
+ buff->is_ip_cso = (is_err & 0x08U) ? 0 : 1;
+
+ if (0x4U == (pkt_type & 0x1CU))
+ buff->is_udp_cso = (is_err & 0x10U) ? 0 : 1;
+ else if (0x0U == (pkt_type & 0x1CU))
+ buff->is_tcp_cso = (is_err & 0x10U) ? 0 : 1;
+ }
+
+ is_err &= ~0x18U;
+ is_err &= ~0x04U;
+
+ dma_unmap_page(ndev, buff->pa, buff->len, DMA_FROM_DEVICE);
+
+ if (is_err || rxd_wb->type & 0x1000U) {
+ /* status error or DMA error */
+ buff->is_error = 1U;
+ } else {
+ if (self->aq_nic_cfg->is_rss) {
+ /* last 4 byte */
+ u16 rss_type = rxd_wb->type & 0xFU;
+
+ if (rss_type && rss_type < 0x8U) {
+ buff->is_hash_l4 = (rss_type == 0x4 ||
+ rss_type == 0x5);
+ buff->rss_hash = rxd_wb->rss_hash;
+ }
+ }
+
+ if (HW_ATL_A0_RXD_WB_STAT2_EOP & rxd_wb->status) {
+ buff->len = (rxd_wb->pkt_len &
+ (AQ_CFG_RX_FRAME_MAX - 1U));
+ buff->len = buff->len ?
+ buff->len : AQ_CFG_RX_FRAME_MAX;
+ buff->next = 0U;
+ buff->is_eop = 1U;
+ } else {
+ /* jumbo */
+ buff->next = aq_ring_next_dx(ring,
+ ring->hw_head);
+ ++ring->stats.rx.jumbo_packets;
+ }
+ }
+ }
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_irq_enable(struct aq_hw_s *self, u64 mask)
+{
+ itr_irq_msk_setlsw_set(self, LODWORD(mask) |
+ (1U << HW_ATL_A0_ERR_INT));
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_irq_disable(struct aq_hw_s *self, u64 mask)
+{
+ itr_irq_msk_clearlsw_set(self, LODWORD(mask));
+ itr_irq_status_clearlsw_set(self, LODWORD(mask));
+
+ if ((1U << 16) & reg_gen_irq_status_get(self))
+
+ atomic_inc(&PHAL_ATLANTIC_A0->dpc);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_irq_read(struct aq_hw_s *self, u64 *mask)
+{
+ *mask = itr_irq_statuslsw_get(self);
+ return aq_hw_err_from_flags(self);
+}
+
+#define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U)
+
+static int hw_atl_a0_hw_packet_filter_set(struct aq_hw_s *self,
+ unsigned int packet_filter)
+{
+ unsigned int i = 0U;
+
+ rpfl2promiscuous_mode_en_set(self, IS_FILTER_ENABLED(IFF_PROMISC));
+ rpfl2multicast_flr_en_set(self, IS_FILTER_ENABLED(IFF_MULTICAST), 0);
+ rpfl2broadcast_en_set(self, IS_FILTER_ENABLED(IFF_BROADCAST));
+
+ self->aq_nic_cfg->is_mc_list_enabled =
+ IS_FILTER_ENABLED(IFF_MULTICAST);
+
+ for (i = HW_ATL_A0_MAC_MIN; i < HW_ATL_A0_MAC_MAX; ++i)
+ rpfl2_uc_flr_en_set(self,
+ (self->aq_nic_cfg->is_mc_list_enabled &&
+ (i <= self->aq_nic_cfg->mc_list_count)) ?
+ 1U : 0U, i);
+
+ return aq_hw_err_from_flags(self);
+}
+
+#undef IS_FILTER_ENABLED
+
+static int hw_atl_a0_hw_multicast_list_set(struct aq_hw_s *self,
+ u8 ar_mac
+ [AQ_CFG_MULTICAST_ADDRESS_MAX]
+ [ETH_ALEN],
+ u32 count)
+{
+ int err = 0;
+
+ if (count > (HW_ATL_A0_MAC_MAX - HW_ATL_A0_MAC_MIN)) {
+ err = EBADRQC;
+ goto err_exit;
+ }
+ for (self->aq_nic_cfg->mc_list_count = 0U;
+ self->aq_nic_cfg->mc_list_count < count;
+ ++self->aq_nic_cfg->mc_list_count) {
+ u32 i = self->aq_nic_cfg->mc_list_count;
+ u32 h = (ar_mac[i][0] << 8) | (ar_mac[i][1]);
+ u32 l = (ar_mac[i][2] << 24) | (ar_mac[i][3] << 16) |
+ (ar_mac[i][4] << 8) | ar_mac[i][5];
+
+ rpfl2_uc_flr_en_set(self, 0U, HW_ATL_A0_MAC_MIN + i);
+
+ rpfl2unicast_dest_addresslsw_set(self,
+ l, HW_ATL_A0_MAC_MIN + i);
+
+ rpfl2unicast_dest_addressmsw_set(self,
+ h, HW_ATL_A0_MAC_MIN + i);
+
+ rpfl2_uc_flr_en_set(self,
+ (self->aq_nic_cfg->is_mc_list_enabled),
+ HW_ATL_A0_MAC_MIN + i);
+ }
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_a0_hw_interrupt_moderation_set(struct aq_hw_s *self,
+ bool itr_enabled)
+{
+ unsigned int i = 0U;
+
+ if (itr_enabled && self->aq_nic_cfg->itr) {
+ if (self->aq_nic_cfg->itr != 0xFFFFU) {
+ u32 itr_ = (self->aq_nic_cfg->itr >> 1);
+
+ itr_ = min(AQ_CFG_IRQ_MASK, itr_);
+
+ PHAL_ATLANTIC_A0->itr_rx = 0x80000000U |
+ (itr_ << 0x10);
+ } else {
+ u32 n = 0xFFFFU & aq_hw_read_reg(self, 0x00002A00U);
+
+ if (n < (do_div(self->aq_link_status.bps, 1000000U))) {
+ PHAL_ATLANTIC_A0->itr_rx = 0U;
+ } else {
+ static unsigned int hw_timers_tbl_[] = {
+ 0x01CU, /* 10Gbit */
+ 0x039U, /* 5Gbit */
+ 0x039U, /* 5Gbit 5GS */
+ 0x073U, /* 2.5Gbit */
+ 0x120U, /* 1Gbit */
+ 0x1FFU, /* 100Mbit */
+ };
+
+ unsigned int speed_index =
+ hw_atl_utils_bps_2_speed_index(
+ self->aq_link_status.bps);
+
+ PHAL_ATLANTIC_A0->itr_rx =
+ 0x80000000U |
+ (hw_timers_tbl_[speed_index] << 0x10U);
+ }
+
+ aq_hw_write_reg(self, 0x00002A00U, 0x40000000U);
+ aq_hw_write_reg(self, 0x00002A00U, 0x8D000000U);
+ }
+ } else {
+ PHAL_ATLANTIC_A0->itr_rx = 0U;
+ }
+
+ for (i = HW_ATL_A0_RINGS_MAX; i--;)
+ reg_irq_thr_set(self, PHAL_ATLANTIC_A0->itr_rx, i);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_stop(struct aq_hw_s *self)
+{
+ hw_atl_a0_hw_irq_disable(self, HW_ATL_A0_INT_MASK);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_ring_tx_stop(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ tdm_tx_desc_en_set(self, 0U, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_ring_rx_stop(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ rdm_rx_desc_en_set(self, 0U, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_a0_hw_set_speed(struct aq_hw_s *self, u32 speed)
+{
+ int err = 0;
+
+ err = hw_atl_utils_mpi_set_speed(self, speed, MPI_INIT);
+ if (err < 0)
+ goto err_exit;
+
+err_exit:
+ return err;
+}
+
+static struct aq_hw_ops hw_atl_ops_ = {
+ .create = hw_atl_a0_create,
+ .destroy = hw_atl_a0_destroy,
+ .get_hw_caps = hw_atl_a0_get_hw_caps,
+
+ .hw_get_mac_permanent = hw_atl_utils_get_mac_permanent,
+ .hw_set_mac_address = hw_atl_a0_hw_mac_addr_set,
+ .hw_get_link_status = hw_atl_utils_mpi_get_link_state,
+ .hw_set_link_speed = hw_atl_a0_hw_set_speed,
+ .hw_init = hw_atl_a0_hw_init,
+ .hw_deinit = hw_atl_utils_hw_deinit,
+ .hw_set_power = hw_atl_utils_hw_set_power,
+ .hw_reset = hw_atl_a0_hw_reset,
+ .hw_start = hw_atl_a0_hw_start,
+ .hw_ring_tx_start = hw_atl_a0_hw_ring_tx_start,
+ .hw_ring_tx_stop = hw_atl_a0_hw_ring_tx_stop,
+ .hw_ring_rx_start = hw_atl_a0_hw_ring_rx_start,
+ .hw_ring_rx_stop = hw_atl_a0_hw_ring_rx_stop,
+ .hw_stop = hw_atl_a0_hw_stop,
+
+ .hw_ring_tx_xmit = hw_atl_a0_hw_ring_tx_xmit,
+ .hw_ring_tx_head_update = hw_atl_a0_hw_ring_tx_head_update,
+
+ .hw_ring_rx_receive = hw_atl_a0_hw_ring_rx_receive,
+ .hw_ring_rx_fill = hw_atl_a0_hw_ring_rx_fill,
+
+ .hw_irq_enable = hw_atl_a0_hw_irq_enable,
+ .hw_irq_disable = hw_atl_a0_hw_irq_disable,
+ .hw_irq_read = hw_atl_a0_hw_irq_read,
+
+ .hw_ring_rx_init = hw_atl_a0_hw_ring_rx_init,
+ .hw_ring_tx_init = hw_atl_a0_hw_ring_tx_init,
+ .hw_packet_filter_set = hw_atl_a0_hw_packet_filter_set,
+ .hw_multicast_list_set = hw_atl_a0_hw_multicast_list_set,
+ .hw_interrupt_moderation_set = hw_atl_a0_hw_interrupt_moderation_set,
+ .hw_rss_set = hw_atl_a0_hw_rss_set,
+ .hw_rss_hash_set = hw_atl_a0_hw_rss_hash_set,
+ .hw_get_regs = hw_atl_utils_hw_get_regs,
+ .hw_get_hw_stats = hw_atl_utils_get_hw_stats,
+ .hw_get_fw_version = hw_atl_utils_get_fw_version,
+};
+
+struct aq_hw_ops *hw_atl_a0_get_ops_by_id(struct pci_dev *pdev)
+{
+ bool is_vid_ok = (pdev->vendor == PCI_VENDOR_ID_AQUANTIA);
+ bool is_did_ok = ((pdev->device == HW_ATL_DEVICE_ID_0001) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D100) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D107) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D108) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D109));
+
+ bool is_rev_ok = (pdev->revision == 1U);
+
+ return (is_vid_ok && is_did_ok && is_rev_ok) ? &hw_atl_ops_ : NULL;
+}
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.h b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.h
new file mode 100644
index 0000000..6e1d527
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0.h
@@ -0,0 +1,34 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_a0.h: Declaration of abstract interface for Atlantic hardware
+ * specific functions.
+ */
+
+#ifndef HW_ATL_A0_H
+#define HW_ATL_A0_H
+
+#include "../aq_common.h"
+
+#ifndef PCI_VENDOR_ID_AQUANTIA
+
+#define PCI_VENDOR_ID_AQUANTIA 0x1D6A
+#define HW_ATL_DEVICE_ID_0001 0x0001
+#define HW_ATL_DEVICE_ID_D100 0xD100
+#define HW_ATL_DEVICE_ID_D107 0xD107
+#define HW_ATL_DEVICE_ID_D108 0xD108
+#define HW_ATL_DEVICE_ID_D109 0xD109
+
+#define HW_ATL_NIC_NAME "aQuantia AQtion 5Gbit Network Adapter"
+
+#endif
+
+struct aq_hw_ops *hw_atl_a0_get_ops_by_id(struct pci_dev *pdev);
+
+#endif /* HW_ATL_A0_H */
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0_internal.h b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0_internal.h
new file mode 100644
index 0000000..760e627
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_a0_internal.h
@@ -0,0 +1,152 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_a0_internal.h: Definition of Atlantic A0 chip specific
+ * constants.
+ */
+
+#ifndef HW_ATL_A0_INTERNAL_H
+#define HW_ATL_A0_INTERNAL_H
+
+#include "../aq_common.h"
+
+#define HW_ATL_A0_MTU_JUMBO 9014U
+
+#define HW_ATL_A0_TX_RINGS 4U
+#define HW_ATL_A0_RX_RINGS 4U
+
+#define HW_ATL_A0_RINGS_MAX 32U
+#define HW_ATL_A0_TXD_SIZE 16U
+#define HW_ATL_A0_RXD_SIZE 16U
+
+#define HW_ATL_A0_MAC 0U
+#define HW_ATL_A0_MAC_MIN 1U
+#define HW_ATL_A0_MAC_MAX 33U
+
+/* interrupts */
+#define HW_ATL_A0_ERR_INT 8U
+#define HW_ATL_A0_INT_MASK 0xFFFFFFFFU
+
+#define HW_ATL_A0_TXD_CTL2_LEN 0xFFFFC000U
+#define HW_ATL_A0_TXD_CTL2_CTX_EN 0x00002000U
+#define HW_ATL_A0_TXD_CTL2_CTX_IDX 0x00001000U
+
+#define HW_ATL_A0_TXD_CTL_DESC_TYPE_TXD 0x00000001U
+#define HW_ATL_A0_TXD_CTL_DESC_TYPE_TXC 0x00000002U
+#define HW_ATL_A0_TXD_CTL_BLEN 0x000FFFF0U
+#define HW_ATL_A0_TXD_CTL_DD 0x00100000U
+#define HW_ATL_A0_TXD_CTL_EOP 0x00200000U
+
+#define HW_ATL_A0_TXD_CTL_CMD_X 0x3FC00000U
+
+#define HW_ATL_A0_TXD_CTL_CMD_VLAN BIT(22)
+#define HW_ATL_A0_TXD_CTL_CMD_FCS BIT(23)
+#define HW_ATL_A0_TXD_CTL_CMD_IPCSO BIT(24)
+#define HW_ATL_A0_TXD_CTL_CMD_TUCSO BIT(25)
+#define HW_ATL_A0_TXD_CTL_CMD_LSO BIT(26)
+#define HW_ATL_A0_TXD_CTL_CMD_WB BIT(27)
+#define HW_ATL_A0_TXD_CTL_CMD_VXLAN BIT(28)
+
+#define HW_ATL_A0_TXD_CTL_CMD_IPV6 BIT(21)
+#define HW_ATL_A0_TXD_CTL_CMD_TCP BIT(22)
+
+#define HW_ATL_A0_MPI_CONTROL_ADR 0x0368U
+#define HW_ATL_A0_MPI_STATE_ADR 0x036CU
+
+#define HW_ATL_A0_MPI_SPEED_MSK 0xFFFFU
+#define HW_ATL_A0_MPI_SPEED_SHIFT 16U
+
+#define HW_ATL_A0_RATE_10G BIT(0)
+#define HW_ATL_A0_RATE_5G BIT(1)
+#define HW_ATL_A0_RATE_2G5 BIT(3)
+#define HW_ATL_A0_RATE_1G BIT(4)
+#define HW_ATL_A0_RATE_100M BIT(5)
+
+#define HW_ATL_A0_TXBUF_MAX 160U
+#define HW_ATL_A0_RXBUF_MAX 320U
+
+#define HW_ATL_A0_RSS_REDIRECTION_MAX 64U
+#define HW_ATL_A0_RSS_REDIRECTION_BITS 3U
+
+#define HW_ATL_A0_TC_MAX 1U
+#define HW_ATL_A0_RSS_MAX 8U
+
+#define HW_ATL_A0_FW_SEMA_RAM 0x2U
+
+#define HW_ATL_A0_RXD_DD 0x1U
+#define HW_ATL_A0_RXD_NCEA0 0x1U
+
+#define HW_ATL_A0_RXD_WB_STAT2_EOP 0x0002U
+
+#define HW_ATL_A0_UCP_0X370_REG 0x370U
+
+/* Hardware tx descriptor */
+struct __packed hw_atl_txd_s {
+ u64 buf_addr;
+ u32 ctl;
+ u32 ctl2; /* 63..46 - payload length, 45 - ctx enable, 44 - ctx index */
+};
+
+/* Hardware tx context descriptor */
+struct __packed hw_atl_txc_s {
+ u32 rsvd;
+ u32 len;
+ u32 ctl;
+ u32 len2;
+};
+
+/* Hardware rx descriptor */
+struct __packed hw_atl_rxd_s {
+ u64 buf_addr;
+ u64 hdr_addr;
+};
+
+/* Hardware rx descriptor writeback */
+struct __packed hw_atl_rxd_wb_s {
+ u32 type;
+ u32 rss_hash;
+ u16 status;
+ u16 pkt_len;
+ u16 next_desc_ptr;
+ u16 vlan;
+};
+
+/* HW layer capabilities */
+static struct aq_hw_caps_s hw_atl_a0_hw_caps_ = {
+ .ports = 1U,
+ .is_64_dma = true,
+ .msix_irqs = 4U,
+ .irq_mask = ~0U,
+ .vecs = HW_ATL_A0_RSS_MAX,
+ .tcs = HW_ATL_A0_TC_MAX,
+ .rxd_alignment = 1U,
+ .rxd_size = HW_ATL_A0_RXD_SIZE,
+ .rxds = 248U,
+ .txd_alignment = 1U,
+ .txd_size = HW_ATL_A0_TXD_SIZE,
+ .txds = 8U * 1024U,
+ .txhwb_alignment = 4096U,
+ .tx_rings = HW_ATL_A0_TX_RINGS,
+ .rx_rings = HW_ATL_A0_RX_RINGS,
+ .hw_features = NETIF_F_HW_CSUM |
+ NETIF_F_RXHASH |
+ NETIF_F_SG |
+ NETIF_F_TSO,
+ .hw_priv_flags = IFF_UNICAST_FLT,
+ .link_speed_msk = (HW_ATL_A0_RATE_10G |
+ HW_ATL_A0_RATE_5G |
+ HW_ATL_A0_RATE_2G5 |
+ HW_ATL_A0_RATE_1G |
+ HW_ATL_A0_RATE_100M),
+ .flow_control = true,
+ .mtu = HW_ATL_A0_MTU_JUMBO,
+ .mac_regs_count = 88,
+};
+
+#endif /* HW_ATL_A0_INTERNAL_H */
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.c
new file mode 100644
index 0000000..c4fa1cf
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.c
@@ -0,0 +1,960 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_b0.c: Definition of Atlantic hardware specific functions. */
+
+#include "../aq_hw.h"
+#include "../aq_hw_utils.h"
+#include "../aq_ring.h"
+#include "hw_atl_b0.h"
+#include "hw_atl_utils.h"
+#include "hw_atl_llh.h"
+#include "hw_atl_b0_internal.h"
+
+static int hw_atl_b0_get_hw_caps(struct aq_hw_s *self,
+ struct aq_hw_caps_s *aq_hw_caps)
+{
+ memcpy(aq_hw_caps, &hw_atl_b0_hw_caps_, sizeof(*aq_hw_caps));
+ return 0;
+}
+
+static struct aq_hw_s *hw_atl_b0_create(struct aq_pci_func_s *aq_pci_func,
+ unsigned int port,
+ struct aq_hw_ops *ops)
+{
+ struct hw_atl_s *self = NULL;
+ int err = 0;
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ err = -ENOMEM;
+ goto err_exit;
+ }
+ self->base.aq_pci_func = aq_pci_func;
+
+ self->base.not_ff_addr = 0x10U;
+
+err_exit:
+ return (struct aq_hw_s *)self;
+}
+
+static void hw_atl_b0_destroy(struct aq_hw_s *self)
+{
+ kfree(self);
+}
+
+static int hw_atl_b0_hw_reset(struct aq_hw_s *self)
+{
+ int err = 0;
+
+ glb_glb_reg_res_dis_set(self, 1U);
+ pci_pci_reg_res_dis_set(self, 0U);
+ rx_rx_reg_res_dis_set(self, 0U);
+ tx_tx_reg_res_dis_set(self, 0U);
+
+ HW_ATL_FLUSH();
+ glb_soft_res_set(self, 1);
+
+ /* check 10 times by 1ms */
+ AQ_HW_WAIT_FOR(glb_soft_res_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+
+ itr_irq_reg_res_dis_set(self, 0U);
+ itr_res_irq_set(self, 1U);
+
+ /* check 10 times by 1ms */
+ AQ_HW_WAIT_FOR(itr_res_irq_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+
+ hw_atl_utils_mpi_set(self, MPI_RESET, 0x0U);
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_qos_set(struct aq_hw_s *self)
+{
+ u32 tc = 0U;
+ u32 buff_size = 0U;
+ unsigned int i_priority = 0U;
+ bool is_rx_flow_control = false;
+
+ /* TPS Descriptor rate init */
+ tps_tx_pkt_shed_desc_rate_curr_time_res_set(self, 0x0U);
+ tps_tx_pkt_shed_desc_rate_lim_set(self, 0xA);
+
+ /* TPS VM init */
+ tps_tx_pkt_shed_desc_vm_arb_mode_set(self, 0U);
+
+ /* TPS TC credits init */
+ tps_tx_pkt_shed_desc_tc_arb_mode_set(self, 0U);
+ tps_tx_pkt_shed_data_arb_mode_set(self, 0U);
+
+ tps_tx_pkt_shed_tc_data_max_credit_set(self, 0xFFF, 0U);
+ tps_tx_pkt_shed_tc_data_weight_set(self, 0x64, 0U);
+ tps_tx_pkt_shed_desc_tc_max_credit_set(self, 0x50, 0U);
+ tps_tx_pkt_shed_desc_tc_weight_set(self, 0x1E, 0U);
+
+ /* Tx buf size */
+ buff_size = HW_ATL_B0_TXBUF_MAX;
+
+ tpb_tx_pkt_buff_size_per_tc_set(self, buff_size, tc);
+ tpb_tx_buff_hi_threshold_per_tc_set(self,
+ (buff_size * (1024 / 32U) * 66U) /
+ 100U, tc);
+ tpb_tx_buff_lo_threshold_per_tc_set(self,
+ (buff_size * (1024 / 32U) * 50U) /
+ 100U, tc);
+
+ /* QoS Rx buf size per TC */
+ tc = 0;
+ is_rx_flow_control = (AQ_NIC_FC_RX & self->aq_nic_cfg->flow_control);
+ buff_size = HW_ATL_B0_RXBUF_MAX;
+
+ rpb_rx_pkt_buff_size_per_tc_set(self, buff_size, tc);
+ rpb_rx_buff_hi_threshold_per_tc_set(self,
+ (buff_size *
+ (1024U / 32U) * 66U) /
+ 100U, tc);
+ rpb_rx_buff_lo_threshold_per_tc_set(self,
+ (buff_size *
+ (1024U / 32U) * 50U) /
+ 100U, tc);
+ rpb_rx_xoff_en_per_tc_set(self, is_rx_flow_control ? 1U : 0U, tc);
+
+ /* QoS 802.1p priority -> TC mapping */
+ for (i_priority = 8U; i_priority--;)
+ rpf_rpb_user_priority_tc_map_set(self, i_priority, 0U);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_rss_hash_set(struct aq_hw_s *self,
+ struct aq_rss_parameters *rss_params)
+{
+ struct aq_nic_cfg_s *cfg = NULL;
+ int err = 0;
+ unsigned int i = 0U;
+ unsigned int addr = 0U;
+
+ cfg = self->aq_nic_cfg;
+
+ for (i = 10, addr = 0U; i--; ++addr) {
+ u32 key_data = cfg->is_rss ?
+ __swab32(rss_params->hash_secret_key[i]) : 0U;
+ rpf_rss_key_wr_data_set(self, key_data);
+ rpf_rss_key_addr_set(self, addr);
+ rpf_rss_key_wr_en_set(self, 1U);
+ AQ_HW_WAIT_FOR(rpf_rss_key_wr_en_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+ }
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_rss_set(struct aq_hw_s *self,
+ struct aq_rss_parameters *rss_params)
+{
+ u8 *indirection_table = rss_params->indirection_table;
+ u32 i = 0U;
+ u32 num_rss_queues = max(1U, self->aq_nic_cfg->num_rss_queues);
+ int err = 0;
+ u16 bitary[(HW_ATL_B0_RSS_REDIRECTION_MAX *
+ HW_ATL_B0_RSS_REDIRECTION_BITS / 16U)];
+
+ memset(bitary, 0, sizeof(bitary));
+
+ for (i = HW_ATL_B0_RSS_REDIRECTION_MAX; i--;) {
+ (*(u32 *)(bitary + ((i * 3U) / 16U))) |=
+ ((indirection_table[i] % num_rss_queues) <<
+ ((i * 3U) & 0xFU));
+ }
+
+ for (i = AQ_DIMOF(bitary); i--;) {
+ rpf_rss_redir_tbl_wr_data_set(self, bitary[i]);
+ rpf_rss_redir_tbl_addr_set(self, i);
+ rpf_rss_redir_wr_en_set(self, 1U);
+ AQ_HW_WAIT_FOR(rpf_rss_redir_wr_en_get(self) == 0, 1000U, 10U);
+ if (err < 0)
+ goto err_exit;
+ }
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_offload_set(struct aq_hw_s *self,
+ struct aq_nic_cfg_s *aq_nic_cfg)
+{
+ int err = 0;
+ unsigned int i;
+
+ /* TX checksums offloads*/
+ tpo_ipv4header_crc_offload_en_set(self, 1);
+ tpo_tcp_udp_crc_offload_en_set(self, 1);
+ if (err < 0)
+ goto err_exit;
+
+ /* RX checksums offloads*/
+ rpo_ipv4header_crc_offload_en_set(self, 1);
+ rpo_tcp_udp_crc_offload_en_set(self, 1);
+ if (err < 0)
+ goto err_exit;
+
+ /* LSO offloads*/
+ tdm_large_send_offload_en_set(self, 0xFFFFFFFFU);
+ if (err < 0)
+ goto err_exit;
+
+/* LRO offloads */
+ {
+ unsigned int val = (8U < HW_ATL_B0_LRO_RXD_MAX) ? 0x3U :
+ ((4U < HW_ATL_B0_LRO_RXD_MAX) ? 0x2U :
+ ((2U < HW_ATL_B0_LRO_RXD_MAX) ? 0x1U : 0x0));
+
+ for (i = 0; i < HW_ATL_B0_RINGS_MAX; i++)
+ rpo_lro_max_num_of_descriptors_set(self, val, i);
+
+ rpo_lro_time_base_divider_set(self, 0x61AU);
+ rpo_lro_inactive_interval_set(self, 0);
+ rpo_lro_max_coalescing_interval_set(self, 2);
+
+ rpo_lro_qsessions_lim_set(self, 1U);
+
+ rpo_lro_total_desc_lim_set(self, 2U);
+
+ rpo_lro_patch_optimization_en_set(self, 0U);
+
+ rpo_lro_min_pay_of_first_pkt_set(self, 10U);
+
+ rpo_lro_pkt_lim_set(self, 1U);
+
+ rpo_lro_en_set(self, aq_nic_cfg->is_lro ? 0xFFFFFFFFU : 0U);
+ }
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_init_tx_path(struct aq_hw_s *self)
+{
+ thm_lso_tcp_flag_of_first_pkt_set(self, 0x0FF6U);
+ thm_lso_tcp_flag_of_middle_pkt_set(self, 0x0FF6U);
+ thm_lso_tcp_flag_of_last_pkt_set(self, 0x0F7FU);
+
+ /* Tx interrupts */
+ tdm_tx_desc_wr_wb_irq_en_set(self, 1U);
+
+ /* misc */
+ aq_hw_write_reg(self, 0x00007040U, IS_CHIP_FEATURE(TPO2) ?
+ 0x00010000U : 0x00000000U);
+ tdm_tx_dca_en_set(self, 0U);
+ tdm_tx_dca_mode_set(self, 0U);
+
+ tpb_tx_path_scp_ins_en_set(self, 1U);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_init_rx_path(struct aq_hw_s *self)
+{
+ struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
+ int i;
+
+ /* Rx TC/RSS number config */
+ rpb_rpf_rx_traf_class_mode_set(self, 1U);
+
+ /* Rx flow control */
+ rpb_rx_flow_ctl_mode_set(self, 1U);
+
+ /* RSS Ring selection */
+ reg_rx_flr_rss_control1set(self, cfg->is_rss ?
+ 0xB3333333U : 0x00000000U);
+
+ /* Multicast filters */
+ for (i = HW_ATL_B0_MAC_MAX; i--;) {
+ rpfl2_uc_flr_en_set(self, (i == 0U) ? 1U : 0U, i);
+ rpfl2unicast_flr_act_set(self, 1U, i);
+ }
+
+ reg_rx_flr_mcst_flr_msk_set(self, 0x00000000U);
+ reg_rx_flr_mcst_flr_set(self, 0x00010FFFU, 0U);
+
+ /* Vlan filters */
+ rpf_vlan_outer_etht_set(self, 0x88A8U);
+ rpf_vlan_inner_etht_set(self, 0x8100U);
+
+ if (cfg->vlan_id) {
+ rpf_vlan_flr_act_set(self, 1U, 0U);
+ rpf_vlan_id_flr_set(self, 0U, 0U);
+ rpf_vlan_flr_en_set(self, 0U, 0U);
+
+ rpf_vlan_accept_untagged_packets_set(self, 1U);
+ rpf_vlan_untagged_act_set(self, 1U);
+
+ rpf_vlan_flr_act_set(self, 1U, 1U);
+ rpf_vlan_id_flr_set(self, cfg->vlan_id, 0U);
+ rpf_vlan_flr_en_set(self, 1U, 1U);
+ } else {
+ rpf_vlan_prom_mode_en_set(self, 1);
+ }
+
+ /* Rx Interrupts */
+ rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
+
+ /* misc */
+ aq_hw_write_reg(self, 0x00005040U,
+ IS_CHIP_FEATURE(RPF2) ? 0x000F0000U : 0x00000000U);
+
+ rpfl2broadcast_flr_act_set(self, 1U);
+ rpfl2broadcast_count_threshold_set(self, 0xFFFFU & (~0U / 256U));
+
+ rdm_rx_dca_en_set(self, 0U);
+ rdm_rx_dca_mode_set(self, 0U);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr)
+{
+ int err = 0;
+ unsigned int h = 0U;
+ unsigned int l = 0U;
+
+ if (!mac_addr) {
+ err = -EINVAL;
+ goto err_exit;
+ }
+ h = (mac_addr[0] << 8) | (mac_addr[1]);
+ l = (mac_addr[2] << 24) | (mac_addr[3] << 16) |
+ (mac_addr[4] << 8) | mac_addr[5];
+
+ rpfl2_uc_flr_en_set(self, 0U, HW_ATL_B0_MAC);
+ rpfl2unicast_dest_addresslsw_set(self, l, HW_ATL_B0_MAC);
+ rpfl2unicast_dest_addressmsw_set(self, h, HW_ATL_B0_MAC);
+ rpfl2_uc_flr_en_set(self, 1U, HW_ATL_B0_MAC);
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_init(struct aq_hw_s *self,
+ struct aq_nic_cfg_s *aq_nic_cfg,
+ u8 *mac_addr)
+{
+ static u32 aq_hw_atl_igcr_table_[4][2] = {
+ { 0x20000000U, 0x20000000U }, /* AQ_IRQ_INVALID */
+ { 0x20000080U, 0x20000080U }, /* AQ_IRQ_LEGACY */
+ { 0x20000021U, 0x20000025U }, /* AQ_IRQ_MSI */
+ { 0x20000022U, 0x20000026U } /* AQ_IRQ_MSIX */
+ };
+
+ int err = 0;
+
+ self->aq_nic_cfg = aq_nic_cfg;
+
+ hw_atl_utils_hw_chip_features_init(self,
+ &PHAL_ATLANTIC_B0->chip_features);
+
+ hw_atl_b0_hw_init_tx_path(self);
+ hw_atl_b0_hw_init_rx_path(self);
+
+ hw_atl_b0_hw_mac_addr_set(self, mac_addr);
+
+ hw_atl_utils_mpi_set(self, MPI_INIT, aq_nic_cfg->link_speed_msk);
+
+ hw_atl_b0_hw_qos_set(self);
+ hw_atl_b0_hw_rss_set(self, &aq_nic_cfg->aq_rss);
+ hw_atl_b0_hw_rss_hash_set(self, &aq_nic_cfg->aq_rss);
+
+ err = aq_hw_err_from_flags(self);
+ if (err < 0)
+ goto err_exit;
+
+ /* Interrupts */
+ reg_irq_glb_ctl_set(self,
+ aq_hw_atl_igcr_table_[aq_nic_cfg->irq_type]
+ [(aq_nic_cfg->vecs > 1U) ?
+ 1 : 0]);
+
+ itr_irq_auto_masklsw_set(self, aq_nic_cfg->aq_hw_caps->irq_mask);
+
+ /* Interrupts */
+ reg_gen_irq_map_set(self,
+ ((HW_ATL_B0_ERR_INT << 0x18) | (1U << 0x1F)) |
+ ((HW_ATL_B0_ERR_INT << 0x10) | (1U << 0x17)), 0U);
+
+ hw_atl_b0_hw_offload_set(self, aq_nic_cfg);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_ring_tx_start(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ tdm_tx_desc_en_set(self, 1, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_ring_rx_start(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ rdm_rx_desc_en_set(self, 1, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_start(struct aq_hw_s *self)
+{
+ tpb_tx_buff_en_set(self, 1);
+ rpb_rx_buff_en_set(self, 1);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_tx_ring_tail_update(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ reg_tx_dma_desc_tail_ptr_set(self, ring->sw_tail, ring->idx);
+ return 0;
+}
+
+static int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self,
+ struct aq_ring_s *ring,
+ unsigned int frags)
+{
+ struct aq_ring_buff_s *buff = NULL;
+ struct hw_atl_txd_s *txd = NULL;
+ unsigned int buff_pa_len = 0U;
+ unsigned int pkt_len = 0U;
+ unsigned int frag_count = 0U;
+ bool is_gso = false;
+
+ buff = &ring->buff_ring[ring->sw_tail];
+ pkt_len = (buff->is_eop && buff->is_sop) ? buff->len : buff->len_pkt;
+
+ for (frag_count = 0; frag_count < frags; frag_count++) {
+ txd = (struct hw_atl_txd_s *)&ring->dx_ring[ring->sw_tail *
+ HW_ATL_B0_TXD_SIZE];
+ txd->ctl = 0;
+ txd->ctl2 = 0;
+ txd->buf_addr = 0;
+
+ buff = &ring->buff_ring[ring->sw_tail];
+
+ if (buff->is_txc) {
+ txd->ctl |= (buff->len_l3 << 31) |
+ (buff->len_l2 << 24) |
+ HW_ATL_B0_TXD_CTL_CMD_TCP |
+ HW_ATL_B0_TXD_CTL_DESC_TYPE_TXC;
+ txd->ctl2 |= (buff->mss << 16) |
+ (buff->len_l4 << 8) |
+ (buff->len_l3 >> 1);
+
+ pkt_len -= (buff->len_l4 +
+ buff->len_l3 +
+ buff->len_l2);
+ is_gso = true;
+ } else {
+ buff_pa_len = buff->len;
+
+ txd->buf_addr = buff->pa;
+ txd->ctl |= (HW_ATL_B0_TXD_CTL_BLEN &
+ ((u32)buff_pa_len << 4));
+ txd->ctl |= HW_ATL_B0_TXD_CTL_DESC_TYPE_TXD;
+ /* PAY_LEN */
+ txd->ctl2 |= HW_ATL_B0_TXD_CTL2_LEN & (pkt_len << 14);
+
+ if (is_gso) {
+ txd->ctl |= HW_ATL_B0_TXD_CTL_CMD_LSO;
+ txd->ctl2 |= HW_ATL_B0_TXD_CTL2_CTX_EN;
+ }
+
+ /* Tx checksum offloads */
+ if (buff->is_ip_cso)
+ txd->ctl |= HW_ATL_B0_TXD_CTL_CMD_IPCSO;
+
+ if (buff->is_udp_cso || buff->is_tcp_cso)
+ txd->ctl |= HW_ATL_B0_TXD_CTL_CMD_TUCSO;
+
+ if (unlikely(buff->is_eop)) {
+ txd->ctl |= HW_ATL_B0_TXD_CTL_EOP;
+ txd->ctl |= HW_ATL_B0_TXD_CTL_CMD_WB;
+ }
+ }
+
+ ring->sw_tail = aq_ring_next_dx(ring, ring->sw_tail);
+ }
+
+ hw_atl_b0_hw_tx_ring_tail_update(self, ring);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_ring_rx_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring,
+ struct aq_ring_param_s *aq_ring_param)
+{
+ u32 dma_desc_addr_lsw = (u32)aq_ring->dx_ring_pa;
+ u32 dma_desc_addr_msw = (u32)(((u64)aq_ring->dx_ring_pa) >> 32);
+
+ rdm_rx_desc_en_set(self, false, aq_ring->idx);
+
+ rdm_rx_desc_head_splitting_set(self, 0U, aq_ring->idx);
+
+ reg_rx_dma_desc_base_addresslswset(self, dma_desc_addr_lsw,
+ aq_ring->idx);
+
+ reg_rx_dma_desc_base_addressmswset(self,
+ dma_desc_addr_msw, aq_ring->idx);
+
+ rdm_rx_desc_len_set(self, aq_ring->size / 8U, aq_ring->idx);
+
+ rdm_rx_desc_data_buff_size_set(self,
+ AQ_CFG_RX_FRAME_MAX / 1024U,
+ aq_ring->idx);
+
+ rdm_rx_desc_head_buff_size_set(self, 0U, aq_ring->idx);
+ rdm_rx_desc_head_splitting_set(self, 0U, aq_ring->idx);
+ rpo_rx_desc_vlan_stripping_set(self, 0U, aq_ring->idx);
+
+ /* Rx ring set mode */
+
+ /* Mapping interrupt vector */
+ itr_irq_map_rx_set(self, aq_ring_param->vec_idx, aq_ring->idx);
+ itr_irq_map_en_rx_set(self, true, aq_ring->idx);
+
+ rdm_cpu_id_set(self, aq_ring_param->cpu, aq_ring->idx);
+ rdm_rx_desc_dca_en_set(self, 0U, aq_ring->idx);
+ rdm_rx_head_dca_en_set(self, 0U, aq_ring->idx);
+ rdm_rx_pld_dca_en_set(self, 0U, aq_ring->idx);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_ring_tx_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring,
+ struct aq_ring_param_s *aq_ring_param)
+{
+ u32 dma_desc_lsw_addr = (u32)aq_ring->dx_ring_pa;
+ u32 dma_desc_msw_addr = (u32)(((u64)aq_ring->dx_ring_pa) >> 32);
+
+ reg_tx_dma_desc_base_addresslswset(self, dma_desc_lsw_addr,
+ aq_ring->idx);
+
+ reg_tx_dma_desc_base_addressmswset(self, dma_desc_msw_addr,
+ aq_ring->idx);
+
+ tdm_tx_desc_len_set(self, aq_ring->size / 8U, aq_ring->idx);
+
+ hw_atl_b0_hw_tx_ring_tail_update(self, aq_ring);
+
+ /* Set Tx threshold */
+ tdm_tx_desc_wr_wb_threshold_set(self, 0U, aq_ring->idx);
+
+ /* Mapping interrupt vector */
+ itr_irq_map_tx_set(self, aq_ring_param->vec_idx, aq_ring->idx);
+ itr_irq_map_en_tx_set(self, true, aq_ring->idx);
+
+ tdm_cpu_id_set(self, aq_ring_param->cpu, aq_ring->idx);
+ tdm_tx_desc_dca_en_set(self, 0U, aq_ring->idx);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_ring_rx_fill(struct aq_hw_s *self,
+ struct aq_ring_s *ring,
+ unsigned int sw_tail_old)
+{
+ for (; sw_tail_old != ring->sw_tail;
+ sw_tail_old = aq_ring_next_dx(ring, sw_tail_old)) {
+ struct hw_atl_rxd_s *rxd =
+ (struct hw_atl_rxd_s *)&ring->dx_ring[sw_tail_old *
+ HW_ATL_B0_RXD_SIZE];
+
+ struct aq_ring_buff_s *buff = &ring->buff_ring[sw_tail_old];
+
+ rxd->buf_addr = buff->pa;
+ rxd->hdr_addr = 0U;
+ }
+
+ reg_rx_dma_desc_tail_ptr_set(self, sw_tail_old, ring->idx);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_ring_tx_head_update(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ int err = 0;
+ unsigned int hw_head_ = tdm_tx_desc_head_ptr_get(self, ring->idx);
+
+ if (AQ_OBJ_TST(self, AQ_HW_FLAG_ERR_UNPLUG)) {
+ err = -ENXIO;
+ goto err_exit;
+ }
+ ring->hw_head = hw_head_;
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ struct device *ndev = aq_nic_get_dev(ring->aq_nic);
+
+ for (; ring->hw_head != ring->sw_tail;
+ ring->hw_head = aq_ring_next_dx(ring, ring->hw_head)) {
+ struct aq_ring_buff_s *buff = NULL;
+ struct hw_atl_rxd_wb_s *rxd_wb = (struct hw_atl_rxd_wb_s *)
+ &ring->dx_ring[ring->hw_head * HW_ATL_B0_RXD_SIZE];
+
+ unsigned int is_err = 1U;
+ unsigned int is_rx_check_sum_enabled = 0U;
+ unsigned int pkt_type = 0U;
+
+ if (!(rxd_wb->status & 0x1U)) { /* RxD is not done */
+ break;
+ }
+
+ buff = &ring->buff_ring[ring->hw_head];
+
+ is_err = (0x0000003CU & rxd_wb->status);
+
+ is_rx_check_sum_enabled = (rxd_wb->type) & (0x3U << 19);
+ is_err &= ~0x20U; /* exclude validity bit */
+
+ pkt_type = 0xFFU & (rxd_wb->type >> 4);
+
+ if (is_rx_check_sum_enabled) {
+ if (0x0U == (pkt_type & 0x3U))
+ buff->is_ip_cso = (is_err & 0x08U) ? 0U : 1U;
+
+ if (0x4U == (pkt_type & 0x1CU))
+ buff->is_udp_cso = buff->is_cso_err ? 0U : 1U;
+ else if (0x0U == (pkt_type & 0x1CU))
+ buff->is_tcp_cso = buff->is_cso_err ? 0U : 1U;
+ }
+
+ is_err &= ~0x18U;
+
+ dma_unmap_page(ndev, buff->pa, buff->len, DMA_FROM_DEVICE);
+
+ if (is_err || rxd_wb->type & 0x1000U) {
+ /* status error or DMA error */
+ buff->is_error = 1U;
+ } else {
+ if (self->aq_nic_cfg->is_rss) {
+ /* last 4 byte */
+ u16 rss_type = rxd_wb->type & 0xFU;
+
+ if (rss_type && rss_type < 0x8U) {
+ buff->is_hash_l4 = (rss_type == 0x4 ||
+ rss_type == 0x5);
+ buff->rss_hash = rxd_wb->rss_hash;
+ }
+ }
+
+ if (HW_ATL_B0_RXD_WB_STAT2_EOP & rxd_wb->status) {
+ buff->len = (rxd_wb->pkt_len &
+ (AQ_CFG_RX_FRAME_MAX - 1U));
+ buff->len = buff->len ?
+ buff->len : AQ_CFG_RX_FRAME_MAX;
+ buff->next = 0U;
+ buff->is_eop = 1U;
+ } else {
+ if (HW_ATL_B0_RXD_WB_STAT2_RSCCNT &
+ rxd_wb->status) {
+ /* LRO */
+ buff->next = rxd_wb->next_desc_ptr;
+ ++ring->stats.rx.lro_packets;
+ } else {
+ /* jumbo */
+ buff->next =
+ aq_ring_next_dx(ring,
+ ring->hw_head);
+ ++ring->stats.rx.jumbo_packets;
+ }
+ }
+ }
+ }
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_irq_enable(struct aq_hw_s *self, u64 mask)
+{
+ itr_irq_msk_setlsw_set(self, LODWORD(mask));
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_irq_disable(struct aq_hw_s *self, u64 mask)
+{
+ itr_irq_msk_clearlsw_set(self, LODWORD(mask));
+ itr_irq_status_clearlsw_set(self, LODWORD(mask));
+
+ atomic_inc(&PHAL_ATLANTIC_B0->dpc);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_irq_read(struct aq_hw_s *self, u64 *mask)
+{
+ *mask = itr_irq_statuslsw_get(self);
+ return aq_hw_err_from_flags(self);
+}
+
+#define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U)
+
+static int hw_atl_b0_hw_packet_filter_set(struct aq_hw_s *self,
+ unsigned int packet_filter)
+{
+ unsigned int i = 0U;
+
+ rpfl2promiscuous_mode_en_set(self, IS_FILTER_ENABLED(IFF_PROMISC));
+ rpfl2multicast_flr_en_set(self,
+ IS_FILTER_ENABLED(IFF_MULTICAST), 0);
+
+ rpfl2_accept_all_mc_packets_set(self,
+ IS_FILTER_ENABLED(IFF_ALLMULTI));
+
+ rpfl2broadcast_en_set(self, IS_FILTER_ENABLED(IFF_BROADCAST));
+
+ self->aq_nic_cfg->is_mc_list_enabled = IS_FILTER_ENABLED(IFF_MULTICAST);
+
+ for (i = HW_ATL_B0_MAC_MIN; i < HW_ATL_B0_MAC_MAX; ++i)
+ rpfl2_uc_flr_en_set(self,
+ (self->aq_nic_cfg->is_mc_list_enabled &&
+ (i <= self->aq_nic_cfg->mc_list_count)) ?
+ 1U : 0U, i);
+
+ return aq_hw_err_from_flags(self);
+}
+
+#undef IS_FILTER_ENABLED
+
+static int hw_atl_b0_hw_multicast_list_set(struct aq_hw_s *self,
+ u8 ar_mac
+ [AQ_CFG_MULTICAST_ADDRESS_MAX]
+ [ETH_ALEN],
+ u32 count)
+{
+ int err = 0;
+
+ if (count > (HW_ATL_B0_MAC_MAX - HW_ATL_B0_MAC_MIN)) {
+ err = -EBADRQC;
+ goto err_exit;
+ }
+ for (self->aq_nic_cfg->mc_list_count = 0U;
+ self->aq_nic_cfg->mc_list_count < count;
+ ++self->aq_nic_cfg->mc_list_count) {
+ u32 i = self->aq_nic_cfg->mc_list_count;
+ u32 h = (ar_mac[i][0] << 8) | (ar_mac[i][1]);
+ u32 l = (ar_mac[i][2] << 24) | (ar_mac[i][3] << 16) |
+ (ar_mac[i][4] << 8) | ar_mac[i][5];
+
+ rpfl2_uc_flr_en_set(self, 0U, HW_ATL_B0_MAC_MIN + i);
+
+ rpfl2unicast_dest_addresslsw_set(self,
+ l, HW_ATL_B0_MAC_MIN + i);
+
+ rpfl2unicast_dest_addressmsw_set(self,
+ h, HW_ATL_B0_MAC_MIN + i);
+
+ rpfl2_uc_flr_en_set(self,
+ (self->aq_nic_cfg->is_mc_list_enabled),
+ HW_ATL_B0_MAC_MIN + i);
+ }
+
+ err = aq_hw_err_from_flags(self);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_b0_hw_interrupt_moderation_set(struct aq_hw_s *self,
+ bool itr_enabled)
+{
+ unsigned int i = 0U;
+
+ if (itr_enabled && self->aq_nic_cfg->itr) {
+ tdm_tx_desc_wr_wb_irq_en_set(self, 0U);
+ tdm_tdm_intr_moder_en_set(self, 1U);
+ rdm_rx_desc_wr_wb_irq_en_set(self, 0U);
+ rdm_rdm_intr_moder_en_set(self, 1U);
+
+ PHAL_ATLANTIC_B0->itr_tx = 2U;
+ PHAL_ATLANTIC_B0->itr_rx = 2U;
+
+ if (self->aq_nic_cfg->itr != 0xFFFFU) {
+ unsigned int max_timer = self->aq_nic_cfg->itr / 2U;
+ unsigned int min_timer = self->aq_nic_cfg->itr / 32U;
+
+ max_timer = min(0x1FFU, max_timer);
+ min_timer = min(0xFFU, min_timer);
+
+ PHAL_ATLANTIC_B0->itr_tx |= min_timer << 0x8U;
+ PHAL_ATLANTIC_B0->itr_tx |= max_timer << 0x10U;
+ PHAL_ATLANTIC_B0->itr_rx |= min_timer << 0x8U;
+ PHAL_ATLANTIC_B0->itr_rx |= max_timer << 0x10U;
+ } else {
+ static unsigned int hw_atl_b0_timers_table_tx_[][2] = {
+ {0xffU, 0xffU}, /* 10Gbit */
+ {0xffU, 0x1ffU}, /* 5Gbit */
+ {0xffU, 0x1ffU}, /* 5Gbit 5GS */
+ {0xffU, 0x1ffU}, /* 2.5Gbit */
+ {0xffU, 0x1ffU}, /* 1Gbit */
+ {0xffU, 0x1ffU}, /* 100Mbit */
+ };
+
+ static unsigned int hw_atl_b0_timers_table_rx_[][2] = {
+ {0x6U, 0x38U},/* 10Gbit */
+ {0xCU, 0x70U},/* 5Gbit */
+ {0xCU, 0x70U},/* 5Gbit 5GS */
+ {0x18U, 0xE0U},/* 2.5Gbit */
+ {0x30U, 0x80U},/* 1Gbit */
+ {0x4U, 0x50U},/* 100Mbit */
+ };
+
+ unsigned int speed_index =
+ hw_atl_utils_bps_2_speed_index(
+ self->aq_link_status.bps);
+
+ PHAL_ATLANTIC_B0->itr_tx |=
+ hw_atl_b0_timers_table_tx_[speed_index]
+ [0] << 0x8U; /* set min timer value */
+ PHAL_ATLANTIC_B0->itr_tx |=
+ hw_atl_b0_timers_table_tx_[speed_index]
+ [1] << 0x10U; /* set max timer value */
+
+ PHAL_ATLANTIC_B0->itr_rx |=
+ hw_atl_b0_timers_table_rx_[speed_index]
+ [0] << 0x8U; /* set min timer value */
+ PHAL_ATLANTIC_B0->itr_rx |=
+ hw_atl_b0_timers_table_rx_[speed_index]
+ [1] << 0x10U; /* set max timer value */
+ }
+ } else {
+ tdm_tx_desc_wr_wb_irq_en_set(self, 1U);
+ tdm_tdm_intr_moder_en_set(self, 0U);
+ rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
+ rdm_rdm_intr_moder_en_set(self, 0U);
+ PHAL_ATLANTIC_B0->itr_tx = 0U;
+ PHAL_ATLANTIC_B0->itr_rx = 0U;
+ }
+
+ for (i = HW_ATL_B0_RINGS_MAX; i--;) {
+ reg_tx_intr_moder_ctrl_set(self,
+ PHAL_ATLANTIC_B0->itr_tx, i);
+ reg_rx_intr_moder_ctrl_set(self,
+ PHAL_ATLANTIC_B0->itr_rx, i);
+ }
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_stop(struct aq_hw_s *self)
+{
+ hw_atl_b0_hw_irq_disable(self, HW_ATL_B0_INT_MASK);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_ring_tx_stop(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ tdm_tx_desc_en_set(self, 0U, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
+ struct aq_ring_s *ring)
+{
+ rdm_rx_desc_en_set(self, 0U, ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_set_speed(struct aq_hw_s *self, u32 speed)
+{
+ int err = 0;
+
+ err = hw_atl_utils_mpi_set_speed(self, speed, MPI_INIT);
+ if (err < 0)
+ goto err_exit;
+
+err_exit:
+ return err;
+}
+
+static struct aq_hw_ops hw_atl_ops_ = {
+ .create = hw_atl_b0_create,
+ .destroy = hw_atl_b0_destroy,
+ .get_hw_caps = hw_atl_b0_get_hw_caps,
+
+ .hw_get_mac_permanent = hw_atl_utils_get_mac_permanent,
+ .hw_set_mac_address = hw_atl_b0_hw_mac_addr_set,
+ .hw_get_link_status = hw_atl_utils_mpi_get_link_state,
+ .hw_set_link_speed = hw_atl_b0_hw_set_speed,
+ .hw_init = hw_atl_b0_hw_init,
+ .hw_deinit = hw_atl_utils_hw_deinit,
+ .hw_set_power = hw_atl_utils_hw_set_power,
+ .hw_reset = hw_atl_b0_hw_reset,
+ .hw_start = hw_atl_b0_hw_start,
+ .hw_ring_tx_start = hw_atl_b0_hw_ring_tx_start,
+ .hw_ring_tx_stop = hw_atl_b0_hw_ring_tx_stop,
+ .hw_ring_rx_start = hw_atl_b0_hw_ring_rx_start,
+ .hw_ring_rx_stop = hw_atl_b0_hw_ring_rx_stop,
+ .hw_stop = hw_atl_b0_hw_stop,
+
+ .hw_ring_tx_xmit = hw_atl_b0_hw_ring_tx_xmit,
+ .hw_ring_tx_head_update = hw_atl_b0_hw_ring_tx_head_update,
+
+ .hw_ring_rx_receive = hw_atl_b0_hw_ring_rx_receive,
+ .hw_ring_rx_fill = hw_atl_b0_hw_ring_rx_fill,
+
+ .hw_irq_enable = hw_atl_b0_hw_irq_enable,
+ .hw_irq_disable = hw_atl_b0_hw_irq_disable,
+ .hw_irq_read = hw_atl_b0_hw_irq_read,
+
+ .hw_ring_rx_init = hw_atl_b0_hw_ring_rx_init,
+ .hw_ring_tx_init = hw_atl_b0_hw_ring_tx_init,
+ .hw_packet_filter_set = hw_atl_b0_hw_packet_filter_set,
+ .hw_multicast_list_set = hw_atl_b0_hw_multicast_list_set,
+ .hw_interrupt_moderation_set = hw_atl_b0_hw_interrupt_moderation_set,
+ .hw_rss_set = hw_atl_b0_hw_rss_set,
+ .hw_rss_hash_set = hw_atl_b0_hw_rss_hash_set,
+ .hw_get_regs = hw_atl_utils_hw_get_regs,
+ .hw_get_hw_stats = hw_atl_utils_get_hw_stats,
+ .hw_get_fw_version = hw_atl_utils_get_fw_version,
+};
+
+struct aq_hw_ops *hw_atl_b0_get_ops_by_id(struct pci_dev *pdev)
+{
+ bool is_vid_ok = (pdev->vendor == PCI_VENDOR_ID_AQUANTIA);
+ bool is_did_ok = ((pdev->device == HW_ATL_DEVICE_ID_0001) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D100) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D107) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D108) ||
+ (pdev->device == HW_ATL_DEVICE_ID_D109));
+
+ bool is_rev_ok = (pdev->revision == 2U);
+
+ return (is_vid_ok && is_did_ok && is_rev_ok) ? &hw_atl_ops_ : NULL;
+}
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.h b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.h
new file mode 100644
index 0000000..a1e1bce6
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0.h
@@ -0,0 +1,34 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_b0.h: Declaration of abstract interface for Atlantic hardware
+ * specific functions.
+ */
+
+#ifndef HW_ATL_B0_H
+#define HW_ATL_B0_H
+
+#include "../aq_common.h"
+
+#ifndef PCI_VENDOR_ID_AQUANTIA
+
+#define PCI_VENDOR_ID_AQUANTIA 0x1D6A
+#define HW_ATL_DEVICE_ID_0001 0x0001
+#define HW_ATL_DEVICE_ID_D100 0xD100
+#define HW_ATL_DEVICE_ID_D107 0xD107
+#define HW_ATL_DEVICE_ID_D108 0xD108
+#define HW_ATL_DEVICE_ID_D109 0xD109
+
+#define HW_ATL_NIC_NAME "aQuantia AQtion 5Gbit Network Adapter"
+
+#endif
+
+struct aq_hw_ops *hw_atl_b0_get_ops_by_id(struct pci_dev *pdev);
+
+#endif /* HW_ATL_B0_H */
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0_internal.h b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0_internal.h
new file mode 100644
index 0000000..4c3506a
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_b0_internal.h
@@ -0,0 +1,205 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_b0_internal.h: Definition of Atlantic B0 chip specific
+ * constants.
+ */
+
+#ifndef HW_ATL_B0_INTERNAL_H
+#define HW_ATL_B0_INTERNAL_H
+
+#include "../aq_common.h"
+
+#define HW_ATL_B0_MTU_JUMBO (16000U)
+#define HW_ATL_B0_MTU 1514U
+
+#define HW_ATL_B0_TX_RINGS 4U
+#define HW_ATL_B0_RX_RINGS 4U
+
+#define HW_ATL_B0_RINGS_MAX 32U
+#define HW_ATL_B0_TXD_SIZE (16U)
+#define HW_ATL_B0_RXD_SIZE (16U)
+
+#define HW_ATL_B0_MAC 0U
+#define HW_ATL_B0_MAC_MIN 1U
+#define HW_ATL_B0_MAC_MAX 33U
+
+/* UCAST/MCAST filters */
+#define HW_ATL_B0_UCAST_FILTERS_MAX 38
+#define HW_ATL_B0_MCAST_FILTERS_MAX 8
+
+/* interrupts */
+#define HW_ATL_B0_ERR_INT 8U
+#define HW_ATL_B0_INT_MASK (0xFFFFFFFFU)
+
+#define HW_ATL_B0_TXD_CTL2_LEN (0xFFFFC000)
+#define HW_ATL_B0_TXD_CTL2_CTX_EN (0x00002000)
+#define HW_ATL_B0_TXD_CTL2_CTX_IDX (0x00001000)
+
+#define HW_ATL_B0_TXD_CTL_DESC_TYPE_TXD (0x00000001)
+#define HW_ATL_B0_TXD_CTL_DESC_TYPE_TXC (0x00000002)
+#define HW_ATL_B0_TXD_CTL_BLEN (0x000FFFF0)
+#define HW_ATL_B0_TXD_CTL_DD (0x00100000)
+#define HW_ATL_B0_TXD_CTL_EOP (0x00200000)
+
+#define HW_ATL_B0_TXD_CTL_CMD_X (0x3FC00000)
+
+#define HW_ATL_B0_TXD_CTL_CMD_VLAN BIT(22)
+#define HW_ATL_B0_TXD_CTL_CMD_FCS BIT(23)
+#define HW_ATL_B0_TXD_CTL_CMD_IPCSO BIT(24)
+#define HW_ATL_B0_TXD_CTL_CMD_TUCSO BIT(25)
+#define HW_ATL_B0_TXD_CTL_CMD_LSO BIT(26)
+#define HW_ATL_B0_TXD_CTL_CMD_WB BIT(27)
+#define HW_ATL_B0_TXD_CTL_CMD_VXLAN BIT(28)
+
+#define HW_ATL_B0_TXD_CTL_CMD_IPV6 BIT(21)
+#define HW_ATL_B0_TXD_CTL_CMD_TCP BIT(22)
+
+#define HW_ATL_B0_MPI_CONTROL_ADR 0x0368U
+#define HW_ATL_B0_MPI_STATE_ADR 0x036CU
+
+#define HW_ATL_B0_MPI_SPEED_MSK 0xFFFFU
+#define HW_ATL_B0_MPI_SPEED_SHIFT 16U
+
+#define HW_ATL_B0_RATE_10G BIT(0)
+#define HW_ATL_B0_RATE_5G BIT(1)
+#define HW_ATL_B0_RATE_2G5 BIT(3)
+#define HW_ATL_B0_RATE_1G BIT(4)
+#define HW_ATL_B0_RATE_100M BIT(5)
+
+#define HW_ATL_B0_TXBUF_MAX 160U
+#define HW_ATL_B0_RXBUF_MAX 320U
+
+#define HW_ATL_B0_RSS_REDIRECTION_MAX 64U
+#define HW_ATL_B0_RSS_REDIRECTION_BITS 3U
+#define HW_ATL_B0_RSS_HASHKEY_BITS 320U
+
+#define HW_ATL_B0_TCRSS_4_8 1
+#define HW_ATL_B0_TC_MAX 1U
+#define HW_ATL_B0_RSS_MAX 8U
+
+#define HW_ATL_B0_LRO_RXD_MAX 2U
+#define HW_ATL_B0_RS_SLIP_ENABLED 0U
+
+/* (256k -1(max pay_len) - 54(header)) */
+#define HAL_ATL_B0_LSO_MAX_SEGMENT_SIZE 262089U
+
+/* (256k -1(max pay_len) - 74(header)) */
+#define HAL_ATL_B0_LSO_IPV6_MAX_SEGMENT_SIZE 262069U
+
+#define HW_ATL_B0_CHIP_REVISION_B0 0xA0U
+#define HW_ATL_B0_CHIP_REVISION_UNKNOWN 0xFFU
+
+#define HW_ATL_B0_FW_VER_EXPECTED 0x01030006U
+#define HW_ATL_B0_FW_SEMA_RAM 0x2U
+
+#define HW_ATL_B0_TXC_LEN_TUNLEN (0x0000FF00)
+#define HW_ATL_B0_TXC_LEN_OUTLEN (0xFFFF0000)
+
+#define HW_ATL_B0_TXC_CTL_DESC_TYPE (0x00000007)
+#define HW_ATL_B0_TXC_CTL_CTX_ID (0x00000008)
+#define HW_ATL_B0_TXC_CTL_VLAN (0x000FFFF0)
+#define HW_ATL_B0_TXC_CTL_CMD (0x00F00000)
+#define HW_ATL_B0_TXC_CTL_L2LEN (0x7F000000)
+
+#define HW_ATL_B0_TXC_CTL_L3LEN (0x80000000) /* L3LEN lsb */
+#define HW_ATL_B0_TXC_LEN2_L3LEN (0x000000FF) /* L3LE upper bits */
+#define HW_ATL_B0_TXC_LEN2_L4LEN (0x0000FF00)
+#define HW_ATL_B0_TXC_LEN2_MSSLEN (0xFFFF0000)
+
+#define HW_ATL_B0_RXD_DD (0x1)
+#define HW_ATL_B0_RXD_NCEA0 (0x1)
+
+#define HW_ATL_B0_RXD_WB_STAT_RSSTYPE (0x0000000F)
+#define HW_ATL_B0_RXD_WB_STAT_PKTTYPE (0x00000FF0)
+#define HW_ATL_B0_RXD_WB_STAT_RXCTRL (0x00180000)
+#define HW_ATL_B0_RXD_WB_STAT_SPLHDR (0x00200000)
+#define HW_ATL_B0_RXD_WB_STAT_HDRLEN (0xFFC00000)
+
+#define HW_ATL_B0_RXD_WB_STAT2_DD (0x0001)
+#define HW_ATL_B0_RXD_WB_STAT2_EOP (0x0002)
+#define HW_ATL_B0_RXD_WB_STAT2_RXSTAT (0x003C)
+#define HW_ATL_B0_RXD_WB_STAT2_MACERR (0x0004)
+#define HW_ATL_B0_RXD_WB_STAT2_IP4ERR (0x0008)
+#define HW_ATL_B0_RXD_WB_STAT2_TCPUPDERR (0x0010)
+#define HW_ATL_B0_RXD_WB_STAT2_RXESTAT (0x0FC0)
+#define HW_ATL_B0_RXD_WB_STAT2_RSCCNT (0xF000)
+
+#define L2_FILTER_ACTION_DISCARD (0x0)
+#define L2_FILTER_ACTION_HOST (0x1)
+
+#define HW_ATL_B0_UCP_0X370_REG (0x370)
+
+#define HW_ATL_B0_FLUSH() AQ_HW_READ_REG(self, 0x10)
+
+/* Hardware tx descriptor */
+struct __packed hw_atl_txd_s {
+ u64 buf_addr;
+ u32 ctl;
+ u32 ctl2; /* 63..46 - payload length, 45 - ctx enable, 44 - ctx index */
+};
+
+/* Hardware tx context descriptor */
+struct __packed hw_atl_txc_s {
+ u32 rsvd;
+ u32 len;
+ u32 ctl;
+ u32 len2;
+};
+
+/* Hardware rx descriptor */
+struct __packed hw_atl_rxd_s {
+ u64 buf_addr;
+ u64 hdr_addr;
+};
+
+/* Hardware rx descriptor writeback */
+struct __packed hw_atl_rxd_wb_s {
+ u32 type;
+ u32 rss_hash;
+ u16 status;
+ u16 pkt_len;
+ u16 next_desc_ptr;
+ u16 vlan;
+};
+
+/* HW layer capabilities */
+static struct aq_hw_caps_s hw_atl_b0_hw_caps_ = {
+ .ports = 1U,
+ .is_64_dma = true,
+ .msix_irqs = 4U,
+ .irq_mask = ~0U,
+ .vecs = HW_ATL_B0_RSS_MAX,
+ .tcs = HW_ATL_B0_TC_MAX,
+ .rxd_alignment = 1U,
+ .rxd_size = HW_ATL_B0_RXD_SIZE,
+ .rxds = 8U * 1024U,
+ .txd_alignment = 1U,
+ .txd_size = HW_ATL_B0_TXD_SIZE,
+ .txds = 8U * 1024U,
+ .txhwb_alignment = 4096U,
+ .tx_rings = HW_ATL_B0_TX_RINGS,
+ .rx_rings = HW_ATL_B0_RX_RINGS,
+ .hw_features = NETIF_F_HW_CSUM |
+ NETIF_F_RXHASH |
+ NETIF_F_SG |
+ NETIF_F_TSO |
+ NETIF_F_LRO,
+ .hw_priv_flags = IFF_UNICAST_FLT,
+ .link_speed_msk = (HW_ATL_B0_RATE_10G |
+ HW_ATL_B0_RATE_5G |
+ HW_ATL_B0_RATE_2G5 |
+ HW_ATL_B0_RATE_1G |
+ HW_ATL_B0_RATE_100M),
+ .flow_control = true,
+ .mtu = HW_ATL_B0_MTU_JUMBO,
+ .mac_regs_count = 88,
+};
+
+#endif /* HW_ATL_B0_INTERNAL_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v5 07/13] net: ethernet: aquantia: Vector operations
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel . Belous, Dmitry Bezrukov
In-Reply-To: <e17f53f9485f69edafa966d2bcbaa4b9fd7c145f.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Add functions to manululate the vector of receive and transmit rings.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel.Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
drivers/net/ethernet/aquantia/aq_vec.c | 385 +++++++++++++++++++++++++++++++++
drivers/net/ethernet/aquantia/aq_vec.h | 42 ++++
2 files changed, 427 insertions(+)
create mode 100644 drivers/net/ethernet/aquantia/aq_vec.c
create mode 100644 drivers/net/ethernet/aquantia/aq_vec.h
diff --git a/drivers/net/ethernet/aquantia/aq_vec.c b/drivers/net/ethernet/aquantia/aq_vec.c
new file mode 100644
index 0000000..7974b7c0
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_vec.c
@@ -0,0 +1,385 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_vec.c: Definition of common structure for vector of Rx and Tx rings.
+ * Definition of functions for Rx and Tx rings. Friendly module for aq_nic.
+ */
+
+#include "aq_vec.h"
+#include "aq_nic.h"
+#include "aq_ring.h"
+#include "aq_hw.h"
+
+#include <linux/netdevice.h>
+
+struct aq_vec_s {
+ AQ_OBJ_HEADER;
+ struct aq_hw_ops *aq_hw_ops;
+ struct aq_hw_s *aq_hw;
+ struct aq_nic_s *aq_nic;
+ unsigned int tx_rings;
+ unsigned int rx_rings;
+ struct aq_ring_param_s aq_ring_param;
+ struct napi_struct napi;
+ struct aq_ring_s ring[AQ_CFG_TCS_MAX][2];
+};
+
+#define AQ_VEC_TX_ID 0
+#define AQ_VEC_RX_ID 1
+
+static int aq_vec_poll(struct napi_struct *napi, int budget)
+__releases(&self->lock)
+__acquires(&self->lock)
+{
+ struct aq_vec_s *self = container_of(napi, struct aq_vec_s, napi);
+ struct aq_ring_s *ring = NULL;
+ int work_done = 0;
+ int err = 0;
+ unsigned int i = 0U;
+ unsigned int sw_tail_old = 0U;
+ bool was_tx_cleaned = false;
+ bool is_locked = false;
+
+ if (!self)
+ return 0;
+
+ if (spin_trylock(&self->lock)) {
+ is_locked = true;
+
+ for (i = 0U, ring = self->ring[0];
+ self->tx_rings > i; ++i, ring = self->ring[i]) {
+ if (self->aq_hw_ops->hw_ring_tx_head_update) {
+ err = self->aq_hw_ops->hw_ring_tx_head_update(
+ self->aq_hw,
+ &ring[AQ_VEC_TX_ID]);
+ if (err < 0)
+ goto err_exit;
+ }
+
+ if (ring[AQ_VEC_TX_ID].sw_head !=
+ ring[AQ_VEC_TX_ID].hw_head) {
+ aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]);
+ was_tx_cleaned = true;
+ }
+
+ err = self->aq_hw_ops->hw_ring_rx_receive(self->aq_hw,
+ &ring[AQ_VEC_RX_ID]);
+ if (err < 0)
+ goto err_exit;
+
+ if (ring[AQ_VEC_RX_ID].sw_head !=
+ ring[AQ_VEC_RX_ID].hw_head) {
+ err = aq_ring_rx_clean(&ring[AQ_VEC_RX_ID],
+ &work_done,
+ budget - work_done);
+ if (err < 0)
+ goto err_exit;
+
+ sw_tail_old = ring[AQ_VEC_RX_ID].sw_tail;
+
+ err = aq_ring_rx_fill(&ring[AQ_VEC_RX_ID]);
+ if (err < 0)
+ goto err_exit;
+
+ err = self->aq_hw_ops->hw_ring_rx_fill(
+ self->aq_hw,
+ &ring[AQ_VEC_RX_ID], sw_tail_old);
+ if (err < 0)
+ goto err_exit;
+ }
+ }
+
+ if (was_tx_cleaned)
+ work_done = budget;
+
+ if (work_done < budget) {
+ napi_complete(napi);
+ self->aq_hw_ops->hw_irq_enable(self->aq_hw,
+ 1U << self->aq_ring_param.vec_idx);
+ }
+
+err_exit:
+ if (is_locked)
+ spin_unlock(&self->lock);
+ }
+
+ return work_done;
+}
+
+struct aq_vec_s *aq_vec_alloc(struct aq_nic_s *aq_nic, unsigned int idx,
+ struct aq_nic_cfg_s *aq_nic_cfg)
+{
+ struct aq_vec_s *self = NULL;
+ struct aq_ring_s *ring = NULL;
+ unsigned int i = 0U;
+ int err = 0;
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ err = -ENOMEM;
+ goto err_exit;
+ }
+
+ self->aq_nic = aq_nic;
+ self->aq_ring_param.vec_idx = idx;
+ self->aq_ring_param.cpu =
+ idx + aq_nic_cfg->aq_rss.base_cpu_number;
+
+ cpumask_set_cpu(self->aq_ring_param.cpu,
+ &self->aq_ring_param.affinity_mask);
+
+ self->tx_rings = 0;
+ self->rx_rings = 0;
+
+ netif_napi_add(aq_nic_get_ndev(aq_nic), &self->napi,
+ aq_vec_poll, AQ_CFG_NAPI_WEIGHT);
+
+ for (i = 0; i < aq_nic_cfg->tcs; ++i) {
+ unsigned int idx_ring = AQ_NIC_TCVEC2RING(self->nic,
+ self->tx_rings,
+ self->aq_ring_param.vec_idx);
+
+ ring = aq_ring_tx_alloc(&self->ring[i][AQ_VEC_TX_ID], aq_nic,
+ idx_ring, aq_nic_cfg);
+ if (!ring) {
+ err = -ENOMEM;
+ goto err_exit;
+ }
+
+ ++self->tx_rings;
+
+ aq_nic_set_tx_ring(aq_nic, idx_ring, ring);
+
+ ring = aq_ring_rx_alloc(&self->ring[i][AQ_VEC_RX_ID], aq_nic,
+ idx_ring, aq_nic_cfg);
+ if (!ring) {
+ err = -ENOMEM;
+ goto err_exit;
+ }
+
+ ++self->rx_rings;
+ }
+
+err_exit:
+ if (err < 0) {
+ aq_vec_free(self);
+ self = NULL;
+ }
+ return self;
+}
+
+int aq_vec_init(struct aq_vec_s *self, struct aq_hw_ops *aq_hw_ops,
+ struct aq_hw_s *aq_hw)
+{
+ struct aq_ring_s *ring = NULL;
+ unsigned int i = 0U;
+ int err = 0;
+
+ self->aq_hw_ops = aq_hw_ops;
+ self->aq_hw = aq_hw;
+
+ spin_lock_init(&self->lock);
+
+ for (i = 0U, ring = self->ring[0];
+ self->tx_rings > i; ++i, ring = self->ring[i]) {
+ aq_ring_init(&ring[AQ_VEC_TX_ID]);
+
+ err = self->aq_hw_ops->hw_ring_tx_init(self->aq_hw,
+ &ring[AQ_VEC_TX_ID],
+ &self->aq_ring_param);
+ if (err < 0)
+ goto err_exit;
+
+ aq_ring_init(&ring[AQ_VEC_RX_ID]);
+
+ err = self->aq_hw_ops->hw_ring_rx_init(self->aq_hw,
+ &ring[AQ_VEC_RX_ID],
+ &self->aq_ring_param);
+ if (err < 0)
+ goto err_exit;
+
+ err = aq_ring_rx_fill(&ring[AQ_VEC_RX_ID]);
+ if (err < 0)
+ goto err_exit;
+
+ err = self->aq_hw_ops->hw_ring_rx_fill(self->aq_hw,
+ &ring[AQ_VEC_RX_ID], 0U);
+ if (err < 0)
+ goto err_exit;
+ }
+
+err_exit:
+ return err;
+}
+
+int aq_vec_start(struct aq_vec_s *self)
+{
+ struct aq_ring_s *ring = NULL;
+ unsigned int i = 0U;
+ int err = 0;
+
+ for (i = 0U, ring = self->ring[0];
+ self->tx_rings > i; ++i, ring = self->ring[i]) {
+ err = self->aq_hw_ops->hw_ring_tx_start(self->aq_hw,
+ &ring[AQ_VEC_TX_ID]);
+ if (err < 0)
+ goto err_exit;
+
+ err = self->aq_hw_ops->hw_ring_rx_start(self->aq_hw,
+ &ring[AQ_VEC_RX_ID]);
+ if (err < 0)
+ goto err_exit;
+ }
+
+ napi_enable(&self->napi);
+
+err_exit:
+ return err;
+}
+
+int aq_vec_stop(struct aq_vec_s *self)
+{
+ struct aq_ring_s *ring = NULL;
+ unsigned int i = 0U;
+
+ for (i = 0U, ring = self->ring[0];
+ self->tx_rings > i; ++i, ring = self->ring[i]) {
+ self->aq_hw_ops->hw_ring_tx_stop(self->aq_hw,
+ &ring[AQ_VEC_TX_ID]);
+
+ self->aq_hw_ops->hw_ring_rx_stop(self->aq_hw,
+ &ring[AQ_VEC_RX_ID]);
+ }
+
+ napi_disable(&self->napi);
+ return 0;
+}
+
+int aq_vec_deinit(struct aq_vec_s *self)
+{
+ struct aq_ring_s *ring = NULL;
+ unsigned int i = 0U;
+
+ for (i = 0U, ring = self->ring[0];
+ self->tx_rings > i; ++i, ring = self->ring[i]) {
+ aq_ring_tx_drop(&ring[AQ_VEC_TX_ID]);
+ aq_ring_rx_drop(&ring[AQ_VEC_RX_ID]);
+ }
+ return 0;
+}
+
+void aq_vec_free(struct aq_vec_s *self)
+{
+ struct aq_ring_s *ring = NULL;
+ unsigned int i = 0U;
+
+ if (!self)
+ return;
+
+ for (i = 0U, ring = self->ring[0];
+ self->tx_rings > i; ++i, ring = self->ring[i]) {
+ aq_ring_free(&ring[AQ_VEC_TX_ID]);
+ aq_ring_free(&ring[AQ_VEC_RX_ID]);
+ }
+
+ netif_napi_del(&self->napi);
+
+ kfree(self);
+}
+
+irqreturn_t aq_vec_isr(int irq, void *private)
+{
+ struct aq_vec_s *self = (struct aq_vec_s *)private;
+ int err = 0;
+
+ if (!self) {
+ err = -EINVAL;
+ goto err_exit;
+ }
+ napi_schedule(&self->napi);
+
+err_exit:
+ return err >= 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+irqreturn_t aq_vec_isr_legacy(int irq, void *private)
+{
+ struct aq_vec_s *self = (struct aq_vec_s *)private;
+ u64 irq_mask = 0U;
+ irqreturn_t err = 0;
+
+ if (!self) {
+ err = -EINVAL;
+ goto err_exit;
+ }
+ err = self->aq_hw_ops->hw_irq_read(self->aq_hw, &irq_mask);
+
+ if (irq_mask) {
+ self->aq_hw_ops->hw_irq_disable(self->aq_hw,
+ 1U << self->aq_ring_param.vec_idx);
+ napi_schedule(&self->napi);
+ } else {
+ self->aq_hw_ops->hw_irq_enable(self->aq_hw, 1U);
+ err = IRQ_NONE;
+ }
+
+err_exit:
+ return err >= 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+cpumask_t *aq_vec_get_affinity_mask(struct aq_vec_s *self)
+{
+ return &self->aq_ring_param.affinity_mask;
+}
+
+void aq_vec_add_stats(struct aq_vec_s *self,
+ struct aq_ring_stats_rx_s *stats_rx,
+ struct aq_ring_stats_tx_s *stats_tx)
+{
+ struct aq_ring_s *ring = NULL;
+ unsigned int r = 0U;
+
+ for (r = 0U, ring = self->ring[0];
+ self->tx_rings > r; ++r, ring = self->ring[r]) {
+ struct aq_ring_stats_tx_s *tx = &ring[AQ_VEC_TX_ID].stats.tx;
+ struct aq_ring_stats_rx_s *rx = &ring[AQ_VEC_RX_ID].stats.rx;
+
+ stats_rx->packets += rx->packets;
+ stats_rx->bytes += rx->bytes;
+ stats_rx->errors += rx->errors;
+ stats_rx->jumbo_packets += rx->jumbo_packets;
+ stats_rx->lro_packets += rx->lro_packets;
+
+ stats_tx->packets += tx->packets;
+ stats_tx->bytes += tx->bytes;
+ stats_tx->errors += tx->errors;
+ }
+}
+
+int aq_vec_get_sw_stats(struct aq_vec_s *self, u64 *data, unsigned int *p_count)
+{
+ unsigned int count = 0U;
+ struct aq_ring_stats_rx_s stats_rx;
+ struct aq_ring_stats_tx_s stats_tx;
+
+ memset(&stats_rx, 0U, sizeof(struct aq_ring_stats_rx_s));
+ memset(&stats_tx, 0U, sizeof(struct aq_ring_stats_tx_s));
+ aq_vec_add_stats(self, &stats_rx, &stats_tx);
+
+ data[count] += stats_rx.packets;
+ data[++count] += stats_tx.packets;
+ data[++count] += stats_rx.jumbo_packets;
+ data[++count] += stats_rx.lro_packets;
+ data[++count] += stats_rx.errors;
+
+ if (p_count)
+ *p_count = ++count;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/aquantia/aq_vec.h b/drivers/net/ethernet/aquantia/aq_vec.h
new file mode 100644
index 0000000..e3a0ceb
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_vec.h
@@ -0,0 +1,42 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_vec.h: Definition of common structures for vector of Rx and Tx rings.
+ * Declaration of functions for Rx and Tx rings.
+ */
+
+#ifndef AQ_VEC_H
+#define AQ_VEC_H
+
+#include "aq_common.h"
+#include <linux/irqreturn.h>
+
+struct aq_hw_s;
+struct aq_hw_ops;
+struct aq_ring_stats_rx_s;
+struct aq_ring_stats_tx_s;
+
+irqreturn_t aq_vec_isr(int irq, void *private);
+irqreturn_t aq_vec_isr_legacy(int irq, void *private);
+struct aq_vec_s *aq_vec_alloc(struct aq_nic_s *aq_nic, unsigned int idx,
+ struct aq_nic_cfg_s *aq_nic_cfg);
+int aq_vec_init(struct aq_vec_s *self, struct aq_hw_ops *aq_hw_ops,
+ struct aq_hw_s *aq_hw);
+int aq_vec_deinit(struct aq_vec_s *self);
+void aq_vec_free(struct aq_vec_s *self);
+int aq_vec_start(struct aq_vec_s *self);
+int aq_vec_stop(struct aq_vec_s *self);
+cpumask_t *aq_vec_get_affinity_mask(struct aq_vec_s *self);
+int aq_vec_get_sw_stats(struct aq_vec_s *self, u64 *data,
+ unsigned int *p_count);
+void aq_vec_add_stats(struct aq_vec_s *self,
+ struct aq_ring_stats_rx_s *stats_rx,
+ struct aq_ring_stats_tx_s *stats_tx);
+
+#endif /* AQ_VEC_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v5 08/13] net: ethernet: aquantia: PCI operations
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel Belous, Dmitry Bezrukov
In-Reply-To: <e3f7eda0cc3bfa3426932c39465971ba4c6226ef.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Add functions that handle the PCI bus interface.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
drivers/net/ethernet/aquantia/aq_pci_func.c | 347 ++++++++++++++++++++++++++++
drivers/net/ethernet/aquantia/aq_pci_func.h | 34 +++
2 files changed, 381 insertions(+)
create mode 100644 drivers/net/ethernet/aquantia/aq_pci_func.c
create mode 100644 drivers/net/ethernet/aquantia/aq_pci_func.h
diff --git a/drivers/net/ethernet/aquantia/aq_pci_func.c b/drivers/net/ethernet/aquantia/aq_pci_func.c
new file mode 100644
index 0000000..0b9052c
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_pci_func.c
@@ -0,0 +1,347 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_pci_func.c: Definition of PCI functions. */
+
+#include "aq_pci_func.h"
+#include "aq_nic.h"
+#include "aq_vec.h"
+#include "aq_hw.h"
+#include <linux/interrupt.h>
+
+struct aq_pci_func_s {
+ struct pci_dev *pdev;
+ struct aq_nic_s *port[AQ_CFG_PCI_FUNC_PORTS];
+ void __iomem *mmio;
+ void *aq_vec[AQ_CFG_PCI_FUNC_MSIX_IRQS];
+ resource_size_t mmio_pa;
+ unsigned int msix_entry_mask;
+ unsigned int irq_type;
+ unsigned int ports;
+ bool is_pci_enabled;
+ bool is_regions;
+ bool is_pci_using_dac;
+ struct aq_hw_caps_s aq_hw_caps;
+ struct msix_entry msix_entry[AQ_CFG_PCI_FUNC_MSIX_IRQS];
+};
+
+struct aq_pci_func_s *aq_pci_func_alloc(struct aq_hw_ops *aq_hw_ops,
+ struct pci_dev *pdev,
+ const struct net_device_ops *ndev_ops,
+ const struct ethtool_ops *eth_ops)
+{
+ struct aq_pci_func_s *self = NULL;
+ int err = 0;
+ unsigned int port = 0U;
+
+ if (!aq_hw_ops) {
+ err = -EFAULT;
+ goto err_exit;
+ }
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ err = -ENOMEM;
+ goto err_exit;
+ }
+
+ pci_set_drvdata(pdev, self);
+ self->pdev = pdev;
+
+ err = aq_hw_ops->get_hw_caps(NULL, &self->aq_hw_caps);
+ if (err < 0)
+ goto err_exit;
+
+ self->ports = self->aq_hw_caps.ports;
+
+ for (port = 0; port < self->ports; ++port) {
+ struct aq_nic_s *aq_nic = aq_nic_alloc_cold(ndev_ops, eth_ops,
+ &pdev->dev, self,
+ port, aq_hw_ops);
+
+ if (!aq_nic) {
+ err = -ENOMEM;
+ goto err_exit;
+ }
+ self->port[port] = aq_nic;
+ }
+
+err_exit:
+ if (err < 0) {
+ if (self)
+ aq_pci_func_free(self);
+ self = NULL;
+ }
+
+ (void)err;
+ return self;
+}
+
+int aq_pci_func_init(struct aq_pci_func_s *self)
+{
+ int err = 0;
+ unsigned int bar = 0U;
+ unsigned int port = 0U;
+ unsigned int i = 0U;
+
+ err = pci_enable_device(self->pdev);
+ if (err < 0)
+ goto err_exit;
+
+ self->is_pci_enabled = true;
+
+ err = pci_set_dma_mask(self->pdev, DMA_BIT_MASK(64));
+ if (!err) {
+ err = pci_set_consistent_dma_mask(self->pdev, DMA_BIT_MASK(64));
+ self->is_pci_using_dac = 1;
+ }
+ if (err) {
+ err = pci_set_dma_mask(self->pdev, DMA_BIT_MASK(32));
+ if (!err)
+ err = pci_set_consistent_dma_mask(self->pdev,
+ DMA_BIT_MASK(32));
+ self->is_pci_using_dac = 0;
+ }
+ if (err != 0) {
+ err = -ENOSR;
+ goto err_exit;
+ }
+
+ err = pci_request_regions(self->pdev, AQ_CFG_DRV_NAME "_mmio");
+ if (err < 0)
+ goto err_exit;
+
+ self->is_regions = true;
+
+ pci_set_master(self->pdev);
+
+ for (bar = 0; bar < 4; ++bar) {
+ if (IORESOURCE_MEM & pci_resource_flags(self->pdev, bar)) {
+ resource_size_t reg_sz;
+
+ self->mmio_pa = pci_resource_start(self->pdev, bar);
+ if (self->mmio_pa == 0U) {
+ err = -EIO;
+ goto err_exit;
+ }
+
+ reg_sz = pci_resource_len(self->pdev, bar);
+ if ((reg_sz <= 24 /*ATL_REGS_SIZE*/)) {
+ err = -EIO;
+ goto err_exit;
+ }
+
+ self->mmio = ioremap_nocache(self->mmio_pa, reg_sz);
+ if (!self->mmio) {
+ err = -EIO;
+ goto err_exit;
+ }
+ break;
+ }
+ }
+
+ if (err < 0)
+ goto err_exit;
+ for (i = 0; i < self->aq_hw_caps.msix_irqs; i++)
+ self->msix_entry[i].entry = i;
+
+ /*enable interrupts */
+#if AQ_CFG_FORCE_LEGACY_INT
+ self->irq_type = AQ_IRQ_LEGACY;
+#else
+ err = pci_enable_msix(self->pdev, self->msix_entry,
+ self->aq_hw_caps.msix_irqs);
+
+ if (err >= 0) {
+ self->irq_type = AQ_IRQ_MSIX;
+ } else {
+ err = pci_enable_msi(self->pdev);
+
+ if (err >= 0) {
+ self->irq_type = AQ_IRQ_MSI;
+ } else {
+ self->irq_type = AQ_IRQ_LEGACY;
+ err = 0;
+ }
+ }
+#endif
+
+ /* net device init */
+ for (port = 0; port < self->ports; ++port) {
+ if (!self->port[port])
+ continue;
+
+ aq_nic_cfg_start(self->port[port]);
+ aq_nic_ndev_init(self->port[port]);
+
+ err = aq_nic_ndev_register(self->port[port]);
+ if (err < 0)
+ goto err_exit;
+ }
+
+err_exit:
+ if (err < 0)
+ aq_pci_func_deinit(self);
+ return err;
+}
+
+int aq_pci_func_alloc_irq(struct aq_pci_func_s *self, unsigned int i,
+ char *name, void *aq_vec, cpumask_t *affinity_mask)
+{
+ int err = 0;
+
+ switch (self->irq_type) {
+ case AQ_IRQ_MSIX:
+ err = request_irq(self->msix_entry[i].vector, aq_vec_isr, 0,
+ name, aq_vec);
+ break;
+
+ case AQ_IRQ_MSI:
+ err = request_irq(self->pdev->irq, aq_vec_isr, 0, name, aq_vec);
+ break;
+
+ case AQ_IRQ_LEGACY:
+ err = request_irq(self->pdev->irq, aq_vec_isr_legacy,
+ IRQF_SHARED, name, aq_vec);
+ break;
+
+ default:
+ err = -EFAULT;
+ break;
+ }
+
+ if (err >= 0) {
+ self->msix_entry_mask |= (1 << i);
+ self->aq_vec[i] = aq_vec;
+
+ if (self->irq_type == AQ_IRQ_MSIX)
+ irq_set_affinity_hint(self->msix_entry[i].vector,
+ affinity_mask);
+ }
+
+ return err;
+}
+
+void aq_pci_func_free_irqs(struct aq_pci_func_s *self)
+{
+ unsigned int i = 0U;
+
+ for (i = 32U; i--;) {
+ if (!((1U << i) & self->msix_entry_mask))
+ continue;
+
+ switch (self->irq_type) {
+ case AQ_IRQ_MSIX:
+ irq_set_affinity_hint(self->msix_entry[i].vector, NULL);
+ free_irq(self->msix_entry[i].vector, self->aq_vec[i]);
+ break;
+
+ case AQ_IRQ_MSI:
+ free_irq(self->pdev->irq, self->aq_vec[i]);
+ break;
+
+ case AQ_IRQ_LEGACY:
+ free_irq(self->pdev->irq, self->aq_vec[i]);
+ break;
+
+ default:
+ break;
+ }
+
+ self->msix_entry_mask &= ~(1U << i);
+ }
+}
+
+void __iomem *aq_pci_func_get_mmio(struct aq_pci_func_s *self)
+{
+ return self->mmio;
+}
+
+unsigned int aq_pci_func_get_irq_type(struct aq_pci_func_s *self)
+{
+ return self->irq_type;
+}
+
+int aq_pci_func_deinit(struct aq_pci_func_s *self)
+{
+ int err = 0;
+
+ if (!self) {
+ err = -EFAULT;
+ goto err_exit;
+ }
+ aq_pci_func_free_irqs(self);
+
+ switch (self->irq_type) {
+ case AQ_IRQ_MSI:
+ pci_disable_msi(self->pdev);
+ break;
+
+ case AQ_IRQ_MSIX:
+ pci_disable_msix(self->pdev);
+ break;
+
+ case AQ_IRQ_LEGACY:
+ break;
+
+ default:
+ break;
+ }
+
+ if (self->is_regions)
+ pci_release_regions(self->pdev);
+
+ if (self->is_pci_enabled)
+ pci_disable_device(self->pdev);
+
+err_exit:
+ return err;
+}
+
+void aq_pci_func_free(struct aq_pci_func_s *self)
+{
+ int err = 0;
+ unsigned int port = 0U;
+
+ if (!self) {
+ err = -EFAULT;
+ goto err_exit;
+ }
+ for (port = 0; port < self->ports; ++port) {
+ if (!self->port[port])
+ continue;
+
+ aq_nic_ndev_free(self->port[port]);
+ }
+
+ kfree(self);
+
+err_exit:
+ (void)err;
+}
+
+int aq_pci_func_change_pm_state(struct aq_pci_func_s *self,
+ pm_message_t *pm_msg)
+{
+ int err = 0;
+ unsigned int port = 0U;
+
+ if (!self) {
+ err = -EFAULT;
+ goto err_exit;
+ }
+ for (port = 0; port < self->ports; ++port) {
+ if (!self->port[port])
+ continue;
+
+ aq_nic_change_pm_state(self->port[port], pm_msg);
+ }
+
+err_exit:
+ return err;
+}
diff --git a/drivers/net/ethernet/aquantia/aq_pci_func.h b/drivers/net/ethernet/aquantia/aq_pci_func.h
new file mode 100644
index 0000000..f7479a3
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_pci_func.h
@@ -0,0 +1,34 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_pci_func.h: Declaration of PCI functions. */
+
+#ifndef AQ_PCI_FUNC_H
+#define AQ_PCI_FUNC_H
+
+#include "aq_common.h"
+
+struct aq_pci_func_s *aq_pci_func_alloc(struct aq_hw_ops *hw_ops,
+ struct pci_dev *pdev,
+ const struct net_device_ops *ndev_ops,
+ const struct ethtool_ops *eth_ops);
+int aq_pci_func_init(struct aq_pci_func_s *self);
+int aq_pci_func_alloc_irq(struct aq_pci_func_s *self, unsigned int i,
+ char *name, void *aq_vec,
+ cpumask_t *affinity_mask);
+void aq_pci_func_free_irqs(struct aq_pci_func_s *self);
+int aq_pci_func_start(struct aq_pci_func_s *self);
+void __iomem *aq_pci_func_get_mmio(struct aq_pci_func_s *self);
+unsigned int aq_pci_func_get_irq_type(struct aq_pci_func_s *self);
+int aq_pci_func_deinit(struct aq_pci_func_s *self);
+void aq_pci_func_free(struct aq_pci_func_s *self);
+int aq_pci_func_change_pm_state(struct aq_pci_func_s *self,
+ pm_message_t *pm_msg);
+
+#endif /* AQ_PCI_FUNC_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v5 09/13] net: ethernet: aquantia: Atlantic hardware abstraction layer
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel Belous, Dmitry Bezrukov
In-Reply-To: <2889e8e87b8b89945f34e57a7f8ca253f204de47.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Add common functions for Atlantic hardware abstraction layer.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
.../net/ethernet/aquantia/hw_atl/hw_atl_utils.c | 547 +++++++++++++++++++++
.../net/ethernet/aquantia/hw_atl/hw_atl_utils.h | 210 ++++++++
2 files changed, 757 insertions(+)
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.c
create mode 100644 drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.h
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.c b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.c
new file mode 100644
index 0000000..bb24d64
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.c
@@ -0,0 +1,547 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_utils.c: Definition of common functions for Atlantic hardware
+ * abstraction layer.
+ */
+
+#include "../aq_hw.h"
+#include "../aq_hw_utils.h"
+#include "../aq_pci_func.h"
+#include "../aq_ring.h"
+#include "../aq_vec.h"
+#include "hw_atl_utils.h"
+#include "hw_atl_llh.h"
+
+#include <linux/random.h>
+
+#define HW_ATL_UCP_0X370_REG 0x0370U
+
+#define HW_ATL_FW_SM_RAM 0x2U
+#define HW_ATL_MPI_CONTROL_ADR 0x0368U
+#define HW_ATL_MPI_STATE_ADR 0x036CU
+
+#define HW_ATL_MPI_STATE_MSK 0x00FFU
+#define HW_ATL_MPI_STATE_SHIFT 0U
+#define HW_ATL_MPI_SPEED_MSK 0xFFFFU
+#define HW_ATL_MPI_SPEED_SHIFT 16U
+
+static int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
+ u32 *p, u32 cnt)
+{
+ int err = 0;
+
+ AQ_HW_WAIT_FOR(reg_glb_cpu_sem_get(self,
+ HW_ATL_FW_SM_RAM) == 1U, 1U, 1000U);
+
+ if (err < 0) {
+ bool is_locked;
+
+ reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
+ is_locked = reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM);
+ if (!is_locked) {
+ err = -ETIME;
+ goto err_exit;
+ }
+ }
+
+ aq_hw_write_reg(self, 0x00000208U, a);
+
+ for (++cnt; --cnt;) {
+ u32 i = 0U;
+
+ aq_hw_write_reg(self, 0x00000200U, 0x00008000U);
+
+ for (i = 1024U;
+ (0x100U & aq_hw_read_reg(self, 0x00000200U)) && --i;) {
+ }
+
+ *(p++) = aq_hw_read_reg(self, 0x0000020CU);
+ }
+
+ reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
+
+err_exit:
+ return err;
+}
+
+static void hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p,
+ u32 cnt)
+{
+ int err = 0;
+ bool is_locked;
+
+ is_locked = reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM);
+ if (!is_locked) {
+ err = -ETIME;
+ goto err_exit;
+ }
+
+ aq_hw_write_reg(self, 0x00000208U, a);
+
+ for (++cnt; --cnt;) {
+ u32 i = 0U;
+
+ aq_hw_write_reg(self, 0x0000020CU, *(p++));
+ aq_hw_write_reg(self, 0x00000200U, 0xC000U);
+
+ for (i = 1024U;
+ (0x100U & aq_hw_read_reg(self, 0x00000200U)) && --i;) {
+ }
+ }
+
+ reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
+
+err_exit:;
+ (void)err;
+}
+
+static int hw_atl_utils_init_ucp(struct aq_hw_s *self)
+{
+ int err = 0;
+
+ if (!aq_hw_read_reg(self, 0x370U)) {
+ unsigned int rnd = 0U;
+ unsigned int ucp_0x370 = 0U;
+
+ get_random_bytes(&rnd, sizeof(unsigned int));
+
+ ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd);
+ aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
+ }
+
+ reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U);
+
+ /* check 10 times by 1ms */
+ AQ_HW_WAIT_FOR(0U != (PHAL_ATLANTIC_A0->mbox_addr =
+ aq_hw_read_reg(self, 0x360U)), 1000U, 10U);
+
+ return err;
+}
+
+#define HW_ATL_RPC_CONTROL_ADR 0x0338U
+#define HW_ATL_RPC_STATE_ADR 0x033CU
+
+struct aq_hw_atl_utils_fw_rpc_tid_s {
+ union {
+ u32 val;
+ struct {
+ u16 tid;
+ u16 len;
+ };
+ };
+};
+
+#define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL)
+
+static int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
+{
+ int err = 0;
+ struct aq_hw_atl_utils_fw_rpc_tid_s sw;
+
+ if (!IS_CHIP_FEATURE(MIPS)) {
+ err = -1;
+ goto err_exit;
+ }
+ hw_atl_utils_fw_upload_dwords(self, PHAL_ATLANTIC->rpc_addr,
+ (u32 *)(void *)&PHAL_ATLANTIC->rpc,
+ (rpc_size + sizeof(u32) -
+ sizeof(u8)) / sizeof(u32));
+
+ sw.tid = 0xFFFFU & (++PHAL_ATLANTIC->rpc_tid);
+ sw.len = (u16)rpc_size;
+ aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val);
+
+err_exit:
+ return err;
+}
+
+static int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
+ struct hw_aq_atl_utils_fw_rpc **rpc)
+{
+ int err = 0;
+ struct aq_hw_atl_utils_fw_rpc_tid_s sw;
+ struct aq_hw_atl_utils_fw_rpc_tid_s fw;
+
+ if (IS_CHIP_FEATURE(MIPS)) {
+ if (rpc) {
+ PHAL_ATLANTIC->rpc.msg_id = 0U;
+ *rpc = &PHAL_ATLANTIC->rpc;
+ }
+ goto err_exit;
+ }
+
+ do {
+ sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR);
+
+ PHAL_ATLANTIC->rpc_tid = sw.tid;
+
+ AQ_HW_WAIT_FOR(sw.tid ==
+ (fw.val =
+ aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR),
+ fw.tid), 10000U, 10U);
+ if (err < 0)
+ goto err_exit;
+
+ if (fw.len == 0xFFFFU) {
+ err = hw_atl_utils_fw_rpc_call(self, sw.len);
+ if (err < 0)
+ goto err_exit;
+ }
+ } while (sw.tid != fw.tid || 0xFFFFU == fw.len);
+ if (err < 0)
+ goto err_exit;
+
+ if (rpc) {
+ if (fw.len)
+ hw_atl_utils_fw_downld_dwords(self,
+ PHAL_ATLANTIC->rpc_addr,
+ (u32 *)(void *)
+ &PHAL_ATLANTIC->rpc,
+ (fw.len + sizeof(u32) -
+ sizeof(u8)) /
+ sizeof(u32));
+
+ *rpc = &PHAL_ATLANTIC->rpc;
+ }
+
+err_exit:
+ return err;
+}
+
+int hw_atl_utils_mpi_create(struct aq_hw_s *self)
+{
+ int err = 0;
+
+ err = hw_atl_utils_init_ucp(self);
+ if (err < 0)
+ goto err_exit;
+
+ err = hw_atl_utils_fw_rpc_init(self);
+ if (err < 0)
+ goto err_exit;
+
+err_exit:
+ return err;
+}
+
+void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
+ struct hw_aq_atl_utils_mbox *pmbox)
+{
+ int err = 0;
+
+ err = hw_atl_utils_fw_downld_dwords(self,
+ PHAL_ATLANTIC->mbox_addr,
+ (u32 *)(void *)pmbox,
+ sizeof(*pmbox) / sizeof(u32));
+ if (err < 0)
+ goto err_exit;
+
+ if (pmbox != &PHAL_ATLANTIC->mbox)
+ memcpy(pmbox, &PHAL_ATLANTIC->mbox, sizeof(*pmbox));
+
+ if (IS_CHIP_FEATURE(REVISION_A0)) {
+ unsigned int mtu = self->aq_nic_cfg ?
+ self->aq_nic_cfg->mtu : 1514U;
+ pmbox->stats.ubrc = pmbox->stats.uprc * mtu;
+ pmbox->stats.ubtc = pmbox->stats.uptc * mtu;
+ pmbox->stats.dpc = atomic_read(&PHAL_ATLANTIC_A0->dpc);
+ } else {
+ pmbox->stats.dpc = reg_rx_dma_stat_counter7get(self);
+ }
+
+err_exit:;
+}
+
+int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed,
+ enum hal_atl_utils_fw_state_e state)
+{
+ u32 ucp_0x368 = 0;
+
+ ucp_0x368 = (speed << HW_ATL_MPI_SPEED_SHIFT) | state;
+ aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, ucp_0x368);
+
+ return 0;
+}
+
+void hw_atl_utils_mpi_set(struct aq_hw_s *self,
+ enum hal_atl_utils_fw_state_e state, u32 speed)
+{
+ int err = 0;
+ u32 transaction_id = 0;
+
+ if (state == MPI_RESET) {
+ hw_atl_utils_mpi_read_stats(self, &PHAL_ATLANTIC->mbox);
+
+ transaction_id = PHAL_ATLANTIC->mbox.transaction_id;
+
+ AQ_HW_WAIT_FOR(transaction_id !=
+ (hw_atl_utils_mpi_read_stats
+ (self, &PHAL_ATLANTIC->mbox),
+ PHAL_ATLANTIC->mbox.transaction_id),
+ 1000, 10);
+ if (err < 0)
+ goto err_exit;
+ }
+
+ err = hw_atl_utils_mpi_set_speed(self, speed, state);
+
+err_exit:;
+}
+
+int hw_atl_utils_mpi_get_link_state(struct aq_hw_s *self,
+ struct aq_hw_link_status_s *link_status)
+{
+ u32 cp0x036C = aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR);
+ u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT;
+
+ if (!link_speed_mask) {
+ link_status->bps = 0U;
+ } else {
+ u64 link_speed;
+
+ switch (link_speed_mask) {
+ case HAL_ATLANTIC_RATE_10G:
+ link_speed = 10 * GBIT;
+ break;
+
+ case HAL_ATLANTIC_RATE_5G:
+ case HAL_ATLANTIC_RATE_5GSR:
+ link_speed = 5 * GBIT;
+ break;
+
+ case HAL_ATLANTIC_RATE_2GS:
+ link_speed = 2500 * MBIT;
+ break;
+
+ case HAL_ATLANTIC_RATE_1G:
+ link_speed = 1 * GBIT;
+ break;
+
+ case HAL_ATLANTIC_RATE_100M:
+ link_speed = 100 * MBIT;
+ break;
+
+ default:
+ link_speed = 0U;
+ break;
+ }
+
+ link_status->bps = link_speed;
+ }
+
+ return 0;
+}
+
+int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
+{
+ int err = 0;
+ u32 h = 0U;
+ u32 l = 0U;
+ u32 mac_addr[2];
+
+ self->mmio = aq_pci_func_get_mmio(self->aq_pci_func);
+
+ hw_atl_utils_hw_chip_features_init(self,
+ &PHAL_ATLANTIC_A0->chip_features);
+
+ err = hw_atl_utils_mpi_create(self);
+ if (err < 0)
+ goto err_exit;
+
+ if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) {
+ unsigned int rnd = 0;
+ unsigned int ucp_0x370 = 0;
+
+ get_random_bytes(&rnd, sizeof(unsigned int));
+
+ ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd);
+ aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
+ }
+
+ hw_atl_utils_fw_downld_dwords(self, aq_hw_read_reg(self, 0x00000374U) +
+ (40U * 4U),
+ mac_addr,
+ AQ_DIMOF(mac_addr));
+ mac_addr[0] = __swab32(mac_addr[0]);
+ mac_addr[1] = __swab32(mac_addr[1]);
+
+ ether_addr_copy(mac, (u8 *)mac_addr);
+
+ if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
+ /* chip revision */
+ l = 0xE3000000U
+ | (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG))
+ | (0x00 << 16);
+ h = 0x8001300EU;
+
+ mac[5] = (u8)(0xFFU & l);
+ l >>= 8;
+ mac[4] = (u8)(0xFFU & l);
+ l >>= 8;
+ mac[3] = (u8)(0xFFU & l);
+ l >>= 8;
+ mac[2] = (u8)(0xFFU & l);
+ mac[1] = (u8)(0xFFU & h);
+ h >>= 8;
+ mac[0] = (u8)(0xFFU & h);
+ }
+
+err_exit:
+ return err;
+}
+
+int hw_atl_utils_bps_2_speed_index(u64 bps)
+{
+ unsigned int ret = 0U;
+
+ switch (bps) {
+ case 100 * MBIT:
+ ret = 5U;
+ break;
+
+ case 1 * GBIT:
+ ret = 4U;
+ break;
+
+ case 2500 * MBIT:
+ ret = 3U;
+ break;
+
+ case 5 * GBIT:
+ ret = 1U;
+ break;
+
+ case 10 * GBIT:
+ ret = 0U;
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+}
+
+void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p)
+{
+ u32 chip_features = 0U;
+ u32 val = reg_glb_mif_id_get(self);
+ u32 mif_rev = val & 0xFFU;
+
+ if ((3U & mif_rev) == 1U) {
+ chip_features |=
+ HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 |
+ HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
+ HAL_ATLANTIC_UTILS_CHIP_MIPS;
+ } else if ((3U & mif_rev) == 2U) {
+ chip_features |=
+ HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 |
+ HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
+ HAL_ATLANTIC_UTILS_CHIP_MIPS |
+ HAL_ATLANTIC_UTILS_CHIP_TPO2 |
+ HAL_ATLANTIC_UTILS_CHIP_RPF2;
+ }
+
+ *p = chip_features;
+}
+
+int hw_atl_utils_hw_deinit(struct aq_hw_s *self)
+{
+ hw_atl_utils_mpi_set(self, MPI_DEINIT, 0x0U);
+ return 0;
+}
+
+int hw_atl_utils_hw_set_power(struct aq_hw_s *self,
+ unsigned int power_state)
+{
+ hw_atl_utils_mpi_set(self, MPI_POWER, 0x0U);
+ return 0;
+}
+
+int hw_atl_utils_get_hw_stats(struct aq_hw_s *self,
+ u64 *data, unsigned int *p_count)
+{
+ struct hw_atl_stats_s *stats = NULL;
+ int i = 0;
+
+ hw_atl_utils_mpi_read_stats(self, &PHAL_ATLANTIC->mbox);
+
+ stats = &PHAL_ATLANTIC->mbox.stats;
+
+ data[i] = stats->uprc + stats->mprc + stats->bprc;
+ data[++i] = stats->uprc;
+ data[++i] = stats->mprc;
+ data[++i] = stats->bprc;
+ data[++i] = stats->erpt;
+ data[++i] = stats->uptc + stats->mptc + stats->bptc;
+ data[++i] = stats->uptc;
+ data[++i] = stats->mptc;
+ data[++i] = stats->bptc;
+ data[++i] = stats->ubrc;
+ data[++i] = stats->ubtc;
+ data[++i] = stats->mbrc;
+ data[++i] = stats->mbtc;
+ data[++i] = stats->bbrc;
+ data[++i] = stats->bbtc;
+ data[++i] = stats->ubrc + stats->mbrc + stats->bbrc;
+ data[++i] = stats->ubtc + stats->mbtc + stats->bbtc;
+ data[++i] = stats_rx_dma_good_pkt_counterlsw_get(self);
+ data[++i] = stats_tx_dma_good_pkt_counterlsw_get(self);
+ data[++i] = stats_rx_dma_good_octet_counterlsw_get(self);
+ data[++i] = stats_tx_dma_good_octet_counterlsw_get(self);
+ data[++i] = stats->dpc;
+
+ if (p_count)
+ *p_count = ++i;
+
+ return 0;
+}
+
+static const u32 hw_atl_utils_hw_mac_regs[] = {
+ 0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U,
+ 0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U,
+ 0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U,
+ 0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U,
+ 0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U,
+ 0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U,
+ 0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U,
+ 0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U,
+ 0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U,
+ 0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U,
+ 0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U,
+ 0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U,
+ 0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U,
+ 0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U,
+ 0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U,
+ 0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U,
+ 0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU,
+ 0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU,
+ 0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U,
+ 0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U,
+ 0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U,
+ 0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U,
+};
+
+int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
+ struct aq_hw_caps_s *aq_hw_caps,
+ u32 *regs_buff)
+{
+ unsigned int i = 0U;
+
+ for (i = 0; i < aq_hw_caps->mac_regs_count; i++)
+ regs_buff[i] = aq_hw_read_reg(self,
+ hw_atl_utils_hw_mac_regs[i]);
+ return 0;
+}
+
+int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
+{
+ *fw_version = aq_hw_read_reg(self, 0x18U);
+ return 0;
+}
diff --git a/drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.h b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.h
new file mode 100644
index 0000000..17c4d52
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/hw_atl/hw_atl_utils.h
@@ -0,0 +1,210 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File hw_atl_utils.h: Declaration of common functions for Atlantic hardware
+ * abstraction layer.
+ */
+
+#ifndef HW_ATL_UTILS_H
+#define HW_ATL_UTILS_H
+
+#include "../aq_common.h"
+
+#define HW_ATL_FLUSH() { (void)aq_hw_read_reg(self, 0x10); }
+
+struct __packed hw_atl_stats_s {
+ u32 uprc;
+ u32 mprc;
+ u32 bprc;
+ u32 erpt;
+ u32 uptc;
+ u32 mptc;
+ u32 bptc;
+ u32 erpr;
+ u32 mbtc;
+ u32 bbtc;
+ u32 mbrc;
+ u32 bbrc;
+ u32 ubrc;
+ u32 ubtc;
+ u32 dpc;
+};
+
+union __packed ip_addr {
+ struct {
+ u8 addr[16];
+ } v6;
+ struct {
+ u8 padding[12];
+ u8 addr[4];
+ } v4;
+};
+
+struct __packed hw_aq_atl_utils_fw_rpc {
+ u32 msg_id;
+
+ union {
+ struct {
+ u32 pong;
+ } msg_ping;
+
+ struct {
+ u8 mac_addr[6];
+ u32 ip_addr_cnt;
+
+ struct {
+ union ip_addr addr;
+ union ip_addr mask;
+ } ip[1];
+ } msg_arp;
+
+ struct {
+ u32 len;
+ u8 packet[1514U];
+ } msg_inject;
+
+ struct {
+ u32 priority;
+ u32 wol_packet_type;
+ u16 friendly_name_len;
+ u16 friendly_name[65];
+ u32 pattern_id;
+ u32 next_wol_pattern_offset;
+
+ union {
+ struct {
+ u32 flags;
+ u8 ipv4_source_address[4];
+ u8 ipv4_dest_address[4];
+ u16 tcp_source_port_number;
+ u16 tcp_dest_port_number;
+ } ipv4_tcp_syn_parameters;
+
+ struct {
+ u32 flags;
+ u8 ipv6_source_address[16];
+ u8 ipv6_dest_address[16];
+ u16 tcp_source_port_number;
+ u16 tcp_dest_port_number;
+ } ipv6_tcp_syn_parameters;
+
+ struct {
+ u32 flags;
+ } eapol_request_id_message_parameters;
+
+ struct {
+ u32 flags;
+ u32 mask_offset;
+ u32 mask_size;
+ u32 pattern_offset;
+ u32 pattern_size;
+ } wol_bit_map_pattern;
+ } wol_pattern;
+ } msg_wol;
+
+ struct {
+ u32 is_wake_on_link_down;
+ u32 is_wake_on_link_up;
+ } msg_wolink;
+ };
+};
+
+struct __packed hw_aq_atl_utils_mbox {
+ u32 version;
+ u32 transaction_id;
+ int error;
+ struct hw_atl_stats_s stats;
+};
+
+struct __packed hw_atl_s {
+ struct aq_hw_s base;
+ struct hw_aq_atl_utils_mbox mbox;
+ u64 speed;
+ u32 itr_tx;
+ u32 itr_rx;
+ unsigned int chip_features;
+ u32 fw_ver_actual;
+ atomic_t dpc;
+ u32 mbox_addr;
+ u32 rpc_addr;
+ u32 rpc_tid;
+ struct hw_aq_atl_utils_fw_rpc rpc;
+};
+
+#define SELF ((struct hw_atl_s *)self)
+
+#define PHAL_ATLANTIC ((struct hw_atl_s *)((void *)(self)))
+#define PHAL_ATLANTIC_A0 ((struct hw_atl_s *)((void *)(self)))
+#define PHAL_ATLANTIC_B0 ((struct hw_atl_s *)((void *)(self)))
+
+#define HAL_ATLANTIC_UTILS_CHIP_MIPS 0x00000001U
+#define HAL_ATLANTIC_UTILS_CHIP_TPO2 0x00000002U
+#define HAL_ATLANTIC_UTILS_CHIP_RPF2 0x00000004U
+#define HAL_ATLANTIC_UTILS_CHIP_MPI_AQ 0x00000010U
+#define HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 0x01000000U
+#define HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 0x02000000U
+
+#define IS_CHIP_FEATURE(_F_) (HAL_ATLANTIC_UTILS_CHIP_##_F_ & \
+ PHAL_ATLANTIC->chip_features)
+
+enum hal_atl_utils_fw_state_e {
+ MPI_DEINIT = 0,
+ MPI_RESET = 1,
+ MPI_INIT = 2,
+ MPI_POWER = 4,
+};
+
+#define HAL_ATLANTIC_RATE_10G BIT(0)
+#define HAL_ATLANTIC_RATE_5G BIT(1)
+#define HAL_ATLANTIC_RATE_5GSR BIT(2)
+#define HAL_ATLANTIC_RATE_2GS BIT(3)
+#define HAL_ATLANTIC_RATE_1G BIT(4)
+#define HAL_ATLANTIC_RATE_100M BIT(5)
+#define HAL_ATLANTIC_RATE_INVALID BIT(6)
+
+int hw_atl_utils_mpi_create(struct aq_hw_s *self);
+
+void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p);
+
+void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
+ struct hw_aq_atl_utils_mbox *pmbox);
+
+void hw_atl_utils_mpi_set(struct aq_hw_s *self,
+ enum hal_atl_utils_fw_state_e state,
+ u32 speed);
+
+int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed,
+ enum hal_atl_utils_fw_state_e state);
+
+int hw_atl_utils_mpi_get_link_state(struct aq_hw_s *self,
+ struct aq_hw_link_status_s *link_status);
+
+int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, u8 *mac);
+
+int hw_atl_utils_bps_2_speed_index(u64 bps);
+
+int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
+ struct aq_hw_caps_s *aq_hw_caps,
+ u32 *regs_buff);
+
+int hw_atl_utils_hw_get_settings(struct aq_hw_s *self,
+ struct ethtool_cmd *cmd);
+
+int hw_atl_utils_hw_set_power(struct aq_hw_s *self,
+ unsigned int power_state);
+
+int hw_atl_utils_hw_deinit(struct aq_hw_s *self);
+
+int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version);
+
+int hw_atl_utils_get_hw_stats(struct aq_hw_s *self,
+ u64 *data,
+ unsigned int *p_count);
+
+#endif /* HW_ATL_UTILS_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v5 10/13] net: ethernet: aquantia: Hardware interface and utility functions
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel Belous, Dmitry Bezrukov
In-Reply-To: <fef6ab77998a2914f94549367b88ab31e1f734e7.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Add functions to interface with the hardware and some utility functions.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
drivers/net/ethernet/aquantia/aq_hw.h | 169 ++++++++++++++++++++++++++++
drivers/net/ethernet/aquantia/aq_hw_utils.c | 68 +++++++++++
drivers/net/ethernet/aquantia/aq_hw_utils.h | 47 ++++++++
3 files changed, 284 insertions(+)
create mode 100644 drivers/net/ethernet/aquantia/aq_hw.h
create mode 100644 drivers/net/ethernet/aquantia/aq_hw_utils.c
create mode 100644 drivers/net/ethernet/aquantia/aq_hw_utils.h
diff --git a/drivers/net/ethernet/aquantia/aq_hw.h b/drivers/net/ethernet/aquantia/aq_hw.h
new file mode 100644
index 0000000..a3c727e
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_hw.h
@@ -0,0 +1,169 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_hw.h: Declaraion of abstract interface for NIC hardware specific
+ * functions.
+ */
+
+#ifndef AQ_HW_H
+#define AQ_HW_H
+
+#include "aq_common.h"
+
+/* NIC H/W capabilities */
+struct aq_hw_caps_s {
+ u64 hw_features;
+ u64 link_speed_msk;
+ unsigned int hw_priv_flags;
+ u32 rxds;
+ u32 txds;
+ u32 txhwb_alignment;
+ u32 irq_mask;
+ u32 vecs;
+ u32 mtu;
+ u32 mac_regs_count;
+ u8 ports;
+ u8 msix_irqs;
+ u8 tcs;
+ u8 rxd_alignment;
+ u8 rxd_size;
+ u8 txd_alignment;
+ u8 txd_size;
+ u8 tx_rings;
+ u8 rx_rings;
+ bool flow_control;
+ bool is_64_dma;
+};
+
+struct aq_hw_link_status_s {
+ u64 bps;
+};
+
+#define AQ_HW_POWER_STATE_D0 0U
+#define AQ_HW_POWER_STATE_D3 3U
+
+#define AQ_HW_FLAG_STARTED 0x00000004U
+#define AQ_HW_FLAG_STOPPING 0x00000008U
+#define AQ_HW_FLAG_RESETTING 0x00000010U
+#define AQ_HW_FLAG_CLOSING 0x00000020U
+#define AQ_HW_LINK_DOWN 0x04000000U
+#define AQ_HW_FLAG_ERR_UNPLUG 0x40000000U
+#define AQ_HW_FLAG_ERR_HW 0x80000000U
+
+#define AQ_HW_FLAG_ERRORS (AQ_HW_FLAG_ERR_HW | AQ_HW_FLAG_ERR_UNPLUG)
+
+struct aq_hw_s {
+ AQ_OBJ_HEADER;
+ struct aq_nic_cfg_s *aq_nic_cfg;
+ struct aq_pci_func_s *aq_pci_func;
+ void __iomem *mmio;
+ unsigned int not_ff_addr;
+ struct aq_hw_link_status_s aq_link_status;
+};
+
+struct aq_ring_s;
+struct aq_ring_param_s;
+struct aq_nic_cfg_s;
+struct sk_buff;
+
+struct aq_hw_ops {
+ struct aq_hw_s *(*create)(struct aq_pci_func_s *aq_pci_func,
+ unsigned int port, struct aq_hw_ops *ops);
+
+ void (*destroy)(struct aq_hw_s *self);
+
+ int (*get_hw_caps)(struct aq_hw_s *self,
+ struct aq_hw_caps_s *aq_hw_caps);
+
+ int (*hw_ring_tx_xmit)(struct aq_hw_s *self, struct aq_ring_s *aq_ring,
+ unsigned int frags);
+
+ int (*hw_ring_rx_receive)(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+
+ int (*hw_ring_rx_fill)(struct aq_hw_s *self, struct aq_ring_s *aq_ring,
+ unsigned int sw_tail_old);
+
+ int (*hw_ring_tx_head_update)(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+
+ int (*hw_get_mac_permanent)(struct aq_hw_s *self, u8 *mac);
+
+ int (*hw_set_mac_address)(struct aq_hw_s *self, u8 *mac_addr);
+
+ int (*hw_get_link_status)(struct aq_hw_s *self,
+ struct aq_hw_link_status_s *link_status);
+
+ int (*hw_set_link_speed)(struct aq_hw_s *self, u32 speed);
+
+ int (*hw_reset)(struct aq_hw_s *self);
+
+ int (*hw_init)(struct aq_hw_s *self, struct aq_nic_cfg_s *aq_nic_cfg,
+ u8 *mac_addr);
+
+ int (*hw_start)(struct aq_hw_s *self);
+
+ int (*hw_stop)(struct aq_hw_s *self);
+
+ int (*hw_ring_tx_init)(struct aq_hw_s *self, struct aq_ring_s *aq_ring,
+ struct aq_ring_param_s *aq_ring_param);
+
+ int (*hw_ring_tx_start)(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+
+ int (*hw_ring_tx_stop)(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+
+ int (*hw_ring_rx_init)(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring,
+ struct aq_ring_param_s *aq_ring_param);
+
+ int (*hw_ring_rx_start)(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+
+ int (*hw_ring_rx_stop)(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+
+ int (*hw_irq_enable)(struct aq_hw_s *self, u64 mask);
+
+ int (*hw_irq_disable)(struct aq_hw_s *self, u64 mask);
+
+ int (*hw_irq_read)(struct aq_hw_s *self, u64 *mask);
+
+ int (*hw_packet_filter_set)(struct aq_hw_s *self,
+ unsigned int packet_filter);
+
+ int (*hw_multicast_list_set)(struct aq_hw_s *self,
+ u8 ar_mac[AQ_CFG_MULTICAST_ADDRESS_MAX]
+ [ETH_ALEN],
+ u32 count);
+
+ int (*hw_interrupt_moderation_set)(struct aq_hw_s *self,
+ bool itr_enabled);
+
+ int (*hw_rss_set)(struct aq_hw_s *self,
+ struct aq_rss_parameters *rss_params);
+
+ int (*hw_rss_hash_set)(struct aq_hw_s *self,
+ struct aq_rss_parameters *rss_params);
+
+ int (*hw_get_regs)(struct aq_hw_s *self,
+ struct aq_hw_caps_s *aq_hw_caps, u32 *regs_buff);
+
+ int (*hw_get_hw_stats)(struct aq_hw_s *self, u64 *data,
+ unsigned int *p_count);
+
+ int (*hw_get_fw_version)(struct aq_hw_s *self, u32 *fw_version);
+
+ int (*hw_deinit)(struct aq_hw_s *self);
+
+ int (*hw_set_power)(struct aq_hw_s *self, unsigned int power_state);
+};
+
+#endif /* AQ_HW_H */
diff --git a/drivers/net/ethernet/aquantia/aq_hw_utils.c b/drivers/net/ethernet/aquantia/aq_hw_utils.c
new file mode 100644
index 0000000..3ce6d80
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_hw_utils.c
@@ -0,0 +1,68 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_hw_utils.c: Definitions of helper functions used across
+ * hardware layer.
+ */
+
+#include "aq_hw_utils.h"
+#include "aq_hw.h"
+
+void aq_hw_write_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk,
+ u32 shift, u32 val)
+{
+ if (msk ^ ~0) {
+ u32 reg_old, reg_new;
+
+ reg_old = aq_hw_read_reg(aq_hw, addr);
+ reg_new = (reg_old & (~msk)) | (val << shift);
+
+ if (reg_old != reg_new)
+ aq_hw_write_reg(aq_hw, addr, reg_new);
+ } else {
+ aq_hw_write_reg(aq_hw, addr, val);
+ }
+}
+
+u32 aq_hw_read_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk, u32 shift)
+{
+ return ((aq_hw_read_reg(aq_hw, addr) & msk) >> shift);
+}
+
+u32 aq_hw_read_reg(struct aq_hw_s *hw, u32 reg)
+{
+ u32 value = readl(hw->mmio + reg);
+
+ if ((~0U) == value && (~0U) == readl(hw->mmio + hw->not_ff_addr))
+ AQ_OBJ_SET(hw, AQ_HW_FLAG_ERR_UNPLUG);
+
+ return value;
+}
+
+inline void aq_hw_write_reg(struct aq_hw_s *hw, u32 reg, u32 value)
+{
+ writel(value, hw->mmio + reg);
+}
+
+int aq_hw_err_from_flags(struct aq_hw_s *hw)
+{
+ int err = 0;
+
+ if (AQ_OBJ_TST(hw, AQ_HW_FLAG_ERR_UNPLUG)) {
+ err = -ENXIO;
+ goto err_exit;
+ }
+ if (AQ_OBJ_TST(hw, AQ_HW_FLAG_ERR_HW)) {
+ err = -EIO;
+ goto err_exit;
+ }
+
+err_exit:
+ return err;
+}
diff --git a/drivers/net/ethernet/aquantia/aq_hw_utils.h b/drivers/net/ethernet/aquantia/aq_hw_utils.h
new file mode 100644
index 0000000..78fcc0c
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_hw_utils.h
@@ -0,0 +1,47 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_hw_utils.h: Declaration of helper functions used across hardware
+ * layer.
+ */
+
+#ifndef AQ_HW_UTILS_H
+#define AQ_HW_UTILS_H
+
+#include "aq_common.h"
+
+#ifndef HIDWORD
+#define LODWORD(_qw) ((u32)(_qw))
+#define HIDWORD(_qw) ((u32)(((_qw) >> 32) & 0xffffffff))
+#endif
+
+#define AQ_HW_SLEEP(_US_) mdelay(_US_)
+
+#define AQ_HW_WAIT_FOR(_B_, _US_, _N_) \
+do { \
+ unsigned int AQ_HW_WAIT_FOR_i; \
+ for (AQ_HW_WAIT_FOR_i = _N_; (!(_B_)) && (AQ_HW_WAIT_FOR_i);\
+ --AQ_HW_WAIT_FOR_i) {\
+ udelay(_US_); \
+ } \
+ if (!AQ_HW_WAIT_FOR_i) {\
+ err = ETIME; \
+ } \
+} while (0)
+
+struct aq_hw_s;
+
+void aq_hw_write_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk,
+ u32 shift, u32 val);
+u32 aq_hw_read_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk, u32 shift);
+u32 aq_hw_read_reg(struct aq_hw_s *hw, u32 reg);
+void aq_hw_write_reg(struct aq_hw_s *hw, u32 reg, u32 value);
+int aq_hw_err_from_flags(struct aq_hw_s *hw);
+
+#endif /* AQ_HW_UTILS_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v5 12/13] net: ethernet: aquantia: Receive side scaling
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel Belous, Dmitry Bezrukov
In-Reply-To: <b2236a13fcf9ac65f15cfc084de670948517e003.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Add definitions that support receive side scaling.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
| 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 drivers/net/ethernet/aquantia/aq_rss.h
--git a/drivers/net/ethernet/aquantia/aq_rss.h b/drivers/net/ethernet/aquantia/aq_rss.h
new file mode 100644
index 0000000..1db6eb2
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_rss.h
@@ -0,0 +1,26 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_rss.h: Receive Side Scaling definitions. */
+
+#ifndef AQ_RSS_H
+#define AQ_RSS_H
+
+#include "aq_common.h"
+#include "aq_cfg.h"
+
+struct aq_rss_parameters {
+ u16 base_cpu_number;
+ u16 indirection_table_size;
+ u16 hash_secret_key_size;
+ u32 hash_secret_key[AQ_CFG_RSS_HASHKEY_SIZE / sizeof(u32)];
+ u8 indirection_table[AQ_CFG_RSS_INDIRECTION_TABLE_MAX];
+};
+
+#endif /* AQ_RSS_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v5 11/13] net: ethernet: aquantia: Ethtool support
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel Belous, Dmitry Bezrukov
In-Reply-To: <bd4244af243c5800bd08cfe6c3859f1b406089be.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Add the driver interfaces required for support by the ethtool utility.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
drivers/net/ethernet/aquantia/aq_ethtool.c | 250 +++++++++++++++++++++++++++++
drivers/net/ethernet/aquantia/aq_ethtool.h | 19 +++
2 files changed, 269 insertions(+)
create mode 100644 drivers/net/ethernet/aquantia/aq_ethtool.c
create mode 100644 drivers/net/ethernet/aquantia/aq_ethtool.h
diff --git a/drivers/net/ethernet/aquantia/aq_ethtool.c b/drivers/net/ethernet/aquantia/aq_ethtool.c
new file mode 100644
index 0000000..f11bdb1
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_ethtool.c
@@ -0,0 +1,250 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_ethtool.c: Definition of ethertool related functions. */
+
+#include "aq_ethtool.h"
+#include "aq_nic.h"
+
+static void aq_ethtool_get_regs(struct net_device *ndev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+ u32 regs_count = aq_nic_get_regs_count(aq_nic);
+
+ memset(p, 0, regs_count * sizeof(u32));
+ aq_nic_get_regs(aq_nic, regs, p);
+}
+
+static int aq_ethtool_get_regs_len(struct net_device *ndev)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+ u32 regs_count = aq_nic_get_regs_count(aq_nic);
+
+ return regs_count * sizeof(u32);
+}
+
+static u32 aq_ethtool_get_link(struct net_device *ndev)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+
+ return aq_nic_get_link_speed(aq_nic) ? 1U : 0U;
+}
+
+static int aq_ethtool_get_settings(struct net_device *ndev,
+ struct ethtool_cmd *cmd)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+
+ cmd->port = PORT_TP;
+ cmd->transceiver = XCVR_EXTERNAL;
+
+ ethtool_cmd_speed_set(cmd, netif_carrier_ok(ndev) ?
+ aq_nic_get_link_speed(aq_nic) : 0U);
+
+ cmd->duplex = DUPLEX_FULL;
+ aq_nic_get_link_settings(aq_nic, cmd);
+ return 0;
+}
+
+static int aq_ethtool_set_settings(struct net_device *ndev,
+ struct ethtool_cmd *cmd)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+
+ return aq_nic_set_link_settings(aq_nic, cmd);
+}
+
+static const char aq_ethtool_stat_names[][ETH_GSTRING_LEN] = {
+ "InPackets",
+ "InUCast",
+ "InMCast",
+ "InBCast",
+ "InErrors",
+ "OutPackets",
+ "OutUCast",
+ "OutMCast",
+ "OutBCast",
+ "InUCastOctects",
+ "OutUCastOctects",
+ "InMCastOctects",
+ "OutMCastOctects",
+ "InBCastOctects",
+ "OutBCastOctects",
+ "InOctects",
+ "OutOctects",
+ "InPacketsDma",
+ "OutPacketsDma",
+ "InOctetsDma",
+ "OutOctetsDma",
+ "InDroppedDma",
+ "Queue[0] InPackets",
+ "Queue[0] OutPackets",
+ "Queue[0] InJumboPackets",
+ "Queue[0] InLroPackets",
+ "Queue[0] InErrors",
+#if 1 < AQ_CFG_VECS_DEF
+ "Queue[1] InPackets",
+ "Queue[1] OutPackets",
+ "Queue[1] InJumboPackets",
+ "Queue[1] InLroPackets",
+ "Queue[1] InErrors",
+#endif
+#if 2 < AQ_CFG_VECS_DEF
+ "Queue[2] InPackets",
+ "Queue[2] OutPackets",
+ "Queue[2] InJumboPackets",
+ "Queue[2] InLroPackets",
+ "Queue[2] InErrors",
+#endif
+#if 3 < AQ_CFG_VECS_DEF
+ "Queue[3] InPackets",
+ "Queue[3] OutPackets",
+ "Queue[3] InJumboPackets",
+ "Queue[3] InLroPackets",
+ "Queue[3] InErrors",
+#endif
+#if 4 < AQ_CFG_VECS_DEF
+ "Queue[4] InPackets",
+ "Queue[4] OutPackets",
+ "Queue[4] InJumboPackets",
+ "Queue[4] InLroPackets",
+ "Queue[4] InErrors",
+#endif
+#if 5 < AQ_CFG_VECS_DEF
+ "Queue[5] InPackets",
+ "Queue[5] OutPackets",
+ "Queue[5] InJumboPackets",
+ "Queue[5] InLroPackets",
+ "Queue[5] InErrors",
+#endif
+#if 6 < AQ_CFG_VECS_DEF
+ "Queue[6] InPackets",
+ "Queue[6] OutPackets",
+ "Queue[6] InJumboPackets",
+ "Queue[6] InLroPackets",
+ "Queue[6] InErrors",
+#endif
+#if 7 < AQ_CFG_VECS_DEF
+ "Queue[7] InPackets",
+ "Queue[7] OutPackets",
+ "Queue[7] InJumboPackets",
+ "Queue[7] InLroPackets",
+ "Queue[7] InErrors",
+#endif
+};
+
+static void aq_ethtool_stats(struct net_device *ndev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+
+ memset(data, 0, ARRAY_SIZE(aq_ethtool_stat_names) * sizeof(u64));
+ aq_nic_get_stats(aq_nic, data);
+}
+
+static void aq_ethtool_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+ u32 firmware_version = aq_nic_get_fw_version(aq_nic);
+ u32 regs_count = aq_nic_get_regs_count(aq_nic);
+
+ strlcat(drvinfo->driver, AQ_CFG_DRV_NAME, sizeof(drvinfo->driver));
+ strlcat(drvinfo->version, AQ_CFG_DRV_VERSION, sizeof(drvinfo->version));
+
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+ "%u.%u.%u", firmware_version >> 24,
+ (firmware_version >> 16) & 0xFFU, firmware_version & 0xFFFFU);
+
+ drvinfo->n_stats = ARRAY_SIZE(aq_ethtool_stat_names);
+ drvinfo->testinfo_len = 0;
+ drvinfo->regdump_len = regs_count;
+ drvinfo->eedump_len = 0;
+}
+
+static void aq_ethtool_get_strings(struct net_device *ndev,
+ u32 stringset, u8 *data)
+{
+ memcpy(data, *aq_ethtool_stat_names, sizeof(aq_ethtool_stat_names));
+}
+
+static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset)
+{
+ return ARRAY_SIZE(aq_ethtool_stat_names);
+}
+
+static u32 aq_ethtool_get_rss_indir_size(struct net_device *ndev)
+{
+ return AQ_CFG_RSS_INDIRECTION_TABLE_MAX;
+}
+
+static u32 aq_ethtool_get_rss_key_size(struct net_device *ndev)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+ struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
+
+ return sizeof(cfg->aq_rss.hash_secret_key);
+}
+
+static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+ struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
+ unsigned int i = 0U;
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */
+ if (indir) {
+ for (i = 0; i < AQ_CFG_RSS_INDIRECTION_TABLE_MAX; i++)
+ indir[i] = cfg->aq_rss.indirection_table[i];
+ }
+ if (key)
+ memcpy(key, cfg->aq_rss.hash_secret_key,
+ sizeof(cfg->aq_rss.hash_secret_key));
+ return 0;
+}
+
+static int aq_ethtool_get_rxnfc(struct net_device *ndev,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs)
+
+{
+ struct aq_nic_s *aq_nic = (struct aq_nic_s *)netdev_priv(ndev);
+ struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
+ int err = -EOPNOTSUPP;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXRINGS:
+ cmd->data = cfg->vecs;
+ err = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return err;
+}
+
+const struct ethtool_ops aq_ethtool_ops = {
+ .get_link = aq_ethtool_get_link,
+ .get_regs_len = aq_ethtool_get_regs_len,
+ .get_regs = aq_ethtool_get_regs,
+ .get_settings = aq_ethtool_get_settings,
+ .set_settings = aq_ethtool_set_settings,
+ .get_drvinfo = aq_ethtool_get_drvinfo,
+ .get_strings = aq_ethtool_get_strings,
+ .get_rxfh_indir_size = aq_ethtool_get_rss_indir_size,
+ .get_rxfh_key_size = aq_ethtool_get_rss_key_size,
+ .get_rxfh = aq_ethtool_get_rss,
+ .get_rxnfc = aq_ethtool_get_rxnfc,
+ .get_sset_count = aq_ethtool_get_sset_count,
+ .get_ethtool_stats = aq_ethtool_stats
+};
diff --git a/drivers/net/ethernet/aquantia/aq_ethtool.h b/drivers/net/ethernet/aquantia/aq_ethtool.h
new file mode 100644
index 0000000..21c126e
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/aq_ethtool.h
@@ -0,0 +1,19 @@
+/*
+ * aQuantia Corporation Network Driver
+ * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+/* File aq_ethtool.h: Declaration of ethertool related functions. */
+
+#ifndef AQ_ETHTOOL_H
+#define AQ_ETHTOOL_H
+
+#include "aq_common.h"
+
+extern const struct ethtool_ops aq_ethtool_ops;
+
+#endif /* AQ_ETHTOOL_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v5 13/13] net: ethernet: aquantia: Integrate AQtion 2.5/5 GB NIC driver
From: Alexander Loktionov @ 2017-01-13 5:02 UTC (permalink / raw)
To: netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Alexander Loktionov,
Dmitrii Tarakanov, Pavel Belous, Dmitry Bezrukov
In-Reply-To: <c54a5830be99bc8f7ecbb4408bbc6b979b056c6a.1484283610.git.vomlehn@texas.net>
From: David VomLehn <vomlehn@texas.net>
Modify the drivers/net/ethernet/{Makefile,Kconfig} file to make them a
part of the network drivers build.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 8cc7467..d467c8b 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/ethernet/amazon/Kconfig"
source "drivers/net/ethernet/amd/Kconfig"
source "drivers/net/ethernet/apm/Kconfig"
source "drivers/net/ethernet/apple/Kconfig"
+source "drivers/net/ethernet/aquantia/Kconfig"
source "drivers/net/ethernet/arc/Kconfig"
source "drivers/net/ethernet/atheros/Kconfig"
source "drivers/net/ethernet/aurora/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index a09423d..123ef8e 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_NET_VENDOR_AMAZON) += amazon/
obj-$(CONFIG_NET_VENDOR_AMD) += amd/
obj-$(CONFIG_NET_XGENE) += apm/
obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
+obj-$(CONFIG_NET_VENDOR_AQUANTIA) += aquantia/
obj-$(CONFIG_NET_VENDOR_ARC) += arc/
obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
obj-$(CONFIG_NET_VENDOR_AURORA) += aurora/
--
2.7.4
^ permalink raw reply related
* Re: [PATCH v5 01/13] net: ethernet: aquantia: Make and configuration files.
From: Joe Perches @ 2017-01-13 5:06 UTC (permalink / raw)
To: Alexander Loktionov, netdev, David VomLehn
Cc: David S . Miller, Simon Edelhaus, Dmitrii Tarakanov, Pavel Belous,
Dmitry Bezrukov
In-Reply-To: <fe1664e502d28bcb6a5689787b8000e6c9288307.1484283610.git.vomlehn@texas.net>
On Thu, 2017-01-12 at 21:02 -0800, Alexander Loktionov wrote:
> From: David VomLehn <vomlehn@texas.net>
>
> Patches to create the make and configuration files.
This patch should _really_ be the last in the series
not the first.
^ permalink raw reply
* Re: [PATCH v5 01/13] net: ethernet: aquantia: Make and configuration files.
From: David VomLehn @ 2017-01-13 5:24 UTC (permalink / raw)
To: Joe Perches, Alexander Loktionov, netdev
Cc: David S . Miller, Simon Edelhaus, Dmitrii Tarakanov, Pavel Belous,
Dmitry Bezrukov
In-Reply-To: <1484283977.3065.24.camel@perches.com>
On 01/12/2017 09:06 PM, Joe Perches wrote:
> On Thu, 2017-01-12 at 21:02 -0800, Alexander Loktionov wrote:
>> From: David VomLehn <vomlehn@texas.net>
>>
>> Patches to create the make and configuration files.
> This patch should _really_ be the last in the series
> not the first.
>
Could you explain the basis for this? By convention, we put tables of
content at the beginning of books and only indices at the back.
Analogously, make and config files can be used to established the
context for what follows, making it easier to understand. Once
committed, of course, the order no longer matters except as bisection is
concerned.
--
David VL
^ permalink raw reply
* Re: [PATCH v5 01/13] net: ethernet: aquantia: Make and configuration files.
From: Joe Perches @ 2017-01-13 5:59 UTC (permalink / raw)
To: David VomLehn, Alexander Loktionov, netdev
Cc: David S . Miller, Simon Edelhaus, Dmitrii Tarakanov, Pavel Belous,
Dmitry Bezrukov
In-Reply-To: <a1dcfa57-97a1-dc20-6d8d-60cba3be3a56@texas.net>
On Thu, 2017-01-12 at 21:24 -0800, David VomLehn wrote:
> On 01/12/2017 09:06 PM, Joe Perches wrote:
> > On Thu, 2017-01-12 at 21:02 -0800, Alexander Loktionov wrote:
> > > From: David VomLehn <vomlehn@texas.net>
> > >
> > > Patches to create the make and configuration files.
> >
> > This patch should _really_ be the last in the series
> > not the first.
> >
>
> Could you explain the basis for this? By convention, we put tables of
> content at the beginning of books and only indices at the back.
> Analogously, make and config files can be used to established the
> context for what follows, making it easier to understand. Once
> committed, of course, the order no longer matters except as bisection is
> concerned.
As I wrote the first time:
On Tue, 2016-12-27 at 08:15 -0800, Joe Perches wrote:
> On Tue, 2016-12-27 at 05:17 -0800, David VomLehn wrote:
> > Patches to create the make and configuration files.
[]
> Patch 1 will not build if CONFIG_AQTION is enabled.
> Patch 1/12 should be reordered to be patch 12/12 and
> all the other patches moved up appropriately.
You don't create the files until later patches.
If you applied just this first patch and tried to
add CONFIG_AQTION=y to the .config, make fails.
That's bad for git bisect.
Every patch in this series should build properly.
If you delay the adding of the Makefile and Kconfig
until all the files are added, then it'd bisect fine.
^ permalink raw reply
* [net-next 00/13] RACK fast recovery
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
The patch set enables RACK loss detection (draft-ietf-tcpm-rack-01)
to trigger fast recovery with a reordering timer.
Previously RACK has been running in auxiliary mode where it is
used to detect packet losses once the recovery has triggered by
other algorithms (e.g., FACK). By inspecting packet timestamps,
RACK can start ACK-driven repairs timely. A few similar heuristics
are no longer needed and are either removed or disabled to reduce
the complexity of the Linux TCP loss recovery engine:
1. FACK (Forward Acknowledgement)
2. Early Retransmit (RFC5827)
3. thin_dupack (fast recovery on single DUPACK for thin-streams)
4. NCR (Non-Congestion Robustness RFC4653) (RFC4653)
5. Forward Retransmit
After this change, Linux's loss recovery algorithms consist of
1. Conventional DUPACK threshold approach (RFC6675)
2. RACK and Tail Loss Probe (draft-ietf-tcpm-rack-01)
3. RTO plus F-RTO extension (RFC5682)
The patch set has been tested on Google servers extensively and
presented in several IETF meetings. The data suggests that RACK
successfully improves recovery performance:
https://www.ietf.org/proceedings/97/slides/slides-97-tcpm-draft-ietf-tcpm-rack-01.pdf
https://www.ietf.org/proceedings/96/slides/slides-96-tcpm-3.pdf
Yuchung Cheng (13):
tcp: new helper function for RACK loss detection
tcp: new helper for RACK to detect loss
tcp: record most recent RTT in RACK loss detection
tcp: add reordering timer in RACK loss detection
tcp: use sequence to break TS ties for RACK loss detection
tcp: check undo conditions before detecting losses
tcp: enable RACK loss detection to trigger recovery
tcp: extend F-RTO to catch more spurious timeouts
tcp: remove forward retransmit feature
tcp: remove early retransmit
tcp: remove RFC4653 NCR
tcp: remove thin_dupack feature
tcp: disable fack by default
Documentation/networking/ip-sysctl.txt | 31 +----
include/linux/tcp.h | 8 +-
include/net/inet_connection_sock.h | 4 +-
include/net/tcp.h | 40 ++----
net/ipv4/inet_diag.c | 2 +-
net/ipv4/sysctl_net_ipv4.c | 7 --
net/ipv4/tcp.c | 9 +-
net/ipv4/tcp_input.c | 224 +++++++++++++--------------------
net/ipv4/tcp_ipv4.c | 2 +-
net/ipv4/tcp_metrics.c | 1 -
net/ipv4/tcp_minisocks.c | 1 -
net/ipv4/tcp_output.c | 75 ++---------
net/ipv4/tcp_recovery.c | 148 ++++++++++++++++------
net/ipv4/tcp_timer.c | 4 +-
net/ipv6/tcp_ipv6.c | 2 +-
15 files changed, 237 insertions(+), 321 deletions(-)
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply
* [net-next 01/13] tcp: new helper function for RACK loss detection
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
Create a new helper tcp_rack_mark_skb_lost to prepare the
upcoming RACK reordering timer support.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_recovery.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c
index e36df4fcfeba..f38dba5aed7a 100644
--- a/net/ipv4/tcp_recovery.c
+++ b/net/ipv4/tcp_recovery.c
@@ -3,6 +3,19 @@
int sysctl_tcp_recovery __read_mostly = TCP_RACK_LOST_RETRANS;
+static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ tcp_skb_mark_lost_uncond_verify(tp, skb);
+ if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
+ /* Account for retransmits that are lost again */
+ TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
+ tp->retrans_out -= tcp_skb_pcount(skb);
+ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPLOSTRETRANSMIT);
+ }
+}
+
/* Marks a packet lost, if some packet sent later has been (s)acked.
* The underlying idea is similar to the traditional dupthresh and FACK
* but they look at different metrics:
@@ -61,13 +74,7 @@ int tcp_rack_mark_lost(struct sock *sk)
continue;
/* skb is lost if packet sent later is sacked */
- tcp_skb_mark_lost_uncond_verify(tp, skb);
- if (scb->sacked & TCPCB_SACKED_RETRANS) {
- scb->sacked &= ~TCPCB_SACKED_RETRANS;
- tp->retrans_out -= tcp_skb_pcount(skb);
- NET_INC_STATS(sock_net(sk),
- LINUX_MIB_TCPLOSTRETRANSMIT);
- }
+ tcp_rack_mark_skb_lost(sk, skb);
} else if (!(scb->sacked & TCPCB_RETRANS)) {
/* Original data are sent sequentially so stop early
* b/c the rest are all sent after rack_sent
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 02/13] tcp: new helper for RACK to detect loss
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
Create a new helper tcp_rack_detect_loss to prepare the upcoming
RACK reordering timer patch.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
include/net/tcp.h | 3 +--
net/ipv4/tcp_input.c | 12 ++++++++----
net/ipv4/tcp_recovery.c | 22 +++++++++++++---------
3 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 1da0aa724929..51183bba3835 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1863,8 +1863,7 @@ extern int sysctl_tcp_recovery;
/* Use TCP RACK to detect (some) tail and retransmit losses */
#define TCP_RACK_LOST_RETRANS 0x1
-extern int tcp_rack_mark_lost(struct sock *sk);
-
+extern void tcp_rack_mark_lost(struct sock *sk);
extern void tcp_rack_advance(struct tcp_sock *tp,
const struct skb_mstamp *xmit_time, u8 sacked);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index ec6d84363024..bb24b93e64bc 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2865,10 +2865,14 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
}
/* Use RACK to detect loss */
- if (sysctl_tcp_recovery & TCP_RACK_LOST_RETRANS &&
- tcp_rack_mark_lost(sk)) {
- flag |= FLAG_LOST_RETRANS;
- *ack_flag |= FLAG_LOST_RETRANS;
+ if (sysctl_tcp_recovery & TCP_RACK_LOST_RETRANS) {
+ u32 prior_retrans = tp->retrans_out;
+
+ tcp_rack_mark_lost(sk);
+ if (prior_retrans > tp->retrans_out) {
+ flag |= FLAG_LOST_RETRANS;
+ *ack_flag |= FLAG_LOST_RETRANS;
+ }
}
/* E. Process state. */
diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c
index f38dba5aed7a..7ea0377229c0 100644
--- a/net/ipv4/tcp_recovery.c
+++ b/net/ipv4/tcp_recovery.c
@@ -32,17 +32,11 @@ static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
* The current version is only used after recovery starts but can be
* easily extended to detect the first loss.
*/
-int tcp_rack_mark_lost(struct sock *sk)
+static void tcp_rack_detect_loss(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
- u32 reo_wnd, prior_retrans = tp->retrans_out;
-
- if (inet_csk(sk)->icsk_ca_state < TCP_CA_Recovery || !tp->rack.advanced)
- return 0;
-
- /* Reset the advanced flag to avoid unnecessary queue scanning */
- tp->rack.advanced = 0;
+ u32 reo_wnd;
/* To be more reordering resilient, allow min_rtt/4 settling delay
* (lower-bounded to 1000uS). We use min_rtt instead of the smoothed
@@ -82,7 +76,17 @@ int tcp_rack_mark_lost(struct sock *sk)
break;
}
}
- return prior_retrans - tp->retrans_out;
+}
+
+void tcp_rack_mark_lost(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (inet_csk(sk)->icsk_ca_state < TCP_CA_Recovery || !tp->rack.advanced)
+ return;
+ /* Reset the advanced flag to avoid unnecessary queue scanning */
+ tp->rack.advanced = 0;
+ tcp_rack_detect_loss(sk);
}
/* Record the most recently (re)sent time among the (s)acked packets */
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 03/13] tcp: record most recent RTT in RACK loss detection
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
Record the most recent RTT in RACK. It is often identical to the
"ca_rtt_us" values in tcp_clean_rtx_queue. But when the packet has
been retransmitted, RACK choses to believe the ACK is for the
(latest) retransmitted packet if the RTT is over minimum RTT.
This requires passing the arrival time of the most recent ACK to
RACK routines. The timestamp is now recorded in the "ack_time"
in tcp_sacktag_state during the ACK processing.
This patch does not change the RACK algorithm itself. It only adds
the RTT variable to prepare the next main patch.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
include/linux/tcp.h | 1 +
include/net/tcp.h | 7 ++++---
net/ipv4/tcp_input.c | 36 ++++++++++++++++++++++--------------
net/ipv4/tcp_recovery.c | 41 +++++++++++++++++++++++------------------
4 files changed, 50 insertions(+), 35 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index fc5848dad7a4..1255c592719c 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -207,6 +207,7 @@ struct tcp_sock {
/* Information of the most recently (s)acked skb */
struct tcp_rack {
struct skb_mstamp mstamp; /* (Re)sent time of the skb */
+ u32 rtt_us; /* Associated RTT */
u8 advanced; /* mstamp advanced since last lost marking */
u8 reord; /* reordering detected */
} rack;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 51183bba3835..1439107658c2 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1863,9 +1863,10 @@ extern int sysctl_tcp_recovery;
/* Use TCP RACK to detect (some) tail and retransmit losses */
#define TCP_RACK_LOST_RETRANS 0x1
-extern void tcp_rack_mark_lost(struct sock *sk);
-extern void tcp_rack_advance(struct tcp_sock *tp,
- const struct skb_mstamp *xmit_time, u8 sacked);
+extern void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now);
+extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked,
+ const struct skb_mstamp *xmit_time,
+ const struct skb_mstamp *ack_time);
/*
* Save and compile IPv4 options, return a pointer to it
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index bb24b93e64bc..8ccd171999bf 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1135,6 +1135,7 @@ struct tcp_sacktag_state {
*/
struct skb_mstamp first_sackt;
struct skb_mstamp last_sackt;
+ struct skb_mstamp ack_time; /* Timestamp when the S/ACK was received */
struct rate_sample *rate;
int flag;
};
@@ -1217,7 +1218,7 @@ static u8 tcp_sacktag_one(struct sock *sk,
return sacked;
if (!(sacked & TCPCB_SACKED_ACKED)) {
- tcp_rack_advance(tp, xmit_time, sacked);
+ tcp_rack_advance(tp, sacked, xmit_time, &state->ack_time);
if (sacked & TCPCB_SACKED_RETRANS) {
/* If the segment is not tagged as lost,
@@ -2813,7 +2814,8 @@ static bool tcp_try_undo_partial(struct sock *sk, const int acked)
* tcp_xmit_retransmit_queue().
*/
static void tcp_fastretrans_alert(struct sock *sk, const int acked,
- bool is_dupack, int *ack_flag, int *rexmit)
+ bool is_dupack, int *ack_flag, int *rexmit,
+ const struct skb_mstamp *ack_time)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
@@ -2868,7 +2870,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
if (sysctl_tcp_recovery & TCP_RACK_LOST_RETRANS) {
u32 prior_retrans = tp->retrans_out;
- tcp_rack_mark_lost(sk);
+ tcp_rack_mark_lost(sk, ack_time);
if (prior_retrans > tp->retrans_out) {
flag |= FLAG_LOST_RETRANS;
*ack_flag |= FLAG_LOST_RETRANS;
@@ -3105,11 +3107,11 @@ static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb,
*/
static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
u32 prior_snd_una, int *acked,
- struct tcp_sacktag_state *sack,
- struct skb_mstamp *now)
+ struct tcp_sacktag_state *sack)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
struct skb_mstamp first_ackt, last_ackt;
+ struct skb_mstamp *now = &sack->ack_time;
struct tcp_sock *tp = tcp_sk(sk);
u32 prior_sacked = tp->sacked_out;
u32 reord = tp->packets_out;
@@ -3169,7 +3171,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
} else if (tcp_is_sack(tp)) {
tp->delivered += acked_pcount;
if (!tcp_skb_spurious_retrans(tp, skb))
- tcp_rack_advance(tp, &skb->skb_mstamp, sacked);
+ tcp_rack_advance(tp, sacked,
+ &skb->skb_mstamp,
+ &sack->ack_time);
}
if (sacked & TCPCB_LOST)
tp->lost_out -= acked_pcount;
@@ -3599,7 +3603,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
u32 lost = tp->lost;
int acked = 0; /* Number of packets newly acked */
int rexmit = REXMIT_NONE; /* Flag to (re)transmit to recover losses */
- struct skb_mstamp now;
sack_state.first_sackt.v64 = 0;
sack_state.rate = &rs;
@@ -3625,7 +3628,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
if (after(ack, tp->snd_nxt))
goto invalid_ack;
- skb_mstamp_get(&now);
+ skb_mstamp_get(&sack_state.ack_time);
if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
@@ -3693,11 +3696,12 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
/* See if we can take anything off of the retransmit queue. */
flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una, &acked,
- &sack_state, &now);
+ &sack_state);
if (tcp_ack_is_dubious(sk, flag)) {
is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
- tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit);
+ tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit,
+ &sack_state.ack_time);
}
if (tp->tlp_high_seq)
tcp_process_tlp_ack(sk, ack, flag);
@@ -3712,15 +3716,17 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
tcp_schedule_loss_probe(sk);
delivered = tp->delivered - delivered; /* freshly ACKed or SACKed */
lost = tp->lost - lost; /* freshly marked lost */
- tcp_rate_gen(sk, delivered, lost, &now, &rs);
- tcp_cong_control(sk, ack, delivered, flag, &rs);
+ tcp_rate_gen(sk, delivered, lost, &sack_state.ack_time,
+ sack_state.rate);
+ tcp_cong_control(sk, ack, delivered, flag, sack_state.rate);
tcp_xmit_recovery(sk, rexmit);
return 1;
no_queue:
/* If data was DSACKed, see if we can undo a cwnd reduction. */
if (flag & FLAG_DSACKING_ACK)
- tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit);
+ tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit,
+ &sack_state.ack_time);
/* If this ack opens up a zero window, clear backoff. It was
* being used to time the probes, and is probably far higher than
* it needs to be for normal retransmission.
@@ -3741,9 +3747,11 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
* If data was DSACKed, see if we can undo a cwnd reduction.
*/
if (TCP_SKB_CB(skb)->sacked) {
+ skb_mstamp_get(&sack_state.ack_time);
flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
&sack_state);
- tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit);
+ tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit,
+ &sack_state.ack_time);
tcp_xmit_recovery(sk, rexmit);
}
diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c
index 7ea0377229c0..557363cde58a 100644
--- a/net/ipv4/tcp_recovery.c
+++ b/net/ipv4/tcp_recovery.c
@@ -32,7 +32,7 @@ static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
* The current version is only used after recovery starts but can be
* easily extended to detect the first loss.
*/
-static void tcp_rack_detect_loss(struct sock *sk)
+static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
@@ -62,13 +62,14 @@ static void tcp_rack_detect_loss(struct sock *sk)
continue;
if (skb_mstamp_after(&tp->rack.mstamp, &skb->skb_mstamp)) {
-
- if (skb_mstamp_us_delta(&tp->rack.mstamp,
- &skb->skb_mstamp) <= reo_wnd)
- continue;
-
- /* skb is lost if packet sent later is sacked */
- tcp_rack_mark_skb_lost(sk, skb);
+ /* Step 3 in draft-cheng-tcpm-rack-00.txt:
+ * A packet is lost if its elapsed time is beyond
+ * the recent RTT plus the reordering window.
+ */
+ if (skb_mstamp_us_delta(now, &skb->skb_mstamp) >
+ tp->rack.rtt_us + reo_wnd) {
+ tcp_rack_mark_skb_lost(sk, skb);
+ }
} else if (!(scb->sacked & TCPCB_RETRANS)) {
/* Original data are sent sequentially so stop early
* b/c the rest are all sent after rack_sent
@@ -78,7 +79,7 @@ static void tcp_rack_detect_loss(struct sock *sk)
}
}
-void tcp_rack_mark_lost(struct sock *sk)
+void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -86,20 +87,25 @@ void tcp_rack_mark_lost(struct sock *sk)
return;
/* Reset the advanced flag to avoid unnecessary queue scanning */
tp->rack.advanced = 0;
- tcp_rack_detect_loss(sk);
+ tcp_rack_detect_loss(sk, now);
}
-/* Record the most recently (re)sent time among the (s)acked packets */
-void tcp_rack_advance(struct tcp_sock *tp,
- const struct skb_mstamp *xmit_time, u8 sacked)
+/* Record the most recently (re)sent time among the (s)acked packets
+ * This is "Step 3: Advance RACK.xmit_time and update RACK.RTT" from
+ * draft-cheng-tcpm-rack-00.txt
+ */
+void tcp_rack_advance(struct tcp_sock *tp, u8 sacked,
+ const struct skb_mstamp *xmit_time,
+ const struct skb_mstamp *ack_time)
{
+ u32 rtt_us;
+
if (tp->rack.mstamp.v64 &&
!skb_mstamp_after(xmit_time, &tp->rack.mstamp))
return;
+ rtt_us = skb_mstamp_us_delta(ack_time, xmit_time);
if (sacked & TCPCB_RETRANS) {
- struct skb_mstamp now;
-
/* If the sacked packet was retransmitted, it's ambiguous
* whether the retransmission or the original (or the prior
* retransmission) was sacked.
@@ -110,11 +116,10 @@ void tcp_rack_advance(struct tcp_sock *tp,
* so it's at least one RTT (i.e., retransmission is at least
* an RTT later).
*/
- skb_mstamp_get(&now);
- if (skb_mstamp_us_delta(&now, xmit_time) < tcp_min_rtt(tp))
+ if (rtt_us < tcp_min_rtt(tp))
return;
}
-
+ tp->rack.rtt_us = rtt_us;
tp->rack.mstamp = *xmit_time;
tp->rack.advanced = 1;
}
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 04/13] tcp: add reordering timer in RACK loss detection
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
This patch makes RACK install a reordering timer when it suspects
some packets might be lost, but wants to delay the decision
a little bit to accomodate reordering.
It does not create a new timer but instead repurposes the existing
RTO timer, because both are meant to retransmit packets.
Specifically it arms a timer ICSK_TIME_REO_TIMEOUT when
the RACK timing check fails. The wait time is set to
RACK.RTT + RACK.reo_wnd - (NOW - Packet.xmit_time) + fudge
This translates to expecting a packet (Packet) should take
(RACK.RTT + RACK.reo_wnd + fudge) to deliver after it was sent.
When there are multiple packets that need a timer, we use one timer
with the maximum timeout. Therefore the timer conservatively uses
the maximum window to expire N packets by one timeout, instead of
N timeouts to expire N packets sent at different times.
The fudge factor is 2 jiffies to ensure when the timer fires, all
the suspected packets would exceed the deadline and be marked lost
by tcp_rack_detect_loss(). It has to be at least 1 jiffy because the
clock may tick between calling icsk_reset_xmit_timer(timeout) and
actually hang the timer. The next jiffy is to lower-bound the timeout
to 2 jiffies when reo_wnd is < 1ms.
When the reordering timer fires (tcp_rack_reo_timeout): If we aren't
in Recovery we'll enter fast recovery and force fast retransmit.
This is very similar to the early retransmit (RFC5827) except RACK
is not constrained to only enter recovery for small outstanding
flights.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
include/net/inet_connection_sock.h | 4 ++-
include/net/tcp.h | 4 +++
net/ipv4/inet_diag.c | 1 +
net/ipv4/tcp_input.c | 6 ++--
net/ipv4/tcp_ipv4.c | 1 +
net/ipv4/tcp_output.c | 3 +-
net/ipv4/tcp_recovery.c | 57 +++++++++++++++++++++++++++++++++-----
net/ipv4/tcp_timer.c | 3 ++
net/ipv6/tcp_ipv6.c | 1 +
9 files changed, 68 insertions(+), 12 deletions(-)
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index 85ee3879499e..84b2edde09b1 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -144,6 +144,7 @@ struct inet_connection_sock {
#define ICSK_TIME_PROBE0 3 /* Zero window probe timer */
#define ICSK_TIME_EARLY_RETRANS 4 /* Early retransmit timer */
#define ICSK_TIME_LOSS_PROBE 5 /* Tail loss probe timer */
+#define ICSK_TIME_REO_TIMEOUT 6 /* Reordering timer */
static inline struct inet_connection_sock *inet_csk(const struct sock *sk)
{
@@ -234,7 +235,8 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what,
}
if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0 ||
- what == ICSK_TIME_EARLY_RETRANS || what == ICSK_TIME_LOSS_PROBE) {
+ what == ICSK_TIME_EARLY_RETRANS || what == ICSK_TIME_LOSS_PROBE ||
+ what == ICSK_TIME_REO_TIMEOUT) {
icsk->icsk_pending = what;
icsk->icsk_timeout = jiffies + when;
sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 1439107658c2..64fcdeb3358b 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -143,6 +143,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
#define TCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ/2U)) /* Maximal interval between probes
* for local resources.
*/
+#define TCP_REO_TIMEOUT_MIN (2000) /* Min RACK reordering timeout in usec */
#define TCP_KEEPALIVE_TIME (120*60*HZ) /* two hours */
#define TCP_KEEPALIVE_PROBES 9 /* Max of 9 keepalive probes */
@@ -397,6 +398,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
int tcp_child_process(struct sock *parent, struct sock *child,
struct sk_buff *skb);
void tcp_enter_loss(struct sock *sk);
+void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked, int flag);
void tcp_clear_retrans(struct tcp_sock *tp);
void tcp_update_metrics(struct sock *sk);
void tcp_init_metrics(struct sock *sk);
@@ -541,6 +543,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs);
void tcp_retransmit_timer(struct sock *sk);
void tcp_xmit_retransmit_queue(struct sock *);
void tcp_simple_retransmit(struct sock *);
+void tcp_enter_recovery(struct sock *sk, bool ece_ack);
int tcp_trim_head(struct sock *, struct sk_buff *, u32);
int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int, gfp_t);
@@ -1867,6 +1870,7 @@ extern void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now);
extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked,
const struct skb_mstamp *xmit_time,
const struct skb_mstamp *ack_time);
+extern void tcp_rack_reo_timeout(struct sock *sk);
/*
* Save and compile IPv4 options, return a pointer to it
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 4dea33e5f295..d216e40623d3 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -216,6 +216,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+ icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
r->idiag_timer = 1;
r->idiag_retrans = icsk->icsk_retransmits;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 8ccd171999bf..be1191829963 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2522,8 +2522,7 @@ static void tcp_init_cwnd_reduction(struct sock *sk)
tcp_ecn_queue_cwr(tp);
}
-static void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked,
- int flag)
+void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked, int flag)
{
struct tcp_sock *tp = tcp_sk(sk);
int sndcnt = 0;
@@ -2691,7 +2690,7 @@ void tcp_simple_retransmit(struct sock *sk)
}
EXPORT_SYMBOL(tcp_simple_retransmit);
-static void tcp_enter_recovery(struct sock *sk, bool ece_ack)
+void tcp_enter_recovery(struct sock *sk, bool ece_ack)
{
struct tcp_sock *tp = tcp_sk(sk);
int mib_idx;
@@ -3031,6 +3030,7 @@ void tcp_rearm_rto(struct sock *sk)
u32 rto = inet_csk(sk)->icsk_rto;
/* Offset the time elapsed after installing regular RTO */
if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+ icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
struct sk_buff *skb = tcp_write_queue_head(sk);
const u32 rto_time_stamp =
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 56d756ecfb59..ebf3e0c4967a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2230,6 +2230,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+ icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
timer_active = 1;
timer_expires = icsk->icsk_timeout;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 1d5331a1b1dc..0ba9026cb70d 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2960,7 +2960,8 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
if (tcp_in_cwnd_reduction(sk))
tp->prr_out += tcp_skb_pcount(skb);
- if (skb == tcp_write_queue_head(sk))
+ if (skb == tcp_write_queue_head(sk) &&
+ icsk->icsk_pending != ICSK_TIME_REO_TIMEOUT)
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
inet_csk(sk)->icsk_rto,
TCP_RTO_MAX);
diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c
index 557363cde58a..eb39b1b6d1dc 100644
--- a/net/ipv4/tcp_recovery.c
+++ b/net/ipv4/tcp_recovery.c
@@ -32,19 +32,18 @@ static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
* The current version is only used after recovery starts but can be
* easily extended to detect the first loss.
*/
-static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now)
+static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now,
+ u32 *reo_timeout)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
u32 reo_wnd;
+ *reo_timeout = 0;
/* To be more reordering resilient, allow min_rtt/4 settling delay
* (lower-bounded to 1000uS). We use min_rtt instead of the smoothed
* RTT because reordering is often a path property and less related
* to queuing or delayed ACKs.
- *
- * TODO: measure and adapt to the observed reordering delay, and
- * use a timer to retransmit like the delayed early retransmit.
*/
reo_wnd = 1000;
if (tp->rack.reord && tcp_min_rtt(tp) != ~0U)
@@ -66,10 +65,23 @@ static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now)
* A packet is lost if its elapsed time is beyond
* the recent RTT plus the reordering window.
*/
- if (skb_mstamp_us_delta(now, &skb->skb_mstamp) >
- tp->rack.rtt_us + reo_wnd) {
+ u32 elapsed = skb_mstamp_us_delta(now,
+ &skb->skb_mstamp);
+ s32 remaining = tp->rack.rtt_us + reo_wnd - elapsed;
+
+ if (remaining < 0) {
tcp_rack_mark_skb_lost(sk, skb);
+ continue;
}
+
+ /* Skip ones marked lost but not yet retransmitted */
+ if ((scb->sacked & TCPCB_LOST) &&
+ !(scb->sacked & TCPCB_SACKED_RETRANS))
+ continue;
+
+ /* Record maximum wait time (+1 to avoid 0) */
+ *reo_timeout = max_t(u32, *reo_timeout, 1 + remaining);
+
} else if (!(scb->sacked & TCPCB_RETRANS)) {
/* Original data are sent sequentially so stop early
* b/c the rest are all sent after rack_sent
@@ -82,12 +94,19 @@ static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now)
void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now)
{
struct tcp_sock *tp = tcp_sk(sk);
+ u32 timeout;
if (inet_csk(sk)->icsk_ca_state < TCP_CA_Recovery || !tp->rack.advanced)
return;
+
/* Reset the advanced flag to avoid unnecessary queue scanning */
tp->rack.advanced = 0;
- tcp_rack_detect_loss(sk, now);
+ tcp_rack_detect_loss(sk, now, &timeout);
+ if (timeout) {
+ timeout = usecs_to_jiffies(timeout + TCP_REO_TIMEOUT_MIN);
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_REO_TIMEOUT,
+ timeout, inet_csk(sk)->icsk_rto);
+ }
}
/* Record the most recently (re)sent time among the (s)acked packets
@@ -123,3 +142,27 @@ void tcp_rack_advance(struct tcp_sock *tp, u8 sacked,
tp->rack.mstamp = *xmit_time;
tp->rack.advanced = 1;
}
+
+/* We have waited long enough to accommodate reordering. Mark the expired
+ * packets lost and retransmit them.
+ */
+void tcp_rack_reo_timeout(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct skb_mstamp now;
+ u32 timeout, prior_inflight;
+
+ skb_mstamp_get(&now);
+ prior_inflight = tcp_packets_in_flight(tp);
+ tcp_rack_detect_loss(sk, &now, &timeout);
+ if (prior_inflight != tcp_packets_in_flight(tp)) {
+ if (inet_csk(sk)->icsk_ca_state != TCP_CA_Recovery) {
+ tcp_enter_recovery(sk, false);
+ if (!inet_csk(sk)->icsk_ca_ops->cong_control)
+ tcp_cwnd_reduction(sk, 1, 0);
+ }
+ tcp_xmit_retransmit_queue(sk);
+ }
+ if (inet_csk(sk)->icsk_pending != ICSK_TIME_RETRANS)
+ tcp_rearm_rto(sk);
+}
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 29a9bd5f1225..953c02a8566e 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -563,6 +563,9 @@ void tcp_write_timer_handler(struct sock *sk)
event = icsk->icsk_pending;
switch (event) {
+ case ICSK_TIME_REO_TIMEOUT:
+ tcp_rack_reo_timeout(sk);
+ break;
case ICSK_TIME_EARLY_RETRANS:
tcp_resume_early_retransmit(sk);
break;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 228965dca3c5..f52c3742b404 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1746,6 +1746,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+ icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
timer_active = 1;
timer_expires = icsk->icsk_timeout;
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 05/13] tcp: use sequence to break TS ties for RACK loss detection
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
The packets inside a jumbo skb (e.g., TSO) share the same skb
timestamp, even though they are sent sequentially on the wire. Since
RACK is based on time, it can not detect some packets inside the
same skb are lost. However, we can leverage the packet sequence
numbers as extended timestamps to detect losses. Therefore, when
RACK timestamp is identical to skb's timestamp (i.e., one of the
packets of the skb is acked or sacked), we use the sequence numbers
of the acked and unacked packets to break ties.
We can use the same sequence logic to advance RACK xmit time as
well to detect more losses and avoid timeout.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
include/linux/tcp.h | 1 +
include/net/tcp.h | 2 +-
net/ipv4/tcp_input.c | 5 +++--
net/ipv4/tcp_recovery.c | 17 ++++++++++++++---
4 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 1255c592719c..970d5f00589f 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -208,6 +208,7 @@ struct tcp_sock {
struct tcp_rack {
struct skb_mstamp mstamp; /* (Re)sent time of the skb */
u32 rtt_us; /* Associated RTT */
+ u32 end_seq; /* Ending TCP sequence of the skb */
u8 advanced; /* mstamp advanced since last lost marking */
u8 reord; /* reordering detected */
} rack;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 64fcdeb3358b..5fb1e75a32a9 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1867,7 +1867,7 @@ extern int sysctl_tcp_recovery;
#define TCP_RACK_LOST_RETRANS 0x1
extern void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now);
-extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked,
+extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq,
const struct skb_mstamp *xmit_time,
const struct skb_mstamp *ack_time);
extern void tcp_rack_reo_timeout(struct sock *sk);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index be1191829963..e42ca11c0326 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1218,7 +1218,8 @@ static u8 tcp_sacktag_one(struct sock *sk,
return sacked;
if (!(sacked & TCPCB_SACKED_ACKED)) {
- tcp_rack_advance(tp, sacked, xmit_time, &state->ack_time);
+ tcp_rack_advance(tp, sacked, end_seq,
+ xmit_time, &state->ack_time);
if (sacked & TCPCB_SACKED_RETRANS) {
/* If the segment is not tagged as lost,
@@ -3171,7 +3172,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
} else if (tcp_is_sack(tp)) {
tp->delivered += acked_pcount;
if (!tcp_skb_spurious_retrans(tp, skb))
- tcp_rack_advance(tp, sacked,
+ tcp_rack_advance(tp, sacked, scb->end_seq,
&skb->skb_mstamp,
&sack->ack_time);
}
diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c
index eb39b1b6d1dc..1e330a2f913d 100644
--- a/net/ipv4/tcp_recovery.c
+++ b/net/ipv4/tcp_recovery.c
@@ -16,6 +16,14 @@ static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
}
}
+static bool tcp_rack_sent_after(const struct skb_mstamp *t1,
+ const struct skb_mstamp *t2,
+ u32 seq1, u32 seq2)
+{
+ return skb_mstamp_after(t1, t2) ||
+ (t1->v64 == t2->v64 && after(seq1, seq2));
+}
+
/* Marks a packet lost, if some packet sent later has been (s)acked.
* The underlying idea is similar to the traditional dupthresh and FACK
* but they look at different metrics:
@@ -60,7 +68,8 @@ static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now,
scb->sacked & TCPCB_SACKED_ACKED)
continue;
- if (skb_mstamp_after(&tp->rack.mstamp, &skb->skb_mstamp)) {
+ if (tcp_rack_sent_after(&tp->rack.mstamp, &skb->skb_mstamp,
+ tp->rack.end_seq, scb->end_seq)) {
/* Step 3 in draft-cheng-tcpm-rack-00.txt:
* A packet is lost if its elapsed time is beyond
* the recent RTT plus the reordering window.
@@ -113,14 +122,15 @@ void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now)
* This is "Step 3: Advance RACK.xmit_time and update RACK.RTT" from
* draft-cheng-tcpm-rack-00.txt
*/
-void tcp_rack_advance(struct tcp_sock *tp, u8 sacked,
+void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq,
const struct skb_mstamp *xmit_time,
const struct skb_mstamp *ack_time)
{
u32 rtt_us;
if (tp->rack.mstamp.v64 &&
- !skb_mstamp_after(xmit_time, &tp->rack.mstamp))
+ !tcp_rack_sent_after(xmit_time, &tp->rack.mstamp,
+ end_seq, tp->rack.end_seq))
return;
rtt_us = skb_mstamp_us_delta(ack_time, xmit_time);
@@ -140,6 +150,7 @@ void tcp_rack_advance(struct tcp_sock *tp, u8 sacked,
}
tp->rack.rtt_us = rtt_us;
tp->rack.mstamp = *xmit_time;
+ tp->rack.end_seq = end_seq;
tp->rack.advanced = 1;
}
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 06/13] tcp: check undo conditions before detecting losses
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
Currently RACK would mark loss before the undo operations in TCP
loss recovery. This could incorrectly identify real losses as
spurious. For example a sender first experiences a delay spike and
then eventually some packets were lost due to buffer overrun.
In this case, the sender should perform fast recovery b/c not all
the packets were lost.
But the sender may first trigger a (spurious) RTO and reset
cwnd to 1. The following ACKs may used to mark real losses by
tcp_rack_mark_lost. Then in tcp_process_loss this ACK could trigger
F-RTO undo condition and unmark real losses and revert the cwnd
reduction. If there are no more ACKs coming back, eventually the
sender would timeout again instead of performing fast recovery.
The patch fixes this incorrect process by always performing
the undo checks before detecting losses.
Fixes: 4f41b1c58a32 ("tcp: use RACK to detect losses")
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_input.c | 33 ++++++++++++++++++++-------------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index e42ca11c0326..9c98dc874825 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2801,6 +2801,21 @@ static bool tcp_try_undo_partial(struct sock *sk, const int acked)
return false;
}
+static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag,
+ const struct skb_mstamp *ack_time)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ /* Use RACK to detect loss */
+ if (sysctl_tcp_recovery & TCP_RACK_LOST_RETRANS) {
+ u32 prior_retrans = tp->retrans_out;
+
+ tcp_rack_mark_lost(sk, ack_time);
+ if (prior_retrans > tp->retrans_out)
+ *ack_flag |= FLAG_LOST_RETRANS;
+ }
+}
+
/* Process an event, which can update packets-in-flight not trivially.
* Main goal of this function is to calculate new estimate for left_out,
* taking into account both packets sitting in receiver's buffer and
@@ -2866,17 +2881,6 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
}
}
- /* Use RACK to detect loss */
- if (sysctl_tcp_recovery & TCP_RACK_LOST_RETRANS) {
- u32 prior_retrans = tp->retrans_out;
-
- tcp_rack_mark_lost(sk, ack_time);
- if (prior_retrans > tp->retrans_out) {
- flag |= FLAG_LOST_RETRANS;
- *ack_flag |= FLAG_LOST_RETRANS;
- }
- }
-
/* E. Process state. */
switch (icsk->icsk_ca_state) {
case TCP_CA_Recovery:
@@ -2894,11 +2898,13 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
tcp_try_keep_open(sk);
return;
}
+ tcp_rack_identify_loss(sk, ack_flag, ack_time);
break;
case TCP_CA_Loss:
tcp_process_loss(sk, flag, is_dupack, rexmit);
- if (icsk->icsk_ca_state != TCP_CA_Open &&
- !(flag & FLAG_LOST_RETRANS))
+ tcp_rack_identify_loss(sk, ack_flag, ack_time);
+ if (!(icsk->icsk_ca_state == TCP_CA_Open ||
+ (*ack_flag & FLAG_LOST_RETRANS)))
return;
/* Change state if cwnd is undone or retransmits are lost */
default:
@@ -2912,6 +2918,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
if (icsk->icsk_ca_state <= TCP_CA_Disorder)
tcp_try_undo_dsack(sk);
+ tcp_rack_identify_loss(sk, ack_flag, ack_time);
if (!tcp_time_to_recover(sk, flag)) {
tcp_try_to_open(sk, flag);
return;
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 07/13] tcp: enable RACK loss detection to trigger recovery
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
This patch changes two things:
1. Start fast recovery with RACK in addition to other heuristics
(e.g., DUPACK threshold, FACK). Prior to this change RACK
is enabled to detect losses only after the recovery has
started by other algorithms.
2. Disable TCP early retransmit. RACK subsumes the early retransmit
with the new reordering timer feature. A latter patch in this
series removes the early retransmit code.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
include/net/tcp.h | 11 ++++-------
net/ipv4/tcp_input.c | 29 +++++++++++++++++++++--------
net/ipv4/tcp_recovery.c | 16 ++++++++++------
3 files changed, 35 insertions(+), 21 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 5fb1e75a32a9..423438dd6fe9 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -262,6 +262,9 @@ extern int sysctl_tcp_slow_start_after_idle;
extern int sysctl_tcp_thin_linear_timeouts;
extern int sysctl_tcp_thin_dupack;
extern int sysctl_tcp_early_retrans;
+extern int sysctl_tcp_recovery;
+#define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */
+
extern int sysctl_tcp_limit_output_bytes;
extern int sysctl_tcp_challenge_ack_limit;
extern int sysctl_tcp_min_tso_segs;
@@ -1043,6 +1046,7 @@ static inline void tcp_enable_early_retrans(struct tcp_sock *tp)
tp->do_early_retrans = sysctl_tcp_early_retrans &&
sysctl_tcp_early_retrans < 4 && !sysctl_tcp_thin_dupack &&
+ !(sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION) &&
net->ipv4.sysctl_tcp_reordering == 3;
}
@@ -1859,13 +1863,6 @@ void tcp_v4_init(void);
void tcp_init(void);
/* tcp_recovery.c */
-
-/* Flags to enable various loss recovery features. See below */
-extern int sysctl_tcp_recovery;
-
-/* Use TCP RACK to detect (some) tail and retransmit losses */
-#define TCP_RACK_LOST_RETRANS 0x1
-
extern void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now);
extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq,
const struct skb_mstamp *xmit_time,
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 9c98dc874825..4ad75b8c4fee 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2129,10 +2129,25 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag)
* F.e. after RTO, when all the queue is considered as lost,
* lost_out = packets_out and in_flight = retrans_out.
*
- * Essentially, we have now two algorithms counting
+ * Essentially, we have now a few algorithms detecting
* lost packets.
*
- * FACK: It is the simplest heuristics. As soon as we decided
+ * If the receiver supports SACK:
+ *
+ * RFC6675/3517: It is the conventional algorithm. A packet is
+ * considered lost if the number of higher sequence packets
+ * SACKed is greater than or equal the DUPACK thoreshold
+ * (reordering). This is implemented in tcp_mark_head_lost and
+ * tcp_update_scoreboard.
+ *
+ * RACK (draft-ietf-tcpm-rack-01): it is a newer algorithm
+ * (2017-) that checks timing instead of counting DUPACKs.
+ * Essentially a packet is considered lost if it's not S/ACKed
+ * after RTT + reordering_window, where both metrics are
+ * dynamically measured and adjusted. This is implemented in
+ * tcp_rack_mark_lost.
+ *
+ * FACK: it is the simplest heuristics. As soon as we decided
* that something is lost, we decide that _all_ not SACKed
* packets until the most forward SACK are lost. I.e.
* lost_out = fackets_out - sacked_out and left_out = fackets_out.
@@ -2141,16 +2156,14 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag)
* takes place. We use FACK by default until reordering
* is suspected on the path to this destination.
*
- * NewReno: when Recovery is entered, we assume that one segment
+ * If the receiver does not support SACK:
+ *
+ * NewReno (RFC6582): in Recovery we assume that one segment
* is lost (classic Reno). While we are in Recovery and
* a partial ACK arrives, we assume that one more packet
* is lost (NewReno). This heuristics are the same in NewReno
* and SACK.
*
- * Imagine, that's all! Forget about all this shamanism about CWND inflation
- * deflation etc. CWND is real congestion window, never inflated, changes
- * only according to classic VJ rules.
- *
* Really tricky (and requiring careful tuning) part of algorithm
* is hidden in functions tcp_time_to_recover() and tcp_xmit_retransmit_queue().
* The first determines the moment _when_ we should reduce CWND and,
@@ -2807,7 +2820,7 @@ static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag,
struct tcp_sock *tp = tcp_sk(sk);
/* Use RACK to detect loss */
- if (sysctl_tcp_recovery & TCP_RACK_LOST_RETRANS) {
+ if (sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION) {
u32 prior_retrans = tp->retrans_out;
tcp_rack_mark_lost(sk, ack_time);
diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c
index 1e330a2f913d..4ecb38ae8504 100644
--- a/net/ipv4/tcp_recovery.c
+++ b/net/ipv4/tcp_recovery.c
@@ -1,7 +1,7 @@
#include <linux/tcp.h>
#include <net/tcp.h>
-int sysctl_tcp_recovery __read_mostly = TCP_RACK_LOST_RETRANS;
+int sysctl_tcp_recovery __read_mostly = TCP_RACK_LOSS_DETECTION;
static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
{
@@ -24,7 +24,9 @@ static bool tcp_rack_sent_after(const struct skb_mstamp *t1,
(t1->v64 == t2->v64 && after(seq1, seq2));
}
-/* Marks a packet lost, if some packet sent later has been (s)acked.
+/* RACK loss detection (IETF draft draft-ietf-tcpm-rack-01):
+ *
+ * Marks a packet lost, if some packet sent later has been (s)acked.
* The underlying idea is similar to the traditional dupthresh and FACK
* but they look at different metrics:
*
@@ -37,8 +39,10 @@ static bool tcp_rack_sent_after(const struct skb_mstamp *t1,
* is being more resilient to reordering by simply allowing some
* "settling delay", instead of tweaking the dupthresh.
*
- * The current version is only used after recovery starts but can be
- * easily extended to detect the first loss.
+ * When tcp_rack_detect_loss() detects some packets are lost and we
+ * are not already in the CA_Recovery state, either tcp_rack_reo_timeout()
+ * or tcp_time_to_recover()'s "Trick#1: the loss is proven" code path will
+ * make us enter the CA_Recovery state.
*/
static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now,
u32 *reo_timeout)
@@ -54,7 +58,7 @@ static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now,
* to queuing or delayed ACKs.
*/
reo_wnd = 1000;
- if (tp->rack.reord && tcp_min_rtt(tp) != ~0U)
+ if ((tp->rack.reord || !tp->lost_out) && tcp_min_rtt(tp) != ~0U)
reo_wnd = max(tcp_min_rtt(tp) >> 2, reo_wnd);
tcp_for_write_queue(skb, sk) {
@@ -105,7 +109,7 @@ void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now)
struct tcp_sock *tp = tcp_sk(sk);
u32 timeout;
- if (inet_csk(sk)->icsk_ca_state < TCP_CA_Recovery || !tp->rack.advanced)
+ if (!tp->rack.advanced)
return;
/* Reset the advanced flag to avoid unnecessary queue scanning */
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 08/13] tcp: extend F-RTO to catch more spurious timeouts
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
Current F-RTO reverts cwnd reset whenever a never-retransmitted
packet was (s)acked. The timeout can be declared spurious because
the packets acknoledged with this ACK was transmitted before the
timeout, so clearly not all the packets are lost to reset the cwnd.
This nice detection does not really depend F-RTO internals. This
patch applies the detection universally. On Google servers this
change detected 20% more spurious timeouts.
Suggested-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_input.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 4ad75b8c4fee..9469ce384d3b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1939,7 +1939,6 @@ void tcp_enter_loss(struct sock *sk)
struct tcp_sock *tp = tcp_sk(sk);
struct net *net = sock_net(sk);
struct sk_buff *skb;
- bool new_recovery = icsk->icsk_ca_state < TCP_CA_Recovery;
bool is_reneg; /* is receiver reneging on SACKs? */
bool mark_lost;
@@ -2000,13 +1999,15 @@ void tcp_enter_loss(struct sock *sk)
tp->high_seq = tp->snd_nxt;
tcp_ecn_queue_cwr(tp);
- /* F-RTO RFC5682 sec 3.1 step 1: retransmit SND.UNA if no previous
- * loss recovery is underway except recurring timeout(s) on
- * the same SND.UNA (sec 3.2). Disable F-RTO on path MTU probing
+ /* F-RTO RFC5682 sec 3.1 step 1 mandates to disable F-RTO
+ * if a previous recovery is underway, otherwise it may incorrectly
+ * call a timeout spurious if some previously retransmitted packets
+ * are s/acked (sec 3.2). We do not apply that retriction since
+ * retransmitted skbs are permanently tagged with TCPCB_EVER_RETRANS
+ * so FLAG_ORIG_SACK_ACKED is always correct. But we do disable F-RTO
+ * on PTMU discovery to avoid sending new data.
*/
- tp->frto = sysctl_tcp_frto &&
- (new_recovery || icsk->icsk_retransmits) &&
- !inet_csk(sk)->icsk_mtup.probe_size;
+ tp->frto = sysctl_tcp_frto && !inet_csk(sk)->icsk_mtup.probe_size;
}
/* If ACK arrived pointing to a remembered SACK, it means that our
@@ -2740,14 +2741,18 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack,
tcp_try_undo_loss(sk, false))
return;
- if (tp->frto) { /* F-RTO RFC5682 sec 3.1 (sack enhanced version). */
- /* Step 3.b. A timeout is spurious if not all data are
- * lost, i.e., never-retransmitted data are (s)acked.
- */
- if ((flag & FLAG_ORIG_SACK_ACKED) &&
- tcp_try_undo_loss(sk, true))
- return;
+ /* The ACK (s)acks some never-retransmitted data meaning not all
+ * the data packets before the timeout were lost. Therefore we
+ * undo the congestion window and state. This is essentially
+ * the operation in F-RTO (RFC5682 section 3.1 step 3.b). Since
+ * a retransmitted skb is permantly marked, we can apply such an
+ * operation even if F-RTO was not used.
+ */
+ if ((flag & FLAG_ORIG_SACK_ACKED) &&
+ tcp_try_undo_loss(sk, tp->undo_marker))
+ return;
+ if (tp->frto) { /* F-RTO RFC5682 sec 3.1 (sack enhanced version). */
if (after(tp->snd_nxt, tp->high_seq)) {
if (flag & FLAG_DATA_SACKED || is_dupack)
tp->frto = 0; /* Step 3.a. loss was real */
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 09/13] tcp: remove forward retransmit feature
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
Forward retransmit is an esoteric feature in RFC3517 (condition(3)
in the NextSeg()). Basically if a packet is not considered lost by
the current criteria (# of dupacks etc), but the congestion window
has room for more packets, then retransmit this packet.
However it actually conflicts with the rest of recovery design. For
example, when reordering is detected we want to be conservative
in retransmitting packets but forward-retransmit feature would
break that to force more retransmission. Also the implementation is
fairly complicated inside the retransmission logic inducing extra
iterations in the write queue. With RACK losses are being detected
timely and this heuristic is no longer necessary. There this patch
removes the feature.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
include/linux/tcp.h | 1 -
net/ipv4/tcp_input.c | 5 -----
net/ipv4/tcp_output.c | 61 +++------------------------------------------------
3 files changed, 3 insertions(+), 64 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 970d5f00589f..8e5f4c15d0e5 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -307,7 +307,6 @@ struct tcp_sock {
*/
int lost_cnt_hint;
- u32 retransmit_high; /* L-bits may be on up to this seqno */
u32 prior_ssthresh; /* ssthresh saved at recovery start */
u32 high_seq; /* snd_nxt at onset of congestion */
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 9469ce384d3b..a041a92348ee 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -916,10 +916,6 @@ static void tcp_verify_retransmit_hint(struct tcp_sock *tp, struct sk_buff *skb)
before(TCP_SKB_CB(skb)->seq,
TCP_SKB_CB(tp->retransmit_skb_hint)->seq))
tp->retransmit_skb_hint = skb;
-
- if (!tp->lost_out ||
- after(TCP_SKB_CB(skb)->end_seq, tp->retransmit_high))
- tp->retransmit_high = TCP_SKB_CB(skb)->end_seq;
}
/* Sum the number of packets on the wire we have marked as lost.
@@ -1983,7 +1979,6 @@ void tcp_enter_loss(struct sock *sk)
TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_ACKED;
TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
tp->lost_out += tcp_skb_pcount(skb);
- tp->retransmit_high = TCP_SKB_CB(skb)->end_seq;
}
}
tcp_verify_left_out(tp);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 0ba9026cb70d..6327e4d368a4 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2831,36 +2831,6 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
return err;
}
-/* Check if we forward retransmits are possible in the current
- * window/congestion state.
- */
-static bool tcp_can_forward_retransmit(struct sock *sk)
-{
- const struct inet_connection_sock *icsk = inet_csk(sk);
- const struct tcp_sock *tp = tcp_sk(sk);
-
- /* Forward retransmissions are possible only during Recovery. */
- if (icsk->icsk_ca_state != TCP_CA_Recovery)
- return false;
-
- /* No forward retransmissions in Reno are possible. */
- if (tcp_is_reno(tp))
- return false;
-
- /* Yeah, we have to make difficult choice between forward transmission
- * and retransmission... Both ways have their merits...
- *
- * For now we do not retransmit anything, while we have some new
- * segments to send. In the other cases, follow rule 3 for
- * NextSeg() specified in RFC3517.
- */
-
- if (tcp_may_send_now(sk))
- return false;
-
- return true;
-}
-
/* This gets called after a retransmit timeout, and the initially
* retransmitted data is acknowledged. It tries to continue
* resending the rest of the retransmit queue, until either
@@ -2875,24 +2845,16 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
struct sk_buff *hole = NULL;
- u32 max_segs, last_lost;
+ u32 max_segs;
int mib_idx;
- int fwd_rexmitting = 0;
if (!tp->packets_out)
return;
- if (!tp->lost_out)
- tp->retransmit_high = tp->snd_una;
-
if (tp->retransmit_skb_hint) {
skb = tp->retransmit_skb_hint;
- last_lost = TCP_SKB_CB(skb)->end_seq;
- if (after(last_lost, tp->retransmit_high))
- last_lost = tp->retransmit_high;
} else {
skb = tcp_write_queue_head(sk);
- last_lost = tp->snd_una;
}
max_segs = tcp_tso_segs(sk, tcp_current_mss(sk));
@@ -2915,31 +2877,14 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
*/
segs = min_t(int, segs, max_segs);
- if (fwd_rexmitting) {
-begin_fwd:
- if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp)))
- break;
- mib_idx = LINUX_MIB_TCPFORWARDRETRANS;
-
- } else if (!before(TCP_SKB_CB(skb)->seq, tp->retransmit_high)) {
- tp->retransmit_high = last_lost;
- if (!tcp_can_forward_retransmit(sk))
- break;
- /* Backtrack if necessary to non-L'ed skb */
- if (hole) {
- skb = hole;
- hole = NULL;
- }
- fwd_rexmitting = 1;
- goto begin_fwd;
-
+ if (tp->retrans_out >= tp->lost_out) {
+ break;
} else if (!(sacked & TCPCB_LOST)) {
if (!hole && !(sacked & (TCPCB_SACKED_RETRANS|TCPCB_SACKED_ACKED)))
hole = skb;
continue;
} else {
- last_lost = TCP_SKB_CB(skb)->end_seq;
if (icsk->icsk_ca_state != TCP_CA_Loss)
mib_idx = LINUX_MIB_TCPFASTRETRANS;
else
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 10/13] tcp: remove early retransmit
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
This patch removes the support of RFC5827 early retransmit (i.e.,
fast recovery on small inflight with <3 dupacks) because it is
subsumed by the new RACK loss detection. More specifically when
RACK receives DUPACKs, it'll arm a reordering timer to start fast
recovery after a quarter of (min)RTT, hence it covers the early
retransmit except RACK does not limit itself to specific inflight
or dupack numbers.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
Documentation/networking/ip-sysctl.txt | 19 +++--------
include/linux/tcp.h | 3 +-
include/net/tcp.h | 19 -----------
net/ipv4/inet_diag.c | 1 -
net/ipv4/tcp.c | 3 --
net/ipv4/tcp_input.c | 60 ++--------------------------------
net/ipv4/tcp_ipv4.c | 1 -
net/ipv4/tcp_metrics.c | 1 -
net/ipv4/tcp_minisocks.c | 1 -
net/ipv4/tcp_output.c | 11 +++----
net/ipv4/tcp_timer.c | 3 --
net/ipv6/tcp_ipv6.c | 1 -
12 files changed, 12 insertions(+), 111 deletions(-)
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 7dd65c9cf707..7de2cf79e16f 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -246,21 +246,12 @@ tcp_dsack - BOOLEAN
Allows TCP to send "duplicate" SACKs.
tcp_early_retrans - INTEGER
- Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold
- for triggering fast retransmit when the amount of outstanding data is
- small and when no previously unsent data can be transmitted (such
- that limited transmit could be used). Also controls the use of
- Tail loss probe (TLP) that converts RTOs occurring due to tail
- losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01).
+ Tail loss probe (TLP) converts RTOs occurring due to tail
+ losses into fast recovery (draft-ietf-tcpm-rack). Note that
+ TLP requires RACK to function properly (see tcp_recovery below)
Possible values:
- 0 disables ER
- 1 enables ER
- 2 enables ER but delays fast recovery and fast retransmit
- by a fourth of RTT. This mitigates connection falsely
- recovers when network has a small degree of reordering
- (less than 3 packets).
- 3 enables delayed ER and TLP.
- 4 enables TLP only.
+ 0 disables TLP
+ 3 or 4 enables TLP
Default: 3
tcp_ecn - INTEGER
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 8e5f4c15d0e5..4733368f953a 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -224,8 +224,7 @@ struct tcp_sock {
repair : 1,
frto : 1;/* F-RTO (RFC5682) activated in CA_Loss */
u8 repair_queue;
- u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */
- syn_data:1, /* SYN includes data */
+ u8 syn_data:1, /* SYN includes data */
syn_fastopen:1, /* SYN includes Fast Open option */
syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */
syn_data_acked:1,/* data in SYN is acked by SYN-ACK */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 423438dd6fe9..c55d65f74f7f 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -565,7 +565,6 @@ void tcp_skb_collapse_tstamp(struct sk_buff *skb,
const struct sk_buff *next_skb);
/* tcp_input.c */
-void tcp_resume_early_retransmit(struct sock *sk);
void tcp_rearm_rto(struct sock *sk);
void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req);
void tcp_reset(struct sock *sk);
@@ -1037,24 +1036,6 @@ static inline void tcp_enable_fack(struct tcp_sock *tp)
tp->rx_opt.sack_ok |= TCP_FACK_ENABLED;
}
-/* TCP early-retransmit (ER) is similar to but more conservative than
- * the thin-dupack feature. Enable ER only if thin-dupack is disabled.
- */
-static inline void tcp_enable_early_retrans(struct tcp_sock *tp)
-{
- struct net *net = sock_net((struct sock *)tp);
-
- tp->do_early_retrans = sysctl_tcp_early_retrans &&
- sysctl_tcp_early_retrans < 4 && !sysctl_tcp_thin_dupack &&
- !(sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION) &&
- net->ipv4.sysctl_tcp_reordering == 3;
-}
-
-static inline void tcp_disable_early_retrans(struct tcp_sock *tp)
-{
- tp->do_early_retrans = 0;
-}
-
static inline unsigned int tcp_left_out(const struct tcp_sock *tp)
{
return tp->sacked_out + tp->lost_out;
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index d216e40623d3..3828b3a805cd 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -215,7 +215,6 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
}
if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
- icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
r->idiag_timer = 1;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c8d46c140b4a..d9023e8ed53e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -406,7 +406,6 @@ void tcp_init_sock(struct sock *sk)
tp->mss_cache = TCP_MSS_DEFAULT;
tp->reordering = sock_net(sk)->ipv4.sysctl_tcp_reordering;
- tcp_enable_early_retrans(tp);
tcp_assign_congestion_control(sk);
tp->tsoffset = 0;
@@ -2477,8 +2476,6 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
err = -EINVAL;
else {
tp->thin_dupack = val;
- if (tp->thin_dupack)
- tcp_disable_early_retrans(tp);
}
break;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index a041a92348ee..79c819077a59 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -904,8 +904,6 @@ static void tcp_update_reordering(struct sock *sk, const int metric,
tcp_disable_fack(tp);
}
- if (metric > 0)
- tcp_disable_early_retrans(tp);
tp->rack.reord = 1;
}
@@ -2054,30 +2052,6 @@ static inline int tcp_dupack_heuristics(const struct tcp_sock *tp)
return tcp_is_fack(tp) ? tp->fackets_out : tp->sacked_out + 1;
}
-static bool tcp_pause_early_retransmit(struct sock *sk, int flag)
-{
- struct tcp_sock *tp = tcp_sk(sk);
- unsigned long delay;
-
- /* Delay early retransmit and entering fast recovery for
- * max(RTT/4, 2msec) unless ack has ECE mark, no RTT samples
- * available, or RTO is scheduled to fire first.
- */
- if (sysctl_tcp_early_retrans < 2 || sysctl_tcp_early_retrans > 3 ||
- (flag & FLAG_ECE) || !tp->srtt_us)
- return false;
-
- delay = max(usecs_to_jiffies(tp->srtt_us >> 5),
- msecs_to_jiffies(2));
-
- if (!time_after(inet_csk(sk)->icsk_timeout, (jiffies + delay)))
- return false;
-
- inet_csk_reset_xmit_timer(sk, ICSK_TIME_EARLY_RETRANS, delay,
- TCP_RTO_MAX);
- return true;
-}
-
/* Linux NewReno/SACK/FACK/ECN state machine.
* --------------------------------------
*
@@ -2221,16 +2195,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
tcp_is_sack(tp) && !tcp_send_head(sk))
return true;
- /* Trick#6: TCP early retransmit, per RFC5827. To avoid spurious
- * retransmissions due to small network reorderings, we implement
- * Mitigation A.3 in the RFC and delay the retransmission for a short
- * interval if appropriate.
- */
- if (tp->do_early_retrans && !tp->retrans_out && tp->sacked_out &&
- (tp->packets_out >= (tp->sacked_out + 1) && tp->packets_out < 4) &&
- !tcp_may_send_now(sk))
- return !tcp_pause_early_retransmit(sk, flag);
-
return false;
}
@@ -3050,8 +3014,7 @@ void tcp_rearm_rto(struct sock *sk)
} else {
u32 rto = inet_csk(sk)->icsk_rto;
/* Offset the time elapsed after installing regular RTO */
- if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
- icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
+ if (icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
struct sk_buff *skb = tcp_write_queue_head(sk);
const u32 rto_time_stamp =
@@ -3068,24 +3031,6 @@ void tcp_rearm_rto(struct sock *sk)
}
}
-/* This function is called when the delayed ER timer fires. TCP enters
- * fast recovery and performs fast-retransmit.
- */
-void tcp_resume_early_retransmit(struct sock *sk)
-{
- struct tcp_sock *tp = tcp_sk(sk);
-
- tcp_rearm_rto(sk);
-
- /* Stop if ER is disabled after the delayed ER timer is scheduled */
- if (!tp->do_early_retrans)
- return;
-
- tcp_enter_recovery(sk, false);
- tcp_update_scoreboard(sk, 1);
- tcp_xmit_retransmit_queue(sk);
-}
-
/* If we get here, the whole TSO packet has not been acked. */
static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb)
{
@@ -3651,8 +3596,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
skb_mstamp_get(&sack_state.ack_time);
- if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
- icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
+ if (icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
tcp_rearm_rto(sk);
if (after(ack, prior_snd_una)) {
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index ebf3e0c4967a..63214136cf1c 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2229,7 +2229,6 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
int state;
if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
- icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
timer_active = 1;
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index ba8f02d0f283..b9ed0d50aead 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -522,7 +522,6 @@ void tcp_init_metrics(struct sock *sk)
val = tcp_metric_get(tm, TCP_METRIC_REORDERING);
if (val && tp->reordering != val) {
tcp_disable_fack(tp);
- tcp_disable_early_retrans(tp);
tp->reordering = val;
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 06fde26a82b7..bdb443471c39 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -468,7 +468,6 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
newtp->sacked_out = 0;
newtp->fackets_out = 0;
newtp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
- tcp_enable_early_retrans(newtp);
newtp->tlp_high_seq = 0;
newtp->lsndtime = treq->snt_synack.stamp_jiffies;
newsk->sk_txhash = treq->txhash;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 6327e4d368a4..9a1a1494b9dd 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -76,10 +76,8 @@ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb)
tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
tp->packets_out += tcp_skb_pcount(skb);
- if (!prior_packets || icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
- icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
+ if (!prior_packets || icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
tcp_rearm_rto(sk);
- }
NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT,
tcp_skb_pcount(skb));
@@ -2289,8 +2287,6 @@ bool tcp_schedule_loss_probe(struct sock *sk)
u32 timeout, tlp_time_stamp, rto_time_stamp;
u32 rtt = usecs_to_jiffies(tp->srtt_us >> 3);
- if (WARN_ON(icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS))
- return false;
/* No consecutive loss probes. */
if (WARN_ON(icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)) {
tcp_rearm_rto(sk);
@@ -2309,8 +2305,9 @@ bool tcp_schedule_loss_probe(struct sock *sk)
/* Schedule a loss probe in 2*RTT for SACK capable connections
* in Open state, that are either limited by cwnd or application.
*/
- if (sysctl_tcp_early_retrans < 3 || !tp->packets_out ||
- !tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open)
+ if ((sysctl_tcp_early_retrans != 3 && sysctl_tcp_early_retrans != 4) ||
+ !tp->packets_out || !tcp_is_sack(tp) ||
+ icsk->icsk_ca_state != TCP_CA_Open)
return false;
if ((tp->snd_cwnd > tcp_packets_in_flight(tp)) &&
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 953c02a8566e..40d893556e67 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -566,9 +566,6 @@ void tcp_write_timer_handler(struct sock *sk)
case ICSK_TIME_REO_TIMEOUT:
tcp_rack_reo_timeout(sk);
break;
- case ICSK_TIME_EARLY_RETRANS:
- tcp_resume_early_retransmit(sk);
- break;
case ICSK_TIME_LOSS_PROBE:
tcp_send_loss_probe(sk);
break;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f52c3742b404..fc14e04028bf 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1745,7 +1745,6 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
srcp = ntohs(inet->inet_sport);
if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
- icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
timer_active = 1;
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 11/13] tcp: remove RFC4653 NCR
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
This patch removes the (partial) implementation of the aggressive
limited transmit in RFC4653 TCP Non-Congestion Robustness (NCR).
NCR is a mitigation to the problem created by the dynamic
DUPACK threshold. With the current adaptive DUPACK threshold
(tp->reordering) could cause timeouts by preventing fast recovery.
For example, if the last packet of a cwnd burst was reordered, the
threshold will be set to the size of cwnd. But if next application
burst is smaller than threshold and has drops instead of reorderings,
the sender would not trigger fast recovery but instead resorts to a
timeout recovery.
NCR mitigates this issue by checking the number of DUPACKs against
the current flight size additionally. The techniqueue is similar to
the early retransmit RFC.
With RACK loss detection, this mitigation is not needed, because RACK
does not use DUPACK threshold to detect losses. RACK arms a reordering
timer to fire at most a quarter RTT later to start fast recovery.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_input.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 79c819077a59..87315ab1ab1a 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2161,8 +2161,6 @@ static inline int tcp_dupack_heuristics(const struct tcp_sock *tp)
static bool tcp_time_to_recover(struct sock *sk, int flag)
{
struct tcp_sock *tp = tcp_sk(sk);
- __u32 packets_out;
- int tcp_reordering = sock_net(sk)->ipv4.sysctl_tcp_reordering;
/* Trick#1: The loss is proven. */
if (tp->lost_out)
@@ -2172,19 +2170,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
if (tcp_dupack_heuristics(tp) > tp->reordering)
return true;
- /* Trick#4: It is still not OK... But will it be useful to delay
- * recovery more?
- */
- packets_out = tp->packets_out;
- if (packets_out <= tp->reordering &&
- tp->sacked_out >= max_t(__u32, packets_out/2, tcp_reordering) &&
- !tcp_may_send_now(sk)) {
- /* We have nothing to send. This connection is limited
- * either by receiver window or by application.
- */
- return true;
- }
-
/* If a thin stream is detected, retransmit after first
* received dupack. Employ only if SACK is supported in order
* to avoid possible corner-case series of spurious retransmissions
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 12/13] tcp: remove thin_dupack feature
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
Thin stream DUPACK is to start fast recovery on only one DUPACK
provided the connection is a thin stream (i.e., low inflight). But
this older feature is now subsumed with RACK. If a connection
receives only a single DUPACK, RACK would arm a reordering timer
and soon starts fast recovery instead of timeout if no further
ACKs are received.
The socket option (THIN_DUPACK) is kept as a nop for compatibility.
Note that this patch does not change another thin-stream feature
which enables linear RTO. Although it might be good to generalize
that in the future (i.e., linear RTO for the first say 3 retries).
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
Documentation/networking/ip-sysctl.txt | 12 ------------
include/linux/tcp.h | 2 +-
net/ipv4/sysctl_net_ipv4.c | 7 -------
net/ipv4/tcp.c | 6 ++----
net/ipv4/tcp_input.c | 13 -------------
5 files changed, 3 insertions(+), 37 deletions(-)
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 7de2cf79e16f..aa1bb49f1dc6 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -703,18 +703,6 @@ tcp_thin_linear_timeouts - BOOLEAN
Documentation/networking/tcp-thin.txt
Default: 0
-tcp_thin_dupack - BOOLEAN
- Enable dynamic triggering of retransmissions after one dupACK
- for thin streams. If set, a check is performed upon reception
- of a dupACK to determine if the stream is thin (less than 4
- packets in flight). As long as the stream is found to be thin,
- data is retransmitted on the first received dupACK. This
- improves retransmission latency for non-aggressive thin
- streams, often found to be time-dependent.
- For more information on thin streams, see
- Documentation/networking/tcp-thin.txt
- Default: 0
-
tcp_limit_output_bytes - INTEGER
Controls TCP Small Queue limit per tcp socket.
TCP bulk sender tends to increase packets in flight until it
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 4733368f953a..6c22332afb75 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -220,7 +220,7 @@ struct tcp_sock {
unused:5;
u8 nonagle : 4,/* Disable Nagle algorithm? */
thin_lto : 1,/* Use linear timeouts for thin streams */
- thin_dupack : 1,/* Fast retransmit on first dupack */
+ unused1 : 1,
repair : 1,
frto : 1;/* F-RTO (RFC5682) activated in CA_Loss */
u8 repair_queue;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 0f2d37e8e983..c8d283615c6f 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -537,13 +537,6 @@ static struct ctl_table ipv4_table[] = {
.proc_handler = proc_dointvec
},
{
- .procname = "tcp_thin_dupack",
- .data = &sysctl_tcp_thin_dupack,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
- {
.procname = "tcp_early_retrans",
.data = &sysctl_tcp_early_retrans,
.maxlen = sizeof(int),
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index d9023e8ed53e..aba6ea76338e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2474,9 +2474,6 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
case TCP_THIN_DUPACK:
if (val < 0 || val > 1)
err = -EINVAL;
- else {
- tp->thin_dupack = val;
- }
break;
case TCP_REPAIR:
@@ -2966,8 +2963,9 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
case TCP_THIN_LINEAR_TIMEOUTS:
val = tp->thin_lto;
break;
+
case TCP_THIN_DUPACK:
- val = tp->thin_dupack;
+ val = 0;
break;
case TCP_REPAIR:
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 87315ab1ab1a..39ebc20ca1b2 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -95,9 +95,6 @@ int sysctl_tcp_rfc1337 __read_mostly;
int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
int sysctl_tcp_frto __read_mostly = 2;
int sysctl_tcp_min_rtt_wlen __read_mostly = 300;
-
-int sysctl_tcp_thin_dupack __read_mostly;
-
int sysctl_tcp_moderate_rcvbuf __read_mostly = 1;
int sysctl_tcp_early_retrans __read_mostly = 3;
int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2;
@@ -2170,16 +2167,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
if (tcp_dupack_heuristics(tp) > tp->reordering)
return true;
- /* If a thin stream is detected, retransmit after first
- * received dupack. Employ only if SACK is supported in order
- * to avoid possible corner-case series of spurious retransmissions
- * Use only if there are no unsent data.
- */
- if ((tp->thin_dupack || sysctl_tcp_thin_dupack) &&
- tcp_stream_is_thin(tp) && tcp_dupack_heuristics(tp) > 1 &&
- tcp_is_sack(tp) && !tcp_send_head(sk))
- return true;
-
return false;
}
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
* [net-next 13/13] tcp: disable fack by default
From: Yuchung Cheng @ 2017-01-13 6:03 UTC (permalink / raw)
To: davem; +Cc: netdev, edumazet, ncardwell, nanditad, Yuchung Cheng
In-Reply-To: <20170113060354.85234-1-ycheng@google.com>
This patch disables FACK by default as RACK is the successor of FACK
(inspired by the insights behind FACK).
FACK[1] in Linux works as follows: a packet P is deemed lost,
if packet Q of higher sequence is s/acked and P and Q are distant
by at least dupthresh number of packets in sequence space.
FACK is more aggressive than the IETF recommened recovery for SACK
(RFC3517 A Conservative Selective Acknowledgment (SACK)-based Loss
Recovery Algorithm for TCP), because a single SACK may trigger
fast recovery. This obviously won't work well with reordering so
FACK is dynamically disabled upon detecting reordering.
RACK supersedes FACK by using time distance instead of sequence
distance. On reordering, RACK waits for a quarter of RTT receiving
a single SACK before starting recovery. (the timer can be made more
adaptive in the future by measuring reordering distance in time,
but currently RTT/4 seem to work well.) Once the recovery starts,
RACK behaves almost like FACK because it reduces the reodering
window to 1ms, so it fast retransmits quickly. In addition RACK
can detect loss retransmission as it does not care about the packet
sequences (being repeated or not), which is extremely useful when
the connection is going through a traffic policer.
Google server experiments indicate that disabling FACK after enabling
RACK has negligible impact on the overall loss recovery performance
with more reordering events detected. But we still keep the FACK
implementation for backup if RACK has bugs that needs to be disabled.
[1] M. Mathis, J. Mahdavi, "Forward Acknowledgment: Refining
TCP Congestion Control," In Proceedings of SIGCOMM '96, August 1996.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_input.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 39ebc20ca1b2..1a34e9278c07 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -79,7 +79,7 @@
int sysctl_tcp_timestamps __read_mostly = 1;
int sysctl_tcp_window_scaling __read_mostly = 1;
int sysctl_tcp_sack __read_mostly = 1;
-int sysctl_tcp_fack __read_mostly = 1;
+int sysctl_tcp_fack __read_mostly;
int sysctl_tcp_max_reordering __read_mostly = 300;
int sysctl_tcp_dsack __read_mostly = 1;
int sysctl_tcp_app_win __read_mostly = 31;
@@ -2114,7 +2114,8 @@ static inline int tcp_dupack_heuristics(const struct tcp_sock *tp)
* dynamically measured and adjusted. This is implemented in
* tcp_rack_mark_lost.
*
- * FACK: it is the simplest heuristics. As soon as we decided
+ * FACK (Disabled by default. Subsumbed by RACK):
+ * It is the simplest heuristics. As soon as we decided
* that something is lost, we decide that _all_ not SACKed
* packets until the most forward SACK are lost. I.e.
* lost_out = fackets_out - sacked_out and left_out = fackets_out.
--
2.11.0.483.g087da7b7c-goog
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox