|
@@ -80,6 +80,116 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
|
|
|
put_unaligned_le32(reg, fw_ver);
|
|
|
}
|
|
|
|
|
|
+/* Firmware reconfig
|
|
|
+ *
|
|
|
+ * Firmware reconfig may take a while so we have two versions of it -
|
|
|
+ * synchronous and asynchronous (posted). All synchronous callers are holding
|
|
|
+ * RTNL so we don't have to worry about serializing them.
|
|
|
+ */
|
|
|
+static void nfp_net_reconfig_start(struct nfp_net *nn, u32 update)
|
|
|
+{
|
|
|
+ nn_writel(nn, NFP_NET_CFG_UPDATE, update);
|
|
|
+ /* ensure update is written before pinging HW */
|
|
|
+ nn_pci_flush(nn);
|
|
|
+ nfp_qcp_wr_ptr_add(nn->qcp_cfg, 1);
|
|
|
+}
|
|
|
+
|
|
|
+/* Pass 0 as update to run posted reconfigs. */
|
|
|
+static void nfp_net_reconfig_start_async(struct nfp_net *nn, u32 update)
|
|
|
+{
|
|
|
+ update |= nn->reconfig_posted;
|
|
|
+ nn->reconfig_posted = 0;
|
|
|
+
|
|
|
+ nfp_net_reconfig_start(nn, update);
|
|
|
+
|
|
|
+ nn->reconfig_timer_active = true;
|
|
|
+ mod_timer(&nn->reconfig_timer, jiffies + NFP_NET_POLL_TIMEOUT * HZ);
|
|
|
+}
|
|
|
+
|
|
|
+static bool nfp_net_reconfig_check_done(struct nfp_net *nn, bool last_check)
|
|
|
+{
|
|
|
+ u32 reg;
|
|
|
+
|
|
|
+ reg = nn_readl(nn, NFP_NET_CFG_UPDATE);
|
|
|
+ if (reg == 0)
|
|
|
+ return true;
|
|
|
+ if (reg & NFP_NET_CFG_UPDATE_ERR) {
|
|
|
+ nn_err(nn, "Reconfig error: 0x%08x\n", reg);
|
|
|
+ return true;
|
|
|
+ } else if (last_check) {
|
|
|
+ nn_err(nn, "Reconfig timeout: 0x%08x\n", reg);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static int nfp_net_reconfig_wait(struct nfp_net *nn, unsigned long deadline)
|
|
|
+{
|
|
|
+ bool timed_out = false;
|
|
|
+
|
|
|
+ /* Poll update field, waiting for NFP to ack the config */
|
|
|
+ while (!nfp_net_reconfig_check_done(nn, timed_out)) {
|
|
|
+ msleep(1);
|
|
|
+ timed_out = time_is_before_eq_jiffies(deadline);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nn_readl(nn, NFP_NET_CFG_UPDATE) & NFP_NET_CFG_UPDATE_ERR)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return timed_out ? -EIO : 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void nfp_net_reconfig_timer(unsigned long data)
|
|
|
+{
|
|
|
+ struct nfp_net *nn = (void *)data;
|
|
|
+
|
|
|
+ spin_lock_bh(&nn->reconfig_lock);
|
|
|
+
|
|
|
+ nn->reconfig_timer_active = false;
|
|
|
+
|
|
|
+ /* If sync caller is present it will take over from us */
|
|
|
+ if (nn->reconfig_sync_present)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ /* Read reconfig status and report errors */
|
|
|
+ nfp_net_reconfig_check_done(nn, true);
|
|
|
+
|
|
|
+ if (nn->reconfig_posted)
|
|
|
+ nfp_net_reconfig_start_async(nn, 0);
|
|
|
+done:
|
|
|
+ spin_unlock_bh(&nn->reconfig_lock);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * nfp_net_reconfig_post() - Post async reconfig request
|
|
|
+ * @nn: NFP Net device to reconfigure
|
|
|
+ * @update: The value for the update field in the BAR config
|
|
|
+ *
|
|
|
+ * Record FW reconfiguration request. Reconfiguration will be kicked off
|
|
|
+ * whenever reconfiguration machinery is idle. Multiple requests can be
|
|
|
+ * merged together!
|
|
|
+ */
|
|
|
+static void nfp_net_reconfig_post(struct nfp_net *nn, u32 update)
|
|
|
+{
|
|
|
+ spin_lock_bh(&nn->reconfig_lock);
|
|
|
+
|
|
|
+ /* Sync caller will kick off async reconf when it's done, just post */
|
|
|
+ if (nn->reconfig_sync_present) {
|
|
|
+ nn->reconfig_posted |= update;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Opportunistically check if the previous command is done */
|
|
|
+ if (!nn->reconfig_timer_active ||
|
|
|
+ nfp_net_reconfig_check_done(nn, false))
|
|
|
+ nfp_net_reconfig_start_async(nn, update);
|
|
|
+ else
|
|
|
+ nn->reconfig_posted |= update;
|
|
|
+done:
|
|
|
+ spin_unlock_bh(&nn->reconfig_lock);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* nfp_net_reconfig() - Reconfigure the firmware
|
|
|
* @nn: NFP Net device to reconfigure
|
|
@@ -93,35 +203,45 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
|
|
|
*/
|
|
|
int nfp_net_reconfig(struct nfp_net *nn, u32 update)
|
|
|
{
|
|
|
- int cnt, ret = 0;
|
|
|
- u32 new;
|
|
|
+ bool cancelled_timer = false;
|
|
|
+ u32 pre_posted_requests;
|
|
|
+ int ret;
|
|
|
|
|
|
spin_lock_bh(&nn->reconfig_lock);
|
|
|
|
|
|
- nn_writel(nn, NFP_NET_CFG_UPDATE, update);
|
|
|
- /* ensure update is written before pinging HW */
|
|
|
- nn_pci_flush(nn);
|
|
|
- nfp_qcp_wr_ptr_add(nn->qcp_cfg, 1);
|
|
|
+ nn->reconfig_sync_present = true;
|
|
|
|
|
|
- /* Poll update field, waiting for NFP to ack the config */
|
|
|
- for (cnt = 0; ; cnt++) {
|
|
|
- new = nn_readl(nn, NFP_NET_CFG_UPDATE);
|
|
|
- if (new == 0)
|
|
|
- break;
|
|
|
- if (new & NFP_NET_CFG_UPDATE_ERR) {
|
|
|
- nn_err(nn, "Reconfig error: 0x%08x\n", new);
|
|
|
- ret = -EIO;
|
|
|
- break;
|
|
|
- } else if (cnt >= NFP_NET_POLL_TIMEOUT) {
|
|
|
- nn_err(nn, "Reconfig timeout for 0x%08x after %dms\n",
|
|
|
- update, cnt);
|
|
|
- ret = -EIO;
|
|
|
- break;
|
|
|
- }
|
|
|
- mdelay(1);
|
|
|
+ if (nn->reconfig_timer_active) {
|
|
|
+ del_timer(&nn->reconfig_timer);
|
|
|
+ nn->reconfig_timer_active = false;
|
|
|
+ cancelled_timer = true;
|
|
|
+ }
|
|
|
+ pre_posted_requests = nn->reconfig_posted;
|
|
|
+ nn->reconfig_posted = 0;
|
|
|
+
|
|
|
+ spin_unlock_bh(&nn->reconfig_lock);
|
|
|
+
|
|
|
+ if (cancelled_timer)
|
|
|
+ nfp_net_reconfig_wait(nn, nn->reconfig_timer.expires);
|
|
|
+
|
|
|
+ /* Run the posted reconfigs which were issued before we started */
|
|
|
+ if (pre_posted_requests) {
|
|
|
+ nfp_net_reconfig_start(nn, pre_posted_requests);
|
|
|
+ nfp_net_reconfig_wait(nn, jiffies + HZ * NFP_NET_POLL_TIMEOUT);
|
|
|
}
|
|
|
|
|
|
+ nfp_net_reconfig_start(nn, update);
|
|
|
+ ret = nfp_net_reconfig_wait(nn, jiffies + HZ * NFP_NET_POLL_TIMEOUT);
|
|
|
+
|
|
|
+ spin_lock_bh(&nn->reconfig_lock);
|
|
|
+
|
|
|
+ if (nn->reconfig_posted)
|
|
|
+ nfp_net_reconfig_start_async(nn, 0);
|
|
|
+
|
|
|
+ nn->reconfig_sync_present = false;
|
|
|
+
|
|
|
spin_unlock_bh(&nn->reconfig_lock);
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1298,23 +1418,25 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
|
|
|
|
|
|
nfp_net_rx_give_one(rx_ring, new_skb, new_dma_addr);
|
|
|
|
|
|
+ /* < meta_len >
|
|
|
+ * <-- [rx_offset] -->
|
|
|
+ * ---------------------------------------------------------
|
|
|
+ * | [XX] | metadata | packet | XXXX |
|
|
|
+ * ---------------------------------------------------------
|
|
|
+ * <---------------- data_len --------------->
|
|
|
+ *
|
|
|
+ * The rx_offset is fixed for all packets, the meta_len can vary
|
|
|
+ * on a packet by packet basis. If rx_offset is set to zero
|
|
|
+ * (_RX_OFFSET_DYNAMIC) metadata starts at the beginning of the
|
|
|
+ * buffer and is immediately followed by the packet (no [XX]).
|
|
|
+ */
|
|
|
meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK;
|
|
|
data_len = le16_to_cpu(rxd->rxd.data_len);
|
|
|
|
|
|
- if (WARN_ON_ONCE(data_len > nn->fl_bufsz)) {
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) {
|
|
|
- /* The packet data starts after the metadata */
|
|
|
+ if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC)
|
|
|
skb_reserve(skb, meta_len);
|
|
|
- } else {
|
|
|
- /* The packet data starts at a fixed offset */
|
|
|
+ else
|
|
|
skb_reserve(skb, nn->rx_offset);
|
|
|
- }
|
|
|
-
|
|
|
- /* Adjust the SKB for the dynamic meta data pre-pended */
|
|
|
skb_put(skb, data_len - meta_len);
|
|
|
|
|
|
nfp_net_set_hash(nn->netdev, skb, rxd);
|
|
@@ -2094,8 +2216,7 @@ static void nfp_net_set_rx_mode(struct net_device *netdev)
|
|
|
return;
|
|
|
|
|
|
nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
|
|
|
- if (nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN))
|
|
|
- return;
|
|
|
+ nfp_net_reconfig_post(nn, NFP_NET_CFG_UPDATE_GEN);
|
|
|
|
|
|
nn->ctrl = new_ctrl;
|
|
|
}
|
|
@@ -2403,7 +2524,7 @@ static void nfp_net_set_vxlan_port(struct nfp_net *nn, int idx, __be16 port)
|
|
|
be16_to_cpu(nn->vxlan_ports[i + 1]) << 16 |
|
|
|
be16_to_cpu(nn->vxlan_ports[i]));
|
|
|
|
|
|
- nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VXLAN);
|
|
|
+ nfp_net_reconfig_post(nn, NFP_NET_CFG_UPDATE_VXLAN);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2549,6 +2670,9 @@ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev,
|
|
|
spin_lock_init(&nn->reconfig_lock);
|
|
|
spin_lock_init(&nn->link_status_lock);
|
|
|
|
|
|
+ setup_timer(&nn->reconfig_timer,
|
|
|
+ nfp_net_reconfig_timer, (unsigned long)nn);
|
|
|
+
|
|
|
return nn;
|
|
|
}
|
|
|
|