aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/dpdk/device/cli.c
blob: aeeb772d4fb44c5d8444fa672516f3585d7e5ded (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
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

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
#!/usr/bin/python

# Copyright (c) 2017 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.

"""This script renames the given robot keywords in the given directory
recursively.

Example:

  ./rename_robot_keywords.py -i kws.csv -s ";" -d ~/ws/vpp/git/csit/ -vvv

  Input file "kws.csv" is CSV file exported from e.g. MS Excel. Its structure
  must be:

    <Old keyword name><separator><New keyword name>

  One keyword per line.

"""

import argparse
import sys
import re
from os import walk, rename
from os.path import join


def time_interval(func):
    """Decorator function to measure the time spent by the decorated function.

    :param func: Decorated function.
    :type func: Callable object.
    :returns: Wrapper function.
    :rtype: Callable object.
    """

    import time

    def wrapper(*args, **kwargs):
        start = time.clock()
        result = func(*args, **kwargs)
        stop = time.clock()
        print("\nRenaming done in {:.5g} seconds\n".
              format(stop - start))
        return result
    return wrapper


def get_files(path, extension):
    """Generates the list of files to process.

    :param path: Path to files.
    :param extension: Extension of files to process. If it is the empty string,
    all files will be processed.
    :type path: str
    :type extension: str
    :returns: List of files to process.
    :rtype: list
    """

    file_list = list()
    for root, dirs, files in walk(path):
        for filename in files:
            if extension:
                if filename.endswith(extension):
                    file_list.append(join(root, filename))
            else:
                file_list.append(join(root, filename))

    return file_list


def read_keywords(args):
    """This function reads the keywords from the input file and creates:

    - a dictionary where the key is the old name and the value is the new name,
      these keywords will be further processed.
    - a list of keywords which will not be processed, typically keywords with
    argument(s) in its names.
    - a list of duplicates - duplicated keyword names or names which are parts
    of another keyword name, they will not be processed.

    :param args:  Parsed arguments.
    :type args: ArgumentParser
    :returns: keyword names - dictionary where the key is the old name and the
    value is the new name; ignored keyword names - list of keywords which will
    not be processed; duplicates - duplicated keyword names or names which are
    parts of another keyword name, they will not be processed.
    :rtype: tuple(dict, list, list)
    """

    kw_names = dict()
    ignored_kw_names = list()
    duplicates = list()

    for line in args.input:
        old_name, new_name = line.split(args.separator)
        if '$' in old_name:
            ignored_kw_names.append((old_name, new_name[:-1]))
        elif old_name in kw_names.keys():
            duplicates.append((old_name, new_name[:-1]))
        else:
            kw_names[old_name] = new_name[:-1]

    # Remove duplicates:
    for old_name, _ in duplicates:
        new_name = kw_names.pop(old_name, None)
        if new_name:
            duplicates.append((old_name, new_name))

    # Find KW names which are parts of other KW names:
    for old_name in kw_names.keys():
        count = 0
        for key in kw_names.keys():
            if old_name in key:
                count += 1
            if old_name in kw_names[key]:
                if old_name != key:
                    count += 1
        if count > 1:
            duplicates.append((old_name, kw_names[old_name]))
            kw_names.pop(old_name)

    return kw_names, ignored_kw_names, duplicates


def rename_keywords(file_list, kw_names, args):
    """Rename the keywords in specified files.

    :param file_list: List of files to be processed.
    :param kw_names: Dictionary  where the key is the old name and the value is
    the new name
    :type file_list: list
    :type kw_names: dict
    """

    kw_not_found = list()

    for old_name, new_name in kw_names.items():
        kw_found = False
        if args.verbosity > 0:
            print("\nFrom: {}\n  To: {}\n".format(old_name, new_name))
        for file_name in file_list:
            tmp_file_name = file_name + ".new"
            with open(file_name) as file_read:
                file_write = open(tmp_file_name, 'w')
                occurrences = 0
                for line in file_read:
                    new_line = re.sub(old_name, new_name, line)
                    file_write.write(new_line)
                    if new_line != line:
                        occurrences += 1
                if occurrences:
                    kw_found = True
                    if args.verbosity > 1:
                        print(" {:3d}: {}".format(occurrences, file_name))
                file_write.close()
            rename(tmp_file_name, file_name)
        if not kw_found:
            kw_not_found.append(old_name)

    if args.verbosity > 0:
        print("\nKeywords not found:")
        for item in kw_not_found:
            print("  {}".format(item))


def parse_args():
    """Parse arguments from command line.

    :returns: Parsed arguments.
    :rtype: ArgumentParser
    """

    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.
                                     RawDescriptionHelpFormatter)
    parser.add_argument("-i", "--input",
                        required=True,
                        type=argparse.FileType('r'),
                        help="Text file with the old keyword name and the new "
                             "keyword name separated by separator per line.")
    parser.add_argument("-s", "--separator",
                        default=";",
                        type=str,
                        help="Separator which separates the old and the new "
                             "keyword name.")
    parser.add_argument("-d", "--dir",
                        required=True,
                        type=str,
                        help="Directory with robot files where the keywords "
                             "should be recursively searched.")
    parser.add_argument("-v", "--verbosity", action="count",
                        help="Set the output verbosity.")
    return parser.parse_args()


@time_interval
def main():
    """Main function."""

    args = parse_args()

    kw_names, ignored_kw_names, duplicates = read_keywords(args)

    file_list = get_files(args.dir, "robot")

    if args.verbosity > 2:
        print("\nList of files to be processed:")
        for item in file_list:
            print("  {}".format(item))
        print("\n{} files to be processed.\n".format(len(file_list)))

        print("\nList of keywords to be renamed:")
        for item in kw_names:
            print("  {}".format(item))
        print("\n{} keywords to be renamed.\n".format(len(kw_names)))

    rename_keywords(file_list, kw_names, args)

    if args.verbosity >= 0:
        print("\nIgnored keywords: ({})".format(len(ignored_kw_names)))
        for old, new in ignored_kw_names:
            print("  From: {}\n    To: {}\n".format(old, new))

        print("\nIgnored duplicates ({}):".format(len(duplicates)))
        for old, new in duplicates:
            print("  From: {}\n    To: {}\n".format(old, new))


if __name__ == "__main__":
    sys.exit(main())
1510' href='#n1510'>1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902
/*
 * 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/error.h>
#include <vppinfra/format.h>
#include <vppinfra/xxhash.h>

#include <vnet/ethernet/ethernet.h>
#include <dpdk/device/dpdk.h>
#include <vnet/classify/vnet_classify.h>
#include <vnet/mpls/packet.h>

#include <dpdk/device/dpdk_priv.h>

/**
 * @file
 * @brief CLI for DPDK Abstraction Layer and pcap Tx Trace.
 *
 * This file contains the source code for CLI for DPDK
 * Abstraction Layer and pcap Tx Trace.
 */


