aboutsummaryrefslogtreecommitdiffstats
path: root/extras/hs-test/Makefile
AgeCommit message (Expand)AuthorFilesLines
2023-06-27hs-test: add nginx+quic testFilip Tehlar1-3/+6
2023-05-20hs-test: support for multiple workersFilip Tehlar1-1/+6
2023-04-26hs-test: add missing make target for .deps.okDave Wallace1-0/+3
2023-03-08hs-test: fix install-depsDave Wallace1-6/+10
2023-03-08hs-test: fix docker-ce installDave Wallace1-5/+17
2023-03-02hs-test: add support for running vpp in gdbFilip Tehlar1-1/+6
2023-03-01hs-test: fix wait for app after ldp changeFlorin Coras1-0/+1
2023-02-28hs-test: add option to unconfigure topologyMaros Ondrejicka1-1/+6
2023-02-24hs-test: improve test infraFilip Tehlar1-7/+64
2023-02-22hs-test: fix install/build on new ubuntu instanceDave Wallace1-1/+1
2023-02-16hs-test: check for missing output in nginx testsMaros Ondrejicka1-0/+3
2023-02-14hs-test: clean-up obsolete codeMaros Ondrejicka1-0/+1
2023-02-10hs-test: refactor test cases from no-topo suiteMaros Ondrejicka1-1/+0
2023-01-09hs-test: remove exec flags from source filesFilip Tehlar1-0/+0
2023-01-09hs-test: fix code styleFilip Tehlar1-0/+3
2022-09-19misc: add test framework for host stackFilip Tehlar1-0/+10
href='#n311'>311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * 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 <vnet/vnet.h>
#include <vppinfra/vec.h>
#include <vppinfra/format.h>
#include <assert.h>

#include <vnet/ethernet/ethernet.h>
#include <vnet/ethernet/sfp.h>
#include <dpdk/device/dpdk.h>

#include <dpdk/device/dpdk_priv.h>
#include <vppinfra/error.h>

#define foreach_dpdk_counter                    \
  _ (tx_frames_ok, opackets)                    \
  _ (tx_bytes_ok, obytes)                       \
  _ (tx_errors, oerrors)                        \
  _ (rx_frames_ok, ipackets)                    \
  _ (rx_bytes_ok, ibytes)                       \
  _ (rx_errors, ierrors)                        \
  _ (rx_missed, imissed)                        \
  _ (rx_no_bufs, rx_nombuf)

#define foreach_dpdk_q_counter                  \
  _ (rx_frames_ok, q_ipackets)                  \
  _ (tx_frames_ok, q_opackets)                  \
  _ (rx_bytes_ok, q_ibytes)                     \
  _ (tx_bytes_ok, q_obytes)                     \
  _ (rx_errors, q_errors)

#if RTE_VERSION < RTE_VERSION_NUM(21, 5, 0, 0)
#define PKT_RX_OUTER_IP_CKSUM_BAD PKT_RX_EIP_CKSUM_BAD
#endif

#define foreach_dpdk_pkt_rx_offload_flag                                      \
  _ (RX_FDIR, "RX packet with FDIR infos")                                    \
  _ (RX_FDIR_FLX, "RX packet with FDIR_FLX info")                             \
  _ (RX_FDIR_ID, "RX packet with FDIR_ID info")                               \
  _ (RX_IEEE1588_PTP, "RX IEEE1588 L2 Ethernet PT Packet")                    \
  _ (RX_IEEE1588_TMST, "RX IEEE1588 L2/L4 timestamped packet")                \
  _ (RX_IP_CKSUM_BAD, "IP cksum of RX pkt. is not OK")                        \
  _ (RX_IP_CKSUM_GOOD, "IP cksum of RX pkt. is valid")                        \
  _ (RX_IP_CKSUM_NONE, "no IP cksum of RX pkt.")                              \
  _ (RX_L4_CKSUM_BAD, "L4 cksum of RX pkt. is not OK")                        \
  _ (RX_L4_CKSUM_GOOD, "L4 cksum of RX pkt. is valid")                        \
  _ (RX_L4_CKSUM_NONE, "no L4 cksum of RX pkt.")                              \
  _ (RX_LRO, "LRO packet")                                                    \
  _ (RX_OUTER_IP_CKSUM_BAD, "External IP header checksum error")              \
  _ (RX_OUTER_L4_CKSUM_BAD, "External L4 header checksum error")              \
  _ (RX_OUTER_L4_CKSUM_GOOD, "External L4 header checksum OK")                \
  _ (RX_QINQ, "RX packet with QinQ tags")                                     \
  _ (RX_QINQ_STRIPPED, "RX packet QinQ tags stripped")                        \
  _ (RX_RSS_HASH, "RX packet with RSS hash result")                           \
  _ (RX_SEC_OFFLOAD, "RX packet with security offload")                       \
  _ (RX_SEC_OFFLOAD_FAILED, "RX packet with security offload failed")         \
  _ (RX_VLAN, "RX packet is a 802.1q VLAN packet")                            \
  _ (RX_VLAN_STRIPPED, "RX packet VLAN tag stripped")

