sfc: Correct RX dropped count for drops while interface is down
authorJon Cooper <jcooper@solarflare.com>
Mon, 30 Sep 2013 16:36:50 +0000 (17:36 +0100)
committerBen Hutchings <bhutchings@solarflare.com>
Thu, 12 Dec 2013 22:06:51 +0000 (22:06 +0000)
We don't directly control RX ingress on Siena or any later
controllers, and so we cannot prevent packets from entering the RX
datapath while the RX queues are not set up.  This results in
the hardware incrementing RX_NODESC_DROP_CNT, but it's not an
error and we should not include it in error stats.

When bringing an interface up or down, pull (or wait for) stats and
count the number of packets that were dropped while the interface was
down.  Subtract this from the reported RX dropped count.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/falcon.c
drivers/net/ethernet/sfc/mcdi.h
drivers/net/ethernet/sfc/mcdi_port.c
drivers/net/ethernet/sfc/net_driver.h
drivers/net/ethernet/sfc/nic.c
drivers/net/ethernet/sfc/nic.h
drivers/net/ethernet/sfc/siena.c

index e9c546bdbdfedae8b1debc9f0e2cd41fa04bec9e..c1b85edcb2047bd2040c2719c98dcf683ba2aa39 100644 (file)
@@ -902,6 +902,7 @@ static int efx_ef10_try_update_nic_stats(struct efx_nic *efx)
                return -EAGAIN;
 
        /* Update derived statistics */
+       efx_nic_fix_nodesc_drop_stat(efx, &stats[EF10_STAT_rx_nodesc_drops]);
        stats[EF10_STAT_rx_good_bytes] =
                stats[EF10_STAT_rx_bytes] -
                stats[EF10_STAT_rx_bytes_minus_good_bytes];
@@ -3423,6 +3424,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
        .describe_stats = efx_ef10_describe_stats,
        .update_stats = efx_ef10_update_stats,
        .start_stats = efx_mcdi_mac_start_stats,
+       .pull_stats = efx_mcdi_mac_pull_stats,
        .stop_stats = efx_mcdi_mac_stop_stats,
        .set_id_led = efx_mcdi_set_id_led,
        .push_irq_moderation = efx_ef10_push_irq_moderation,
index 5e2454d071374641938541c47f84ac97acf93f04..c734fba8c99c787c539a6dd08abf59d94213f341 100644 (file)
@@ -1684,6 +1684,10 @@ static void efx_start_all(struct efx_nic *efx)
        }
 
        efx->type->start_stats(efx);
+       efx->type->pull_stats(efx);
+       spin_lock_bh(&efx->stats_lock);
+       efx->type->update_stats(efx, NULL, NULL);
+       spin_unlock_bh(&efx->stats_lock);
 }
 
 /* Flush all delayed work. Should only be called when no more delayed work
@@ -1711,6 +1715,13 @@ static void efx_stop_all(struct efx_nic *efx)
        if (!efx->port_enabled)
                return;
 
+       /* update stats before we go down so we can accurately count
+        * rx_nodesc_drops
+        */
+       efx->type->pull_stats(efx);
+       spin_lock_bh(&efx->stats_lock);
+       efx->type->update_stats(efx, NULL, NULL);
+       spin_unlock_bh(&efx->stats_lock);
        efx->type->stop_stats(efx);
        efx_stop_port(efx);
 
index ff5d322b9b49698842bdfc7a19117c8054829c86..4a9e05c82e2a1363ed6bf9ddf726131972cc82e5 100644 (file)
@@ -2593,6 +2593,14 @@ void falcon_start_nic_stats(struct efx_nic *efx)
        spin_unlock_bh(&efx->stats_lock);
 }
 