static clib_error_t *
get_hqos (u32 hw_if_index, u32 subport_id, dpdk_device_t ** xd,
	  dpdk_device_config_t ** devconf)
{
  dpdk_main_t *dm = &dpdk_main;
  vnet_hw_interface_t *hw;
  struct rte_eth_dev_info dev_info;
  uword *p = 0;
  clib_error_t *error = NULL;


  if (hw_if_index == (u32) ~ 0)
    {
      error = clib_error_return (0, "please specify valid interface name");
      goto done;
    }

  if (subport_id != 0)
    {
      error = clib_error_return (0, "Invalid subport");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  *xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  rte_eth_dev_info_get ((*xd)->device_index, &dev_info);
  if (dev_info.pci_dev)
    {				/* bonded interface has no pci info */
      vlib_pci_addr_t pci_addr;

      pci_addr.domain = dev_info.pci_dev->addr.domain;
      pci_addr.bus = dev_info.pci_dev->addr.bus;
      pci_addr.slot = dev_info.pci_dev->addr.devid;
      pci_addr.function = dev_info.pci_dev->addr.function;

      p =
	hash_get (dm->conf->device_config_index_by_pci_addr, pci_addr.as_u32);
    }

  if (p)
    (*devconf) = pool_elt_at_index (dm->conf->dev_confs, p[0]);
  else
    (*devconf) = &dm->conf->default_devconf;

done:
  return error;
}

static clib_error_t *
pcap_trace_command_fn (vlib_main_t * vm,
		       unformat_input_t * input, vlib_cli_command_t * cmd)
{
#define PCAP_DEF_PKT_TO_CAPTURE (100)

  unformat_input_t _line_input, *line_input = &_line_input;
  dpdk_main_t *dm = &dpdk_main;
  u8 *filename;
  u8 *chroot_filename = 0;
  u32 max = 0;
  int enabled = 0;
  int errorFlag = 0;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "on"))
	{
	  if (dm->tx_pcap_enable == 0)
	    {
	      enabled = 1;
	    }
	  else
	    {
	      vlib_cli_output (vm, "pcap tx capture already on...");
	      errorFlag = 1;
	      break;
	    }
	}
      else if (unformat (line_input, "off"))
	{
	  if (dm->tx_pcap_enable)
	    {
	      vlib_cli_output (vm, "captured %d pkts...",
			       dm->pcap_main.n_packets_captured + 1);
	      if (dm->pcap_main.n_packets_captured)
		{
		  dm->pcap_main.n_packets_to_capture =
		    dm->pcap_main.n_packets_captured;
		  error = pcap_write (&dm->pcap_main);
		  if (error)
		    clib_error_report (error);
		  else
		    vlib_cli_output (vm, "saved to %s...", dm->pcap_filename);
		}

	      dm->tx_pcap_enable = 0;
	    }
	  else
	    {
	      vlib_cli_output (vm, "pcap tx capture already off...");
	      errorFlag = 1;
	      break;
	    }
	}
      else if (unformat (line_input, "max %d", &max))
	{
	  if (dm->tx_pcap_enable)
	    {
	      vlib_cli_output (vm,
			       "can't change max value while pcap tx capture active...");
	      errorFlag = 1;
	      break;
	    }
	}
      else if (unformat (line_input, "intfc %U",
			 unformat_vnet_sw_interface, dm->vnet_main,
			 &dm->pcap_sw_if_index))
	;

      else if (unformat (line_input, "intfc any"))
	{
	  dm->pcap_sw_if_index = 0;
	}
      else if (unformat (line_input, "file %s", &filename))
	{
	  if (dm->tx_pcap_enable)
	    {
	      vlib_cli_output (vm,
			       "can't change file while pcap tx capture active...");
	      errorFlag = 1;
	      break;
	    }

	  /* Brain-police user path input */
	  if (strstr ((char *) filename, "..")
	      || index ((char *) filename, '/'))
	    {
	      vlib_cli_output (vm, "illegal characters in filename '%s'",
			       filename);
	      vlib_cli_output (vm,
			       "Hint: Only filename, do not enter directory structure.");
	      vec_free (filename);
	      errorFlag = 1;
	      break;
	    }

	  chroot_filename = format (0, "/tmp/%s%c", filename, 0);
	  vec_free (filename);
	}
      else if (unformat (line_input, "status"))
	{
	  if (dm->pcap_sw_if_index == 0)
	    {
	      vlib_cli_output (vm, "max is %d for any interface to file %s",
			       dm->
			       pcap_pkts_to_capture ? dm->pcap_pkts_to_capture
			       : PCAP_DEF_PKT_TO_CAPTURE,
			       dm->
			       pcap_filename ? dm->pcap_filename : (u8 *)
			       "/tmp/vpe.pcap");
	    }
	  else
	    {
	      vlib_cli_output (vm, "max is %d for interface %U to file %s",
			       dm->
			       pcap_pkts_to_capture ? dm->pcap_pkts_to_capture
			       : PCAP_DEF_PKT_TO_CAPTURE,
			       format_vnet_sw_if_index_name, dm->vnet_main,
			       dm->pcap_sw_if_index,
			       dm->
			       pcap_filename ? dm->pcap_filename : (u8 *)
			       "/tmp/vpe.pcap");
	    }

	  if (dm->tx_pcap_enable == 0)
	    {
	      vlib_cli_output (vm, "pcap tx capture is off...");
	    }
	  else
	    {
	      vlib_cli_output (vm, "pcap tx capture is on: %d of %d pkts...",
			       dm->pcap_main.n_packets_captured,
			       dm->pcap_main.n_packets_to_capture);
	    }
	  break;
	}

      else
	{
	  error = clib_error_return (0, "unknown input `%U'",
				     format_unformat_error, line_input);
	  errorFlag = 1;
	  break;
	}
    }
  unformat_free (line_input);


  if (errorFlag == 0)
    {
      /* Since no error, save configured values. */
      if (chroot_filename)
	{
	  if (dm->pcap_filename)
	    vec_free (dm->pcap_filename);
	  vec_add1 (chroot_filename, 0);
	  dm->pcap_filename = chroot_filename;
	}

      if (max)
	dm->pcap_pkts_to_capture = max;


      if (enabled)
	{
	  if (dm->pcap_filename == 0)
	    dm->pcap_filename = format (0, "/tmp/vpe.pcap%c", 0);

	  memset (&dm->pcap_main, 0, sizeof (dm->pcap_main));
	  dm->pcap_main.file_name = (char *) dm->pcap_filename;
	  dm->pcap_main.n_packets_to_capture = PCAP_DEF_PKT_TO_CAPTURE;
	  if (dm->pcap_pkts_to_capture)
	    dm->pcap_main.n_packets_to_capture = dm->pcap_pkts_to_capture;

	  dm->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
	  dm->tx_pcap_enable = 1;
	  vlib_cli_output (vm, "pcap tx capture on...");
	}
    }
  else if (chroot_filename)
    vec_free (chroot_filename);


  return error;
}

/*?
 * This command is used to start or stop a packet capture, or show
 * the status of packet capture.
 *
 * This command has the following optional parameters:
 *
 * - <b>on|off</b> - Used to start or stop a packet capture.
 *
 * - <b>max <nn></b> - Depth of local buffer. Once '<em>nn</em>' number
 *   of packets have been received, buffer is flushed to file. Once another
 *   '<em>nn</em>' number of packets have been received, buffer is flushed
 *   to file, overwriting previous write. If not entered, value defaults
 *   to 100. Can only be updated if packet capture is off.
 *
 * - <b>intfc <interface>|any</b> - Used to specify a given interface,
 *   or use '<em>any</em>' to run packet capture on all interfaces.
 *   '<em>any</em>' is the default if not provided. Settings from a previous
 *   packet capture are preserved, so '<em>any</em>' can be used to reset
 *   the interface setting.
 *
 * - <b>file <name></b> - Used to specify the output filename. The file will
 *   be placed in the '<em>/tmp</em>' directory, so only the filename is
 *   supported. Directory should not be entered. If file already exists, file
 *   will be overwritten. If no filename is provided, '<em>/tmp/vpe.pcap</em>'
 *   will be used. Can only be updated if packet capture is off.
 *
 * - <b>status</b> - Displays the current status and configured attributes
 *   associated with a packet capture. If packet capture is in progress,
 *   '<em>status</em>' also will return the number of packets currently in
 *   the local buffer. All additional attributes entered on command line
 *   with '<em>status</em>' will be ingnored and not applied.
 *
 * @cliexpar
 * Example of how to display the status of a tx packet capture when off:
 * @cliexstart{pcap tx trace status}
 * max is 100, for any interface to file /tmp/vpe.pcap
 * pcap tx capture is off...
 * @cliexend
 * Example of how to start a tx packet capture:
 * @cliexstart{pcap tx trace on max 35 intfc GigabitEthernet0/8/0 file vppTest.pcap}
 * pcap tx capture on...
 * @cliexend
 * Example of how to display the status of a tx packet capture in progress:
 * @cliexstart{pcap tx trace status}
 * max is 35, for interface GigabitEthernet0/8/0 to file /tmp/vppTest.pcap
 * pcap tx capture is on: 20 of 35 pkts...
 * @cliexend
 * Example of how to stop a tx packet capture:
 * @cliexstart{vppctl pcap tx trace off}
 * captured 21 pkts...
 * saved to /tmp/vppTest.pcap...
 * @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (pcap_trace_command, static) = {
    .path = "pcap tx trace",
    .short_help =
    "pcap tx trace [on|off] [max <nn>] [intfc <interface>|any] [file <name>] [status]",
    .function = pcap_trace_command_fn,
};
/* *INDENT-ON* */