#define foreach_dpdk_pkt_type                                           \
  _ (L2, ETHER, "Ethernet packet")                                      \
  _ (L2, ETHER_TIMESYNC, "Ethernet packet for time sync")               \
  _ (L2, ETHER_ARP, "ARP packet")                                       \
  _ (L2, ETHER_LLDP, "LLDP (Link Layer Discovery Protocol) packet")     \
  _ (L2, ETHER_NSH, "NSH (Network Service Header) packet")              \
  _ (L2, ETHER_VLAN, "VLAN packet")                                     \
  _ (L2, ETHER_QINQ, "QinQ packet")                                     \
  _ (L3, IPV4, "IPv4 packet without extension headers")                 \
  _ (L3, IPV4_EXT, "IPv4 packet with extension headers")                \
  _ (L3, IPV4_EXT_UNKNOWN, "IPv4 packet with or without extension headers") \
  _ (L3, IPV6, "IPv6 packet without extension headers")                 \
  _ (L3, IPV6_EXT, "IPv6 packet with extension headers")                \
  _ (L3, IPV6_EXT_UNKNOWN, "IPv6 packet with or without extension headers") \
  _ (L4, TCP, "TCP packet")                                             \
  _ (L4, UDP, "UDP packet")                                             \
  _ (L4, FRAG, "Fragmented IP packet")                                  \
  _ (L4, SCTP, "SCTP (Stream Control Transmission Protocol) packet")    \
  _ (L4, ICMP, "ICMP packet")                                           \
  _ (L4, NONFRAG, "Non-fragmented IP packet")                           \
  _ (TUNNEL, GRE, "GRE tunneling packet")                               \
  _ (TUNNEL, VXLAN, "VXLAN tunneling packet")                           \
  _ (TUNNEL, NVGRE, "NVGRE Tunneling packet")                           \
  _ (TUNNEL, GENEVE, "GENEVE Tunneling packet")                         \
  _ (TUNNEL, GRENAT, "Teredo, VXLAN or GRE Tunneling packet")           \
  _ (INNER_L2, ETHER, "Inner Ethernet packet")                          \
  _ (INNER_L2, ETHER_VLAN, "Inner Ethernet packet with VLAN")           \
  _ (INNER_L3, IPV4, "Inner IPv4 packet without extension headers")     \
  _ (INNER_L3, IPV4_EXT, "Inner IPv4 packet with extension headers")    \
  _ (INNER_L3, IPV4_EXT_UNKNOWN, "Inner IPv4 packet with or without extension headers") \
  _ (INNER_L3, IPV6, "Inner IPv6 packet without extension headers")     \
  _ (INNER_L3, IPV6_EXT, "Inner IPv6 packet with extension headers")    \
  _ (INNER_L3, IPV6_EXT_UNKNOWN, "Inner IPv6 packet with or without extension headers") \
  _ (INNER_L4, TCP, "Inner TCP packet")                                 \
  _ (INNER_L4, UDP, "Inner UDP packet")                                 \
  _ (INNER_L4, FRAG, "Inner fragmented IP packet")                       \
  _ (INNER_L4, SCTP, "Inner SCTP (Stream Control Transmission Protocol) packet") \
  _ (INNER_L4, ICMP, "Inner ICMP packet")                               \
  _ (INNER_L4, NONFRAG, "Inner non-fragmented IP packet")

