diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index db3c7015c56c43..6c596e5a315175 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -643,6 +643,7 @@ struct ice_pf {
*/
u16 num_dmac_chnl_fltrs;
struct hlist_head tc_flower_fltr_list;
+ struct hlist_head tc_sw_fltr_list;
u64 supported_rxdids;
diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
index fbd8cbad150a98..7cfc8941b7ba57 100644
--- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
@@ -2269,6 +2269,112 @@ ice_find_tc_flower_fltr(struct ice_pf *pf, unsigned long cookie)
return NULL;
}
+/**
+ * ice_tc_fltr_is_drop - check if a filter carries a drop action
+ * @cls_flower: offload request describing the filter
+ *
+ * Return: true if any action of the filter is a drop, false otherwise.
+ */
+static bool ice_tc_fltr_is_drop(struct flow_cls_offload *cls_flower)
+{
+ struct flow_rule *rule = flow_cls_offload_flow_rule(cls_flower);
+ struct flow_action_entry *act;
+ int i;
+
+ if (cls_flower->classid)
+ return false;
+
+ flow_action_for_each(i, act, &rule->action)
+ if (act->id == FLOW_ACTION_DROP)
+ return true;
+
+ return false;
+}
+
+/**
+ * ice_tc_untrack_sw_fltr - forget a tracked software-only filter
+ * @pf: pointer to PF structure
+ * @cookie: unique filter identifier from the offload request
+ *
+ * Return: true if the filter was tracked, false otherwise.
+ */
+static bool ice_tc_untrack_sw_fltr(struct ice_pf *pf, unsigned long cookie)
+{
+ struct ice_tc_sw_fltr *sw_fltr;
+
+ hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node) {
+ if (sw_fltr->cookie != cookie)
+ continue;
+
+ hlist_del(&sw_fltr->node);
+ kfree(sw_fltr);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ice_tc_track_sw_fltr - remember a filter that was not offloaded
+ * @pf: pointer to PF structure
+ * @filter_dev: device the filter was requested on
+ * @cls_flower: offload request describing the filter
+ * @direction: block direction the filter was requested for
+ */
+static void ice_tc_track_sw_fltr(struct ice_pf *pf,
+ struct net_device *filter_dev,
+ struct flow_cls_offload *cls_flower,
+ enum ice_eswitch_fltr_direction direction)
+{
+ struct ice_tc_sw_fltr *sw_fltr;
+
+ hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node)
+ if (sw_fltr->cookie == cls_flower->cookie)
+ return;
+
+ sw_fltr = kzalloc_obj(*sw_fltr);
+ if (!sw_fltr)
+ return;
+
+ sw_fltr->cookie = cls_flower->cookie;
+ sw_fltr->filter_dev = filter_dev;
+ sw_fltr->prio = cls_flower->common.prio;
+ sw_fltr->direction = direction;
+ sw_fltr->is_drop = ice_tc_fltr_is_drop(cls_flower);
+ hlist_add_head(&sw_fltr->node, &pf->tc_sw_fltr_list);
+}
+
+/**
+ * ice_tc_drop_bypasses_fltr - check if a drop rule would bypass a filter
+ * @pf: pointer to PF structure
+ * @filter_dev: device the drop filter is requested on
+ * @prio: TC priority of the drop filter
+ * @direction: block direction of the drop filter
+ *
+ * Return: true if such a filter exists, false otherwise.
+ */
+static bool
+ice_tc_drop_bypasses_fltr(struct ice_pf *pf, struct net_device *filter_dev,
+ u32 prio, enum ice_eswitch_fltr_direction direction)
+{
+ struct ice_tc_flower_fltr *fltr;
+ struct ice_tc_sw_fltr *sw_fltr;
+
+ hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node)
+ if (sw_fltr->filter_dev == filter_dev &&
+ sw_fltr->direction == direction && sw_fltr->prio < prio &&
+ !sw_fltr->is_drop)
+ return true;
+
+ hlist_for_each_entry(fltr, &pf->tc_flower_fltr_list, tc_flower_node)
+ if (fltr->filter_dev == filter_dev &&
+ fltr->direction == direction && fltr->prio < prio &&
+ fltr->action.fltr_act != ICE_DROP_PACKET)
+ return true;
+
+ return false;
+}
+
/**
* ice_add_cls_flower - add TC flower filters
* @netdev: Pointer to filter device@@ -2284,14 +2390,24 @@ int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi,
{
struct netlink_ext_ack *extack = cls_flower->common.extack;
struct net_device *vsi_netdev = vsi->netdev;
+ enum ice_eswitch_fltr_direction direction;
struct ice_tc_flower_fltr *fltr;
struct ice_pf *pf = vsi->back;
+ bool track_sw_fltrs;
int err;
- if (ice_is_reset_in_progress(pf->state))
- return -EBUSY;
- if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags))
- return -EINVAL;
+ direction = ingress ? ICE_ESWITCH_FLTR_INGRESS :
+ ICE_ESWITCH_FLTR_EGRESS;
+ track_sw_fltrs = !ice_is_eswitch_mode_switchdev(pf);
+
+ if (ice_is_reset_in_progress(pf->state)) {
+ err = -EBUSY;
+ goto track_sw;
+ }
+ if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) {
+ err = -EINVAL;
+ goto track_sw;
+ }
if (ice_is_port_repr_netdev(netdev))
vsi_netdev = netdev;@@ -2304,7 +2420,8 @@ int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi,
*/
if (netdev == vsi_netdev)
NL_SET_ERR_MSG_MOD(extack, "can't apply TC flower filters, turn ON hw-tc-offload and try again");
- return -EINVAL;
+ err = -EINVAL;
+ goto track_sw;
}
/* avoid duplicate entries, if exists - return error */
@@ -2314,14 +2431,32 @@ int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi,
return -EEXIST;
}
+ if (track_sw_fltrs && !cls_flower->common.skip_sw &&
+ ice_tc_fltr_is_drop(cls_flower) &&
+ ice_tc_drop_bypasses_fltr(pf, netdev, cls_flower->common.prio,
+ direction)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Drop filter not offloaded because it would bypass a higher priority filter");
+ err = -EOPNOTSUPP;
+ goto track_sw;
+ }
+
/* prep and add TC-flower filter in HW */
err = ice_add_tc_fltr(netdev, vsi, cls_flower, &fltr, ingress);
if (err)
- return err;
+ goto track_sw;
+
+ fltr->filter_dev = netdev;
+ fltr->prio = cls_flower->common.prio;
/* add filter into an ordered list */
hlist_add_head(&fltr->tc_flower_node, &pf->tc_flower_fltr_list);
return 0;
+
+track_sw:
+ if (track_sw_fltrs && !cls_flower->common.skip_sw)
+ ice_tc_track_sw_fltr(pf, netdev, cls_flower, direction);
+ return err;
}
/**@@ -2336,6 +2471,9 @@ ice_del_cls_flower(struct ice_vsi *vsi, struct flow_cls_offload *cls_flower)
struct ice_pf *pf = vsi->back;
int err;
+ if (ice_tc_untrack_sw_fltr(pf, cls_flower->cookie))
+ return 0;
+
/* find filter */
fltr = ice_find_tc_flower_fltr(pf, cls_flower->cookie);
if (!fltr) {diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h b/drivers/net/ethernet/intel/ice/ice_tc_lib.h
index 752af65e70b7bf..37d6f4100ecfbc 100644
--- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h
@@ -139,12 +139,33 @@ enum ice_eswitch_fltr_direction {
ICE_ESWITCH_FLTR_EGRESS,
};
+/**
+ * struct ice_tc_sw_fltr - filter presented to the driver but not offloaded
+ * @node: node in the pf->tc_sw_fltr_list
+ * @cookie: unique filter identifier from the offload request
+ * @filter_dev: device the filter was requested on
+ * @prio: TC priority, lower value is evaluated first
+ * @direction: block direction the filter was requested for
+ * @is_drop: the filter carries a drop action
+ */
+struct ice_tc_sw_fltr {
+ struct hlist_node node;
+ unsigned long cookie;
+ struct net_device *filter_dev;
+ u32 prio;
+ enum ice_eswitch_fltr_direction direction;
+ bool is_drop;
+};
+
struct ice_tc_flower_fltr {
struct hlist_node tc_flower_node;
/* cookie becomes filter_rule_id if rule is added successfully */
unsigned long cookie;
+ struct net_device *filter_dev;
+ u32 prio;
+
/* add_adv_rule returns information like recipe ID, rule_id. Store
* those values since they are needed to remove advanced rule
*/--
2.55.0