static clib_error_t *
show_dpdk_buffer (vlib_main_t * vm, unformat_input_t * input,
		  vlib_cli_command_t * cmd)
{
  struct rte_mempool *rmp;
  int i;

  for (i = 0; i < vec_len (dpdk_main.pktmbuf_pools); i++)
    {
      rmp = dpdk_main.pktmbuf_pools[i];
      if (rmp)
	{
	  unsigned count = rte_mempool_avail_count (rmp);
	  unsigned free_count = rte_mempool_in_use_count (rmp);

	  vlib_cli_output (vm,
			   "name=\"%s\"  available = %7d allocated = %7d total = %7d\n",
			   rmp->name, (u32) count, (u32) free_count,
			   (u32) (count + free_count));
	}
      else
	{
	  vlib_cli_output (vm, "rte_mempool is NULL (!)\n");
	}
    }
  return 0;
}

/*?
 * This command displays statistics of each DPDK mempool.
 *
 * @cliexpar
 * Example of how to display DPDK buffer data:
 * @cliexstart{show dpdk buffer}
 * name="mbuf_pool_socket0"  available =   15104 allocated =    1280 total =   16384
 * @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_show_dpdk_bufferr,static) = {
    .path = "show dpdk buffer",
    .short_help = "show dpdk buffer",
    .function = show_dpdk_buffer,
    .is_mp_safe = 1,
};
/* *INDENT-ON* */

static clib_error_t *
test_dpdk_buffer (vlib_main_t * vm, unformat_input_t * input,
		  vlib_cli_command_t * cmd)
{
  static u32 *allocated_buffers;
  u32 n_alloc = 0;
  u32 n_free = 0;
  u32 first, actual_alloc;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "allocate %d", &n_alloc))
	;
      else if (unformat (input, "free %d", &n_free))
	;
      else
	break;
    }

  if (n_free)
    {
      if (vec_len (allocated_buffers) < n_free)
	return clib_error_return (0, "Can't free %d, only %d allocated",
				  n_free, vec_len (allocated_buffers));

      first = vec_len (allocated_buffers) - n_free;
      vlib_buffer_free (vm, allocated_buffers + first, n_free);
      _vec_len (allocated_buffers) = first;
    }
  if (n_alloc)
    {
      first = vec_len (allocated_buffers);
      vec_validate (allocated_buffers,
		    vec_len (allocated_buffers) + n_alloc - 1);

      actual_alloc = vlib_buffer_alloc (vm, allocated_buffers + first,
					n_alloc);
      _vec_len (allocated_buffers) = first + actual_alloc;

      if (actual_alloc < n_alloc)
	vlib_cli_output (vm, "WARNING: only allocated %d buffers",
			 actual_alloc);
    }

  vlib_cli_output (vm, "Currently %d buffers allocated",
		   vec_len (allocated_buffers));

  if (allocated_buffers && vec_len (allocated_buffers) == 0)
    vec_free (allocated_buffers);

  return 0;
}

/*?
 * This command tests the allocation and freeing of DPDK buffers.
 * If both '<em>allocate</em>' and '<em>free</em>' are entered on the
 * same command, the '<em>free</em>' is executed first. If no
 * parameters are provided, this command display how many DPDK buffers
 * the test command has allocated.
 *
 * @cliexpar
 * @parblock
 *
 * Example of how to display how many DPDK buffer test command has allcoated:
 * @cliexstart{test dpdk buffer}
 * Currently 0 buffers allocated
 * @cliexend
 *
 * Example of how to allocate DPDK buffers using the test command:
 * @cliexstart{test dpdk buffer allocate 10}
 * Currently 10 buffers allocated
 * @cliexend
 *
 * Example of how to free DPDK buffers allocated by the test command:
 * @cliexstart{test dpdk buffer free 10}
 * Currently 0 buffers allocated
 * @cliexend
 * @endparblock
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_test_dpdk_buffer,static) = {
    .path = "test dpdk buffer",
    .short_help = "test dpdk buffer [allocate <nn>] [free <nn>]",
    .function = test_dpdk_buffer,
    .is_mp_safe = 1,
};
/* *INDENT-ON* */