#define foreach_dpdk_pkt_tx_offload_flag                                      \
  _ (TX_IEEE1588_TMST, "TX IEEE1588 packet to timestamp")                     \
  _ (TX_IPV4, "TX IPV4")                                                      \
  _ (TX_IPV6, "TX IPV6")                                                      \
  _ (TX_IP_CKSUM, "IP cksum of TX pkt. computed by NIC")                      \
  _ (TX_MACSEC, "TX MACSEC")                                                  \
  _ (TX_OUTER_IPV4, "TX outer IPV4")                                          \
  _ (TX_OUTER_IPV6, "TX outer IPV6")                                          \
  _ (TX_OUTER_IP_CKSUM, "Outer IP cksum of Tx pkt. computed by NIC")          \
  _ (TX_OUTER_UDP_CKSUM, "TX outer UDP cksum")                                \
  _ (TX_QINQ, "TX QINQ")                                                      \
  _ (TX_SCTP_CKSUM, "SCTP cksum of TX pkt. computed by NIC")                  \
  _ (TX_SEC_OFFLOAD, "TX SEC OFFLOAD")                                        \
  _ (TX_TCP_CKSUM, "TCP cksum of TX pkt. computed by NIC")                    \
  _ (TX_TCP_SEG, "TSO of TX pkt. done by NIC")                                \
  _ (TX_TUNNEL_GENEVE, "TX tunnel GENEVE")                                    \
  _ (TX_TUNNEL_GRE, "TX tunnel GRE")                                          \
  _ (TX_TUNNEL_GTP, "TX tunnel GTP")                                          \
  _ (TX_TUNNEL_IP, "TX tunnel IP")                                            \
  _ (TX_TUNNEL_IPIP, "TX tunnel IPIP")                                        \
  _ (TX_TUNNEL_MPLSINUDP, "TX tunnel MPLSinUDP")                              \
  _ (TX_TUNNEL_UDP, "TX tunnel UDP")                                          \
  _ (TX_TUNNEL_VXLAN, "TX packet is a VXLAN packet")                          \
  _ (TX_TUNNEL_VXLAN_GPE, "TX tunnel VXLAN GPE")                              \
  _ (TX_UDP_CKSUM, "TX UDP cksum")                                            \
  _ (TX_UDP_SEG, "TX UDP SEG")                                                \
  _ (TX_VLAN, "TX packet is a 802.1q VLAN packet")

#define foreach_dpdk_pkt_offload_flag           \
  foreach_dpdk_pkt_rx_offload_flag              \
  foreach_dpdk_pkt_tx_offload_flag

#define foreach_dpdk_pkt_dyn_rx_offload_flag				\
  _ (RX_TIMESTAMP, 0, "Timestamp field is valid")

u8 *
format_dpdk_device_name (u8 * s, va_list * args)
{
  dpdk_main_t *dm = &dpdk_main;
  u32 i = va_arg (*args, u32);
  dpdk_device_t *xd = vec_elt_at_index (dm->devices, i);

  return format (s, "%v", xd->name);
}

u8 *
format_dpdk_device_flags (u8 * s, va_list * args)
{
  dpdk_device_t *xd = va_arg (*args, dpdk_device_t *);
  u8 *t = 0;

#define _(a, b, c) if (xd->flags & (1 << a)) \
t = format (t, "%s%s", t ? " ":"", c);
  foreach_dpdk_device_flags
#undef _
    s = format (s, "%v", t);
  vec_free (t);
  return s;
}

static u8 *
format_dpdk_device_type (u8 * s, va_list * args)
{
  dpdk_main_t *dm = &dpdk_main;
  u32 i = va_arg (*args, u32);

  if (dm->devices[i].if_desc)
    return format (s, dm->devices[i].if_desc);
  else
    return format (s, "### UNKNOWN ###");
}

static u8 *
format_dpdk_link_status (u8 * s, va_list * args)
{
  dpdk_device_t *xd = va_arg (*args, dpdk_device_t *);
  struct rte_eth_link *l = &xd->link;
  vnet_main_t *vnm = vnet_get_main ();
  vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, xd->hw_if_index);

  s = format (s, "%s ", l->link_status ? "up" : "down");
  if (l->link_status)
    {
      u32 promisc = rte_eth_promiscuous_get (xd->port_id);

      s = format (s, "%s duplex ",
		  (l->link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ? "full" :
								       "half");
      s = format (s, "max-frame-size %d %s\n", hi->max_frame_size,
		  promisc ? " promisc" : "");
    }
  else
    s = format (s, "\n");

  return s;
}

#define _(n, v, str)                                            \
if (bitmap & v) {                                            \
  if (format_get_indent (s) > 72)                            \
    s = format(s,"\n%U", format_white_space, indent);        \
  s = format(s, "%s ", str);                                 \
}

u8 *
format_dpdk_rss_hf_name (u8 * s, va_list * args)
{
  u64 bitmap = va_arg (*args, u64);
  u32 indent = format_get_indent (s);

  if (!bitmap)
    return format (s, "none");

  foreach_dpdk_rss_hf return s;
}

