Show a patch.

Update a patch.

Update a patch.

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

    "id": 1528339,
    "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": "<>",
    "list_archive_url": null,
    "date": "2021-09-15T12:43:17",
    "name": "[ovs-dev,v15,5/7] dpif-offload-netlink: Implement dpif-offload-provider API",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "4b720553cf8bb09ea47e84cdc3ed5799a5dd2e61",
    "submitter": {
        "id": 80086,
        "url": "",
        "name": "Chris Mi",
        "email": ""
    "delegate": null,
    "mbox": "",
    "series": [
            "id": 262447,
            "url": "",
            "web_url": "",
            "date": "2021-09-15T12:43:12",
            "name": "Add offload support for sFlow",
            "version": 15,
            "mbox": ""
    "comments": "",
    "check": "success",
    "checks": "",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<>",
        "X-Original-To": [
        "Delivered-To": [
        "Authentication-Results": ";\n spf=pass (sender SPF authorized)\n (client-ip=2605:bc80:3010::136;;\n; receiver=<UNKNOWN>)",
        "Received": [
            "from ( [IPv6:2605:bc80:3010::136])\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 4H8fz06zWsz9sSs\n\tfor <>; Wed, 15 Sep 2021 22:43:40 +1000 (AEST)",
            "from localhost (localhost [])\n\tby (Postfix) with ESMTP id 9F85360BE6;\n\tWed, 15 Sep 2021 12:43:37 +0000 (UTC)",
            "from ([])\n\tby localhost ( []) (amavisd-new, port 10024)\n\twith ESMTP id htWCLFcvAeUK; Wed, 15 Sep 2021 12:43:36 +0000 (UTC)",
            "from ( [])\n\tby (Postfix) with ESMTPS id 7001760BB0;\n\tWed, 15 Sep 2021 12:43:35 +0000 (UTC)",
            "from (localhost [])\n\tby (Postfix) with ESMTP id 8640CC0027;\n\tWed, 15 Sep 2021 12:43:33 +0000 (UTC)",
            "from ( [IPv6:2605:bc80:3010::137])\n by (Postfix) with ESMTP id 4CC7CC0011\n for <>; Wed, 15 Sep 2021 12:43:30 +0000 (UTC)",
            "from localhost (localhost [])\n by (Postfix) with ESMTP id 20B62401DA\n for <>; Wed, 15 Sep 2021 12:43:30 +0000 (UTC)",
            "from ([])\n by localhost ( []) (amavisd-new, port 10024)\n with ESMTP id k-QbkbYkNUbR for <>;\n Wed, 15 Sep 2021 12:43:29 +0000 (UTC)",
            "from ( [])\n by (Postfix) with ESMTP id 8DED640121\n for <>; Wed, 15 Sep 2021 12:43:28 +0000 (UTC)",
            "from Internal Mail-Server by MTLPINE1 (envelope-from\n with SMTP; 15 Sep 2021 15:43:21 +0300",
            "from c-141-46-1-010.mtl.labs.mlnx (c-141-46-1-010.mtl.labs.mlnx\n [])\n by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id 18FChLbZ010242;\n Wed, 15 Sep 2021 15:43:21 +0300"
        "X-Virus-Scanned": [
            "amavisd-new at",
            "amavisd-new at"
        "X-Greylist": "domain auto-whitelisted by SQLgrey-1.8.0",
        "To": "",
        "Date": "Wed, 15 Sep 2021 15:43:17 +0300",
        "Message-Id": "<>",
        "X-Mailer": "git-send-email 2.27.0",
        "In-Reply-To": "<>",
        "References": "<>",
        "MIME-Version": "1.0",
        "Cc": ",,,\n",
        "Subject": "[ovs-dev] [PATCH v15 5/7] dpif-offload-netlink: Implement\n\tdpif-offload-provider API",
        "X-BeenThere": "",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "<>",
        "List-Unsubscribe": "<>,\n <>",
        "List-Archive": "<>",
        "List-Post": "<>",
        "List-Help": "<>",
        "List-Subscribe": "<>,\n <>",
        "From": "Chris Mi via dev <>",
        "Reply-To": "Chris Mi <>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "",
        "Sender": "\"dev\" <>"
    "content": "Implement dpif-offload API for netlink datapath.\n\nSigned-off-by: Chris Mi <>\nReviewed-by: Eli Britstein <>\n---\n lib/             |   1 +\n lib/dpif-netlink.c          |   2 +-\n lib/dpif-offload-netlink.c  | 208 ++++++++++++++++++++++++++++++++++++\n lib/dpif-offload-provider.h |  13 ++-\n 4 files changed, 222 insertions(+), 2 deletions(-)\n create mode 100644 lib/dpif-offload-netlink.c",
    "diff": "diff --git a/lib/ b/lib/\nindex 9259f57de..18cffa827 100644\n--- a/lib/\n+++ b/lib/\n@@ -446,6 +446,7 @@ lib_libopenvswitch_la_SOURCES += \\\n \tlib/dpif-netlink.h \\\n \tlib/dpif-netlink-rtnl.c \\\n \tlib/dpif-netlink-rtnl.h \\\n+\tlib/dpif-offload-netlink.c \\\n \tlib/if-notifier.c \\\n \tlib/netdev-linux.c \\\n \tlib/netdev-linux.h \\\ndiff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c\nindex f546b48e5..19ae543e6 100644\n--- a/lib/dpif-netlink.c\n+++ b/lib/dpif-netlink.c\n@@ -4341,7 +4341,7 @@ const struct dpif_class dpif_netlink_class = {\n     NULL,                       /* bond_add */\n     NULL,                       /* bond_del */\n     NULL,                       /* bond_stats_get */\n-    NULL,                       /* dpif_offload_api */\n+    &dpif_offload_netlink,\n };\n \n static int\ndiff --git a/lib/dpif-offload-netlink.c b/lib/dpif-offload-netlink.c\nnew file mode 100644\nindex 000000000..fc3712764\n--- /dev/null\n+++ b/lib/dpif-offload-netlink.c\n@@ -0,0 +1,208 @@\n+/*\n+ * Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#include <config.h>\n+#include <errno.h>\n+#include <linux/psample.h>\n+#include <sys/poll.h>\n+\n+#include \"dpif-offload-provider.h\"\n+#include \"netdev-offload.h\"\n+#include \"netlink-protocol.h\"\n+#include \"netlink-socket.h\"\n+#include \"openvswitch/vlog.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(dpif_offload_netlink);\n+\n+static struct nl_sock *psample_sock;\n+static int psample_family;\n+\n+/* Receive psample netlink message and save the attributes. */\n+struct offload_psample {\n+    struct nlattr *packet;      /* Packet data. */\n+    int dp_group_id;            /* Mapping id for sFlow offload. */\n+    int iifindex;               /* Input ifindex. */\n+};\n+\n+static void\n+dpif_offload_netlink_init(void)\n+{\n+    unsigned int psample_mcgroup;\n+    int err;\n+\n+    if (!netdev_is_flow_api_enabled()) {\n+        VLOG_DBG(\"Flow API is not enabled.\");\n+        return;\n+    }\n+\n+    if (psample_sock) {\n+        VLOG_DBG(\"Psample socket is already initialized.\");\n+        return;\n+    }\n+\n+    err = nl_lookup_genl_family(PSAMPLE_GENL_NAME,\n+                                &psample_family);\n+    if (err) {\n+        VLOG_INFO(\"%s: Generic Netlink family '%s' does not exist. \"\n+                  \"Please make sure the kernel module psample is loaded. \"\n+                  \"Error %d\", __func__, PSAMPLE_GENL_NAME, err);\n+        return;\n+    }\n+\n+    err = nl_lookup_genl_mcgroup(PSAMPLE_GENL_NAME,\n+                                 PSAMPLE_NL_MCGRP_SAMPLE_NAME,\n+                                 &psample_mcgroup);\n+    if (err) {\n+        VLOG_INFO(\"%s: Failed to join multicast group '%s' for Generic \"\n+                  \"Netlink family '%s' with error %d\", __func__,\n+                  PSAMPLE_NL_MCGRP_SAMPLE_NAME,\n+                  PSAMPLE_GENL_NAME, err);\n+        return;\n+    }\n+\n+    err = nl_sock_create(NETLINK_GENERIC, &psample_sock);\n+    if (err) {\n+        VLOG_INFO(\"%s: Failed to create psample socket with error %d\",\n+                  __func__, err);\n+        return;\n+    }\n+\n+    err = nl_sock_join_mcgroup(psample_sock, psample_mcgroup);\n+    if (err) {\n+        VLOG_INFO(\"%s: Failed to join psample mcgroup with error %d\",\n+                  __func__, err);\n+        nl_sock_destroy(psample_sock);\n+        return;\n+    }\n+}\n+\n+static void\n+dpif_offload_netlink_destroy(void)\n+{\n+    if (!psample_sock) {\n+        return;\n+    }\n+\n+    nl_sock_destroy(psample_sock);\n+    psample_sock = NULL;\n+}\n+\n+static void\n+dpif_offload_netlink_sflow_recv_wait(void)\n+{\n+    if (psample_sock) {\n+        nl_sock_wait(psample_sock, POLLIN);\n+    }\n+}\n+\n+static int\n+psample_from_ofpbuf(struct offload_psample *psample,\n+                    const struct ofpbuf *buf)\n+{\n+    static const struct nl_policy ovs_psample_policy[] = {\n+        [PSAMPLE_ATTR_IIFINDEX] = { .type = NL_A_U16 },\n+        [PSAMPLE_ATTR_SAMPLE_GROUP] = { .type = NL_A_U32 },\n+        [PSAMPLE_ATTR_GROUP_SEQ] = { .type = NL_A_U32 },\n+        [PSAMPLE_ATTR_DATA] = { .type = NL_A_UNSPEC },\n+    };\n+    struct nlattr *a[ARRAY_SIZE(ovs_psample_policy)];\n+    struct genlmsghdr *genl;\n+    struct nlmsghdr *nlmsg;\n+    struct ofpbuf b;\n+\n+    b = ofpbuf_const_initializer(buf->data, buf->size);\n+    nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);\n+    genl = ofpbuf_try_pull(&b, sizeof *genl);\n+    if (!nlmsg || !genl || nlmsg->nlmsg_type != psample_family\n+        || !nl_policy_parse(&b, 0, ovs_psample_policy, a,\n+                            ARRAY_SIZE(ovs_psample_policy))) {\n+        return EINVAL;\n+    }\n+\n+    psample->iifindex = nl_attr_get_u16(a[PSAMPLE_ATTR_IIFINDEX]);\n+    psample->dp_group_id = nl_attr_get_u32(a[PSAMPLE_ATTR_SAMPLE_GROUP]);\n+    psample->packet = a[PSAMPLE_ATTR_DATA];\n+\n+    return 0;\n+}\n+\n+static int\n+psample_parse_packet(struct offload_psample *psample,\n+                     struct dpif_offload_sflow *sflow)\n+{\n+    dp_packet_use_stub(&sflow->packet,\n+                       CONST_CAST(struct nlattr *,\n+                                  nl_attr_get(psample->packet)) - 1,\n+                       nl_attr_get_size(psample->packet) +\n+                       sizeof(struct nlattr));\n+    dp_packet_set_data(&sflow->packet,\n+                       (char *) dp_packet_data(&sflow->packet) +\n+                       sizeof(struct nlattr));\n+    dp_packet_set_size(&sflow->packet, nl_attr_get_size(psample->packet));\n+\n+    sflow->attr = dpif_offload_sflow_attr_find(psample->dp_group_id);\n+    if (!sflow->attr) {\n+        return ENOENT;\n+    }\n+    sflow->iifindex = psample->iifindex;\n+\n+    return 0;\n+}\n+\n+static int\n+dpif_offload_netlink_sflow_recv(struct dpif_offload_sflow *sflow)\n+{\n+    if (!psample_sock) {\n+        return ENOENT;\n+    }\n+\n+    for (;;) {\n+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n+        struct offload_psample psample;\n+        uint64_t buf_stub[4096 / 8];\n+        struct ofpbuf buf;\n+        int error;\n+\n+        ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);\n+        error = nl_sock_recv(psample_sock, &buf, NULL, false);\n+\n+        if (!error) {\n+            error = psample_from_ofpbuf(&psample, &buf);\n+            if (!error) {\n+                    ofpbuf_uninit(&buf);\n+                    error = psample_parse_packet(&psample, sflow);\n+                    return error;\n+            }\n+        } else if (error != EAGAIN) {\n+            VLOG_WARN_RL(&rl, \"%s: error reading or parsing netlink (%s)\",\n+                         __func__, ovs_strerror(error));\n+            nl_sock_drain(psample_sock);\n+            error = ENOBUFS;\n+        }\n+\n+        ofpbuf_uninit(&buf);\n+        if (error) {\n+            return error;\n+        }\n+    }\n+}\n+\n+const struct dpif_offload_api dpif_offload_netlink = {\n+    .init = dpif_offload_netlink_init,\n+    .destroy = dpif_offload_netlink_destroy,\n+    .sflow_recv_wait = dpif_offload_netlink_sflow_recv_wait,\n+    .sflow_recv = dpif_offload_netlink_sflow_recv,\n+};\ndiff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h\nindex 95c29c0d8..db46209e1 100644\n--- a/lib/dpif-offload-provider.h\n+++ b/lib/dpif-offload-provider.h\n@@ -17,12 +17,12 @@\n #ifndef DPIF_OFFLOAD_PROVIDER_H\n #define DPIF_OFFLOAD_PROVIDER_H\n \n+#include \"dp-packet.h\"\n #include \"netlink-protocol.h\"\n #include \"openvswitch/packets.h\"\n #include \"openvswitch/types.h\"\n \n struct dpif;\n-struct dpif_offload_sflow;\n \n /* When offloading sample action, userspace creates a unique ID to map\n  * sFlow action and tunnel info and passes this ID to datapath instead\n@@ -37,6 +37,13 @@ struct dpif_sflow_attr {\n     ovs_u128 ufid;                  /* Flow ufid. */\n };\n \n+/* Parse the specific dpif message to sFlow. So OVS can process it. */\n+struct dpif_offload_sflow {\n+    struct dp_packet packet;            /* Packet data. */\n+    uint32_t iifindex;                  /* Input ifindex. */\n+    const struct dpif_sflow_attr *attr; /* SFlow attribute. */\n+};\n+\n /* Datapath interface offload structure, to be defined by each implementation\n  * of a datapath interface.\n  */\n@@ -61,4 +68,8 @@ void dpif_offload_sflow_recv_wait(const struct dpif *dpif);\n int dpif_offload_sflow_recv(const struct dpif *dpif,\n                             struct dpif_offload_sflow *sflow);\n \n+#ifdef __linux__\n+extern const struct dpif_offload_api dpif_offload_netlink;\n+#endif\n+\n #endif /* DPIF_OFFLOAD_PROVIDER_H */\n",
    "prefixes": [