Signed-off-by: Maxim Uvarov <[email protected]>
---
 platform/linux-generic/Makefile.am                 |   2 +
 .../linux-generic/include/odp_packet_io_internal.h |  15 ++
 platform/linux-generic/include/odp_packet_socket.h |   5 +
 platform/linux-generic/odp_packet_io.c             |  53 +++++++
 platform/linux-generic/pktio/ethtool.c             | 164 +++++++++++++++++++++
 platform/linux-generic/pktio/pktio_common.c        |  72 +++++++++
 platform/linux-generic/pktio/socket.c              |  51 +++++++
 platform/linux-generic/pktio/socket_mmap.c         |  49 ++++++
 platform/linux-generic/pktio/sysfs.c               |  76 ++++++++++
 9 files changed, 487 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 9fbb3bd..e6881e7 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -132,12 +132,14 @@ __LIB__libodp_la_SOURCES = \
                           odp_packet.c \
                           odp_packet_flags.c \
                           odp_packet_io.c \
+                          pktio/ethtool.c \
                           pktio/io_ops.c \
                           pktio/pktio_common.c \
                           pktio/loop.c \
                           pktio/netmap.c \
                           pktio/socket.c \
                           pktio/socket_mmap.c \
+                          pktio/sysfs.c \
                           pktio/tap.c \
                           odp_pkt_queue.c \
                           odp_pool.c \
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h 
b/platform/linux-generic/include/odp_packet_io_internal.h
index 4fd3c87..7666b98 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -90,6 +90,12 @@ struct pktio_entry {
                STATE_STOP
        } state;
        classifier_t cls;               /**< classifier linked with this pktio*/
+       odp_pktio_stats_t stats;        /**< statistic counters for pktio */
+       enum {
+               STATS_SYSFS = 0,
+               STATS_ETHTOOL,
+               STATS_UNSUPPORTED
+       } stats_type;
        char name[PKTIO_NAME_LEN];      /**< name of pktio provided to
                                           pktio_open() */
        odp_pktio_t id;
@@ -129,6 +135,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[],
@@ -219,6 +227,13 @@ extern const pktio_if_ops_t pcap_pktio_ops;
 extern const pktio_if_ops_t tap_pktio_ops;
 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_fd(pktio_entry_t *pktio_entry,
+                 odp_pktio_stats_t *stats,
+                 int fd);
+int sock_stats_reset_fd(pktio_entry_t *pktio_entry, int fd);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/platform/linux-generic/include/odp_packet_socket.h 
b/platform/linux-generic/include/odp_packet_socket.h
index ec202db..a7797d1 100644
--- a/platform/linux-generic/include/odp_packet_socket.h
+++ b/platform/linux-generic/include/odp_packet_socket.h
@@ -168,4 +168,9 @@ int rss_conf_set_fd(int fd, const char *name,
  */
 void rss_conf_print(const odp_pktin_hash_proto_t *hash_proto);
 
+/**
+ * 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 95b8904..f8179d5 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -967,6 +967,59 @@ int odp_pktio_capability(odp_pktio_t pktio, 
odp_pktio_capability_t *capa)
        return single_capability(capa);
 }
 
+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;
+}
+
 int odp_pktio_input_queues_config(odp_pktio_t pktio,
                                  const odp_pktio_input_queue_param_t *param)
 {
diff --git a/platform/linux-generic/pktio/ethtool.c 
b/platform/linux-generic/pktio/ethtool.c
new file mode 100644
index 0000000..1f29438
--- /dev/null
+++ b/platform/linux-generic/pktio/ethtool.c
@@ -0,0 +1,164 @@
+/* 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 *)(void *)((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/pktio_common.c 
b/platform/linux-generic/pktio/pktio_common.c
index 5729db6..7867cc1 100644
--- a/platform/linux-generic/pktio/pktio_common.c
+++ b/platform/linux-generic/pktio/pktio_common.c
@@ -55,3 +55,75 @@ int _odp_packet_cls_enq(pktio_entry_t *pktio_entry,
 
        return 0;
 }
+
+int sock_stats_reset_fd(pktio_entry_t *pktio_entry, int fd)
+{
+       int err = 0;
+       odp_pktio_stats_t cur_stats;
+
+       if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
+               memset(&pktio_entry->s.stats, 0,
+                      sizeof(odp_pktio_stats_t));
+               return 0;
+       }
+
+       memset(&cur_stats, 0, sizeof(odp_pktio_stats_t));
+
+       if (pktio_entry->s.stats_type == STATS_ETHTOOL) {
+               (void)ethtool_stats_get_fd(fd,
+                                          pktio_entry->s.name,
+                                          &cur_stats);
+       } else if (pktio_entry->s.stats_type == STATS_SYSFS) {
+               err = sysfs_stats(pktio_entry, &cur_stats);
+               if (err != 0)
+                       ODP_ERR("stats error\n");
+       }
+
+       if (err == 0)
+               memcpy(&pktio_entry->s.stats, &cur_stats,
+                      sizeof(odp_pktio_stats_t));
+
+       return err;
+}
+
+int sock_stats_fd(pktio_entry_t *pktio_entry,
+                 odp_pktio_stats_t *stats,
+                 int fd)
+{
+       odp_pktio_stats_t cur_stats;
+       int ret = 0;
+
+       if (pktio_entry->s.stats_type == STATS_UNSUPPORTED)
+               return 0;
+
+       memset(&cur_stats, 0, sizeof(odp_pktio_stats_t));
+       if (pktio_entry->s.stats_type == STATS_ETHTOOL) {
+               (void)ethtool_stats_get_fd(fd,
+                                          pktio_entry->s.name,
+                                          &cur_stats);
+       } else if (pktio_entry->s.stats_type == STATS_SYSFS) {
+               sysfs_stats(pktio_entry, &cur_stats);
+       }
+
+       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 ret;
+}
diff --git a/platform/linux-generic/pktio/socket.c 
b/platform/linux-generic/pktio/socket.c
index 343f89e..cacc498 100644
--- a/platform/linux-generic/pktio/socket.c
+++ b/platform/linux-generic/pktio/socket.c
@@ -48,6 +48,8 @@
 #include <odp/helper/eth.h>
 #include <odp/helper/ip.h>
 
+static int sock_stats_reset(pktio_entry_t *pktio_entry);
+
 /** Provide a sendmmsg wrapper for systems with no libc or kernel support.
  *  As it is implemented as a weak symbol, it has zero effect on systems
  *  with both.
@@ -476,6 +478,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const 
char *netdev,
        char shm_name[ODP_SHM_NAME_LEN];
        pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
        uint8_t *addr;
+       odp_pktio_stats_t cur_stats;
 
        /* Init pktio entry */
        memset(pkt_sock, 0, sizeof(*pkt_sock));
@@ -534,6 +537,29 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, 
const char *netdev,
                ODP_ERR("bind(to IF): %s\n", strerror(errno));
                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) {
+                       memset(&pktio_entry->s.stats, 0,
+                              sizeof(odp_pktio_stats_t));
+                       pktio_entry->s.stats_type = STATS_UNSUPPORTED;
+                       ODP_DBG("pktio: %s unsupported stats\n",
+                               pktio_entry->s.name);
+               } else {
+                       pktio_entry->s.stats_type = STATS_SYSFS;
+               }
+       } else {
+               pktio_entry->s.stats_type = STATS_ETHTOOL;
+       }
+
+       err = sock_stats_reset(pktio_entry);
+       if (err != 0)
+               goto error;
+
        return 0;
 
 error:
