diff mbox

[API-NEXT,PATCHv4,1/5] linux-generic: sockets: implement pktio statistics counters

Message ID 1448370648-18974-2-git-send-email-maxim.uvarov@linaro.org
State New
Headers show

Commit Message

Maxim Uvarov Nov. 24, 2015, 1:10 p.m. UTC
Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
---
 platform/linux-generic/Makefile.am                 |   2 +
 .../linux-generic/include/odp_packet_io_internal.h |  11 ++
 platform/linux-generic/include/odp_packet_socket.h |   6 +
 platform/linux-generic/odp_packet_io.c             |  53 +++++++
 platform/linux-generic/pktio/ethtool.c             | 163 +++++++++++++++++++++
 platform/linux-generic/pktio/socket.c              |  84 +++++++++++
 platform/linux-generic/pktio/socket_mmap.c         |  19 +++
 platform/linux-generic/pktio/sysfs.c               |  76 ++++++++++
 8 files changed, 414 insertions(+)
 create mode 100644 platform/linux-generic/pktio/ethtool.c
 create mode 100644 platform/linux-generic/pktio/sysfs.c

Comments

Stuart Haslam Dec. 3, 2015, 12:32 p.m. UTC | #1
On Tue, Nov 24, 2015 at 04:10:44PM +0300, Maxim Uvarov wrote:
> Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
> ---
>  platform/linux-generic/Makefile.am                 |   2 +
>  .../linux-generic/include/odp_packet_io_internal.h |  11 ++
>  platform/linux-generic/include/odp_packet_socket.h |   6 +
>  platform/linux-generic/odp_packet_io.c             |  53 +++++++
>  platform/linux-generic/pktio/ethtool.c             | 163 +++++++++++++++++++++
>  platform/linux-generic/pktio/socket.c              |  84 +++++++++++
>  platform/linux-generic/pktio/socket_mmap.c         |  19 +++
>  platform/linux-generic/pktio/sysfs.c               |  76 ++++++++++
>  8 files changed, 414 insertions(+)
>  create mode 100644 platform/linux-generic/pktio/ethtool.c
>  create mode 100644 platform/linux-generic/pktio/sysfs.c
> 
> diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
> index a6b6029..72c16f3 100644
> --- a/platform/linux-generic/Makefile.am
> +++ b/platform/linux-generic/Makefile.am
> @@ -166,11 +166,13 @@ __LIB__libodp_la_SOURCES = \
>  			   odp_packet.c \
>  			   odp_packet_flags.c \
>  			   odp_packet_io.c \
> +			   pktio/ethtool.c \
>  			   pktio/io_ops.c \
>  			   pktio/loop.c \
>  			   pktio/netmap.c \
>  			   pktio/socket.c \
>  			   pktio/socket_mmap.c \
> +			   pktio/sysfs.c \
>  			   odp_pool.c \
>  			   odp_queue.c \
>  			   odp_rwlock.c \
> diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
> index a46c6fe..862cb0f 100644
> --- a/platform/linux-generic/include/odp_packet_io_internal.h
> +++ b/platform/linux-generic/include/odp_packet_io_internal.h
> @@ -84,6 +84,9 @@ struct pktio_entry {
>  		STATE_STOP
>  	} state;
>  	classifier_t cls;		/**< classifier linked with this pktio*/
> +	odp_pktio_stats_t stats;	/**< statistic counters for pktio */
> +	int use_ethtool;		/**< 1 - use ethtool,
> +					     0 - sysfs for statistics */
>  	char name[PKTIO_NAME_LEN];	/**< name of pktio provided to
>  					   pktio_open() */
>  	odp_pktio_param_t param;
> @@ -108,6 +111,8 @@ typedef struct pktio_if_ops {
>  	int (*close)(pktio_entry_t *pktio_entry);
>  	int (*start)(pktio_entry_t *pktio_entry);
>  	int (*stop)(pktio_entry_t *pktio_entry);
> +	int (*stats)(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats);
> +	int (*stats_reset)(pktio_entry_t *pktio_entry);
>  	int (*recv)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
>  		    unsigned len);
>  	int (*send)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
> @@ -160,6 +165,12 @@ extern const pktio_if_ops_t pcap_pktio_ops;
>  #endif
>  extern const pktio_if_ops_t * const pktio_if_ops[];
>  
> +int sysfs_stats(pktio_entry_t *pktio_entry,
> +		odp_pktio_stats_t *stats);
> +int sock_stats_reset(pktio_entry_t *pktio_entry);
> +int sock_stats(pktio_entry_t *pktio_entry,
> +	       odp_pktio_stats_t *stats);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/platform/linux-generic/include/odp_packet_socket.h b/platform/linux-generic/include/odp_packet_socket.h
> index a5e0eb3..0836e3c 100644
> --- a/platform/linux-generic/include/odp_packet_socket.h
> +++ b/platform/linux-generic/include/odp_packet_socket.h
> @@ -18,6 +18,7 @@
>  #include <odp/debug.h>
>  #include <odp/pool.h>
>  #include <odp/packet.h>
> +#include <odp/packet_io.h>
>  
>  #include <linux/version.h>
>  
> @@ -114,4 +115,9 @@ int promisc_mode_set_fd(int fd, const char *name, int enable);
>   */
>  int promisc_mode_get_fd(int fd, const char *name);
>  
> +/**
> + * Get ethtool statistics of a packet socket
> + */
> +int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats);
> +
>  #endif
> diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
> index 14fb0c5..bb755aa 100644
> --- a/platform/linux-generic/odp_packet_io.c
> +++ b/platform/linux-generic/odp_packet_io.c
> @@ -905,3 +905,56 @@ int odp_pktio_term_global(void)
>  
>  	return ret;
>  }
> +
> +int odp_pktio_stats(odp_pktio_t pktio,
> +		    odp_pktio_stats_t *stats)
> +{
> +	pktio_entry_t *entry;
> +	int ret = -1;
> +
> +	entry = get_pktio_entry(pktio);
> +	if (entry == NULL) {
> +		ODP_DBG("pktio entry %d does not exist\n", pktio);
> +		return -1;
> +	}
> +
> +	lock_entry(entry);
> +
> +	if (odp_unlikely(is_free(entry))) {
> +		unlock_entry(entry);
> +		ODP_DBG("already freed pktio\n");
> +		return -1;
> +	}
> +
> +	if (entry->s.ops->stats)
> +		ret = entry->s.ops->stats(entry, stats);
> +	unlock_entry(entry);
> +
> +	return ret;
> +}
> +
> +int odp_pktio_stats_reset(odp_pktio_t pktio)
> +{
> +	pktio_entry_t *entry;
> +	int ret = -1;
> +
> +	entry = get_pktio_entry(pktio);
> +	if (entry == NULL) {
> +		ODP_DBG("pktio entry %d does not exist\n", pktio);
> +		return -1;
> +	}
> +
> +	lock_entry(entry);
> +
> +	if (odp_unlikely(is_free(entry))) {
> +		unlock_entry(entry);
> +		ODP_DBG("already freed pktio\n");
> +		return -1;
> +	}
> +
> +	if (entry->s.ops->stats)
> +		ret = entry->s.ops->stats_reset(entry);
> +	unlock_entry(entry);
> +
> +	return ret;
> +}
> diff --git a/platform/linux-generic/pktio/ethtool.c b/platform/linux-generic/pktio/ethtool.c
> new file mode 100644
> index 0000000..573cfe6
> --- /dev/null
> +++ b/platform/linux-generic/pktio/ethtool.c
> @@ -0,0 +1,163 @@
> +/* Copyright (c) 2015, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +#include <sys/ioctl.h>
> +#include <netinet/in.h>
> +#include <linux/sockios.h>
> +#include <linux/if.h>
> +#include <linux/ethtool.h>
> +#include <errno.h>
> +#include <net/if.h>
> +
> +#include <odp.h>
> +#include <odp_packet_socket.h>
> +#include <odp_debug_internal.h>
> +
> +static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
> +{
> +	struct {
> +		struct ethtool_sset_info hdr;
> +		uint32_t buf[1];
> +	} sset_info;
> +	struct ethtool_drvinfo drvinfo;
> +	uint32_t len;
> +	struct ethtool_gstrings *strings;
> +	ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo, n_stats);
> +
> +	sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
> +	sset_info.hdr.reserved = 0;
> +	sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;
> +	ifr->ifr_data =  &sset_info;
> +	if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {
> +		len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
> +	} else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
> +		/* Fallback for old kernel versions */
> +		drvinfo.cmd = ETHTOOL_GDRVINFO;
> +		ifr->ifr_data = &drvinfo;
> +		if (ioctl(fd, SIOCETHTOOL, ifr)) {
> +			__odp_errno = errno;
> +			ODP_ERR("Cannot get stats information\n");
> +			return NULL;
> +		}
> +		len = *(uint32_t *)((char *)&drvinfo + drvinfo_offset);
> +	} else {
> +		__odp_errno = errno;
> +		return NULL;
> +	}
> +
> +	if (!len) {
> +		ODP_ERR("len is zero");
> +		return NULL;
> +	}
> +
> +	strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
> +	if (!strings) {
> +		ODP_ERR("alloc failed\n");
> +		return NULL;
> +	}
> +
> +	strings->cmd = ETHTOOL_GSTRINGS;
> +	strings->string_set = ETH_SS_STATS;
> +	strings->len = len;
> +	ifr->ifr_data = strings;
> +	if (ioctl(fd, SIOCETHTOOL, ifr)) {
> +		__odp_errno = errno;
> +		ODP_ERR("Cannot get stats information\n");
> +		free(strings);
> +		return NULL;
> +	}
> +
> +	return strings;
> +}
> +
> +static int ethtool_stats(int fd, struct ifreq *ifr, odp_pktio_stats_t *stats)
> +{
> +	struct ethtool_gstrings *strings;
> +	struct ethtool_stats *estats;
> +	unsigned int n_stats, i;
> +	int err;
> +	int cnts;
> +
> +	strings = get_stringset(fd, ifr);
> +	if (!strings)
> +		return -1;
> +
> +	n_stats = strings->len;
> +	if (n_stats < 1) {
> +		ODP_ERR("no stats available\n");
> +		free(strings);
> +		return -1;
> +	}
> +
> +	estats = calloc(1, n_stats * sizeof(uint64_t) +
> +			sizeof(struct ethtool_stats));
> +	if (!estats) {
> +		free(strings);
> +		return -1;
> +	}
> +
> +	estats->cmd = ETHTOOL_GSTATS;
> +	estats->n_stats = n_stats;
> +	ifr->ifr_data = estats;
> +	err = ioctl(fd, SIOCETHTOOL, ifr);
> +	if (err < 0) {
> +		__odp_errno = errno;
> +		free(strings);
> +		free(estats);
> +		return -1;
> +	}
> +
> +	cnts = 0;
> +	for (i = 0; i < n_stats; i++) {
> +		char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
> +		uint64_t val = estats->data[i];
> +
> +		if (!strcmp(cnt, "rx_octets")) {
> +			stats->in_octets = val;
> +			cnts++;
> +		} else if (!strcmp(cnt, "rx_ucast_packets")) {
> +			stats->in_ucast_pkts = val;
> +			cnts++;
> +		} else if (!strcmp(cnt, "rx_discards")) {
> +			stats->in_discards = val;
> +			cnts++;
> +		} else if (!strcmp(cnt, "rx_errors")) {
> +			stats->in_errors = val;
> +			cnts++;
> +		} else if (!strcmp(cnt, "tx_octets")) {
> +			stats->out_octets = val;
> +			cnts++;
> +		} else if (!strcmp(cnt, "tx_ucast_packets")) {
> +			stats->out_ucast_pkts = val;
> +			cnts++;
> +		} else if (!strcmp(cnt, "tx_discards")) {
> +			stats->out_discards = val;
> +			cnts++;
> +		} else if (!strcmp(cnt, "tx_errors")) {
> +			stats->out_errors = val;
> +			cnts++;
> +		}
> +	}
> +
> +	free(strings);
> +	free(estats);
> +
> +	/* Ethtool strings came from kernel driver. Name of that
> +	 * strings is not universal. Current function needs to be updated
> +	 * if your driver has different names for counters */
> +	if (cnts < 8)
> +		return -1;

I commented on this in previous version, cnts < 8 is most likely to
indicate there is at least 1 unsupported counter, rather than there
being a genuine error in reading the counter. So this should really
just return 0 and let the fact that the counter values are set to 0
indicate that they're unsupported.

As it is now, the validation test fails when run against the netmap
pktio with an interface that has unsupported stats string names.

--
Stuart.
Maxim Uvarov Dec. 15, 2015, 12:25 p.m. UTC | #2
On 12/03/2015 15:32, Stuart Haslam wrote:
> On Tue, Nov 24, 2015 at 04:10:44PM +0300, Maxim Uvarov wrote:
>> Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
>> ---
>>   platform/linux-generic/Makefile.am                 |   2 +
>>   .../linux-generic/include/odp_packet_io_internal.h |  11 ++
>>   platform/linux-generic/include/odp_packet_socket.h |   6 +
>>   platform/linux-generic/odp_packet_io.c             |  53 +++++++
>>   platform/linux-generic/pktio/ethtool.c             | 163 +++++++++++++++++++++
>>   platform/linux-generic/pktio/socket.c              |  84 +++++++++++
>>   platform/linux-generic/pktio/socket_mmap.c         |  19 +++
>>   platform/linux-generic/pktio/sysfs.c               |  76 ++++++++++
>>   8 files changed, 414 insertions(+)
>>   create mode 100644 platform/linux-generic/pktio/ethtool.c
>>   create mode 100644 platform/linux-generic/pktio/sysfs.c
>>
>> diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
>> index a6b6029..72c16f3 100644
>> --- a/platform/linux-generic/Makefile.am
>> +++ b/platform/linux-generic/Makefile.am
>> @@ -166,11 +166,13 @@ __LIB__libodp_la_SOURCES = \
>>   			   odp_packet.c \
>>   			   odp_packet_flags.c \
>>   			   odp_packet_io.c \
>> +			   pktio/ethtool.c \
>>   			   pktio/io_ops.c \
>>   			   pktio/loop.c \
>>   			   pktio/netmap.c \
>>   			   pktio/socket.c \
>>   			   pktio/socket_mmap.c \
>> +			   pktio/sysfs.c \
>>   			   odp_pool.c \
>>   			   odp_queue.c \
>>   			   odp_rwlock.c \
>> diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
>> index a46c6fe..862cb0f 100644
>> --- a/platform/linux-generic/include/odp_packet_io_internal.h
>> +++ b/platform/linux-generic/include/odp_packet_io_internal.h
>> @@ -84,6 +84,9 @@ struct pktio_entry {
>>   		STATE_STOP
>>   	} state;
>>   	classifier_t cls;		/**< classifier linked with this pktio*/
>> +	odp_pktio_stats_t stats;	/**< statistic counters for pktio */
>> +	int use_ethtool;		/**< 1 - use ethtool,
>> +					     0 - sysfs for statistics */
>>   	char name[PKTIO_NAME_LEN];	/**< name of pktio provided to
>>   					   pktio_open() */
>>   	odp_pktio_param_t param;
>> @@ -108,6 +111,8 @@ typedef struct pktio_if_ops {
>>   	int (*close)(pktio_entry_t *pktio_entry);
>>   	int (*start)(pktio_entry_t *pktio_entry);
>>   	int (*stop)(pktio_entry_t *pktio_entry);
>> +	int (*stats)(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats);
>> +	int (*stats_reset)(pktio_entry_t *pktio_entry);
>>   	int (*recv)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
>>   		    unsigned len);
>>   	int (*send)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
>> @@ -160,6 +165,12 @@ extern const pktio_if_ops_t pcap_pktio_ops;
>>   #endif
>>   extern const pktio_if_ops_t * const pktio_if_ops[];
>>   
>> +int sysfs_stats(pktio_entry_t *pktio_entry,
>> +		odp_pktio_stats_t *stats);
>> +int sock_stats_reset(pktio_entry_t *pktio_entry);
>> +int sock_stats(pktio_entry_t *pktio_entry,
>> +	       odp_pktio_stats_t *stats);
>> +
>>   #ifdef __cplusplus
>>   }
>>   #endif
>> diff --git a/platform/linux-generic/include/odp_packet_socket.h b/platform/linux-generic/include/odp_packet_socket.h
>> index a5e0eb3..0836e3c 100644
>> --- a/platform/linux-generic/include/odp_packet_socket.h
>> +++ b/platform/linux-generic/include/odp_packet_socket.h
>> @@ -18,6 +18,7 @@
>>   #include <odp/debug.h>
>>   #include <odp/pool.h>
>>   #include <odp/packet.h>
>> +#include <odp/packet_io.h>
>>   
>>   #include <linux/version.h>
>>   
>> @@ -114,4 +115,9 @@ int promisc_mode_set_fd(int fd, const char *name, int enable);
>>    */
>>   int promisc_mode_get_fd(int fd, const char *name);
>>   
>> +/**
>> + * Get ethtool statistics of a packet socket
>> + */
>> +int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats);
>> +
>>   #endif
>> diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
>> index 14fb0c5..bb755aa 100644
>> --- a/platform/linux-generic/odp_packet_io.c
>> +++ b/platform/linux-generic/odp_packet_io.c
>> @@ -905,3 +905,56 @@ int odp_pktio_term_global(void)
>>   
>>   	return ret;
>>   }
>> +
>> +int odp_pktio_stats(odp_pktio_t pktio,
>> +		    odp_pktio_stats_t *stats)
>> +{
>> +	pktio_entry_t *entry;
>> +	int ret = -1;
>> +
>> +	entry = get_pktio_entry(pktio);
>> +	if (entry == NULL) {
>> +		ODP_DBG("pktio entry %d does not exist\n", pktio);
>> +		return -1;
>> +	}
>> +
>> +	lock_entry(entry);
>> +
>> +	if (odp_unlikely(is_free(entry))) {
>> +		unlock_entry(entry);
>> +		ODP_DBG("already freed pktio\n");
>> +		return -1;
>> +	}
>> +
>> +	if (entry->s.ops->stats)
>> +		ret = entry->s.ops->stats(entry, stats);
>> +	unlock_entry(entry);
>> +
>> +	return ret;
>> +}
>> +
>> +int odp_pktio_stats_reset(odp_pktio_t pktio)
>> +{
>> +	pktio_entry_t *entry;
>> +	int ret = -1;
>> +
>> +	entry = get_pktio_entry(pktio);
>> +	if (entry == NULL) {
>> +		ODP_DBG("pktio entry %d does not exist\n", pktio);
>> +		return -1;
>> +	}
>> +
>> +	lock_entry(entry);
>> +
>> +	if (odp_unlikely(is_free(entry))) {
>> +		unlock_entry(entry);
>> +		ODP_DBG("already freed pktio\n");
>> +		return -1;
>> +	}
>> +
>> +	if (entry->s.ops->stats)
>> +		ret = entry->s.ops->stats_reset(entry);
>> +	unlock_entry(entry);
>> +
>> +	return ret;
>> +}
>> diff --git a/platform/linux-generic/pktio/ethtool.c b/platform/linux-generic/pktio/ethtool.c
>> new file mode 100644
>> index 0000000..573cfe6
>> --- /dev/null
>> +++ b/platform/linux-generic/pktio/ethtool.c
>> @@ -0,0 +1,163 @@
>> +/* Copyright (c) 2015, Linaro Limited
>> + * All rights reserved.
>> + *
>> + * SPDX-License-Identifier:     BSD-3-Clause
>> + */
>> +
>> +#include <sys/ioctl.h>
>> +#include <netinet/in.h>
>> +#include <linux/sockios.h>
>> +#include <linux/if.h>
>> +#include <linux/ethtool.h>
>> +#include <errno.h>
>> +#include <net/if.h>
>> +
>> +#include <odp.h>
>> +#include <odp_packet_socket.h>
>> +#include <odp_debug_internal.h>
>> +
>> +static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
>> +{
>> +	struct {
>> +		struct ethtool_sset_info hdr;
>> +		uint32_t buf[1];
>> +	} sset_info;
>> +	struct ethtool_drvinfo drvinfo;
>> +	uint32_t len;
>> +	struct ethtool_gstrings *strings;
>> +	ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo, n_stats);
>> +
>> +	sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
>> +	sset_info.hdr.reserved = 0;
>> +	sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;
>> +	ifr->ifr_data =  &sset_info;
>> +	if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {
>> +		len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
>> +	} else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
>> +		/* Fallback for old kernel versions */
>> +		drvinfo.cmd = ETHTOOL_GDRVINFO;
>> +		ifr->ifr_data = &drvinfo;
>> +		if (ioctl(fd, SIOCETHTOOL, ifr)) {
>> +			__odp_errno = errno;
>> +			ODP_ERR("Cannot get stats information\n");
>> +			return NULL;
>> +		}
>> +		len = *(uint32_t *)((char *)&drvinfo + drvinfo_offset);
>> +	} else {
>> +		__odp_errno = errno;
>> +		return NULL;
>> +	}
>> +
>> +	if (!len) {
>> +		ODP_ERR("len is zero");
>> +		return NULL;
>> +	}
>> +
>> +	strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
>> +	if (!strings) {
>> +		ODP_ERR("alloc failed\n");
>> +		return NULL;
>> +	}
>> +
>> +	strings->cmd = ETHTOOL_GSTRINGS;
>> +	strings->string_set = ETH_SS_STATS;
>> +	strings->len = len;
>> +	ifr->ifr_data = strings;
>> +	if (ioctl(fd, SIOCETHTOOL, ifr)) {
>> +		__odp_errno = errno;
>> +		ODP_ERR("Cannot get stats information\n");
>> +		free(strings);
>> +		return NULL;
>> +	}
>> +
>> +	return strings;
>> +}
>> +
>> +static int ethtool_stats(int fd, struct ifreq *ifr, odp_pktio_stats_t *stats)
>> +{
>> +	struct ethtool_gstrings *strings;
>> +	struct ethtool_stats *estats;
>> +	unsigned int n_stats, i;
>> +	int err;
>> +	int cnts;
>> +
>> +	strings = get_stringset(fd, ifr);
>> +	if (!strings)
>> +		return -1;
>> +
>> +	n_stats = strings->len;
>> +	if (n_stats < 1) {
>> +		ODP_ERR("no stats available\n");
>> +		free(strings);
>> +		return -1;
>> +	}
>> +
>> +	estats = calloc(1, n_stats * sizeof(uint64_t) +
>> +			sizeof(struct ethtool_stats));
>> +	if (!estats) {
>> +		free(strings);
>> +		return -1;
>> +	}
>> +
>> +	estats->cmd = ETHTOOL_GSTATS;
>> +	estats->n_stats = n_stats;
>> +	ifr->ifr_data = estats;
>> +	err = ioctl(fd, SIOCETHTOOL, ifr);
>> +	if (err < 0) {
>> +		__odp_errno = errno;
>> +		free(strings);
>> +		free(estats);
>> +		return -1;
>> +	}
>> +
>> +	cnts = 0;
>> +	for (i = 0; i < n_stats; i++) {
>> +		char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
>> +		uint64_t val = estats->data[i];
>> +
>> +		if (!strcmp(cnt, "rx_octets")) {
>> +			stats->in_octets = val;
>> +			cnts++;
>> +		} else if (!strcmp(cnt, "rx_ucast_packets")) {
>> +			stats->in_ucast_pkts = val;
>> +			cnts++;
>> +		} else if (!strcmp(cnt, "rx_discards")) {
>> +			stats->in_discards = val;
>> +			cnts++;
>> +		} else if (!strcmp(cnt, "rx_errors")) {
>> +			stats->in_errors = val;
>> +			cnts++;
>> +		} else if (!strcmp(cnt, "tx_octets")) {
>> +			stats->out_octets = val;
>> +			cnts++;
>> +		} else if (!strcmp(cnt, "tx_ucast_packets")) {
>> +			stats->out_ucast_pkts = val;
>> +			cnts++;
>> +		} else if (!strcmp(cnt, "tx_discards")) {
>> +			stats->out_discards = val;
>> +			cnts++;
>> +		} else if (!strcmp(cnt, "tx_errors")) {
>> +			stats->out_errors = val;
>> +			cnts++;
>> +		}
>> +	}
>> +
>> +	free(strings);
>> +	free(estats);
>> +
>> +	/* Ethtool strings came from kernel driver. Name of that
>> +	 * strings is not universal. Current function needs to be updated
>> +	 * if your driver has different names for counters */
>> +	if (cnts < 8)
>> +		return -1;
> I commented on this in previous version, cnts < 8 is most likely to
> indicate there is at least 1 unsupported counter, rather than there
> being a genuine error in reading the counter. So this should really
> just return 0 and let the fact that the counter values are set to 0
> indicate that they're unsupported.
>
> As it is now, the validation test fails when run against the netmap
> pktio with an interface that has unsupported stats string names.
>
> --
> Stuart.

Hi Stuart,

the logic here is following: if any counter is not supported like
rx_errors, or tx_errors that programmer should go to that file
and add required stings. These are basic counters and all of them
have to be supported.

Also I defined test as:
+       ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters,
+                                 pktio_check_statistics_counters),