#undef _

/* Convert to all lower case e.g "VLAN_STRIP" -> "vlan-strip"
   Works for both vector names and null terminated c strings. */
static u8 *
format_offload (u8 * s, va_list * va)
{
  u8 *id = va_arg (*va, u8 *);
  uword i, l;

  l = ~0;

  if (id)
    for (i = 0; id[i] != 0 && i < l; i++)
      {
	u8 c = id[i];

	if (c == '_')
	  c = '-';
	else
	  c = tolower (c);
	vec_add1 (s, c);
      }

  return s;
}

#define _(v, func)                                           \
if (bitmap & v) {                                            \
  if (format_get_indent (s) > 72)                            \
    s = format(s,"\n%U", format_white_space, indent);        \
  s = format(s, "%U ", format_offload, func (v));	     \
}

u8 *
format_dpdk_rx_offload_caps (u8 * s, va_list * args)
{
  u64 bitmap = va_arg (*args, u32);
  u32 indent = format_get_indent (s);
  uword i;

  if (!bitmap)
    return format (s, "none");

  for (i = 0; i < 64; i++)
    {
      u64 mask = (u64) 1 << i;

      _(mask, rte_eth_dev_rx_offload_name);
    }
  return s;
}

u8 *
format_dpdk_tx_offload_caps (u8 * s, va_list * args)
{
  u64 bitmap = va_arg (*args, u32);
  u32 indent = format_get_indent (s);
  uword i;

  if (!bitmap)
    return format (s, "none");

  for (i = 0; i < 64; i++)
    {
      u64 mask = (u64) 1 << i;

      _(mask, rte_eth_dev_tx_offload_name);
    }
  return s;
}

#undef _

u8 *
format_dpdk_device_errors (u8 * s, va_list * args)
{
  dpdk_device_t *xd = va_arg (*args, dpdk_device_t *);
  clib_error_t *e;
  u32 indent = format_get_indent (s);

  vec_foreach (e, xd->errors)
  {
    s = format (s, "%U%v\n", format_white_space, indent, e->what);
  }
  return s;
}

static u8 *
format_dpdk_device_module_info (u8 * s, va_list * args)
{
  dpdk_device_t *xd = va_arg (*args, dpdk_device_t *);
  struct rte_eth_dev_module_info mi = { 0 };
  struct rte_dev_eeprom_info ei = { 0 };

  if (rte_eth_dev_get_module_info (xd->port_id, &mi) != 0)
    return format (s, "unknown");

  ei.length = mi.eeprom_len;
  ei.data = clib_mem_alloc (mi.eeprom_len);

  if (rte_eth_dev_get_module_eeprom (xd->port_id, &ei) == 0)
    {
      s = format (s, "%U", format_sfp_eeprom, ei.data +
		  (mi.type == RTE_ETH_MODULE_SFF_8436 ? 0x80 : 0));
    }
  else
    s = format (s, "eeprom read error");

  clib_mem_free (ei.data);
  return s;
}

u8 *
format_dpdk_burst_fn (u8 *s, va_list *args)
{
  dpdk_device_t *xd = va_arg (*args, dpdk_device_t *);
  vlib_rx_or_tx_t dir = va_arg (*args, vlib_rx_or_tx_t);
  void *p;
  clib_elf_symbol_t sym;

#if RTE_VERSION < RTE_VERSION_NUM(21, 11, 0, 0)
#define rte_eth_fp_ops rte_eth_devices
#endif

  p = (dir == VLIB_TX) ? rte_eth_fp_ops[xd->port_id].tx_pkt_burst :
			 rte_eth_fp_ops[xd->port_id].rx_pkt_burst;

  if (clib_elf_symbol_by_address (pointer_to_uword (p), &sym))
    {
      return format (s, "%s", clib_elf_symbol_name (&sym));
    }
  else
    {
      return format (s, "(not available)");
    }
}

static u8 *
format_switch_info (u8 * s, va_list * args)
{
  struct rte_eth_switch_info *si =
    va_arg (*args, struct rte_eth_switch_info *);

  if (si->name)
    s = format (s, "name %s ", si->name);

  s = format (s, "domain id %d port id %d", si->domain_id, si->port_id);

  return s;
}

