Skip to content

Commit

Permalink
sfc: Add PTP counters to ethtool stats
Browse files Browse the repository at this point in the history
These were implemented by Andrew Jackson and Laurence Evans but not
previously included in-tree.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
  • Loading branch information
Ben Hutchings committed Dec 12, 2013
1 parent 79ac47a commit 99691c4
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 9 deletions.
8 changes: 7 additions & 1 deletion drivers/net/ethernet/sfc/ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ static int efx_ethtool_get_sset_count(struct net_device *net_dev,
switch (string_set) {
case ETH_SS_STATS:
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:
return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL);
default:
Expand All @@ -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++)
strlcpy(strings + i * 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;
case ETH_SS_TEST:
efx_ethtool_fill_self_tests(efx, NULL, strings, NULL);
Expand Down Expand Up @@ -429,8 +432,11 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
break;
}
}
data += EFX_ETHTOOL_SW_STAT_COUNT;

spin_unlock_bh(&efx->stats_lock);

efx_ptp_update_stats(efx, data);
}

static void efx_ethtool_self_test(struct net_device *net_dev,
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/sfc/nic.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,8 @@ int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
unsigned int new_mode);
int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
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_rx_skb_attach_timestamp(struct efx_channel *channel,
struct sk_buff *skb);
Expand Down
126 changes: 118 additions & 8 deletions drivers/net/ethernet/sfc/ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,15 @@ struct efx_ptp_timeset {
* @nic_ts_enabled: Flag indicating if NIC generated TS events are handled
* @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids
* 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.
*/
struct efx_ptp_data {
Expand Down Expand Up @@ -300,6 +309,16 @@ struct efx_ptp_data {
struct workqueue_struct *pps_workwq;
bool nic_ts_enabled;
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
timeset[MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_MAXNUM];
};
Expand All @@ -312,6 +331,78 @@ static int efx_phc_settime(struct ptp_clock_info *ptp,
static int efx_phc_enable(struct ptp_clock_info *ptp,
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 */
static void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor)
{
Expand Down Expand Up @@ -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
* synchronization result. The MC times when it finishes reading the
* 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++) {
s32 window, corrected;
Expand All @@ -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
* time and writing it to MC memory.
*/
if (window >= SYNCHRONISATION_GRANULARITY_NS &&
corrected < MAX_SYNCHRONISATION_NS &&
corrected >= ptp->min_synchronisation_ns) {
if (window < SYNCHRONISATION_GRANULARITY_NS) {
++ptp->invalid_sync_windows;
} else if (corrected >= MAX_SYNCHRONISATION_NS) {
++ptp->undersize_sync_windows;
} else if (corrected < ptp->min_synchronisation_ns) {
++ptp->oversize_sync_windows;
} else {
ngood++;
last_good = i;
}
Expand Down Expand Up @@ -741,6 +837,11 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
loops++;
}

if (loops <= 1)
++ptp->fast_syncs;
if (!time_before(jiffies, timeout))
++ptp->sync_timeouts;

if (ACCESS_ONCE(*start))
efx_ptp_send_times(efx, &last_time);

Expand All @@ -749,9 +850,20 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
MC_CMD_PTP_IN_SYNCHRONIZE_LEN,
synch_buf, sizeof(synch_buf),
&response_length);
if (rc == 0)
if (rc == 0) {
rc = efx_ptp_process_times(efx, synch_buf, response_length,
&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;
}
Expand Down Expand Up @@ -907,9 +1019,7 @@ static void efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q)
__skb_queue_tail(q, skb);
} else if (time_after(jiffies, match->expiry)) {
match->state = PTP_PACKET_STATE_TIMED_OUT;
if (net_ratelimit())
netif_warn(efx, rx_err, efx->net_dev,
"PTP packet - no timestamp seen\n");
++ptp->rx_no_timestamp;
__skb_queue_tail(q, skb);
} else {
/* Replace unprocessed entry and stop */
Expand Down

0 comments on commit 99691c4

Please sign in to comment.