get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 77053,
    "url": "http://patches.dpdk.org/api/patches/77053/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200909135656.18892-5-hyonkim@cisco.com/",
    "project": {
        "id": 1,
        "url": "http://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": "<20200909135656.18892-5-hyonkim@cisco.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200909135656.18892-5-hyonkim@cisco.com",
    "date": "2020-09-09T13:56:55",
    "name": "[4/5] net/enic: extend flow handler to support VF representors",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "a583b3891bdb78630a00b03107a19cd2e239be73",
    "submitter": {
        "id": 948,
        "url": "http://patches.dpdk.org/api/people/948/?format=api",
        "name": "Hyong Youb Kim (hyonkim)",
        "email": "hyonkim@cisco.com"
    },
    "delegate": {
        "id": 319,
        "url": "http://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20200909135656.18892-5-hyonkim@cisco.com/mbox/",
    "series": [
        {
            "id": 12057,
            "url": "http://patches.dpdk.org/api/series/12057/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=12057",
            "date": "2020-09-09T13:56:51",
            "name": "net/enic: add SR-IOV VF representor",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/12057/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/77053/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/77053/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 dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 15964A04B1;\n\tWed,  9 Sep 2020 15:58:11 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id DC0241C0CA;\n\tWed,  9 Sep 2020 15:58:10 +0200 (CEST)",
            "from alln-iport-6.cisco.com (alln-iport-6.cisco.com [173.37.142.93])\n by dpdk.org (Postfix) with ESMTP id E0AF31C0CA\n for <dev@dpdk.org>; Wed,  9 Sep 2020 15:58:08 +0200 (CEST)",
            "from rcdn-core-10.cisco.com ([173.37.93.146])\n by alln-iport-6.cisco.com with ESMTP/TLS/DHE-RSA-SEED-SHA;\n 09 Sep 2020 13:58:07 +0000",
            "from cisco.com (savbu-usnic-a.cisco.com [10.193.184.48])\n by rcdn-core-10.cisco.com (8.15.2/8.15.2) with ESMTP id 089Dw3pL017271;\n Wed, 9 Sep 2020 13:58:06 GMT",
            "by cisco.com (Postfix, from userid 508933)\n id 8D39A20F2005; Wed,  9 Sep 2020 06:58:02 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=cisco.com; i=@cisco.com; l=28180; q=dns/txt;\n s=iport; t=1599659889; x=1600869489;\n h=from:to:cc:subject:date:message-id:in-reply-to:\n references:mime-version:content-transfer-encoding;\n bh=pn/2E9lU05sawgu88jbaShVRJrShsPDS45icAOqIiMY=;\n b=IsQKbZGrj7A+qEh4Vv7QDkMJtEyO4qTjDHbxYnVbSB+7YNV2rZF8AFXz\n OOks9fLmOXj6X9T7GPVADPbCzMC5NjsGojnrZwXcMfsLs8aWCThIgNBf6\n Zft9QmNQ/Hv8H53f2/B+8oJr2RaZndiuhUE6Sp9WW9sKhaPOaKiW2n6mt g=;",
        "X-IronPort-AV": "E=Sophos;i=\"5.76,409,1592870400\"; d=\"scan'208\";a=\"572557674\"",
        "From": "Hyong Youb Kim <hyonkim@cisco.com>",
        "To": "Ferruh Yigit <ferruh.yigit@intel.com>",
        "Cc": "dev@dpdk.org, Hyong Youb Kim <hyonkim@cisco.com>,\n John Daley <johndale@cisco.com>",
        "Date": "Wed,  9 Sep 2020 06:56:55 -0700",
        "Message-Id": "<20200909135656.18892-5-hyonkim@cisco.com>",
        "X-Mailer": "git-send-email 2.26.2",
        "In-Reply-To": "<20200909135656.18892-1-hyonkim@cisco.com>",
        "References": "<20200909135656.18892-1-hyonkim@cisco.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-Outbound-SMTP-Client": "10.193.184.48, savbu-usnic-a.cisco.com",
        "X-Outbound-Node": "rcdn-core-10.cisco.com",
        "Subject": "[dpdk-dev] [PATCH 4/5] net/enic: extend flow handler to support VF\n\trepresentors",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "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": "VF representor ports can create flows on VFs through the PF flowman\n(Flow Manager) instance in the firmware. These flows match packets\negressing from VFs and apply flowman actions.\n\n1. Make flow handler aware of VF representors\nWhen a representor port invokes flow APIs, use the PF port's flowman\ninstance to perform flowman devcmd. If the port ID refers to a\nrepresentor, use VF handle instead of PF handle.\n\n2. Serialize flow API calls\nMultiple application thread may invoke flow APIs through PF and VF\nrepresentor ports simultaneously. This leads to races, as ports all\nshare the same PF flowman instance. Use a lock to serialize API\ncalls. Lock is used only when representors exist.\n\n3. Add functions to create flows for implicit representor paths\nThere is an implicit path between VF and its representor. The\nfunctions below create flow rules to implement that path.\n- enic_fm_add_rep2vf_flow()\n- enic_fm_add_vf2rep_flow()\n\nThe flows created for representor paths are marked as internal. These\nare not visible to application, and the flush API does not destroy\nthem. They are automatically deleted when the representor port stops\n(enic_fm_destroy).\n\nSigned-off-by: Hyong Youb Kim <hyonkim@cisco.com>\nReviewed-by: John Daley <johndale@cisco.com>\n---\n drivers/net/enic/enic.h         |   8 +\n drivers/net/enic/enic_fm_flow.c | 432 ++++++++++++++++++++++++++++----\n 2 files changed, 396 insertions(+), 44 deletions(-)",
    "diff": "diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h\nindex d51781d8c..9b25e6aa4 100644\n--- a/drivers/net/enic/enic.h\n+++ b/drivers/net/enic/enic.h\n@@ -101,6 +101,7 @@ struct rte_flow {\n \tstruct filter_v2 enic_filter;\n \t/* Data for flow manager based flow (enic_fm_flow.c) */\n \tstruct enic_fm_flow *fm;\n+\tint internal;\n };\n \n /* Per-instance private data structure */\n@@ -210,6 +211,8 @@ struct enic {\n \n \t/* Flow manager API */\n \tstruct enic_flowman *fm;\n+\tuint64_t fm_vnic_handle;\n+\tuint32_t fm_vnic_uif;\n \t/* switchdev */\n \tuint8_t switchdev_mode;\n \tuint16_t switch_domain_id;\n@@ -241,6 +244,9 @@ struct enic_vf_representor {\n \tuint16_t pf_wq_cq_idx;   /* CQ for WQ */\n \tuint16_t pf_rq_sop_idx;  /* SOP RQ dedicated to VF rep */\n \tuint16_t pf_rq_data_idx; /* Data RQ */\n+\t/* Representor flows managed by flowman */\n+\tstruct rte_flow *vf2rep_flow[2];\n+\tstruct rte_flow *rep2vf_flow[2];\n };\n \n #define VF_ENIC_TO_VF_REP(vf_enic) \\\n@@ -467,6 +473,8 @@ void enic_fdir_info_get(struct enic *enic, struct rte_eth_fdir_info *stats);\n int enic_vf_representor_init(struct rte_eth_dev *eth_dev, void *init_params);\n int enic_vf_representor_uninit(struct rte_eth_dev *ethdev);\n int enic_fm_allocate_switch_domain(struct enic *pf);\n+int enic_fm_add_rep2vf_flow(struct enic_vf_representor *vf);\n+int enic_fm_add_vf2rep_flow(struct enic_vf_representor *vf);\n int enic_alloc_rx_queue_mbufs(struct enic *enic, struct vnic_rq *rq);\n void enic_rxmbuf_queue_release(struct enic *enic, struct vnic_rq *rq);\n void enic_free_wq_buf(struct rte_mbuf **buf);\ndiff --git a/drivers/net/enic/enic_fm_flow.c b/drivers/net/enic/enic_fm_flow.c\nindex 49eaefdec..e299b3247 100644\n--- a/drivers/net/enic/enic_fm_flow.c\n+++ b/drivers/net/enic/enic_fm_flow.c\n@@ -34,6 +34,15 @@\n \n #define FM_INVALID_HANDLE 0\n \n+/* Low priority used for implicit VF -> representor flow */\n+#define FM_LOWEST_PRIORITY 100000\n+\n+/* High priority used for implicit representor -> VF flow */\n+#define FM_HIGHEST_PRIORITY 0\n+\n+/* Tag used for implicit VF <-> representor flows */\n+#define FM_VF_REP_TAG 1\n+\n /*\n  * Flow exact match tables (FET) in the VIC and rte_flow groups.\n  * Use a simple scheme to map groups to tables.\n@@ -110,8 +119,20 @@ union enic_flowman_cmd_mem {\n \tstruct fm_action fm_action;\n };\n \n+/*\n+ * PF has a flowman instance, and VF representors share it with PF.\n+ * PF allocates this structure and owns it. VF representors borrow\n+ * the PF's structure during API calls (e.g. create, query).\n+ */\n struct enic_flowman {\n-\tstruct enic *enic;\n+\tstruct enic *owner_enic; /* PF */\n+\tstruct enic *user_enic;  /* API caller (PF or representor) */\n+\t/*\n+\t * Representors and PF share the same underlying flowman.\n+\t * Lock API calls to serialize accesses from them. Only used\n+\t * when VF representors are present.\n+\t */\n+\trte_spinlock_t lock;\n \t/* Command buffer */\n \tstruct {\n \t\tunion enic_flowman_cmd_mem *va;\n@@ -143,9 +164,20 @@ struct enic_flowman {\n \tstruct fm_action action;\n \tstruct fm_action action_tmp; /* enic_fm_reorder_action_op */\n \tint action_op_count;\n+\t/* Tags used for representor flows */\n+\tuint8_t vf_rep_tag;\n };\n \n static int enic_fm_tbl_free(struct enic_flowman *fm, uint64_t handle);\n+/*\n+ * API functions (create, destroy, validate, flush) call begin_fm()\n+ * upon entering to save the caller enic (PF or VF representor) and\n+ * lock. Upon exit, they call end_fm() to unlock.\n+ */\n+static struct enic_flowman *begin_fm(struct enic *enic);\n+static void end_fm(struct enic_flowman *fm);\n+/* Delete internal flows created for representor paths */\n+static void delete_rep_flows(struct enic *enic);\n \n /*\n  * Common arguments passed to copy_item functions. Use this structure\n@@ -627,6 +659,12 @@ enic_fm_copy_item_raw(struct copy_item_args *arg)\n \treturn 0;\n }\n \n+static int\n+flowman_cmd(struct enic_flowman *fm, uint64_t *args, int nargs)\n+{\n+\treturn vnic_dev_flowman_cmd(fm->owner_enic->vdev, args, nargs);\n+}\n+\n static int\n enic_fet_alloc(struct enic_flowman *fm, uint8_t ingress,\n \t       struct fm_key_template *key, int entries,\n@@ -665,7 +703,7 @@ enic_fet_alloc(struct enic_flowman *fm, uint8_t ingress,\n \n \targs[0] = FM_EXACT_TABLE_ALLOC;\n \targs[1] = fm->cmd.pa;\n-\tret = vnic_dev_flowman_cmd(fm->enic->vdev, args, 2);\n+\tret = flowman_cmd(fm, args, 2);\n \tif (ret) {\n \t\tENICPMD_LOG(ERR, \"cannot alloc exact match table: rc=%d\", ret);\n \t\tfree(fet);\n@@ -1096,6 +1134,7 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \t\tCOUNT = 1 << 3,\n \t\tENCAP = 1 << 4,\n \t\tPUSH_VLAN = 1 << 5,\n+\t\tPORT_ID = 1 << 6,\n \t};\n \tstruct fm_tcam_match_entry *fmt;\n \tstruct fm_action_op fm_op;\n@@ -1105,6 +1144,7 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \tuint64_t vnic_h;\n \tuint16_t ovlan;\n \tbool first_rq;\n+\tbool steer;\n \tint ret;\n \n \tENICPMD_FUNC_TRACE();\n@@ -1112,9 +1152,11 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \tneed_ovlan_action = false;\n \tovlan = 0;\n \tfirst_rq = true;\n-\tenic = fm->enic;\n+\tsteer = false;\n+\tenic = fm->user_enic;\n \toverlap = 0;\n-\tvnic_h = 0; /* 0 = current vNIC */\n+\tvnic_h = enic->fm_vnic_handle;\n+\n \tfor (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {\n \t\tswitch (actions->type) {\n \t\tcase RTE_FLOW_ACTION_TYPE_VOID:\n@@ -1195,6 +1237,7 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \t\t\t\treturn ret;\n \t\t\tENICPMD_LOG(DEBUG, \"create QUEUE action rq: %u\",\n \t\t\t\t    fm_op.rq_steer.rq_index);\n+\t\t\tsteer = true;\n \t\t\tbreak;\n \t\t}\n \t\tcase RTE_FLOW_ACTION_TYPE_DROP: {\n@@ -1261,16 +1304,16 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \t\t\t\treturn ret;\n \t\t\tENICPMD_LOG(DEBUG, \"create QUEUE action rq: %u\",\n \t\t\t\t    fm_op.rq_steer.rq_index);\n+\t\t\tsteer = true;\n \t\t\tbreak;\n \t\t}\n \t\tcase RTE_FLOW_ACTION_TYPE_PORT_ID: {\n \t\t\tconst struct rte_flow_action_port_id *port;\n-\t\t\tstruct rte_pci_device *pdev;\n \t\t\tstruct rte_eth_dev *dev;\n \n \t\t\tport = actions->conf;\n \t\t\tif (port->original) {\n-\t\t\t\tvnic_h = 0; /* This port */\n+\t\t\t\tvnic_h = enic->fm_vnic_handle; /* This port */\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\tENICPMD_LOG(DEBUG, \"port id %u\", port->id);\n@@ -1285,12 +1328,18 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \t\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION,\n \t\t\t\t\tNULL, \"port_id is not enic\");\n \t\t\t}\n-\t\t\tpdev = RTE_ETH_DEV_TO_PCI(dev);\n-\t\t\tif (enic_fm_find_vnic(enic, &pdev->addr, &vnic_h)) {\n+\t\t\tif (enic->switch_domain_id !=\n+\t\t\t    pmd_priv(dev)->switch_domain_id) {\n \t\t\t\treturn rte_flow_error_set(error, EINVAL,\n \t\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION,\n-\t\t\t\t\tNULL, \"port_id is not vnic\");\n+\t\t\t\t\tNULL, \"destination and source ports are not in the same switch domain\");\n \t\t\t}\n+\t\t\tvnic_h = pmd_priv(dev)->fm_vnic_handle;\n+\t\t\toverlap |= PORT_ID;\n+\t\t\t/*\n+\t\t\t * Ingress. Nothing more to do. We add an implicit\n+\t\t\t * steer at the end if needed.\n+\t\t\t */\n \t\t\tbreak;\n \t\t}\n \t\tcase RTE_FLOW_ACTION_TYPE_VXLAN_DECAP: {\n@@ -1366,8 +1415,16 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \t\t}\n \t}\n \n-\tif (!(overlap & (FATE | PASSTHRU | COUNT)))\n+\tif (!(overlap & (FATE | PASSTHRU | COUNT | PORT_ID)))\n \t\tgoto unsupported;\n+\t/* Egress from VF: need implicit WQ match */\n+\tif (enic_is_vf_rep(enic) && !ingress) {\n+\t\tfmt->ftm_data.fk_wq_id = 0;\n+\t\tfmt->ftm_mask.fk_wq_id = 0xffff;\n+\t\tfmt->ftm_data.fk_wq_vnic = enic->fm_vnic_handle;\n+\t\tENICPMD_LOG(DEBUG, \"add implicit wq id match for vf %d\",\n+\t\t\t    VF_ENIC_TO_VF_REP(enic)->vf_id);\n+\t}\n \tif (need_ovlan_action) {\n \t\tmemset(&fm_op, 0, sizeof(fm_op));\n \t\tfm_op.fa_op = FMOP_SET_OVLAN;\n@@ -1376,6 +1433,19 @@ enic_fm_copy_action(struct enic_flowman *fm,\n \t\tif (ret)\n \t\t\treturn ret;\n \t}\n+\t/* Add steer op for PORT_ID without QUEUE */\n+\tif ((overlap & PORT_ID) && !steer && ingress) {\n+\t\tmemset(&fm_op, 0, sizeof(fm_op));\n+\t\t/* Always to queue 0 for now as generic RSS is not available */\n+\t\tfm_op.fa_op = FMOP_RQ_STEER;\n+\t\tfm_op.rq_steer.rq_index = 0;\n+\t\tfm_op.rq_steer.vnic_handle = vnic_h;\n+\t\tret = enic_fm_append_action_op(fm, &fm_op, error);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tENICPMD_LOG(DEBUG, \"add implicit steer op\");\n+\t}\n+\t/* Add required END */\n \tmemset(&fm_op, 0, sizeof(fm_op));\n \tfm_op.fa_op = FMOP_END;\n \tret = enic_fm_append_action_op(fm, &fm_op, error);\n@@ -1618,7 +1688,7 @@ enic_fm_flow_parse(struct enic_flowman *fm,\n \t\t\t\t\t   NULL,\n \t\t\t\t\t   \"priorities are not supported\");\n \t\t\treturn -rte_errno;\n-\t\t} else if (attrs->transfer) {\n+\t\t} else if (!fm->owner_enic->switchdev_mode && attrs->transfer) {\n \t\t\trte_flow_error_set(error, ENOTSUP,\n \t\t\t\t\t   RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,\n \t\t\t\t\t   NULL,\n@@ -1675,12 +1745,10 @@ enic_fm_more_counters(struct enic_flowman *fm)\n {\n \tstruct enic_fm_counter *new_stack;\n \tstruct enic_fm_counter *ctrs;\n-\tstruct enic *enic;\n \tint i, rc;\n \tuint64_t args[2];\n \n \tENICPMD_FUNC_TRACE();\n-\tenic = fm->enic;\n \tnew_stack = rte_realloc(fm->counter_stack, (fm->counters_alloced +\n \t\t\t\tFM_COUNTERS_EXPAND) *\n \t\t\t\tsizeof(struct enic_fm_counter), 0);\n@@ -1692,7 +1760,7 @@ enic_fm_more_counters(struct enic_flowman *fm)\n \n \targs[0] = FM_COUNTER_BRK;\n \targs[1] = fm->counters_alloced + FM_COUNTERS_EXPAND;\n-\trc = vnic_dev_flowman_cmd(enic->vdev, args, 2);\n+\trc = flowman_cmd(fm, args, 2);\n \tif (rc != 0) {\n \t\tENICPMD_LOG(ERR, \"cannot alloc counters rc=%d\", rc);\n \t\treturn rc;\n@@ -1712,16 +1780,14 @@ enic_fm_more_counters(struct enic_flowman *fm)\n static int\n enic_fm_counter_zero(struct enic_flowman *fm, struct enic_fm_counter *c)\n {\n-\tstruct enic *enic;\n \tuint64_t args[3];\n \tint ret;\n \n \tENICPMD_FUNC_TRACE();\n-\tenic = fm->enic;\n \targs[0] = FM_COUNTER_QUERY;\n \targs[1] = c->handle;\n \targs[2] = 1; /* clear */\n-\tret = vnic_dev_flowman_cmd(enic->vdev, args, 3);\n+\tret = flowman_cmd(fm, args, 3);\n \tif (ret) {\n \t\tENICPMD_LOG(ERR, \"counter init: rc=%d handle=0x%x\",\n \t\t\t    ret, c->handle);\n@@ -1761,7 +1827,7 @@ enic_fm_action_free(struct enic_flowman *fm, uint64_t handle)\n \tENICPMD_FUNC_TRACE();\n \targs[0] = FM_ACTION_FREE;\n \targs[1] = handle;\n-\trc = vnic_dev_flowman_cmd(fm->enic->vdev, args, 2);\n+\trc = flowman_cmd(fm, args, 2);\n \tif (rc)\n \t\tENICPMD_LOG(ERR, \"cannot free action: rc=%d handle=0x%\" PRIx64,\n \t\t\t    rc, handle);\n@@ -1777,7 +1843,7 @@ enic_fm_entry_free(struct enic_flowman *fm, uint64_t handle)\n \tENICPMD_FUNC_TRACE();\n \targs[0] = FM_MATCH_ENTRY_REMOVE;\n \targs[1] = handle;\n-\trc = vnic_dev_flowman_cmd(fm->enic->vdev, args, 2);\n+\trc = flowman_cmd(fm, args, 2);\n \tif (rc)\n \t\tENICPMD_LOG(ERR, \"cannot free match entry: rc=%d\"\n \t\t\t    \" handle=0x%\" PRIx64, rc, handle);\n@@ -1881,7 +1947,7 @@ enic_fm_add_tcam_entry(struct enic_flowman *fm,\n \targs[0] = FM_TCAM_ENTRY_INSTALL;\n \targs[1] = ingress ? fm->ig_tcam_hndl : fm->eg_tcam_hndl;\n \targs[2] = fm->cmd.pa;\n-\tret = vnic_dev_flowman_cmd(fm->enic->vdev, args, 3);\n+\tret = flowman_cmd(fm, args, 3);\n \tif (ret != 0) {\n \t\tENICPMD_LOG(ERR, \"cannot add %s TCAM entry: rc=%d\",\n \t\t\t    ingress ? \"ingress\" : \"egress\", ret);\n@@ -1931,7 +1997,7 @@ enic_fm_add_exact_entry(struct enic_flowman *fm,\n \targs[0] = FM_EXACT_ENTRY_INSTALL;\n \targs[1] = fet->handle;\n \targs[2] = fm->cmd.pa;\n-\tret = vnic_dev_flowman_cmd(fm->enic->vdev, args, 3);\n+\tret = flowman_cmd(fm, args, 3);\n \tif (ret != 0) {\n \t\tENICPMD_LOG(ERR, \"cannot add %s exact entry: group=%u\",\n \t\t\t    fet->ingress ? \"ingress\" : \"egress\", fet->group);\n@@ -1970,7 +2036,7 @@ __enic_fm_flow_add_entry(struct enic_flowman *fm,\n \tmemcpy(fma, action_in, sizeof(*fma));\n \targs[0] = FM_ACTION_ALLOC;\n \targs[1] = fm->cmd.pa;\n-\tret = vnic_dev_flowman_cmd(fm->enic->vdev, args, 2);\n+\tret = flowman_cmd(fm, args, 2);\n \tif (ret != 0) {\n \t\tENICPMD_LOG(ERR, \"allocating TCAM table action rc=%d\", ret);\n \t\trte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,\n@@ -2140,7 +2206,7 @@ enic_fm_flow_validate(struct rte_eth_dev *dev,\n \tint ret;\n \n \tENICPMD_FUNC_TRACE();\n-\tfm = pmd_priv(dev)->fm;\n+\tfm = begin_fm(pmd_priv(dev));\n \tif (fm == NULL)\n \t\treturn -ENOTSUP;\n \tenic_fm_open_scratch(fm);\n@@ -2152,6 +2218,7 @@ enic_fm_flow_validate(struct rte_eth_dev *dev,\n \t\t\t\t\tattrs->ingress);\n \t}\n \tenic_fm_close_scratch(fm);\n+\tend_fm(fm);\n \treturn ret;\n }\n \n@@ -2162,33 +2229,38 @@ enic_fm_flow_query_count(struct rte_eth_dev *dev,\n {\n \tstruct rte_flow_query_count *query;\n \tstruct enic_fm_flow *fm_flow;\n-\tstruct enic *enic;\n+\tstruct enic_flowman *fm;\n \tuint64_t args[3];\n \tint rc;\n \n \tENICPMD_FUNC_TRACE();\n-\tenic = pmd_priv(dev);\n+\tfm = begin_fm(pmd_priv(dev));\n \tquery = data;\n \tfm_flow = flow->fm;\n-\tif (!fm_flow->counter_valid)\n-\t\treturn rte_flow_error_set(error, ENOTSUP,\n+\tif (!fm_flow->counter_valid) {\n+\t\trc = rte_flow_error_set(error, ENOTSUP,\n \t\t\tRTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,\n \t\t\t\"enic: flow does not have counter\");\n+\t\tgoto exit;\n+\t}\n \n \targs[0] = FM_COUNTER_QUERY;\n \targs[1] = fm_flow->counter->handle;\n \targs[2] = query->reset;\n-\trc = vnic_dev_flowman_cmd(enic->vdev, args, 3);\n+\trc = flowman_cmd(fm, args, 3);\n \tif (rc) {\n \t\tENICPMD_LOG(ERR, \"cannot query counter: rc=%d handle=0x%x\",\n \t\t\t    rc, fm_flow->counter->handle);\n-\t\treturn rc;\n+\t\tgoto exit;\n \t}\n \tquery->hits_set = 1;\n \tquery->hits = args[0];\n \tquery->bytes_set = 1;\n \tquery->bytes = args[1];\n-\treturn 0;\n+\trc = 0;\n+exit:\n+\tend_fm(fm);\n+\treturn rc;\n }\n \n static int\n@@ -2237,7 +2309,7 @@ enic_fm_flow_create(struct rte_eth_dev *dev,\n \n \tENICPMD_FUNC_TRACE();\n \tenic = pmd_priv(dev);\n-\tfm = enic->fm;\n+\tfm = begin_fm(enic);\n \tif (fm == NULL) {\n \t\trte_flow_error_set(error, ENOTSUP,\n \t\t\tRTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,\n@@ -2275,6 +2347,7 @@ enic_fm_flow_create(struct rte_eth_dev *dev,\n \n error_with_scratch:\n \tenic_fm_close_scratch(fm);\n+\tend_fm(fm);\n \treturn flow;\n }\n \n@@ -2283,12 +2356,15 @@ enic_fm_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,\n \t\t     __rte_unused struct rte_flow_error *error)\n {\n \tstruct enic *enic = pmd_priv(dev);\n+\tstruct enic_flowman *fm;\n \n \tENICPMD_FUNC_TRACE();\n-\tif (enic->fm == NULL)\n+\tfm = begin_fm(enic);\n+\tif (fm == NULL)\n \t\treturn 0;\n \tLIST_REMOVE(flow, next);\n-\tenic_fm_flow_free(enic->fm, flow);\n+\tenic_fm_flow_free(fm, flow);\n+\tend_fm(fm);\n \treturn 0;\n }\n \n@@ -2296,19 +2372,27 @@ static int\n enic_fm_flow_flush(struct rte_eth_dev *dev,\n \t\t   __rte_unused struct rte_flow_error *error)\n {\n+\tLIST_HEAD(enic_flows, rte_flow) internal;\n \tstruct enic_fm_flow *fm_flow;\n \tstruct enic_flowman *fm;\n \tstruct rte_flow *flow;\n \tstruct enic *enic = pmd_priv(dev);\n \n \tENICPMD_FUNC_TRACE();\n-\tif (enic->fm == NULL)\n+\n+\tfm = begin_fm(enic);\n+\tif (fm == NULL)\n \t\treturn 0;\n-\tfm = enic->fm;\n+\t/* Destroy all non-internal flows */\n+\tLIST_INIT(&internal);\n \twhile (!LIST_EMPTY(&enic->flows)) {\n \t\tflow = LIST_FIRST(&enic->flows);\n \t\tfm_flow = flow->fm;\n \t\tLIST_REMOVE(flow, next);\n+\t\tif (flow->internal) {\n+\t\t\tLIST_INSERT_HEAD(&internal, flow, next);\n+\t\t\tcontinue;\n+\t\t}\n \t\t/*\n \t\t * If tables are null, then vNIC is closing, and the firmware\n \t\t * has already cleaned up flowman state. So do not try to free\n@@ -2321,6 +2405,12 @@ enic_fm_flow_flush(struct rte_eth_dev *dev,\n \t\t}\n \t\tenic_fm_flow_free(fm, flow);\n \t}\n+\twhile (!LIST_EMPTY(&internal)) {\n+\t\tflow = LIST_FIRST(&internal);\n+\t\tLIST_REMOVE(flow, next);\n+\t\tLIST_INSERT_HEAD(&enic->flows, flow, next);\n+\t}\n+\tend_fm(fm);\n \treturn 0;\n }\n \n@@ -2332,7 +2422,7 @@ enic_fm_tbl_free(struct enic_flowman *fm, uint64_t handle)\n \n \targs[0] = FM_MATCH_TABLE_FREE;\n \targs[1] = handle;\n-\trc = vnic_dev_flowman_cmd(fm->enic->vdev, args, 2);\n+\trc = flowman_cmd(fm, args, 2);\n \tif (rc)\n \t\tENICPMD_LOG(ERR, \"cannot free table: rc=%d handle=0x%\" PRIx64,\n \t\t\t    rc, handle);\n@@ -2344,19 +2434,17 @@ enic_fm_tcam_tbl_alloc(struct enic_flowman *fm, uint32_t direction,\n \t\t\tuint32_t max_entries, uint64_t *handle)\n {\n \tstruct fm_tcam_match_table *tcam_tbl;\n-\tstruct enic *enic;\n \tuint64_t args[2];\n \tint rc;\n \n \tENICPMD_FUNC_TRACE();\n-\tenic = fm->enic;\n \ttcam_tbl = &fm->cmd.va->fm_tcam_match_table;\n \ttcam_tbl->ftt_direction = direction;\n \ttcam_tbl->ftt_stage = FM_STAGE_LAST;\n \ttcam_tbl->ftt_max_entries = max_entries;\n \targs[0] = FM_TCAM_TABLE_ALLOC;\n \targs[1] = fm->cmd.pa;\n-\trc = vnic_dev_flowman_cmd(enic->vdev, args, 2);\n+\trc = flowman_cmd(fm, args, 2);\n \tif (rc) {\n \t\tENICPMD_LOG(ERR, \"cannot alloc %s TCAM table: rc=%d\",\n \t\t\t    (direction == FM_INGRESS) ? \"IG\" : \"EG\", rc);\n@@ -2379,14 +2467,12 @@ enic_fm_init_counters(struct enic_flowman *fm)\n static void\n enic_fm_free_all_counters(struct enic_flowman *fm)\n {\n-\tstruct enic *enic;\n \tuint64_t args[2];\n \tint rc;\n \n-\tenic = fm->enic;\n \targs[0] = FM_COUNTER_BRK;\n \targs[1] = 0;\n-\trc = vnic_dev_flowman_cmd(enic->vdev, args, 2);\n+\trc = flowman_cmd(fm, args, 2);\n \tif (rc != 0)\n \t\tENICPMD_LOG(ERR, \"cannot free counters: rc=%d\", rc);\n \trte_free(fm->counter_stack);\n@@ -2428,6 +2514,7 @@ enic_fm_free_tcam_tables(struct enic_flowman *fm)\n int\n enic_fm_init(struct enic *enic)\n {\n+\tconst struct rte_pci_addr *addr;\n \tstruct enic_flowman *fm;\n \tuint8_t name[RTE_MEMZONE_NAMESIZE];\n \tint rc;\n@@ -2435,12 +2522,30 @@ enic_fm_init(struct enic *enic)\n \tif (enic->flow_filter_mode != FILTER_FLOWMAN)\n \t\treturn 0;\n \tENICPMD_FUNC_TRACE();\n+\t/* Get vnic handle and save for port-id action */\n+\tif (enic_is_vf_rep(enic))\n+\t\taddr = &VF_ENIC_TO_VF_REP(enic)->bdf;\n+\telse\n+\t\taddr = &RTE_ETH_DEV_TO_PCI(enic->rte_dev)->addr;\n+\trc = enic_fm_find_vnic(enic, addr, &enic->fm_vnic_handle);\n+\tif (rc) {\n+\t\tENICPMD_LOG(ERR, \"cannot find vnic handle for %x:%x:%x\",\n+\t\t\t    addr->bus, addr->devid, addr->function);\n+\t\treturn rc;\n+\t}\n+\t/* Save UIF for egport action */\n+\tenic->fm_vnic_uif = vnic_dev_uif(enic->vdev);\n+\tENICPMD_LOG(DEBUG, \"uif %u\", enic->fm_vnic_uif);\n+\t/* Nothing else to do for representor. It will share the PF flowman */\n+\tif (enic_is_vf_rep(enic))\n+\t\treturn 0;\n \tfm = calloc(1, sizeof(*fm));\n \tif (fm == NULL) {\n \t\tENICPMD_LOG(ERR, \"cannot alloc flowman struct\");\n \t\treturn -ENOMEM;\n \t}\n-\tfm->enic = enic;\n+\tfm->owner_enic = enic;\n+\trte_spinlock_init(&fm->lock);\n \tTAILQ_INIT(&fm->fet_list);\n \tTAILQ_INIT(&fm->jump_list);\n \t/* Allocate host memory for flowman commands */\n@@ -2480,6 +2585,7 @@ enic_fm_init(struct enic *enic)\n \t\tgoto error_ig_fet;\n \t}\n \tfm->default_eg_fet->ref = 1;\n+\tfm->vf_rep_tag = FM_VF_REP_TAG;\n \tenic->fm = fm;\n \treturn 0;\n \n@@ -2503,9 +2609,13 @@ enic_fm_destroy(struct enic *enic)\n \tstruct enic_flowman *fm;\n \tstruct enic_fm_fet *fet;\n \n+\tENICPMD_FUNC_TRACE();\n+\tif (enic_is_vf_rep(enic)) {\n+\t\tdelete_rep_flows(enic);\n+\t\treturn;\n+\t}\n \tif (enic->fm == NULL)\n \t\treturn;\n-\tENICPMD_FUNC_TRACE();\n \tfm = enic->fm;\n \tenic_fet_free(fm, fm->default_eg_fet);\n \tenic_fet_free(fm, fm->default_ig_fet);\n@@ -2582,3 +2692,237 @@ const struct rte_flow_ops enic_fm_flow_ops = {\n \t.flush = enic_fm_flow_flush,\n \t.query = enic_fm_flow_query,\n };\n+\n+/* Add a high priority flow that loops representor packets to VF */\n+int\n+enic_fm_add_rep2vf_flow(struct enic_vf_representor *vf)\n+{\n+\tstruct fm_tcam_match_entry *fm_tcam_entry;\n+\tstruct rte_flow *flow0, *flow1;\n+\tstruct fm_action *fm_action;\n+\tstruct rte_flow_error error;\n+\tstruct rte_flow_attr attrs;\n+\tstruct fm_action_op fm_op;\n+\tstruct enic_flowman *fm;\n+\tstruct enic *pf;\n+\tuint8_t tag;\n+\n+\tpf = vf->pf;\n+\tfm = pf->fm;\n+\ttag = fm->vf_rep_tag;\n+\tenic_fm_open_scratch(fm);\n+\tfm_tcam_entry = &fm->tcam_entry;\n+\tfm_action = &fm->action;\n+\t/* Egress rule: match WQ ID and tag+hairpin */\n+\tfm_tcam_entry->ftm_data.fk_wq_id = vf->pf_wq_idx;\n+\tfm_tcam_entry->ftm_mask.fk_wq_id = 0xffff;\n+\tfm_tcam_entry->ftm_flags |= FMEF_COUNTER;\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_TAG;\n+\tfm_op.tag.tag = tag;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_EG_HAIRPIN;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_END;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tattrs.group = 0;\n+\tattrs.ingress = 0;\n+\tattrs.egress = 1;\n+\tattrs.priority = FM_HIGHEST_PRIORITY;\n+\tflow0 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,\n+\t\t\t\t       &attrs, &error);\n+\tenic_fm_close_scratch(fm);\n+\tif (flow0 == NULL) {\n+\t\tENICPMD_LOG(ERR, \"Cannot create flow 0 for representor->VF\");\n+\t\treturn -EINVAL;\n+\t}\n+\tLIST_INSERT_HEAD(&pf->flows, flow0, next);\n+\t/* Make this flow internal, so the user app cannot delete it */\n+\tflow0->internal = 1;\n+\tENICPMD_LOG(DEBUG, \"representor->VF %d flow created: wq %d -> tag %d hairpin\",\n+\t\t    vf->vf_id, vf->pf_wq_idx, tag);\n+\n+\t/* Ingress: steer hairpinned to VF RQ 0 */\n+\tenic_fm_open_scratch(fm);\n+\tfm_tcam_entry->ftm_flags |= FMEF_COUNTER;\n+\tfm_tcam_entry->ftm_data.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;\n+\tfm_tcam_entry->ftm_mask.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;\n+\tfm_tcam_entry->ftm_data.fk_packet_tag = tag;\n+\tfm_tcam_entry->ftm_mask.fk_packet_tag = 0xff;\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_RQ_STEER;\n+\tfm_op.rq_steer.rq_index = 0;\n+\tfm_op.rq_steer.vnic_handle = vf->enic.fm_vnic_handle;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_END;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tattrs.group = 0;\n+\tattrs.ingress = 1;\n+\tattrs.egress = 0;\n+\tattrs.priority = FM_HIGHEST_PRIORITY;\n+\tflow1 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,\n+\t\t\t\t       &attrs, &error);\n+\tenic_fm_close_scratch(fm);\n+\tif (flow1 == NULL) {\n+\t\tENICPMD_LOG(ERR, \"Cannot create flow 1 for representor->VF\");\n+\t\tenic_fm_flow_destroy(pf->rte_dev, flow0, &error);\n+\t\treturn -EINVAL;\n+\t}\n+\tLIST_INSERT_HEAD(&pf->flows, flow1, next);\n+\tflow1->internal = 1;\n+\tENICPMD_LOG(DEBUG, \"representor->VF %d flow created: tag %d hairpinned -> VF RQ %d\",\n+\t\t    vf->vf_id, tag, fm_op.rq_steer.rq_index);\n+\tvf->rep2vf_flow[0] = flow0;\n+\tvf->rep2vf_flow[1] = flow1;\n+\t/* Done with this tag, use a different one next time */\n+\tfm->vf_rep_tag++;\n+\treturn 0;\n+}\n+\n+/*\n+ * Add a low priority flow that matches all packets from VF and loops them\n+ * back to the representor.\n+ */\n+int\n+enic_fm_add_vf2rep_flow(struct enic_vf_representor *vf)\n+{\n+\tstruct fm_tcam_match_entry *fm_tcam_entry;\n+\tstruct rte_flow *flow0, *flow1;\n+\tstruct fm_action *fm_action;\n+\tstruct rte_flow_error error;\n+\tstruct rte_flow_attr attrs;\n+\tstruct fm_action_op fm_op;\n+\tstruct enic_flowman *fm;\n+\tstruct enic *pf;\n+\tuint8_t tag;\n+\n+\tpf = vf->pf;\n+\tfm = pf->fm;\n+\ttag = fm->vf_rep_tag;\n+\tenic_fm_open_scratch(fm);\n+\tfm_tcam_entry = &fm->tcam_entry;\n+\tfm_action = &fm->action;\n+\t/* Egress rule: match-any and tag+hairpin */\n+\tfm_tcam_entry->ftm_data.fk_wq_id = 0;\n+\tfm_tcam_entry->ftm_mask.fk_wq_id = 0xffff;\n+\tfm_tcam_entry->ftm_data.fk_wq_vnic = vf->enic.fm_vnic_handle;\n+\tfm_tcam_entry->ftm_flags |= FMEF_COUNTER;\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_TAG;\n+\tfm_op.tag.tag = tag;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_EG_HAIRPIN;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_END;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tattrs.group = 0;\n+\tattrs.ingress = 0;\n+\tattrs.egress = 1;\n+\tattrs.priority = FM_LOWEST_PRIORITY;\n+\tflow0 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,\n+\t\t\t\t       &attrs, &error);\n+\tenic_fm_close_scratch(fm);\n+\tif (flow0 == NULL) {\n+\t\tENICPMD_LOG(ERR, \"Cannot create flow 0 for VF->representor\");\n+\t\treturn -EINVAL;\n+\t}\n+\tLIST_INSERT_HEAD(&pf->flows, flow0, next);\n+\t/* Make this flow internal, so the user app cannot delete it */\n+\tflow0->internal = 1;\n+\tENICPMD_LOG(DEBUG, \"VF %d->representor flow created: wq %d (low prio) -> tag %d hairpin\",\n+\t\t    vf->vf_id, fm_tcam_entry->ftm_data.fk_wq_id, tag);\n+\n+\t/* Ingress: steer hairpinned to VF rep RQ */\n+\tenic_fm_open_scratch(fm);\n+\tfm_tcam_entry->ftm_flags |= FMEF_COUNTER;\n+\tfm_tcam_entry->ftm_data.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;\n+\tfm_tcam_entry->ftm_mask.fk_hdrset[0].fk_metadata |= FKM_EG_HAIRPINNED;\n+\tfm_tcam_entry->ftm_data.fk_packet_tag = tag;\n+\tfm_tcam_entry->ftm_mask.fk_packet_tag = 0xff;\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_RQ_STEER;\n+\tfm_op.rq_steer.rq_index = vf->pf_rq_sop_idx;\n+\tfm_op.rq_steer.vnic_handle = pf->fm_vnic_handle;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tmemset(&fm_op, 0, sizeof(fm_op));\n+\tfm_op.fa_op = FMOP_END;\n+\tenic_fm_append_action_op(fm, &fm_op, &error);\n+\tattrs.group = 0;\n+\tattrs.ingress = 1;\n+\tattrs.egress = 0;\n+\tattrs.priority = FM_HIGHEST_PRIORITY;\n+\tflow1 = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,\n+\t\t\t\t       &attrs, &error);\n+\tenic_fm_close_scratch(fm);\n+\tif (flow1 == NULL) {\n+\t\tENICPMD_LOG(ERR, \"Cannot create flow 1 for VF->representor\");\n+\t\tenic_fm_flow_destroy(pf->rte_dev, flow0, &error);\n+\t\treturn -EINVAL;\n+\t}\n+\tLIST_INSERT_HEAD(&pf->flows, flow1, next);\n+\tflow1->internal = 1;\n+\tENICPMD_LOG(DEBUG, \"VF %d->representor flow created: tag %d hairpinned -> PF RQ %d\",\n+\t\t    vf->vf_id, tag, vf->pf_rq_sop_idx);\n+\tvf->vf2rep_flow[0] = flow0;\n+\tvf->vf2rep_flow[1] = flow1;\n+\t/* Done with this tag, use a different one next time */\n+\tfm->vf_rep_tag++;\n+\treturn 0;\n+}\n+\n+/* Destroy representor flows created by enic_fm_add_{rep2vf,vf2rep}_flow */\n+static void\n+delete_rep_flows(struct enic *enic)\n+{\n+\tstruct enic_vf_representor *vf;\n+\tstruct rte_flow_error error;\n+\tstruct rte_eth_dev *dev;\n+\tuint32_t i;\n+\n+\tRTE_ASSERT(enic_is_vf_rep(enic));\n+\tvf = VF_ENIC_TO_VF_REP(enic);\n+\tdev = vf->pf->rte_dev;\n+\tfor (i = 0; i < ARRAY_SIZE(vf->vf2rep_flow); i++) {\n+\t\tif (vf->vf2rep_flow[i])\n+\t\t\tenic_fm_flow_destroy(dev, vf->vf2rep_flow[i], &error);\n+\t}\n+\tfor (i = 0; i < ARRAY_SIZE(vf->rep2vf_flow); i++) {\n+\t\tif (vf->rep2vf_flow[i])\n+\t\t\tenic_fm_flow_destroy(dev, vf->rep2vf_flow[i], &error);\n+\t}\n+}\n+\n+static struct enic_flowman *\n+begin_fm(struct enic *enic)\n+{\n+\tstruct enic_vf_representor *vf;\n+\tstruct enic_flowman *fm;\n+\n+\t/* Representor uses PF flowman */\n+\tif (enic_is_vf_rep(enic)) {\n+\t\tvf = VF_ENIC_TO_VF_REP(enic);\n+\t\tfm = vf->pf->fm;\n+\t} else {\n+\t\tfm = enic->fm;\n+\t}\n+\t/* Save the API caller and lock if representors exist */\n+\tif (fm) {\n+\t\tif (fm->owner_enic->switchdev_mode)\n+\t\t\trte_spinlock_lock(&fm->lock);\n+\t\tfm->user_enic = enic;\n+\t}\n+\treturn fm;\n+}\n+\n+static void\n+end_fm(struct enic_flowman *fm)\n+{\n+\tfm->user_enic = NULL;\n+\tif (fm->owner_enic->switchdev_mode)\n+\t\trte_spinlock_unlock(&fm->lock);\n+}\n",
    "prefixes": [
        "4/5"
    ]
}