+/* We don't acutally pull stats on falcon. Wait 10ms so that
+ * they arrive when we call this just after start_stats
+ */
+void falcon_pull_nic_stats(struct efx_nic *efx)
+{
+       msleep(10);
+}
+
 void falcon_stop_nic_stats(struct efx_nic *efx)
 {
        struct falcon_nic_data *nic_data = efx->nic_data;
@@ -2672,6 +2680,7 @@ const struct efx_nic_type falcon_a1_nic_type = {
        .describe_stats = falcon_describe_nic_stats,
        .update_stats = falcon_update_nic_stats,
        .start_stats = falcon_start_nic_stats,
+       .pull_stats = falcon_pull_nic_stats,
        .stop_stats = falcon_stop_nic_stats,
        .set_id_led = falcon_set_id_led,
        .push_irq_moderation = falcon_push_irq_moderation,
@@ -2765,6 +2774,7 @@ const struct efx_nic_type falcon_b0_nic_type = {
        .describe_stats = falcon_describe_nic_stats,
        .update_stats = falcon_update_nic_stats,
        .start_stats = falcon_start_nic_stats,
+       .pull_stats = falcon_pull_nic_stats,
        .stop_stats = falcon_stop_nic_stats,
        .set_id_led = falcon_set_id_led,
        .push_irq_moderation = falcon_push_irq_moderation,
index 15816cacb548161bd4a7299909bb83ccd9c222be..d861628b7ee67fa5247b0fafec7fb4aa2a22f6ad 100644 (file)
@@ -301,6 +301,7 @@ int efx_mcdi_set_mac(struct efx_nic *efx);
 #define EFX_MC_STATS_GENERATION_INVALID ((__force __le64)(-1))
 void efx_mcdi_mac_start_stats(struct efx_nic *efx);
 void efx_mcdi_mac_stop_stats(struct efx_nic *efx);
+void efx_mcdi_mac_pull_stats(struct efx_nic *efx);
 bool efx_mcdi_mac_check_fault(struct efx_nic *efx);
 enum reset_type efx_mcdi_map_reset_reason(enum reset_type reason);
 int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method);
index 7b6be61d549fd57f81d1d7a31b6ef374d0f5acfb..7288aefc287728fc81527af135b8325261c356db 100644 (file)
@@ -927,12 +927,23 @@ bool efx_mcdi_mac_check_fault(struct efx_nic *efx)
        return MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT) != 0;
 }
 
-static int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr,
-                             u32 dma_len, int enable, int clear)
+enum efx_stats_action {
+       EFX_STATS_ENABLE,
+       EFX_STATS_DISABLE,
+       EFX_STATS_PULL,
+};
+
+static int efx_mcdi_mac_stats(struct efx_nic *efx,
+                             enum efx_stats_action action, int clear)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_MAC_STATS_IN_LEN);
        int rc;
-       int period = enable ? 1000 : 0;
+       int change = action == EFX_STATS_PULL ? 0 : 1;
+       int enable = action == EFX_STATS_ENABLE ? 1 : 0;
+       int period = action == EFX_STATS_ENABLE ? 1000 : 0;
+       dma_addr_t dma_addr = efx->stats_buffer.dma_addr;
+       u32 dma_len = action != EFX_STATS_DISABLE ?
+               MC_CMD_MAC_NSTATS * sizeof(u64) : 0;
 
        BUILD_BUG_ON(MC_CMD_MAC_STATS_OUT_DMA_LEN != 0);
 
@@ -940,8 +951,8 @@ static int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr,
        MCDI_POPULATE_DWORD_7(inbuf, MAC_STATS_IN_CMD,
                              MAC_STATS_IN_DMA, !!enable,
                              MAC_STATS_IN_CLEAR, clear,
-                             MAC_STATS_IN_PERIODIC_CHANGE, 1,
-                             MAC_STATS_IN_PERIODIC_ENABLE, !!enable,
+                             MAC_STATS_IN_PERIODIC_CHANGE, change,
+                             MAC_STATS_IN_PERIODIC_ENABLE, enable,
                              MAC_STATS_IN_PERIODIC_CLEAR, 0,
                              MAC_STATS_IN_PERIODIC_NOEVENT, 1,
                              MAC_STATS_IN_PERIOD_MS, period);
@@ -955,8 +966,8 @@ static int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr,
        return 0;
 
 fail:
-       netif_err(efx, hw, efx->net_dev, "%s: %s failed rc=%d\n",
-                 __func__, enable ? "enable" : "disable", rc);
+       netif_err(efx, hw, efx->net_dev, "%s: action %d failed rc=%d\n",
+                 __func__, action, rc);
        return rc;
 }
 
@@ -966,13 +977,29 @@ void efx_mcdi_mac_start_stats(struct efx_nic *efx)
 
        dma_stats[MC_CMD_MAC_GENERATION_END] = EFX_MC_STATS_GENERATION_INVALID;
 
-       efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr,
-                          MC_CMD_MAC_NSTATS * sizeof(u64), 1, 0);
+       efx_mcdi_mac_stats(efx, EFX_STATS_ENABLE, 0);
 }
 
 void efx_mcdi_mac_stop_stats(struct efx_nic *efx)
 {
-       efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 0);
+       efx_mcdi_mac_stats(efx, EFX_STATS_DISABLE, 0);
+}
+
+#define EFX_MAC_STATS_WAIT_US 100
+#define EFX_MAC_STATS_WAIT_ATTEMPTS 10
+
+void efx_mcdi_mac_pull_stats(struct efx_nic *efx)
+{
+       __le64 *dma_stats = efx->stats_buffer.addr;
+       int attempts = EFX_MAC_STATS_WAIT_ATTEMPTS;
+
+       dma_stats[MC_CMD_MAC_GENERATION_END] = EFX_MC_STATS_GENERATION_INVALID;
+       efx_mcdi_mac_stats(efx, EFX_STATS_PULL, 0);
+
+       while (dma_stats[MC_CMD_MAC_GENERATION_END] ==
+                               EFX_MC_STATS_GENERATION_INVALID &&
+                       attempts-- != 0)
+               udelay(EFX_MAC_STATS_WAIT_US);
 }
 
 int efx_mcdi_port_probe(struct efx_nic *efx)