I just checked on tap pktio that validation test do not fail. Will try
to build netmap support and take a look what is there.

Maxim.
Maxim Uvarov Dec. 15, 2015, 1:41 p.m. UTC | #3
Hm, I do not have any hw on that laptop which is supported by netmap. Looks
like we need to speed up your VALE patch inclusion.

On 15 December 2015 at 15:25, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:

> On 12/03/2015 15:32, Stuart Haslam wrote:

>

>> On Tue, Nov 24, 2015 at 04:10:44PM +0300, Maxim Uvarov wrote:

>>

>>> Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>

>>> ---

>>>   platform/linux-generic/Makefile.am                 |   2 +

>>>   .../linux-generic/include/odp_packet_io_internal.h |  11 ++

>>>   platform/linux-generic/include/odp_packet_socket.h |   6 +

>>>   platform/linux-generic/odp_packet_io.c             |  53 +++++++

>>>   platform/linux-generic/pktio/ethtool.c             | 163

>>> +++++++++++++++++++++

>>>   platform/linux-generic/pktio/socket.c              |  84 +++++++++++

>>>   platform/linux-generic/pktio/socket_mmap.c         |  19 +++

>>>   platform/linux-generic/pktio/sysfs.c               |  76 ++++++++++

>>>   8 files changed, 414 insertions(+)

