act_skbedit: skbedit queue mapping for receive queue
authorAmritha Nambiar <amritha.nambiar@intel.com>
Fri, 21 Oct 2022 07:58:39 +0000 (00:58 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 25 Oct 2022 08:32:40 +0000 (10:32 +0200)
Add support for skbedit queue mapping action on receive
side. This is supported only in hardware, so the skip_sw
flag is enforced. This enables offloading filters for
receive queue selection in the hardware using the
skbedit action. Traffic arrives on the Rx queue requested
in the skbedit action parameter. A new tc action flag
TCA_ACT_FLAGS_AT_INGRESS is introduced to identify the
traffic direction the action queue_mapping is requested
on during filter addition. This is used to disallow
offloading the skbedit queue mapping action on transmit
side.

Example:
$tc filter add dev $IFACE ingress protocol ip flower dst_ip $DST_IP\
 action skbedit queue_mapping $rxq_id skip_sw

Reviewed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/net/act_api.h
include/net/flow_offload.h
include/net/tc_act/tc_skbedit.h
net/sched/act_skbedit.c
net/sched/cls_api.c

index 61f2ceb..c94ea1a 100644 (file)
@@ -67,6 +67,7 @@ struct tc_action {
 #define TCA_ACT_FLAGS_BIND     (1U << (TCA_ACT_FLAGS_USER_BITS + 1))
 #define TCA_ACT_FLAGS_REPLACE  (1U << (TCA_ACT_FLAGS_USER_BITS + 2))
 #define TCA_ACT_FLAGS_NO_RTNL  (1U << (TCA_ACT_FLAGS_USER_BITS + 3))
+#define TCA_ACT_FLAGS_AT_INGRESS       (1U << (TCA_ACT_FLAGS_USER_BITS + 4))
 
 /* Update lastuse only if needed, to avoid dirtying a cache line.
  * We use a temp variable to avoid fetching jiffies twice.
index e343f9f..7a60bc6 100644 (file)
@@ -155,6 +155,7 @@ enum flow_action_id {
        FLOW_ACTION_MARK,
        FLOW_ACTION_PTYPE,
        FLOW_ACTION_PRIORITY,
+       FLOW_ACTION_RX_QUEUE_MAPPING,
        FLOW_ACTION_WAKE,
        FLOW_ACTION_QUEUE,
        FLOW_ACTION_SAMPLE,
@@ -247,6 +248,7 @@ struct flow_action_entry {
                u32                     csum_flags;     /* FLOW_ACTION_CSUM */
                u32                     mark;           /* FLOW_ACTION_MARK */
                u16                     ptype;          /* FLOW_ACTION_PTYPE */
+               u16                     rx_queue;       /* FLOW_ACTION_RX_QUEUE_MAPPING */
                u32                     priority;       /* FLOW_ACTION_PRIORITY */
                struct {                                /* FLOW_ACTION_QUEUE */
                        u32             ctx;
index dc1079f..9649600 100644 (file)
@@ -95,12 +95,41 @@ static inline u32 tcf_skbedit_priority(const struct tc_action *a)
        return priority;
 }
 
+static inline u16 tcf_skbedit_rx_queue_mapping(const struct tc_action *a)
+{
+       u16 rx_queue;
+
+       rcu_read_lock();
+       rx_queue = rcu_dereference(to_skbedit(a)->params)->queue_mapping;
+       rcu_read_unlock();
+
+       return rx_queue;
+}
+
 /* Return true iff action is queue_mapping */
 static inline bool is_tcf_skbedit_queue_mapping(const struct tc_action *a)
 {
        return is_tcf_skbedit_with_flag(a, SKBEDIT_F_QUEUE_MAPPING);
 }
 
+/* Return true if action is on ingress traffic */
+static inline bool is_tcf_skbedit_ingress(u32 flags)
+{
+       return flags & TCA_ACT_FLAGS_AT_INGRESS;
+}
+
+static inline bool is_tcf_skbedit_tx_queue_mapping(const struct tc_action *a)
+{
+       return is_tcf_skbedit_queue_mapping(a) &&
+              !is_tcf_skbedit_ingress(a->tcfa_flags);
+}
+
+static inline bool is_tcf_skbedit_rx_queue_mapping(const struct tc_action *a)
+{
+       return is_tcf_skbedit_queue_mapping(a) &&
+              is_tcf_skbedit_ingress(a->tcfa_flags);
+}
+
 /* Return true iff action is inheritdsfield */
 static inline bool is_tcf_skbedit_inheritdsfield(const struct tc_action *a)
 {
index 7f59878..1710780 100644 (file)
@@ -148,6 +148,11 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
        }
 
        if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
+               if (is_tcf_skbedit_ingress(act_flags) &&
+                   !(act_flags & TCA_ACT_FLAGS_SKIP_SW)) {
+                       NL_SET_ERR_MSG_MOD(extack, "\"queue_mapping\" option on receive side is hardware only, use skip_sw");
+                       return -EOPNOTSUPP;
+               }
                flags |= SKBEDIT_F_QUEUE_MAPPING;
                queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
        }
@@ -374,9 +379,12 @@ static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data
                } else if (is_tcf_skbedit_priority(act)) {
                        entry->id = FLOW_ACTION_PRIORITY;
                        entry->priority = tcf_skbedit_priority(act);
-               } else if (is_tcf_skbedit_queue_mapping(act)) {
-                       NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used");
+               } else if (is_tcf_skbedit_tx_queue_mapping(act)) {
+                       NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used on transmit side");
                        return -EOPNOTSUPP;
+               } else if (is_tcf_skbedit_rx_queue_mapping(act)) {
+                       entry->id = FLOW_ACTION_RX_QUEUE_MAPPING;
+                       entry->rx_queue = tcf_skbedit_rx_queue_mapping(act);
                } else if (is_tcf_skbedit_inheritdsfield(act)) {
                        NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"inheritdsfield\" option is used");
                        return -EOPNOTSUPP;
@@ -394,6 +402,8 @@ static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data
                        fl_action->id = FLOW_ACTION_PTYPE;
                else if (is_tcf_skbedit_priority(act))
                        fl_action->id = FLOW_ACTION_PRIORITY;
+               else if (is_tcf_skbedit_rx_queue_mapping(act))
+                       fl_action->id = FLOW_ACTION_RX_QUEUE_MAPPING;
                else
                        return -EOPNOTSUPP;
        }
index 50566db..23d1cfa 100644 (file)
@@ -1953,6 +1953,11 @@ static void tfilter_put(struct tcf_proto *tp, void *fh)
                tp->ops->put(tp, fh);
 }
 
+static bool is_qdisc_ingress(__u32 classid)
+{
+       return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS));
+}
+
 static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
                          struct netlink_ext_ack *extack)
 {
@@ -2144,6 +2149,8 @@ replay:
                flags |= TCA_ACT_FLAGS_REPLACE;
        if (!rtnl_held)
                flags |= TCA_ACT_FLAGS_NO_RTNL;
+       if (is_qdisc_ingress(parent))
+               flags |= TCA_ACT_FLAGS_AT_INGRESS;
        err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
                              flags, extack);
        if (err == 0) {