Support packets in flight
[samplevnf.git] / VNFs / DPPD-PROX / stats_port.c
1 /*
2 // Copyright (c) 2010-2017 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16
17 #include <string.h>
18 #include <stdio.h>
19
20 #include <rte_version.h>
21 #if RTE_VERSION >= RTE_VERSION_NUM(21,11,0,0)
22 #include <ethdev_driver.h>      // Please configure DPDK with meson option -Denable_driver_sdk=true
23 #endif
24 #include <rte_ethdev.h>
25 #include <rte_cycles.h>
26 #include <rte_byteorder.h>
27
28 #include "prox_malloc.h"
29 #include "log.h"
30 #include "quit.h"
31 #include "stats_port.h"
32 #include "prox_port_cfg.h"
33 #include "rw_reg.h"
34 #include "prox_compat.h"
35
36 #if defined(PROX_STATS) && defined(PROX_HW_DIRECT_STATS)
37
38 /* Directly access hardware counters instead of going through DPDK. This allows getting
39  * specific counters that DPDK does not report or aggregates with other ones.
40  */
41
42 /* Definitions for IXGBE (taken from PMD) */
43 #define PROX_IXGBE_MPC(_i)           (0x03FA0 + ((_i) * 4)) /* 8 of these 3FA0-3FBC*/
44 #define PROX_IXGBE_QBRC_L(_i)        (0x01034 + ((_i) * 0x40)) /* 16 of these */
45 #define PROX_IXGBE_QBRC_H(_i)        (0x01038 + ((_i) * 0x40)) /* 16 of these */
46 #define PROX_IXGBE_QPRC(_i)          (0x01030 + ((_i) * 0x40)) /* 16 of these */
47 #define PROX_IXGBE_GPTC              0x04080
48 #define PROX_IXGBE_TPR               0x040D0
49 #define PROX_IXGBE_TORL              0x040C0
50 #define PROX_IXGBE_TORH              0x040C4
51 #define PROX_IXGBE_GOTCL             0x04090
52 #define PROX_IXGBE_GOTCH             0x04094
53
54 #define IXGBE_QUEUE_STAT_COUNTERS 16
55
56 static void ixgbe_read_stats(uint8_t port_id, struct port_stats_sample* stats, struct port_stats_sample *prev, int last_stat)
57 {
58         uint64_t before, after;
59         unsigned i;
60
61         struct rte_eth_dev* dev = &rte_eth_devices[port_id];
62
63         /* WARNING: Assumes hardware address is first field of structure! This may change! */
64         struct _dev_hw* hw = (struct _dev_hw *)(dev->data->dev_private);
65
66         stats->no_mbufs = dev->data->rx_mbuf_alloc_failed;
67
68         /* Since we only read deltas from the NIC, we have to add to previous values
69          * even though we actually substract again later to find out the rates!
70          */
71         stats->ierrors = prev->ierrors;
72         stats->imissed = prev->imissed;
73         stats->rx_bytes = prev->rx_bytes;
74         stats->rx_tot = prev->rx_tot;
75         stats->tx_bytes = prev->tx_bytes;
76         stats->tx_tot = prev->tx_tot;
77
78         /* WARNING: In this implementation, we count as imiised only the "no descriptor"
79          * missed packets cases and not the actual receive errors.
80          */
81         before = rte_rdtsc();
82         for (i = 0; i < 8; i++) {
83                 stats->imissed += PROX_READ_REG(hw, PROX_IXGBE_MPC(i));
84         }
85
86         /* RX stats */
87 #if 0
88         /* This version is equivalent to what ixgbe PMD does. It only accounts for packets
89          * actually received on the host.
90          */
91         for (i = 0; i < IXGBE_QUEUE_STAT_COUNTERS; i++) {
92                 /* ipackets: */
93                 stats->rx_tot += PROX_READ_REG(hw, PROX_IXGBE_QPRC(i));
94                 /* ibytes: */
95                 stats->rx_bytes += PROX_READ_REG(hw, PROX_IXGBE_QBRC_L(i));
96                 stats->rx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_QBRC_H(i)) << 32);
97         }
98 #else
99         /* This version reports the packets received by the NIC, regardless of whether they
100          * reached the host or not, etc. (no need to add ierrors or imissedto this packet count)
101          */
102         stats->rx_tot += PROX_READ_REG(hw, PROX_IXGBE_TPR);
103         stats->rx_bytes += PROX_READ_REG(hw, PROX_IXGBE_TORL);
104         stats->rx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_TORH) << 32);
105 #endif
106
107         /* TX stats */
108         /* opackets: */
109         stats->tx_tot += PROX_READ_REG(hw, PROX_IXGBE_GPTC);
110         /* obytes: */
111         stats->tx_bytes += PROX_READ_REG(hw, PROX_IXGBE_GOTCL);
112         stats->tx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_GOTCH) << 32);
113         after = rte_rdtsc();
114         stats->tsc = (before >> 1) + (after >> 1);
115 }
116
117 #endif
118
119 extern int last_stat;
120 static struct port_stats   port_stats[PROX_MAX_PORTS];
121 static uint8_t nb_interface;
122 static uint8_t n_ports;
123 static int num_xstats[PROX_MAX_PORTS] = {0};
124 static int num_ixgbe_xstats = 0;
125
126 #if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,1)
127 #define XSTATS_SUPPORT 1
128 #else
129 #define XSTATS_SUPPORT 0
130 #endif
131
132 #if XSTATS_SUPPORT
133 #if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0)
134 static struct rte_eth_xstat *eth_xstats[PROX_MAX_PORTS] = {0};
135 static struct rte_eth_xstat_name *eth_xstat_names[PROX_MAX_PORTS] = {0};
136 #else
137 static struct rte_eth_xstats *eth_xstats[PROX_MAX_PORTS] = {0};
138 static struct rte_eth_xstats *eth_xstat_names[PROX_MAX_PORTS] = {0};
139 #endif
140 static int xstat_tpr_offset[PROX_MAX_PORTS] ={0}, xstat_tor_offset[PROX_MAX_PORTS] = {0};
141 static int tx_pkt_size_offset[PROX_MAX_PORTS][PKT_SIZE_COUNT];
142 #endif
143
144 #if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0)
145 static int find_xstats_str(struct rte_eth_xstat_name *xstats, int n, const char *name)
146 #else
147 static int find_xstats_str(struct rte_eth_xstats *xstats, int n, const char *name)
148 #endif
149 {
150         for (int i = 0; i < n; i++) {
151                 if (strcmp(xstats[i].name, name) == 0)
152                         return i;
153         }
154
155         return -1;
156 }
157
158 void stats_port_init(void)
159 {
160         int potential_ixgbe_warn = 0;
161         for (int i = 0; i < PROX_MAX_PORTS; i++) {
162                 xstat_tpr_offset[i] = -1;
163                 xstat_tor_offset[i] = -1;
164                 for (int j = 0; j < PKT_SIZE_COUNT; j++) {
165                         tx_pkt_size_offset[i][j] = -1;
166                 }
167         }
168 #if XSTATS_SUPPORT
169         nb_interface = prox_last_port_active() + 1;
170         n_ports = prox_nb_active_ports();
171
172         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
173                 if (prox_port_cfg[port_id].active) {
174 #if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0)
175                         if ((num_xstats[port_id] = rte_eth_xstats_get_names(port_id, NULL, 0)) < 0) {
176                                 plog_err("\tport %u: rte_eth_xstats_get_names returns %d\n", port_id, num_xstats[port_id]);
177                                 continue;
178                         }
179                         eth_xstat_names[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstat_name), prox_port_cfg[port_id].socket);
180                         PROX_PANIC(eth_xstat_names[port_id] == NULL, "Error allocating memory for xstats");
181                         num_xstats[port_id] = rte_eth_xstats_get_names(port_id, eth_xstat_names[port_id], num_xstats[port_id]);
182                         eth_xstats[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstat), prox_port_cfg[port_id].socket);
183                         PROX_PANIC(eth_xstats[port_id] == NULL, "Error allocating memory for xstats");
184 #else
185                         num_xstats[port_id] = rte_eth_xstats_get(port_id, NULL, 0);
186                         eth_xstats[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstats), prox_port_cfg[port_id].socket);
187                         PROX_PANIC(eth_xstats[port_id] == NULL, "Error allocating memory for xstats");
188                         eth_xstat_names[port_id] = eth_xstats[port_id];
189                         num_xstats[port_id] = rte_eth_xstats_get(port_id, eth_xstats[port_id], num_xstats[port_id]);
190 #endif
191                         if (!strcmp(prox_port_cfg[port_id].short_name, "ixgbe")) {
192                                 potential_ixgbe_warn = 1;
193                                 xstat_tor_offset[port_id] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "rx_total_bytes");
194                                 xstat_tpr_offset[port_id] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "rx_total_packets");
195                         }
196                         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");
197                         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");
198                         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");
199                         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");
200                         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");
201                         if (0 == strcmp(prox_port_cfg[port_id].short_name, "ixgbe")) {
202                                 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");
203                         } else {
204                                 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");
205                                 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");
206                         }
207                         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]);
208 #if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0)
209                         prox_free(eth_xstat_names[port_id]);
210 #endif
211                         if (num_xstats[port_id] == 0 || eth_xstats[port_id] == NULL) {
212                                 plog_warn("Failed to initialize xstat for port %d, running without xstats\n", port_id);
213                                 num_xstats[port_id] = 0;
214                         }
215                 }
216         }
217         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
218                 if ((xstat_tor_offset[port_id] != -1) && (xstat_tpr_offset[port_id] != -1)) {
219                         num_ixgbe_xstats = 2;   // ixgbe PMD supports tor and tpr xstats
220                         break;
221                 }
222         }
223         if ((num_ixgbe_xstats == 0) && (potential_ixgbe_warn))
224                 plog_warn("Failed to initialize ixgbe xstat, running without ixgbe xstats\n");
225 #endif
226 }
227
228 static void nic_read_stats(uint8_t port_id)
229 {
230         unsigned is_ixgbe = (0 == strcmp(prox_port_cfg[port_id].short_name, "ixgbe"));
231
232         struct port_stats_sample *stats = &port_stats[port_id].sample[last_stat];
233
234 #if defined(PROX_STATS) && defined(PROX_HW_DIRECT_STATS)
235         if (is_ixgbe) {
236                 struct port_stats_sample *prev = &port_stats[port_id].sample[!last_stat];
237                 ixgbe_read_stats(port_id, stats, prev, last_stat);
238                 return;
239         }
240 #endif
241         uint64_t before, after;
242
243         struct rte_eth_stats eth_stat;
244
245         before = rte_rdtsc();
246         rte_eth_stats_get(port_id, &eth_stat);
247         after = rte_rdtsc();
248
249         stats->tsc = (before >> 1) + (after >> 1);
250         stats->no_mbufs = eth_stat.rx_nombuf;
251         stats->ierrors = eth_stat.ierrors;
252         stats->imissed = eth_stat.imissed;
253         stats->oerrors = eth_stat.oerrors;
254         stats->rx_bytes = eth_stat.ibytes;
255
256         /* The goal would be to get the total number of bytes received
257            by the NIC (including overhead). Without the patch
258            (i.e. num_ixgbe_xstats == 0) we can't do this directly with
259            DPDK 2.1 API. So, we report the number of bytes (including
260            overhead) received by the host. */
261
262 #if XSTATS_SUPPORT
263         if (num_xstats[port_id]) {
264                 rte_eth_xstats_get(port_id, eth_xstats[port_id], num_xstats[port_id]);
265                 for (size_t i = 0; i < sizeof(tx_pkt_size_offset[0])/sizeof(tx_pkt_size_offset[0][0]); ++i) {
266                         if (tx_pkt_size_offset[port_id][i] != -1)
267                                 stats->tx_pkt_size[i] = (eth_xstats[port_id][tx_pkt_size_offset[port_id][i]]).value;
268                         else
269                                 stats->tx_pkt_size[i] = -1;
270                 }
271         } else {
272                 for (size_t i = 0; i < sizeof(tx_pkt_size_offset[0])/sizeof(tx_pkt_size_offset[0][0]); ++i) {
273                         stats->tx_pkt_size[i] = -1;
274                 }
275         }
276 #endif
277         if (is_ixgbe) {
278 #if XSTATS_SUPPORT
279                 if (num_ixgbe_xstats) {
280                         stats->rx_tot = eth_xstats[port_id][xstat_tpr_offset[port_id]].value;
281                         stats->rx_bytes = eth_xstats[port_id][xstat_tor_offset[port_id]].value;
282                 } else
283 #endif
284                 {
285                         stats->rx_tot = eth_stat.ipackets + eth_stat.ierrors + eth_stat.imissed;
286                         /* On ixgbe, the rx_bytes counts bytes
287                            received by Host without overhead. The
288                            rx_tot counts the number of packets
289                            received by the NIC. If we only add 20 *
290                            rx_tot to rx_bytes, the result will also
291                            take into account 20 * "number of packets
292                            dropped by the nic". Note that in case CRC
293                            is stripped on ixgbe, the CRC bytes are not
294                            counted. */
295 #if defined (RTE_ETH_RX_OFFLOAD_CRC_STRIP)
296                         if (prox_port_cfg[port_id].requested_rx_offload & RTE_ETH_RX_OFFLOAD_CRC_STRIP)
297                                 stats->rx_bytes = eth_stat.ibytes +
298                                         (24 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed));
299                         else
300                                 stats->rx_bytes = eth_stat.ibytes +
301                                         (20 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed));
302 #else
303 #if defined (RTE_ETH_RX_OFFLOAD_KEEP_CRC)
304                         if (prox_port_cfg[port_id].requested_rx_offload & RTE_ETH_RX_OFFLOAD_KEEP_CRC)
305                                 stats->rx_bytes = eth_stat.ibytes +
306                                         (20 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed));
307                         else
308                                 stats->rx_bytes = eth_stat.ibytes +
309                                         (24 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed));
310 #endif
311 #endif
312                 }
313         } else if (strcmp(prox_port_cfg[port_id].short_name, "i40e_vf") == 0) {
314                 // For I40E VF, imissed already part of received packets
315                 stats->rx_tot = eth_stat.ipackets;
316         } else {
317                 stats->rx_tot = eth_stat.ipackets + eth_stat.imissed;
318         }
319         stats->tx_tot = eth_stat.opackets;
320         stats->tx_bytes = eth_stat.obytes;
321 }
322
323 void stats_port_reset(void)
324 {
325         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
326                 if (prox_port_cfg[port_id].active) {
327                         rte_eth_stats_reset(port_id);
328                         memset(&port_stats[port_id], 0, sizeof(struct port_stats));
329                 }
330         }
331 }
332
333 void stats_port_update(void)
334 {
335         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
336                 if (prox_port_cfg[port_id].active) {
337                         nic_read_stats(port_id);
338                 }
339         }
340 }
341
342 uint64_t stats_port_get_ierrors(void)
343 {
344         uint64_t ret = 0;
345
346         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
347                 if (prox_port_cfg[port_id].active)
348                         ret += port_stats[port_id].sample[last_stat].ierrors;
349         }
350         return ret;
351 }
352
353 uint64_t stats_port_get_imissed(void)
354 {
355         uint64_t ret = 0;
356
357         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
358                 if (prox_port_cfg[port_id].active)
359                         ret += port_stats[port_id].sample[last_stat].imissed;
360         }
361         return ret;
362 }
363
364 uint64_t stats_port_get_rx_packets(void)
365 {
366         uint64_t ret = 0;
367
368         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
369                 if (prox_port_cfg[port_id].active)
370                         ret += port_stats[port_id].sample[last_stat].rx_tot;
371         }
372         return ret;
373 }
374
375 uint64_t stats_port_get_tx_packets(void)
376 {
377         uint64_t ret = 0;
378
379         for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) {
380                 if (prox_port_cfg[port_id].active)
381                         ret += port_stats[port_id].sample[last_stat].tx_tot;
382         }
383         return ret;
384 }
385
386 int stats_get_n_ports(void)
387 {
388         return n_ports;
389 }
390
391 struct port_stats_sample *stats_get_port_stats_sample(uint32_t port_id, int l)
392 {
393         return &port_stats[port_id].sample[l == last_stat];
394 }
395
396 int stats_port(uint8_t port_id, struct get_port_stats *gps)
397 {
398         if (!prox_port_cfg[port_id].active)
399                 return -1;
400
401         struct port_stats_sample *last = &port_stats[port_id].sample[last_stat];
402         struct port_stats_sample *prev = &port_stats[port_id].sample[!last_stat];
403
404         gps->no_mbufs_diff = last->no_mbufs - prev->no_mbufs;
405         gps->ierrors_diff = last->ierrors - prev->ierrors;
406         gps->imissed_diff = last->imissed - prev->imissed;
407         gps->rx_bytes_diff = last->rx_bytes - prev->rx_bytes;
408         gps->tx_bytes_diff = last->tx_bytes - prev->tx_bytes;
409         gps->rx_pkts_diff = last->rx_tot - prev->rx_tot;
410         if (unlikely(prev->rx_tot > last->rx_tot))
411                 gps->rx_pkts_diff = 0;
412         gps->tx_pkts_diff = last->tx_tot - prev->tx_tot;
413         if (unlikely(prev->tx_tot > last->tx_tot))
414                 gps->rx_pkts_diff = 0;
415         gps->rx_tot = last->rx_tot;
416         gps->tx_tot = last->tx_tot;
417         gps->no_mbufs_tot = last->no_mbufs;
418         gps->ierrors_tot = last->ierrors;
419         gps->imissed_tot = last->imissed;
420
421         gps->last_tsc = last->tsc;
422         gps->prev_tsc = prev->tsc;
423
424         return 0;
425 }