>>>   create mode 100644 platform/linux-generic/pktio/ethtool.c

>>>   create mode 100644 platform/linux-generic/pktio/sysfs.c

>>>

>>> diff --git a/platform/linux-generic/Makefile.am

>>> b/platform/linux-generic/Makefile.am

>>> index a6b6029..72c16f3 100644

>>> --- a/platform/linux-generic/Makefile.am

>>> +++ b/platform/linux-generic/Makefile.am

>>> @@ -166,11 +166,13 @@ __LIB__libodp_la_SOURCES = \

>>>                            odp_packet.c \

>>>                            odp_packet_flags.c \

>>>                            odp_packet_io.c \

>>> +                          pktio/ethtool.c \

>>>                            pktio/io_ops.c \

>>>                            pktio/loop.c \

>>>                            pktio/netmap.c \

>>>                            pktio/socket.c \

>>>                            pktio/socket_mmap.c \

>>> +                          pktio/sysfs.c \

>>>                            odp_pool.c \

>>>                            odp_queue.c \

>>>                            odp_rwlock.c \

>>> diff --git a/platform/linux-generic/include/odp_packet_io_internal.h

>>> b/platform/linux-generic/include/odp_packet_io_internal.h

>>> index a46c6fe..862cb0f 100644

>>> --- a/platform/linux-generic/include/odp_packet_io_internal.h