static clib_error_t *
set_dpdk_if_desc (vlib_main_t * vm, unformat_input_t * input,
		  vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  dpdk_main_t *dm = &dpdk_main;
  vnet_hw_interface_t *hw;
  dpdk_device_t *xd;
  u32 hw_if_index = (u32) ~ 0;
  u32 nb_rx_desc = (u32) ~ 0;
  u32 nb_tx_desc = (u32) ~ 0;
  clib_error_t *error = NULL;

  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;
      else if (unformat (line_input, "tx %d", &nb_tx_desc))
	;
      else if (unformat (line_input, "rx %d", &nb_rx_desc))
	;
      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (hw_if_index == (u32) ~ 0)
    {
      error = clib_error_return (0, "please specify valid interface name");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  if ((xd->flags & DPDK_DEVICE_FLAG_PMD) == 0)
    {
      error =
	clib_error_return (0,
			   "number of descriptors can be set only for "
			   "physical devices");
      goto done;
    }

  if ((nb_rx_desc == (u32) ~ 0 || nb_rx_desc == xd->nb_rx_desc) &&
      (nb_tx_desc == (u32) ~ 0 || nb_tx_desc == xd->nb_tx_desc))
    {
      error = clib_error_return (0, "nothing changed");
      goto done;
    }

  if (nb_rx_desc != (u32) ~ 0)
    xd->nb_rx_desc = nb_rx_desc;

  if (nb_tx_desc != (u32) ~ 0)
    xd->nb_tx_desc = nb_tx_desc;

  dpdk_device_setup (xd);

  if (vec_len (xd->errors))
    return clib_error_return (0, "%U", format_dpdk_device_errors, xd);

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command sets the number of DPDK '<em>rx</em>' and
 * '<em>tx</em>' descriptors for the given physical interface. Use
 * the command '<em>show hardware-interface</em>' to display the
 * current descriptor allocation.
 *
 * @cliexpar
 * Example of how to set the DPDK interface descriptors:
 * @cliexcmd{set dpdk interface descriptors GigabitEthernet0/8/0 rx 512 tx 512}
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_set_dpdk_if_desc,static) = {
    .path = "set dpdk interface descriptors",
    .short_help = "set dpdk interface descriptors <interface> [rx <nn>] [tx <nn>]",
    .function = set_dpdk_if_desc,
};
/* *INDENT-ON* */

static int
dpdk_device_queue_sort (void *a1, void *a2)
{
  dpdk_device_and_queue_t *dq1 = a1;
  dpdk_device_and_queue_t *dq2 = a2;

  if (dq1->device > dq2->device)
    return 1;
  else if (dq1->device < dq2->device)
    return -1;
  else if (dq1->queue_id > dq2->queue_id)
    return 1;
  else if (dq1->queue_id < dq2->queue_id)
    return -1;
  else
    return 0;
}


static clib_error_t *
show_dpdk_if_hqos_placement (vlib_main_t * vm, unformat_input_t * input,
			     vlib_cli_command_t * cmd)
{
  vlib_thread_main_t *tm = vlib_get_thread_main ();
  dpdk_main_t *dm = &dpdk_main;
  dpdk_device_and_queue_t *dq;
  int cpu;

  if (tm->n_vlib_mains == 1)
    vlib_cli_output (vm, "All interfaces are handled by main thread");

  for (cpu = 0; cpu < vec_len (dm->devices_by_hqos_cpu); cpu++)
    {
      if (cpu >= dm->hqos_cpu_first_index &&
	  cpu < (dm->hqos_cpu_first_index + dm->hqos_cpu_count))
	vlib_cli_output (vm, "Thread %u (%s at lcore %u):", cpu,
			 vlib_worker_threads[cpu].name,
			 vlib_worker_threads[cpu].lcore_id);

      vec_foreach (dq, dm->devices_by_hqos_cpu[cpu])
      {
	u32 hw_if_index = dm->devices[dq->device].hw_if_index;
	vnet_hw_interface_t *hi =
	  vnet_get_hw_interface (dm->vnet_main, hw_if_index);
	vlib_cli_output (vm, "  %v queue %u", hi->name, dq->queue_id);
      }
    }
  return 0;
}

/*?
 * This command is used to display the thread and core each
 * DPDK output interface and HQoS queue is assigned too.
 *
 * @cliexpar
 * Example of how to display the DPDK output interface and HQoS queue placement:
 * @cliexstart{show dpdk interface hqos placement}
 * Thread 1 (vpp_hqos-threads_0 at lcore 3):
 *   GigabitEthernet0/8/0 queue 0
 * Thread 2 (vpp_hqos-threads_1 at lcore 4):
 *   GigabitEthernet0/9/0 queue 0
 * @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_show_dpdk_if_hqos_placement, static) = {
  .path = "show dpdk interface hqos placement",
  .short_help = "show dpdk interface hqos placement",
  .function = show_dpdk_if_hqos_placement,
};
/* *INDENT-ON* */

static clib_error_t *
set_dpdk_if_hqos_placement (vlib_main_t * vm, unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  dpdk_main_t *dm = &dpdk_main;
  dpdk_device_and_queue_t *dq;
  vnet_hw_interface_t *hw;
  dpdk_device_t *xd;
  u32 hw_if_index = (u32) ~ 0;
  u32 cpu = (u32) ~ 0;
  int i;
  clib_error_t *error = NULL;

  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;
      else if (unformat (line_input, "thread %d", &cpu))
	;
      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (hw_if_index == (u32) ~ 0)
    return clib_error_return (0, "please specify valid interface name");

  if (cpu < dm->hqos_cpu_first_index ||
      cpu >= (dm->hqos_cpu_first_index + dm->hqos_cpu_count))
    {
      error = clib_error_return (0, "please specify valid thread id");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  for (i = 0; i < vec_len (dm->devices_by_hqos_cpu); i++)
    {
      vec_foreach (dq, dm->devices_by_hqos_cpu[i])
      {
	if (hw_if_index == dm->devices[dq->device].hw_if_index)
	  {
	    if (cpu == i)	/* nothing to do */
	      goto done;

	    vec_del1 (dm->devices_by_hqos_cpu[i],
		      dq - dm->devices_by_hqos_cpu[i]);
	    vec_add2 (dm->devices_by_hqos_cpu[cpu], dq, 1);
	    dq->queue_id = 0;
	    dq->device = xd->device_index;

	    vec_sort_with_function (dm->devices_by_hqos_cpu[i],
				    dpdk_device_queue_sort);

	    vec_sort_with_function (dm->devices_by_hqos_cpu[cpu],
				    dpdk_device_queue_sort);

	    goto done;
	  }
      }
    }

  error = clib_error_return (0, "not found");

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to assign a given DPDK output interface and
 * HQoS queue to a different thread. This will not create a thread,
 * so the thread must already exist. Use '<em>/etc/vpp/startup.conf</em>'
 * for the initial thread creation. See @ref qos_doc for more details.
 *
 * @cliexpar
 * Example of how to display the DPDK output interface and HQoS queue placement:
 * @cliexstart{show dpdk interface hqos placement}
 * Thread 1 (vpp_hqos-threads_0 at lcore 3):
 *   GigabitEthernet0/8/0 queue 0
 * Thread 2 (vpp_hqos-threads_1 at lcore 4):
 *   GigabitEthernet0/9/0 queue 0
 * @cliexend
 * Example of how to assign a DPDK output interface and HQoS queue to a thread:
 * @cliexcmd{set dpdk interface hqos placement GigabitEthernet0/8/0 thread 2}
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_set_dpdk_if_hqos_placement, static) = {
  .path = "set dpdk interface hqos placement",
  .short_help = "set dpdk interface hqos placement <interface> thread <n>",
  .function = set_dpdk_if_hqos_placement,
};
/* *INDENT-ON* */

static clib_error_t *
set_dpdk_if_hqos_pipe (vlib_main_t * vm, unformat_input_t * input,
		       vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  dpdk_main_t *dm = &dpdk_main;
  vnet_hw_interface_t *hw;
  dpdk_device_t *xd;
  u32 hw_if_index = (u32) ~ 0;
  u32 subport_id = (u32) ~ 0;
  u32 pipe_id = (u32) ~ 0;
  u32 profile_id = (u32) ~ 0;
  int rv;
  clib_error_t *error = NULL;

  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;
      else if (unformat (line_input, "subport %d", &subport_id))
	;
      else if (unformat (line_input, "pipe %d", &pipe_id))
	;
      else if (unformat (line_input, "profile %d", &profile_id))
	;
      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (hw_if_index == (u32) ~ 0)
    {
      error = clib_error_return (0, "please specify valid interface name");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  rv =
    rte_sched_pipe_config (xd->hqos_ht->hqos, subport_id, pipe_id,
			   profile_id);
  if (rv)
    {
      error = clib_error_return (0, "pipe configuration failed");
      goto done;
    }

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to change the profile associate with a HQoS pipe. The
 * '<em><profile_id></em>' is zero based. Use the command
 * '<em>show dpdk interface hqos</em>' to display the content of each profile.
 * See @ref qos_doc for more details.
 *
 * @note
 * Currently there is not an API to create a new HQoS pipe profile. One is
 * created by default in the code (search for '<em>hqos_pipe_params_default</em>'').
 * Additional profiles can be created in code and code recompiled. Then use this
 * command to assign it.
 *
 * @cliexpar
 * Example of how to assign a new profile to a HQoS pipe:
 * @cliexcmd{set dpdk interface hqos pipe GigabitEthernet0/8/0 subport 0 pipe 2 profile 1}
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_set_dpdk_if_hqos_pipe, static) =
{
  .path = "set dpdk interface hqos pipe",
  .short_help = "set dpdk interface hqos pipe <interface> subport <subport_id> pipe <pipe_id> "
                  "profile <profile_id>",
  .function = set_dpdk_if_hqos_pipe,
};
/* *INDENT-ON* */

static clib_error_t *
set_dpdk_if_hqos_subport (vlib_main_t * vm, unformat_input_t * input,
			  vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  dpdk_main_t *dm = &dpdk_main;
  dpdk_device_t *xd = NULL;
  u32 hw_if_index = (u32) ~ 0;
  u32 subport_id = (u32) ~ 0;
  struct rte_sched_subport_params p;
  int rv;
  clib_error_t *error = NULL;
  u32 tb_rate = (u32) ~ 0;
  u32 tb_size = (u32) ~ 0;
  u32 tc_rate[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE] =
    { (u32) ~ 0, (u32) ~ 0, (u32) ~ 0, (u32) ~ 0 };
  u32 tc_period = (u32) ~ 0;
  dpdk_device_config_t *devconf = NULL;

  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;
      else if (unformat (line_input, "subport %d", &subport_id))
	;
      else if (unformat (line_input, "rate %d", &tb_rate))
	;
      else if (unformat (line_input, "bktsize %d", &tb_size))
	;
      else if (unformat (line_input, "tc0 %d", &tc_rate[0]))
	;
      else if (unformat (line_input, "tc1 %d", &tc_rate[1]))
	;
      else if (unformat (line_input, "tc2 %d", &tc_rate[2]))
	;
      else if (unformat (line_input, "tc3 %d", &tc_rate[3]))
	;
      else if (unformat (line_input, "period %d", &tc_period))
	;
      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  error = get_hqos (hw_if_index, subport_id, &xd, &devconf);

  if (error == NULL)
    {
      /* Copy the current values over to local structure. */
      memcpy (&p, &devconf->hqos.subport[subport_id], sizeof (p));

      /* Update local structure with input values. */
      if (tb_rate != (u32) ~ 0)
	{
	  p.tb_rate = tb_rate;
	  p.tc_rate[0] = tb_rate;
	  p.tc_rate[1] = tb_rate;
	  p.tc_rate[2] = tb_rate;
	  p.tc_rate[3] = tb_rate;
	}
      if (tb_size != (u32) ~ 0)
	{
	  p.tb_size = tb_size;
	}
      if (tc_rate[0] != (u32) ~ 0)
	{
	  p.tc_rate[0] = tc_rate[0];
	}
      if (tc_rate[1] != (u32) ~ 0)
	{
	  p.tc_rate[1] = tc_rate[1];
	}
      if (tc_rate[2] != (u32) ~ 0)
	{
	  p.tc_rate[2] = tc_rate[2];
	}
      if (tc_rate[3] != (u32) ~ 0)
	{
	  p.tc_rate[3] = tc_rate[3];
	}
      if (tc_period != (u32) ~ 0)
	{
	  p.tc_period = tc_period;
	}

      /* Apply changes. */
      rv = rte_sched_subport_config (xd->hqos_ht->hqos, subport_id, &p);
      if (rv)
	{
	  error = clib_error_return (0, "subport configuration failed");
	  goto done;
	}
      else
	{
	  /* Successfully applied, so save of the input values. */
	  memcpy (&devconf->hqos.subport[subport_id], &p, sizeof (p));
	}
    }

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to set the subport level parameters such as token
 * bucket rate (bytes per seconds), token bucket size (bytes), traffic class
 * rates (bytes per seconds) and token update period (Milliseconds).
 *
 * By default, the '<em>rate</em>' is set to 1250000000 bytes/second (10GbE
 * rate) and each of the four traffic classes is set to 100% of the port rate.
 * If the '<em>rate</em>' is updated by this command, all four traffic classes
 * are assigned the same value. Each of the four traffic classes can be updated
 * individually.
 *
 * @cliexpar
 * Example of how modify the subport attributes for a 1GbE link:
 * @cliexcmd{set dpdk interface hqos subport GigabitEthernet0/8/0 subport 0 rate 125000000}
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_set_dpdk_if_hqos_subport, static) = {
  .path = "set dpdk interface hqos subport",
  .short_help = "set dpdk interface hqos subport <interface> subport <subport_id> "
                 "[rate <n>] [bktsize <n>] [tc0 <n>] [tc1 <n>] [tc2 <n>] [tc3 <n>] "
                 "[period <n>]",
  .function = set_dpdk_if_hqos_subport,
};
/* *INDENT-ON* */

static clib_error_t *
set_dpdk_if_hqos_tctbl (vlib_main_t * vm, unformat_input_t * input,
			vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  vlib_thread_main_t *tm = vlib_get_thread_main ();
  dpdk_main_t *dm = &dpdk_main;
  vnet_hw_interface_t *hw;
  dpdk_device_t *xd;
  u32 hw_if_index = (u32) ~ 0;
  u32 tc = (u32) ~ 0;
  u32 queue = (u32) ~ 0;
  u32 entry = (u32) ~ 0;
  u32 val, i;
  clib_error_t *error = NULL;

  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;
      else if (unformat (line_input, "entry %d", &entry))
	;
      else if (unformat (line_input, "tc %d", &tc))
	;
      else if (unformat (line_input, "queue %d", &queue))
	;
      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (hw_if_index == (u32) ~ 0)
    {
      error = clib_error_return (0, "please specify valid interface name");
      goto done;
    }
  if (entry >= 64)
    {
      error = clib_error_return (0, "invalid entry");
      goto done;
    }
  if (tc >= RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE)
    {
      error = clib_error_return (0, "invalid traffic class");
      goto done;
    }
  if (queue >= RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS)
    {
      error = clib_error_return (0, "invalid traffic class queue");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  /* Detect the set of worker threads */
  uword *p = hash_get_mem (tm->thread_registrations_by_name, "workers");
  /* Should never happen, shut up Coverity warning */
  if (p == 0)
    {
      error = clib_error_return (0, "no worker registrations?");
      goto done;
    }

  vlib_thread_registration_t *tr = (vlib_thread_registration_t *) p[0];
  int worker_thread_first = tr->first_index;
  int worker_thread_count = tr->count;

  val = tc * RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS + queue;
  for (i = 0; i < worker_thread_count; i++)
    xd->hqos_wt[worker_thread_first + i].hqos_tc_table[entry] = val;

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to set the traffic class translation table. The
 * traffic class translation table is used to map 64 values (0-63) to one of
 * four traffic class and one of four HQoS input queue. Use the '<em>show
 * dpdk interface hqos</em>' command to display the traffic class translation
 * table. See @ref qos_doc for more details.
 *
 * This command has the following parameters:
 *
 * - <b><interface></b> - Used to specify the output interface.
 *
 * - <b>entry <map_val></b> - Mapped value (0-63) to assign traffic class and queue to.
 *
 * - <b>tc <tc_id></b> - Traffic class (0-3) to be used by the provided mapped value.
 *
 * - <b>queue <queue_id></b> - HQoS input queue (0-3) to be used by the provided mapped value.
 *
 * @cliexpar
 * Example of how modify the traffic class translation table:
 * @cliexcmd{set dpdk interface hqos tctbl GigabitEthernet0/8/0 entry 16 tc 2 queue 2}
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_set_dpdk_if_hqos_tctbl, static) = {
  .path = "set dpdk interface hqos tctbl",
  .short_help = "set dpdk interface hqos tctbl <interface> entry <map_val> tc <tc_id> queue <queue_id>",
  .function = set_dpdk_if_hqos_tctbl,
};
/* *INDENT-ON* */

static clib_error_t *
set_dpdk_if_hqos_pktfield (vlib_main_t * vm, unformat_input_t * input,
			   vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  vlib_thread_main_t *tm = vlib_get_thread_main ();
  dpdk_main_t *dm = &dpdk_main;
  clib_error_t *error = NULL;

  /* Device specific data */
  struct rte_eth_dev_info dev_info;
  dpdk_device_config_t *devconf = 0;
  vnet_hw_interface_t *hw;
  dpdk_device_t *xd;
  u32 hw_if_index = (u32) ~ 0;

  /* Detect the set of worker threads */
  uword *p = hash_get_mem (tm->thread_registrations_by_name, "workers");
  /* Should never happen, shut up Coverity warning */
  if (p == 0)
    return clib_error_return (0, "no worker registrations?");

  vlib_thread_registration_t *tr = (vlib_thread_registration_t *) p[0];
  int worker_thread_first = tr->first_index;
  int worker_thread_count = tr->count;

  /* Packet field configuration */
  u64 mask = (u64) ~ 0;
  u32 id = (u32) ~ 0;
  u32 offset = (u32) ~ 0;

  /* HQoS params */
  u32 n_subports_per_port, n_pipes_per_subport, tctbl_size;

  u32 i;

  /* Parse input arguments */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;
      else if (unformat (line_input, "id subport"))
	id = 0;
      else if (unformat (line_input, "id pipe"))
	id = 1;
      else if (unformat (line_input, "id tc"))
	id = 2;
      else if (unformat (line_input, "id %d", &id))
	;
      else if (unformat (line_input, "offset %d", &offset))
	;
      else if (unformat (line_input, "mask %llx", &mask))
	;
      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  /* Get interface */
  if (hw_if_index == (u32) ~ 0)
    {
      error = clib_error_return (0, "please specify valid interface name");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  rte_eth_dev_info_get (xd->device_index, &dev_info);
  if (dev_info.pci_dev)
    {				/* bonded interface has no pci info */
      vlib_pci_addr_t pci_addr;

      pci_addr.domain = dev_info.pci_dev->addr.domain;
      pci_addr.bus = dev_info.pci_dev->addr.bus;
      pci_addr.slot = dev_info.pci_dev->addr.devid;
      pci_addr.function = dev_info.pci_dev->addr.function;

      p =
	hash_get (dm->conf->device_config_index_by_pci_addr, pci_addr.as_u32);
    }

  if (p)
    devconf = pool_elt_at_index (dm->conf->dev_confs, p[0]);
  else
    devconf = &dm->conf->default_devconf;

  if (devconf->hqos_enabled == 0)
    {
      vlib_cli_output (vm, "HQoS disabled for this interface");
      goto done;
    }

  n_subports_per_port = devconf->hqos.port.n_subports_per_port;
  n_pipes_per_subport = devconf->hqos.port.n_pipes_per_subport;
  tctbl_size = RTE_DIM (devconf->hqos.tc_table);

  /* Validate packet field configuration: id, offset and mask */
  if (id >= 3)
    {
      error = clib_error_return (0, "invalid packet field id");
      goto done;
    }

  switch (id)
    {
    case 0:
      if (dpdk_hqos_validate_mask (mask, n_subports_per_port) != 0)
	{
	  error = clib_error_return (0, "invalid subport ID mask "
				     "(n_subports_per_port = %u)",
				     n_subports_per_port);
	  goto done;
	}
      break;
    case 1:
      if (dpdk_hqos_validate_mask (mask, n_pipes_per_subport) != 0)
	{
	  error = clib_error_return (0, "invalid pipe ID mask "
				     "(n_pipes_per_subport = %u)",
				     n_pipes_per_subport);
	  goto done;
	}
      break;
    case 2:
    default:
      if (dpdk_hqos_validate_mask (mask, tctbl_size) != 0)
	{
	  error = clib_error_return (0, "invalid TC table index mask "
				     "(TC table size = %u)", tctbl_size);
	  goto done;
	}
    }

  /* Propagate packet field configuration to all workers */
  for (i = 0; i < worker_thread_count; i++)
    switch (id)
      {
      case 0:
	xd->hqos_wt[worker_thread_first + i].hqos_field0_slabpos = offset;
	xd->hqos_wt[worker_thread_first + i].hqos_field0_slabmask = mask;
	xd->hqos_wt[worker_thread_first + i].hqos_field0_slabshr =
	  __builtin_ctzll (mask);
	break;
      case 1:
	xd->hqos_wt[worker_thread_first + i].hqos_field1_slabpos = offset;
	xd->hqos_wt[worker_thread_first + i].hqos_field1_slabmask = mask;
	xd->hqos_wt[worker_thread_first + i].hqos_field1_slabshr =
	  __builtin_ctzll (mask);
	break;
      case 2:
      default:
	xd->hqos_wt[worker_thread_first + i].hqos_field2_slabpos = offset;
	xd->hqos_wt[worker_thread_first + i].hqos_field2_slabmask = mask;
	xd->hqos_wt[worker_thread_first + i].hqos_field2_slabshr =
	  __builtin_ctzll (mask);
      }

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to set the packet fields required for classifiying the
 * incoming packet. As a result of classification process, packet field
 * information will be mapped to 5 tuples (subport, pipe, traffic class, pipe,
 * color) and stored in packet mbuf.
 *
 * This command has the following parameters:
 *
 * - <b><interface></b> - Used to specify the output interface.
 *
 * - <b>id subport|pipe|tc</b> - Classification occurs across three fields.
 * This parameter indicates which of the three masks are being configured. Legacy
 * code used 0-2 to represent these three fields, so 0-2 is still accepted.
 *   - <b>subport|0</b> - Currently only one subport is supported, so only
 * an empty mask is supported for the subport classification.
 *   - <b>pipe|1</b> - Currently, 4096 pipes per subport are supported, so a
 * 12-bit mask should be configure to map to the 0-4095 pipes.
 *   - <b>tc|2</b> - The translation table (see '<em>set dpdk interface hqos
 * tctbl</em>' command) maps each value (0-63) into one of the 4 traffic classes
 * per pipe. A 6-bit mask should be configure to map this field to a traffic class.
 *
 * - <b>offset <n></b> - Offset in the packet to apply the 64-bit mask for classification.
 * The offset should be on an 8-byte boundary (0,8,16,24..).
 *
 * - <b>mask <hex-mask></b> - 64-bit mask to apply to packet at the given '<em>offset</em>'.
 * Bits must be contiguous and should not include '<em>0x</em>'.
 *
 * The default values for the '<em>pktfield</em>' assumes Ethernet/IPv4/UDP packets with
 * no VLAN. Adjust based on expected packet format and desired classification field.
 * - '<em>subport</em>' is always empty (offset 0 mask 0000000000000000)
 * - By default, '<em>pipe</em>' maps to the UDP payload bits 12 .. 23 (offset 40
 * mask 0000000fff000000)
 * - By default, '<em>tc</em>' maps to the DSCP field in IP header (offset 48 mask
 * 00000000000000fc)
 *
 * @cliexpar
 * Example of how modify the '<em>pipe</em>' classification filter to match VLAN:
 * @cliexcmd{set dpdk interface hqos pktfield GigabitEthernet0/8/0 id pipe offset 8 mask 0000000000000FFF}
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_set_dpdk_if_hqos_pktfield, static) = {
  .path = "set dpdk interface hqos pktfield",
  .short_help = "set dpdk interface hqos pktfield <interface> id subport|pipe|tc offset <n> "
                 "mask <hex-mask>",
  .function = set_dpdk_if_hqos_pktfield,
};
/* *INDENT-ON* */

static clib_error_t *
show_dpdk_if_hqos (vlib_main_t * vm, unformat_input_t * input,
		   vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  vlib_thread_main_t *tm = vlib_get_thread_main ();
  dpdk_main_t *dm = &dpdk_main;
  vnet_hw_interface_t *hw;
  dpdk_device_t *xd;
  dpdk_device_config_hqos_t *cfg;
  dpdk_device_hqos_per_hqos_thread_t *ht;
  dpdk_device_hqos_per_worker_thread_t *wk;
  u32 *tctbl;
  u32 hw_if_index = (u32) ~ 0;
  u32 profile_id, subport_id, i;
  struct rte_eth_dev_info dev_info;
  dpdk_device_config_t *devconf = 0;
  vlib_thread_registration_t *tr;
  uword *p = 0;
  clib_error_t *error = NULL;

  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;
      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (hw_if_index == (u32) ~ 0)
    {
      error = clib_error_return (0, "please specify interface name!!");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  rte_eth_dev_info_get (xd->device_index, &dev_info);
  if (dev_info.pci_dev)
    {				/* bonded interface has no pci info */
      vlib_pci_addr_t pci_addr;

      pci_addr.domain = dev_info.pci_dev->addr.domain;
      pci_addr.bus = dev_info.pci_dev->addr.bus;
      pci_addr.slot = dev_info.pci_dev->addr.devid;
      pci_addr.function = dev_info.pci_dev->addr.function;

      p =
	hash_get (dm->conf->device_config_index_by_pci_addr, pci_addr.as_u32);
    }

  if (p)
    devconf = pool_elt_at_index (dm->conf->dev_confs, p[0]);
  else
    devconf = &dm->conf->default_devconf;

  if (devconf->hqos_enabled == 0)
    {
      vlib_cli_output (vm, "HQoS disabled for this interface");
      goto done;
    }

  /* Detect the set of worker threads */
  p = hash_get_mem (tm->thread_registrations_by_name, "workers");

  /* Should never happen, shut up Coverity warning */
  if (p == 0)
    {
      error = clib_error_return (0, "no worker registrations?");
      goto done;
    }

  tr = (vlib_thread_registration_t *) p[0];

  cfg = &devconf->hqos;
  ht = xd->hqos_ht;
  wk = &xd->hqos_wt[tr->first_index];
  tctbl = wk->hqos_tc_table;

  vlib_cli_output (vm, " Thread:");
  vlib_cli_output (vm, "   Input SWQ size = %u packets", cfg->swq_size);
  vlib_cli_output (vm, "   Enqueue burst size = %u packets",
		   ht->hqos_burst_enq);
  vlib_cli_output (vm, "   Dequeue burst size = %u packets",
		   ht->hqos_burst_deq);

  vlib_cli_output (vm,
		   "   Packet field 0: slab position = %4u, slab bitmask = 0x%016llx   (subport)",
		   wk->hqos_field0_slabpos, wk->hqos_field0_slabmask);
  vlib_cli_output (vm,
		   "   Packet field 1: slab position = %4u, slab bitmask = 0x%016llx   (pipe)",
		   wk->hqos_field1_slabpos, wk->hqos_field1_slabmask);
  vlib_cli_output (vm,
		   "   Packet field 2: slab position = %4u, slab bitmask = 0x%016llx   (tc)",
		   wk->hqos_field2_slabpos, wk->hqos_field2_slabmask);
  vlib_cli_output (vm,
		   "   Packet field 2  tc translation table: ([Mapped Value Range]: tc/queue tc/queue ...)");
  vlib_cli_output (vm,
		   "     [ 0 .. 15]: "
		   "%u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u",
		   tctbl[0] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[0] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[1] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[1] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[2] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[2] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[3] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[3] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[4] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[4] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[5] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[5] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[6] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[6] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[7] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[7] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[8] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[8] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[9] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[9] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[10] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[10] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[11] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[11] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[12] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[12] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[13] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[13] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[14] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[14] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[15] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[15] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS);
  vlib_cli_output (vm,
		   "     [16 .. 31]: "
		   "%u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u",
		   tctbl[16] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[16] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[17] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[17] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[18] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[18] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[19] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[19] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[20] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[20] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[21] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[21] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[22] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[22] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[23] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[23] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[24] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[24] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[25] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[25] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[26] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[26] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[27] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[27] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[28] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[28] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[29] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[29] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[30] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[30] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[31] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[31] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS);
  vlib_cli_output (vm,
		   "     [32 .. 47]: "
		   "%u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u",
		   tctbl[32] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[32] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[33] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[33] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[34] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[34] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[35] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[35] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[36] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[36] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[37] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[37] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[38] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[38] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[39] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[39] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[40] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[40] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[41] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[41] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[42] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[42] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[43] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[43] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[44] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[44] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[45] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[45] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[46] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[46] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[47] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[47] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS);
  vlib_cli_output (vm,
		   "     [48 .. 63]: "
		   "%u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u %u/%u",
		   tctbl[48] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[48] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[49] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[49] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[50] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[50] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[51] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[51] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[52] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[52] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[53] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[53] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[54] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[54] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[55] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[55] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[56] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[56] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[57] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[57] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[58] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[58] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[59] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[59] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[60] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[60] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[61] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[61] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[62] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[62] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[63] / RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS,
		   tctbl[63] % RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS);
  vlib_cli_output (vm, " Port:");
  vlib_cli_output (vm, "   Rate = %u bytes/second", cfg->port.rate);
  vlib_cli_output (vm, "   MTU = %u bytes", cfg->port.mtu);
  vlib_cli_output (vm, "   Frame overhead = %u bytes",
		   cfg->port.frame_overhead);
  vlib_cli_output (vm, "   Number of subports = %u",
		   cfg->port.n_subports_per_port);
  vlib_cli_output (vm, "   Number of pipes per subport = %u",
		   cfg->port.n_pipes_per_subport);
  vlib_cli_output (vm,
		   "   Packet queue size: TC0 = %u, TC1 = %u, TC2 = %u, TC3 = %u packets",
		   cfg->port.qsize[0], cfg->port.qsize[1], cfg->port.qsize[2],
		   cfg->port.qsize[3]);
  vlib_cli_output (vm, "   Number of pipe profiles = %u",
		   cfg->port.n_pipe_profiles);

  for (subport_id = 0; subport_id < vec_len (cfg->subport); subport_id++)
    {
      vlib_cli_output (vm, " Subport %u:", subport_id);
      vlib_cli_output (vm, "   Rate = %u bytes/second",
		       cfg->subport[subport_id].tb_rate);
      vlib_cli_output (vm, "   Token bucket size = %u bytes",
		       cfg->subport[subport_id].tb_size);
      vlib_cli_output (vm,
		       "   Traffic class rate: TC0 = %u, TC1 = %u, TC2 = %u, TC3 = %u bytes/second",
		       cfg->subport[subport_id].tc_rate[0],
		       cfg->subport[subport_id].tc_rate[1],
		       cfg->subport[subport_id].tc_rate[2],
		       cfg->subport[subport_id].tc_rate[3]);
      vlib_cli_output (vm, "   TC period = %u milliseconds",
		       cfg->subport[subport_id].tc_period);
    }

  for (profile_id = 0; profile_id < vec_len (cfg->pipe); profile_id++)
    {
      vlib_cli_output (vm, " Pipe profile %u:", profile_id);
      vlib_cli_output (vm, "   Rate = %u bytes/second",
		       cfg->pipe[profile_id].tb_rate);
      vlib_cli_output (vm, "   Token bucket size = %u bytes",
		       cfg->pipe[profile_id].tb_size);
      vlib_cli_output (vm,
		       "   Traffic class rate: TC0 = %u, TC1 = %u, TC2 = %u, TC3 = %u bytes/second",
		       cfg->pipe[profile_id].tc_rate[0],
		       cfg->pipe[profile_id].tc_rate[1],
		       cfg->pipe[profile_id].tc_rate[2],
		       cfg->pipe[profile_id].tc_rate[3]);
      vlib_cli_output (vm, "   TC period = %u milliseconds",
		       cfg->pipe[profile_id].tc_period);
#ifdef RTE_SCHED_SUBPORT_TC_OV
      vlib_cli_output (vm, "   TC3 oversubscription_weight = %u",
		       cfg->pipe[profile_id].tc_ov_weight);
#endif

      for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
	{
	  vlib_cli_output (vm,
			   "   TC%u WRR weights: Q0 = %u, Q1 = %u, Q2 = %u, Q3 = %u",
			   i, cfg->pipe[profile_id].wrr_weights[i * 4],
			   cfg->pipe[profile_id].wrr_weights[i * 4 + 1],
			   cfg->pipe[profile_id].wrr_weights[i * 4 + 2],
			   cfg->pipe[profile_id].wrr_weights[i * 4 + 3]);
	}
    }

#ifdef RTE_SCHED_RED
  vlib_cli_output (vm, " Weighted Random Early Detection (WRED):");
  for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
    {
      vlib_cli_output (vm, "   TC%u min: G = %u, Y = %u, R = %u", i,
		       cfg->port.red_params[i][e_RTE_METER_GREEN].min_th,
		       cfg->port.red_params[i][e_RTE_METER_YELLOW].min_th,
		       cfg->port.red_params[i][e_RTE_METER_RED].min_th);

      vlib_cli_output (vm, "   TC%u max: G = %u, Y = %u, R = %u", i,
		       cfg->port.red_params[i][e_RTE_METER_GREEN].max_th,
		       cfg->port.red_params[i][e_RTE_METER_YELLOW].max_th,
		       cfg->port.red_params[i][e_RTE_METER_RED].max_th);

      vlib_cli_output (vm,
		       "   TC%u inverted probability: G = %u, Y = %u, R = %u",
		       i, cfg->port.red_params[i][e_RTE_METER_GREEN].maxp_inv,
		       cfg->port.red_params[i][e_RTE_METER_YELLOW].maxp_inv,
		       cfg->port.red_params[i][e_RTE_METER_RED].maxp_inv);

      vlib_cli_output (vm, "   TC%u weight: R = %u, Y = %u, R = %u", i,
		       cfg->port.red_params[i][e_RTE_METER_GREEN].wq_log2,
		       cfg->port.red_params[i][e_RTE_METER_YELLOW].wq_log2,
		       cfg->port.red_params[i][e_RTE_METER_RED].wq_log2);
    }
#endif

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to display details of an output interface's HQoS
 * settings.
 *
 * @cliexpar
 * Example of how to display HQoS settings for an interfaces:
 * @cliexstart{show dpdk interface hqos GigabitEthernet0/8/0}
 *  Thread:
 *    Input SWQ size = 4096 packets
 *    Enqueue burst size = 256 packets
 *    Dequeue burst size = 220 packets
 *    Packet field 0: slab position =    0, slab bitmask = 0x0000000000000000   (subport)
 *    Packet field 1: slab position =   40, slab bitmask = 0x0000000fff000000   (pipe)
 *    Packet field 2: slab position =    8, slab bitmask = 0x00000000000000fc   (tc)
 *    Packet field 2  tc translation table: ([Mapped Value Range]: tc/queue tc/queue ...)
 *      [ 0 .. 15]: 0/0 0/1 0/2 0/3 1/0 1/1 1/2 1/3 2/0 2/1 2/2 2/3 3/0 3/1 3/2 3/3
 *      [16 .. 31]: 0/0 0/1 0/2 0/3 1/0 1/1 1/2 1/3 2/0 2/1 2/2 2/3 3/0 3/1 3/2 3/3
 *      [32 .. 47]: 0/0 0/1 0/2 0/3 1/0 1/1 1/2 1/3 2/0 2/1 2/2 2/3 3/0 3/1 3/2 3/3
 *      [48 .. 63]: 0/0 0/1 0/2 0/3 1/0 1/1 1/2 1/3 2/0 2/1 2/2 2/3 3/0 3/1 3/2 3/3
 *  Port:
 *    Rate = 1250000000 bytes/second
 *    MTU = 1514 bytes
 *    Frame overhead = 24 bytes
 *    Number of subports = 1
 *    Number of pipes per subport = 4096
 *    Packet queue size: TC0 = 64, TC1 = 64, TC2 = 64, TC3 = 64 packets
 *    Number of pipe profiles = 2
 *  Subport 0:
 *    Rate = 1250000000 bytes/second
 *    Token bucket size = 1000000 bytes
 *    Traffic class rate: TC0 = 1250000000, TC1 = 1250000000, TC2 = 1250000000, TC3 = 1250000000 bytes/second
 *    TC period = 10 milliseconds
 *  Pipe profile 0:
 *    Rate = 305175 bytes/second
 *    Token bucket size = 1000000 bytes
 *    Traffic class rate: TC0 = 305175, TC1 = 305175, TC2 = 305175, TC3 = 305175 bytes/second
 *    TC period = 40 milliseconds
 *    TC0 WRR weights: Q0 = 1, Q1 = 1, Q2 = 1, Q3 = 1
 *    TC1 WRR weights: Q0 = 1, Q1 = 1, Q2 = 1, Q3 = 1
 *    TC2 WRR weights: Q0 = 1, Q1 = 1, Q2 = 1, Q3 = 1
 *    TC3 WRR weights: Q0 = 1, Q1 = 1, Q2 = 1, Q3 = 1
 * @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_show_dpdk_if_hqos, static) = {
  .path = "show dpdk interface hqos",
  .short_help = "show dpdk interface hqos <interface>",
  .function = show_dpdk_if_hqos,
};

/* *INDENT-ON* */

static clib_error_t *
show_dpdk_hqos_queue_stats (vlib_main_t * vm, unformat_input_t * input,
			    vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = NULL;
#ifdef RTE_SCHED_COLLECT_STATS
  dpdk_main_t *dm = &dpdk_main;
  u32 hw_if_index = (u32) ~ 0;
  u32 subport = (u32) ~ 0;
  u32 pipe = (u32) ~ 0;
  u32 tc = (u32) ~ 0;
  u32 tc_q = (u32) ~ 0;
  vnet_hw_interface_t *hw;
  dpdk_device_t *xd;
  uword *p = 0;
  struct rte_eth_dev_info dev_info;
  dpdk_device_config_t *devconf = 0;
  u32 qindex;
  struct rte_sched_queue_stats stats;
  u16 qlen;

  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_hw_interface, dm->vnet_main,
	   &hw_if_index))
	;

      else if (unformat (line_input, "subport %d", &subport))
	;

      else if (unformat (line_input, "pipe %d", &pipe))
	;

      else if (unformat (line_input, "tc %d", &tc))
	;

      else if (unformat (line_input, "tc_q %d", &tc_q))
	;

      else
	{
	  error = clib_error_return (0, "parse error: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (hw_if_index == (u32) ~ 0)
    {
      error = clib_error_return (0, "please specify interface name!!");
      goto done;
    }

  hw = vnet_get_hw_interface (dm->vnet_main, hw_if_index);
  xd = vec_elt_at_index (dm->devices, hw->dev_instance);

  rte_eth_dev_info_get (xd->device_index, &dev_info);
  if (dev_info.pci_dev)
    {				/* bonded interface has no pci info */
      vlib_pci_addr_t pci_addr;

      pci_addr.domain = dev_info.pci_dev->addr.domain;
      pci_addr.bus = dev_info.pci_dev->addr.bus;
      pci_addr.slot = dev_info.pci_dev->addr.devid;
      pci_addr.function = dev_info.pci_dev->addr.function;

      p =
	hash_get (dm->conf->device_config_index_by_pci_addr, pci_addr.as_u32);
    }

  if (p)
    devconf = pool_elt_at_index (dm->conf->dev_confs, p[0]);
  else
    devconf = &dm->conf->default_devconf;

  if (devconf->hqos_enabled == 0)
    {
      vlib_cli_output (vm, "HQoS disabled for this interface");
      goto done;
    }

  /*
   * Figure out which queue to query.  cf rte_sched_port_qindex.  (Not sure why
   * that method isn't made public by DPDK - how _should_ we get the queue ID?)
   */
  qindex = subport * devconf->hqos.port.n_pipes_per_subport + pipe;
  qindex = qindex * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE + tc;
  qindex = qindex * RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS + tc_q;

  if (rte_sched_queue_read_stats (xd->hqos_ht->hqos, qindex, &stats, &qlen) !=
      0)
    {
      error = clib_error_return (0, "failed to read stats");
      goto done;
    }

  vlib_cli_output (vm, "%=24s%=16s", "Stats Parameter", "Value");
  vlib_cli_output (vm, "%=24s%=16d", "Packets", stats.n_pkts);
  vlib_cli_output (vm, "%=24s%=16d", "Packets dropped", stats.n_pkts_dropped);
#ifdef RTE_SCHED_RED
  vlib_cli_output (vm, "%=24s%=16d", "Packets dropped (RED)",
		   stats.n_pkts_red_dropped);
#endif
  vlib_cli_output (vm, "%=24s%=16d", "Bytes", stats.n_bytes);
  vlib_cli_output (vm, "%=24s%=16d", "Bytes dropped", stats.n_bytes_dropped);

#else

  /* Get a line of input */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  vlib_cli_output (vm, "RTE_SCHED_COLLECT_STATS disabled in DPDK");
  goto done;

#endif

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to display statistics associated with a HQoS traffic class
 * queue.
 *
 * @note
 * Statistic collection by the scheduler is disabled by default in DPDK. In order to
 * turn it on, add the following line to '<em>../vpp/dpdk/Makefile</em>':
 * - <b>$(call set,RTE_SCHED_COLLECT_STATS,y)</b>
 *
 * @cliexpar
 * Example of how to display statistics of HQoS a HQoS traffic class queue:
 * @cliexstart{show dpdk hqos queue GigabitEthernet0/9/0 subport 0 pipe 3181 tc 0 tc_q 0}
 *      Stats Parameter          Value
 *          Packets               140
 *      Packets dropped            0
 *           Bytes               8400
 *       Bytes dropped             0
 * @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cmd_show_dpdk_hqos_queue_stats, static) = {
  .path = "show dpdk hqos queue",
  .short_help = "show dpdk hqos queue <interface> subport <subport_id> pipe <pipe_id> tc <tc_id> tc_q <queue_id>",
  .function = show_dpdk_hqos_queue_stats,
};
/* *INDENT-ON* */

static clib_error_t *
show_dpdk_version_command_fn (vlib_main_t * vm,
			      unformat_input_t * input,
			      vlib_cli_command_t * cmd)
{
#define _(a,b,c) vlib_cli_output (vm, "%-25s " b, a ":", c);
  _("DPDK Version", "%s", rte_version ());
  _("DPDK EAL init args", "%s", dpdk_config_main.eal_init_args_str);
#undef _
  return 0;
}

/*?
 * This command is used to display the current DPDK version and
 * the list of arguments passed to DPDK when started.
 *
 * @cliexpar
 * Example of how to display how many DPDK buffer test command has allcoated:
 * @cliexstart{show dpdk version}
 * DPDK Version:        DPDK 16.11.0
 * DPDK EAL init args:  -c 1 -n 4 --huge-dir /run/vpp/hugepages --file-prefix vpp -w 0000:00:08.0 -w 0000:00:09.0 --master-lcore 0 --socket-mem 256
 * @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (show_vpe_version_command, static) = {
  .path = "show dpdk version",
  .short_help = "show dpdk version",
  .function = show_dpdk_version_command_fn,
};
/* *INDENT-ON* */

clib_error_t *
dpdk_cli_init (vlib_main_t * vm)
{
  return 0;
}

VLIB_INIT_FUNCTION (dpdk_cli_init);

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