From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 77865C10DCE for ; Thu, 12 Mar 2020 22:12:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 30917206CD for ; Thu, 12 Mar 2020 22:12:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726894AbgCLWMK convert rfc822-to-8bit (ORCPT ); Thu, 12 Mar 2020 18:12:10 -0400 Received: from mga18.intel.com ([134.134.136.126]:17203 "EHLO mga18.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726513AbgCLWMJ (ORCPT ); Thu, 12 Mar 2020 18:12:09 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga006.fm.intel.com ([10.253.24.20]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 12 Mar 2020 15:12:08 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.70,546,1574150400"; d="scan'208";a="444098219" Received: from vcostago-desk1.jf.intel.com (HELO vcostago-desk1) ([10.54.70.26]) by fmsmga006.fm.intel.com with ESMTP; 12 Mar 2020 15:12:06 -0700 From: Vinicius Costa Gomes To: Po Liu , davem@davemloft.net, linux-kernel@vger.kernel.org, netdev@vger.kernel.org Cc: po.liu@nxp.com, claudiu.manoil@nxp.com, vladimir.oltean@nxp.com, alexandru.marginean@nxp.com, xiaoliang.yang_1@nxp.com, roy.zang@nxp.com, mingkai.hu@nxp.com, jerry.huang@nxp.com, leoyang.li@nxp.com, michael.chan@broadcom.com, vishal@chelsio.com, saeedm@mellanox.com, leon@kernel.org, jiri@mellanox.com, idosch@mellanox.com, alexandre.belloni@bootlin.com, UNGLinuxDriver@microchip.com, kuba@kernel.org, jhs@mojatatu.com, xiyou.wangcong@gmail.com, john.hurley@netronome.com, simon.horman@netronome.com, pieter.jansenvanvuuren@netronome.com, pablo@netfilter.org, moshe@mellanox.com, ivan.khoronzhuk@linaro.org, m-karicheri2@ti.com, andre.guedes@linux.intel.com, jakub.kicinski@netronome.com, Po Liu Subject: Re: [RFC,net-next 5/9] net: enetc: add tc flower psfp offload driver In-Reply-To: <20200306125608.11717-6-Po.Liu@nxp.com> References: <20200306125608.11717-1-Po.Liu@nxp.com> <20200306125608.11717-6-Po.Liu@nxp.com> Date: Thu, 12 Mar 2020 15:14:18 -0700 Message-ID: <87y2s5gr0l.fsf@linux.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Po Liu writes: > This patch is to add tc flower offload for the enetc IEEE 802.1Qci(PSFP) > function. There are four main feature parts to implement the flow > policing and filtering for ingress flow with IEEE 802.1Qci features. > They are stream identify(this is defined in the P802.1cb exactly but > needed for 802.1Qci), stream filtering, stream gate and flow metering. > Each function block includes many entries by index to assign parameters. > So for one frame would be filtered by stream identify first, then > flow into stream filter block by the same handle between stream identify > and stream filtering. Then flow into stream gate control which assigned > by the stream filtering entry. And then policing by the gate and limited > by the max sdu in the filter block(optional). At last, policing by the > flow metering block, index choosing at the fitering block. > So you can see that each entry of block may link to many upper entries > since they can be assigned same index means more streams want to share > the same feature in the stream filtering or stream gate or flow > metering. > To implement such features, each stream filtered by source/destination > mac address, some stream maybe also plus the vlan id value would be > treated as one flow chain. This would be identified by the chain_index > which already in the tc filter concept. Driver would maintain this chain > and also with gate modules. The stream filter entry create by the gate > index and flow meter(optional) entry id and also one priority value. > Offloading only transfer the gate action and flow filtering parameters. > Driver would create (or search same gate id and flow meter id and > priority) one stream filter entry to set to the hardware. So stream > filtering do not need transfer by the action offloading. > This architecture is same with tc filter and actions relationship. tc > filter maintain the list for each flow feature by keys. And actions > maintain by the action list. > > Below showing a example commands by tc: >> tc qdisc add dev eth0 ingress >> ip link set eth0 address 10:00:80:00:00:00 >> tc filter add dev eth0 parent ffff: protocol ip chain 11 \ > flower skip_sw dst_mac 10:00:80:00:00:00 \ > action gate index 10 \ > sched-entry OPEN 200000000 -1 -1 \ > sched-entry CLOSE 100000000 -1 -1 > (or just create one gate entry list to keep close) > > That means to set the dst_mac 10:00:80:00:00:00 to index 11 of stream > identify module. And the set the gate index 10 of stream gate module. > > Signed-off-by: Po Liu > --- > drivers/net/ethernet/freescale/enetc/enetc.c | 25 +- > drivers/net/ethernet/freescale/enetc/enetc.h | 39 +- > .../net/ethernet/freescale/enetc/enetc_hw.h | 142 +++ > .../net/ethernet/freescale/enetc/enetc_pf.c | 4 +- > .../net/ethernet/freescale/enetc/enetc_qos.c | 1078 ++++++++++++++++- > 5 files changed, 1269 insertions(+), 19 deletions(-) > > diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c > index d810651317e1..df2e77619f64 100644 > --- a/drivers/net/ethernet/freescale/enetc/enetc.c > +++ b/drivers/net/ethernet/freescale/enetc/enetc.c > @@ -1520,6 +1520,8 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type, > return enetc_setup_tc_cbs(ndev, type_data); > case TC_SETUP_QDISC_ETF: > return enetc_setup_tc_txtime(ndev, type_data); > + case TC_SETUP_BLOCK: > + return enetc_setup_tc_psfp(ndev, type_data); > default: > return -EOPNOTSUPP; > } > @@ -1572,17 +1574,23 @@ static int enetc_set_rss(struct net_device *ndev, int en) > static int enetc_set_psfp(struct net_device *ndev, int en) > { > struct enetc_ndev_priv *priv = netdev_priv(ndev); > + int err; > > if (en) { > + err = enetc_psfp_enable(priv); > + if (err) > + return err; > + > priv->active_offloads |= ENETC_F_QCI; > - enetc_get_max_cap(priv); > - enetc_psfp_enable(&priv->si->hw); > - } else { > - priv->active_offloads &= ~ENETC_F_QCI; > - memset(&priv->psfp_cap, 0, sizeof(struct psfp_cap)); > - enetc_psfp_disable(&priv->si->hw); > + return 0; > } > > + err = enetc_psfp_disable(priv); > + if (err) > + return err; > + > + priv->active_offloads &= ~ENETC_F_QCI; > + > return 0; > } > > @@ -1590,14 +1598,15 @@ int enetc_set_features(struct net_device *ndev, > netdev_features_t features) > { > netdev_features_t changed = ndev->features ^ features; > + int err = 0; > > if (changed & NETIF_F_RXHASH) > enetc_set_rss(ndev, !!(features & NETIF_F_RXHASH)); > > if (changed & NETIF_F_HW_TC) > - enetc_set_psfp(ndev, !!(features & NETIF_F_HW_TC)); > + err = enetc_set_psfp(ndev, !!(features & NETIF_F_HW_TC)); > > - return 0; > + return err; > } > > #ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING > diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h > index bcdade8f7b8a..f1a9a4cf1914 100644 > --- a/drivers/net/ethernet/freescale/enetc/enetc.h > +++ b/drivers/net/ethernet/freescale/enetc/enetc.h > @@ -269,6 +269,11 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data); > void enetc_sched_speed_set(struct net_device *ndev); > int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data); > int enetc_setup_tc_txtime(struct net_device *ndev, void *type_data); > +int enetc_setup_tc_block_cb(enum tc_setup_type type, void *type_data, > + void *cb_priv); > +int enetc_setup_tc_psfp(struct net_device *ndev, void *type_data); > +int enetc_psfp_init(struct enetc_ndev_priv *priv); > +int enetc_psfp_clean(struct enetc_ndev_priv *priv); > > static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv) > { > @@ -288,33 +293,59 @@ static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv) > priv->psfp_cap.max_psfp_meter = reg & ENETC_PFMCAPR_MSK; > } > > -static inline void enetc_psfp_enable(struct enetc_hw *hw) > +static inline int enetc_psfp_enable(struct enetc_ndev_priv *priv) > { > + struct enetc_hw *hw = &priv->si->hw; > + int err; > + > + enetc_get_max_cap(priv); > + > + err = enetc_psfp_init(priv); > + if (err) > + return err; > + > enetc_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) > | ENETC_PPSFPMR_PSFPEN | ENETC_PPSFPMR_VS > | ENETC_PPSFPMR_PVC | ENETC_PPSFPMR_PVZC); > + > + return 0; > } > > -static inline void enetc_psfp_disable(struct enetc_hw *hw) > +static inline int enetc_psfp_disable(struct enetc_ndev_priv *priv) > { > + struct enetc_hw *hw = &priv->si->hw; > + int err; > + > + err = enetc_psfp_clean(priv); > + if (err) > + return err; > + > enetc_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) > & ~ENETC_PPSFPMR_PSFPEN & ~ENETC_PPSFPMR_VS > & ~ENETC_PPSFPMR_PVC & ~ENETC_PPSFPMR_PVZC); > + > + memset(&priv->psfp_cap, 0, sizeof(struct psfp_cap)); > + > + return 0; > } > + > #else > #define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP > #define enetc_sched_speed_set(ndev) (void)0 > #define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP > #define enetc_setup_tc_txtime(ndev, type_data) -EOPNOTSUPP > +#define enetc_setup_tc_psfp(ndev, type_data) -EOPNOTSUPP > +#define enetc_setup_tc_block_cb NULL > + > #define enetc_get_max_cap(p) \ > memset(&((p)->psfp_cap), 0, sizeof(struct psfp_cap)) > > -static inline void enetc_psfp_enable(struct enetc_hw *hw) > +static inline int enetc_psfp_enable(struct enetc_hw *hw) > { > return 0; > } > > -static inline void enetc_psfp_disable(struct enetc_hw *hw) > +static inline int enetc_psfp_disable(struct enetc_hw *hw) > { > return 0; > } > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h > index 99d520207069..640099f48a0d 100644 > --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h > +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h > @@ -570,6 +570,9 @@ enum bdcr_cmd_class { > BDCR_CMD_RFS, > BDCR_CMD_PORT_GCL, > BDCR_CMD_RECV_CLASSIFIER, > + BDCR_CMD_STREAM_IDENTIFY, > + BDCR_CMD_STREAM_FILTER, > + BDCR_CMD_STREAM_GCL, > __BDCR_CMD_MAX_LEN, > BDCR_CMD_MAX_LEN = __BDCR_CMD_MAX_LEN - 1, > }; > @@ -601,13 +604,152 @@ struct tgs_gcl_data { > struct gce entry[]; > }; > > +/* class 7, command 0, Stream Identity Entry Configuration */ > +struct streamid_conf { > + __le32 stream_handle; /* init gate value */ > + __le32 iports; > + u8 id_type; > + u8 oui[3]; > + u8 res[3]; > + u8 en; > +}; > + > +#define ENETC_CBDR_SID_VID_MASK 0xfff > +#define ENETC_CBDR_SID_VIDM BIT(12) > +#define ENETC_CBDR_SID_TG_MASK 0xc000 > +/* streamid_conf address point to this data space */ > +struct streamid_data { > + union { > + u8 dmac[6]; > + u8 smac[6]; > + }; > + u16 vid_vidm_tg; > +}; > + > +#define ENETC_CBDR_SFI_PRI_MASK 0x7 > +#define ENETC_CBDR_SFI_PRIM BIT(3) > +#define ENETC_CBDR_SFI_BLOV BIT(4) > +#define ENETC_CBDR_SFI_BLEN BIT(5) > +#define ENETC_CBDR_SFI_MSDUEN BIT(6) > +#define ENETC_CBDR_SFI_FMITEN BIT(7) > +#define ENETC_CBDR_SFI_ENABLE BIT(7) > +/* class 8, command 0, Stream Filter Instance, Short Format */ > +struct sfi_conf { > + __le32 stream_handle; > + u8 multi; > + u8 res[2]; > + u8 sthm; > + /* Max Service Data Unit or Flow Meter Instance Table index. > + * Depending on the value of FLT this represents either Max > + * Service Data Unit (max frame size) allowed by the filter > + * entry or is an index into the Flow Meter Instance table > + * index identifying the policer which will be used to police > + * it. > + */ > + __le16 fm_inst_table_index; > + __le16 msdu; > + __le16 sg_inst_table_index; > + u8 res1[2]; > + __le32 input_ports; > + u8 res2[3]; > + u8 en; > +}; > + > +/* class 8, command 2 stream Filter Instance status query short format > + * command no need structure define > + * Stream Filter Instance Query Statistics Response data > + */ > +struct sfi_counter_data { > + u32 matchl; > + u32 matchh; > + u32 msdu_dropl; > + u32 msdu_droph; > + u32 stream_gate_dropl; > + u32 stream_gate_droph; > + u32 flow_meter_dropl; > + u32 flow_meter_droph; > +}; > + > +#define ENETC_CBDR_SGI_OIPV_MASK 0x7 > +#define ENETC_CBDR_SGI_OIPV_EN BIT(3) > +#define ENETC_CBDR_SGI_CGTST BIT(6) > +#define ENETC_CBDR_SGI_OGTST BIT(7) > +#define ENETC_CBDR_SGI_CFG_CHG BIT(1) > +#define ENETC_CBDR_SGI_CFG_PND BIT(2) > +#define ENETC_CBDR_SGI_OEX BIT(4) > +#define ENETC_CBDR_SGI_OEXEN BIT(5) > +#define ENETC_CBDR_SGI_IRX BIT(6) > +#define ENETC_CBDR_SGI_IRXEN BIT(7) > +#define ENETC_CBDR_SGI_ACLLEN_MASK 0x3 > +#define ENETC_CBDR_SGI_OCLLEN_MASK 0xc > +#define ENETC_CBDR_SGI_EN BIT(7) > +/* class 9, command 0, Stream Gate Instance Table, Short Format > + * class 9, command 2, Stream Gate Instance Table entry query write back > + * Short Format > + */ > +struct sgi_table { > + u8 res[8]; > + u8 oipv; > + u8 res0[2]; > + u8 ocgtst; > + u8 res1[7]; > + u8 gset; > + u8 oacl_len; > + u8 res2[2]; > + u8 en; > +}; > + > +#define ENETC_CBDR_SGI_AIPV_MASK 0x7 > +#define ENETC_CBDR_SGI_AIPV_EN BIT(3) > +#define ENETC_CBDR_SGI_AGTST BIT(7) > + > +/* class 9, command 1, Stream Gate Control List, Long Format */ > +struct sgcl_conf { > + u8 aipv; > + u8 res[2]; > + u8 agtst; > + u8 res1[4]; > + union { > + struct { > + u8 res2[4]; > + u8 acl_len; > + u8 res3[3]; > + }; > + u8 cct[8]; /* Config change time */ > + }; > +}; > + > +#define ENETC_CBDR_SGL_IOMEN BIT(0) > +#define ENETC_CBDR_SGL_IPVEN BIT(3) > +#define ENETC_CBDR_SGL_GTST BIT(4) > +#define ENETC_CBDR_SGL_IPV_MASK 0xe > +/* Stream Gate Control List Entry */ > +struct sgce { > + u32 interval; > + u8 msdu[3]; > + u8 multi; > +}; > + > +/* stream control list class 9 , cmd 1 data buffer */ > +struct sgcl_data { > + u32 btl; > + u32 bth; > + u32 ct; > + u32 cte; > + struct sgce sgcl[0]; > +}; > + > struct enetc_cbd { > union{ Wrong identation here. Should be fixed on a separated patch. > + struct sfi_conf sfi_conf; > + struct sgi_table sgi_table; > struct { > __le32 addr[2]; > union { > __le32 opt[4]; > struct tgs_gcl_conf gcl_conf; > + struct streamid_conf sid_set; > + struct sgcl_conf sgcl_conf; > }; > }; /* Long format */ > __le32 data[6]; > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c > index d880cbdc0d2e..a19095ab7b41 100644 > --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c > +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c > @@ -740,12 +740,10 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev, > if (si->hw_features & ENETC_SI_F_QBV) > priv->active_offloads |= ENETC_F_QBV; > > - if (si->hw_features & ENETC_SI_F_PSFP) { > + if (si->hw_features & ENETC_SI_F_PSFP && !enetc_psfp_enable(priv)) { > priv->active_offloads |= ENETC_F_QCI; > ndev->features |= NETIF_F_HW_TC; > ndev->hw_features |= NETIF_F_HW_TC; > - enetc_get_max_cap(priv); > - enetc_psfp_enable(&si->hw); > } > > /* pick up primary MAC address from SI */ > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c > index 0c6bf3a55a9a..3ef46190d71d 100644 > --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c > +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c > @@ -5,6 +5,8 @@ > > #include > #include > +#include > +#include > > static u16 enetc_get_max_gcl_len(struct enetc_hw *hw) > { > @@ -108,13 +110,13 @@ static int enetc_setup_taprio(struct net_device *ndev, > gcl_data->cte = cpu_to_le32(admin_conf->cycle_time_extension); > > for (i = 0; i < gcl_len; i++) { > - struct tc_taprio_sched_entry *temp_entry; > + struct tc_taprio_sched_entry *to; > struct gce *temp_gce = gce + i; > > - temp_entry = &admin_conf->entries[i]; > + to = &admin_conf->entries[i]; > > - temp_gce->gate = (u8)temp_entry->gate_mask; > - temp_gce->period = cpu_to_le32(temp_entry->interval); > + temp_gce->gate = (u8)to->gate_mask; > + temp_gce->period = cpu_to_le32(to->interval); These changes seem unrelated to the series. > } > > cbd.length = cpu_to_le16(data_size); > @@ -331,3 +333,1071 @@ int enetc_setup_tc_txtime(struct net_device *ndev, void *type_data) > > return 0; > } > + > +enum streamid_type { > + STREAMID_TYPE_RESERVED = 0, > + STREAMID_TYPE_NULL, > + STREAMID_TYPE_SMAC, > +}; > + > +enum streamid_vlan_tagged { > + STREAMID_VLAN_RESERVED = 0, > + STREAMID_VLAN_TAGGED, > + STREAMID_VLAN_UNTAGGED, > + STREAMID_VLAN_ALL, > +}; > + > +#define ENETC_PSFP_WILDCARD -1 > +#define HANDLE_OFFSET 100 > + > +enum forward_type { > + FILTER_ACTION_TYPE_PSFP = BIT(0), > + FILTER_ACTION_TYPE_ACL = BIT(1), > + FILTER_ACTION_TYPE_BOTH = GENMASK(1, 0), > +}; > + > +/* This is for limit output type for input actions */ > +struct actions_fwd { > + u64 actions; > + u64 keys; /* include the must needed keys */ > + enum forward_type output; > +}; > + > +struct psfp_streamfilter_counters { > + u64 matching_frames_count; > + u64 passing_frames_count; > + u64 not_passing_frames_count; > + u64 passing_sdu_count; > + u64 not_passing_sdu_count; > + u64 red_frames_count; > +}; > + > +struct enetc_streamid { > + u32 index; > + union { > + u8 src_mac[6]; > + u8 dst_mac[6]; > + }; > + u8 filtertype; > + u16 vid; > + u8 tagged; > + s32 handle; > +}; > + > +struct enetc_psfp_filter { > + u32 index; > + s32 handle; > + s8 prio; > + u32 gate_id; > + s32 meter_id; > + u32 refcount; It may be more appropriate to use instead of rolling your own. > + struct hlist_node node; > +}; > + > +struct enetc_psfp_gate { > + u32 index; > + s8 init_ipv; > + u64 basetime; > + u64 cycletime; > + u64 cycletimext; > + u32 num_entries; > + u32 refcount; > + struct hlist_node node; > + struct action_gate_entry entries[0]; > +}; > + > +struct enetc_stream_filter { > + struct enetc_streamid sid; > + u32 sfi_index; > + u32 sgi_index; > + struct flow_stats stats; > + struct hlist_node node; > +}; > + > +struct enetc_psfp { > + unsigned long dev_bitmap; > + unsigned long *psfp_sfi_bitmap; > + struct hlist_head stream_list; > + struct hlist_head psfp_filter_list; > + struct hlist_head psfp_gate_list; > + spinlock_t psfp_lock; /* spinlock for the struct enetc_psfp r/w */ > +}; > + > +struct actions_fwd enetc_act_fwd[] = { > + { > + BIT(FLOW_ACTION_GATE), > + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS), > + FILTER_ACTION_TYPE_PSFP > + }, > + /* example for ACL actions */ > + { > + BIT(FLOW_ACTION_DROP), > + 0, > + FILTER_ACTION_TYPE_ACL > + } > +}; > + > +static struct enetc_psfp epsfp = { > + .psfp_sfi_bitmap = NULL, > +}; Is it possible to have more than one these controllers in the same system? If it's possible to have more than one, this should be moved to live under a per-device struct. > + > +static LIST_HEAD(enetc_block_cb_list); > + > +static inline int enetc_get_port(struct enetc_ndev_priv *priv) > +{ > + return priv->si->pdev->devfn & 0x7; > +} > + > +/* Stream Identity Entry Set Descriptor */ > +static int enetc_streamid_hw_set(struct enetc_ndev_priv *priv, > + struct enetc_streamid *sid, > + u8 enable) > +{ > + struct enetc_cbd cbd = {.cmd = 0}; > + struct streamid_data *si_data; > + struct streamid_conf *si_conf; > + u16 data_size; > + dma_addr_t dma; > + int err; > + > + if (sid->index >= priv->psfp_cap.max_streamid) > + return -EINVAL; > + > + if (sid->filtertype != STREAMID_TYPE_NULL && > + sid->filtertype != STREAMID_TYPE_SMAC) > + return -EOPNOTSUPP; > + > + /* Disable operation before enable */ > + cbd.index = cpu_to_le16((u16)sid->index); > + cbd.cls = BDCR_CMD_STREAM_IDENTIFY; > + cbd.status_flags = 0; > + > + data_size = sizeof(struct streamid_data); > + si_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); > + cbd.length = cpu_to_le16(data_size); > + > + dma = dma_map_single(&priv->si->pdev->dev, si_data, > + data_size, DMA_FROM_DEVICE); > + if (dma_mapping_error(&priv->si->pdev->dev, dma)) { > + netdev_err(priv->si->ndev, "DMA mapping failed!\n"); > + kfree(si_data); > + return -ENOMEM; > + } > + > + cbd.addr[0] = lower_32_bits(dma); > + cbd.addr[1] = upper_32_bits(dma); > + memset(si_data->dmac, 0xff, ETH_ALEN); > + si_data->vid_vidm_tg = > + cpu_to_le16(ENETC_CBDR_SID_VID_MASK > + + ((0x3 << 14) | ENETC_CBDR_SID_VIDM)); > + > + si_conf = &cbd.sid_set; > + /* Only one port supported for one entry, set itself */ > + si_conf->iports = 1 << enetc_get_port(priv); > + si_conf->id_type = 1; > + si_conf->oui[2] = 0x0; > + si_conf->oui[1] = 0x80; > + si_conf->oui[0] = 0xC2; > + > + err = enetc_send_cmd(priv->si, &cbd); > + if (err) > + return -EINVAL; > + > + if (!enable) { > + kfree(si_data); > + return 0; > + } > + > + /* Enable the entry overwrite again incase space flushed by hardware */ > + memset(&cbd, 0, sizeof(cbd)); > + > + cbd.index = cpu_to_le16((u16)sid->index); > + cbd.cmd = 0; > + cbd.cls = BDCR_CMD_STREAM_IDENTIFY; > + cbd.status_flags = 0; > + > + si_conf->en = 0x80; > + si_conf->stream_handle = cpu_to_le32(sid->handle); > + si_conf->iports = 1 << enetc_get_port(priv); > + si_conf->id_type = sid->filtertype; > + si_conf->oui[2] = 0x0; > + si_conf->oui[1] = 0x80; > + si_conf->oui[0] = 0xC2; > + > + memset(si_data, 0, data_size); > + > + cbd.length = cpu_to_le16(data_size); > + > + cbd.addr[0] = lower_32_bits(dma); > + cbd.addr[1] = upper_32_bits(dma); > + > + /* VIDM default to be 1. > + * VID Match. If set (b1) then the VID must match, otherwise > + * any VID is considered a match. VIDM setting is only used > + * when TG is set to b01. > + */ > + if (si_conf->id_type == STREAMID_TYPE_NULL) { > + ether_addr_copy(si_data->dmac, sid->dst_mac); > + si_data->vid_vidm_tg = > + cpu_to_le16((sid->vid & ENETC_CBDR_SID_VID_MASK) + > + ((((u16)(sid->tagged) & 0x3) << 14) > + | ENETC_CBDR_SID_VIDM)); > + } else if (si_conf->id_type == STREAMID_TYPE_SMAC) { > + ether_addr_copy(si_data->smac, sid->src_mac); > + si_data->vid_vidm_tg = > + cpu_to_le16((sid->vid & ENETC_CBDR_SID_VID_MASK) + > + ((((u16)(sid->tagged) & 0x3) << 14) > + | ENETC_CBDR_SID_VIDM)); > + } > + > + err = enetc_send_cmd(priv->si, &cbd); > + kfree(si_data); > + > + return err; > +} > + > +/* Stream Filter Instance Set Descriptor */ > +static int enetc_streamfilter_hw_set(struct enetc_ndev_priv *priv, > + struct enetc_psfp_filter *sfi, > + u8 enable) > +{ > + struct enetc_cbd cbd = {.cmd = 0}; > + struct sfi_conf *sfi_config; > + > + cbd.index = cpu_to_le16(sfi->index); > + cbd.cls = BDCR_CMD_STREAM_FILTER; > + cbd.status_flags = 0x80; > + cbd.length = cpu_to_le16(1); > + > + sfi_config = &cbd.sfi_conf; > + if (!enable) > + goto exit; > + > + sfi_config->en = 0x80; > + > + if (sfi->handle >= 0) { > + sfi_config->stream_handle = > + cpu_to_le32(sfi->handle); > + sfi_config->sthm |= 0x80; > + } > + > + sfi_config->sg_inst_table_index = cpu_to_le16(sfi->gate_id); > + sfi_config->input_ports = 1 << enetc_get_port(priv); > + > + /* The priority value which may be matched against the > + * frame’s priority value to determine a match for this entry. > + */ > + if (sfi->prio >= 0) > + sfi_config->multi |= (sfi->prio & 0x7) | 0x8; > + > + /* Filter Type. Identifies the contents of the MSDU/FM_INST_INDEX > + * field as being either an MSDU value or an index into the Flow > + * Meter Instance table. > + * TODO: no limit max sdu > + */ > + > + if (sfi->meter_id >= 0) { > + sfi_config->fm_inst_table_index = cpu_to_le16(sfi->meter_id); > + sfi_config->multi |= 0x80; > + } > + > +exit: > + return enetc_send_cmd(priv->si, &cbd); > +} > + > +static int enetc_streamcounter_hw_get(struct enetc_ndev_priv *priv, > + u32 index, > + struct psfp_streamfilter_counters *cnt) > +{ > + struct enetc_cbd cbd = { .cmd = 2 }; > + struct sfi_counter_data *data_buf; > + dma_addr_t dma; > + u16 data_size; > + int err; > + > + cbd.index = cpu_to_le16((u16)index); > + cbd.cmd = 2; > + cbd.cls = BDCR_CMD_STREAM_FILTER; > + cbd.status_flags = 0; > + > + data_size = sizeof(struct sfi_counter_data); > + data_buf = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); > + if (!data_buf) > + return -ENOMEM; > + > + dma = dma_map_single(&priv->si->pdev->dev, data_buf, > + data_size, DMA_FROM_DEVICE); > + if (dma_mapping_error(&priv->si->pdev->dev, dma)) { > + netdev_err(priv->si->ndev, "DMA mapping failed!\n"); > + err = -ENOMEM; > + goto exit; > + } > + cbd.addr[0] = lower_32_bits(dma); > + cbd.addr[1] = upper_32_bits(dma); > + > + cbd.length = cpu_to_le16(data_size); > + > + err = enetc_send_cmd(priv->si, &cbd); > + if (err) > + goto exit; > + > + cnt->matching_frames_count = > + ((u64)le32_to_cpu(data_buf->matchh) << 32) > + + data_buf->matchl; > + > + cnt->not_passing_sdu_count = > + ((u64)le32_to_cpu(data_buf->msdu_droph) << 32) > + + data_buf->msdu_dropl; > + > + cnt->passing_sdu_count = cnt->matching_frames_count > + - cnt->not_passing_sdu_count; > + > + cnt->not_passing_frames_count = > + ((u64)le32_to_cpu(data_buf->stream_gate_droph) << 32) > + + le32_to_cpu(data_buf->stream_gate_dropl); > + > + cnt->passing_frames_count = cnt->matching_frames_count > + - cnt->not_passing_sdu_count > + - cnt->not_passing_frames_count; > + > + cnt->red_frames_count = > + ((u64)le32_to_cpu(data_buf->flow_meter_droph) << 32) > + + le32_to_cpu(data_buf->flow_meter_dropl); > + > +exit: > + kfree(data_buf); > + return err; > +} > + > +static int get_start_ns(struct enetc_ndev_priv *priv, u64 cycle, u64 *start) > +{ > + u64 now_lo, now_hi, now, n; > + > + now_lo = enetc_rd(&priv->si->hw, ENETC_SICTR0); > + now_hi = enetc_rd(&priv->si->hw, ENETC_SICTR1); > + now = now_lo | now_hi << 32; > + > + if (WARN_ON(!cycle)) > + return -EFAULT; > + > + n = div64_u64(now, cycle); > + > + *start = (n + 1) * cycle; > + > + return 0; > +} > + > +/* Stream Gate Instance Set Descriptor */ > +static int enetc_streamgate_hw_set(struct enetc_ndev_priv *priv, > + struct enetc_psfp_gate *sgi, > + u8 enable) > +{ > + struct enetc_cbd cbd = { .cmd = 0 }; > + struct sgi_table *sgi_config; > + struct sgcl_conf *sgcl_config; > + struct sgcl_data *sgcl_data; > + struct sgce *sgce; > + dma_addr_t dma; > + u16 data_size; > + int err, i; > + > + cbd.index = cpu_to_le16(sgi->index); > + cbd.cmd = 0; > + cbd.cls = BDCR_CMD_STREAM_GCL; > + cbd.status_flags = 0x80; > + > + /* disable */ > + if (!enable) > + return enetc_send_cmd(priv->si, &cbd); > + > + /* enable */ > + sgi_config = &cbd.sgi_table; > + > + /* Keep open before gate list start */ > + sgi_config->ocgtst = 0x80; > + > + sgi_config->oipv = (sgi->init_ipv < 0) ? > + 0x0 : ((sgi->init_ipv & 0x7) | 0x8); > + > + sgi_config->en = 0x80; > + > + /* Basic config */ > + err = enetc_send_cmd(priv->si, &cbd); > + if (err) > + return -EINVAL; > + > + if (!sgi->num_entries) > + return 0; > + > + if (sgi->num_entries > priv->psfp_cap.max_psfp_gatelist || > + !sgi->cycletime) > + return -EINVAL; You already check this when "sgi" is created, this check doesn't seem to be needed. If it's needed, it should be moved to the top of the function. > + > + memset(&cbd, 0, sizeof(cbd)); > + > + cbd.index = cpu_to_le16(sgi->index); > + cbd.cmd = 1; > + cbd.cls = BDCR_CMD_STREAM_GCL; > + cbd.status_flags = 0; > + > + sgcl_config = &cbd.sgcl_conf; > + > + sgcl_config->acl_len = (sgi->num_entries - 1) & 0x3; > + > + data_size = struct_size(sgcl_data, sgcl, sgi->num_entries); > + > + sgcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); > + if (!sgcl_data) > + return -ENOMEM; > + > + cbd.length = cpu_to_le16(data_size); > + > + dma = dma_map_single(&priv->si->pdev->dev, > + sgcl_data, data_size, > + DMA_FROM_DEVICE); > + if (dma_mapping_error(&priv->si->pdev->dev, dma)) { > + netdev_err(priv->si->ndev, "DMA mapping failed!\n"); > + kfree(sgcl_data); > + return -ENOMEM; > + } > + > + cbd.addr[0] = lower_32_bits(dma); > + cbd.addr[1] = upper_32_bits(dma); > + > + sgce = &sgcl_data->sgcl[0]; > + > + sgcl_config->agtst = 0x80; > + > + sgcl_data->ct = cpu_to_le32(sgi->cycletime); > + sgcl_data->cte = cpu_to_le32(sgi->cycletimext); > + > + if (sgi->init_ipv >= 0) > + sgcl_config->aipv = (sgi->init_ipv & 0x7) | 0x8; > + > + for (i = 0; i < sgi->num_entries; i++) { > + struct action_gate_entry *from = &sgi->entries[i]; > + struct sgce *to = &sgce[i]; > + > + if (from->gate_state) > + to->multi |= 0x10; > + > + if (from->ipv >= 0) > + to->multi |= ((from->ipv & 0x7) << 5) | 0x08; > + > + if (from->maxoctets) > + to->multi |= 0x01; > + > + to->interval = cpu_to_le32(from->interval); > + to->msdu[0] = from->maxoctets & 0xFF; > + to->msdu[1] = (from->maxoctets >> 8) & 0xFF; > + to->msdu[2] = (from->maxoctets >> 16) & 0xFF; > + } > + > + /* If basetime is 0, calculate start time */ > + if (!sgi->basetime) { > + u64 start; > + > + err = get_start_ns(priv, sgi->cycletime, &start); > + if (err) > + goto exit; > + sgcl_data->btl = cpu_to_le32(lower_32_bits(start)); > + sgcl_data->bth = cpu_to_le32(upper_32_bits(start)); > + } else { > + u32 hi, lo; > + > + hi = upper_32_bits(sgi->basetime); > + lo = lower_32_bits(sgi->basetime); > + sgcl_data->bth = cpu_to_le32(hi); > + sgcl_data->btl = cpu_to_le32(lo); > + } > + > + err = enetc_send_cmd(priv->si, &cbd); > + > +exit: > + kfree(sgcl_data); > + > + return err; > +} > + > +static struct enetc_stream_filter *enetc_get_stream_by_index(u32 index) > +{ > + struct enetc_stream_filter *f; > + > + hlist_for_each_entry(f, &epsfp.stream_list, node) > + if (f->sid.index == index) > + return f; > + > + return NULL; > +} > + > +static struct enetc_psfp_gate *enetc_get_gate_by_index(u32 index) > +{ > + struct enetc_psfp_gate *g; > + > + hlist_for_each_entry(g, &epsfp.psfp_gate_list, node) > + if (g->index == index) > + return g; > + > + return NULL; > +} > + > +static struct enetc_psfp_filter *enetc_get_filter_by_index(u32 index) > +{ > + struct enetc_psfp_filter *s; > + > + hlist_for_each_entry(s, &epsfp.psfp_filter_list, node) > + if (s->index == index) > + return s; > + > + return NULL; > +} > + > +static struct enetc_psfp_filter > + *enetc_psfp_check_sfi(struct enetc_psfp_filter *sfi) > +{ > + struct enetc_psfp_filter *s; > + > + hlist_for_each_entry(s, &epsfp.psfp_filter_list, node) > + if (s->gate_id == sfi->gate_id && > + s->prio == sfi->prio && > + s->meter_id == sfi->meter_id) > + return s; > + > + return NULL; > +} > + > +static int enetc_get_free_index(struct enetc_ndev_priv *priv) > +{ > + u32 max_size = priv->psfp_cap.max_psfp_filter; > + unsigned long index; > + > + index = find_first_zero_bit(epsfp.psfp_sfi_bitmap, max_size); > + if (index == max_size) > + return -1; > + > + return index; > +} > + > +static void reduce_ref_sfi(struct enetc_ndev_priv *priv, u32 index) Please use the more usual idioms, something like "sfi_unref()" or "stream_filter_unref". > +{ > + struct enetc_psfp_filter *sfi; > + > + sfi = enetc_get_filter_by_index(index); > + WARN_ON(!sfi); > + sfi->refcount--; > + > + if (!sfi->refcount) { > + enetc_streamfilter_hw_set(priv, sfi, false); > + hlist_del(&sfi->node); > + kfree(sfi); > + clear_bit(sfi->index, epsfp.psfp_sfi_bitmap); > + } > +} > + > +static void reduce_ref_sgi(struct enetc_ndev_priv *priv, u32 index) > +{ > + struct enetc_psfp_gate *sgi; > + > + sgi = enetc_get_gate_by_index(index); > + WARN_ON(!sgi); > + sgi->refcount--; > + > + if (!sgi->refcount) { > + enetc_streamgate_hw_set(priv, sgi, false); > + hlist_del(&sgi->node); > + kfree(sgi); > + } > +} > + > +static void remove_one_chain(struct enetc_ndev_priv *priv, > + struct enetc_stream_filter *filter) > +{ > + reduce_ref_sgi(priv, filter->sgi_index); > + reduce_ref_sfi(priv, filter->sfi_index); > + > + hlist_del(&filter->node); > + kfree(filter); > +} > + > +static int enetc_psfp_hw_set(struct enetc_ndev_priv *priv, > + struct enetc_streamid *sid, > + struct enetc_psfp_filter *sfi, > + struct enetc_psfp_gate *sgi) > +{ > + int err; > + > + err = enetc_streamid_hw_set(priv, sid, true); > + if (err) > + return err; > + > + if (sfi) { > + err = enetc_streamfilter_hw_set(priv, sfi, true); > + if (err) > + goto revert_sid; > + } > + > + err = enetc_streamgate_hw_set(priv, sgi, true); > + if (err) > + goto revert_sfi; > + > + return 0; > + > +revert_sfi: > + if (sfi && !sfi->refcount) > + enetc_streamfilter_hw_set(priv, sfi, false); > +revert_sid: > + enetc_streamid_hw_set(priv, sid, false); > + return err; > +} > + > +struct actions_fwd *enetc_check_flow_actions(u64 acts, unsigned int inputkeys) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(enetc_act_fwd); i++) > + if (acts == enetc_act_fwd[i].actions && > + inputkeys & enetc_act_fwd[i].keys) > + return &enetc_act_fwd[i]; > + > + return NULL; > +} > + > +static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, > + struct flow_cls_offload *f) > +{ > + struct flow_rule *rule = flow_cls_offload_flow_rule(f); > + struct netlink_ext_ack *extack = f->common.extack; > + struct enetc_stream_filter *filter, *old_filter; > + struct enetc_psfp_filter *sfi, *old_sfi; > + struct enetc_psfp_gate *sgi, *old_sgi; > + struct flow_action_entry *entry; > + struct action_gate_entry *e; > + u8 sfi_overwrite = 0; > + int entries_size; > + int i, err; > + > + if (f->common.chain_index >= priv->psfp_cap.max_streamid) { > + NL_SET_ERR_MSG_MOD(extack, "No Stream identify resource!"); > + return -ENOSPC; > + } > + > + flow_action_for_each(i, entry, &rule->action) > + if (entry->id == FLOW_ACTION_GATE) > + break; > + > + if (entry->id != FLOW_ACTION_GATE) > + return -EINVAL; > + > + filter = kzalloc(sizeof(*filter), GFP_KERNEL); > + if (!filter) > + return -ENOMEM; > + > + filter->sid.index = f->common.chain_index; > + > + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { > + struct flow_match_eth_addrs match; > + > + flow_rule_match_eth_addrs(rule, &match); > + > + if (!is_zero_ether_addr(match.mask->dst)) { > + ether_addr_copy(filter->sid.dst_mac, match.key->dst); > + filter->sid.filtertype = STREAMID_TYPE_NULL; > + } > + > + if (!is_zero_ether_addr(match.mask->src)) { > + ether_addr_copy(filter->sid.src_mac, match.key->src); > + filter->sid.filtertype = STREAMID_TYPE_SMAC; > + } > + } else { > + NL_SET_ERR_MSG_MOD(extack, "Unsupported, must ETH_ADDRS"); > + return -EINVAL; > + } > + > + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { > + struct flow_match_vlan match; > + > + flow_rule_match_vlan(rule, &match); > + if (match.mask->vlan_priority) { > + if (match.mask->vlan_priority != > + (VLAN_PRIO_MASK >> VLAN_PRIO_SHIFT)) { > + NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN priority"); > + err = -EINVAL; > + goto free_filter; > + } > + } > + > + if (match.mask->vlan_tpid) { > + if (match.mask->vlan_tpid != VLAN_VID_MASK) { > + NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN id"); > + err = -EINVAL; > + goto free_filter; > + } > + > + filter->sid.vid = match.key->vlan_tpid; > + if (!filter->sid.vid) > + filter->sid.tagged = STREAMID_VLAN_UNTAGGED; > + else > + filter->sid.tagged = STREAMID_VLAN_TAGGED; > + } > + } else { > + filter->sid.tagged = STREAMID_VLAN_ALL; > + } > + > + /* parsing gate action */ > + if (entry->gate.index >= priv->psfp_cap.max_psfp_gate) { > + NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!"); > + err = -ENOSPC; > + goto free_filter; > + } > + > + if (entry->gate.num_entries >= priv->psfp_cap.max_psfp_gatelist) { > + NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!"); > + err = -ENOSPC; > + goto free_filter; > + } > + > + entries_size = struct_size(sgi, entries, entry->gate.num_entries); > + sgi = kzalloc(entries_size, GFP_KERNEL); > + if (!sgi) { > + err = -ENOMEM; > + goto free_filter; > + } > + > + sgi->index = entry->gate.index; > + sgi->init_ipv = entry->gate.prio; > + sgi->basetime = entry->gate.basetime; > + sgi->cycletime = entry->gate.cycletime; > + sgi->num_entries = entry->gate.num_entries; > + > + e = sgi->entries; > + for (i = 0; i < entry->gate.num_entries; i++) { > + e[i].gate_state = entry->gate.entries[i].gate_state; > + e[i].interval = entry->gate.entries[i].interval; > + e[i].ipv = entry->gate.entries[i].ipv; > + e[i].maxoctets = entry->gate.entries[i].maxoctets; > + } > + > + filter->sgi_index = sgi->index; > + > + sfi = kzalloc(sizeof(*sfi), GFP_KERNEL); > + if (!sfi) { > + err = -ENOMEM; > + goto free_gate; > + } > + > + sfi->gate_id = sgi->index; > + > + /* flow meter not support yet */ > + sfi->meter_id = ENETC_PSFP_WILDCARD; > + > + /* prio ref the filter prio */ > + if (f->common.prio && f->common.prio <= BIT(3)) > + sfi->prio = f->common.prio - 1; > + else > + sfi->prio = ENETC_PSFP_WILDCARD; > + > + old_sfi = enetc_psfp_check_sfi(sfi); > + if (!old_sfi) { > + int index; > + > + index = enetc_get_free_index(priv); > + if (sfi->handle < 0) { > + NL_SET_ERR_MSG_MOD(extack, "No Stream Filter resource!"); > + err = -ENOSPC; > + goto free_sfi; > + } > + > + sfi->index = index; > + sfi->handle = index + HANDLE_OFFSET; > + /* Update the stream filter handle also */ > + filter->sid.handle = sfi->handle; > + filter->sfi_index = sfi->index; > + sfi_overwrite = 0; > + } else { > + filter->sfi_index = old_sfi->index; > + filter->sid.handle = old_sfi->handle; > + sfi_overwrite = 1; > + } > + > + err = enetc_psfp_hw_set(priv, &filter->sid, > + sfi_overwrite ? NULL : sfi, sgi); > + if (err) > + goto free_sfi; > + > + spin_lock(&epsfp.psfp_lock); > + old_sgi = enetc_get_gate_by_index(filter->sgi_index); > + if (old_sgi) { > + sgi->refcount = old_sgi->refcount; > + hlist_del(&old_sgi->node); > + kfree(old_sgi); I don't understand what you are trying to achieve here. But there should be a cleaner way. > + } > + > + sgi->refcount++; > + hlist_add_head(&sgi->node, &epsfp.psfp_gate_list); > + > + if (!old_sfi) { > + hlist_add_head(&sfi->node, &epsfp.psfp_filter_list); > + set_bit(sfi->index, epsfp.psfp_sfi_bitmap); > + sfi->refcount++; > + } else { > + kfree(sfi); > + old_sfi->refcount++; > + } > + > + old_filter = enetc_get_stream_by_index(filter->sid.index); > + if (old_filter) > + remove_one_chain(priv, old_filter); > + > + filter->stats.lastused = jiffies; > + hlist_add_head(&filter->node, &epsfp.stream_list); > + > + spin_unlock(&epsfp.psfp_lock); > + > + return 0; > + > +free_sfi: > + kfree(sfi); > +free_gate: > + kfree(sgi); > +free_filter: > + kfree(filter); > + > + return err; > +} > + > +static int enetc_config_clsflower(struct enetc_ndev_priv *priv, > + struct flow_cls_offload *cls_flower) > +{ > + struct flow_rule *rule = flow_cls_offload_flow_rule(cls_flower); > + struct netlink_ext_ack *extack = cls_flower->common.extack; > + struct flow_dissector *dissector = rule->match.dissector; > + struct flow_action *action = &rule->action; > + struct flow_action_entry *entry; > + struct actions_fwd *fwd; > + u64 actions = 0; > + int i, err; > + > + if (!flow_action_has_entries(action)) { > + NL_SET_ERR_MSG_MOD(extack, "At least one action is needed"); > + return -EINVAL; > + } > + > + flow_action_for_each(i, entry, action) > + actions |= BIT(entry->id); > + > + fwd = enetc_check_flow_actions(actions, dissector->used_keys); > + if (!fwd) { > + NL_SET_ERR_MSG_MOD(extack, "Unsupported filter type!"); > + return -EOPNOTSUPP; > + } > + > + if (fwd->output & FILTER_ACTION_TYPE_PSFP) { > + err = enetc_psfp_parse_clsflower(priv, cls_flower); > + if (err) { > + NL_SET_ERR_MSG_MOD(extack, "Invalid PSFP inputs"); > + return err; > + } > + } else { > + NL_SET_ERR_MSG_MOD(extack, "Unsupported actions"); > + return -EOPNOTSUPP; > + } > + > + return 0; > +} > + > +static int enetc_psfp_destroy_clsflower(struct enetc_ndev_priv *priv, > + struct flow_cls_offload *f) > +{ > + struct enetc_stream_filter *filter; > + struct netlink_ext_ack *extack = f->common.extack; > + int err; > + > + if (f->common.chain_index >= priv->psfp_cap.max_streamid) { > + NL_SET_ERR_MSG_MOD(extack, "No Stream identify resource!"); > + return -ENOSPC; > + } > + > + filter = enetc_get_stream_by_index(f->common.chain_index); > + if (!filter) > + return -EINVAL; > + > + err = enetc_streamid_hw_set(priv, &filter->sid, false); > + if (err) > + return err; > + > + remove_one_chain(priv, filter); > + > + return 0; > +} > + > +static int enetc_destroy_clsflower(struct enetc_ndev_priv *priv, > + struct flow_cls_offload *f) > +{ > + return enetc_psfp_destroy_clsflower(priv, f); > +} > + > +static int enetc_psfp_get_stats(struct enetc_ndev_priv *priv, > + struct flow_cls_offload *f) > +{ > + struct psfp_streamfilter_counters counters = {}; > + struct enetc_stream_filter *filter; > + struct flow_stats stats = {}; > + int err; > + > + filter = enetc_get_stream_by_index(f->common.chain_index); > + if (!filter) > + return -EINVAL; > + > + err = enetc_streamcounter_hw_get(priv, filter->sfi_index, &counters); > + if (err) > + return -EINVAL; > + > + spin_lock(&epsfp.psfp_lock); > + stats.pkts = counters.matching_frames_count - filter->stats.pkts; > + stats.dropped = counters.not_passing_frames_count - > + filter->stats.dropped; > + stats.lastused = filter->stats.lastused; > + filter->stats.pkts += stats.pkts; > + filter->stats.dropped += stats.dropped; > + > + spin_unlock(&epsfp.psfp_lock); > + > + flow_stats_update(&f->stats, 0x0, stats.pkts, stats.lastused, > + stats.dropped); > + > + return 0; > +} > + > +static int enetc_setup_tc_cls_flower(struct enetc_ndev_priv *priv, > + struct flow_cls_offload *cls_flower) > +{ > + switch (cls_flower->command) { > + case FLOW_CLS_REPLACE: > + return enetc_config_clsflower(priv, cls_flower); > + case FLOW_CLS_DESTROY: > + return enetc_destroy_clsflower(priv, cls_flower); > + case FLOW_CLS_STATS: > + return enetc_psfp_get_stats(priv, cls_flower); > + default: > + return -EOPNOTSUPP; > + } > +} > + > +static inline void clean_psfp_sfi_bitmap(void) > +{ > + bitmap_free(epsfp.psfp_sfi_bitmap); > + epsfp.psfp_sfi_bitmap = NULL; > +} > + > +static void clean_stream_list(void) > +{ > + struct enetc_stream_filter *s; > + struct hlist_node *tmp; > + > + hlist_for_each_entry_safe(s, tmp, &epsfp.stream_list, node) { > + hlist_del(&s->node); > + kfree(s); > + } > +} > + > +static void clean_sfi_list(void) > +{ > + struct enetc_psfp_filter *sfi; > + struct hlist_node *tmp; > + > + hlist_for_each_entry_safe(sfi, tmp, &epsfp.psfp_filter_list, node) { > + hlist_del(&sfi->node); > + kfree(sfi); > + } > +} > + > +static void clean_sgi_list(void) > +{ > + struct enetc_psfp_gate *sgi; > + struct hlist_node *tmp; > + > + hlist_for_each_entry_safe(sgi, tmp, &epsfp.psfp_gate_list, node) { > + hlist_del(&sgi->node); > + kfree(sgi); > + } > +} > + > +static void clean_psfp_all(void) > +{ > + /* Disable all list nodes and free all memory */ > + clean_sfi_list(); > + clean_sgi_list(); > + clean_stream_list(); > + epsfp.dev_bitmap = 0; > + clean_psfp_sfi_bitmap(); > +} > + > +int enetc_setup_tc_block_cb(enum tc_setup_type type, void *type_data, > + void *cb_priv) > +{ > + struct net_device *ndev = cb_priv; > + > + if (!tc_can_offload(ndev)) > + return -EOPNOTSUPP; > + > + switch (type) { > + case TC_SETUP_CLSFLOWER: > + return enetc_setup_tc_cls_flower(netdev_priv(ndev), type_data); > + default: > + return -EOPNOTSUPP; > + } > +} > + > +int enetc_psfp_init(struct enetc_ndev_priv *priv) > +{ > + if (epsfp.psfp_sfi_bitmap) > + return 0; > + > + epsfp.psfp_sfi_bitmap = bitmap_zalloc(priv->psfp_cap.max_psfp_filter, > + GFP_KERNEL); > + if (!epsfp.psfp_sfi_bitmap) > + return -ENOMEM; > + > + spin_lock_init(&epsfp.psfp_lock); > + > + if (list_empty(&enetc_block_cb_list)) > + epsfp.dev_bitmap = 0; > + > + return 0; > +} > + > +int enetc_psfp_clean(struct enetc_ndev_priv *priv) > +{ > + if (!list_empty(&enetc_block_cb_list)) > + return -EBUSY; > + > + clean_psfp_all(); > + > + return 0; > +} > + > +int enetc_setup_tc_psfp(struct net_device *ndev, void *type_data) > +{ > + struct enetc_ndev_priv *priv = netdev_priv(ndev); > + struct flow_block_offload *f = type_data; > + int err; > + > + err = flow_block_cb_setup_simple(f, &enetc_block_cb_list, > + enetc_setup_tc_block_cb, > + ndev, ndev, true); > + if (err) > + return err; > + > + switch (f->command) { > + case FLOW_BLOCK_BIND: > + set_bit(enetc_get_port(priv), &epsfp.dev_bitmap); > + break; > + case FLOW_BLOCK_UNBIND: > + clear_bit(enetc_get_port(priv), &epsfp.dev_bitmap); > + if (!epsfp.dev_bitmap) > + clean_psfp_all(); > + break; > + } > + > + return 0; > +} > -- > 2.17.1 > -- Vinicius