@@ -1003,7 +1030,7 @@ int efx_mcdi_port_probe(struct efx_nic *efx)
                  efx->stats_buffer.addr,
                  (u64)virt_to_phys(efx->stats_buffer.addr));
 
-       efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 1);
+       efx_mcdi_mac_stats(efx, EFX_STATS_DISABLE, 1);
 
        return 0;
 }
index d98b3f031ab57954d551a7316614f721cf243728..f47bac78b92c1b75b91ae312a86fb31b1f704eb3 100644 (file)
@@ -857,6 +857,9 @@ struct efx_nic {
        struct net_device *net_dev;
 
        struct efx_buffer stats_buffer;
+       u64 rx_nodesc_drops_total;
+       u64 rx_nodesc_drops_while_down;
+       bool rx_nodesc_drops_prev_state;
 
        unsigned int phy_type;
        const struct efx_phy_operations *phy_op;
@@ -960,6 +963,7 @@ struct efx_mtd_partition {
  * @update_stats: Update statistics not provided by event handling.
  *     Either argument may be %NULL.
  * @start_stats: Start the regular fetching of statistics
+ * @pull_stats: Pull stats from the NIC and wait until they arrive.
  * @stop_stats: Stop the regular fetching of statistics
  * @set_id_led: Set state of identifying LED or revert to automatic function
  * @push_irq_moderation: Apply interrupt moderation value
@@ -1078,6 +1082,7 @@ struct efx_nic_type {
        size_t (*update_stats)(struct efx_nic *efx, u64 *full_stats,
                               struct rtnl_link_stats64 *core_stats);
        void (*start_stats)(struct efx_nic *efx);
+       void (*pull_stats)(struct efx_nic *efx);
        void (*stop_stats)(struct efx_nic *efx);
        void (*set_id_led)(struct efx_nic *efx, enum efx_led_mode mode);
        void (*push_irq_moderation)(struct efx_channel *channel);
index 9c90bf56090f604b4b4df77fb12b0353c922b6e2..79226b19e3c40d072bf929046bd8b0b219a9ab1a 100644 (file)
@@ -519,3 +519,15 @@ void efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count,
                }
        }
 }
+
+void efx_nic_fix_nodesc_drop_stat(struct efx_nic *efx, u64 *rx_nodesc_drops)
+{
+       /* if down, or this is the first update after coming up */
+       if (!(efx->net_dev->flags & IFF_UP) || !efx->rx_nodesc_drops_prev_state)
+               efx->rx_nodesc_drops_while_down +=
+                       *rx_nodesc_drops - efx->rx_nodesc_drops_total;
+       efx->rx_nodesc_drops_total = *rx_nodesc_drops;
+       efx->rx_nodesc_drops_prev_state = !!(efx->net_dev->flags & IFF_UP);
+       *rx_nodesc_drops -= efx->rx_nodesc_drops_while_down;
+}
+
index fe71e81ee8bf0cb9284b04a234457d77bbf5a0f5..7ac9c000696f154f83f31f4614634a07d4ea8608 100644 (file)
@@ -775,6 +775,7 @@ size_t efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count,
 void efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count,
                          const unsigned long *mask, u64 *stats,
                          const void *dma_buf, bool accumulate);
+void efx_nic_fix_nodesc_drop_stat(struct efx_nic *efx, u64 *stat);
 
 #define EFX_MAX_FLUSH_TIME 5000
 
index d034bcd124ef1e777f9d19dfba868e9275e6eae2..f2a7ad4c76aa430496c70622e5afe2f7de6430e1 100644 (file)
@@ -458,6 +458,8 @@ static int siena_try_update_nic_stats(struct efx_nic *efx)
                return -EAGAIN;
 
        /* Update derived statistics */
+       efx_nic_fix_nodesc_drop_stat(efx,
+                                    &stats[SIENA_STAT_rx_nodesc_drop_cnt]);
        efx_update_diff_stat(&stats[SIENA_STAT_tx_good_bytes],
                             stats[SIENA_STAT_tx_bytes] -
                             stats[SIENA_STAT_tx_bad_bytes]);
@@ -878,6 +880,7 @@ const struct efx_nic_type siena_a0_nic_type = {
        .describe_stats = siena_describe_nic_stats,
        .update_stats = siena_update_nic_stats,
        .start_stats = efx_mcdi_mac_start_stats,
+       .pull_stats = efx_mcdi_mac_pull_stats,
        .stop_stats = efx_mcdi_mac_stop_stats,
        .set_id_led = efx_mcdi_set_id_led,
        .push_irq_moderation = siena_push_irq_moderation,