Show a patch.

Update a patch.

Update a patch.

GET /api/patches/1524858/
Content-Type: application/json
Vary: Accept

    "id": 1524858,
    "url": "",
    "web_url": "",
    "project": {
        "id": 47,
        "url": "",
        "name": "Open vSwitch",
        "link_name": "openvswitch",
        "list_id": "",
        "list_email": "",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    "msgid": "<163091843101.96839.16631709571000877173.stgit@ebuild>",
    "list_archive_url": null,
    "date": "2021-09-06T08:53:58",
    "name": "[ovs-dev,v6,2/2] dpctl: dpif: allow viewing and configuring dp cache sizes",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "a95e31f3970753850e80ca55c14abdf41e7dcbfe",
    "submitter": {
        "id": 70613,
        "url": "",
        "name": "Eelco Chaudron",
        "email": ""
    "delegate": null,
    "mbox": "",
    "series": [
            "id": 261091,
            "url": "",
            "web_url": "",
            "date": "2021-09-06T08:53:28",
            "name": "dpctl: cache visibility/configuration enhancements",
            "version": 6,
            "mbox": ""
    "comments": "",
    "check": "success",
    "checks": "",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<>",
        "X-Original-To": [
        "Delivered-To": [
        "Authentication-Results": [
            ";\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.a=rsa-sha256\n header.s=mimecast20190719 header.b=a0I7i6nA;\n\tdkim-atps=neutral",
            ";\n spf=pass (sender SPF authorized)\n (client-ip=2605:bc80:3010::133;;\n; receiver=<UNKNOWN>)",
            ";\n auth=pass smtp.auth=CUSA124A263"
        "Received": [
            "from ( [IPv6:2605:bc80:3010::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby (Postfix) with ESMTPS id 4H32JY4hb9z9sRN\n\tfor <>; Mon,  6 Sep 2021 18:54:21 +1000 (AEST)",
            "from localhost (localhost [])\n\tby (Postfix) with ESMTP id BF7E74041A;\n\tMon,  6 Sep 2021 08:54:18 +0000 (UTC)",
            "from ([])\n\tby localhost ( []) (amavisd-new, port 10024)\n\twith ESMTP id 8zjEhPFE7UuU; Mon,  6 Sep 2021 08:54:17 +0000 (UTC)",
            "from (\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby (Postfix) with ESMTPS id 0ACE2403F0;\n\tMon,  6 Sep 2021 08:54:16 +0000 (UTC)",
            "from (localhost [])\n\tby (Postfix) with ESMTP id CE49AC001C;\n\tMon,  6 Sep 2021 08:54:15 +0000 (UTC)",
            "from ( [])\n by (Postfix) with ESMTP id 6E891C001B\n for <>; Mon,  6 Sep 2021 08:54:14 +0000 (UTC)",
            "from localhost (localhost [])\n by (Postfix) with ESMTP id E4E5640445\n for <>; Mon,  6 Sep 2021 08:54:08 +0000 (UTC)",
            "from ([])\n by localhost ( []) (amavisd-new, port 10024)\n with ESMTP id jVZj_dqGctHS for <>;\n Mon,  6 Sep 2021 08:54:07 +0000 (UTC)",
            "from\n ( [])\n by (Postfix) with ESMTPS id 163324043C\n for <>; Mon,  6 Sep 2021 08:54:06 +0000 (UTC)",
            "from (\n []) (Using TLS) by with ESMTP id\n us-mta-244-yp0odMNaNv66wFPlUzRf1A-1; Mon, 06 Sep 2021 04:54:02 -0400",
            "from (\n [])\n (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n (No client certificate requested)\n by (Postfix) with ESMTPS id 6A15710866B9;\n Mon,  6 Sep 2021 08:54:01 +0000 (UTC)",
            "from (unknown [])\n by (Postfix) with ESMTP id 8198B10016F4;\n Mon,  6 Sep 2021 08:54:00 +0000 (UTC)"
        "X-Virus-Scanned": [
            "amavisd-new at",
            "amavisd-new at"
        "X-Greylist": "domain auto-whitelisted by SQLgrey-1.8.0",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;;\n s=mimecast20190719; t=1630918446;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=fCdaO5IPmFdFSj4SZrJIhiqMIZCGewKi6ghQSuAYzAI=;\n b=a0I7i6nAGuNjZn2qIxY/pIoAs8nqugtlZzwRZI15j/JQBOvsUEckcoNQ9OMUIwzfoEQoU8\n zYYvTaeY6w/xecOzwAJSGaEoemCZPkiuV3MM9DrCeePfZgUcpIqsFC1o0m3Xq71cu3srO+\n Y/6Da9hYFtAy4zYuy9TQRl3wcCQM97E=",
        "X-MC-Unique": "yp0odMNaNv66wFPlUzRf1A-1",
        "From": "Eelco Chaudron <>",
        "To": "",
        "Date": "Mon,  6 Sep 2021 10:53:58 +0200",
        "Message-Id": "<163091843101.96839.16631709571000877173.stgit@ebuild>",
        "In-Reply-To": "<163091839517.96839.8006181203263313854.stgit@ebuild>",
        "References": "<163091839517.96839.8006181203263313854.stgit@ebuild>",
        "User-Agent": "StGit/0.21",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 2.84 on",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-Originator": "",
        "Cc": "",
        "Subject": "[ovs-dev] [PATCH v6 2/2] dpctl: dpif: allow viewing and configuring\n\tdp cache sizes",
        "X-BeenThere": "",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "<>",
        "List-Unsubscribe": "<>,\n <>",
        "List-Archive": "<>",
        "List-Post": "<>",
        "List-Help": "<>",
        "List-Subscribe": "<>,\n <>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "",
        "Sender": "\"dev\" <>"
    "content": "This patch adds a general way of viewing/configuring datapath\ncache sizes. With an implementation for the netlink interface.\n\nThe ovs-dpctl/ovs-appctl show commands will display the\ncurrent cache sizes configured:\n\novs-dpctl show\nsystem@ovs-system:\n  lookups: hit:25 missed:63 lost:0\n  flows: 0\n  masks: hit:282 total:0 hit/pkt:3.20\n  cache: hit:4 hit rate:4.54%\n  caches:\n    masks-cache: size: 256\n  port 0: ovs-system (internal)\n  port 1: br-int (internal)\n  port 2: genev_sys_6081 (geneve: packet_type=ptap)\n  port 3: br-ex (internal)\n  port 4: eth2\n  port 5: sw0p1 (internal)\n  port 6: sw0p3 (internal)\n\nA specific cache can be configured as follows:\n\novs-appctl dpctl/cache-set-size DP CACHE SIZE\novs-dpctl cache-set-size DP CACHE SIZE\n\nFor example to disable the cache do:\n\n$ ovs-dpctl cache-set-size system@ovs-system masks-cache 0\nSetting cache size successful, new size 0.\n\nSigned-off-by: Eelco Chaudron <>\nAcked-by: Paolo Valerio <>\nAcked-by: Flavio Leitner <>\n\nv2: - Changed precision to 2 digits\n    - Handle missing kernel feature at netlink level\nv3: - Rebase on the latest master branch\nv4: - Fixed commit message\n    - Fix issue with resetting user_features\nv5: - Include the actual resetting user_features fix\nv6: - Rebase on the latest master branch\n    - Add ACK's\n---\n datapath/linux/compat/include/linux/openvswitch.h |    2 \n lib/dpctl.c                                       |  120 +++++++++++++++++++++\n lib/                                     |   14 ++\n lib/dpif-netdev.c                                 |    4 +\n lib/dpif-netlink.c                                |  117 ++++++++++++++++++++\n lib/dpif-provider.h                               |   20 ++++\n lib/dpif.c                                        |   32 ++++++\n lib/dpif.h                                        |    7 +\n tests/                           |   36 ++++++\n utilities/ovs-dpctl.c                             |    4 +\n 10 files changed, 355 insertions(+), 1 deletion(-)",
    "diff": "diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h\nindex a3f5fb919..936644192 100644\n--- a/datapath/linux/compat/include/linux/openvswitch.h\n+++ b/datapath/linux/compat/include/linux/openvswitch.h\n@@ -107,7 +107,7 @@ enum ovs_datapath_attr {\n \tOVS_DP_ATTR_MEGAFLOW_STATS,\t/* struct ovs_dp_megaflow_stats */\n \tOVS_DP_ATTR_USER_FEATURES,\t/* OVS_DP_F_*  */\n \tOVS_DP_ATTR_PAD,\n-\tOVS_DP_ATTR_PAD2,\n+\tOVS_DP_ATTR_MASKS_CACHE_SIZE,\n \tOVS_DP_ATTR_PER_CPU_PIDS,\t/* Netlink PIDS to receive upcalls */\n \t__OVS_DP_ATTR_MAX\n };\ndiff --git a/lib/dpctl.c b/lib/dpctl.c\nindex acc677974..6cba8db51 100644\n--- a/lib/dpctl.c\n+++ b/lib/dpctl.c\n@@ -591,6 +591,36 @@ compare_port_nos(const void *a_, const void *b_)\n     return a < b ? -1 : a > b;\n }\n \n+static void\n+show_dpif_cache__(struct dpif *dpif, struct dpctl_params *dpctl_p)\n+{\n+    uint32_t nr_caches;\n+    int error = dpif_cache_get_supported_levels(dpif, &nr_caches);\n+\n+    if (error || nr_caches == 0) {\n+        return;\n+    }\n+\n+    dpctl_print(dpctl_p, \"  caches:\\n\");\n+    for (int i = 0; i < nr_caches; i++) {\n+        const char *name;\n+        uint32_t size;\n+\n+        if (dpif_cache_get_name(dpif, i, &name) ||\n+            dpif_cache_get_size(dpif, i, &size)) {\n+            continue;\n+        }\n+        dpctl_print(dpctl_p, \"    %s: size: %u\\n\", name, size);\n+    }\n+}\n+\n+static void\n+show_dpif_cache(struct dpif *dpif, struct dpctl_params *dpctl_p)\n+{\n+    dpctl_print(dpctl_p, \"%s:\\n\", dpif_name(dpif));\n+    show_dpif_cache__(dpif, dpctl_p);\n+}\n+\n static void\n show_dpif(struct dpif *dpif, struct dpctl_params *dpctl_p)\n {\n@@ -623,6 +653,8 @@ show_dpif(struct dpif *dpif, struct dpctl_params *dpctl_p)\n         }\n     }\n \n+    show_dpif_cache__(dpif, dpctl_p);\n+\n     odp_port_t *port_nos = NULL;\n     size_t allocated_port_nos = 0, n_port_nos = 0;\n     DPIF_PORT_FOR_EACH (&dpif_port, &dump, dpif) {\n@@ -2409,6 +2441,92 @@ dpctl_ct_ipf_get_status(int argc, const char *argv[],\n     return error;\n }\n \n+static int\n+dpctl_cache_get_size(int argc, const char *argv[],\n+                     struct dpctl_params *dpctl_p)\n+{\n+    int error;\n+\n+    if (argc > 1) {\n+        struct dpif *dpif;\n+\n+        error = parsed_dpif_open(argv[1], false, &dpif);\n+        if (!error) {\n+            show_dpif_cache(dpif, dpctl_p);\n+            dpif_close(dpif);\n+        } else {\n+            dpctl_error(dpctl_p, error, \"Opening datapath %s failed\", argv[1]);\n+        }\n+    } else {\n+        error = dps_for_each(dpctl_p, show_dpif_cache);\n+    }\n+\n+    return error;\n+}\n+\n+static int\n+dpctl_cache_set_size(int argc, const char *argv[],\n+                     struct dpctl_params *dpctl_p)\n+{\n+    int i, error = EINVAL;\n+    uint32_t nr_caches, size;\n+    struct dpif *dpif;\n+\n+    if (argc != 4) {\n+        dpctl_error(dpctl_p, error, \"Invalid number of arguments\");\n+        return error;\n+    }\n+\n+    if (!ovs_scan(argv[3], \"%\"SCNu32, &size)) {\n+        dpctl_error(dpctl_p, error, \"size is malformed\");\n+        return error;\n+    }\n+\n+    error = parsed_dpif_open(argv[1], false, &dpif);\n+    if (error) {\n+            dpctl_error(dpctl_p, error, \"Opening datapath %s failed\",\n+                        argv[1]);\n+            return error;\n+    }\n+\n+    error = dpif_cache_get_supported_levels(dpif, &nr_caches);\n+    if (error || nr_caches == 0) {\n+        dpctl_error(dpctl_p, error, \"Setting caches not supported\");\n+        goto exit;\n+    }\n+\n+    for (i = 0; i < nr_caches; i++) {\n+        const char *name;\n+\n+        if (dpif_cache_get_name(dpif, i, &name)) {\n+            continue;\n+        }\n+        if (!strcmp(argv[2], name)) {\n+            break;\n+        }\n+    }\n+\n+    if (i == nr_caches) {\n+        error = EINVAL;\n+        dpctl_error(dpctl_p, error, \"Cache name \\\"%s\\\" not found on dpif\",\n+                    argv[2]);\n+        goto exit;\n+    }\n+\n+    error = dpif_cache_set_size(dpif, i, size);\n+    if (!error) {\n+        dpif_cache_get_size(dpif, i, &size);\n+        dpctl_print(dpctl_p, \"Setting cache size successful, new size %u\\n\",\n+                    size);\n+    } else {\n+        dpctl_error(dpctl_p, error, \"Setting cache size failed\");\n+    }\n+\n+exit:\n+    dpif_close(dpif);\n+    return error;\n+}\n+\n /* Undocumented commands for unit testing. */\n \n static int\n@@ -2710,6 +2828,8 @@ static const struct dpctl_command all_commands[] = {\n       0, 4, dpctl_dump_conntrack, DP_RO },\n     { \"flush-conntrack\", \"[dp] [zone=N] [ct-tuple]\", 0, 3,\n       dpctl_flush_conntrack, DP_RW },\n+    { \"cache-get-size\", \"[dp]\", 0, 1, dpctl_cache_get_size, DP_RO },\n+    { \"cache-set-size\", \"dp cache <size>\", 3, 3, dpctl_cache_set_size, DP_RW },\n     { \"ct-stats-show\", \"[dp] [zone=N]\",\n       0, 3, dpctl_ct_stats_show, DP_RO },\n     { \"ct-bkts\", \"[dp] [gt=N]\", 0, 2, dpctl_ct_bkts, DP_RO },\ndiff --git a/lib/ b/lib/\nindex 81046ef39..c1361f217 100644\n--- a/lib/\n+++ b/lib/\n@@ -226,6 +226,20 @@ Fetches the flow from \\fIdp\\fR's flow table with unique identifier \\fIufid\\fR.\n .\n .IP \"\\*(DX\\fBdel\\-flows\\fR [\\fIdp\\fR]\"\n Deletes all flow entries from datapath \\fIdp\\fR's flow table.\n+.SS \"DATAPATH FLOW CACHE COMMANDS\"\n+The following commands are useful for debugging and configuring\n+the datapath flow cache settings.\n+.\n+.TP\n+\\*(DX\\fBcache\\-get\\-size\\fR [\\fIdp\\fR]\n+Prints the current cache setting to the console.\n+.\n+.TP\n+\\*(DX\\fBcache\\-set\\-size\\fR \\fIdp\\fR \\fIcache\\fR \\fIsize\\fR\n+Set the \\fIdp\\fR's specific \\fIcache\\fR to the given \\fIsize\\fR.\n+The cache name can be found by using the \\fBcache\\-get\\-size\\fR\n+command.\n+.\n .SS \"CONNECTION TRACKING TABLE COMMANDS\"\n The following commands are useful for debugging and configuring\n the connection tracking table in the datapath.\ndiff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c\nindex 0d0623013..642204b54 100644\n--- a/lib/dpif-netdev.c\n+++ b/lib/dpif-netdev.c\n@@ -8791,6 +8791,10 @@ const struct dpif_class dpif_netdev_class = {\n     dpif_netdev_bond_add,\n     dpif_netdev_bond_del,\n     dpif_netdev_bond_stats_get,\n+    NULL,                       /* cache_get_supported_levels */\n+    NULL,                       /* cache_get_name */\n+    NULL,                       /* cache_get_size */\n+    NULL,                       /* cache_set_size */\n };\n \n static void\ndiff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c\nindex 70751e634..18bd1125a 100644\n--- a/lib/dpif-netlink.c\n+++ b/lib/dpif-netlink.c\n@@ -98,6 +98,7 @@ struct dpif_netlink_dp {\n     const char *name;                  /* OVS_DP_ATTR_NAME. */\n     const uint32_t *upcall_pid;        /* OVS_DP_ATTR_UPCALL_PID. */\n     uint32_t user_features;            /* OVS_DP_ATTR_USER_FEATURES */\n+    uint32_t cache_size;               /* OVS_DP_ATTR_MASKS_CACHE_SIZE */\n     const struct ovs_dp_stats *stats;  /* OVS_DP_ATTR_STATS. */\n     const struct ovs_dp_megaflow_stats *megaflow_stats;\n                                        /* OVS_DP_ATTR_MEGAFLOW_STATS.*/\n@@ -4270,6 +4271,104 @@ probe_broken_meters(struct dpif *dpif)\n     }\n     return broken_meters;\n }\n+\n+\n+static int\n+dpif_netlink_cache_get_supported_levels(struct dpif *dpif_, uint32_t *levels)\n+{\n+    int error;\n+    struct ofpbuf *buf;\n+    struct dpif_netlink_dp dp;\n+\n+    /* If available, in the kernel we support one level of cache.\n+     * Unfortunately, there is no way to detect if the older kernel module has\n+     * the cache feature. For now, we only report the cache information if the\n+     * kernel module reports the  OVS_DP_ATTR_MASKS_CACHE_SIZE attribute. */\n+\n+    *levels = 0;\n+    error = dpif_netlink_dp_get(dpif_, &dp, &buf);\n+    if (!error) {\n+\n+        if (dp.cache_size != UINT32_MAX) {\n+            *levels = 1;\n+        }\n+        ofpbuf_delete(buf);\n+    }\n+\n+    return error;\n+}\n+\n+static int\n+dpif_netlink_cache_get_name(struct dpif *dpif_ OVS_UNUSED, uint32_t level,\n+                            const char **name)\n+{\n+    if (level != 0) {\n+        return EINVAL;\n+    }\n+\n+    *name = \"masks-cache\";\n+    return 0;\n+}\n+\n+static int\n+dpif_netlink_cache_get_size(struct dpif *dpif_, uint32_t level, uint32_t *size)\n+{\n+    int error;\n+    struct ofpbuf *buf;\n+    struct dpif_netlink_dp dp;\n+\n+    if (level != 0) {\n+        return EINVAL;\n+    }\n+\n+    error = dpif_netlink_dp_get(dpif_, &dp, &buf);\n+    if (!error) {\n+\n+        ofpbuf_delete(buf);\n+\n+        if (dp.cache_size == UINT32_MAX) {\n+            return EOPNOTSUPP;\n+        }\n+        *size = dp.cache_size;\n+    }\n+    return error;\n+}\n+\n+static int\n+dpif_netlink_cache_set_size(struct dpif *dpif_, uint32_t level, uint32_t size)\n+{\n+    int error;\n+    struct ofpbuf *bufp;\n+    struct dpif_netlink_dp request, reply;\n+    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);\n+\n+    size = ROUND_UP_POW2(size);\n+\n+    if (level != 0) {\n+        return EINVAL;\n+    }\n+\n+    dpif_netlink_dp_init(&request);\n+    request.cmd = OVS_DP_CMD_SET;\n+ = dpif_->base_name;\n+    request.dp_ifindex = dpif->dp_ifindex;\n+    request.cache_size = size;\n+    /* We need to set the dpif user_features, as the kernel module assumes the\n+     * OVS_DP_ATTR_USER_FEATURES attribute is always present. If not, it will\n+     * reset all the features. */\n+    request.user_features = dpif->user_features;\n+\n+    error = dpif_netlink_dp_transact(&request, &reply, &bufp);\n+    if (!error) {\n+        ofpbuf_delete(bufp);\n+        if (reply.cache_size != size) {\n+            return EINVAL;\n+        }\n+    }\n+\n+    return error;\n+}\n+\n \f\n const struct dpif_class dpif_netlink_class = {\n     \"system\",\n@@ -4349,6 +4448,10 @@ const struct dpif_class dpif_netlink_class = {\n     NULL,                       /* bond_add */\n     NULL,                       /* bond_del */\n     NULL,                       /* bond_stats_get */\n+    dpif_netlink_cache_get_supported_levels,\n+    dpif_netlink_cache_get_name,\n+    dpif_netlink_cache_get_size,\n+    dpif_netlink_cache_set_size,\n };\n \n static int\n@@ -4612,6 +4715,9 @@ dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *dp, const struct ofpbuf *buf\n         [OVS_DP_ATTR_USER_FEATURES] = {\n                         .type = NL_A_U32,\n                         .optional = true },\n+        [OVS_DP_ATTR_MASKS_CACHE_SIZE] = {\n+                        .type = NL_A_U32,\n+                        .optional = true },\n     };\n \n     dpif_netlink_dp_init(dp);\n@@ -4644,6 +4750,12 @@ dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *dp, const struct ofpbuf *buf\n         dp->user_features = nl_attr_get_u32(a[OVS_DP_ATTR_USER_FEATURES]);\n     }\n \n+    if (a[OVS_DP_ATTR_MASKS_CACHE_SIZE]) {\n+        dp->cache_size = nl_attr_get_u32(a[OVS_DP_ATTR_MASKS_CACHE_SIZE]);\n+    } else {\n+        dp->cache_size = UINT32_MAX;\n+    }\n+\n     return 0;\n }\n \n@@ -4677,6 +4789,10 @@ dpif_netlink_dp_to_ofpbuf(const struct dpif_netlink_dp *dp, struct ofpbuf *buf)\n                           sizeof *dp->upcall_pids * dp->n_upcall_pids);\n     }\n \n+    if (dp->cache_size != UINT32_MAX) {\n+        nl_msg_put_u32(buf, OVS_DP_ATTR_MASKS_CACHE_SIZE, dp->cache_size);\n+    }\n+\n     /* Skip OVS_DP_ATTR_STATS since we never have a reason to serialize it. */\n }\n \n@@ -4685,6 +4801,7 @@ static void\n dpif_netlink_dp_init(struct dpif_netlink_dp *dp)\n {\n     memset(dp, 0, sizeof *dp);\n+    dp->cache_size = UINT32_MAX;\n }\n \n static void\ndiff --git a/lib/dpif-provider.h b/lib/dpif-provider.h\nindex 7e11b9697..27e3a7658 100644\n--- a/lib/dpif-provider.h\n+++ b/lib/dpif-provider.h\n@@ -635,6 +635,26 @@ struct dpif_class {\n      * sufficient to store BOND_BUCKETS number of elements. */\n     int (*bond_stats_get)(struct dpif *dpif, uint32_t bond_id,\n                           uint64_t *n_bytes);\n+\n+    /* Cache configuration\n+     *\n+     * Multiple levels of cache can exist in a given datapath implementation.\n+     * An API has been provided to get the number of supported caches, which\n+     * can then be used to get/set specific configuration. Cache level is 0\n+     * indexed, i.e. if 1 level is supported, the level value to use is 0.\n+     *\n+     * Get the number of cache levels supported. */\n+    int (*cache_get_supported_levels)(struct dpif *dpif, uint32_t *levels);\n+\n+    /* Get the cache name for the given level. */\n+    int (*cache_get_name)(struct dpif *dpif, uint32_t level,\n+                          const char **name);\n+\n+    /* Get currently configured cache size. */\n+    int (*cache_get_size)(struct dpif *dpif, uint32_t level, uint32_t *size);\n+\n+    /* Set cache size. */\n+    int (*cache_set_size)(struct dpif *dpif, uint32_t level, uint32_t size);\n };\n \n extern const struct dpif_class dpif_netlink_class;\ndiff --git a/lib/dpif.c b/lib/dpif.c\nindex 8c4aed47b..0b94d6ff0 100644\n--- a/lib/dpif.c\n+++ b/lib/dpif.c\n@@ -2058,3 +2058,35 @@ dpif_get_n_offloaded_flows(struct dpif *dpif, uint64_t *n_flows)\n     }\n     return n_devs ? 0 : EOPNOTSUPP;\n }\n+\n+int\n+dpif_cache_get_supported_levels(struct dpif *dpif, uint32_t *levels)\n+{\n+    return dpif->dpif_class->cache_get_supported_levels\n+        ? dpif->dpif_class->cache_get_supported_levels(dpif, levels)\n+        : EOPNOTSUPP;\n+}\n+\n+int\n+dpif_cache_get_name(struct dpif *dpif, uint32_t level, const char **name)\n+{\n+    return dpif->dpif_class->cache_get_name\n+        ? dpif->dpif_class->cache_get_name(dpif, level, name)\n+        : EOPNOTSUPP;\n+}\n+\n+int\n+dpif_cache_get_size(struct dpif *dpif, uint32_t level, uint32_t *size)\n+{\n+    return dpif->dpif_class->cache_get_size\n+        ? dpif->dpif_class->cache_get_size(dpif, level, size)\n+        : EOPNOTSUPP;\n+}\n+\n+int\n+dpif_cache_set_size(struct dpif *dpif, uint32_t level, uint32_t size)\n+{\n+    return dpif->dpif_class->cache_set_size\n+        ? dpif->dpif_class->cache_set_size(dpif, level, size)\n+        : EOPNOTSUPP;\n+}\ndiff --git a/lib/dpif.h b/lib/dpif.h\nindex b32ae5fc7..8febfb9f6 100644\n--- a/lib/dpif.h\n+++ b/lib/dpif.h\n@@ -908,6 +908,13 @@ int dpif_bond_del(struct dpif *, uint32_t bond_id);\n int dpif_bond_stats_get(struct dpif *, uint32_t bond_id, uint64_t *n_bytes);\n bool dpif_supports_lb_output_action(const struct dpif *);\n \n+\f\n+/* Cache */\n+int dpif_cache_get_supported_levels(struct dpif *dpif, uint32_t *levels);\n+int dpif_cache_get_name(struct dpif *dpif, uint32_t level, const char **name);\n+int dpif_cache_get_size(struct dpif *dpif, uint32_t level, uint32_t *size);\n+int dpif_cache_set_size(struct dpif *dpif, uint32_t level, uint32_t size);\n+\n \f\n /* Miscellaneous. */\n \ndiff --git a/tests/ b/tests/\nindex f400cfabc..662ad2a8f 100644\n--- a/tests/\n+++ b/tests/\n@@ -1454,6 +1454,42 @@ AT_CHECK([ovs-ofctl dump-flows br0 | grep \"in_port=4\" | ofctl_strip], [0], [dnl\n OVS_TRAFFIC_VSWITCHD_STOP\n AT_CLEANUP\n \n+AT_SETUP([datapath - configure cache size])\n+\n+OVS_TRAFFIC_VSWITCHD_START()\n+OVS_CHECK_KERNEL_EXCL(3, 10, 5, 8)\n+\n+AT_CHECK([ovs-dpctl cache-get-size one-bad-dp], [1], [], [dnl\n+ovs-dpctl: Opening datapath one-bad-dp failed (No such device)\n+])\n+AT_CHECK([ovs-dpctl cache-get-size | grep masks-cache | tr -d [[:blank:]]], [0], [dnl\n+masks-cache:size:256\n+])\n+AT_CHECK([ovs-dpctl cache-set-size one-bad-dp masks-cache 0], [1], [], [dnl\n+ovs-dpctl: Opening datapath one-bad-dp failed (No such device)\n+])\n+AT_CHECK([ovs-dpctl cache-set-size system@ovs-system dummy-cache 0], [1], [], [dnl\n+ovs-dpctl: Cache name \"dummy-cache\" not found on dpif (Invalid argument)\n+])\n+AT_CHECK([ovs-dpctl cache-set-size system@ovs-system masks-cache 80000], [1], [], [dnl\n+ovs-dpctl: Setting cache size failed (Numerical result out of range)\n+])\n+AT_CHECK([ovs-dpctl cache-set-size system@ovs-system masks-cache 0], [0], [dnl\n+Setting cache size successful, new size 0\n+])\n+AT_CHECK([ovs-dpctl cache-get-size | grep masks-cache | tr -d [[:blank:]]], [0], [dnl\n+masks-cache:size:0\n+])\n+AT_CHECK([ovs-dpctl cache-set-size system@ovs-system masks-cache 256], [0], [dnl\n+Setting cache size successful, new size 256\n+])\n+AT_CHECK([ovs-dpctl cache-get-size | grep masks-cache | tr -d [[:blank:]]], [0], [dnl\n+masks-cache:size:256\n+])\n+\n+OVS_TRAFFIC_VSWITCHD_STOP\n+AT_CLEANUP\n+\n AT_BANNER([conntrack])\n \n AT_SETUP([conntrack - controller])\ndiff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c\nindex f616995c3..56d7a942b 100644\n--- a/utilities/ovs-dpctl.c\n+++ b/utilities/ovs-dpctl.c\n@@ -198,6 +198,10 @@ usage(void *userdata OVS_UNUSED)\n            \"  del-flow [DP] FLOW         delete FLOW from DP\\n\"\n            \"  del-flows [DP] [FILE]      \" \\\n                \"delete all or specified flows from DP\\n\"\n+           \"  cache-get-size [DP]             \" \\\n+               \"Show the current size for all caches\\n\"\n+           \"  cache-set-size DP CACHE SIZE  \" \\\n+               \"Set cache size for a specific cache\\n\"\n            \"  dump-conntrack [DP] [zone=ZONE]  \" \\\n                \"display conntrack entries for ZONE\\n\"\n            \"  flush-conntrack [DP] [zone=ZONE] [ct-tuple]\" \\\n",
    "prefixes": [