Message ID | 1447839697-21140-2-git-send-email-maxim.uvarov@linaro.org |
---|---|
State | Superseded |
Headers | show |
On 18 November 2015 at 04:41, Maxim Uvarov <maxim.uvarov@linaro.org> 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++; > + } 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; > +} > + > checkpatch new blank line at EOF > -- > 1.9.1 > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > https://lists.linaro.org/mailman/listinfo/lng-odp > -- Mike Holmes Technical Manager - Linaro Networking Group Linaro.org <http://www.linaro.org/> *│ *Open source software for ARM SoCs
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