/** * fm10k_disable_queues_generic - Stop Tx/Rx queues * @hw: pointer to hardware structure * @q_cnt: number of queues to be disabled * **/ s32 fm10k_disable_queues_generic(struct fm10k_hw *hw, u16 q_cnt) { u32 reg; u16 i, time; /* clear tx_ready to prevent any false hits for reset */ hw->mac.tx_ready = false; if (FM10K_REMOVED(hw->hw_addr)) return 0; /* clear the enable bit for all rings */ for (i = 0; i < q_cnt; i++) { reg = fm10k_read_reg(hw, FM10K_TXDCTL(i)); fm10k_write_reg(hw, FM10K_TXDCTL(i), reg & ~FM10K_TXDCTL_ENABLE); reg = fm10k_read_reg(hw, FM10K_RXQCTL(i)); fm10k_write_reg(hw, FM10K_RXQCTL(i), reg & ~FM10K_RXQCTL_ENABLE); } fm10k_write_flush(hw); udelay(1); /* loop through all queues to verify that they are all disabled */ for (i = 0, time = FM10K_QUEUE_DISABLE_TIMEOUT; time;) { /* if we are at end of rings all rings are disabled */ if (i == q_cnt) return 0; /* if queue enables cleared, then move to next ring pair */ reg = fm10k_read_reg(hw, FM10K_TXDCTL(i)); if (!~reg || !(reg & FM10K_TXDCTL_ENABLE)) { reg = fm10k_read_reg(hw, FM10K_RXQCTL(i)); if (!~reg || !(reg & FM10K_RXQCTL_ENABLE)) { i++; continue; } } /* decrement time and wait 1 usec */ time--; if (time) udelay(1); } return FM10K_ERR_REQUESTS_PENDING; }
static int fm10k_set_reta(struct net_device *netdev, const u32 *indir) { struct fm10k_intfc *interface = netdev_priv(netdev); struct fm10k_hw *hw = &interface->hw; int i; u16 rss_i; if (!indir) return 0; /* Verify user input. */ rss_i = interface->ring_feature[RING_F_RSS].indices; for (i = fm10k_get_reta_size(netdev); i--;) { if (indir[i] < rss_i) continue; return -EINVAL; } /* record entries to reta table */ for (i = 0; i < FM10K_RETA_SIZE; i++, indir += 4) { u32 reta = indir[0] | (indir[1] << 8) | (indir[2] << 16) | (indir[3] << 24); if (interface->reta[i] == reta) continue; interface->reta[i] = reta; fm10k_write_reg(hw, FM10K_RETA(0, i), reta); } return 0; }
static int fm10k_set_rssh(struct net_device *netdev, const u32 *indir, const u8 *key, const u8 hfunc) { struct fm10k_intfc *interface = netdev_priv(netdev); struct fm10k_hw *hw = &interface->hw; int i, err; /* We do not allow change in unsupported parameters */ if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; err = fm10k_set_reta(netdev, indir); if (err || !key) return err; for (i = 0; i < FM10K_RSSRK_SIZE; i++, key += 4) { u32 rssrk = le32_to_cpu(*(__le32 *)key); if (interface->rssrk[i] == rssrk) continue; interface->rssrk[i] = rssrk; fm10k_write_reg(hw, FM10K_RSSRK(0, i), rssrk); } return 0; }
void fm10k_write_reta(struct fm10k_intfc *interface, const u32 *indir) { u16 rss_i = interface->ring_feature[RING_F_RSS].indices; struct fm10k_hw *hw = &interface->hw; u32 table[4]; int i, j; /* record entries to reta table */ for (i = 0; i < FM10K_RETA_SIZE; i++) { u32 reta, n; /* generate a new table if we weren't given one */ for (j = 0; j < 4; j++) { if (indir) n = indir[i + j]; else n = ethtool_rxfh_indir_default(i + j, rss_i); table[j] = n; } reta = table[0] | (table[1] << 8) | (table[2] << 16) | (table[3] << 24); if (interface->reta[i] == reta) continue; interface->reta[i] = reta; fm10k_write_reg(hw, FM10K_RETA(0, i), reta); } }
/** * fm10k_restore_udp_port_info * @interface: board private structure * * This function restores the value in the tunnel_cfg register(s) after reset **/ static void fm10k_restore_udp_port_info(struct fm10k_intfc *interface) { struct fm10k_hw *hw = &interface->hw; struct fm10k_udp_port *port; /* only the PF supports configuring tunnels */ if (hw->mac.type != fm10k_mac_pf) return; port = list_first_entry_or_null(&interface->vxlan_port, struct fm10k_udp_port, list); /* restore tunnel configuration register */ fm10k_write_reg(hw, FM10K_TUNNEL_CFG, (port ? ntohs(port->port) : 0) | (ETH_P_TEB << FM10K_TUNNEL_CFG_NVGRE_SHIFT)); port = list_first_entry_or_null(&interface->geneve_port, struct fm10k_udp_port, list); /* restore Geneve tunnel configuration register */ fm10k_write_reg(hw, FM10K_TUNNEL_CFG_GENEVE, (port ? ntohs(port->port) : 0)); }
static int fm10k_set_rssh(struct net_device *netdev, const u32 *indir, const u8 *key) { struct fm10k_intfc *interface = netdev_priv(netdev); struct fm10k_hw *hw = &interface->hw; int i, err; err = fm10k_set_reta(netdev, indir); if (err || !key) return err; for (i = 0; i < FM10K_RSSRK_SIZE; i++, key += 4) { u32 rssrk = le32_to_cpu(*(__le32 *)key); if (interface->rssrk[i] == rssrk) continue; interface->rssrk[i] = rssrk; fm10k_write_reg(hw, FM10K_RSSRK(0, i), rssrk); } return 0; }
static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, struct ethtool_rxnfc *nfc) { u32 flags = interface->flags; /* RSS does not support anything other than hashing * to queues on src and dst IPs and ports */ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) return -EINVAL; switch (nfc->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || !(nfc->data & RXH_L4_B_0_1) || !(nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; case UDP_V4_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: flags &= ~FM10K_FLAG_RSS_FIELD_IPV4_UDP; break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): flags |= FM10K_FLAG_RSS_FIELD_IPV4_UDP; break; default: return -EINVAL; } break; case UDP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: flags &= ~FM10K_FLAG_RSS_FIELD_IPV6_UDP; break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): flags |= FM10K_FLAG_RSS_FIELD_IPV6_UDP; break; default: return -EINVAL; } break; case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case SCTP_V4_FLOW: case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case SCTP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || (nfc->data & RXH_L4_B_0_1) || (nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; default: return -EINVAL; } /* if we changed something we need to update flags */ if (flags != interface->flags) { struct fm10k_hw *hw = &interface->hw; u32 mrqc; if ((flags & UDP_RSS_FLAGS) && !(interface->flags & UDP_RSS_FLAGS)) netif_warn(interface, drv, interface->netdev, "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); interface->flags = flags; /* Perform hash on these packet types */ mrqc = FM10K_MRQC_IPV4 | FM10K_MRQC_TCP_IPV4 | FM10K_MRQC_IPV6 | FM10K_MRQC_TCP_IPV6; if (flags & FM10K_FLAG_RSS_FIELD_IPV4_UDP) mrqc |= FM10K_MRQC_UDP_IPV4; if (flags & FM10K_FLAG_RSS_FIELD_IPV6_UDP) mrqc |= FM10K_MRQC_UDP_IPV6; fm10k_write_reg(hw, FM10K_MRQC(0), mrqc); } return 0; }
static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, struct ethtool_rxnfc *nfc) { int rss_ipv4_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags); int rss_ipv6_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags); /* RSS does not support anything other than hashing * to queues on src and dst IPs and ports */ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) return -EINVAL; switch (nfc->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || !(nfc->data & RXH_L4_B_0_1) || !(nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; case UDP_V4_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: clear_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): set_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags); break; default: return -EINVAL; } break; case UDP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: clear_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): set_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags); break; default: return -EINVAL; } break; case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case SCTP_V4_FLOW: case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case SCTP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || (nfc->data & RXH_L4_B_0_1) || (nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; default: return -EINVAL; } /* If something changed we need to update the MRQC register. Note that * test_bit() is guaranteed to return strictly 0 or 1, so testing for * equality is safe. */ if ((rss_ipv4_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags)) || (rss_ipv6_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags))) { struct fm10k_hw *hw = &interface->hw; bool warn = false; u32 mrqc; /* Perform hash on these packet types */ mrqc = FM10K_MRQC_IPV4 | FM10K_MRQC_TCP_IPV4 | FM10K_MRQC_IPV6 | FM10K_MRQC_TCP_IPV6; if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags)) { mrqc |= FM10K_MRQC_UDP_IPV4; warn = true; } if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags)) { mrqc |= FM10K_MRQC_UDP_IPV6; warn = true; } /* If we enable UDP RSS display a warning that this may cause * fragmented UDP packets to arrive out of order. */ if (warn) netif_warn(interface, drv, interface->netdev, "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); fm10k_write_reg(hw, FM10K_MRQC(0), mrqc); } return 0; }