Commit 4b5026ad authored by David S. Miller's avatar David S. Miller

Merge branch 'net-sched-reflect-hw-offload-in-classifiers'

Or Gerlitz says:

====================
net/sched: Reflect HW offload status in classifiers

Currently there is no way of querying whether a filter is
offloaded to HW or not when using "both" policy (where none
of skip_sw or skip_hw flags are set by user-space).

Added two new flags, "in hw" and "not in hw" such that user space
can determine if a filter is actually offloaded to hw. The "in hw"
UAPI semantics was chosen so it's similar to the "skip hw" flag logic.

If none of these two flags are set, this signals running
over older kernel.

As an example, add one vlan push + fwd rule, one matchall rule and one u32 rule
without any flags, and another vlan + fwd skip_sw rule, such that the different TC
classifier attempt to offload all of them -- all over mlx5 SRIOV VF rep:

	flower skip_sw indev eth2_0 src_mac e4:11:22:33:44:50 dst_mac e4:1d:2d:a5:f3:9d
	action vlan push id 52 action mirred egress redirect dev eth2

	flower indev eth2_0 src_mac e4:11:22:33:44:50 dst_mac e4:11:22:33:44:51
	action vlan push id 53 action mirred egress redirect dev eth2

	u32 ht 800: flowid 800:1 match ip src 192.168.1.0/24 action drop

Since that VF rep doesn't offload matchall/u32 and can currently offload
only one vlan push rule we expect three of the rules not to be offloaded:

filter protocol ip pref 99 u32
filter protocol ip pref 99 u32 fh 800: ht divisor 1
filter protocol ip pref 99 u32 fh 800::1 order 1 key ht 800 bkt 0 flowid 800:1 not in_hw
  match c0a80100/ffffff00 at 12
	action order 1: gact action drop
	 random type none pass val 0
	 index 8 ref 1 bind 1

filter protocol all pref 49150 matchall
filter protocol all pref 49150 matchall handle 0x1
  not in_hw
	action order 1: mirred (Egress Mirror to device veth1) pipe
 	index 27 ref 1 bind 1

filter protocol ip pref 49151 flower
filter protocol ip pref 49151 flower handle 0x1
  indev eth2_0
  dst_mac e4:11:22:33:44:51
  src_mac e4:11:22:33:44:50
  eth_type ipv4
  not in_hw
	action order 1:  vlan push id 53 protocol 802.1Q priority 0 pipe
	 index 20 ref 1 bind 1

	action order 2: mirred (Egress Redirect to device eth2) stolen
 	index 26 ref 1 bind 1

filter protocol ip pref 49152 flower
filter protocol ip pref 49152 flower handle 0x1
  indev eth2_0
  dst_mac e4:1d:2d:a5:f3:9d
  src_mac e4:11:22:33:44:50
  eth_type ipv4
  skip_sw
  in_hw
	action order 1:  vlan push id 52 protocol 802.1Q priority 0 pipe
	 index 19 ref 1 bind 1

	action order 2: mirred (Egress Redirect to device eth2) stolen
 	index 25 ref 1 bind 1

v3 --> v4 changes:
 - removed extra parenthesis (Dave)

v2 --> v3 changes:
 - fixed the matchall dump flags patch to do proper checks (Jakub)
 - added the same proper checks to flower where they were missing
 - that flower patch was added as #1 and hence all the other patches are offed-by-one

v1 --> v2 changes:
 - applied feedback from Jakub and Dave -- where none of the skip flags were set,
   the suggested approach didn't allow user space to distringuish between old kernel
   to a case when offloading to HW worked fine.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ad8e963c 5cecb6cc