>>> +++ b/platform/linux-generic/include/odp_packet_io_internal.h

>>> @@ -84,6 +84,9 @@ struct pktio_entry {

>>>                 STATE_STOP

>>>         } state;

>>>         classifier_t cls;               /**< classifier linked with this

>>> pktio*/

>>> +       odp_pktio_stats_t stats;        /**< statistic counters for

>>> pktio */

>>> +       int use_ethtool;                /**< 1 - use ethtool,

>>> +                                            0 - sysfs for statistics */

>>>         char name[PKTIO_NAME_LEN];      /**< name of pktio provided to

>>>                                            pktio_open() */

>>>         odp_pktio_param_t param;

>>> @@ -108,6 +111,8 @@ typedef struct pktio_if_ops {

>>>         int (*close)(pktio_entry_t *pktio_entry);

>>>         int (*start)(pktio_entry_t *pktio_entry);

>>>         int (*stop)(pktio_entry_t *pktio_entry);

>>> +       int (*stats)(pktio_entry_t *pktio_entry, odp_pktio_stats_t

>>> *stats);

>>> +       int (*stats_reset)(pktio_entry_t *pktio_entry);

>>>         int (*recv)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],

>>>                     unsigned len);

>>>         int (*send)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],

>>> @@ -160,6 +165,12 @@ extern const pktio_if_ops_t pcap_pktio_ops;

>>>   #endif

>>>   extern const pktio_if_ops_t * const pktio_if_ops[];

>>>   +int sysfs_stats(pktio_entry_t *pktio_entry,

>>> +               odp_pktio_stats_t *stats);

>>> +int sock_stats_reset(pktio_entry_t *pktio_entry);

>>> +int sock_stats(pktio_entry_t *pktio_entry,

>>> +              odp_pktio_stats_t *stats);

>>> +

>>>   #ifdef __cplusplus

>>>   }

>>>   #endif

>>> diff --git a/platform/linux-generic/include/odp_packet_socket.h

>>> b/platform/linux-generic/include/odp_packet_socket.h

>>> index a5e0eb3..0836e3c 100644

>>> --- a/platform/linux-generic/include/odp_packet_socket.h

>>> +++ b/platform/linux-generic/include/odp_packet_socket.h

>>> @@ -18,6 +18,7 @@

>>>   #include <odp/debug.h>

>>>   #include <odp/pool.h>

>>>   #include <odp/packet.h>

>>> +#include <odp/packet_io.h>

>>>     #include <linux/version.h>

>>>   @@ -114,4 +115,9 @@ int promisc_mode_set_fd(int fd, const char *name,

>>> int enable);

>>>    */

>>>   int promisc_mode_get_fd(int fd, const char *name);

>>>   +/**

>>> + * Get ethtool statistics of a packet socket

>>> + */

>>> +int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t

>>> *stats);

>>> +

>>>   #endif

>>> diff --git a/platform/linux-generic/odp_packet_io.c

>>> b/platform/linux-generic/odp_packet_io.c

>>> index 14fb0c5..bb755aa 100644

>>> --- a/platform/linux-generic/odp_packet_io.c

>>> +++ b/platform/linux-generic/odp_packet_io.c

>>> @@ -905,3 +905,56 @@ int odp_pktio_term_global(void)

>>>         return ret;

>>>   }

>>> +

>>> +int odp_pktio_stats(odp_pktio_t pktio,

>>> +                   odp_pktio_stats_t *stats)

>>> +{

>>> +       pktio_entry_t *entry;

>>> +       int ret = -1;

>>> +

>>> +       entry = get_pktio_entry(pktio);

>>> +       if (entry == NULL) {

>>> +               ODP_DBG("pktio entry %d does not exist\n", pktio);

>>> +               return -1;

>>> +       }

>>> +

>>> +       lock_entry(entry);

>>> +

>>> +       if (odp_unlikely(is_free(entry))) {

>>> +               unlock_entry(entry);

>>> +               ODP_DBG("already freed pktio\n");

>>> +               return -1;

>>> +       }

>>> +

>>> +       if (entry->s.ops->stats)

>>> +               ret = entry->s.ops->stats(entry, stats);

>>> +       unlock_entry(entry);

>>> +

>>> +       return ret;

>>> +}

>>> +

>>> +int odp_pktio_stats_reset(odp_pktio_t pktio)

>>> +{

>>> +       pktio_entry_t *entry;

>>> +       int ret = -1;

>>> +

>>> +       entry = get_pktio_entry(pktio);

>>> +       if (entry == NULL) {

>>> +               ODP_DBG("pktio entry %d does not exist\n", pktio);

>>> +               return -1;

>>> +       }

>>> +

>>> +       lock_entry(entry);

>>> +

>>> +       if (odp_unlikely(is_free(entry))) {

>>> +               unlock_entry(entry);

>>> +               ODP_DBG("already freed pktio\n");

>>> +               return -1;

>>> +       }

>>> +

>>> +       if (entry->s.ops->stats)

>>> +               ret = entry->s.ops->stats_reset(entry);

>>> +       unlock_entry(entry);

>>> +

>>> +       return ret;

>>> +}

>>> diff --git a/platform/linux-generic/pktio/ethtool.c

>>> b/platform/linux-generic/pktio/ethtool.c

>>> new file mode 100644

>>> index 0000000..573cfe6

>>> --- /dev/null

>>> +++ b/platform/linux-generic/pktio/ethtool.c

>>> @@ -0,0 +1,163 @@

>>> +/* Copyright (c) 2015, Linaro Limited

>>> + * All rights reserved.

>>> + *

>>> + * SPDX-License-Identifier:     BSD-3-Clause

>>> + */

>>> +

>>> +#include <sys/ioctl.h>

>>> +#include <netinet/in.h>

>>> +#include <linux/sockios.h>

>>> +#include <linux/if.h>

>>> +#include <linux/ethtool.h>

>>> +#include <errno.h>

>>> +#include <net/if.h>

>>> +

>>> +#include <odp.h>

>>> +#include <odp_packet_socket.h>

>>> +#include <odp_debug_internal.h>

>>> +

>>> +static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)

>>> +{

>>> +       struct {

>>> +               struct ethtool_sset_info hdr;

>>> +               uint32_t buf[1];

>>> +       } sset_info;

>>> +       struct ethtool_drvinfo drvinfo;

>>> +       uint32_t len;

>>> +       struct ethtool_gstrings *strings;

>>> +       ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo,

>>> n_stats);

>>> +

>>> +       sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;

>>> +       sset_info.hdr.reserved = 0;

>>> +       sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;

>>> +       ifr->ifr_data =  &sset_info;

>>> +       if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {

>>> +               len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] :

>>> 0;

>>> +       } else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {

>>> +               /* Fallback for old kernel versions */

>>> +               drvinfo.cmd = ETHTOOL_GDRVINFO;

>>> +               ifr->ifr_data = &drvinfo;

>>> +               if (ioctl(fd, SIOCETHTOOL, ifr)) {

>>> +                       __odp_errno = errno;

>>> +                       ODP_ERR("Cannot get stats information\n");

>>> +                       return NULL;

>>> +               }

>>> +               len = *(uint32_t *)((char *)&drvinfo + drvinfo_offset);

>>> +       } else {

>>> +               __odp_errno = errno;

>>> +               return NULL;

>>> +       }

>>> +

>>> +       if (!len) {

>>> +               ODP_ERR("len is zero");

>>> +               return NULL;

>>> +       }

>>> +

>>> +       strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);

>>> +       if (!strings) {

>>> +               ODP_ERR("alloc failed\n");

>>> +               return NULL;

>>> +       }