u8 *
format_dpdk_rte_device (u8 *s, va_list *args)
{
  struct rte_device *d = va_arg (*args, struct rte_device *);

  if (!d)
    return format (s, "not available");

#if RTE_VERSION >= RTE_VERSION_NUM(22, 11, 0, 0)
  s =
    format (s, "name: %s, numa: %d", rte_dev_name (d), rte_dev_numa_node (d));

  if (rte_dev_driver (d))
    s = format (s, ", driver: %s", rte_driver_name (rte_dev_driver (d)));

  if (rte_dev_bus (d))
    s = format (s, ", bus: %s", rte_bus_name (rte_dev_bus (d)));
#else
  s = format (s, "name: %s, numa: %d", d->name, d->numa_node);

  if (d->driver)
    s = format (s, ", driver: %s", d->driver->name);

  if (d->bus)
    s = format (s, ", bus: %s", d->bus->name);
#endif

  return s;
}

u8 *
format_dpdk_device (u8 * s, va_list * args)
{
  u32 dev_instance = va_arg (*args, u32);
  int verbose = va_arg (*args, int);
  dpdk_main_t *dm = &dpdk_main;
  vlib_main_t *vm = vlib_get_main ();
  dpdk_device_t *xd = vec_elt_at_index (dm->devices, dev_instance);
  u32 indent = format_get_indent (s);
  f64 now = vlib_time_now (vm);
  struct rte_eth_dev_info di;
  struct rte_eth_burst_mode mode;
  struct rte_pci_device *pci;
  struct rte_eth_rss_conf rss_conf;
  int vlan_off;
  int retval;

  dpdk_update_counters (xd, now);
  dpdk_update_link_state (xd, now);
  rte_eth_dev_info_get (xd->port_id, &di);

  s = format (s, "%U\n%Ucarrier %U",
	      format_dpdk_device_type, dev_instance,
	      format_white_space, indent + 2, format_dpdk_link_status, xd);
  s = format (s, "%Uflags: %U\n",
	      format_white_space, indent + 2, format_dpdk_device_flags, xd);
#if RTE_VERSION >= RTE_VERSION_NUM(22, 11, 0, 0)
  if (rte_dev_devargs (di.device) && rte_dev_devargs (di.device)->args)
    s = format (s, "%UDevargs: %s\n", format_white_space, indent + 2,
		rte_dev_devargs (di.device)->args);
#else
  if (di.device->devargs && di.device->devargs->args)
    s = format (s, "%UDevargs: %s\n",
		format_white_space, indent + 2, di.device->devargs->args);
#endif
  s = format (s,
	      "%Urx: queues %d (max %d), desc %d "
	      "(min %d max %d align %d)\n",
	      format_white_space, indent + 2, xd->conf.n_rx_queues,
	      di.max_rx_queues, xd->conf.n_rx_desc, di.rx_desc_lim.nb_min,
	      di.rx_desc_lim.nb_max, di.rx_desc_lim.nb_align);
  s = format (s,
	      "%Utx: queues %d (max %d), desc %d "
	      "(min %d max %d align %d)\n",
	      format_white_space, indent + 2, xd->conf.n_tx_queues,
	      di.max_tx_queues, xd->conf.n_tx_desc, di.tx_desc_lim.nb_min,
	      di.tx_desc_lim.nb_max, di.tx_desc_lim.nb_align);

  rss_conf.rss_key = 0;
  rss_conf.rss_hf = 0;
  retval = rte_eth_dev_rss_hash_conf_get (xd->port_id, &rss_conf);
  if (retval < 0)
    clib_warning ("rte_eth_dev_rss_hash_conf_get returned %d", retval);

  pci = dpdk_get_pci_device (&di);

  if (pci)
    {
      u8 *s2;
      if (xd->cpu_socket > -1)
	s2 = format (0, "%d", xd->cpu_socket);
      else
	s2 = format (0, "unknown");
      s = format (s,
		  "%Upci: device %04x:%04x subsystem %04x:%04x "
		  "address %04x:%02x:%02x.%02x numa %v\n",
		  format_white_space, indent + 2, pci->id.vendor_id,
		  pci->id.device_id, pci->id.subsystem_vendor_id,
		  pci->id.subsystem_device_id, pci->addr.domain, pci->addr.bus,
		  pci->addr.devid, pci->addr.function, s2);
      vec_free (s2);
    }

  if (di.switch_info.domain_id != RTE_ETH_DEV_SWITCH_DOMAIN_ID_INVALID)
    {
      s = format (s, "%Uswitch info: %U\n", format_white_space, indent + 2,
		  format_switch_info, &di.switch_info);
    }

  if (1 < verbose)
    {
      s = format (s, "%Umodule: %U\n", format_white_space, indent + 2,
		  format_dpdk_device_module_info, xd);
    }

  s = format (s, "%Umax rx packet len: %d\n", format_white_space, indent + 2,
	      di.max_rx_pktlen);
  s = format (s, "%Upromiscuous: unicast %s all-multicast %s\n",
	      format_white_space, indent + 2,
	      rte_eth_promiscuous_get (xd->port_id) ? "on" : "off",
	      rte_eth_allmulticast_get (xd->port_id) ? "on" : "off");
  vlan_off = rte_eth_dev_get_vlan_offload (xd->port_id);
  s = format (s, "%Uvlan offload: strip %s filter %s qinq %s\n",
	      format_white_space, indent + 2,
	      vlan_off & RTE_ETH_VLAN_STRIP_OFFLOAD ? "on" : "off",
	      vlan_off & RTE_ETH_VLAN_FILTER_OFFLOAD ? "on" : "off",
	      vlan_off & RTE_ETH_VLAN_EXTEND_OFFLOAD ? "on" : "off");
  s = format (s, "%Urx offload avail:  %U\n", format_white_space, indent + 2,
	      format_dpdk_rx_offload_caps, di.rx_offload_capa);
  s = format (s, "%Urx offload active: %U\n", format_white_space, indent + 2,
	      format_dpdk_rx_offload_caps, xd->enabled_rx_off);
  s = format (s, "%Utx offload avail:  %U\n", format_white_space, indent + 2,
	      format_dpdk_tx_offload_caps, di.tx_offload_capa);
  s = format (s, "%Utx offload active: %U\n", format_white_space, indent + 2,
	      format_dpdk_tx_offload_caps, xd->enabled_tx_off);
  s = format (s,
	      "%Urss avail:         %U\n"
	      "%Urss active:        %U\n",
	      format_white_space, indent + 2, format_dpdk_rss_hf_name,
	      di.flow_type_rss_offloads, format_white_space, indent + 2,
	      format_dpdk_rss_hf_name, rss_conf.rss_hf);

  if (rte_eth_tx_burst_mode_get (xd->port_id, 0, &mode) == 0)
    {
      s = format (s, "%Utx burst mode: %s%s\n", format_white_space, indent + 2,
		  mode.info,
		  mode.flags & RTE_ETH_BURST_FLAG_PER_QUEUE ? " (per queue)" :
							      "");
    }

  s = format (s, "%Utx burst function: %U\n", format_white_space, indent + 2,
	      format_dpdk_burst_fn, xd, VLIB_TX);

  if (rte_eth_rx_burst_mode_get (xd->port_id, 0, &mode) == 0)
    {
      s = format (s, "%Urx burst mode: %s%s\n", format_white_space, indent + 2,
		  mode.info,
		  mode.flags & RTE_ETH_BURST_FLAG_PER_QUEUE ? " (per queue)" :
							      "");
    }

  s = format (s, "%Urx burst function: %U\n", format_white_space, indent + 2,
	      format_dpdk_burst_fn, xd, VLIB_RX);

  /* $$$ MIB counters  */
  {
#define _(N, V)							\
    if (xd->stats.V != 0) {                                    \
      s = format (s, "\n%U%-40U%16Lu",                         \
                  format_white_space, indent + 2,              \
                  format_c_identifier, #N,                     \
                  xd->stats.V);                                \
    }                                                          \

    foreach_dpdk_counter
#undef _
  }

  u8 *xs = 0;
  u32 i = 0;
  struct rte_eth_xstat *xstat;
  struct rte_eth_xstat_name *xstat_names = 0;
  int len = vec_len (xd->xstats);
  vec_validate (xstat_names, len - 1);
  int ret = rte_eth_xstats_get_names (xd->port_id, xstat_names, len);

  if (ret >= 0 && ret <= len)
    {
      /* *INDENT-OFF* */
      vec_foreach_index(i, xd->xstats)
        {
          xstat = vec_elt_at_index(xd->xstats, i);
          if (verbose == 2 || (verbose && xstat->value))
            {
              xs = format(xs, "\n%U%-38s%16Lu",
                          format_white_space, indent + 4,
                          xstat_names[i].name,
                          xstat->value);
            }
        }
      /* *INDENT-ON* */

      vec_free (xstat_names);
    }

  if (xs)
    {
      s = format (s, "\n%Uextended stats:%v",
		  format_white_space, indent + 2, xs);
      vec_free (xs);
    }

  if (vec_len (xd->errors))
    {
      s = format (s, "%UErrors:\n  %U", format_white_space, indent,
		  format_dpdk_device_errors, xd);
    }

  return s;
}

