On 11/18/2015 17:24, Stuart Haslam wrote:
On Wed, Nov 18, 2015 at 12:34:24PM +0300, Maxim Uvarov wrote:
Signed-off-by: Maxim Uvarov <maxim.uva...@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++;
This won't work on all devices, ixgbe for example doesn't have this
string. As per my email on Friday it looks like the strings used in
ethtool aren't standardised:

https://lists.linaro.org/pipermail/lng-odp/2015-November/017351.html

Also whether they're updated while in netmap mode seems to be somewhat
device specific. So we still don't have a universally working solution
for netmap.

I think the best way around this for now is just to ensure that the
validation test doesn't fail if an implementation reports 0 for all
counters, since the spec allows that. We then may or may not get stats
from netmap, depending on the device used.

yes, for netmap there is no standard way. So let's  try ethtool,
then sysfs. If  it will be needed then we will add more string names
to ethtool parsing. I will make test to allow not supported stats now,
then I think we can add pktio caps if stats are supported or not.

+               } 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;
This shouldn't be treated as a failure.
That is important check to detect if ethtool is supported or we should fallback to ethtool. One of the example is strings named differently. In case of netmap we do not switch to sysfs,
but in case of other drivers we do.

+
+       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;
Should be int

yes, I always change it but always forgot to ammend that chunk for some reason :)

+
+       file = fopen(fname, "rt");
+       if (file == NULL) {
+               /* File not found */
Only if errno == ENOENT, some (maybe all?) other failures should be
reported as failures.

+               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
lng-odp@lists.linaro.org
https://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to