Commit 888ae5a3 authored by Po Liu's avatar Po Liu Committed by David S. Miller

net: enetc: add tc flower psfp offload driver

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 8000000 \
	sched-entry close 100000000 -1 -1

Command means to set the dst_mac 10:00:80:00:00:00 to index 11 of stream
identify module. Then setting the gate index 10 of stream gate module.
Keep the gate open for 200ms and limit the traffic volume to 8MB in this
sched-entry. Then direct the frames to the ingress queue 1.
Signed-off-by: default avatarPo Liu <Po.Liu@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 79e49982
...@@ -1521,6 +1521,8 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type, ...@@ -1521,6 +1521,8 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
return enetc_setup_tc_cbs(ndev, type_data); return enetc_setup_tc_cbs(ndev, type_data);
case TC_SETUP_QDISC_ETF: case TC_SETUP_QDISC_ETF:
return enetc_setup_tc_txtime(ndev, type_data); return enetc_setup_tc_txtime(ndev, type_data);
case TC_SETUP_BLOCK:
return enetc_setup_tc_psfp(ndev, type_data);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -1573,17 +1575,23 @@ static int enetc_set_rss(struct net_device *ndev, int en) ...@@ -1573,17 +1575,23 @@ static int enetc_set_rss(struct net_device *ndev, int en)
static int enetc_set_psfp(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); struct enetc_ndev_priv *priv = netdev_priv(ndev);
int err;
if (en) { if (en) {
err = enetc_psfp_enable(priv);
if (err)
return err;
priv->active_offloads |= ENETC_F_QCI; priv->active_offloads |= ENETC_F_QCI;
enetc_get_max_cap(priv); return 0;
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);
} }
err = enetc_psfp_disable(priv);
if (err)
return err;
priv->active_offloads &= ~ENETC_F_QCI;
return 0; return 0;
} }
...@@ -1591,14 +1599,15 @@ int enetc_set_features(struct net_device *ndev, ...@@ -1591,14 +1599,15 @@ int enetc_set_features(struct net_device *ndev,
netdev_features_t features) netdev_features_t features)
{ {
netdev_features_t changed = ndev->features ^ features; netdev_features_t changed = ndev->features ^ features;
int err = 0;
if (changed & NETIF_F_RXHASH) if (changed & NETIF_F_RXHASH)
enetc_set_rss(ndev, !!(features & NETIF_F_RXHASH)); enetc_set_rss(ndev, !!(features & NETIF_F_RXHASH));
if (changed & NETIF_F_HW_TC) 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_PTP_CLOCK #ifdef CONFIG_FSL_ENETC_PTP_CLOCK
......
...@@ -300,6 +300,11 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data); ...@@ -300,6 +300,11 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
void enetc_sched_speed_set(struct net_device *ndev); 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_cbs(struct net_device *ndev, void *type_data);
int enetc_setup_tc_txtime(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) static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv)
{ {
...@@ -319,27 +324,60 @@ static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv) ...@@ -319,27 +324,60 @@ static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv)
priv->psfp_cap.max_psfp_meter = reg & ENETC_PFMCAPR_MSK; 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_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) |
ENETC_PPSFPMR_PSFPEN | ENETC_PPSFPMR_VS | ENETC_PPSFPMR_PSFPEN | ENETC_PPSFPMR_VS |
ENETC_PPSFPMR_PVC | ENETC_PPSFPMR_PVZC); 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_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) &
~ENETC_PPSFPMR_PSFPEN & ~ENETC_PPSFPMR_VS & ~ENETC_PPSFPMR_PSFPEN & ~ENETC_PPSFPMR_VS &
~ENETC_PPSFPMR_PVC & ~ENETC_PPSFPMR_PVZC); ~ENETC_PPSFPMR_PVC & ~ENETC_PPSFPMR_PVZC);
memset(&priv->psfp_cap, 0, sizeof(struct psfp_cap));
return 0;
} }
#else #else
#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP #define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
#define enetc_sched_speed_set(ndev) (void)0 #define enetc_sched_speed_set(ndev) (void)0
#define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP #define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP
#define enetc_setup_tc_txtime(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) \ #define enetc_get_max_cap(p) \
memset(&((p)->psfp_cap), 0, sizeof(struct psfp_cap)) memset(&((p)->psfp_cap), 0, sizeof(struct psfp_cap))
#define enetc_psfp_enable(hw) (void)0 static inline int enetc_psfp_enable(struct enetc_ndev_priv *priv)
#define enetc_psfp_disable(hw) (void)0 {
return 0;
}
static inline int enetc_psfp_disable(struct enetc_ndev_priv *priv)
{
return 0;
}
#endif #endif
...@@ -567,6 +567,9 @@ enum bdcr_cmd_class { ...@@ -567,6 +567,9 @@ enum bdcr_cmd_class {
BDCR_CMD_RFS, BDCR_CMD_RFS,
BDCR_CMD_PORT_GCL, BDCR_CMD_PORT_GCL,
BDCR_CMD_RECV_CLASSIFIER, 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 = __BDCR_CMD_MAX_LEN - 1, BDCR_CMD_MAX_LEN = __BDCR_CMD_MAX_LEN - 1,
}; };
...@@ -598,13 +601,152 @@ struct tgs_gcl_data { ...@@ -598,13 +601,152 @@ struct tgs_gcl_data {
struct gce entry[]; 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 { struct enetc_cbd {
union{ union{
struct sfi_conf sfi_conf;
struct sgi_table sgi_table;
struct { struct {
__le32 addr[2]; __le32 addr[2];
union { union {
__le32 opt[4]; __le32 opt[4];
struct tgs_gcl_conf gcl_conf; struct tgs_gcl_conf gcl_conf;
struct streamid_conf sid_set;
struct sgcl_conf sgcl_conf;
}; };
}; /* Long format */ }; /* Long format */
__le32 data[6]; __le32 data[6];
......
...@@ -727,12 +727,10 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev, ...@@ -727,12 +727,10 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
if (si->hw_features & ENETC_SI_F_QBV) if (si->hw_features & ENETC_SI_F_QBV)
priv->active_offloads |= ENETC_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; priv->active_offloads |= ENETC_F_QCI;
ndev->features |= NETIF_F_HW_TC; ndev->features |= NETIF_F_HW_TC;
ndev->hw_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 */ /* pick up primary MAC address from SI */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment