Message ID | 1447839268-20423-2-git-send-email-maxim.uvarov@linaro.org |
---|---|
State | Superseded |
Headers | show |
On Wed, Nov 18, 2015 at 12:34:24PM +0300, Maxim Uvarov 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++; 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 >
On Wed, Nov 18, 2015 at 12:34:24PM +0300, Maxim Uvarov 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; Should be estats
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.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++; > 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 >>
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; +} +
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