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