net: stats: Add infrastructure for collecting ethernet stats
Make sure we are able to collect ethernet statistics and query
it via net management API.
Fixes #6899
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
diff --git a/include/net/ethernet.h b/include/net/ethernet.h
index 2696785..08193af 100644
--- a/include/net/ethernet.h
+++ b/include/net/ethernet.h
@@ -104,6 +104,14 @@
*/
struct net_if_api iface_api;
+#if defined(CONFIG_NET_STATISTICS_ETHERNET)
+ /** Collect optional ethernet specific statistics. This pointer
+ * should be set by driver if statistics needs to be collected
+ * for that driver.
+ */
+ struct net_stats_eth *stats;
+#endif
+
/** Get the device capabilities */
enum ethernet_hw_caps (*get_capabilities)(struct device *dev);
diff --git a/include/net/net_stats.h b/include/net/net_stats.h
index 3538355..d0ac1b5 100644
--- a/include/net/net_stats.h
+++ b/include/net/net_stats.h
@@ -31,8 +31,13 @@
typedef u32_t net_stats_t;
struct net_stats_bytes {
- u32_t sent;
- u32_t received;
+ net_stats_t sent;
+ net_stats_t received;
+};
+
+struct net_stats_pkts {
+ net_stats_t tx;
+ net_stats_t rx;
};
struct net_stats_ip {
@@ -294,6 +299,65 @@
#endif
};
+struct net_stats_eth_errors {
+ net_stats_t rx_length_errors;
+ net_stats_t rx_over_errors;
+ net_stats_t rx_crc_errors;
+ net_stats_t rx_frame_errors;
+ net_stats_t rx_no_buffer_count;
+ net_stats_t rx_missed_errors;
+ net_stats_t rx_long_length_errors;
+ net_stats_t rx_short_length_errors;
+ net_stats_t rx_align_errors;
+ net_stats_t rx_dma_failed;
+ net_stats_t rx_buf_alloc_failed;
+
+ net_stats_t tx_aborted_errors;
+ net_stats_t tx_carrier_errors;
+ net_stats_t tx_fifo_errors;
+ net_stats_t tx_heartbeat_errors;
+ net_stats_t tx_window_errors;
+ net_stats_t tx_dma_failed;
+
+ net_stats_t uncorr_ecc_errors;
+ net_stats_t corr_ecc_errors;
+};
+
+struct net_stats_eth_flow {
+ net_stats_t rx_flow_control_xon;
+ net_stats_t rx_flow_control_xoff;
+ net_stats_t tx_flow_control_xon;
+ net_stats_t tx_flow_control_xoff;
+};
+
+struct net_stats_eth_csum {
+ net_stats_t rx_csum_offload_good;
+ net_stats_t rx_csum_offload_errors;
+};
+
+struct net_stats_eth_hw_timestamp {
+ net_stats_t rx_hwtstamp_cleared;
+ net_stats_t tx_hwtstamp_timeouts;
+ net_stats_t tx_hwtstamp_skipped;
+};
+
+/* Ethernet specific statistics */
+struct net_stats_eth {
+ struct net_stats_bytes bytes;
+ struct net_stats_pkts pkts;
+ struct net_stats_pkts broadcast;
+ struct net_stats_pkts multicast;
+ struct net_stats_pkts errors;
+ struct net_stats_eth_errors error_details;
+ struct net_stats_eth_flow flow_control;
+ struct net_stats_eth_csum csum;
+ struct net_stats_eth_hw_timestamp hw_timestamp;
+ net_stats_t collisions;
+ net_stats_t tx_dropped;
+ net_stats_t tx_timeout_count;
+ net_stats_t tx_restart_queue;
+};
+
#if defined(CONFIG_NET_STATISTICS_USER_API)
/* Management part definitions */
@@ -316,6 +380,7 @@
NET_REQUEST_STATS_CMD_GET_UDP,
NET_REQUEST_STATS_CMD_GET_TCP,
NET_REQUEST_STATS_CMD_GET_RPL,
+ NET_REQUEST_STATS_CMD_GET_ETHERNET,
};
#define NET_REQUEST_STATS_GET_ALL \
@@ -387,6 +452,13 @@
NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_RPL);
#endif /* CONFIG_NET_STATISTICS_RPL */
+#if defined(CONFIG_NET_STATISTICS_ETHERNET)
+#define NET_REQUEST_STATS_GET_ETHERNET \
+ (_NET_STATS_BASE | NET_REQUEST_STATS_CMD_GET_ETHERNET)
+
+NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_ETHERNET);
+#endif /* CONFIG_NET_STATISTICS_ETHERNET */
+
#endif /* CONFIG_NET_STATISTICS_USER_API */
/**
diff --git a/subsys/net/ip/Kconfig.stats b/subsys/net/ip/Kconfig.stats
index 7c9c13b..4035107 100644
--- a/subsys/net/ip/Kconfig.stats
+++ b/subsys/net/ip/Kconfig.stats
@@ -93,4 +93,13 @@
help
Keep track of MLD related statistics
+config NET_STATISTICS_ETHERNET
+ bool "Ethernet statistics"
+ depends on NET_L2_ETHERNET
+ default y
+ help
+ Keep track of Ethernet related statistics. Note that this
+ requires support from the ethernet driver. The driver needs
+ to collect the statistics.
+
endif # NET_STATISTICS
diff --git a/subsys/net/ip/l2/ethernet/CMakeLists.txt b/subsys/net/ip/l2/ethernet/CMakeLists.txt
index d37d4ac..54257d7 100644
--- a/subsys/net/ip/l2/ethernet/CMakeLists.txt
+++ b/subsys/net/ip/l2/ethernet/CMakeLists.txt
@@ -7,3 +7,4 @@
zephyr_library_sources_ifdef(CONFIG_NET_ARP arp.c)
zephyr_library_sources_ifdef(CONFIG_NET_L2_ETHERNET ethernet.c)
zephyr_library_sources_ifdef(CONFIG_NET_L2_ETHERNET_MGMT ethernet_mgmt.c)
+zephyr_library_sources_ifdef(CONFIG_NET_STATISTICS_ETHERNET ethernet_stats.c)
diff --git a/subsys/net/ip/l2/ethernet/eth_stats.h b/subsys/net/ip/l2/ethernet/eth_stats.h
new file mode 100644
index 0000000..e511a3a
--- /dev/null
+++ b/subsys/net/ip/l2/ethernet/eth_stats.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __ETH_STATS_H__
+#define __ETH_STATS_H__
+
+#if defined(CONFIG_NET_STATISTICS_ETHERNET)
+
+#include <net/net_ip.h>
+#include <net/net_stats.h>
+#include <net/net_if.h>
+
+static inline void eth_stats_update_bytes_rx(struct net_if *iface,
+ u32_t bytes)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->bytes.received += bytes;
+}
+
+static inline void eth_stats_update_bytes_tx(struct net_if *iface,
+ u32_t bytes)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->bytes.sent += bytes;
+}
+
+static inline void eth_stats_update_pkts_rx(struct net_if *iface)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->pkts.rx++;
+}
+
+static inline void eth_stats_update_pkts_tx(struct net_if *iface)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->pkts.tx++;
+}
+
+static inline void eth_stats_update_broadcast_rx(struct net_if *iface)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->broadcast.rx++;
+}
+
+static inline void eth_stats_update_broadcast_tx(struct net_if *iface)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->broadcast.tx++;
+}
+
+static inline void eth_stats_update_multicast_rx(struct net_if *iface)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->multicast.rx++;
+}
+
+static inline void eth_stats_update_multicast_tx(struct net_if *iface)
+{
+ struct net_stats_eth *stats;
+
+ stats = ((const struct ethernet_api *)
+ net_if_get_device(iface)->driver_api)->stats;
+ if (!stats) {
+ return;
+ }
+
+ stats->multicast.tx++;
+}
+
+#else /* CONFIG_NET_STATISTICS_ETHERNET */
+
+#define eth_stats_update_bytes_rx(iface, bytes)
+#define eth_stats_update_bytes_tx(iface, bytes)
+#define eth_stats_update_pkts_rx(iface)
+#define eth_stats_update_pkts_tx(iface)
+#define eth_stats_update_broadcast_rx(iface)
+#define eth_stats_update_broadcast_tx(iface)
+#define eth_stats_update_multicast_rx(iface)
+#define eth_stats_update_multicast_tx(iface)
+
+#endif /* CONFIG_NET_STATISTICS_ETHERNET */
+
+#endif /* __ETH_STATS_H__ */
diff --git a/subsys/net/ip/l2/ethernet/ethernet_stats.c b/subsys/net/ip/l2/ethernet/ethernet_stats.c
new file mode 100644
index 0000000..de475d5
--- /dev/null
+++ b/subsys/net/ip/l2/ethernet/ethernet_stats.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <kernel.h>
+#include <string.h>
+#include <errno.h>
+#include <net/net_core.h>
+#include <net/ethernet.h>
+
+#include "net_stats.h"
+
+#if defined(CONFIG_NET_STATISTICS_USER_API)
+
+static int eth_stats_get(u32_t mgmt_request, struct net_if *iface,
+ void *data, size_t len)
+{
+ const struct ethernet_api *eth;
+ size_t len_chk = 0;
+ void *src = NULL;
+
+ switch (NET_MGMT_GET_COMMAND(mgmt_request)) {
+ case NET_REQUEST_STATS_CMD_GET_ETHERNET:
+ if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
+ return -ENOENT;
+ }
+
+ eth = net_if_get_device(iface)->driver_api;
+ len_chk = sizeof(struct net_stats_eth);
+ src = eth->stats;
+ break;
+ }
+
+ if (len != len_chk || !src) {
+ return -EINVAL;
+ }
+
+ memcpy(data, src, len);
+
+ return 0;
+}
+
+NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_ETHERNET,
+ eth_stats_get);
+
+#endif /* CONFIG_NET_STATISTICS_USER_API */