From 7286b2518ec8e4398b512ce95def9166a7af2e4a Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 13 Jul 2017 21:26:50 -0700 Subject: Adding PROX(Packet pROcessing eXecution engine) VNF to sampleVNF JIRA: SAMPLEVNF-55 PROX is a DPDK-based application implementing Telco use-cases such as a simplified BRAS/BNG, light-weight AFTR... It also allows configuring finer grained network functions like QoS, Routing, load-balancing... (We are moving PROX version v039 to sampleVNF https://01.org/intel-data-plane-performance-demonstrators/prox-overview) Change-Id: Ia3cb02cf0e49ac5596e922c197ff7e010293d033 Signed-off-by: Deepak S --- VNFs/DPPD-PROX/stats_port.c | 407 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 VNFs/DPPD-PROX/stats_port.c (limited to 'VNFs/DPPD-PROX/stats_port.c') diff --git a/VNFs/DPPD-PROX/stats_port.c b/VNFs/DPPD-PROX/stats_port.c new file mode 100644 index 00000000..b5e70dcc --- /dev/null +++ b/VNFs/DPPD-PROX/stats_port.c @@ -0,0 +1,407 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include +#include + +#include +#include +#include +#include + +#include "prox_malloc.h" +#include "log.h" +#include "quit.h" +#include "stats_port.h" +#include "prox_port_cfg.h" +#include "rw_reg.h" + +#if defined(PROX_STATS) && defined(PROX_HW_DIRECT_STATS) + +/* Directly access hardware counters instead of going through DPDK. This allows getting + * specific counters that DPDK does not report or aggregates with other ones. + */ + +/* Definitions for IXGBE (taken from PMD) */ +#define PROX_IXGBE_MPC(_i) (0x03FA0 + ((_i) * 4)) /* 8 of these 3FA0-3FBC*/ +#define PROX_IXGBE_QBRC_L(_i) (0x01034 + ((_i) * 0x40)) /* 16 of these */ +#define PROX_IXGBE_QBRC_H(_i) (0x01038 + ((_i) * 0x40)) /* 16 of these */ +#define PROX_IXGBE_QPRC(_i) (0x01030 + ((_i) * 0x40)) /* 16 of these */ +#define PROX_IXGBE_GPTC 0x04080 +#define PROX_IXGBE_TPR 0x040D0 +#define PROX_IXGBE_TORL 0x040C0 +#define PROX_IXGBE_TORH 0x040C4 +#define PROX_IXGBE_GOTCL 0x04090 +#define PROX_IXGBE_GOTCH 0x04094 + +#define IXGBE_QUEUE_STAT_COUNTERS 16 + +static void ixgbe_read_stats(uint8_t port_id, struct port_stats_sample* stats, struct port_stats_sample *prev, int last_stat) +{ + uint64_t before, after; + unsigned i; + + struct rte_eth_dev* dev = &rte_eth_devices[port_id]; + + /* WARNING: Assumes hardware address is first field of structure! This may change! */ + struct _dev_hw* hw = (struct _dev_hw *)(dev->data->dev_private); + + stats->no_mbufs = dev->data->rx_mbuf_alloc_failed; + + /* Since we only read deltas from the NIC, we have to add to previous values + * even though we actually substract again later to find out the rates! + */ + stats->ierrors = prev->ierrors; + stats->imissed = prev->imissed; + stats->rx_bytes = prev->rx_bytes; + stats->rx_tot = prev->rx_tot; + stats->tx_bytes = prev->tx_bytes; + stats->tx_tot = prev->tx_tot; + + /* WARNING: In this implementation, we count as imiised only the "no descriptor" + * missed packets cases and not the actual receive errors. + */ + before = rte_rdtsc(); + for (i = 0; i < 8; i++) { + stats->imissed += PROX_READ_REG(hw, PROX_IXGBE_MPC(i)); + } + + /* RX stats */ +#if 0 + /* This version is equivalent to what ixgbe PMD does. It only accounts for packets + * actually received on the host. + */ + for (i = 0; i < IXGBE_QUEUE_STAT_COUNTERS; i++) { + /* ipackets: */ + stats->rx_tot += PROX_READ_REG(hw, PROX_IXGBE_QPRC(i)); + /* ibytes: */ + stats->rx_bytes += PROX_READ_REG(hw, PROX_IXGBE_QBRC_L(i)); + stats->rx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_QBRC_H(i)) << 32); + } +#else + /* This version reports the packets received by the NIC, regardless of whether they + * reached the host or not, etc. (no need to add ierrors or imissedto this packet count) + */ + stats->rx_tot += PROX_READ_REG(hw, PROX_IXGBE_TPR); + stats->rx_bytes += PROX_READ_REG(hw, PROX_IXGBE_TORL); + stats->rx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_TORH) << 32); +#endif + + /* TX stats */ + /* opackets: */ + stats->tx_tot += PROX_READ_REG(hw, PROX_IXGBE_GPTC); + /* obytes: */ + stats->tx_bytes += PROX_READ_REG(hw, PROX_IXGBE_GOTCL); + stats->tx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_GOTCH) << 32); + after = rte_rdtsc(); + stats->tsc = (before >> 1) + (after >> 1); +} + +#endif + +extern int last_stat; +static struct port_stats port_stats[PROX_MAX_PORTS]; +static uint8_t nb_interface; +static uint8_t n_ports; +static int num_xstats[PROX_MAX_PORTS] = {0}; +static int num_ixgbe_xstats = 0; + +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,1) +#define XSTATS_SUPPORT 1 +#else +#define XSTATS_SUPPORT 0 +#endif + +#if XSTATS_SUPPORT +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) +static struct rte_eth_xstat *eth_xstats[PROX_MAX_PORTS] = {0}; +static struct rte_eth_xstat_name *eth_xstat_names[PROX_MAX_PORTS] = {0}; +#else +static struct rte_eth_xstats *eth_xstats[PROX_MAX_PORTS] = {0}; +static struct rte_eth_xstats *eth_xstat_names[PROX_MAX_PORTS] = {0}; +#endif +static int xstat_tpr_offset[PROX_MAX_PORTS] ={0}, xstat_tor_offset[PROX_MAX_PORTS] = {0}; +static int tx_pkt_size_offset[PROX_MAX_PORTS][PKT_SIZE_COUNT]; +#endif + +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) +static int find_xstats_str(struct rte_eth_xstat_name *xstats, int n, const char *name) +#else +static int find_xstats_str(struct rte_eth_xstats *xstats, int n, const char *name) +#endif +{ + for (int i = 0; i < n; i++) { + if (strcmp(xstats[i].name, name) == 0) + return i; + } + + return -1; +} + +void stats_port_init(void) +{ + int potential_ixgbe_warn = 0; + for (int i = 0; i < PROX_MAX_PORTS; i++) { + xstat_tpr_offset[i] = -1; + xstat_tor_offset[i] = -1; + for (int j = 0; j < PKT_SIZE_COUNT; j++) { + tx_pkt_size_offset[i][j] = -1; + } + } +#if XSTATS_SUPPORT + nb_interface = prox_last_port_active() + 1; + n_ports = prox_nb_active_ports(); + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) { +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) + num_xstats[port_id] = rte_eth_xstats_get_names(port_id, NULL, 0); + eth_xstat_names[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstat_name), prox_port_cfg[port_id].socket); + PROX_PANIC(eth_xstat_names[port_id] == NULL, "Error allocating memory for xstats"); + num_xstats[port_id] = rte_eth_xstats_get_names(port_id, eth_xstat_names[port_id], num_xstats[port_id]); + eth_xstats[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstat), prox_port_cfg[port_id].socket); + PROX_PANIC(eth_xstats[port_id] == NULL, "Error allocating memory for xstats"); +#else + num_xstats[port_id] = rte_eth_xstats_get(port_id, NULL, 0); + eth_xstats[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstats), prox_port_cfg[port_id].socket); + PROX_PANIC(eth_xstats[port_id] == NULL, "Error allocating memory for xstats"); + eth_xstat_names[port_id] = eth_xstats[port_id]; + num_xstats[port_id] = rte_eth_xstats_get(port_id, eth_xstats[port_id], num_xstats[port_id]); +#endif + if (!strcmp(prox_port_cfg[port_id].short_name, "ixgbe")) { + potential_ixgbe_warn = 1; + xstat_tor_offset[port_id] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "rx_total_bytes"); + xstat_tpr_offset[port_id] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "rx_total_packets"); + } + tx_pkt_size_offset[port_id][PKT_SIZE_64] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_64_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_65] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_65_to_127_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_128] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_128_to_255_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_256] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_256_to_511_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_512] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_512_to_1023_packets"); + if (0 == strcmp(prox_port_cfg[port_id].short_name, "ixgbe")) { + tx_pkt_size_offset[port_id][PKT_SIZE_1024] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_1024_to_max_packets"); + } else { + tx_pkt_size_offset[port_id][PKT_SIZE_1024] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_1024_to_1522_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_1522] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_1523_to_max_packets"); + } + plog_info("offset = %d, %d, %d, %d, %d, %d %d\n", tx_pkt_size_offset[port_id][PKT_SIZE_64], tx_pkt_size_offset[port_id][PKT_SIZE_65], tx_pkt_size_offset[port_id][PKT_SIZE_128], tx_pkt_size_offset[port_id][PKT_SIZE_256], tx_pkt_size_offset[port_id][PKT_SIZE_512], tx_pkt_size_offset[port_id][PKT_SIZE_1024], tx_pkt_size_offset[port_id][PKT_SIZE_1522]); +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) + prox_free(eth_xstat_names[port_id]); +#endif + if (num_xstats[port_id] == 0 || eth_xstats[port_id] == NULL) { + plog_warn("Failed to initialize xstat for port %d, running without xstats\n", port_id); + num_xstats[port_id] = 0; + } + } + } + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if ((xstat_tor_offset[port_id] != -1) && (xstat_tpr_offset[port_id] != -1)) { + num_ixgbe_xstats = 2; // ixgbe PMD supports tor and tpr xstats + break; + } + } + if ((num_ixgbe_xstats == 0) && (potential_ixgbe_warn)) + plog_warn("Failed to initialize ixgbe xstat, running without ixgbe xstats\n"); +#endif +} + +static void nic_read_stats(uint8_t port_id) +{ + unsigned is_ixgbe = (0 == strcmp(prox_port_cfg[port_id].short_name, "ixgbe")); + + struct port_stats_sample *stats = &port_stats[port_id].sample[last_stat]; + +#if defined(PROX_STATS) && defined(PROX_HW_DIRECT_STATS) + if (is_ixgbe) { + struct port_stats_sample *prev = &port_stats[port_id].sample[!last_stat]; + ixgbe_read_stats(port_id, stats, prev, last_stat); + return; + } +#endif + uint64_t before, after; + + struct rte_eth_stats eth_stat; + + before = rte_rdtsc(); + rte_eth_stats_get(port_id, ð_stat); + after = rte_rdtsc(); + + stats->tsc = (before >> 1) + (after >> 1); + stats->no_mbufs = eth_stat.rx_nombuf; + stats->ierrors = eth_stat.ierrors; + stats->imissed = eth_stat.imissed; + stats->oerrors = eth_stat.oerrors; + stats->rx_bytes = eth_stat.ibytes; + + /* The goal would be to get the total number of bytes received + by the NIC (including overhead). Without the patch + (i.e. num_ixgbe_xstats == 0) we can't do this directly with + DPDK 2.1 API. So, we report the number of bytes (including + overhead) received by the host. */ + +#if XSTATS_SUPPORT + if (num_xstats[port_id]) { + rte_eth_xstats_get(port_id, eth_xstats[port_id], num_xstats[port_id]); + for (size_t i = 0; i < sizeof(tx_pkt_size_offset[0])/sizeof(tx_pkt_size_offset[0][0]); ++i) { + if (tx_pkt_size_offset[port_id][i] != -1) + stats->tx_pkt_size[i] = (eth_xstats[port_id][tx_pkt_size_offset[port_id][i]]).value; + else + stats->tx_pkt_size[i] = -1; + } + } else { + for (size_t i = 0; i < sizeof(tx_pkt_size_offset[0])/sizeof(tx_pkt_size_offset[0][0]); ++i) { + stats->tx_pkt_size[i] = -1; + } + } +#endif + if (is_ixgbe) { +#if XSTATS_SUPPORT + if (num_ixgbe_xstats) { + stats->rx_tot = eth_xstats[port_id][xstat_tpr_offset[port_id]].value; + stats->rx_bytes = eth_xstats[port_id][xstat_tor_offset[port_id]].value; + } else +#endif + { + stats->rx_tot = eth_stat.ipackets + eth_stat.ierrors + eth_stat.imissed; + /* On ixgbe, the rx_bytes counts bytes + received by Host without overhead. The + rx_tot counts the number of packets + received by the NIC. If we only add 20 * + rx_tot to rx_bytes, the result will also + take into account 20 * "number of packets + dropped by the nic". Note that in case CRC + is stripped on ixgbe, the CRC bytes are not + counted. */ + if (prox_port_cfg[port_id].port_conf.rxmode.hw_strip_crc == 1) + stats->rx_bytes = eth_stat.ibytes + + (24 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed)); + else + stats->rx_bytes = eth_stat.ibytes + + (20 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed)); + } + } else if (strcmp(prox_port_cfg[port_id].short_name, "i40e_vf") == 0) { + // For I40E VF, imissed already part of received packets + stats->rx_tot = eth_stat.ipackets; + } else { + stats->rx_tot = eth_stat.ipackets + eth_stat.imissed; + } + stats->tx_tot = eth_stat.opackets; + stats->tx_bytes = eth_stat.obytes; +} + +void stats_port_reset(void) +{ + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) { + rte_eth_stats_reset(port_id); + memset(&port_stats[port_id], 0, sizeof(struct port_stats)); + } + } +} + +void stats_port_update(void) +{ + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) { + nic_read_stats(port_id); + } + } +} + +uint64_t stats_port_get_ierrors(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].ierrors; + } + return ret; +} + +uint64_t stats_port_get_imissed(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].imissed; + } + return ret; +} + +uint64_t stats_port_get_rx_packets(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].rx_tot; + } + return ret; +} + +uint64_t stats_port_get_tx_packets(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].tx_tot; + } + return ret; +} + +int stats_get_n_ports(void) +{ + return n_ports; +} + +struct port_stats_sample *stats_get_port_stats_sample(uint32_t port_id, int l) +{ + return &port_stats[port_id].sample[l == last_stat]; +} + +int stats_port(uint8_t port_id, struct get_port_stats *gps) +{ + if (!prox_port_cfg[port_id].active) + return -1; + + struct port_stats_sample *last = &port_stats[port_id].sample[last_stat]; + struct port_stats_sample *prev = &port_stats[port_id].sample[!last_stat]; + + gps->no_mbufs_diff = last->no_mbufs - prev->no_mbufs; + gps->ierrors_diff = last->ierrors - prev->ierrors; + gps->imissed_diff = last->imissed - prev->imissed; + gps->rx_bytes_diff = last->rx_bytes - prev->rx_bytes; + gps->tx_bytes_diff = last->tx_bytes - prev->tx_bytes; + gps->rx_pkts_diff = last->rx_tot - prev->rx_tot; + if (unlikely(prev->rx_tot > last->rx_tot)) + gps->rx_pkts_diff = 0; + gps->tx_pkts_diff = last->tx_tot - prev->tx_tot; + if (unlikely(prev->tx_tot > last->tx_tot)) + gps->rx_pkts_diff = 0; + gps->rx_tot = last->rx_tot; + gps->tx_tot = last->tx_tot; + gps->no_mbufs_tot = last->no_mbufs; + gps->ierrors_tot = last->ierrors; + gps->imissed_tot = last->imissed; + + gps->last_tsc = last->tsc; + gps->prev_tsc = prev->tsc; + + return 0; +} -- cgit 1.2.3-korg