sfc: Add PTP counters to ethtool stats
These were implemented by Andrew Jackson and Laurence Evans but not previously included in-tree. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
This commit is contained in:
parent
79ac47ae74
commit
99691c4ac1
3 changed files with 127 additions and 9 deletions
|
@ -359,7 +359,8 @@ static int efx_ethtool_get_sset_count(struct net_device *net_dev,
|
||||||
switch (string_set) {
|
switch (string_set) {
|
||||||
case ETH_SS_STATS:
|
case ETH_SS_STATS:
|
||||||
return efx->type->describe_stats(efx, NULL) +
|
return efx->type->describe_stats(efx, NULL) +
|
||||||
EFX_ETHTOOL_SW_STAT_COUNT;
|
EFX_ETHTOOL_SW_STAT_COUNT +
|
||||||
|
efx_ptp_describe_stats(efx, NULL);
|
||||||
case ETH_SS_TEST:
|
case ETH_SS_TEST:
|
||||||
return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL);
|
return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL);
|
||||||
default:
|
default:
|
||||||
|
@ -380,6 +381,8 @@ static void efx_ethtool_get_strings(struct net_device *net_dev,
|
||||||
for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++)
|
for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++)
|
||||||
strlcpy(strings + i * ETH_GSTRING_LEN,
|
strlcpy(strings + i * ETH_GSTRING_LEN,
|
||||||
efx_sw_stat_desc[i].name, ETH_GSTRING_LEN);
|
efx_sw_stat_desc[i].name, ETH_GSTRING_LEN);
|
||||||
|
strings += EFX_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN;
|
||||||
|
efx_ptp_describe_stats(efx, strings);
|
||||||
break;
|
break;
|
||||||
case ETH_SS_TEST:
|
case ETH_SS_TEST:
|
||||||
efx_ethtool_fill_self_tests(efx, NULL, strings, NULL);
|
efx_ethtool_fill_self_tests(efx, NULL, strings, NULL);
|
||||||
|
@ -429,8 +432,11 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
data += EFX_ETHTOOL_SW_STAT_COUNT;
|
||||||
|
|
||||||
spin_unlock_bh(&efx->stats_lock);
|
spin_unlock_bh(&efx->stats_lock);
|
||||||
|
|
||||||
|
efx_ptp_update_stats(efx, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void efx_ethtool_self_test(struct net_device *net_dev,
|
static void efx_ethtool_self_test(struct net_device *net_dev,
|
||||||
|
|
|
@ -566,6 +566,8 @@ int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
|
||||||
unsigned int new_mode);
|
unsigned int new_mode);
|
||||||
int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
|
int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
|
||||||
void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev);
|
void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev);
|
||||||
|
size_t efx_ptp_describe_stats(struct efx_nic *efx, u8 *strings);
|
||||||
|
size_t efx_ptp_update_stats(struct efx_nic *efx, u64 *stats);
|
||||||
void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev);
|
void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev);
|
||||||
void __efx_rx_skb_attach_timestamp(struct efx_channel *channel,
|
void __efx_rx_skb_attach_timestamp(struct efx_channel *channel,
|
||||||
struct sk_buff *skb);
|
struct sk_buff *skb);
|
||||||
|
|
|
@ -255,6 +255,15 @@ struct efx_ptp_timeset {
|
||||||
* @nic_ts_enabled: Flag indicating if NIC generated TS events are handled
|
* @nic_ts_enabled: Flag indicating if NIC generated TS events are handled
|
||||||
* @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids
|
* @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids
|
||||||
* allocations in main data path).
|
* allocations in main data path).
|
||||||
|
* @good_syncs: Number of successful synchronisations.
|
||||||
|
* @fast_syncs: Number of synchronisations requiring short delay
|
||||||
|
* @bad_syncs: Number of failed synchronisations.
|
||||||
|
* @sync_timeouts: Number of synchronisation timeouts
|
||||||
|
* @no_time_syncs: Number of synchronisations with no good times.
|
||||||
|
* @invalid_sync_windows: Number of sync windows with bad durations.
|
||||||
|
* @undersize_sync_windows: Number of corrected sync windows that are too small
|
||||||
|
* @oversize_sync_windows: Number of corrected sync windows that are too large
|
||||||
|
* @rx_no_timestamp: Number of packets received without a timestamp.
|
||||||
* @timeset: Last set of synchronisation statistics.
|
* @timeset: Last set of synchronisation statistics.
|
||||||
*/
|
*/
|
||||||
struct efx_ptp_data {
|
struct efx_ptp_data {
|
||||||
|
@ -300,6 +309,16 @@ struct efx_ptp_data {
|
||||||
struct workqueue_struct *pps_workwq;
|
struct workqueue_struct *pps_workwq;
|
||||||
bool nic_ts_enabled;
|
bool nic_ts_enabled;
|
||||||
MCDI_DECLARE_BUF(txbuf, MC_CMD_PTP_IN_TRANSMIT_LENMAX);
|
MCDI_DECLARE_BUF(txbuf, MC_CMD_PTP_IN_TRANSMIT_LENMAX);
|
||||||
|
|
||||||
|
unsigned int good_syncs;
|
||||||
|
unsigned int fast_syncs;
|
||||||
|
unsigned int bad_syncs;
|
||||||
|
unsigned int sync_timeouts;
|
||||||
|
unsigned int no_time_syncs;
|
||||||
|
unsigned int invalid_sync_windows;
|
||||||
|
unsigned int undersize_sync_windows;
|
||||||
|
unsigned int oversize_sync_windows;
|
||||||
|
unsigned int rx_no_timestamp;
|
||||||
struct efx_ptp_timeset
|
struct efx_ptp_timeset
|
||||||
timeset[MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_MAXNUM];
|
timeset[MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_MAXNUM];
|
||||||
};
|
};
|
||||||
|
@ -312,6 +331,78 @@ static int efx_phc_settime(struct ptp_clock_info *ptp,
|
||||||
static int efx_phc_enable(struct ptp_clock_info *ptp,
|
static int efx_phc_enable(struct ptp_clock_info *ptp,
|
||||||
struct ptp_clock_request *request, int on);
|
struct ptp_clock_request *request, int on);
|
||||||
|
|
||||||
|
#define PTP_SW_STAT(ext_name, field_name) \
|
||||||
|
{ #ext_name, 0, offsetof(struct efx_ptp_data, field_name) }
|
||||||
|
#define PTP_MC_STAT(ext_name, mcdi_name) \
|
||||||
|
{ #ext_name, 32, MC_CMD_PTP_OUT_STATUS_STATS_ ## mcdi_name ## _OFST }
|
||||||
|
static const struct efx_hw_stat_desc efx_ptp_stat_desc[] = {
|
||||||
|
PTP_SW_STAT(ptp_good_syncs, good_syncs),
|
||||||
|
PTP_SW_STAT(ptp_fast_syncs, fast_syncs),
|
||||||
|
PTP_SW_STAT(ptp_bad_syncs, bad_syncs),
|
||||||
|
PTP_SW_STAT(ptp_sync_timeouts, sync_timeouts),
|
||||||
|
PTP_SW_STAT(ptp_no_time_syncs, no_time_syncs),
|
||||||
|
PTP_SW_STAT(ptp_invalid_sync_windows, invalid_sync_windows),
|
||||||
|
PTP_SW_STAT(ptp_undersize_sync_windows, undersize_sync_windows),
|
||||||
|
PTP_SW_STAT(ptp_oversize_sync_windows, oversize_sync_windows),
|
||||||
|
PTP_SW_STAT(ptp_rx_no_timestamp, rx_no_timestamp),
|
||||||
|
PTP_MC_STAT(ptp_tx_timestamp_packets, TX),
|
||||||
|
PTP_MC_STAT(ptp_rx_timestamp_packets, RX),
|
||||||
|
PTP_MC_STAT(ptp_timestamp_packets, TS),
|
||||||
|
PTP_MC_STAT(ptp_filter_matches, FM),
|
||||||
|
PTP_MC_STAT(ptp_non_filter_matches, NFM),
|
||||||
|
};
|
||||||
|
#define PTP_STAT_COUNT ARRAY_SIZE(efx_ptp_stat_desc)
|
||||||
|
static const unsigned long efx_ptp_stat_mask[] = {
|
||||||
|
[0 ... BITS_TO_LONGS(PTP_STAT_COUNT) - 1] = ~0UL,
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t efx_ptp_describe_stats(struct efx_nic *efx, u8 *strings)
|
||||||
|
{
|
||||||
|
if (!efx->ptp_data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return efx_nic_describe_stats(efx_ptp_stat_desc, PTP_STAT_COUNT,
|
||||||
|
efx_ptp_stat_mask, strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t efx_ptp_update_stats(struct efx_nic *efx, u64 *stats)
|
||||||
|
{
|
||||||
|
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_STATUS_LEN);
|
||||||
|
MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_STATUS_LEN);
|
||||||
|
size_t i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!efx->ptp_data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Copy software statistics */
|
||||||
|
for (i = 0; i < PTP_STAT_COUNT; i++) {
|
||||||
|
if (efx_ptp_stat_desc[i].dma_width)
|
||||||
|
continue;
|
||||||
|
stats[i] = *(unsigned int *)((char *)efx->ptp_data +
|
||||||
|
efx_ptp_stat_desc[i].offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetch MC statistics. We *must* fill in all statistics or
|
||||||
|
* risk leaking kernel memory to userland, so if the MCDI
|
||||||
|
* request fails we pretend we got zeroes.
|
||||||
|
*/
|
||||||
|
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_STATUS);
|
||||||
|
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
|
||||||
|
rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf),
|
||||||
|
outbuf, sizeof(outbuf), NULL);
|
||||||
|
if (rc) {
|
||||||
|
netif_err(efx, hw, efx->net_dev,
|
||||||
|
"MC_CMD_PTP_OP_STATUS failed (%d)\n", rc);
|
||||||
|
memset(outbuf, 0, sizeof(outbuf));
|
||||||
|
}
|
||||||
|
efx_nic_update_stats(efx_ptp_stat_desc, PTP_STAT_COUNT,
|
||||||
|
efx_ptp_stat_mask,
|
||||||
|
stats, _MCDI_PTR(outbuf, 0), false);
|
||||||
|
|
||||||
|
return PTP_STAT_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
/* For Siena platforms NIC time is s and ns */
|
/* For Siena platforms NIC time is s and ns */
|
||||||
static void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor)
|
static void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor)
|
||||||
{
|
{
|
||||||
|
@ -633,7 +724,8 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
|
||||||
/* Read the set of results and find the last good host-MC
|
/* Read the set of results and find the last good host-MC
|
||||||
* synchronization result. The MC times when it finishes reading the
|
* synchronization result. The MC times when it finishes reading the
|
||||||
* host time so the corrected window time should be fairly constant
|
* host time so the corrected window time should be fairly constant
|
||||||
* for a given platform.
|
* for a given platform. Increment stats for any results that appear
|
||||||
|
* to be erroneous.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < number_readings; i++) {
|
for (i = 0; i < number_readings; i++) {
|
||||||
s32 window, corrected;
|
s32 window, corrected;
|
||||||
|
@ -658,9 +750,13 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
|
||||||
* interrupt or other delay occurred between reading the system
|
* interrupt or other delay occurred between reading the system
|
||||||
* time and writing it to MC memory.
|
* time and writing it to MC memory.
|
||||||
*/
|
*/
|
||||||
if (window >= SYNCHRONISATION_GRANULARITY_NS &&
|
if (window < SYNCHRONISATION_GRANULARITY_NS) {
|
||||||
corrected < MAX_SYNCHRONISATION_NS &&
|
++ptp->invalid_sync_windows;
|
||||||
corrected >= ptp->min_synchronisation_ns) {
|
} else if (corrected >= MAX_SYNCHRONISATION_NS) {
|
||||||
|
++ptp->undersize_sync_windows;
|
||||||
|
} else if (corrected < ptp->min_synchronisation_ns) {
|
||||||
|
++ptp->oversize_sync_windows;
|
||||||
|
} else {
|
||||||
ngood++;
|
ngood++;
|
||||||
last_good = i;
|
last_good = i;
|
||||||
}
|
}
|
||||||
|
@ -741,6 +837,11 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
|
||||||
loops++;
|
loops++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loops <= 1)
|
||||||
|
++ptp->fast_syncs;
|
||||||
|
if (!time_before(jiffies, timeout))
|
||||||
|
++ptp->sync_timeouts;
|
||||||
|
|
||||||
if (ACCESS_ONCE(*start))
|
if (ACCESS_ONCE(*start))
|
||||||
efx_ptp_send_times(efx, &last_time);
|
efx_ptp_send_times(efx, &last_time);
|
||||||
|
|
||||||
|
@ -749,9 +850,20 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
|
||||||
MC_CMD_PTP_IN_SYNCHRONIZE_LEN,
|
MC_CMD_PTP_IN_SYNCHRONIZE_LEN,
|
||||||
synch_buf, sizeof(synch_buf),
|
synch_buf, sizeof(synch_buf),
|
||||||
&response_length);
|
&response_length);
|
||||||
if (rc == 0)
|
if (rc == 0) {
|
||||||
rc = efx_ptp_process_times(efx, synch_buf, response_length,
|
rc = efx_ptp_process_times(efx, synch_buf, response_length,
|
||||||
&last_time);
|
&last_time);
|
||||||
|
if (rc == 0)
|
||||||
|
++ptp->good_syncs;
|
||||||
|
else
|
||||||
|
++ptp->no_time_syncs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increment the bad syncs counter if the synchronize fails, whatever
|
||||||
|
* the reason.
|
||||||
|
*/
|
||||||
|
if (rc != 0)
|
||||||
|
++ptp->bad_syncs;
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -907,9 +1019,7 @@ static void efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q)
|
||||||
__skb_queue_tail(q, skb);
|
__skb_queue_tail(q, skb);
|
||||||
} else if (time_after(jiffies, match->expiry)) {
|
} else if (time_after(jiffies, match->expiry)) {
|
||||||
match->state = PTP_PACKET_STATE_TIMED_OUT;
|
match->state = PTP_PACKET_STATE_TIMED_OUT;
|
||||||
if (net_ratelimit())
|
++ptp->rx_no_timestamp;
|
||||||
netif_warn(efx, rx_err, efx->net_dev,
|
|
||||||
"PTP packet - no timestamp seen\n");
|
|
||||||
__skb_queue_tail(q, skb);
|
__skb_queue_tail(q, skb);
|
||||||
} else {
|
} else {
|
||||||
/* Replace unprocessed entry and stop */
|
/* Replace unprocessed entry and stop */
|
||||||
|
|
Loading…
Add table
Reference in a new issue