@@ -790,6 +816,29 @@ static int sock_link_status(pktio_entry_t *pktio_entry)
                              pktio_entry->s.name);
 }
 
+static int sock_stats(pktio_entry_t *pktio_entry,
+                     odp_pktio_stats_t *stats)
+{
+       if (pktio_entry->s.stats_type == STATS_UNSUPPORTED)
+               return 0;
+
+       return sock_stats_fd(pktio_entry,
+                            stats,
+                            pktio_entry->s.pkt_sock.sockfd);
+}
+
+static int sock_stats_reset(pktio_entry_t *pktio_entry)
+{
+       if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
+               memset(&pktio_entry->s.stats, 0,
+                      sizeof(odp_pktio_stats_t));
+               return 0;
+       }
+
+       return sock_stats_reset_fd(pktio_entry,
+                                  pktio_entry->s.pkt_sock.sockfd);
+}
+
 const pktio_if_ops_t sock_mmsg_pktio_ops = {
        .name = "socket",
        .init = NULL,
@@ -798,6 +847,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 b8f2665..4f4fb76 100644
--- a/platform/linux-generic/pktio/socket_mmap.c
+++ b/platform/linux-generic/pktio/socket_mmap.c
@@ -445,6 +445,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;
@@ -504,6 +505,29 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
                        goto error;
        }
 
+       ret = ethtool_stats_get_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
+                                  pktio_entry->s.name,
+                                  &cur_stats);
+       if (ret != 0) {
+               ret = sysfs_stats(pktio_entry, &cur_stats);
+               if (ret != 0) {
+                       memset(&pktio_entry->s.stats, 0,
+                              sizeof(odp_pktio_stats_t));
+                       pktio_entry->s.stats_type = STATS_UNSUPPORTED;
+                       ODP_DBG("pktio: %s unsupported stats\n",
+                               pktio_entry->s.name);
+               } else {
+                       pktio_entry->s.stats_type = STATS_SYSFS;
+               }
+       } else {
+               pktio_entry->s.stats_type = STATS_ETHTOOL;
+       }
+
+       ret = sock_stats_reset_fd(pktio_entry,
+                                 pktio_entry->s.pkt_sock_mmap.sockfd);
+       if (ret != 0)
+               goto error;
+
        return 0;
 
 error:
@@ -560,6 +584,29 @@ static int sock_mmap_link_status(pktio_entry_t 
*pktio_entry)
                              pktio_entry->s.name);
 }
 
+static int sock_mmap_stats(pktio_entry_t *pktio_entry,
+                          odp_pktio_stats_t *stats)
+{
+       if (pktio_entry->s.stats_type == STATS_UNSUPPORTED)
+               return 0;
+
+       return sock_stats_fd(pktio_entry,
+                            stats,
+                            pktio_entry->s.pkt_sock_mmap.sockfd);
+}
+
+static int sock_mmap_stats_reset(pktio_entry_t *pktio_entry)
+{
+       if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) {
+               memset(&pktio_entry->s.stats, 0,
+                      sizeof(odp_pktio_stats_t));
+               return 0;
+       }
+
+       return sock_stats_reset_fd(pktio_entry,
+                                  pktio_entry->s.pkt_sock_mmap.sockfd);
+}
+
 const pktio_if_ops_t sock_mmap_pktio_ops = {
        .name = "socket_mmap",
        .init = NULL,
@@ -568,6 +615,8 @@ const pktio_if_ops_t sock_mmap_pktio_ops = {
        .close = sock_mmap_close,
        .start = NULL,
        .stop = NULL,
+       .stats = sock_mmap_stats,
+       .stats_reset = sock_mmap_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;
+}
-- 
1.9.1

_______________________________________________
lng-odp mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to