get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/98678/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 98678,
    "url": "https://patches.dpdk.org/api/patches/98678/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20210910181841.530280-7-stephen@networkplumber.org/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20210910181841.530280-7-stephen@networkplumber.org>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20210910181841.530280-7-stephen@networkplumber.org",
    "date": "2021-09-10T18:18:36",
    "name": "[v7,06/11] pdump: support pcapng and filtering",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "5ef9f02d031865396dd7b05de17b12433ac732bf",
    "submitter": {
        "id": 27,
        "url": "https://patches.dpdk.org/api/people/27/?format=api",
        "name": "Stephen Hemminger",
        "email": "stephen@networkplumber.org"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20210910181841.530280-7-stephen@networkplumber.org/mbox/",
    "series": [
        {
            "id": 18848,
            "url": "https://patches.dpdk.org/api/series/18848/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=18848",
            "date": "2021-09-10T18:18:30",
            "name": "Packet capture framework enhancements",
            "version": 7,
            "mbox": "https://patches.dpdk.org/series/18848/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/98678/comments/",
    "check": "success",
    "checks": "https://patches.dpdk.org/api/patches/98678/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 68556A0547;\n\tFri, 10 Sep 2021 20:19:27 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id E1A5741155;\n\tFri, 10 Sep 2021 20:18:58 +0200 (CEST)",
            "from mail-pg1-f175.google.com (mail-pg1-f175.google.com\n [209.85.215.175])\n by mails.dpdk.org (Postfix) with ESMTP id 93B974114E\n for <dev@dpdk.org>; Fri, 10 Sep 2021 20:18:53 +0200 (CEST)",
            "by mail-pg1-f175.google.com with SMTP id r2so2558934pgl.10\n for <dev@dpdk.org>; Fri, 10 Sep 2021 11:18:53 -0700 (PDT)",
            "from hermes.local (204-195-33-123.wavecable.com. [204.195.33.123])\n by smtp.gmail.com with ESMTPSA id p13sm5652857pjo.9.2021.09.10.11.18.50\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 10 Sep 2021 11:18:50 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=networkplumber-org.20150623.gappssmtp.com; s=20150623;\n h=from:to:cc:subject:date:message-id:in-reply-to:references\n :mime-version:content-transfer-encoding;\n bh=ILYXBi6oR68ploilVtGOYTguVitUDnZ6veSRxnLdhVo=;\n b=G5xT+2c1t5Fu+RI5mfEanFLJk6GZkCBZg8w/1uMTeXoxgF4QiN9NkerU0S7ItNIio1\n pupuxV+EKyGfKQNe+xb5HX2SyFCxWaIbbXUamkZJXJUgns8uWOsOMuxkvIugjzuxEpXP\n 6c9ePV+NWtDNwD6uikT3s51POmSWFbtDAN/tLpJePfXLjOK3FI8vcMx0NlSJYZcF7O+D\n ePKn3D2Nnh8vQUyQKrnvAEjt+6ioTeSDScewwL5AfWKk6EqQhyo4kh+UEIQgSLcIxeQf\n oFw7AXWjh1OnXodYy7qolei1NSYcaIeTs6eSAdXUQssMTKEy255fEtrnA+yiuDeO5Q2C\n 4QxQ==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20210112;\n h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n :references:mime-version:content-transfer-encoding;\n bh=ILYXBi6oR68ploilVtGOYTguVitUDnZ6veSRxnLdhVo=;\n b=pqPt6Qcpwox3DPP9F0Eb4S0ROT0ZN7eHD8QOM5od5YrQiPwWmAe5T6QyzB2WWkUv91\n SQ70zvW1Tib/e3WlCOFJ8kj3tkGZakcG1pK2hNOa8f1Tc4DWkclHdyUMv5WIURVo6Sf4\n 3bUJ7GQEB7i6C6nUcpwO14eymtX0ELTCqRO/EsSmfAqIaCtAo7S4h6cwEslsVi1s1Jiv\n hQDxp+Sl8KGALi9aFBUAEM6yPfXO/3JTAhWXbzuu0Qnpby14k/pgl6sl5svPQgRSsQEz\n T6wsBJ/Z20XNo5W+mxjdtwU3dm0qq6of2wFwwsSluMaKGQr1joV0LLwmzOxqtcND+kbJ\n /Egg==",
        "X-Gm-Message-State": "AOAM532/EHnVVDB8FmgolZZAqDLu/W79TvmLrOfpdZWM+6W6swqkAzdL\n wWu5g3PfdaG0FaXXopYPLeWoUzUsShxm2A==",
        "X-Google-Smtp-Source": "\n ABdhPJzp0OIPyRj+tBnGhIVqk51i7pxt88ibcndKm3baNM1yvDJI/icg2iXsJPC3/kIlRn5GHVgaLg==",
        "X-Received": "by 2002:a63:3449:: with SMTP id b70mr8340516pga.315.1631297931631;\n Fri, 10 Sep 2021 11:18:51 -0700 (PDT)",
        "From": "Stephen Hemminger <stephen@networkplumber.org>",
        "To": "dev@dpdk.org",
        "Cc": "Stephen Hemminger <stephen@networkplumber.org>",
        "Date": "Fri, 10 Sep 2021 11:18:36 -0700",
        "Message-Id": "<20210910181841.530280-7-stephen@networkplumber.org>",
        "X-Mailer": "git-send-email 2.30.2",
        "In-Reply-To": "<20210910181841.530280-1-stephen@networkplumber.org>",
        "References": "<20210903004732.109023-1-stephen@networkplumber.org>\n <20210910181841.530280-1-stephen@networkplumber.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dpdk-dev] [PATCH v7 06/11] pdump: support pcapng and filtering",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "This enhances the DPDK pdump library to support new\npcapng format and filtering via BPF.\n\nThe internal client/server protocol is changed to support\ntwo versions: the original pdump basic version and a\nnew pcapng version.\n\nThe internal version number (not part of exposed API or ABI)\nis intentionally increased to cause any attempt to try\nmismatched primary/secondary process to fail.\n\nAdd new API to do allow filtering of captured packets with\nDPDK BPF (eBPF) filter program. It keeps statistics\non packets captured, filtered, and missed (because ring was full).\n\nSigned-off-by: Stephen Hemminger <stephen@networkplumber.org>\n---\n lib/meson.build       |   4 +-\n lib/pdump/meson.build |   2 +-\n lib/pdump/rte_pdump.c | 437 ++++++++++++++++++++++++++++++------------\n lib/pdump/rte_pdump.h | 110 ++++++++++-\n lib/pdump/version.map |   8 +\n 5 files changed, 435 insertions(+), 126 deletions(-)",
    "diff": "diff --git a/lib/meson.build b/lib/meson.build\nindex ba88e9eabc58..1da521ea6185 100644\n--- a/lib/meson.build\n+++ b/lib/meson.build\n@@ -26,6 +26,7 @@ libraries = [\n         'timer',   # eventdev depends on this\n         'acl',\n         'bbdev',\n+        'bpf',\n         'bitratestats',\n         'cfgfile',\n         'compressdev',\n@@ -43,7 +44,6 @@ libraries = [\n         'member',\n         'pcapng',\n         'power',\n-        'pdump',\n         'rawdev',\n         'regexdev',\n         'rib',\n@@ -55,10 +55,10 @@ libraries = [\n         'ipsec', # ipsec lib depends on net, crypto and security\n         'fib', #fib lib depends on rib\n         'port', # pkt framework libs which use other libs from above\n+        'pdump', # pdump lib depends on bpf pcapng\n         'table',\n         'pipeline',\n         'flow_classify', # flow_classify lib depends on pkt framework table lib\n-        'bpf',\n         'graph',\n         'node',\n ]\ndiff --git a/lib/pdump/meson.build b/lib/pdump/meson.build\nindex 3a95eabde6a6..51ceb2afdec5 100644\n--- a/lib/pdump/meson.build\n+++ b/lib/pdump/meson.build\n@@ -3,4 +3,4 @@\n \n sources = files('rte_pdump.c')\n headers = files('rte_pdump.h')\n-deps += ['ethdev']\n+deps += ['ethdev', 'bpf', 'pcapng']\ndiff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c\nindex 382217bc1564..f2047ad9f001 100644\n--- a/lib/pdump/rte_pdump.c\n+++ b/lib/pdump/rte_pdump.c\n@@ -7,8 +7,10 @@\n #include <rte_ethdev.h>\n #include <rte_lcore.h>\n #include <rte_log.h>\n+#include <rte_memzone.h>\n #include <rte_errno.h>\n #include <rte_string_fns.h>\n+#include <rte_pcapng.h>\n \n #include \"rte_pdump.h\"\n \n@@ -27,30 +29,26 @@ enum pdump_operation {\n \tENABLE = 2\n };\n \n+/*\n+ * Note: version numbers intentionally start at 3\n+ * in order to catch any application built with older out\n+ * version of DPDK using incompatible client request format.\n+ */\n enum pdump_version {\n-\tV1 = 1\n+\tPDUMP_CLIENT_LEGACY = 3,\n+\tPDUMP_CLIENT_PCAPNG = 4,\n };\n \n struct pdump_request {\n \tuint16_t ver;\n \tuint16_t op;\n-\tuint32_t flags;\n-\tunion pdump_data {\n-\t\tstruct enable_v1 {\n-\t\t\tchar device[RTE_DEV_NAME_MAX_LEN];\n-\t\t\tuint16_t queue;\n-\t\t\tstruct rte_ring *ring;\n-\t\t\tstruct rte_mempool *mp;\n-\t\t\tvoid *filter;\n-\t\t} en_v1;\n-\t\tstruct disable_v1 {\n-\t\t\tchar device[RTE_DEV_NAME_MAX_LEN];\n-\t\t\tuint16_t queue;\n-\t\t\tstruct rte_ring *ring;\n-\t\t\tstruct rte_mempool *mp;\n-\t\t\tvoid *filter;\n-\t\t} dis_v1;\n-\t} data;\n+\tuint16_t flags;\n+\tuint16_t queue;\n+\tstruct rte_ring *ring;\n+\tstruct rte_mempool *mp;\n+\tconst struct rte_bpf_prm *prm;\n+\tuint32_t snaplen;\n+\tchar device[RTE_DEV_NAME_MAX_LEN];\n };\n \n struct pdump_response {\n@@ -63,80 +61,140 @@ static struct pdump_rxtx_cbs {\n \tstruct rte_ring *ring;\n \tstruct rte_mempool *mp;\n \tconst struct rte_eth_rxtx_callback *cb;\n-\tvoid *filter;\n+\tconst struct rte_bpf *filter;\n+\tenum pdump_version ver;\n+\tuint32_t snaplen;\n } rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],\n tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];\n \n-\n-static inline void\n-pdump_copy(struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params)\n+static const char *MZ_RTE_PDUMP_STATS = \"rte_pdump_stats\";\n+\n+/* Shared memory between primary and secondary processes. */\n+static struct {\n+\tstruct rte_pdump_stats rx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];\n+\tstruct rte_pdump_stats tx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];\n+} *pdump_stats;\n+\n+/* Create a clone of mbuf to be placed into ring. */\n+static void\n+pdump_copy(uint16_t port_id, uint16_t queue,\n+\t   enum rte_pcapng_direction direction,\n+\t   struct rte_mbuf **pkts, uint16_t nb_pkts,\n+\t   const struct pdump_rxtx_cbs *cbs,\n+\t   struct rte_pdump_stats *stats)\n {\n \tunsigned int i;\n \tint ring_enq;\n \tuint16_t d_pkts = 0;\n \tstruct rte_mbuf *dup_bufs[nb_pkts];\n-\tstruct pdump_rxtx_cbs *cbs;\n+\tuint64_t ts;\n \tstruct rte_ring *ring;\n \tstruct rte_mempool *mp;\n \tstruct rte_mbuf *p;\n+\tuint64_t rcs[nb_pkts];\n+\n+\tif (cbs->filter &&\n+\t    rte_bpf_exec_burst(cbs->filter, (void **)pkts, rcs, nb_pkts) == 0) {\n+\t\t/* All packets were filtered out */\n+\t\t__atomic_fetch_add(&stats->filtered, nb_pkts,\n+\t\t\t\t   __ATOMIC_RELAXED);\n+\t\treturn;\n+\t}\n \n-\tcbs  = user_params;\n+\tts = rte_get_tsc_cycles();\n \tring = cbs->ring;\n \tmp = cbs->mp;\n \tfor (i = 0; i < nb_pkts; i++) {\n-\t\tp = rte_pktmbuf_copy(pkts[i], mp, 0, UINT32_MAX);\n-\t\tif (p)\n+\t\t/*\n+\t\t * Similar behavior to rte_bpf_eth callback.\n+\t\t * if BPF program returns zero value for a given packet,\n+\t\t * then it will be ignored.\n+\t\t */\n+\t\tif (cbs->filter && rcs[i] == 0) {\n+\t\t\t__atomic_fetch_add(&stats->filtered,\n+\t\t\t\t\t   1, __ATOMIC_RELAXED);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/*\n+\t\t * If using pcapng then want to wrap packets\n+\t\t * otherwise a simple copy.\n+\t\t */\n+\t\tif (cbs->ver == PDUMP_CLIENT_PCAPNG)\n+\t\t\tp = rte_pcapng_copy(port_id, queue,\n+\t\t\t\t\t    pkts[i], mp, cbs->snaplen,\n+\t\t\t\t\t    ts, direction);\n+\t\telse\n+\t\t\tp = rte_pktmbuf_copy(pkts[i], mp, 0, cbs->snaplen);\n+\n+\t\tif (unlikely(p == NULL))\n+\t\t\t__atomic_fetch_add(&stats->nombuf, 1, __ATOMIC_RELAXED);\n+\t\telse\n \t\t\tdup_bufs[d_pkts++] = p;\n \t}\n \n+\t__atomic_fetch_add(&stats->accepted, d_pkts, __ATOMIC_RELAXED);\n+\n \tring_enq = rte_ring_enqueue_burst(ring, (void *)dup_bufs, d_pkts, NULL);\n \tif (unlikely(ring_enq < d_pkts)) {\n \t\tunsigned int drops = d_pkts - ring_enq;\n \n-\t\tPDUMP_LOG(DEBUG,\n-\t\t\t\"only %d of packets enqueued to ring\\n\", ring_enq);\n+\t\t__atomic_fetch_add(&stats->ringfull, drops, __ATOMIC_RELAXED);\n \t\trte_pktmbuf_free_bulk(&dup_bufs[ring_enq], drops);\n \t}\n }\n \n static uint16_t\n-pdump_rx(uint16_t port __rte_unused, uint16_t qidx __rte_unused,\n+pdump_rx(uint16_t port, uint16_t queue,\n \tstruct rte_mbuf **pkts, uint16_t nb_pkts,\n-\tuint16_t max_pkts __rte_unused,\n-\tvoid *user_params)\n+\tuint16_t max_pkts __rte_unused, void *user_params)\n {\n-\tpdump_copy(pkts, nb_pkts, user_params);\n+\tconst struct pdump_rxtx_cbs *cbs = user_params;\n+\tstruct rte_pdump_stats *stats = &pdump_stats->rx[port][queue];\n+\n+\tpdump_copy(port, queue, RTE_PCAPNG_DIRECTION_IN,\n+\t\t   pkts, nb_pkts, cbs, stats);\n \treturn nb_pkts;\n }\n \n static uint16_t\n-pdump_tx(uint16_t port __rte_unused, uint16_t qidx __rte_unused,\n+pdump_tx(uint16_t port, uint16_t queue,\n \t\tstruct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params)\n {\n-\tpdump_copy(pkts, nb_pkts, user_params);\n+\tconst struct pdump_rxtx_cbs *cbs = user_params;\n+\tstruct rte_pdump_stats *stats = &pdump_stats->tx[port][queue];\n+\n+\tpdump_copy(port, queue, RTE_PCAPNG_DIRECTION_OUT,\n+\t\t   pkts, nb_pkts, cbs, stats);\n \treturn nb_pkts;\n }\n \n static int\n-pdump_register_rx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue,\n-\t\t\t\tstruct rte_ring *ring, struct rte_mempool *mp,\n-\t\t\t\tuint16_t operation)\n+pdump_register_rx_callbacks(enum pdump_version ver,\n+\t\t\t    uint16_t end_q, uint16_t port, uint16_t queue,\n+\t\t\t    struct rte_ring *ring, struct rte_mempool *mp,\n+\t\t\t    struct rte_bpf *filter,\n+\t\t\t    uint16_t operation, uint32_t snaplen)\n {\n \tuint16_t qid;\n-\tstruct pdump_rxtx_cbs *cbs = NULL;\n \n \tqid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue;\n \tfor (; qid < end_q; qid++) {\n-\t\tcbs = &rx_cbs[port][qid];\n-\t\tif (cbs && operation == ENABLE) {\n+\t\tstruct pdump_rxtx_cbs *cbs = &rx_cbs[port][qid];\n+\n+\t\tif (operation == ENABLE) {\n \t\t\tif (cbs->cb) {\n \t\t\t\tPDUMP_LOG(ERR,\n \t\t\t\t\t\"rx callback for port=%d queue=%d, already exists\\n\",\n \t\t\t\t\tport, qid);\n \t\t\t\treturn -EEXIST;\n \t\t\t}\n+\t\t\tcbs->ver = ver;\n \t\t\tcbs->ring = ring;\n \t\t\tcbs->mp = mp;\n+\t\t\tcbs->snaplen = snaplen;\n+\t\t\tcbs->filter = filter;\n+\n \t\t\tcbs->cb = rte_eth_add_first_rx_callback(port, qid,\n \t\t\t\t\t\t\t\tpdump_rx, cbs);\n \t\t\tif (cbs->cb == NULL) {\n@@ -145,8 +203,7 @@ pdump_register_rx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue,\n \t\t\t\t\trte_errno);\n \t\t\t\treturn rte_errno;\n \t\t\t}\n-\t\t}\n-\t\tif (cbs && operation == DISABLE) {\n+\t\t} else if (operation == DISABLE) {\n \t\t\tint ret;\n \n \t\t\tif (cbs->cb == NULL) {\n@@ -170,26 +227,32 @@ pdump_register_rx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue,\n }\n \n static int\n-pdump_register_tx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue,\n-\t\t\t\tstruct rte_ring *ring, struct rte_mempool *mp,\n-\t\t\t\tuint16_t operation)\n+pdump_register_tx_callbacks(enum pdump_version ver,\n+\t\t\t    uint16_t end_q, uint16_t port, uint16_t queue,\n+\t\t\t    struct rte_ring *ring, struct rte_mempool *mp,\n+\t\t\t    struct rte_bpf *filter,\n+\t\t\t    uint16_t operation, uint32_t snaplen)\n {\n \n \tuint16_t qid;\n-\tstruct pdump_rxtx_cbs *cbs = NULL;\n \n \tqid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue;\n \tfor (; qid < end_q; qid++) {\n-\t\tcbs = &tx_cbs[port][qid];\n-\t\tif (cbs && operation == ENABLE) {\n+\t\tstruct pdump_rxtx_cbs *cbs = &tx_cbs[port][qid];\n+\n+\t\tif (operation == ENABLE) {\n \t\t\tif (cbs->cb) {\n \t\t\t\tPDUMP_LOG(ERR,\n \t\t\t\t\t\"tx callback for port=%d queue=%d, already exists\\n\",\n \t\t\t\t\tport, qid);\n \t\t\t\treturn -EEXIST;\n \t\t\t}\n+\t\t\tcbs->ver = ver;\n \t\t\tcbs->ring = ring;\n \t\t\tcbs->mp = mp;\n+\t\t\tcbs->snaplen = snaplen;\n+\t\t\tcbs->filter = filter;\n+\n \t\t\tcbs->cb = rte_eth_add_tx_callback(port, qid, pdump_tx,\n \t\t\t\t\t\t\t\tcbs);\n \t\t\tif (cbs->cb == NULL) {\n@@ -198,8 +261,7 @@ pdump_register_tx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue,\n \t\t\t\t\trte_errno);\n \t\t\t\treturn rte_errno;\n \t\t\t}\n-\t\t}\n-\t\tif (cbs && operation == DISABLE) {\n+\t\t} else if (operation == DISABLE) {\n \t\t\tint ret;\n \n \t\t\tif (cbs->cb == NULL) {\n@@ -228,37 +290,47 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)\n \tuint16_t nb_rx_q = 0, nb_tx_q = 0, end_q, queue;\n \tuint16_t port;\n \tint ret = 0;\n+\tstruct rte_bpf *filter = NULL;\n \tuint32_t flags;\n \tuint16_t operation;\n \tstruct rte_ring *ring;\n \tstruct rte_mempool *mp;\n \n-\tflags = p->flags;\n-\toperation = p->op;\n-\tif (operation == ENABLE) {\n-\t\tret = rte_eth_dev_get_port_by_name(p->data.en_v1.device,\n-\t\t\t\t&port);\n-\t\tif (ret < 0) {\n+\tif (!(p->ver == PDUMP_CLIENT_LEGACY ||\n+\t      p->ver == PDUMP_CLIENT_PCAPNG)) {\n+\t\tPDUMP_LOG(ERR,\n+\t\t\t  \"incorrect client version %u\\n\", p->ver);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (p->prm) {\n+\t\tif (p->prm->prog_arg.type != RTE_BPF_ARG_PTR_MBUF) {\n \t\t\tPDUMP_LOG(ERR,\n-\t\t\t\t\"failed to get port id for device id=%s\\n\",\n-\t\t\t\tp->data.en_v1.device);\n+\t\t\t\t  \"invalid BPF program type: %u\\n\",\n+\t\t\t\t  p->prm->prog_arg.type);\n \t\t\treturn -EINVAL;\n \t\t}\n-\t\tqueue = p->data.en_v1.queue;\n-\t\tring = p->data.en_v1.ring;\n-\t\tmp = p->data.en_v1.mp;\n-\t} else {\n-\t\tret = rte_eth_dev_get_port_by_name(p->data.dis_v1.device,\n-\t\t\t\t&port);\n-\t\tif (ret < 0) {\n-\t\t\tPDUMP_LOG(ERR,\n-\t\t\t\t\"failed to get port id for device id=%s\\n\",\n-\t\t\t\tp->data.dis_v1.device);\n-\t\t\treturn -EINVAL;\n+\n+\t\tfilter = rte_bpf_load(p->prm);\n+\t\tif (filter == NULL) {\n+\t\t\tPDUMP_LOG(ERR, \"cannot load BPF filter: %s\\n\",\n+\t\t\t\t  rte_strerror(rte_errno));\n+\t\t\treturn -rte_errno;\n \t\t}\n-\t\tqueue = p->data.dis_v1.queue;\n-\t\tring = p->data.dis_v1.ring;\n-\t\tmp = p->data.dis_v1.mp;\n+\t}\n+\n+\tflags = p->flags;\n+\toperation = p->op;\n+\tqueue = p->queue;\n+\tring = p->ring;\n+\tmp = p->mp;\n+\n+\tret = rte_eth_dev_get_port_by_name(p->device, &port);\n+\tif (ret < 0) {\n+\t\tPDUMP_LOG(ERR,\n+\t\t\t  \"failed to get port id for device id=%s\\n\",\n+\t\t\t  p->device);\n+\t\treturn -EINVAL;\n \t}\n \n \t/* validation if packet capture is for all queues */\n@@ -296,8 +368,9 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)\n \t/* register RX callback */\n \tif (flags & RTE_PDUMP_FLAG_RX) {\n \t\tend_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_rx_q : queue + 1;\n-\t\tret = pdump_register_rx_callbacks(end_q, port, queue, ring, mp,\n-\t\t\t\t\t\t\toperation);\n+\t\tret = pdump_register_rx_callbacks(p->ver, end_q, port, queue,\n+\t\t\t\t\t\t  ring, mp, filter,\n+\t\t\t\t\t\t  operation, p->snaplen);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n \t}\n@@ -305,8 +378,9 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)\n \t/* register TX callback */\n \tif (flags & RTE_PDUMP_FLAG_TX) {\n \t\tend_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_tx_q : queue + 1;\n-\t\tret = pdump_register_tx_callbacks(end_q, port, queue, ring, mp,\n-\t\t\t\t\t\t\toperation);\n+\t\tret = pdump_register_tx_callbacks(p->ver, end_q, port, queue,\n+\t\t\t\t\t\t  ring, mp, filter,\n+\t\t\t\t\t\t  operation, p->snaplen);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n \t}\n@@ -347,8 +421,18 @@ pdump_server(const struct rte_mp_msg *mp_msg, const void *peer)\n int\n rte_pdump_init(void)\n {\n+\tconst struct rte_memzone *mz;\n \tint ret;\n \n+\tmz = rte_memzone_reserve(MZ_RTE_PDUMP_STATS, sizeof(*pdump_stats),\n+\t\t\t\t rte_socket_id(), 0);\n+\tif (mz == NULL) {\n+\t\tPDUMP_LOG(ERR, \"cannot allocate pdump statistics\\n\");\n+\t\trte_errno = ENOMEM;\n+\t\treturn -1;\n+\t}\n+\tpdump_stats = mz->addr;\n+\n \tret = rte_mp_action_register(PDUMP_MP, pdump_server);\n \tif (ret && rte_errno != ENOTSUP)\n \t\treturn -1;\n@@ -392,14 +476,21 @@ pdump_validate_ring_mp(struct rte_ring *ring, struct rte_mempool *mp)\n static int\n pdump_validate_flags(uint32_t flags)\n {\n-\tif (flags != RTE_PDUMP_FLAG_RX && flags != RTE_PDUMP_FLAG_TX &&\n-\t\tflags != RTE_PDUMP_FLAG_RXTX) {\n+\tif ((flags & RTE_PDUMP_FLAG_RXTX) == 0) {\n \t\tPDUMP_LOG(ERR,\n \t\t\t\"invalid flags, should be either rx/tx/rxtx\\n\");\n \t\trte_errno = EINVAL;\n \t\treturn -1;\n \t}\n \n+\t/* mask off the flags we know about */\n+\tif (flags & ~(RTE_PDUMP_FLAG_RXTX | RTE_PDUMP_FLAG_PCAPNG)) {\n+\t\tPDUMP_LOG(ERR,\n+\t\t\t  \"unknown flags: %#x\\n\", flags);\n+\t\trte_errno = ENOTSUP;\n+\t\treturn -1;\n+\t}\n+\n \treturn 0;\n }\n \n@@ -426,12 +517,12 @@ pdump_validate_port(uint16_t port, char *name)\n }\n \n static int\n-pdump_prepare_client_request(char *device, uint16_t queue,\n-\t\t\t\tuint32_t flags,\n-\t\t\t\tuint16_t operation,\n-\t\t\t\tstruct rte_ring *ring,\n-\t\t\t\tstruct rte_mempool *mp,\n-\t\t\t\tvoid *filter)\n+pdump_prepare_client_request(const char *device, uint16_t queue,\n+\t\t\t     uint32_t flags, uint32_t snaplen,\n+\t\t\t     uint16_t operation,\n+\t\t\t     struct rte_ring *ring,\n+\t\t\t     struct rte_mempool *mp,\n+\t\t\t     const struct rte_bpf_prm *prm)\n {\n \tint ret = -1;\n \tstruct rte_mp_msg mp_req, *mp_rep;\n@@ -440,23 +531,23 @@ pdump_prepare_client_request(char *device, uint16_t queue,\n \tstruct pdump_request *req = (struct pdump_request *)mp_req.param;\n \tstruct pdump_response *resp;\n \n-\treq->ver = 1;\n-\treq->flags = flags;\n+\tmemset(req, 0, sizeof(*req));\n+\tif (flags & RTE_PDUMP_FLAG_PCAPNG)\n+\t\treq->ver = PDUMP_CLIENT_PCAPNG;\n+\telse\n+\t\treq->ver = PDUMP_CLIENT_LEGACY;\n+\n+\treq->flags = flags & RTE_PDUMP_FLAG_RXTX;\n \treq->op = operation;\n+\treq->queue = queue;\n+\tstrlcpy(req->device, device, sizeof(req->device));\n+\n \tif ((operation & ENABLE) != 0) {\n-\t\tstrlcpy(req->data.en_v1.device, device,\n-\t\t\tsizeof(req->data.en_v1.device));\n-\t\treq->data.en_v1.queue = queue;\n-\t\treq->data.en_v1.ring = ring;\n-\t\treq->data.en_v1.mp = mp;\n-\t\treq->data.en_v1.filter = filter;\n-\t} else {\n-\t\tstrlcpy(req->data.dis_v1.device, device,\n-\t\t\tsizeof(req->data.dis_v1.device));\n-\t\treq->data.dis_v1.queue = queue;\n-\t\treq->data.dis_v1.ring = NULL;\n-\t\treq->data.dis_v1.mp = NULL;\n-\t\treq->data.dis_v1.filter = NULL;\n+\t\treq->queue = queue;\n+\t\treq->ring = ring;\n+\t\treq->mp = mp;\n+\t\treq->prm = prm;\n+\t\treq->snaplen = snaplen;\n \t}\n \n \tstrlcpy(mp_req.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN);\n@@ -477,11 +568,17 @@ pdump_prepare_client_request(char *device, uint16_t queue,\n \treturn ret;\n }\n \n-int\n-rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags,\n-\t\t\tstruct rte_ring *ring,\n-\t\t\tstruct rte_mempool *mp,\n-\t\t\tvoid *filter)\n+/*\n+ * There are two versions of this function, because although original API\n+ * left place holder for future filter, it never checked the value.\n+ * Therefore the API can't depend on application passing a non\n+ * bogus value.\n+ */\n+static int\n+pdump_enable(uint16_t port, uint16_t queue,\n+\t     uint32_t flags, uint32_t snaplen,\n+\t     struct rte_ring *ring, struct rte_mempool *mp,\n+\t     const struct rte_bpf_prm *prm)\n {\n \tint ret;\n \tchar name[RTE_DEV_NAME_MAX_LEN];\n@@ -496,20 +593,42 @@ rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags,\n \tif (ret < 0)\n \t\treturn ret;\n \n-\tret = pdump_prepare_client_request(name, queue, flags,\n-\t\t\t\t\t\tENABLE, ring, mp, filter);\n+\tif (snaplen == 0)\n+\t\tsnaplen = UINT32_MAX;\n \n-\treturn ret;\n+\treturn pdump_prepare_client_request(name, queue, flags, snaplen,\n+\t\t\t\t\t    ENABLE, ring, mp, prm);\n }\n \n int\n-rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue,\n-\t\t\t\tuint32_t flags,\n-\t\t\t\tstruct rte_ring *ring,\n-\t\t\t\tstruct rte_mempool *mp,\n-\t\t\t\tvoid *filter)\n+rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags,\n+\t\t struct rte_ring *ring,\n+\t\t struct rte_mempool *mp,\n+\t\t void *filter __rte_unused)\n {\n-\tint ret = 0;\n+\treturn pdump_enable(port, queue, flags, 0,\n+\t\t\t    ring, mp, NULL);\n+}\n+\n+int\n+rte_pdump_enable_bpf(uint16_t port, uint16_t queue,\n+\t\t     uint32_t flags, uint32_t snaplen,\n+\t\t     struct rte_ring *ring,\n+\t\t     struct rte_mempool *mp,\n+\t\t     const struct rte_bpf_prm *prm)\n+{\n+\treturn pdump_enable(port, queue, flags, snaplen,\n+\t\t\t    ring, mp, prm);\n+}\n+\n+static int\n+pdump_enable_by_deviceid(const char *device_id, uint16_t queue,\n+\t\t\t uint32_t flags, uint32_t snaplen,\n+\t\t\t struct rte_ring *ring,\n+\t\t\t struct rte_mempool *mp,\n+\t\t\t const struct rte_bpf_prm *prm)\n+{\n+\tint ret;\n \n \tret = pdump_validate_ring_mp(ring, mp);\n \tif (ret < 0)\n@@ -518,10 +637,30 @@ rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue,\n \tif (ret < 0)\n \t\treturn ret;\n \n-\tret = pdump_prepare_client_request(device_id, queue, flags,\n-\t\t\t\t\t\tENABLE, ring, mp, filter);\n+\treturn pdump_prepare_client_request(device_id, queue, flags, snaplen,\n+\t\t\t\t\t    ENABLE, ring, mp, prm);\n+}\n \n-\treturn ret;\n+int\n+rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue,\n+\t\t\t     uint32_t flags,\n+\t\t\t     struct rte_ring *ring,\n+\t\t\t     struct rte_mempool *mp,\n+\t\t\t     void *filter __rte_unused)\n+{\n+\treturn pdump_enable_by_deviceid(device_id, queue, flags, 0,\n+\t\t\t\t\tring, mp, NULL);\n+}\n+\n+int\n+rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue,\n+\t\t\t\t uint32_t flags, uint32_t snaplen,\n+\t\t\t\t struct rte_ring *ring,\n+\t\t\t\t struct rte_mempool *mp,\n+\t\t\t\t const struct rte_bpf_prm *prm)\n+{\n+\treturn pdump_enable_by_deviceid(device_id, queue, flags, snaplen,\n+\t\t\t\t\tring, mp, prm);\n }\n \n int\n@@ -537,8 +676,8 @@ rte_pdump_disable(uint16_t port, uint16_t queue, uint32_t flags)\n \tif (ret < 0)\n \t\treturn ret;\n \n-\tret = pdump_prepare_client_request(name, queue, flags,\n-\t\t\t\t\t\tDISABLE, NULL, NULL, NULL);\n+\tret = pdump_prepare_client_request(name, queue, flags, 0,\n+\t\t\t\t\t   DISABLE, NULL, NULL, NULL);\n \n \treturn ret;\n }\n@@ -553,8 +692,66 @@ rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue,\n \tif (ret < 0)\n \t\treturn ret;\n \n-\tret = pdump_prepare_client_request(device_id, queue, flags,\n-\t\t\t\t\t\tDISABLE, NULL, NULL, NULL);\n+\tret = pdump_prepare_client_request(device_id, queue, flags, 0,\n+\t\t\t\t\t   DISABLE, NULL, NULL, NULL);\n \n \treturn ret;\n }\n+\n+static void\n+pdump_sum_stats(uint16_t port, uint16_t nq,\n+\t\tstruct rte_pdump_stats stats[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],\n+\t\tstruct rte_pdump_stats *total)\n+{\n+\tuint64_t *sum = (uint64_t *)total;\n+\tunsigned int i;\n+\tuint64_t val;\n+\tuint16_t qid;\n+\n+\tfor (qid = 0; qid < nq; qid++) {\n+\t\tconst uint64_t *perq = (const uint64_t *)&stats[port][qid];\n+\n+\t\tfor (i = 0; i < sizeof(*total) / sizeof(uint64_t); i++) {\n+\t\t\tval = __atomic_load_n(&perq[i], __ATOMIC_RELAXED);\n+\t\t\tsum[i] += val;\n+\t\t}\n+\t}\n+}\n+\n+int\n+rte_pdump_stats(uint16_t port, struct rte_pdump_stats *stats)\n+{\n+\tstruct rte_eth_dev_info dev_info;\n+\tconst struct rte_memzone *mz;\n+\tint ret;\n+\n+\tmemset(stats, 0, sizeof(*stats));\n+\tret = rte_eth_dev_info_get(port, &dev_info);\n+\tif (ret != 0) {\n+\t\tPDUMP_LOG(ERR,\n+\t\t\t  \"Error during getting device (port %u) info: %s\\n\",\n+\t\t\t  port, strerror(-ret));\n+\t\treturn ret;\n+\t}\n+\n+\tif (pdump_stats == NULL) {\n+\t\tif (rte_eal_process_type() == RTE_PROC_PRIMARY) {\n+\t\t\tPDUMP_LOG(ERR,\n+\t\t\t\t  \"pdump not initialized\\n\");\n+\t\t\trte_errno = EINVAL;\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tmz = rte_memzone_lookup(MZ_RTE_PDUMP_STATS);\n+\t\tif (mz == NULL) {\n+\t\t\tPDUMP_LOG(ERR, \"can not find pdump stats\\n\");\n+\t\t\trte_errno = EINVAL;\n+\t\t\treturn -1;\n+\t\t}\n+\t\tpdump_stats = mz->addr;\n+\t}\n+\n+\tpdump_sum_stats(port, dev_info.nb_rx_queues, pdump_stats->rx, stats);\n+\tpdump_sum_stats(port, dev_info.nb_tx_queues, pdump_stats->tx, stats);\n+\treturn 0;\n+}\ndiff --git a/lib/pdump/rte_pdump.h b/lib/pdump/rte_pdump.h\nindex 6b00fc17aeb2..be3fd14c4bd3 100644\n--- a/lib/pdump/rte_pdump.h\n+++ b/lib/pdump/rte_pdump.h\n@@ -15,6 +15,7 @@\n #include <stdint.h>\n #include <rte_mempool.h>\n #include <rte_ring.h>\n+#include <rte_bpf.h>\n \n #ifdef __cplusplus\n extern \"C\" {\n@@ -26,7 +27,9 @@ enum {\n \tRTE_PDUMP_FLAG_RX = 1,  /* receive direction */\n \tRTE_PDUMP_FLAG_TX = 2,  /* transmit direction */\n \t/* both receive and transmit directions */\n-\tRTE_PDUMP_FLAG_RXTX = (RTE_PDUMP_FLAG_RX|RTE_PDUMP_FLAG_TX)\n+\tRTE_PDUMP_FLAG_RXTX = (RTE_PDUMP_FLAG_RX|RTE_PDUMP_FLAG_TX),\n+\n+\tRTE_PDUMP_FLAG_PCAPNG = 4, /* format for pcapng */\n };\n \n /**\n@@ -68,7 +71,7 @@ rte_pdump_uninit(void);\n  * @param mp\n  *  mempool on to which original packets will be mirrored or duplicated.\n  * @param filter\n- *  place holder for packet filtering.\n+ *  Unused should be NULL.\n  *\n  * @return\n  *    0 on success, -1 on error, rte_errno is set accordingly.\n@@ -80,6 +83,41 @@ rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags,\n \t\tstruct rte_mempool *mp,\n \t\tvoid *filter);\n \n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice\n+ *\n+ * Enables packet capturing on given port and queue with filtering.\n+ *\n+ * @param port_id\n+ *  The Ethernet port on which packet capturing should be enabled.\n+ * @param queue\n+ *  The queue on the Ethernet port which packet capturing\n+ *  should be enabled. Pass UINT16_MAX to enable packet capturing on all\n+ *  queues of a given port.\n+ * @param flags\n+ *  Pdump library flags that specify direction and packet format.\n+ * @param snaplen\n+ *  The upper limit on bytes to copy.\n+ *  Passing UINT32_MAX means capture all the possible data.\n+ * @param ring\n+ *  The ring on which captured packets will be enqueued for user.\n+ * @param mp\n+ *  The mempool on to which original packets will be mirrored or duplicated.\n+ * @param prm\n+ *  Use BPF program to run to filter packes (can be NULL)\n+ *\n+ * @return\n+ *    0 on success, -1 on error, rte_errno is set accordingly.\n+ */\n+__rte_experimental\n+int\n+rte_pdump_enable_bpf(uint16_t port_id, uint16_t queue,\n+\t\t     uint32_t flags, uint32_t snaplen,\n+\t\t     struct rte_ring *ring,\n+\t\t     struct rte_mempool *mp,\n+\t\t     const struct rte_bpf_prm *prm);\n+\n /**\n  * Disables packet capturing on given port and queue.\n  *\n@@ -118,7 +156,7 @@ rte_pdump_disable(uint16_t port, uint16_t queue, uint32_t flags);\n  * @param mp\n  *  mempool on to which original packets will be mirrored or duplicated.\n  * @param filter\n- *  place holder for packet filtering.\n+ *  unused should be NULL\n  *\n  * @return\n  *    0 on success, -1 on error, rte_errno is set accordingly.\n@@ -131,6 +169,43 @@ rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue,\n \t\t\t\tstruct rte_mempool *mp,\n \t\t\t\tvoid *filter);\n \n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice\n+ *\n+ * Enables packet capturing on given device id and queue with filtering.\n+ * device_id can be name or pci address of device.\n+ *\n+ * @param device_id\n+ *  device id on which packet capturing should be enabled.\n+ * @param queue\n+ *  The queue on the Ethernet port which packet capturing\n+ *  should be enabled. Pass UINT16_MAX to enable packet capturing on all\n+ *  queues of a given port.\n+ * @param flags\n+ *  Pdump library flags that specify direction and packet format.\n+ * @param snaplen\n+ *  The upper limit on bytes to copy.\n+ *  Passing UINT32_MAX means capture all the possible data.\n+ * @param ring\n+ *  The ring on which captured packets will be enqueued for user.\n+ * @param mp\n+ *  The mempool on to which original packets will be mirrored or duplicated.\n+ * @param filter\n+ *  Use BPF program to run to filter packes (can be NULL)\n+ *\n+ * @return\n+ *    0 on success, -1 on error, rte_errno is set accordingly.\n+ */\n+__rte_experimental\n+int\n+rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue,\n+\t\t\t\t uint32_t flags, uint32_t snaplen,\n+\t\t\t\t struct rte_ring *ring,\n+\t\t\t\t struct rte_mempool *mp,\n+\t\t\t\t const struct rte_bpf_prm *filter);\n+\n+\n /**\n  * Disables packet capturing on given device_id and queue.\n  * device_id can be name or pci address of device.\n@@ -153,6 +228,35 @@ int\n rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue,\n \t\t\t\tuint32_t flags);\n \n+\n+/**\n+ * A structure used to retrieve statistics from packet capture.\n+ * The statistics are sum of both receive and transmit queues.\n+ */\n+struct rte_pdump_stats {\n+\tuint64_t accepted; /**< Number of packets accepted by filter. */\n+\tuint64_t filtered; /**< Number of packets rejected by filter. */\n+\tuint64_t nombuf;   /**< Number of mbuf allocation failures. */\n+\tuint64_t ringfull; /**< Number of missed packets due to ring full. */\n+\n+\tuint64_t reserved[4]; /**< Reserved and pad to cache line */\n+};\n+\n+/**\n+ * Retrieve the packet capture statistics for a queue.\n+ *\n+ * @param port_id\n+ *   The port identifier of the Ethernet device.\n+ * @param stats\n+ *   A pointer to structure of type *rte_pdump_stats* to be filled in.\n+ * @return\n+ *   Zero if successful. -1 on error and rte_errno is set.\n+ */\n+__rte_experimental\n+int\n+rte_pdump_stats(uint16_t port_id, struct rte_pdump_stats *stats);\n+\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/lib/pdump/version.map b/lib/pdump/version.map\nindex f0a9d12c9a9e..ce5502d9cdf4 100644\n--- a/lib/pdump/version.map\n+++ b/lib/pdump/version.map\n@@ -10,3 +10,11 @@ DPDK_22 {\n \n \tlocal: *;\n };\n+\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\trte_pdump_enable_bpf;\n+\trte_pdump_enable_bpf_by_deviceid;\n+\trte_pdump_stats;\n+};\n",
    "prefixes": [
        "v7",
        "06/11"
    ]
}