u8 *
format_dpdk_tx_trace (u8 * s, va_list * va)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
  CLIB_UNUSED (vnet_main_t * vnm) = vnet_get_main ();
  dpdk_tx_trace_t *t = va_arg (*va, dpdk_tx_trace_t *);
  dpdk_main_t *dm = &dpdk_main;
  dpdk_device_t *xd = vec_elt_at_index (dm->devices, t->device_index);
  u32 indent = format_get_indent (s);

  s = format (s, "%U tx queue %d", format_vnet_sw_if_index_name, vnm,
	      xd->sw_if_index, t->queue_index);

  s = format (s, "\n%Ubuffer 0x%x: %U", format_white_space, indent,
	      t->buffer_index, format_vnet_buffer_no_chain, &t->buffer);

  s = format (s, "\n%U%U",
	      format_white_space, indent,
	      format_dpdk_rte_mbuf, &t->mb, &t->data);

  s = format (s, "\n%U%U", format_white_space, indent,
	      format_ethernet_header_with_length, t->buffer.pre_data,
	      sizeof (t->buffer.pre_data));

  return s;
}

u8 *
format_dpdk_rx_trace (u8 * s, va_list * va)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
  CLIB_UNUSED (vnet_main_t * vnm) = vnet_get_main ();
  dpdk_rx_trace_t *t = va_arg (*va, dpdk_rx_trace_t *);
  dpdk_main_t *dm = &dpdk_main;
  dpdk_device_t *xd = vec_elt_at_index (dm->devices, t->device_index);
  format_function_t *f;
  u32 indent = format_get_indent (s);

  s = format (s, "%U rx queue %d", format_vnet_sw_if_index_name, vnm,
	      xd->sw_if_index, t->queue_index);

  s = format (s, "\n%Ubuffer 0x%x: %U", format_white_space, indent,
	      t->buffer_index, format_vnet_buffer_no_chain, &t->buffer);

  s = format (s, "\n%U%U",
	      format_white_space, indent,
	      format_dpdk_rte_mbuf, &t->mb, &t->data);

  if (vm->trace_main.verbose)
    {
      s = format (s, "\n%UPacket Dump%s", format_white_space, indent + 2,
		  t->mb.data_len > sizeof (t->data) ? " (truncated)" : "");
      s = format (s, "\n%U%U", format_white_space, indent + 4,
		  format_hexdump, &t->data,
		  t->mb.data_len >
		  sizeof (t->data) ? sizeof (t->data) : t->mb.data_len);
    }
  f = node->format_buffer;
  if (!f)
    f = format_hex_bytes;
  s = format (s, "\n%U%U", format_white_space, indent,
	      f, t->buffer.pre_data, sizeof (t->buffer.pre_data));

  return s;
}


