diff mbox

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

Message ID 1447839697-21140-2-git-send-email-maxim.uvarov@linaro.org
State Superseded
Headers show

Commit Message

Maxim Uvarov Nov. 18, 2015, 9:41 a.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             | 160 +++++++++++++++++++++
 platform/linux-generic/pktio/socket.c              |  84 +++++++++++
 platform/linux-generic/pktio/socket_mmap.c         |  19 +++
 platform/linux-generic/pktio/sysfs.c               |  71 +++++++++
 8 files changed, 406 insertions(+)
 create mode 100644 platform/linux-generic/pktio/ethtool.c
 create mode 100644 platform/linux-generic/pktio/sysfs.c

Comments

Mike Holmes Nov. 19, 2015, 11:50 p.m. UTC | #1
On 18 November 2015 at 04:41, Maxim Uvarov <maxim.uvarov@linaro.org> 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             | 160

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

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

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

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

>  8 files changed, 406 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 610c79c..71b030e 100644

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

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

> @@ -165,11 +165,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 1a1118c..6566978 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;

> @@ -107,6 +110,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[],

> @@ -159,6 +164,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 3ef400f..ba97629 100644

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

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

> @@ -862,3 +862,56 @@ void odp_pktio_print(odp_pktio_t id)

>

>         ODP_PRINT("\n%s\n", str);

>  }

> +

> +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..b689015

> --- /dev/null

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

> @@ -0,0 +1,160 @@

> +/* 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 = stats;

> +       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);

> +

> +       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 5f5e0ae..9f896a9 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 = {

>         .init = NULL,

>         .term = NULL,

> @@ -474,6 +556,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 79ff82d..06bdad6 100644

> --- a/platform/linux-generic/pktio/socket_mmap.c

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

> @@ -408,6 +408,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;

> @@ -467,6 +468,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:

> @@ -525,6 +542,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..afa5bfb

> --- /dev/null

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

> @@ -0,0 +1,71 @@

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

> + * All rights reserved.

> + *

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

> + */

> +

> +#include <odp.h>

> +#include <odp_packet_io_internal.h>

> +#include <stdio.h>

> +

> +static int sysfs_get_val(const char *fname, uint64_t *val)

> +{

> +       FILE  *file;

> +       char str[128];

> +       uint64_t ret = -1;

> +

> +       file = fopen(fname, "rt");

> +       if (file == NULL) {

> +               /* File not found */

> +               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;

> +}

> +

>


checkpatch
new blank line at EOF


> --

> 1.9.1

>

> _______________________________________________

> lng-odp mailing list

> lng-odp@lists.linaro.org

> https://lists.linaro.org/mailman/listinfo/lng-odp

>




-- 
Mike Holmes
Technical Manager - Linaro Networking Group
Linaro.org <http://www.linaro.org/> *│ *Open source software for ARM SoCs
diff mbox

Patch

diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 610c79c..71b030e 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -165,11 +165,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 1a1118c..6566978 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;
@@ -107,6 +110,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[],
@@ -159,6 +164,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 3ef400f..ba97629 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -862,3 +862,56 @@  void odp_pktio_print(odp_pktio_t id)
 
 	ODP_PRINT("\n%s\n", str);
 }
+
+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..b689015
--- /dev/null
+++ b/platform/linux-generic/pktio/ethtool.c
@@ -0,0 +1,160 @@ 
+/* 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 = stats;
+	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);
+
+	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 5f5e0ae..9f896a9 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 = {
 	.init = NULL,
 	.term = NULL,
@@ -474,6 +556,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 79ff82d..06bdad6 100644
--- a/platform/linux-generic/pktio/socket_mmap.c
+++ b/platform/linux-generic/pktio/socket_mmap.c
@@ -408,6 +408,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;
@@ -467,6 +468,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:
@@ -525,6 +542,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..afa5bfb
--- /dev/null
+++ b/platform/linux-generic/pktio/sysfs.c
@@ -0,0 +1,71 @@ 
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <odp.h>
+#include <odp_packet_io_internal.h>
+#include <stdio.h>
+
+static int sysfs_get_val(const char *fname, uint64_t *val)
+{
+	FILE  *file;
+	char str[128];
+	uint64_t ret = -1;
+
+	file = fopen(fname, "rt");
+	if (file == NULL) {
+		/* File not found */
+		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;
+}
+