>>> +

>>> +       strings->cmd = ETHTOOL_GSTRINGS;

>>> +       strings->string_set = ETH_SS_STATS;

>>> +       strings->len = len;

>>> +       ifr->ifr_data = strings;

>>> +       if (ioctl(fd, SIOCETHTOOL, ifr)) {

>>> +               __odp_errno = errno;

>>> +               ODP_ERR("Cannot get stats information\n");

>>> +               free(strings);

>>> +               return NULL;

>>> +       }

>>> +

>>> +       return strings;

>>> +}

>>> +

>>> +static int ethtool_stats(int fd, struct ifreq *ifr, odp_pktio_stats_t

>>> *stats)

>>> +{

>>> +       struct ethtool_gstrings *strings;

>>> +       struct ethtool_stats *estats;

>>> +       unsigned int n_stats, i;

>>> +       int err;

>>> +       int cnts;

>>> +

>>> +       strings = get_stringset(fd, ifr);

>>> +       if (!strings)

>>> +               return -1;

>>> +

>>> +       n_stats = strings->len;

>>> +       if (n_stats < 1) {

>>> +               ODP_ERR("no stats available\n");

>>> +               free(strings);

>>> +               return -1;

>>> +       }

>>> +

>>> +       estats = calloc(1, n_stats * sizeof(uint64_t) +

>>> +                       sizeof(struct ethtool_stats));

>>> +       if (!estats) {

>>> +               free(strings);

>>> +               return -1;

>>> +       }

>>> +

>>> +       estats->cmd = ETHTOOL_GSTATS;

>>> +       estats->n_stats = n_stats;

>>> +       ifr->ifr_data = estats;

>>> +       err = ioctl(fd, SIOCETHTOOL, ifr);

>>> +       if (err < 0) {

>>> +               __odp_errno = errno;

>>> +               free(strings);

>>> +               free(estats);

>>> +               return -1;

>>> +       }

>>> +

>>> +       cnts = 0;

>>> +       for (i = 0; i < n_stats; i++) {

>>> +               char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];

>>> +               uint64_t val = estats->data[i];

>>> +

>>> +               if (!strcmp(cnt, "rx_octets")) {

>>> +                       stats->in_octets = val;

>>> +                       cnts++;

>>> +               } else if (!strcmp(cnt, "rx_ucast_packets")) {

>>> +                       stats->in_ucast_pkts = val;

>>> +                       cnts++;

>>> +               } else if (!strcmp(cnt, "rx_discards")) {

>>> +                       stats->in_discards = val;

>>> +                       cnts++;

>>> +               } else if (!strcmp(cnt, "rx_errors")) {

>>> +                       stats->in_errors = val;

>>> +                       cnts++;

>>> +               } else if (!strcmp(cnt, "tx_octets")) {

>>> +                       stats->out_octets = val;

>>> +                       cnts++;

>>> +               } else if (!strcmp(cnt, "tx_ucast_packets")) {

>>> +                       stats->out_ucast_pkts = val;

>>> +                       cnts++;

>>> +               } else if (!strcmp(cnt, "tx_discards")) {

>>> +                       stats->out_discards = val;

>>> +                       cnts++;

>>> +               } else if (!strcmp(cnt, "tx_errors")) {

>>> +                       stats->out_errors = val;

>>> +                       cnts++;

>>> +               }

>>> +       }

>>> +

>>> +       free(strings);

>>> +       free(estats);

>>> +

>>> +       /* Ethtool strings came from kernel driver. Name of that

>>> +        * strings is not universal. Current function needs to be updated

>>> +        * if your driver has different names for counters */

>>> +       if (cnts < 8)

>>> +               return -1;

>>>

>> I commented on this in previous version, cnts < 8 is most likely to

>> indicate there is at least 1 unsupported counter, rather than there

>> being a genuine error in reading the counter. So this should really

>> just return 0 and let the fact that the counter values are set to 0

>> indicate that they're unsupported.

>>

>> As it is now, the validation test fails when run against the netmap

>> pktio with an interface that has unsupported stats string names.

>>

>> --

>> Stuart.

>>

>

> Hi Stuart,

>

> the logic here is following: if any counter is not supported like

> rx_errors, or tx_errors that programmer should go to that file

> and add required stings. These are basic counters and all of them

> have to be supported.

>

> Also I defined test as:

> +       ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters,

> +                                 pktio_check_statistics_counters),

>

> I just checked on tap pktio that validation test do not fail. Will try

> to build netmap support and take a look what is there.

>

> Maxim.

>

>

