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.

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

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

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