...@@ -481,6 +481,11 @@ static inline bool tc_flags_valid(u32 flags) ...@@ -481,6 +481,11 @@ static inline bool tc_flags_valid(u32 flags)
return true; return true;
} }
static inline bool tc_in_hw(u32 flags)
{
return (flags & TCA_CLS_FLAGS_IN_HW) ? true : false;
}
enum tc_fl_command { enum tc_fl_command {
TC_CLSFLOWER_REPLACE, TC_CLSFLOWER_REPLACE,
TC_CLSFLOWER_DESTROY, TC_CLSFLOWER_DESTROY,
......
...@@ -103,8 +103,10 @@ enum { ...@@ -103,8 +103,10 @@ enum {
#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) #define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
/* tca flags definitions */ /* tca flags definitions */
#define TCA_CLS_FLAGS_SKIP_HW (1 << 0) #define TCA_CLS_FLAGS_SKIP_HW (1 << 0) /* don't offload filter to HW */
#define TCA_CLS_FLAGS_SKIP_SW (1 << 1) #define TCA_CLS_FLAGS_SKIP_SW (1 << 1) /* don't use filter in SW */
#define TCA_CLS_FLAGS_IN_HW (1 << 2) /* filter is offloaded to HW */
#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */
/* U32 filters */ /* U32 filters */
......
...@@ -148,6 +148,7 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, ...@@ -148,6 +148,7 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
struct net_device *dev = tp->q->dev_queue->dev; struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_bpf_offload bpf_offload = {}; struct tc_cls_bpf_offload bpf_offload = {};
struct tc_to_netdev offload; struct tc_to_netdev offload;
int err;
offload.type = TC_SETUP_CLSBPF; offload.type = TC_SETUP_CLSBPF;
offload.cls_bpf = &bpf_offload; offload.cls_bpf = &bpf_offload;
...@@ -159,8 +160,13 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, ...@@ -159,8 +160,13 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
bpf_offload.exts_integrated = prog->exts_integrated; bpf_offload.exts_integrated = prog->exts_integrated;
bpf_offload.gen_flags = prog->gen_flags; bpf_offload.gen_flags = prog->gen_flags;
return dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle,
tp->protocol, &offload); tp->protocol, &offload);
if (!err && (cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE))
prog->gen_flags |= TCA_CLS_FLAGS_IN_HW;
return err;
} }
static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog, static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog,
...@@ -511,6 +517,9 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, ...@@ -511,6 +517,9 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
return ret; return ret;
} }
if (!tc_in_hw(prog->gen_flags))
prog->gen_flags |= TCA_CLS_FLAGS_NOT_IN_HW;
if (oldprog) { if (oldprog) {
list_replace_rcu(&oldprog->link, &prog->link); list_replace_rcu(&oldprog->link, &prog->link);
tcf_unbind_filter(tp, &oldprog->res); tcf_unbind_filter(tp, &oldprog->res);
......
...@@ -273,6 +273,8 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, ...@@ -273,6 +273,8 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol,
tc); tc);
if (!err)
f->flags |= TCA_CLS_FLAGS_IN_HW;
if (tc_skip_sw(f->flags)) if (tc_skip_sw(f->flags))
return err; return err;
...@@ -912,6 +914,9 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, ...@@ -912,6 +914,9 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
goto errout; goto errout;
} }
if (!tc_in_hw(fnew->flags))
fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
if (fold) { if (fold) {
if (!tc_skip_sw(fold->flags)) if (!tc_skip_sw(fold->flags))
rhashtable_remove_fast(&head->ht, &fold->ht_node, rhashtable_remove_fast(&head->ht, &fold->ht_node,
...@@ -1229,7 +1234,8 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, ...@@ -1229,7 +1234,8 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags)) if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags))
goto nla_put_failure; goto nla_put_failure;
nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags); if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags))
goto nla_put_failure;
if (tcf_exts_dump(skb, &f->exts)) if (tcf_exts_dump(skb, &f->exts))
goto nla_put_failure; goto nla_put_failure;
......
...@@ -56,6 +56,7 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, ...@@ -56,6 +56,7 @@ static int mall_replace_hw_filter(struct tcf_proto *tp,
struct net_device *dev = tp->q->dev_queue->dev; struct net_device *dev = tp->q->dev_queue->dev;
struct tc_to_netdev offload; struct tc_to_netdev offload;
struct tc_cls_matchall_offload mall_offload = {0}; struct tc_cls_matchall_offload mall_offload = {0};
int err;
offload.type = TC_SETUP_MATCHALL; offload.type = TC_SETUP_MATCHALL;
offload.cls_mall = &mall_offload; offload.cls_mall = &mall_offload;
...@@ -63,8 +64,12 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, ...@@ -63,8 +64,12 @@ static int mall_replace_hw_filter(struct tcf_proto *tp,
offload.cls_mall->exts = &head->exts; offload.cls_mall->exts = &head->exts;
offload.cls_mall->cookie = cookie; offload.cls_mall->cookie = cookie;
return dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol,
&offload); &offload);
if (!err)
head->flags |= TCA_CLS_FLAGS_IN_HW;
return err;
} }
static void mall_destroy_hw_filter(struct tcf_proto *tp, static void mall_destroy_hw_filter(struct tcf_proto *tp,
...@@ -194,6 +199,9 @@ static int mall_change(struct net *net, struct sk_buff *in_skb, ...@@ -194,6 +199,9 @@ static int mall_change(struct net *net, struct sk_buff *in_skb,
} }
} }
if (!tc_in_hw(new->flags))
new->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
*arg = (unsigned long) head; *arg = (unsigned long) head;
rcu_assign_pointer(tp->root, new); rcu_assign_pointer(tp->root, new);
if (head) if (head)
...@@ -244,6 +252,9 @@ static int mall_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, ...@@ -244,6 +252,9 @@ static int mall_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_MATCHALL_CLASSID, head->res.classid)) nla_put_u32(skb, TCA_MATCHALL_CLASSID, head->res.classid))
goto nla_put_failure; goto nla_put_failure;
if (head->flags && nla_put_u32(skb, TCA_MATCHALL_FLAGS, head->flags))
goto nla_put_failure;
if (tcf_exts_dump(skb, &head->exts)) if (tcf_exts_dump(skb, &head->exts))
goto nla_put_failure; goto nla_put_failure;
......
...@@ -523,6 +523,10 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, ...@@ -523,6 +523,10 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle,
tp->protocol, &offload); tp->protocol, &offload);
if (!err)
n->flags |= TCA_CLS_FLAGS_IN_HW;
if (tc_skip_sw(flags)) if (tc_skip_sw(flags))
return err; return err;
...@@ -895,6 +899,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -895,6 +899,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
return err; return err;
} }
if (!tc_in_hw(new->flags))
new->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
u32_replace_knode(tp, tp_c, new); u32_replace_knode(tp, tp_c, new);
tcf_unbind_filter(tp, &n->res); tcf_unbind_filter(tp, &n->res);
call_rcu(&n->rcu, u32_delete_key_rcu); call_rcu(&n->rcu, u32_delete_key_rcu);
...@@ -1014,6 +1021,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -1014,6 +1021,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
if (err) if (err)
goto errhw; goto errhw;
if (!tc_in_hw(n->flags))
n->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
ins = &ht->ht[TC_U32_HASH(handle)]; ins = &ht->ht[TC_U32_HASH(handle)];
for (pins = rtnl_dereference(*ins); pins; for (pins = rtnl_dereference(*ins); pins;
ins = &pins->next, pins = rtnl_dereference(*ins)) ins = &pins->next, pins = rtnl_dereference(*ins))
......
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