>
Stuart Haslam Dec. 15, 2015, 1:43 p.m. UTC | #4
On Tue, Dec 15, 2015 at 03:25:31PM +0300, Maxim Uvarov wrote:
> On 12/03/2015 15:32, Stuart Haslam wrote:
> >On Tue, Nov 24, 2015 at 04:10:44PM +0300, Maxim Uvarov wrote:
> >>Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
> >>---
> >>  platform/linux-generic/Makefile.am                 |   2 +
> >>  .../linux-generic/include/odp_packet_io_internal.h |  11 ++
> >>  platform/linux-generic/include/odp_packet_socket.h |   6 +
> >>  platform/linux-generic/odp_packet_io.c             |  53 +++++++
> >>  platform/linux-generic/pktio/ethtool.c             | 163 +++++++++++++++++++++
> >>  platform/linux-generic/pktio/socket.c              |  84 +++++++++++
> >>  platform/linux-generic/pktio/socket_mmap.c         |  19 +++
> >>  platform/linux-generic/pktio/sysfs.c               |  76 ++++++++++
> >>  8 files changed, 414 insertions(+)
> >>  create mode 100644 platform/linux-generic/pktio/ethtool.c
> >>  create mode 100644 platform/linux-generic/pktio/sysfs.c
> >>
> >>diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
> >>index a6b6029..72c16f3 100644
> >>--- a/platform/linux-generic/Makefile.am
> >>+++ b/platform/linux-generic/Makefile.am
> >>@@ -166,11 +166,13 @@ __LIB__libodp_la_SOURCES = \
> >>  			   odp_packet.c \
> >>  			   odp_packet_flags.c \
> >>  			   odp_packet_io.c \
> >>+			   pktio/ethtool.c \
> >>  			   pktio/io_ops.c \
> >>  			   pktio/loop.c \
> >>  			   pktio/netmap.c \
> >>  			   pktio/socket.c \
> >>  			   pktio/socket_mmap.c \
> >>+			   pktio/sysfs.c \
> >>  			   odp_pool.c \
> >>  			   odp_queue.c \
> >>  			   odp_rwlock.c \
> >>diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
> >>index a46c6fe..862cb0f 100644
> >>--- a/platform/linux-generic/include/odp_packet_io_internal.h
> >>+++ b/platform/linux-generic/include/odp_packet_io_internal.h
> >>@@ -84,6 +84,9 @@ struct pktio_entry {
> >>  		STATE_STOP
> >>  	} state;
> >>  	classifier_t cls;		/**< classifier linked with this pktio*/
> >>+	odp_pktio_stats_t stats;	/**< statistic counters for pktio */
> >>+	int use_ethtool;		/**< 1 - use ethtool,
> >>+					     0 - sysfs for statistics */
> >>  	char name[PKTIO_NAME_LEN];	/**< name of pktio provided to
> >>  					   pktio_open() */
> >>  	odp_pktio_param_t param;
> >>@@ -108,6 +111,8 @@ typedef struct pktio_if_ops {
> >>  	int (*close)(pktio_entry_t *pktio_entry);
> >>  	int (*start)(pktio_entry_t *pktio_entry);
> >>  	int (*stop)(pktio_entry_t *pktio_entry);
> >>+	int (*stats)(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats);
> >>+	int (*stats_reset)(pktio_entry_t *pktio_entry);
> >>  	int (*recv)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
> >>  		    unsigned len);
> >>  	int (*send)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
> >>@@ -160,6 +165,12 @@ extern const pktio_if_ops_t pcap_pktio_ops;
> >>  #endif
> >>  extern const pktio_if_ops_t * const pktio_if_ops[];
> >>+int sysfs_stats(pktio_entry_t *pktio_entry,
> >>+		odp_pktio_stats_t *stats);
> >>+int sock_stats_reset(pktio_entry_t *pktio_entry);
> >>+int sock_stats(pktio_entry_t *pktio_entry,
> >>+	       odp_pktio_stats_t *stats);
> >>+
> >>  #ifdef __cplusplus
> >>  }
> >>  #endif
> >>diff --git a/platform/linux-generic/include/odp_packet_socket.h b/platform/linux-generic/include/odp_packet_socket.h
> >>index a5e0eb3..0836e3c 100644
> >>--- a/platform/linux-generic/include/odp_packet_socket.h
> >>+++ b/platform/linux-generic/include/odp_packet_socket.h
> >>@@ -18,6 +18,7 @@
> >>  #include <odp/debug.h>
> >>  #include <odp/pool.h>
> >>  #include <odp/packet.h>
> >>+#include <odp/packet_io.h>
> >>  #include <linux/version.h>
> >>@@ -114,4 +115,9 @@ int promisc_mode_set_fd(int fd, const char *name, int enable);
> >>   */
> >>  int promisc_mode_get_fd(int fd, const char *name);
> >>+/**
> >>+ * Get ethtool statistics of a packet socket
> >>+ */
> >>+int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats);
> >>+
> >>  #endif
> >>diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
> >>index 14fb0c5..bb755aa 100644
> >>--- a/platform/linux-generic/odp_packet_io.c
> >>+++ b/platform/linux-generic/odp_packet_io.c
> >>@@ -905,3 +905,56 @@ int odp_pktio_term_global(void)
> >>  	return ret;
> >>  }
> >>+
> >>+int odp_pktio_stats(odp_pktio_t pktio,
> >>+		    odp_pktio_stats_t *stats)
> >>+{
> >>+	pktio_entry_t *entry;
> >>+	int ret = -1;
> >>+
> >>+	entry = get_pktio_entry(pktio);
> >>+	if (entry == NULL) {
> >>+		ODP_DBG("pktio entry %d does not exist\n", pktio);
> >>+		return -1;
> >>+	}
> >>+
> >>+	lock_entry(entry);
> >>+
> >>+	if (odp_unlikely(is_free(entry))) {
> >>+		unlock_entry(entry);
> >>+		ODP_DBG("already freed pktio\n");
> >>+		return -1;
> >>+	}
> >>+
> >>+	if (entry->s.ops->stats)
> >>+		ret = entry->s.ops->stats(entry, stats);
> >>+	unlock_entry(entry);
> >>+
> >>+	return ret;
> >>+}
> >>+
> >>+int odp_pktio_stats_reset(odp_pktio_t pktio)
> >>+{
> >>+	pktio_entry_t *entry;
> >>+	int ret = -1;
> >>+
> >>+	entry = get_pktio_entry(pktio);
> >>+	if (entry == NULL) {
> >>+		ODP_DBG("pktio entry %d does not exist\n", pktio);
> >>+		return -1;
> >>+	}
> >>+
> >>+	lock_entry(entry);
> >>+
> >>+	if (odp_unlikely(is_free(entry))) {
> >>+		unlock_entry(entry);
> >>+		ODP_DBG("already freed pktio\n");
> >>+		return -1;
> >>+	}
> >>+
> >>+	if (entry->s.ops->stats)
> >>+		ret = entry->s.ops->stats_reset(entry);
> >>+	unlock_entry(entry);
> >>+
> >>+	return ret;
> >>+}
> >>diff --git a/platform/linux-generic/pktio/ethtool.c b/platform/linux-generic/pktio/ethtool.c
> >>new file mode 100644
> >>index 0000000..573cfe6
> >>--- /dev/null
> >>+++ b/platform/linux-generic/pktio/ethtool.c
> >>@@ -0,0 +1,163 @@
> >>+/* Copyright (c) 2015, Linaro Limited
> >>+ * All rights reserved.
> >>+ *
> >>+ * SPDX-License-Identifier:     BSD-3-Clause
> >>+ */
> >>+
> >>+#include <sys/ioctl.h>
> >>+#include <netinet/in.h>
> >>+#include <linux/sockios.h>
> >>+#include <linux/if.h>
> >>+#include <linux/ethtool.h>
> >>+#include <errno.h>
> >>+#include <net/if.h>
> >>+
> >>+#include <odp.h>
> >>+#include <odp_packet_socket.h>
> >>+#include <odp_debug_internal.h>
> >>+
> >>+static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
> >>+{
> >>+	struct {
> >>+		struct ethtool_sset_info hdr;
> >>+		uint32_t buf[1];
> >>+	} sset_info;
> >>+	struct ethtool_drvinfo drvinfo;
> >>+	uint32_t len;
> >>+	struct ethtool_gstrings *strings;
> >>+	ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo, n_stats);
> >>+
> >>+	sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
> >>+	sset_info.hdr.reserved = 0;
> >>+	sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;
> >>+	ifr->ifr_data =  &sset_info;
> >>+	if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {
> >>+		len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
> >>+	} else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
> >>+		/* Fallback for old kernel versions */
> >>+		drvinfo.cmd = ETHTOOL_GDRVINFO;
> >>+		ifr->ifr_data = &drvinfo;
> >>+		if (ioctl(fd, SIOCETHTOOL, ifr)) {
> >>+			__odp_errno = errno;
> >>+			ODP_ERR("Cannot get stats information\n");
> >>+			return NULL;
> >>+		}
> >>+		len = *(uint32_t *)((char *)&drvinfo + drvinfo_offset);
> >>+	} else {
> >>+		__odp_errno = errno;
> >>+		return NULL;
> >>+	}
> >>+
> >>+	if (!len) {
> >>+		ODP_ERR("len is zero");
> >>+		return NULL;
> >>+	}
> >>+
> >>+	strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
> >>+	if (!strings) {
> >>+		ODP_ERR("alloc failed\n");
> >>+		return NULL;
> >>+	}
> >>+
> >>+	strings->cmd = ETHTOOL_GSTRINGS;
> >>+	strings->string_set = ETH_SS_STATS;
> >>+	strings->len = len;
> >>+	ifr->ifr_data = strings;
> >>+	if (ioctl(fd, SIOCETHTOOL, ifr)) {
> >>+		__odp_errno = errno;
> >>+		ODP_ERR("Cannot get stats information\n");
> >>+		free(strings);
> >>+		return NULL;
> >>+	}
> >>+
> >>+	return strings;
> >>+}
> >>+
> >>+static int ethtool_stats(int fd, struct ifreq *ifr, odp_pktio_stats_t *stats)
> >>+{
> >>+	struct ethtool_gstrings *strings;
> >>+	struct ethtool_stats *estats;
> >>+	unsigned int n_stats, i;
> >>+	int err;
> >>+	int cnts;
> >>+
> >>+	strings = get_stringset(fd, ifr);
> >>+	if (!strings)
> >>+		return -1;
> >>+
> >>+	n_stats = strings->len;
> >>+	if (n_stats < 1) {
> >>+		ODP_ERR("no stats available\n");
> >>+		free(strings);
> >>+		return -1;
> >>+	}
> >>+
> >>+	estats = calloc(1, n_stats * sizeof(uint64_t) +
> >>+			sizeof(struct ethtool_stats));
> >>+	if (!estats) {
> >>+		free(strings);
> >>+		return -1;
> >>+	}
> >>+
> >>+	estats->cmd = ETHTOOL_GSTATS;
> >>+	estats->n_stats = n_stats;
> >>+	ifr->ifr_data = estats;
> >>+	err = ioctl(fd, SIOCETHTOOL, ifr);
> >>+	if (err < 0) {
> >>+		__odp_errno = errno;
> >>+		free(strings);
> >>+		free(estats);
> >>+		return -1;
> >>+	}
> >>+
> >>+	cnts = 0;
> >>+	for (i = 0; i < n_stats; i++) {
> >>+		char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
> >>+		uint64_t val = estats->data[i];
> >>+
> >>+		if (!strcmp(cnt, "rx_octets")) {
> >>+			stats->in_octets = val;
> >>+			cnts++;
> >>+		} else if (!strcmp(cnt, "rx_ucast_packets")) {
> >>+			stats->in_ucast_pkts = val;
> >>+			cnts++;
> >>+		} else if (!strcmp(cnt, "rx_discards")) {
> >>+			stats->in_discards = val;
> >>+			cnts++;
> >>+		} else if (!strcmp(cnt, "rx_errors")) {
> >>+			stats->in_errors = val;
> >>+			cnts++;
> >>+		} else if (!strcmp(cnt, "tx_octets")) {
> >>+			stats->out_octets = val;
> >>+			cnts++;
> >>+		} else if (!strcmp(cnt, "tx_ucast_packets")) {
> >>+			stats->out_ucast_pkts = val;
> >>+			cnts++;
> >>+		} else if (!strcmp(cnt, "tx_discards")) {
> >>+			stats->out_discards = val;
> >>+			cnts++;
> >>+		} else if (!strcmp(cnt, "tx_errors")) {
> >>+			stats->out_errors = val;
> >>+			cnts++;
> >>+		}
> >>+	}
> >>+
> >>+	free(strings);
> >>+	free(estats);
> >>+
> >>+	/* Ethtool strings came from kernel driver. Name of that
> >>+	 * strings is not universal. Current function needs to be updated
> >>+	 * if your driver has different names for counters */
> >>+	if (cnts < 8)
> >>+		return -1;
> >I commented on this in previous version, cnts < 8 is most likely to
> >indicate there is at least 1 unsupported counter, rather than there
> >being a genuine error in reading the counter. So this should really
> >just return 0 and let the fact that the counter values are set to 0
> >indicate that they're unsupported.
> >
> >As it is now, the validation test fails when run against the netmap
> >pktio with an interface that has unsupported stats string names.
> >
> >--
> >Stuart.
> 
> Hi Stuart,
> 
> the logic here is following: if any counter is not supported like
> rx_errors, or tx_errors that programmer should go to that file
> and add required stings. These are basic counters and all of them
> have to be supported.

I understand, and that would be a valid solution, but it's not what the
API definition says. It doesn't specifically refer to an individual
counters as being mandatory/optional but it does say:

* @note: If counter is not supported by platform it has
*        to be set to 0.

So my assumption is then that all counters are optional, if this isn't
correct then I think the API needs clarifying wrt unsupported counters.

--
Stuart.
diff mbox

Patch

diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index a6b6029..72c16f3 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -166,11 +166,13 @@  __LIB__libodp_la_SOURCES = \
 			   odp_packet.c \
 			   odp_packet_flags.c \
 			   odp_packet_io.c \
+			   pktio/ethtool.c \
 			   pktio/io_ops.c \
 			   pktio/loop.c \
 			   pktio/netmap.c \
 			   pktio/socket.c \
 			   pktio/socket_mmap.c \
+			   pktio/sysfs.c \
 			   odp_pool.c \
 			   odp_queue.c \
 			   odp_rwlock.c \
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
index a46c6fe..862cb0f 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -84,6 +84,9 @@  struct pktio_entry {
 		STATE_STOP
 	} state;
 	classifier_t cls;		/**< classifier linked with this pktio*/
+	odp_pktio_stats_t stats;	/**< statistic counters for pktio */
+	int use_ethtool;		/**< 1 - use ethtool,
+					     0 - sysfs for statistics */
 	char name[PKTIO_NAME_LEN];	/**< name of pktio provided to
 					   pktio_open() */
 	odp_pktio_param_t param;
@@ -108,6 +111,8 @@  typedef struct pktio_if_ops {
 	int (*close)(pktio_entry_t *pktio_entry);
 	int (*start)(pktio_entry_t *pktio_entry);
 	int (*stop)(pktio_entry_t *pktio_entry);
+	int (*stats)(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats);
+	int (*stats_reset)(pktio_entry_t *pktio_entry);
 	int (*recv)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
 		    unsigned len);
 	int (*send)(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
@@ -160,6 +165,12 @@  extern const pktio_if_ops_t pcap_pktio_ops;
 #endif
 extern const pktio_if_ops_t * const pktio_if_ops[];
 
+int sysfs_stats(pktio_entry_t *pktio_entry,
+		odp_pktio_stats_t *stats);
+int sock_stats_reset(pktio_entry_t *pktio_entry);
+int sock_stats(pktio_entry_t *pktio_entry,
+	       odp_pktio_stats_t *stats);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/platform/linux-generic/include/odp_packet_socket.h b/platform/linux-generic/include/odp_packet_socket.h
index a5e0eb3..0836e3c 100644
--- a/platform/linux-generic/include/odp_packet_socket.h
+++ b/platform/linux-generic/include/odp_packet_socket.h
@@ -18,6 +18,7 @@ 
 #include <odp/debug.h>
 #include <odp/pool.h>
 #include <odp/packet.h>
+#include <odp/packet_io.h>
 
 #include <linux/version.h>
 
@@ -114,4 +115,9 @@  int promisc_mode_set_fd(int fd, const char *name, int enable);
  */
 int promisc_mode_get_fd(int fd, const char *name);
 
+/**
+ * Get ethtool statistics of a packet socket
+ */
+int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats);
+
 #endif
diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
index 14fb0c5..bb755aa 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -905,3 +905,56 @@  int odp_pktio_term_global(void)
 
 	return ret;
 }
+
+int odp_pktio_stats(odp_pktio_t pktio,
+		    odp_pktio_stats_t *stats)
+{
+	pktio_entry_t *entry;
+	int ret = -1;
+
+	entry = get_pktio_entry(pktio);
+	if (entry == NULL) {
+		ODP_DBG("pktio entry %d does not exist\n", pktio);
+		return -1;
+	}
+
+	lock_entry(entry);
+
+	if (odp_unlikely(is_free(entry))) {
+		unlock_entry(entry);
+		ODP_DBG("already freed pktio\n");
+		return -1;
+	}
+
+	if (entry->s.ops->stats)
+		ret = entry->s.ops->stats(entry, stats);
+	unlock_entry(entry);
+
+	return ret;
+}
+
+int odp_pktio_stats_reset(odp_pktio_t pktio)
+{
+	pktio_entry_t *entry;
+	int ret = -1;
+
+	entry = get_pktio_entry(pktio);
+	if (entry == NULL) {
+		ODP_DBG("pktio entry %d does not exist\n", pktio);
+		return -1;
+	}
+
+	lock_entry(entry);
+
+	if (odp_unlikely(is_free(entry))) {
+		unlock_entry(entry);
+		ODP_DBG("already freed pktio\n");
+		return -1;
+	}
+
+	if (entry->s.ops->stats)
+		ret = entry->s.ops->stats_reset(entry);
+	unlock_entry(entry);
+
+	return ret;
+}
diff --git a/platform/linux-generic/pktio/ethtool.c b/platform/linux-generic/pktio/ethtool.c
new file mode 100644
index 0000000..573cfe6
--- /dev/null
+++ b/platform/linux-generic/pktio/ethtool.c
@@ -0,0 +1,163 @@ 
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/ethtool.h>
+#include <errno.h>
+#include <net/if.h>
+
+#include <odp.h>
+#include <odp_packet_socket.h>
+#include <odp_debug_internal.h>
+
+static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
+{
+	struct {
+		struct ethtool_sset_info hdr;
+		uint32_t buf[1];
+	} sset_info;
+	struct ethtool_drvinfo drvinfo;
+	uint32_t len;
+	struct ethtool_gstrings *strings;
+	ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo, n_stats);
+
+	sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
+	sset_info.hdr.reserved = 0;
+	sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;
+	ifr->ifr_data =  &sset_info;
+	if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {
+		len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
+	} else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
+		/* Fallback for old kernel versions */
+		drvinfo.cmd = ETHTOOL_GDRVINFO;
+		ifr->ifr_data = &drvinfo;
+		if (ioctl(fd, SIOCETHTOOL, ifr)) {
+			__odp_errno = errno;
+			ODP_ERR("Cannot get stats information\n");
+			return NULL;
+		}
+		len = *(uint32_t *)((char *)&drvinfo + drvinfo_offset);
+	} else {
+		__odp_errno = errno;
+		return NULL;
+	}
+
+	if (!len) {
+		ODP_ERR("len is zero");
+		return NULL;
+	}
+
+	strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
+	if (!strings) {
+		ODP_ERR("alloc failed\n");
+		return NULL;
+	}
+
+	strings->cmd = ETHTOOL_GSTRINGS;
+	strings->string_set = ETH_SS_STATS;
+	strings->len = len;
+	ifr->ifr_data = strings;
+	if (ioctl(fd, SIOCETHTOOL, ifr)) {
+		__odp_errno = errno;
+		ODP_ERR("Cannot get stats information\n");
+		free(strings);
+		return NULL;
+	}
+
+	return strings;
+}
+
+static int ethtool_stats(int fd, struct ifreq *ifr, odp_pktio_stats_t *stats)
+{
+	struct ethtool_gstrings *strings;
+	struct ethtool_stats *estats;
+	unsigned int n_stats, i;
+	int err;
+	int cnts;
+
+	strings = get_stringset(fd, ifr);
+	if (!strings)
+		return -1;
+
+	n_stats = strings->len;
+	if (n_stats < 1) {
+		ODP_ERR("no stats available\n");
+		free(strings);
+		return -1;
+	}
+
+	estats = calloc(1, n_stats * sizeof(uint64_t) +
+			sizeof(struct ethtool_stats));
+	if (!estats) {
+		free(strings);
+		return -1;
+	}
+
+	estats->cmd = ETHTOOL_GSTATS;
+	estats->n_stats = n_stats;
+	ifr->ifr_data = estats;
+	err = ioctl(fd, SIOCETHTOOL, ifr);
+	if (err < 0) {
+		__odp_errno = errno;
+		free(strings);
+		free(estats);
+		return -1;
+	}
+
+	cnts = 0;
+	for (i = 0; i < n_stats; i++) {
+		char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
+		uint64_t val = estats->data[i];
+
+		if (!strcmp(cnt, "rx_octets")) {
+			stats->in_octets = val;
+			cnts++;
+		} else if (!strcmp(cnt, "rx_ucast_packets")) {
+			stats->in_ucast_pkts = val;
+			cnts++;
+		} else if (!strcmp(cnt, "rx_discards")) {
+			stats->in_discards = val;
+			cnts++;
+		} else if (!strcmp(cnt, "rx_errors")) {
+			stats->in_errors = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_octets")) {
+			stats->out_octets = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_ucast_packets")) {
+			stats->out_ucast_pkts = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_discards")) {
+			stats->out_discards = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_errors")) {
+			stats->out_errors = val;
+			cnts++;
+		}
+	}
+
+	free(strings);
+	free(estats);
+
+	/* Ethtool strings came from kernel driver. Name of that
+	 * strings is not universal. Current function needs to be updated
+	 * if your driver has different names for counters */
+	if (cnts < 8)
+		return -1;
+
+	return 0;
+}
+
+int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats)
+{
+	struct ifreq ifr;
+
+	snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+	return ethtool_stats(fd, &ifr, stats);
+}
diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c
index 56b0a8b..aa35884 100644
--- a/platform/linux-generic/pktio/socket.c
+++ b/platform/linux-generic/pktio/socket.c
@@ -209,6 +209,7 @@  static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 	struct ifreq ethreq;
 	struct sockaddr_ll sa_ll;
 	pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
+	odp_pktio_stats_t cur_stats;
 
 	/* Init pktio entry */
 	memset(pkt_sock, 0, sizeof(*pkt_sock));
@@ -254,6 +255,22 @@  static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 		goto error;
 	}
 
+	err = ethtool_stats_get_fd(pktio_entry->s.pkt_sock.sockfd,
+				   pktio_entry->s.name,
+				   &cur_stats);
+	if (err != 0) {
+		err = sysfs_stats(pktio_entry, &cur_stats);
+		if (err != 0)
+			ODP_ABORT("statistic counters are not reachable\n");
+		pktio_entry->s.use_ethtool = 0;
+	} else {
+		pktio_entry->s.use_ethtool = 1;
+	}
+
+	err = sock_stats_reset(pktio_entry);
+	if (err != 0)
+		goto error;
+
 	return 0;
 
 error:
@@ -467,6 +484,71 @@  static int sock_promisc_mode_get(pktio_entry_t *pktio_entry)
 				   pktio_entry->s.name);
 }
 
+int sock_stats(pktio_entry_t *pktio_entry,
+	       odp_pktio_stats_t *stats)
+{
+	odp_pktio_stats_t cur_stats;
+	int ret;
+
+	memset(&cur_stats, 0, sizeof(odp_pktio_stats_t));
+	if (pktio_entry->s.use_ethtool) {
+		ret =  ethtool_stats_get_fd(pktio_entry->s.pkt_sock.sockfd,
+					    pktio_entry->s.name,
+					    &cur_stats);
+	} else {
+		ret = sysfs_stats(pktio_entry, &cur_stats);
+	}
+	if (ret)
+		ODP_ABORT("getting statistics error\n");
+
+	stats->in_octets = cur_stats.in_octets -
+				pktio_entry->s.stats.in_octets;
+	stats->in_ucast_pkts = cur_stats.in_ucast_pkts -
+				pktio_entry->s.stats.in_ucast_pkts;
+	stats->in_discards = cur_stats.in_discards -
+				pktio_entry->s.stats.in_discards;
+	stats->in_errors = cur_stats.in_errors -
+				pktio_entry->s.stats.in_errors;
+	stats->in_unknown_protos = cur_stats.in_unknown_protos -
+				pktio_entry->s.stats.in_unknown_protos;
+
+	stats->out_octets = cur_stats.out_octets -
+				pktio_entry->s.stats.out_octets;
+	stats->out_ucast_pkts = cur_stats.out_ucast_pkts -
+				pktio_entry->s.stats.out_ucast_pkts;
+	stats->out_discards = cur_stats.out_discards -
+				pktio_entry->s.stats.out_discards;
+	stats->out_errors = cur_stats.out_errors -
+				pktio_entry->s.stats.out_errors;
+
+	return 0;
+}
+
+int sock_stats_reset(pktio_entry_t *pktio_entry)
+{
+	int err = 0;
+	odp_pktio_stats_t cur_stats;
+
+	memset(&cur_stats, 0, sizeof(odp_pktio_stats_t));
+
+	if (pktio_entry->s.use_ethtool) {
+		err = ethtool_stats_get_fd(pktio_entry->s.pkt_sock.sockfd,
+					   pktio_entry->s.name,
+					   &cur_stats);
+	} else {
+		err = sysfs_stats(pktio_entry, &cur_stats);
+	}
+
+	if (err != 0) {
+		ODP_ERR("stats error\n");
+	} else {
+		memcpy(&pktio_entry->s.stats, &cur_stats,
+		       sizeof(odp_pktio_stats_t));
+	}
+
+	return err;
+}
+
 const pktio_if_ops_t sock_mmsg_pktio_ops = {
 	.name = "socket",
 	.init = NULL,
@@ -475,6 +557,8 @@  const pktio_if_ops_t sock_mmsg_pktio_ops = {
 	.close = sock_close,
 	.start = NULL,
 	.stop = NULL,
+	.stats = sock_stats,
+	.stats_reset = sock_stats_reset,
 	.recv = sock_mmsg_recv,
 	.send = sock_mmsg_send,
 	.mtu_get = sock_mtu_get,
diff --git a/platform/linux-generic/pktio/socket_mmap.c b/platform/linux-generic/pktio/socket_mmap.c
index 6bbe525..5235aa6 100644
--- a/platform/linux-generic/pktio/socket_mmap.c
+++ b/platform/linux-generic/pktio/socket_mmap.c
@@ -431,6 +431,7 @@  static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
 {
 	int if_idx;
 	int ret = 0;
+	odp_pktio_stats_t cur_stats;
 
 	if (getenv("ODP_PKTIO_DISABLE_SOCKET_MMAP"))
 		return -1;
@@ -490,6 +491,22 @@  static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
 			goto error;
 	}
 
+	ret = ethtool_stats_get_fd(pktio_entry->s.pkt_sock.sockfd,
+				   pktio_entry->s.name,
+				   &cur_stats);
+	if (ret != 0) {
+		ret = sysfs_stats(pktio_entry, &cur_stats);
+		if (ret != 0)
+			ODP_ABORT("statistic counters are not reachable\n");
+		pktio_entry->s.use_ethtool = 0;
+	} else {
+		pktio_entry->s.use_ethtool = 1;
+	}
+
+	ret = sock_stats_reset(pktio_entry);
+	if (ret != 0)
+		goto error;
+
 	return 0;
 
 error:
@@ -549,6 +566,8 @@  const pktio_if_ops_t sock_mmap_pktio_ops = {
 	.close = sock_mmap_close,
 	.start = NULL,
 	.stop = NULL,
+	.stats = sock_stats,
+	.stats_reset = sock_stats_reset,
 	.recv = sock_mmap_recv,
 	.send = sock_mmap_send,
 	.mtu_get = sock_mmap_mtu_get,
diff --git a/platform/linux-generic/pktio/sysfs.c b/platform/linux-generic/pktio/sysfs.c
new file mode 100644
index 0000000..4e5c028
--- /dev/null
+++ b/platform/linux-generic/pktio/sysfs.c
@@ -0,0 +1,76 @@ 
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <odp.h>
+#include <odp_packet_io_internal.h>
+#include <errno.h>
+#include <string.h>
+
+static int sysfs_get_val(const char *fname, uint64_t *val)
+{
+	FILE  *file;
+	char str[128];
+	int ret = -1;
+
+	file = fopen(fname, "rt");
+	if (file == NULL) {
+		__odp_errno = errno;
+		/* do not print debug err if sysfs is not supported by
+		 * kernel driver.
+		 */
+		if (errno != ENOENT)
+			ODP_ERR("fopen %s: %s\n", fname, strerror(errno));
+		return 0;
+	}
+
+	if (fgets(str, sizeof(str), file) != NULL)
+		ret = sscanf(str, "%" SCNx64, val);
+
+	(void)fclose(file);
+
+	if (ret != 1) {
+		ODP_ERR("read %s\n", fname);
+		return -1;
+	}
+
+	return 0;
+}
+
+int sysfs_stats(pktio_entry_t *pktio_entry,
+		odp_pktio_stats_t *stats)
+{
+	char fname[256];
+	const char *dev = pktio_entry->s.name;
+	int ret = 0;
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_bytes", dev);
+	ret -= sysfs_get_val(fname, &stats->in_octets);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_packets", dev);
+	ret -= sysfs_get_val(fname, &stats->in_ucast_pkts);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_droppped", dev);
+	ret -= sysfs_get_val(fname, &stats->in_discards);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_errors", dev);
+	ret -= sysfs_get_val(fname, &stats->in_errors);
+
+	/* stats->in_unknown_protos is not supported in sysfs */
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_bytes", dev);
+	ret -= sysfs_get_val(fname, &stats->out_octets);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_packets", dev);
+	ret -= sysfs_get_val(fname, &stats->out_ucast_pkts);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_dropped", dev);
+	ret -= sysfs_get_val(fname, &stats->out_discards);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_errors", dev);
+	ret -= sysfs_get_val(fname, &stats->out_errors);
+
+	return ret;
+}