get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 1555104,
    "url": "http://patchwork.ozlabs.org/api/patches/1555104/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/20211115025312.786953-8-cmi@nvidia.com/",
    "project": {
        "id": 47,
        "url": "http://patchwork.ozlabs.org/api/projects/47/?format=api",
        "name": "Open vSwitch",
        "link_name": "openvswitch",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "git@github.com:openvswitch/ovs.git",
        "webscm_url": "https://github.com/openvswitch/ovs",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20211115025312.786953-8-cmi@nvidia.com>",
    "list_archive_url": null,
    "date": "2021-11-15T02:53:11",
    "name": "[ovs-dev,v18,7/8] netdev-offload-tc: Add offload support for sFlow",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "2e79b3e6c04dc41b747476949898874fff0c6bfb",
    "submitter": {
        "id": 80086,
        "url": "http://patchwork.ozlabs.org/api/people/80086/?format=api",
        "name": "Chris Mi",
        "email": "cmi@nvidia.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/20211115025312.786953-8-cmi@nvidia.com/mbox/",
    "series": [
        {
            "id": 271987,
            "url": "http://patchwork.ozlabs.org/api/series/271987/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/openvswitch/list/?series=271987",
            "date": "2021-11-15T02:53:04",
            "name": "Add offload support for sFlow",
            "version": 18,
            "mbox": "http://patchwork.ozlabs.org/series/271987/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1555104/comments/",
    "check": "success",
    "checks": "http://patchwork.ozlabs.org/api/patches/1555104/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "bilbo.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=i2MnT69S;\n\tdkim-atps=neutral",
            "ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN>)",
            "smtp2.osuosl.org (amavisd-new);\n dkim=pass (2048-bit key) header.d=nvidia.com"
        ],
        "Received": [
            "from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])\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 bilbo.ozlabs.org (Postfix) with ESMTPS id 4Hsv1H2fnFz9s5P\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 15 Nov 2021 13:54:43 +1100 (AEDT)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp4.osuosl.org (Postfix) with ESMTP id F14D84035F;\n\tMon, 15 Nov 2021 02:54:40 +0000 (UTC)",
            "from smtp4.osuosl.org ([127.0.0.1])\n\tby localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id EyNNxY9R4GHT; Mon, 15 Nov 2021 02:54:39 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp4.osuosl.org (Postfix) with ESMTPS id 9AD3140338;\n\tMon, 15 Nov 2021 02:54:38 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 7E8E7C002F;\n\tMon, 15 Nov 2021 02:54:38 +0000 (UTC)",
            "from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 7E387C0012\n for <dev@openvswitch.org>; Mon, 15 Nov 2021 02:54:37 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp2.osuosl.org (Postfix) with ESMTP id D455F40323\n for <dev@openvswitch.org>; Mon, 15 Nov 2021 02:53:48 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id WjqOGsod5kGY for <dev@openvswitch.org>;\n Mon, 15 Nov 2021 02:53:45 +0000 (UTC)",
            "from NAM11-CO1-obe.outbound.protection.outlook.com\n (mail-co1nam11on2062a.outbound.protection.outlook.com\n [IPv6:2a01:111:f400:7eab::62a])\n by smtp2.osuosl.org (Postfix) with ESMTPS id 87337402A0\n for <dev@openvswitch.org>; Mon, 15 Nov 2021 02:53:45 +0000 (UTC)",
            "from BN9PR03CA0281.namprd03.prod.outlook.com (2603:10b6:408:f5::16)\n by MN2PR12MB3613.namprd12.prod.outlook.com (2603:10b6:208:c1::17)\n with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4690.18; Mon, 15 Nov\n 2021 02:53:43 +0000",
            "from BN8NAM11FT058.eop-nam11.prod.protection.outlook.com\n (2603:10b6:408:f5:cafe::ac) by BN9PR03CA0281.outlook.office365.com\n (2603:10b6:408:f5::16) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4690.27 via Frontend\n Transport; Mon, 15 Nov 2021 02:53:43 +0000",
            "from mail.nvidia.com (216.228.112.32) by\n BN8NAM11FT058.mail.protection.outlook.com (10.13.177.58) with Microsoft SMTP\n Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id\n 15.20.4690.15 via Frontend Transport; Mon, 15 Nov 2021 02:53:43 +0000",
            "from HQMAIL105.nvidia.com (172.20.187.12) by HQMAIL109.nvidia.com\n (172.20.187.15) with Microsoft SMTP Server (TLS) id 15.0.1497.18; Sun, 14 Nov\n 2021 18:53:31 -0800",
            "from HQMAIL101.nvidia.com (172.20.187.10) by HQMAIL105.nvidia.com\n (172.20.187.12) with Microsoft SMTP Server (TLS) id 15.0.1497.18; Mon, 15 Nov\n 2021 02:53:30 +0000",
            "from dev-r630-03.lab.mtl.com (172.20.187.5) by mail.nvidia.com\n (172.20.187.10) with Microsoft SMTP Server id 15.0.1497.18 via Frontend\n Transport; Mon, 15 Nov 2021 02:53:29 +0000"
        ],
        "X-Virus-Scanned": [
            "amavisd-new at osuosl.org",
            "amavisd-new at osuosl.org"
        ],
        "X-Greylist": "whitelisted by SQLgrey-1.8.0",
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=jpIwPdgLeLJANsKR8SekgAY9racGx9sYjiSpxxUE8QMWDTL/5DLfdqVNCXHKJEddfkFbPfYkEwDWHOy3zuPhiS9PuNFBr2Ig0AmQ7Qg0WsivoSscnQlus4nF9DTWnxSmg7GdV3Vl+QDdPkDzzGccEn7/8Ylm2HD8DQFf4LIiFbHuzOzCjlgibDLgZ5IjbAAcdUfsVAWc/6kCQGjBzHtHmVUq2OjcI4r0QwdhLBodngl+t8bCB/J0GF92V5bZwpVh3GpVl8HNZuoucz1KB28wTznTL/OREPFeOV+87RjBZqowbePrOBUEgOn0BZ8GXQ4ZvyE/Wl6RAwHqEkFHjmE7Ng==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector9901;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=vX3b5XdySOQiveajMwiihsGtepCgU/NcoV9oAvmOryM=;\n b=Z3PZrIZAb8OJzD1g+QaqEmxeVzPpG5tuQoNIanYsFvadSuCb8MMC7rtZQ3zmpH8Tjc44mOdjagkgcomJ2aUsAQhNdvR+6iIrzWeIaIvVLB6XfLaACALtuKGLQXO1IuT6S37w937AWreqmUIoeNoFGS7C/M4wXBTAkX7cP6njifl5wdS9eKige2WuURPSBDpuZXbtiPSaAbZhgSnT3dlHvoAJrkB2Ic2dEfJLrZzEfK7I2wBjsQo+Ze3LpTK2cjKe5hF/J0BNaKE9CiNLGdktqHpGEu++KV7FdRbylk4RFg05VO27Kyik0lkFan5CJ8aj+UQhOey/SJr/I+Pya94uVQ==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=pass (sender ip is\n 216.228.112.32) smtp.rcpttodomain=netronome.com smtp.mailfrom=nvidia.com;\n dmarc=pass (p=quarantine sp=quarantine pct=100) action=none\n header.from=nvidia.com; dkim=none (message not signed); arc=none",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com;\n s=selector2;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=vX3b5XdySOQiveajMwiihsGtepCgU/NcoV9oAvmOryM=;\n b=i2MnT69SIE6rXOTtRlL7fsxtZ2rCzJgs+8If0cxrJw4sIAqiIMAby9WxlXBVvtIZhC3GVw49ZtnHaEM07Zn1Hbt92QKwCmHjZeynLRYhQx5bYkKUqvYoUD+Jc/nkZ81eVgOhsGS+A1QYv8zPFLgehMssyoFq5g4SjZeuM26a3WV3wGnBRtUpsN9rjkD9I9UuNj5cWftpddEOT29+6ZpwUDzV08q2TtpF3BXlV4oEtHQb3CjVLJ0V3ixGYcJhzX2sAIgBRPOkRNVVlcrZkzW6yujZDTgiTbtak+u9Usl38ZGBsSZ12ptdSmExXBdhTTDuC/2dxya2p+VsFg4I/tZLAw==",
        "X-MS-Exchange-Authentication-Results": "spf=pass (sender IP is 216.228.112.32)\n smtp.mailfrom=nvidia.com; dkim=none (message not signed)\n header.d=none;dmarc=pass action=none header.from=nvidia.com;",
        "Received-SPF": "Pass (protection.outlook.com: domain of nvidia.com designates\n 216.228.112.32 as permitted sender) receiver=protection.outlook.com;\n client-ip=216.228.112.32; helo=mail.nvidia.com;",
        "To": "<dev@openvswitch.org>",
        "Date": "Mon, 15 Nov 2021 10:53:11 +0800",
        "Message-ID": "<20211115025312.786953-8-cmi@nvidia.com>",
        "X-Mailer": "git-send-email 2.30.2",
        "In-Reply-To": "<20211115025312.786953-1-cmi@nvidia.com>",
        "References": "<20211115025312.786953-1-cmi@nvidia.com>",
        "MIME-Version": "1.0",
        "X-EOPAttributedMessage": "0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-Office365-Filtering-Correlation-Id": "a92446d3-0f07-4299-0272-08d9a7e322cb",
        "X-MS-TrafficTypeDiagnostic": "MN2PR12MB3613:",
        "X-Microsoft-Antispam-PRVS": "\n <MN2PR12MB3613AE934628544A092D2E34CF989@MN2PR12MB3613.namprd12.prod.outlook.com>",
        "X-MS-Oob-TLC-OOBClassifiers": "OLM:130;",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n sNiiH8xVckw6G6UI0JoHZMAEh+EZ7NhDR3ZPerMrGKNcJ1Nnw50VtgKMjK8WjJCu7Ns8P6P5wOEum9dIOJF+8Uxff8zziBO3gdvJsISw6EE1zCgM+DztizTg7qeXq6e1wOZmy1TmEDz0wY5LRkgwcrICg+EQrQoLJ187m041E4Oq25SqfGmCzRhmJr6ITBBWC06YNBvjwO3S2Go3wAN7mlwWxztL352sFEcMQDHgn2Caqot9CkqH5c0ZzYpxXyHp+bWLWw0pBBoHtvmFkBWnRIzcoGE+byu/GcS8L6iWoT73yG2/yc7FIHgGsQtyhcDCI7hMjpa1SSWATyr8vfF3vwKU6b/LK1oX2bsyBdKUHljB91VjKLcy0igq9wnKbrEZaLMvUekfulR8rlZ9+1mawwrGjjSb21sLbYoiA3X3NkTdakz91llt8pKZCsuYJrzUhGmfDg2azJS9q5/ql51fgSadECe297PSv/4baQoajAww2wtW4Vz1IPkToPV5i62hYS1BKxVT475ISPvhW5x7DYuwdyJCxwId+cvOR0VcQ5lnUhhdpDbtpSQ9eqxVp4E7hQgEvtnouGSUE5Dbqo7m8I1EnnARKU9Xxd7WRCA90fTfwxMXHERlbPwqBUjOKhNek+eQwKrx9FwW6YPcTzv/vi1FHCUx/omaU4fs0Lo6K25nRR9zSdyhmKlOYXZ+YYSWU+BjG1pHRtAd9DhehqOIQg==",
        "X-Forefront-Antispam-Report": "CIP:216.228.112.32; CTRY:US; LANG:en; SCL:1;\n SRV:;\n IPV:NLI; SFV:NSPM; H:mail.nvidia.com; PTR:schybrid01.nvidia.com; CAT:NONE;\n SFS:(4636009)(36840700001)(46966006)(2906002)(508600001)(4326008)(70206006)(336012)(8936002)(86362001)(8676002)(54906003)(107886003)(316002)(5660300002)(6916009)(30864003)(1076003)(83380400001)(2616005)(36860700001)(356005)(26005)(426003)(6666004)(47076005)(82310400003)(7636003)(186003)(70586007)(36756003);\n DIR:OUT; SFP:1101;",
        "X-OriginatorOrg": "Nvidia.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "15 Nov 2021 02:53:43.0401 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n a92446d3-0f07-4299-0272-08d9a7e322cb",
        "X-MS-Exchange-CrossTenant-Id": "43083d15-7273-40c1-b7db-39efd9ccc17a",
        "X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp": "\n TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a; Ip=[216.228.112.32];\n Helo=[mail.nvidia.com]",
        "X-MS-Exchange-CrossTenant-AuthSource": "\n BN8NAM11FT058.eop-nam11.prod.protection.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "MN2PR12MB3613",
        "Cc": "elibr@nvidia.com, simon.horman@netronome.com, roniba@nvidia.com,\n i.maximets@ovn.org",
        "Subject": "[ovs-dev] [PATCH v18 7/8] netdev-offload-tc: Add offload support\n\tfor sFlow",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "From": "Chris Mi via dev <ovs-dev@openvswitch.org>",
        "Reply-To": "Chris Mi <cmi@nvidia.com>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "Create a unique group ID to map the sFlow info when offloading sFlow\naction to TC. When showing the offloaded datapath flows, translate the\ngroup ID from TC sample action to sFlow info using the mapping.\n\nSigned-off-by: Chris Mi <cmi@nvidia.com>\nReviewed-by: Eli Britstein <elibr@nvidia.com>\nAcked-by: Eelco Chaudron <echaudro@redhat.com>\n---\n NEWS                        |   1 +\n lib/dpif-offload-netlink.c  |   6 +\n lib/dpif-offload-provider.h |   2 +\n lib/netdev-offload-tc.c     | 228 +++++++++++++++++++++++++++++++++---\n lib/tc.c                    |  61 +++++++++-\n lib/tc.h                    |  15 ++-\n 6 files changed, 291 insertions(+), 22 deletions(-)",
    "diff": "diff --git a/NEWS b/NEWS\nindex 434ee570f..a88f614ba 100644\n--- a/NEWS\n+++ b/NEWS\n@@ -16,6 +16,7 @@ Post-v2.16.0\n    - ovs-dpctl and 'ovs-appctl dpctl/':\n      * New commands 'cache-get-size' and 'cache-set-size' that allows to\n        get or configure linux kernel datapath cache sizes.\n+   - Add sFlow offload support for kernel (netlink) datapath.\n \n \n v2.16.0 - 16 Aug 2021\ndiff --git a/lib/dpif-offload-netlink.c b/lib/dpif-offload-netlink.c\nindex 320363bbc..1a45a0b11 100644\n--- a/lib/dpif-offload-netlink.c\n+++ b/lib/dpif-offload-netlink.c\n@@ -220,3 +220,9 @@ const struct dpif_offload_class dpif_offload_netlink_class = {\n     .sflow_recv_wait = dpif_offload_netlink_sflow_recv_wait,\n     .sflow_recv = dpif_offload_netlink_sflow_recv,\n };\n+\n+bool\n+dpif_offload_netlink_psample_supported(void)\n+{\n+    return psample_sock != NULL;\n+}\ndiff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h\nindex 4ef50266d..7e8fcfeb8 100644\n--- a/lib/dpif-offload-provider.h\n+++ b/lib/dpif-offload-provider.h\n@@ -89,4 +89,6 @@ 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+bool dpif_offload_netlink_psample_supported(void);\n+\n #endif /* DPIF_OFFLOAD_PROVIDER_H */\ndiff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c\nindex 204d1c833..51140adc9 100644\n--- a/lib/netdev-offload-tc.c\n+++ b/lib/netdev-offload-tc.c\n@@ -20,6 +20,7 @@\n #include <linux/if_ether.h>\n \n #include \"dpif.h\"\n+#include \"dpif-offload-provider.h\"\n #include \"hash.h\"\n #include \"openvswitch/hmap.h\"\n #include \"openvswitch/match.h\"\n@@ -1052,6 +1053,19 @@ parse_tc_flower_to_match(struct tc_flower *flower,\n         action = flower->actions;\n         for (i = 0; i < flower->action_count; i++, action++) {\n             switch (action->type) {\n+            case TC_ACT_SAMPLE: {\n+                const struct sgid_node *node;\n+\n+                node = sgid_find(action->sample.group_id);\n+                if (!node) {\n+                    VLOG_WARN(\"Can't find sample group ID data for ID: %u\",\n+                              action->sample.group_id);\n+                    return ENOENT;\n+                }\n+                nl_msg_put(buf, node->sflow.action,\n+                           node->sflow.action->nla_len);\n+            }\n+            break;\n             case TC_ACT_VLAN_POP: {\n                 nl_msg_put_flag(buf, OVS_ACTION_ATTR_POP_VLAN);\n             }\n@@ -1790,6 +1804,151 @@ parse_match_ct_state_to_flower(struct tc_flower *flower, struct match *match)\n     }\n }\n \n+static int\n+parse_userspace_attributes(const struct nlattr *actions,\n+                           struct dpif_sflow_attr *sflow_attr)\n+{\n+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);\n+    const struct nlattr *nla;\n+    unsigned int left;\n+\n+    NL_NESTED_FOR_EACH_UNSAFE (nla, left, actions) {\n+        if (nl_attr_type(nla) == OVS_USERSPACE_ATTR_USERDATA) {\n+            struct user_action_cookie *cookie;\n+\n+            cookie = CONST_CAST(struct user_action_cookie *, nl_attr_get(nla));\n+            if (cookie->type == USER_ACTION_COOKIE_SFLOW) {\n+                sflow_attr->userdata = nla;\n+                return 0;\n+            }\n+        }\n+    }\n+\n+    VLOG_DBG_RL(&rl, \"Can't offload userspace action other than sFlow\");\n+    return EOPNOTSUPP;\n+}\n+\n+static int\n+parse_sample_actions_attribute(const struct nlattr *actions,\n+                               struct dpif_sflow_attr *sflow_attr)\n+{\n+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);\n+    const struct nlattr *nla;\n+    unsigned int left;\n+    int err = EINVAL;\n+\n+    NL_NESTED_FOR_EACH_UNSAFE (nla, left, actions) {\n+        if (nl_attr_type(nla) == OVS_ACTION_ATTR_USERSPACE) {\n+            err = parse_userspace_attributes(nla, sflow_attr);\n+        } else {\n+            /* We can't offload other nested actions */\n+            VLOG_DBG_RL(&rl, \"Can only offload OVS_ACTION_ATTR_USERSPACE\"\n+                             \" attribute\");\n+            return EINVAL;\n+        }\n+    }\n+\n+    if (err) {\n+        VLOG_ERR_RL(&error_rl, \"No OVS_ACTION_ATTR_USERSPACE attribute\");\n+    }\n+    return err;\n+}\n+\n+static int\n+parse_sample_action(struct tc_flower *flower, struct tc_action *tc_action,\n+                    const struct nlattr *sample_action,\n+                    const struct flow_tnl *tnl, uint32_t *group_id,\n+                    const ovs_u128 *ufid)\n+{\n+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);\n+    struct dpif_sflow_attr sflow_attr;\n+    const struct nlattr *nla;\n+    unsigned int left;\n+    int ret = EINVAL;\n+\n+    if (*group_id) {\n+        VLOG_ERR_RL(&error_rl,\n+                    \"Only a single TC_SAMPLE action per flow is supported\");\n+        return EOPNOTSUPP;\n+    }\n+\n+    memset(&sflow_attr, 0, sizeof sflow_attr);\n+    sflow_attr.ufid = *ufid;\n+    sflow_attr.action = sample_action;\n+\n+    if (flower->tunnel) {\n+        sflow_attr.tunnel = CONST_CAST(struct flow_tnl *, tnl);\n+    }\n+\n+    NL_NESTED_FOR_EACH_UNSAFE (nla, left, sample_action) {\n+        if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_ACTIONS) {\n+            ret = parse_sample_actions_attribute(nla, &sflow_attr);\n+        } else if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_PROBABILITY) {\n+            tc_action->type = TC_ACT_SAMPLE;\n+            tc_action->sample.rate = UINT32_MAX / nl_attr_get_u32(nla);\n+        } else {\n+            return EINVAL;\n+        }\n+    }\n+\n+    if (!tc_action->sample.rate || ret) {\n+        return EINVAL;\n+    }\n+\n+    *group_id = sgid_alloc_ctx(&sflow_attr);\n+    if (!*group_id) {\n+        VLOG_DBG_RL(&rl, \"Failed allocating group id for sample action\");\n+        return ENOENT;\n+    }\n+    tc_action->sample.group_id = *group_id;\n+    flower->action_count++;\n+\n+    return 0;\n+}\n+\n+static int\n+parse_userspace_action(struct tc_flower *flower, struct tc_action *tc_action,\n+                       const struct nlattr *userspace_action,\n+                       const struct flow_tnl *tnl, uint32_t *group_id,\n+                       const ovs_u128 *ufid)\n+{\n+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);\n+    struct dpif_sflow_attr sflow_attr;\n+    int err;\n+\n+    if (*group_id) {\n+        VLOG_ERR_RL(&error_rl,\n+                    \"Only a single TC_SAMPLE action per flow is supported\");\n+        return EOPNOTSUPP;\n+    }\n+\n+    /* If sampling rate is 1, there is only a sFlow cookie inside of a\n+     * userspace action, but no sample attribute. That means we can\n+     * only offload userspace actions for sFlow.\n+     */\n+    memset(&sflow_attr, 0, sizeof sflow_attr);\n+    sflow_attr.ufid = *ufid;\n+    if (flower->tunnel) {\n+        sflow_attr.tunnel = CONST_CAST(struct flow_tnl *, tnl);\n+    }\n+    err = parse_userspace_attributes(userspace_action, &sflow_attr);\n+    if (err) {\n+        return err;\n+    }\n+    sflow_attr.action = userspace_action;\n+    *group_id = sgid_alloc_ctx(&sflow_attr);\n+    if (!*group_id) {\n+        VLOG_DBG_RL(&rl, \"Failed allocating group id for sample action\");\n+        return ENOENT;\n+    }\n+    tc_action->type = TC_ACT_SAMPLE;\n+    tc_action->sample.group_id = *group_id;\n+    tc_action->sample.rate = 1;\n+    flower->action_count++;\n+\n+    return 0;\n+}\n+\n static int\n netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n                    struct nlattr *actions, size_t actions_len,\n@@ -1805,6 +1964,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n     const struct flow_tnl *tnl_mask = &mask->tunnel;\n     struct tc_action *action;\n     bool recirc_act = false;\n+    uint32_t sample_gid = 0;\n     uint32_t block_id = 0;\n     struct nlattr *nla;\n     struct tcf_id id;\n@@ -2057,7 +2217,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n     NL_ATTR_FOR_EACH(nla, left, actions, actions_len) {\n         if (flower.action_count >= TCA_ACT_MAX_NUM) {\n             VLOG_DBG_RL(&rl, \"Can only support %d actions\", TCA_ACT_MAX_NUM);\n-            return EOPNOTSUPP;\n+            err = EOPNOTSUPP;\n+            goto out;\n         }\n         action = &flower.actions[flower.action_count];\n         if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {\n@@ -2067,7 +2228,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n \n             if (!outdev) {\n                 VLOG_DBG_RL(&rl, \"Can't find netdev for output port %d\", port);\n-                return ENODEV;\n+                err = ENODEV;\n+                goto out;\n             }\n             action->out.ifindex_out = netdev_get_ifindex(outdev);\n             action->out.ingress = is_internal_port(netdev_get_type(outdev));\n@@ -2105,7 +2267,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n \n             err = parse_put_flow_set_action(&flower, action, set, set_len);\n             if (err) {\n-                return err;\n+                goto out;\n             }\n             if (action->type == TC_ACT_ENCAP) {\n                 action->encap.tp_dst = info->tp_dst_port;\n@@ -2118,7 +2280,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n             err = parse_put_flow_set_masked_action(&flower, action, set,\n                                                    set_len, true);\n             if (err) {\n-                return err;\n+                goto out;\n             }\n         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT) {\n             const struct nlattr *ct = nl_attr_get(nla);\n@@ -2130,7 +2292,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n \n             err = parse_put_flow_ct_action(&flower, action, ct, ct_len);\n             if (err) {\n-                return err;\n+                goto out;\n             }\n         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT_CLEAR) {\n             action->type = TC_ACT_CT;\n@@ -2146,20 +2308,41 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n             action->chain = 0;  /* 0 is reserved and not used by recirc. */\n             flower.action_count++;\n         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SAMPLE) {\n-            struct dpif_sflow_attr sflow_attr;\n-\n-            memset(&sflow_attr, 0, sizeof sflow_attr);\n-            sgid_alloc_ctx(&sflow_attr);\n+            if (!dpif_offload_netlink_psample_supported()) {\n+                VLOG_DBG_RL(&rl, \"Unsupported put action type: %d, psample is \"\n+                            \"not initialized successfully\", nl_attr_type(nla));\n+                err = EOPNOTSUPP;\n+                goto out;\n+            }\n+            err = parse_sample_action(&flower, action, nla, tnl, &sample_gid,\n+                                      ufid);\n+            if (err) {\n+                goto out;\n+            }\n+        } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_USERSPACE) {\n+            if (!dpif_offload_netlink_psample_supported()) {\n+                VLOG_DBG_RL(&rl, \"Unsupported put action type: %d, psample is \"\n+                            \"not initialized successfully\", nl_attr_type(nla));\n+                err = EOPNOTSUPP;\n+                goto out;\n+            }\n+            err = parse_userspace_action(&flower, action, nla, tnl,\n+                                         &sample_gid, ufid);\n+            if (err) {\n+                goto out;\n+            }\n         } else {\n             VLOG_DBG_RL(&rl, \"unsupported put action type: %d\",\n                         nl_attr_type(nla));\n-            return EOPNOTSUPP;\n+            err = EOPNOTSUPP;\n+            goto out;\n         }\n     }\n \n     if ((chain || recirc_act) && !info->recirc_id_shared_with_tc) {\n         VLOG_ERR_RL(&error_rl, \"flow_put: recirc_id sharing not supported\");\n-        return EOPNOTSUPP;\n+        err = EOPNOTSUPP;\n+        goto out;\n     }\n \n     if (get_ufid_tc_mapping(ufid, &id) == 0) {\n@@ -2172,20 +2355,29 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,\n     prio = get_prio_for_tc_flower(&flower);\n     if (prio == 0) {\n         VLOG_ERR_RL(&rl, \"couldn't get tc prio: %s\", ovs_strerror(ENOSPC));\n-        return ENOSPC;\n+        err = ENOSPC;\n+        goto out;\n     }\n \n     flower.act_cookie.data = ufid;\n     flower.act_cookie.len = sizeof *ufid;\n \n     block_id = get_block_id_from_netdev(netdev);\n-    id = tc_make_tcf_id_chain(ifindex, block_id, chain, prio, hook);\n+    id = tc_make_tcf_id_all(ifindex, block_id, chain, prio, hook, sample_gid);\n     err = tc_replace_flower(&id, &flower);\n-    if (!err) {\n-        if (stats) {\n-            memset(stats, 0, sizeof *stats);\n-        }\n-        add_ufid_tc_mapping(netdev, ufid, &id);\n+    if (err) {\n+        goto out;\n+    }\n+\n+    if (stats) {\n+        memset(stats, 0, sizeof *stats);\n+    }\n+    add_ufid_tc_mapping(netdev, ufid, &id);\n+    return 0;\n+\n+out:\n+    if (sample_gid) {\n+        sgid_free(sample_gid);\n     }\n \n     return err;\ndiff --git a/lib/tc.c b/lib/tc.c\nindex 38a1dfc0e..a40954168 100644\n--- a/lib/tc.c\n+++ b/lib/tc.c\n@@ -23,14 +23,15 @@\n #include <linux/if_packet.h>\n #include <linux/rtnetlink.h>\n #include <linux/tc_act/tc_csum.h>\n+#include <linux/tc_act/tc_ct.h>\n #include <linux/tc_act/tc_gact.h>\n #include <linux/tc_act/tc_mirred.h>\n #include <linux/tc_act/tc_mpls.h>\n #include <linux/tc_act/tc_pedit.h>\n+#include <linux/tc_act/tc_sample.h>\n #include <linux/tc_act/tc_skbedit.h>\n #include <linux/tc_act/tc_tunnel_key.h>\n #include <linux/tc_act/tc_vlan.h>\n-#include <linux/tc_act/tc_ct.h>\n #include <linux/gen_stats.h>\n #include <net/if.h>\n #include <unistd.h>\n@@ -1341,6 +1342,38 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)\n     return 0;\n }\n \n+static const struct nl_policy sample_policy[] = {\n+    [TCA_SAMPLE_PARMS] = { .type = NL_A_UNSPEC,\n+                           .min_len = sizeof(struct tc_sample),\n+                           .optional = false, },\n+    [TCA_SAMPLE_PSAMPLE_GROUP] = { .type = NL_A_U32,\n+                                   .optional = false, },\n+    [TCA_SAMPLE_RATE] = { .type = NL_A_U32,\n+                          .optional = false, },\n+};\n+\n+static int\n+nl_parse_act_sample(struct nlattr *options, struct tc_flower *flower)\n+{\n+    struct nlattr *sample_attrs[ARRAY_SIZE(sample_policy)];\n+    struct tc_action *action;\n+\n+    if (!nl_parse_nested(options, sample_policy, sample_attrs,\n+                         ARRAY_SIZE(sample_policy))) {\n+        VLOG_ERR_RL(&error_rl, \"Failed to parse sample action options\");\n+        return EPROTO;\n+    }\n+\n+    action = &flower->actions[flower->action_count++];\n+    action->type = TC_ACT_SAMPLE;\n+    action->sample.group_id =\n+        nl_attr_get_u32(sample_attrs[TCA_SAMPLE_PSAMPLE_GROUP]);\n+    action->sample.rate =\n+        nl_attr_get_u32(sample_attrs[TCA_SAMPLE_RATE]);\n+\n+    return 0;\n+}\n+\n static const struct nl_policy mirred_policy[] = {\n     [TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC,\n                            .min_len = sizeof(struct tc_mirred),\n@@ -1749,6 +1782,8 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,\n         /* Added for TC rule only (not in OvS rule) so ignore. */\n     } else if (!strcmp(act_kind, \"ct\")) {\n         nl_parse_act_ct(act_options, flower);\n+    } else if (!strcmp(act_kind, \"sample\")) {\n+        nl_parse_act_sample(act_options, flower);\n     } else {\n         VLOG_ERR_RL(&error_rl, \"unknown tc action kind: %s\", act_kind);\n         err = EINVAL;\n@@ -2375,6 +2410,23 @@ nl_msg_put_act_mirred(struct ofpbuf *request, int ifindex, int action,\n     nl_msg_end_nested(request, offset);\n }\n \n+static void\n+nl_msg_put_act_sample(struct ofpbuf *request, uint32_t rate, uint32_t group_id)\n+{\n+    size_t offset;\n+\n+    nl_msg_put_string(request, TCA_ACT_KIND, \"sample\");\n+    offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);\n+    {\n+        struct tc_sample parm = { .action = TC_ACT_PIPE };\n+\n+        nl_msg_put_unspec(request, TCA_SAMPLE_PARMS, &parm, sizeof parm);\n+        nl_msg_put_u32(request, TCA_SAMPLE_RATE, rate);\n+        nl_msg_put_u32(request, TCA_SAMPLE_PSAMPLE_GROUP, group_id);\n+    }\n+    nl_msg_end_nested(request, offset);\n+}\n+\n static inline void\n nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) {\n     if (ck->len) {\n@@ -2634,6 +2686,13 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)\n                 nl_msg_end_nested(request, act_offset);\n             }\n             break;\n+            case TC_ACT_SAMPLE: {\n+                act_offset = nl_msg_start_nested(request, act_index++);\n+                nl_msg_put_act_sample(request, action->sample.rate,\n+                                      action->sample.group_id);\n+                nl_msg_end_nested(request, act_offset);\n+            }\n+            break;\n             case TC_ACT_OUTPUT: {\n                 if (!released && flower->tunnel) {\n                     act_offset = nl_msg_start_nested(request, act_index++);\ndiff --git a/lib/tc.h b/lib/tc.h\nindex 2e4084f48..f764d7d1e 100644\n--- a/lib/tc.h\n+++ b/lib/tc.h\n@@ -174,6 +174,7 @@ enum tc_action_type {\n     TC_ACT_MPLS_SET,\n     TC_ACT_GOTO,\n     TC_ACT_CT,\n+    TC_ACT_SAMPLE,\n };\n \n enum nat_type {\n@@ -256,6 +257,11 @@ struct tc_action {\n             bool force;\n             bool commit;\n         } ct;\n+\n+        struct {\n+            uint32_t rate;\n+            uint32_t group_id;\n+        } sample;\n      };\n \n      enum tc_action_type type;\n@@ -294,12 +300,14 @@ tc_make_tcf_id(int ifindex, uint32_t block_id, uint16_t prio,\n }\n \n static inline struct tcf_id\n-tc_make_tcf_id_chain(int ifindex, uint32_t block_id, uint32_t chain,\n-                     uint16_t prio, enum tc_qdisc_hook hook)\n+tc_make_tcf_id_all(int ifindex, uint32_t block_id, uint32_t chain,\n+                     uint16_t prio, enum tc_qdisc_hook hook,\n+                     uint32_t sample_group_id)\n {\n     struct tcf_id id = tc_make_tcf_id(ifindex, block_id, prio, hook);\n \n     id.chain = chain;\n+    id.sample_group_id = sample_group_id;\n \n     return id;\n }\n@@ -313,7 +321,8 @@ is_tcf_id_eq(struct tcf_id *id1, struct tcf_id *id2)\n            && id1->hook == id2->hook\n            && id1->block_id == id2->block_id\n            && id1->ifindex == id2->ifindex\n-           && id1->chain == id2->chain;\n+           && id1->chain == id2->chain\n+           && id1->sample_group_id == id2->sample_group_id;\n }\n \n enum tc_offload_policy {\n",
    "prefixes": [
        "ovs-dev",
        "v18",
        "7/8"
    ]
}