Commit 2087ced0 authored by Dmitry Bogdanov's avatar Dmitry Bogdanov Committed by David S. Miller

atlantic: Fix statistics logic for production hardware

B0 is the main and widespread device revision of atlantic2 HW. In the
current state, driver will incorrectly fetch the statistics for this
revision.

Fixes: 5cfd54d7 ("net: atlantic: minimal A2 fw_ops")
Signed-off-by: default avatarDmitry Bogdanov <dbezrukov@marvell.com>
Signed-off-by: default avatarSudarsana Reddy Kalluru <skalluru@marvell.com>
Signed-off-by: default avatarIgor Russkikh <irusskikh@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 03fa5121
...@@ -80,6 +80,8 @@ struct aq_hw_link_status_s { ...@@ -80,6 +80,8 @@ struct aq_hw_link_status_s {
}; };
struct aq_stats_s { struct aq_stats_s {
u64 brc;
u64 btc;
u64 uprc; u64 uprc;
u64 mprc; u64 mprc;
u64 bprc; u64 bprc;
......
...@@ -905,7 +905,13 @@ u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data) ...@@ -905,7 +905,13 @@ u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data)
data[++i] = stats->mbtc; data[++i] = stats->mbtc;
data[++i] = stats->bbrc; data[++i] = stats->bbrc;
data[++i] = stats->bbtc; data[++i] = stats->bbtc;
if (stats->brc)
data[++i] = stats->brc;
else
data[++i] = stats->ubrc + stats->mbrc + stats->bbrc; data[++i] = stats->ubrc + stats->mbrc + stats->bbrc;
if (stats->btc)
data[++i] = stats->btc;
else
data[++i] = stats->ubtc + stats->mbtc + stats->bbtc; data[++i] = stats->ubtc + stats->mbtc + stats->bbtc;
data[++i] = stats->dma_pkt_rc; data[++i] = stats->dma_pkt_rc;
data[++i] = stats->dma_pkt_tc; data[++i] = stats->dma_pkt_tc;
......
...@@ -867,12 +867,20 @@ static int hw_atl_fw1x_deinit(struct aq_hw_s *self) ...@@ -867,12 +867,20 @@ static int hw_atl_fw1x_deinit(struct aq_hw_s *self)
int hw_atl_utils_update_stats(struct aq_hw_s *self) int hw_atl_utils_update_stats(struct aq_hw_s *self)
{ {
struct aq_stats_s *cs = &self->curr_stats; struct aq_stats_s *cs = &self->curr_stats;
struct aq_stats_s curr_stats = *cs;
struct hw_atl_utils_mbox mbox; struct hw_atl_utils_mbox mbox;
bool corrupted_stats = false;
hw_atl_utils_mpi_read_stats(self, &mbox); hw_atl_utils_mpi_read_stats(self, &mbox);
#define AQ_SDELTA(_N_) (self->curr_stats._N_ += \ #define AQ_SDELTA(_N_) \
mbox.stats._N_ - self->last_stats._N_) do { \
if (!corrupted_stats && \
((s64)(mbox.stats._N_ - self->last_stats._N_)) >= 0) \
curr_stats._N_ += mbox.stats._N_ - self->last_stats._N_; \
else \
corrupted_stats = true; \
} while (0)
if (self->aq_link_status.mbps) { if (self->aq_link_status.mbps) {
AQ_SDELTA(uprc); AQ_SDELTA(uprc);
...@@ -892,6 +900,9 @@ int hw_atl_utils_update_stats(struct aq_hw_s *self) ...@@ -892,6 +900,9 @@ int hw_atl_utils_update_stats(struct aq_hw_s *self)
AQ_SDELTA(bbrc); AQ_SDELTA(bbrc);
AQ_SDELTA(bbtc); AQ_SDELTA(bbtc);
AQ_SDELTA(dpc); AQ_SDELTA(dpc);
if (!corrupted_stats)
*cs = curr_stats;
} }
#undef AQ_SDELTA #undef AQ_SDELTA
......
...@@ -239,7 +239,8 @@ struct version_s { ...@@ -239,7 +239,8 @@ struct version_s {
u8 minor; u8 minor;
u16 build; u16 build;
} phy; } phy;
u32 rsvd; u32 drv_iface_ver:4;
u32 rsvd:28;
}; };
struct link_status_s { struct link_status_s {
...@@ -424,7 +425,7 @@ struct cable_diag_status_s { ...@@ -424,7 +425,7 @@ struct cable_diag_status_s {
u16 rsvd2; u16 rsvd2;
}; };
struct statistics_s { struct statistics_a0_s {
struct { struct {
u32 link_up; u32 link_up;
u32 link_down; u32 link_down;
...@@ -457,6 +458,33 @@ struct statistics_s { ...@@ -457,6 +458,33 @@ struct statistics_s {
u32 reserve_fw_gap; u32 reserve_fw_gap;
}; };
struct __packed statistics_b0_s {
u64 rx_good_octets;
u64 rx_pause_frames;
u64 rx_good_frames;
u64 rx_errors;
u64 rx_unicast_frames;
u64 rx_multicast_frames;
u64 rx_broadcast_frames;
u64 tx_good_octets;
u64 tx_pause_frames;
u64 tx_good_frames;
u64 tx_errors;
u64 tx_unicast_frames;
u64 tx_multicast_frames;
u64 tx_broadcast_frames;
u32 main_loop_cycles;
};
struct __packed statistics_s {
union __packed {
struct statistics_a0_s a0;
struct statistics_b0_s b0;
};
};
struct filter_caps_s { struct filter_caps_s {
u8 l2_filters_base_index:6; u8 l2_filters_base_index:6;
u8 flexible_filter_mask:2; u8 flexible_filter_mask:2;
...@@ -545,7 +573,7 @@ struct management_status_s { ...@@ -545,7 +573,7 @@ struct management_status_s {
u32 rsvd5; u32 rsvd5;
}; };
struct fw_interface_out { struct __packed fw_interface_out {
struct transaction_counter_s transaction_id; struct transaction_counter_s transaction_id;
struct version_s version; struct version_s version;
struct link_status_s link_status; struct link_status_s link_status;
...@@ -569,7 +597,6 @@ struct fw_interface_out { ...@@ -569,7 +597,6 @@ struct fw_interface_out {
struct core_dump_s core_dump; struct core_dump_s core_dump;
u32 rsvd11; u32 rsvd11;
struct statistics_s stats; struct statistics_s stats;
u32 rsvd12;
struct filter_caps_s filter_caps; struct filter_caps_s filter_caps;
struct device_caps_s device_caps; struct device_caps_s device_caps;
u32 rsvd13; u32 rsvd13;
...@@ -592,6 +619,9 @@ struct fw_interface_out { ...@@ -592,6 +619,9 @@ struct fw_interface_out {
#define AQ_HOST_MODE_LOW_POWER 3U #define AQ_HOST_MODE_LOW_POWER 3U
#define AQ_HOST_MODE_SHUTDOWN 4U #define AQ_HOST_MODE_SHUTDOWN 4U
#define AQ_A2_FW_INTERFACE_A0 0
#define AQ_A2_FW_INTERFACE_B0 1
int hw_atl2_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops); int hw_atl2_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops);
int hw_atl2_utils_soft_reset(struct aq_hw_s *self); int hw_atl2_utils_soft_reset(struct aq_hw_s *self);
......
...@@ -333,18 +333,22 @@ static int aq_a2_fw_get_mac_permanent(struct aq_hw_s *self, u8 *mac) ...@@ -333,18 +333,22 @@ static int aq_a2_fw_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
return 0; return 0;
} }
static int aq_a2_fw_update_stats(struct aq_hw_s *self) static void aq_a2_fill_a0_stats(struct aq_hw_s *self,
struct statistics_s *stats)
{ {
struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
struct statistics_s stats; struct aq_stats_s *cs = &self->curr_stats;
int err; struct aq_stats_s curr_stats = *cs;
bool corrupted_stats = false;
err = hw_atl2_shared_buffer_read_safe(self, stats, &stats);
if (err) #define AQ_SDELTA(_N, _F) \
return err; do { \
if (!corrupted_stats && \
#define AQ_SDELTA(_N_, _F_) (self->curr_stats._N_ += \ ((s64)(stats->a0.msm._F - priv->last_stats.a0.msm._F)) >= 0) \
stats.msm._F_ - priv->last_stats.msm._F_) curr_stats._N += stats->a0.msm._F - priv->last_stats.a0.msm._F;\
else \
corrupted_stats = true; \
} while (0)
if (self->aq_link_status.mbps) { if (self->aq_link_status.mbps) {
AQ_SDELTA(uprc, rx_unicast_frames); AQ_SDELTA(uprc, rx_unicast_frames);
...@@ -363,17 +367,76 @@ static int aq_a2_fw_update_stats(struct aq_hw_s *self) ...@@ -363,17 +367,76 @@ static int aq_a2_fw_update_stats(struct aq_hw_s *self)
AQ_SDELTA(mbtc, tx_multicast_octets); AQ_SDELTA(mbtc, tx_multicast_octets);
AQ_SDELTA(bbrc, rx_broadcast_octets); AQ_SDELTA(bbrc, rx_broadcast_octets);
AQ_SDELTA(bbtc, tx_broadcast_octets); AQ_SDELTA(bbtc, tx_broadcast_octets);
if (!corrupted_stats)
*cs = curr_stats;
}
#undef AQ_SDELTA
}
static void aq_a2_fill_b0_stats(struct aq_hw_s *self,
struct statistics_s *stats)
{
struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
struct aq_stats_s *cs = &self->curr_stats;
struct aq_stats_s curr_stats = *cs;
bool corrupted_stats = false;
#define AQ_SDELTA(_N, _F) \
do { \
if (!corrupted_stats && \
((s64)(stats->b0._F - priv->last_stats.b0._F)) >= 0) \
curr_stats._N += stats->b0._F - priv->last_stats.b0._F; \
else \
corrupted_stats = true; \
} while (0)
if (self->aq_link_status.mbps) {
AQ_SDELTA(uprc, rx_unicast_frames);
AQ_SDELTA(mprc, rx_multicast_frames);
AQ_SDELTA(bprc, rx_broadcast_frames);
AQ_SDELTA(erpr, rx_errors);
AQ_SDELTA(brc, rx_good_octets);
AQ_SDELTA(uptc, tx_unicast_frames);
AQ_SDELTA(mptc, tx_multicast_frames);
AQ_SDELTA(bptc, tx_broadcast_frames);
AQ_SDELTA(erpt, tx_errors);
AQ_SDELTA(btc, tx_good_octets);
if (!corrupted_stats)
*cs = curr_stats;
} }
#undef AQ_SDELTA #undef AQ_SDELTA
self->curr_stats.dma_pkt_rc = }
hw_atl_stats_rx_dma_good_pkt_counter_get(self);
self->curr_stats.dma_pkt_tc = static int aq_a2_fw_update_stats(struct aq_hw_s *self)
hw_atl_stats_tx_dma_good_pkt_counter_get(self); {
self->curr_stats.dma_oct_rc = struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
hw_atl_stats_rx_dma_good_octet_counter_get(self); struct aq_stats_s *cs = &self->curr_stats;
self->curr_stats.dma_oct_tc = struct statistics_s stats;
hw_atl_stats_tx_dma_good_octet_counter_get(self); struct version_s version;
self->curr_stats.dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self); int err;
err = hw_atl2_shared_buffer_read_safe(self, version, &version);
if (err)
return err;
err = hw_atl2_shared_buffer_read_safe(self, stats, &stats);
if (err)
return err;
if (version.drv_iface_ver == AQ_A2_FW_INTERFACE_A0)
aq_a2_fill_a0_stats(self, &stats);
else
aq_a2_fill_b0_stats(self, &stats);
cs->dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counter_get(self);
cs->dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counter_get(self);
cs->dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counter_get(self);
cs->dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counter_get(self);
cs->dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self);
memcpy(&priv->last_stats, &stats, sizeof(stats)); memcpy(&priv->last_stats, &stats, sizeof(stats));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment