diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2013-12-10 21:36:08 -0500 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2013-12-12 17:07:30 -0500 |
commit | 99691c4ac112666fe8aef30cffef1cc9f6f94f79 (patch) | |
tree | 43fe421c4ca59224489109723d2c96fbadd024e7 | |
parent | 79ac47ae74c1e905c204c2eeb090c8098d570ef2 (diff) |
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>
-rw-r--r-- | drivers/net/ethernet/sfc/ethtool.c | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/nic.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ptp.c | 126 |
3 files changed, 127 insertions, 9 deletions
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index bb2af8045281..f181522688b2 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c | |||
@@ -359,7 +359,8 @@ static int efx_ethtool_get_sset_count(struct net_device *net_dev, | |||
359 | switch (string_set) { | 359 | switch (string_set) { |
360 | case ETH_SS_STATS: | 360 | case ETH_SS_STATS: |
361 | return efx->type->describe_stats(efx, NULL) + | 361 | return efx->type->describe_stats(efx, NULL) + |
362 | EFX_ETHTOOL_SW_STAT_COUNT; | 362 | EFX_ETHTOOL_SW_STAT_COUNT + |
363 | efx_ptp_describe_stats(efx, NULL); | ||
363 | case ETH_SS_TEST: | 364 | case ETH_SS_TEST: |
364 | return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); | 365 | return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); |
365 | default: | 366 | default: |
@@ -380,6 +381,8 @@ static void efx_ethtool_get_strings(struct net_device *net_dev, | |||
380 | for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) | 381 | for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) |
381 | strlcpy(strings + i * ETH_GSTRING_LEN, | 382 | strlcpy(strings + i * ETH_GSTRING_LEN, |
382 | efx_sw_stat_desc[i].name, ETH_GSTRING_LEN); | 383 | efx_sw_stat_desc[i].name, ETH_GSTRING_LEN); |
384 | strings += EFX_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN; | ||
385 | efx_ptp_describe_stats(efx, strings); | ||
383 | break; | 386 | break; |
384 | case ETH_SS_TEST: | 387 | case ETH_SS_TEST: |
385 | efx_ethtool_fill_self_tests(efx, NULL, strings, NULL); | 388 | efx_ethtool_fill_self_tests(efx, NULL, strings, NULL); |
@@ -429,8 +432,11 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, | |||
429 | break; | 432 | break; |
430 | } | 433 | } |
431 | } | 434 | } |
435 | data += EFX_ETHTOOL_SW_STAT_COUNT; | ||
432 | 436 | ||
433 | spin_unlock_bh(&efx->stats_lock); | 437 | spin_unlock_bh(&efx->stats_lock); |
438 | |||
439 | efx_ptp_update_stats(efx, data); | ||
434 | } | 440 | } |
435 | 441 | ||
436 | static void efx_ethtool_self_test(struct net_device *net_dev, | 442 | static void efx_ethtool_self_test(struct net_device *net_dev, |
diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 170ac6ee3ab2..a001fae1a8d7 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h | |||
@@ -566,6 +566,8 @@ int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted, | |||
566 | unsigned int new_mode); | 566 | unsigned int new_mode); |
567 | int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); | 567 | int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); |
568 | void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev); | 568 | void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev); |
569 | size_t efx_ptp_describe_stats(struct efx_nic *efx, u8 *strings); | ||
570 | size_t efx_ptp_update_stats(struct efx_nic *efx, u64 *stats); | ||
569 | void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev); | 571 | void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev); |
570 | void __efx_rx_skb_attach_timestamp(struct efx_channel *channel, | 572 | void __efx_rx_skb_attach_timestamp(struct efx_channel *channel, |
571 | struct sk_buff *skb); | 573 | struct sk_buff *skb); |
diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index 084e2d44790f..7aa070813960 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c | |||
@@ -255,6 +255,15 @@ struct efx_ptp_timeset { | |||
255 | * @nic_ts_enabled: Flag indicating if NIC generated TS events are handled | 255 | * @nic_ts_enabled: Flag indicating if NIC generated TS events are handled |
256 | * @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids | 256 | * @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids |
257 | * allocations in main data path). | 257 | * allocations in main data path). |
258 | * @good_syncs: Number of successful synchronisations. | ||
259 | * @fast_syncs: Number of synchronisations requiring short delay | ||
260 | * @bad_syncs: Number of failed synchronisations. | ||
261 | * @sync_timeouts: Number of synchronisation timeouts | ||
262 | * @no_time_syncs: Number of synchronisations with no good times. | ||
263 | * @invalid_sync_windows: Number of sync windows with bad durations. | ||
264 | * @undersize_sync_windows: Number of corrected sync windows that are too small | ||
265 | * @oversize_sync_windows: Number of corrected sync windows that are too large | ||
266 | * @rx_no_timestamp: Number of packets received without a timestamp. | ||
258 | * @timeset: Last set of synchronisation statistics. | 267 | * @timeset: Last set of synchronisation statistics. |
259 | */ | 268 | */ |
260 | struct efx_ptp_data { | 269 | struct efx_ptp_data { |
@@ -300,6 +309,16 @@ struct efx_ptp_data { | |||
300 | struct workqueue_struct *pps_workwq; | 309 | struct workqueue_struct *pps_workwq; |
301 | bool nic_ts_enabled; | 310 | bool nic_ts_enabled; |
302 | MCDI_DECLARE_BUF(txbuf, MC_CMD_PTP_IN_TRANSMIT_LENMAX); | 311 | MCDI_DECLARE_BUF(txbuf, MC_CMD_PTP_IN_TRANSMIT_LENMAX); |
312 | |||
313 | unsigned int good_syncs; | ||
314 | unsigned int fast_syncs; | ||
315 | unsigned int bad_syncs; | ||
316 | unsigned int sync_timeouts; | ||
317 | unsigned int no_time_syncs; | ||
318 | unsigned int invalid_sync_windows; | ||
319 | unsigned int undersize_sync_windows; | ||
320 | unsigned int oversize_sync_windows; | ||
321 | unsigned int rx_no_timestamp; | ||
303 | struct efx_ptp_timeset | 322 | struct efx_ptp_timeset |
304 | timeset[MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_MAXNUM]; | 323 | timeset[MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_MAXNUM]; |
305 | }; | 324 | }; |
@@ -312,6 +331,78 @@ static int efx_phc_settime(struct ptp_clock_info *ptp, | |||
312 | static int efx_phc_enable(struct ptp_clock_info *ptp, | 331 | static int efx_phc_enable(struct ptp_clock_info *ptp, |
313 | struct ptp_clock_request *request, int on); | 332 | struct ptp_clock_request *request, int on); |
314 | 333 | ||
334 | #define PTP_SW_STAT(ext_name, field_name) \ | ||
335 | { #ext_name, 0, offsetof(struct efx_ptp_data, field_name) } | ||
336 | #define PTP_MC_STAT(ext_name, mcdi_name) \ | ||
337 | { #ext_name, 32, MC_CMD_PTP_OUT_STATUS_STATS_ ## mcdi_name ## _OFST } | ||
338 | static const struct efx_hw_stat_desc efx_ptp_stat_desc[] = { | ||
339 | PTP_SW_STAT(ptp_good_syncs, good_syncs), | ||
340 | PTP_SW_STAT(ptp_fast_syncs, fast_syncs), | ||
341 | PTP_SW_STAT(ptp_bad_syncs, bad_syncs), | ||
342 | PTP_SW_STAT(ptp_sync_timeouts, sync_timeouts), | ||
343 | PTP_SW_STAT(ptp_no_time_syncs, no_time_syncs), | ||
344 | PTP_SW_STAT(ptp_invalid_sync_windows, invalid_sync_windows), | ||
345 | PTP_SW_STAT(ptp_undersize_sync_windows, undersize_sync_windows), | ||
346 | PTP_SW_STAT(ptp_oversize_sync_windows, oversize_sync_windows), | ||
347 | PTP_SW_STAT(ptp_rx_no_timestamp, rx_no_timestamp), | ||
348 | PTP_MC_STAT(ptp_tx_timestamp_packets, TX), | ||
349 | PTP_MC_STAT(ptp_rx_timestamp_packets, RX), | ||
350 | PTP_MC_STAT(ptp_timestamp_packets, TS), | ||
351 | PTP_MC_STAT(ptp_filter_matches, FM), | ||
352 | PTP_MC_STAT(ptp_non_filter_matches, NFM), | ||
353 | }; | ||
354 | #define PTP_STAT_COUNT ARRAY_SIZE(efx_ptp_stat_desc) | ||
355 | static const unsigned long efx_ptp_stat_mask[] = { | ||
356 | [0 ... BITS_TO_LONGS(PTP_STAT_COUNT) - 1] = ~0UL, | ||
357 | }; | ||
358 | |||
359 | size_t efx_ptp_describe_stats(struct efx_nic *efx, u8 *strings) | ||
360 | { | ||
361 | if (!efx->ptp_data) | ||
362 | return 0; | ||
363 | |||
364 | return efx_nic_describe_stats(efx_ptp_stat_desc, PTP_STAT_COUNT, | ||
365 | efx_ptp_stat_mask, strings); | ||
366 | } | ||
367 | |||
368 | size_t efx_ptp_update_stats(struct efx_nic *efx, u64 *stats) | ||
369 | { | ||
370 | MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_STATUS_LEN); | ||
371 | MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_STATUS_LEN); | ||
372 | size_t i; | ||
373 | int rc; | ||
374 | |||
375 | if (!efx->ptp_data) | ||
376 | return 0; | ||
377 | |||
378 | /* Copy software statistics */ | ||
379 | for (i = 0; i < PTP_STAT_COUNT; i++) { | ||
380 | if (efx_ptp_stat_desc[i].dma_width) | ||
381 | continue; | ||
382 | stats[i] = *(unsigned int *)((char *)efx->ptp_data + | ||
383 | efx_ptp_stat_desc[i].offset); | ||
384 | } | ||
385 | |||
386 | /* Fetch MC statistics. We *must* fill in all statistics or | ||
387 | * risk leaking kernel memory to userland, so if the MCDI | ||
388 | * request fails we pretend we got zeroes. | ||
389 | */ | ||
390 | MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_STATUS); | ||
391 | MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); | ||
392 | rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), | ||
393 | outbuf, sizeof(outbuf), NULL); | ||
394 | if (rc) { | ||
395 | netif_err(efx, hw, efx->net_dev, | ||
396 | "MC_CMD_PTP_OP_STATUS failed (%d)\n", rc); | ||
397 | memset(outbuf, 0, sizeof(outbuf)); | ||
398 | } | ||
399 | efx_nic_update_stats(efx_ptp_stat_desc, PTP_STAT_COUNT, | ||
400 | efx_ptp_stat_mask, | ||
401 | stats, _MCDI_PTR(outbuf, 0), false); | ||
402 | |||
403 | return PTP_STAT_COUNT; | ||
404 | } | ||
405 | |||
315 | /* For Siena platforms NIC time is s and ns */ | 406 | /* For Siena platforms NIC time is s and ns */ |
316 | static void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor) | 407 | static void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor) |
317 | { | 408 | { |
@@ -633,7 +724,8 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf), | |||
633 | /* Read the set of results and find the last good host-MC | 724 | /* Read the set of results and find the last good host-MC |
634 | * synchronization result. The MC times when it finishes reading the | 725 | * synchronization result. The MC times when it finishes reading the |
635 | * host time so the corrected window time should be fairly constant | 726 | * host time so the corrected window time should be fairly constant |
636 | * for a given platform. | 727 | * for a given platform. Increment stats for any results that appear |
728 | * to be erroneous. | ||
637 | */ | 729 | */ |
638 | for (i = 0; i < number_readings; i++) { | 730 | for (i = 0; i < number_readings; i++) { |
639 | s32 window, corrected; | 731 | s32 window, corrected; |
@@ -658,9 +750,13 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf), | |||
658 | * interrupt or other delay occurred between reading the system | 750 | * interrupt or other delay occurred between reading the system |
659 | * time and writing it to MC memory. | 751 | * time and writing it to MC memory. |
660 | */ | 752 | */ |
661 | if (window >= SYNCHRONISATION_GRANULARITY_NS && | 753 | if (window < SYNCHRONISATION_GRANULARITY_NS) { |
662 | corrected < MAX_SYNCHRONISATION_NS && | 754 | ++ptp->invalid_sync_windows; |
663 | corrected >= ptp->min_synchronisation_ns) { | 755 | } else if (corrected >= MAX_SYNCHRONISATION_NS) { |
756 | ++ptp->undersize_sync_windows; | ||
757 | } else if (corrected < ptp->min_synchronisation_ns) { | ||
758 | ++ptp->oversize_sync_windows; | ||
759 | } else { | ||
664 | ngood++; | 760 | ngood++; |
665 | last_good = i; | 761 | last_good = i; |
666 | } | 762 | } |
@@ -741,6 +837,11 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings) | |||
741 | loops++; | 837 | loops++; |
742 | } | 838 | } |
743 | 839 | ||
840 | if (loops <= 1) | ||
841 | ++ptp->fast_syncs; | ||
842 | if (!time_before(jiffies, timeout)) | ||
843 | ++ptp->sync_timeouts; | ||
844 | |||
744 | if (ACCESS_ONCE(*start)) | 845 | if (ACCESS_ONCE(*start)) |
745 | efx_ptp_send_times(efx, &last_time); | 846 | efx_ptp_send_times(efx, &last_time); |
746 | 847 | ||
@@ -749,9 +850,20 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings) | |||
749 | MC_CMD_PTP_IN_SYNCHRONIZE_LEN, | 850 | MC_CMD_PTP_IN_SYNCHRONIZE_LEN, |
750 | synch_buf, sizeof(synch_buf), | 851 | synch_buf, sizeof(synch_buf), |
751 | &response_length); | 852 | &response_length); |
752 | if (rc == 0) | 853 | if (rc == 0) { |
753 | rc = efx_ptp_process_times(efx, synch_buf, response_length, | 854 | rc = efx_ptp_process_times(efx, synch_buf, response_length, |
754 | &last_time); | 855 | &last_time); |
856 | if (rc == 0) | ||
857 | ++ptp->good_syncs; | ||
858 | else | ||
859 | ++ptp->no_time_syncs; | ||
860 | } | ||
861 | |||
862 | /* Increment the bad syncs counter if the synchronize fails, whatever | ||
863 | * the reason. | ||
864 | */ | ||
865 | if (rc != 0) | ||
866 | ++ptp->bad_syncs; | ||
755 | 867 | ||
756 | return rc; | 868 | return rc; |
757 | } | 869 | } |
@@ -907,9 +1019,7 @@ static void efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q) | |||
907 | __skb_queue_tail(q, skb); | 1019 | __skb_queue_tail(q, skb); |
908 | } else if (time_after(jiffies, match->expiry)) { | 1020 | } else if (time_after(jiffies, match->expiry)) { |
909 | match->state = PTP_PACKET_STATE_TIMED_OUT; | 1021 | match->state = PTP_PACKET_STATE_TIMED_OUT; |
910 | if (net_ratelimit()) | 1022 | ++ptp->rx_no_timestamp; |
911 | netif_warn(efx, rx_err, efx->net_dev, | ||
912 | "PTP packet - no timestamp seen\n"); | ||
913 | __skb_queue_tail(q, skb); | 1023 | __skb_queue_tail(q, skb); |
914 | } else { | 1024 | } else { |
915 | /* Replace unprocessed entry and stop */ | 1025 | /* Replace unprocessed entry and stop */ |