Message ID | 1448370648-18974-2-git-send-email-maxim.uvarov@linaro.org |
---|---|
State | New |
Headers | show |
On Tue, Nov 24, 2015 at 04:10:44PM +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 | 163 +++++++++++++++++++++ > platform/linux-generic/pktio/socket.c | 84 +++++++++++ > platform/linux-generic/pktio/socket_mmap.c | 19 +++ > platform/linux-generic/pktio/sysfs.c | 76 ++++++++++ > 8 files changed, 414 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 a6b6029..72c16f3 100644 > --- a/platform/linux-generic/Makefile.am > +++ b/platform/linux-generic/Makefile.am > @@ -166,11 +166,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 a46c6fe..862cb0f 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; > @@ -108,6 +111,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[], > @@ -160,6 +165,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 14fb0c5..bb755aa 100644 > --- a/platform/linux-generic/odp_packet_io.c > +++ b/platform/linux-generic/odp_packet_io.c > @@ -905,3 +905,56 @@ int odp_pktio_term_global(void) > > return ret; > } > + > +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..573cfe6 > --- /dev/null > +++ b/platform/linux-generic/pktio/ethtool.c > @@ -0,0 +1,163 @@ > +/* 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 = estats; > + err = ioctl(fd, SIOCETHTOOL, ifr); > + if (err < 0) { > + __odp_errno = errno; > + free(strings); > + free(estats); > + return -1; > + } > + > + cnts = 0; > + for (i = 0; i < n_stats; i++) { > + char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN]; > + uint64_t val = estats->data[i]; > + > + if (!strcmp(cnt, "rx_octets")) { > + stats->in_octets = val; > + cnts++; > + } else if (!strcmp(cnt, "rx_ucast_packets")) { > + stats->in_ucast_pkts = val; > + cnts++; > + } else if (!strcmp(cnt, "rx_discards")) { > + stats->in_discards = val; > + cnts++; > + } else if (!strcmp(cnt, "rx_errors")) { > + stats->in_errors = val; > + cnts++; > + } else if (!strcmp(cnt, "tx_octets")) { > + stats->out_octets = val; > + cnts++; > + } else if (!strcmp(cnt, "tx_ucast_packets")) { > + stats->out_ucast_pkts = val; > + cnts++; > + } else if (!strcmp(cnt, "tx_discards")) { > + stats->out_discards = val; > + cnts++; > + } else if (!strcmp(cnt, "tx_errors")) { > + stats->out_errors = val; > + cnts++; > + } > + } > + > + free(strings); > + free(estats); > + > + /* Ethtool strings came from kernel driver. Name of that > + * strings is not universal. Current function needs to be updated > + * if your driver has different names for counters */ > + if (cnts < 8) > + return -1; I commented on this in previous version, cnts < 8 is most likely to indicate there is at least 1 unsupported counter, rather than there being a genuine error in reading the counter. So this should really just return 0 and let the fact that the counter values are set to 0 indicate that they're unsupported. As it is now, the validation test fails when run against the netmap pktio with an interface that has unsupported stats string names. -- Stuart.
On 12/03/2015 15:32, Stuart Haslam wrote: > On Tue, Nov 24, 2015 at 04:10:44PM +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 | 163 +++++++++++++++++++++ >> platform/linux-generic/pktio/socket.c | 84 +++++++++++ >> platform/linux-generic/pktio/socket_mmap.c | 19 +++ >> platform/linux-generic/pktio/sysfs.c | 76 ++++++++++ >> 8 files changed, 414 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 a6b6029..72c16f3 100644 >> --- a/platform/linux-generic/Makefile.am >> +++ b/platform/linux-generic/Makefile.am >> @@ -166,11 +166,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 a46c6fe..862cb0f 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; >> @@ -108,6 +111,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[], >> @@ -160,6 +165,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 14fb0c5..bb755aa 100644 >> --- a/platform/linux-generic/odp_packet_io.c >> +++ b/platform/linux-generic/odp_packet_io.c >> @@ -905,3 +905,56 @@ int odp_pktio_term_global(void) >> >> return ret; >> } >> + >> +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..573cfe6 >> --- /dev/null >> +++ b/platform/linux-generic/pktio/ethtool.c >> @@ -0,0 +1,163 @@ >> +/* 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 = estats; >> + err = ioctl(fd, SIOCETHTOOL, ifr); >> + if (err < 0) { >> + __odp_errno = errno; >> + free(strings); >> + free(estats); >> + return -1; >> + } >> + >> + cnts = 0; >> + for (i = 0; i < n_stats; i++) { >> + char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN]; >> + uint64_t val = estats->data[i]; >> + >> + if (!strcmp(cnt, "rx_octets")) { >> + stats->in_octets = val; >> + cnts++; >> + } else if (!strcmp(cnt, "rx_ucast_packets")) { >> + stats->in_ucast_pkts = val; >> + cnts++; >> + } else if (!strcmp(cnt, "rx_discards")) { >> + stats->in_discards = val; >> + cnts++; >> + } else if (!strcmp(cnt, "rx_errors")) { >> + stats->in_errors = val; >> + cnts++; >> + } else if (!strcmp(cnt, "tx_octets")) { >> + stats->out_octets = val; >> + cnts++; >> + } else if (!strcmp(cnt, "tx_ucast_packets")) { >> + stats->out_ucast_pkts = val; >> + cnts++; >> + } else if (!strcmp(cnt, "tx_discards")) { >> + stats->out_discards = val; >> + cnts++; >> + } else if (!strcmp(cnt, "tx_errors")) { >> + stats->out_errors = val; >> + cnts++; >> + } >> + } >> + >> + free(strings); >> + free(estats); >> + >> + /* Ethtool strings came from kernel driver. Name of that >> + * strings is not universal. Current function needs to be updated >> + * if your driver has different names for counters */ >> + if (cnts < 8) >> + return -1; > I commented on this in previous version, cnts < 8 is most likely to > indicate there is at least 1 unsupported counter, rather than there > being a genuine error in reading the counter. So this should really > just return 0 and let the fact that the counter values are set to 0 > indicate that they're unsupported. > > As it is now, the validation test fails when run against the netmap > pktio with an interface that has unsupported stats string names. > > -- > Stuart. Hi Stuart, the logic here is following: if any counter is not supported like rx_errors, or tx_errors that programmer should go to that file and add required stings. These are basic counters and all of them have to be supported. Also I defined test as: + ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters, + pktio_check_statistics_counters), I just checked on tap pktio that validation test do not fail. Will try to build netmap support and take a look what is there. Maxim.
Hm, I do not have any hw on that laptop which is supported by netmap. Looks like we need to speed up your VALE patch inclusion. On 15 December 2015 at 15:25, Maxim Uvarov <maxim.uvarov@linaro.org> wrote: > On 12/03/2015 15:32, Stuart Haslam wrote: > >> On Tue, Nov 24, 2015 at 04:10:44PM +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 | 163 >>> +++++++++++++++++++++ >>> platform/linux-generic/pktio/socket.c | 84 +++++++++++ >>> platform/linux-generic/pktio/socket_mmap.c | 19 +++ >>> platform/linux-generic/pktio/sysfs.c | 76 ++++++++++ >>> 8 files changed, 414 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 a6b6029..72c16f3 100644 >>> --- a/platform/linux-generic/Makefile.am >>> +++ b/platform/linux-generic/Makefile.am >>> @@ -166,11 +166,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 a46c6fe..862cb0f 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; >>> @@ -108,6 +111,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[], >>> @@ -160,6 +165,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 14fb0c5..bb755aa 100644 >>> --- a/platform/linux-generic/odp_packet_io.c >>> +++ b/platform/linux-generic/odp_packet_io.c >>> @@ -905,3 +905,56 @@ int odp_pktio_term_global(void) >>> return ret; >>> } >>> + >>> +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..573cfe6 >>> --- /dev/null >>> +++ b/platform/linux-generic/pktio/ethtool.c >>> @@ -0,0 +1,163 @@ >>> +/* 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 = estats; >>> + err = ioctl(fd, SIOCETHTOOL, ifr); >>> + if (err < 0) { >>> + __odp_errno = errno; >>> + free(strings); >>> + free(estats); >>> + return -1; >>> + } >>> + >>> + cnts = 0; >>> + for (i = 0; i < n_stats; i++) { >>> + char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN]; >>> + uint64_t val = estats->data[i]; >>> + >>> + if (!strcmp(cnt, "rx_octets")) { >>> + stats->in_octets = val; >>> + cnts++; >>> + } else if (!strcmp(cnt, "rx_ucast_packets")) { >>> + stats->in_ucast_pkts = val; >>> + cnts++; >>> + } else if (!strcmp(cnt, "rx_discards")) { >>> + stats->in_discards = val; >>> + cnts++; >>> + } else if (!strcmp(cnt, "rx_errors")) { >>> + stats->in_errors = val; >>> + cnts++; >>> + } else if (!strcmp(cnt, "tx_octets")) { >>> + stats->out_octets = val; >>> + cnts++; >>> + } else if (!strcmp(cnt, "tx_ucast_packets")) { >>> + stats->out_ucast_pkts = val; >>> + cnts++; >>> + } else if (!strcmp(cnt, "tx_discards")) { >>> + stats->out_discards = val; >>> + cnts++; >>> + } else if (!strcmp(cnt, "tx_errors")) { >>> + stats->out_errors = val; >>> + cnts++; >>> + } >>> + } >>> + >>> + free(strings); >>> + free(estats); >>> + >>> + /* Ethtool strings came from kernel driver. Name of that >>> + * strings is not universal. Current function needs to be updated >>> + * if your driver has different names for counters */ >>> + if (cnts < 8) >>> + return -1; >>> >> I commented on this in previous version, cnts < 8 is most likely to >> indicate there is at least 1 unsupported counter, rather than there >> being a genuine error in reading the counter. So this should really >> just return 0 and let the fact that the counter values are set to 0 >> indicate that they're unsupported. >> >> As it is now, the validation test fails when run against the netmap >> pktio with an interface that has unsupported stats string names. >> >> -- >> Stuart. >> > > Hi Stuart, > > the logic here is following: if any counter is not supported like > rx_errors, or tx_errors that programmer should go to that file > and add required stings. These are basic counters and all of them > have to be supported. > > Also I defined test as: > + ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters, > + pktio_check_statistics_counters), > > I just checked on tap pktio that validation test do not fail. Will try > to build netmap support and take a look what is there. > > Maxim. > > >
On Tue, Dec 15, 2015 at 03:25:31PM +0300, Maxim Uvarov wrote: > On 12/03/2015 15:32, Stuart Haslam wrote: > >On Tue, Nov 24, 2015 at 04:10:44PM +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 | 163 +++++++++++++++++++++ > >> platform/linux-generic/pktio/socket.c | 84 +++++++++++ > >> platform/linux-generic/pktio/socket_mmap.c | 19 +++ > >> platform/linux-generic/pktio/sysfs.c | 76 ++++++++++ > >> 8 files changed, 414 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 a6b6029..72c16f3 100644 > >>--- a/platform/linux-generic/Makefile.am > >>+++ b/platform/linux-generic/Makefile.am > >>@@ -166,11 +166,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 a46c6fe..862cb0f 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; > >>@@ -108,6 +111,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[], > >>@@ -160,6 +165,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 14fb0c5..bb755aa 100644 > >>--- a/platform/linux-generic/odp_packet_io.c > >>+++ b/platform/linux-generic/odp_packet_io.c > >>@@ -905,3 +905,56 @@ int odp_pktio_term_global(void) > >> return ret; > >> } > >>+ > >>+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..573cfe6 > >>--- /dev/null > >>+++ b/platform/linux-generic/pktio/ethtool.c > >>@@ -0,0 +1,163 @@ > >>+/* 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 = estats; > >>+ err = ioctl(fd, SIOCETHTOOL, ifr); > >>+ if (err < 0) { > >>+ __odp_errno = errno; > >>+ free(strings); > >>+ free(estats); > >>+ return -1; > >>+ } > >>+ > >>+ cnts = 0; > >>+ for (i = 0; i < n_stats; i++) { > >>+ char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN]; > >>+ uint64_t val = estats->data[i]; > >>+ > >>+ if (!strcmp(cnt, "rx_octets")) { > >>+ stats->in_octets = val; > >>+ cnts++; > >>+ } else if (!strcmp(cnt, "rx_ucast_packets")) { > >>+ stats->in_ucast_pkts = val; > >>+ cnts++; > >>+ } else if (!strcmp(cnt, "rx_discards")) { > >>+ stats->in_discards = val; > >>+ cnts++; > >>+ } else if (!strcmp(cnt, "rx_errors")) { > >>+ stats->in_errors = val; > >>+ cnts++; > >>+ } else if (!strcmp(cnt, "tx_octets")) { > >>+ stats->out_octets = val; > >>+ cnts++; > >>+ } else if (!strcmp(cnt, "tx_ucast_packets")) { > >>+ stats->out_ucast_pkts = val; > >>+ cnts++; > >>+ } else if (!strcmp(cnt, "tx_discards")) { > >>+ stats->out_discards = val; > >>+ cnts++; > >>+ } else if (!strcmp(cnt, "tx_errors")) { > >>+ stats->out_errors = val; > >>+ cnts++; > >>+ } > >>+ } > >>+ > >>+ free(strings); > >>+ free(estats); > >>+ > >>+ /* Ethtool strings came from kernel driver. Name of that > >>+ * strings is not universal. Current function needs to be updated > >>+ * if your driver has different names for counters */ > >>+ if (cnts < 8) > >>+ return -1; > >I commented on this in previous version, cnts < 8 is most likely to > >indicate there is at least 1 unsupported counter, rather than there > >being a genuine error in reading the counter. So this should really > >just return 0 and let the fact that the counter values are set to 0 > >indicate that they're unsupported. > > > >As it is now, the validation test fails when run against the netmap > >pktio with an interface that has unsupported stats string names. > > > >-- > >Stuart. > > Hi Stuart, > > the logic here is following: if any counter is not supported like > rx_errors, or tx_errors that programmer should go to that file > and add required stings. These are basic counters and all of them > have to be supported. I understand, and that would be a valid solution, but it's not what the API definition says. It doesn't specifically refer to an individual counters as being mandatory/optional but it does say: * @note: If counter is not supported by platform it has * to be set to 0. So my assumption is then that all counters are optional, if this isn't correct then I think the API needs clarifying wrt unsupported counters. -- Stuart.
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am index a6b6029..72c16f3 100644 --- a/platform/linux-generic/Makefile.am +++ b/platform/linux-generic/Makefile.am @@ -166,11 +166,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 a46c6fe..862cb0f 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; @@ -108,6 +111,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[], @@ -160,6 +165,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 14fb0c5..bb755aa 100644 --- a/platform/linux-generic/odp_packet_io.c +++ b/platform/linux-generic/odp_packet_io.c @@ -905,3 +905,56 @@ int odp_pktio_term_global(void) return ret; } + +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..573cfe6 --- /dev/null +++ b/platform/linux-generic/pktio/ethtool.c @@ -0,0 +1,163 @@ +/* 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 = estats; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + __odp_errno = errno; + free(strings); + free(estats); + return -1; + } + + cnts = 0; + for (i = 0; i < n_stats; i++) { + char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN]; + uint64_t val = estats->data[i]; + + if (!strcmp(cnt, "rx_octets")) { + stats->in_octets = val; + cnts++; + } else if (!strcmp(cnt, "rx_ucast_packets")) { + stats->in_ucast_pkts = val; + cnts++; + } else if (!strcmp(cnt, "rx_discards")) { + stats->in_discards = val; + cnts++; + } else if (!strcmp(cnt, "rx_errors")) { + stats->in_errors = val; + cnts++; + } else if (!strcmp(cnt, "tx_octets")) { + stats->out_octets = val; + cnts++; + } else if (!strcmp(cnt, "tx_ucast_packets")) { + stats->out_ucast_pkts = val; + cnts++; + } else if (!strcmp(cnt, "tx_discards")) { + stats->out_discards = val; + cnts++; + } else if (!strcmp(cnt, "tx_errors")) { + stats->out_errors = val; + cnts++; + } + } + + free(strings); + free(estats); + + /* Ethtool strings came from kernel driver. Name of that + * strings is not universal. Current function needs to be updated + * if your driver has different names for counters */ + if (cnts < 8) + return -1; + + return 0; +} + +int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats) +{ + struct ifreq ifr; + + snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); + return ethtool_stats(fd, &ifr, stats); +} diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c index 56b0a8b..aa35884 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 = { .name = "socket", .init = NULL, @@ -475,6 +557,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 6bbe525..5235aa6 100644 --- a/platform/linux-generic/pktio/socket_mmap.c +++ b/platform/linux-generic/pktio/socket_mmap.c @@ -431,6 +431,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; @@ -490,6 +491,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: @@ -549,6 +566,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..4e5c028 --- /dev/null +++ b/platform/linux-generic/pktio/sysfs.c @@ -0,0 +1,76 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp.h> +#include <odp_packet_io_internal.h> +#include <errno.h> +#include <string.h> + +static int sysfs_get_val(const char *fname, uint64_t *val) +{ + FILE *file; + char str[128]; + int ret = -1; + + file = fopen(fname, "rt"); + if (file == NULL) { + __odp_errno = errno; + /* do not print debug err if sysfs is not supported by + * kernel driver. + */ + if (errno != ENOENT) + ODP_ERR("fopen %s: %s\n", fname, strerror(errno)); + return 0; + } + + if (fgets(str, sizeof(str), file) != NULL) + ret = sscanf(str, "%" SCNx64, val); + + (void)fclose(file); + + if (ret != 1) { + ODP_ERR("read %s\n", fname); + return -1; + } + + return 0; +} + +int sysfs_stats(pktio_entry_t *pktio_entry, + odp_pktio_stats_t *stats) +{ + char fname[256]; + const char *dev = pktio_entry->s.name; + int ret = 0; + + sprintf(fname, "/sys/class/net/%s/statistics/rx_bytes", dev); + ret -= sysfs_get_val(fname, &stats->in_octets); + + sprintf(fname, "/sys/class/net/%s/statistics/rx_packets", dev); + ret -= sysfs_get_val(fname, &stats->in_ucast_pkts); + + sprintf(fname, "/sys/class/net/%s/statistics/rx_droppped", dev); + ret -= sysfs_get_val(fname, &stats->in_discards); + + sprintf(fname, "/sys/class/net/%s/statistics/rx_errors", dev); + ret -= sysfs_get_val(fname, &stats->in_errors); + + /* stats->in_unknown_protos is not supported in sysfs */ + + sprintf(fname, "/sys/class/net/%s/statistics/tx_bytes", dev); + ret -= sysfs_get_val(fname, &stats->out_octets); + + sprintf(fname, "/sys/class/net/%s/statistics/tx_packets", dev); + ret -= sysfs_get_val(fname, &stats->out_ucast_pkts); + + sprintf(fname, "/sys/class/net/%s/statistics/tx_dropped", dev); + ret -= sysfs_get_val(fname, &stats->out_discards); + + sprintf(fname, "/sys/class/net/%s/statistics/tx_errors", dev); + ret -= sysfs_get_val(fname, &stats->out_errors); + + return ret; +}
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 | 163 +++++++++++++++++++++ platform/linux-generic/pktio/socket.c | 84 +++++++++++ platform/linux-generic/pktio/socket_mmap.c | 19 +++ platform/linux-generic/pktio/sysfs.c | 76 ++++++++++ 8 files changed, 414 insertions(+) create mode 100644 platform/linux-generic/pktio/ethtool.c create mode 100644 platform/linux-generic/pktio/sysfs.c