static inline u8 *
format_dpdk_pkt_types (u8 * s, va_list * va)
{
  u32 *pkt_types = va_arg (*va, u32 *);
  u32 indent __attribute__ ((unused)) = format_get_indent (s) + 2;

  if (!*pkt_types)
    return s;

  s = format (s, "Packet Types");

#define _(L, F, S)             \
  if ((*pkt_types & RTE_PTYPE_##L##_MASK) == RTE_PTYPE_##L##_##F)           \
    {                                                                       \
      s = format (s, "\n%U%s (0x%04x) %s", format_white_space, indent,      \
                     "RTE_PTYPE_" #L "_" #F, RTE_PTYPE_##L##_##F, S);       \
    }

  foreach_dpdk_pkt_type
#undef _
    return s;
}

static inline u8 *
format_dpdk_pkt_offload_flags (u8 * s, va_list * va)
{
  u64 *ol_flags = va_arg (*va, u64 *);
  u32 indent = format_get_indent (s) + 2;
  u64 rx_dynflag;
  int rx_dynflag_offset;

  if (!*ol_flags)
    return s;

  s = format (s, "Packet Offload Flags");

#define _(F, S)                                                               \
  if ((*ol_flags & RTE_MBUF_F_##F) == RTE_MBUF_F_##F)                         \
    {                                                                         \
      s = format (s, "\n%U%s (0x%04x) %s", format_white_space, indent,        \
		  "PKT_" #F, RTE_MBUF_F_##F, S);                              \
    }

  foreach_dpdk_pkt_offload_flag
#undef _
#define _(F, P, S)							\
  {									\
    rx_dynflag_offset = rte_mbuf_dynflag_lookup(RTE_MBUF_DYNFLAG_##F##_NAME, \
						P);			\
    if (rx_dynflag_offset >= 0)						\
      {									\
	rx_dynflag = (u64) 1 << rx_dynflag_offset;			\
	if (*ol_flags & rx_dynflag)					\
	  {								\
	    s = format (s, "\n%U%s %s", format_white_space, indent,	\
			#F, S);						\
	  }								\
      }									\
  }
    foreach_dpdk_pkt_dyn_rx_offload_flag
#undef _
    return s;
}

u8 *
format_dpdk_rte_mbuf_tso (u8 *s, va_list *va)
{
  struct rte_mbuf *mb = va_arg (*va, struct rte_mbuf *);
  if (mb->ol_flags & RTE_MBUF_F_TX_TCP_SEG)
    {
      s = format (s, "l4_len %u tso_segsz %u", mb->l4_len, mb->tso_segsz);
    }
  return s;
}

u8 *
format_dpdk_rte_mbuf_vlan (u8 * s, va_list * va)
{
  ethernet_vlan_header_tv_t *vlan_hdr =
    va_arg (*va, ethernet_vlan_header_tv_t *);

  if (clib_net_to_host_u16 (vlan_hdr->type) == ETHERNET_TYPE_DOT1AD)
    {
      s = format (s, "%U 802.1q vlan ",
		  format_ethernet_vlan_tci,
		  clib_net_to_host_u16 (vlan_hdr->priority_cfi_and_id));
      vlan_hdr++;
    }

  s = format (s, "%U",
	      format_ethernet_vlan_tci,
	      clib_net_to_host_u16 (vlan_hdr->priority_cfi_and_id));

  return s;
}

u8 *
format_dpdk_rte_mbuf (u8 * s, va_list * va)
{
  struct rte_mbuf *mb = va_arg (*va, struct rte_mbuf *);
  ethernet_header_t *eth_hdr = va_arg (*va, ethernet_header_t *);
  u32 indent = format_get_indent (s) + 2;

  s = format (
    s,
    "PKT MBUF: port %d, nb_segs %d, pkt_len %d"
    "\n%Ubuf_len %d, data_len %d, ol_flags 0x%lx, data_off %d, phys_addr 0x%x"
    "\n%Upacket_type 0x%x l2_len %u l3_len %u outer_l2_len %u outer_l3_len %u "
    "%U"
    "\n%Urss 0x%x fdir.hi 0x%x fdir.lo 0x%x",
    mb->port, mb->nb_segs, mb->pkt_len, format_white_space, indent,
    mb->buf_len, mb->data_len, mb->ol_flags, mb->data_off, mb->buf_iova,
    format_white_space, indent, mb->packet_type, mb->l2_len, mb->l3_len,
    mb->outer_l2_len, mb->outer_l3_len, format_dpdk_rte_mbuf_tso, mb,
    format_white_space, indent, mb->hash.rss, mb->hash.fdir.hi,
    mb->hash.fdir.lo);

  if (mb->ol_flags)
    s = format (s, "\n%U%U", format_white_space, indent,
		format_dpdk_pkt_offload_flags, &mb->ol_flags);

  if ((mb->ol_flags & RTE_MBUF_F_RX_VLAN) &&
      ((mb->ol_flags &
	(RTE_MBUF_F_RX_VLAN_STRIPPED | RTE_MBUF_F_RX_QINQ_STRIPPED)) == 0))
    {
      ethernet_vlan_header_tv_t *vlan_hdr =
	((ethernet_vlan_header_tv_t *) & (eth_hdr->type));
      s = format (s, " %U", format_dpdk_rte_mbuf_vlan, vlan_hdr);
    }

  if (mb->packet_type)
    s = format (s, "\n%U%U", format_white_space, indent,
		format_dpdk_pkt_types, &mb->packet_type);

  return s;
}

clib_error_t *
unformat_rss_fn (unformat_input_t * input, uword * rss_fn)
{
  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (0)
	;
#undef _
#define _(n, f, s)                                 \
      else if (unformat (input, s))             \
        *rss_fn |= f;

      foreach_dpdk_rss_hf
#undef _
	else
	{
	  return clib_error_return (0, "unknown input `%U'",
				    format_unformat_error, input);
	}
    }
  return 0;
}


/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */