Merge branch 'cls_u32_hw_sw'

Sridhar Samudrala says:

====================
Enable SW only or HW only offloads with u32 classifier

This set of patches export TCA_CLS_FLAGS_SKIP_HW to userspace and also
introduces another flag TCA_CLS_FLAGS_SKIP_SW. These flags enable offloading
u32 filters to either SW or HW only.

The default semantics with no flags is to add the filter to HW if possible and
also into SW.
With SKIP_HW flag, the filter is only added to SW.
With SKIP_SW flag, the filter is added to HW and an error is returned
to user on failure.
These flags are mutually exclusive.
There was an earlier discussion on these semantics in the following email
thread.
	http://thread.gmane.org/gmane.linux.network/401733
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-05-16 13:30:57 -04:00
commit 1ca4673432
3 changed files with 57 additions and 12 deletions

View file

@ -392,9 +392,6 @@ struct tc_cls_u32_offload {
};
};
/* tca flags definitions */
#define TCA_CLS_FLAGS_SKIP_HW 1
static inline bool tc_should_offload(struct net_device *dev, u32 flags)
{
if (!(dev->features & NETIF_F_HW_TC))
@ -409,6 +406,23 @@ static inline bool tc_should_offload(struct net_device *dev, u32 flags)
return true;
}
static inline bool tc_skip_sw(u32 flags)
{
return (flags & TCA_CLS_FLAGS_SKIP_SW) ? true : false;
}
/* SKIP_HW and SKIP_SW are mutually exclusive flags. */
static inline bool tc_flags_valid(u32 flags)
{
if (flags & ~(TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW))
return false;
if (!(flags ^ (TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW)))
return false;
return true;
}
enum tc_fl_command {
TC_CLSFLOWER_REPLACE,
TC_CLSFLOWER_DESTROY,

View file

@ -151,6 +151,10 @@ enum {
#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
/* tca flags definitions */
#define TCA_CLS_FLAGS_SKIP_HW (1 << 0)
#define TCA_CLS_FLAGS_SKIP_SW (1 << 1)
/* U32 filters */
#define TC_U32_HTID(h) ((h)&0xFFF00000)

View file

@ -134,6 +134,11 @@ next_knode:
j = 0;
#endif
if (tc_skip_sw(n->flags)) {
n = rcu_dereference_bh(n->next);
goto next_knode;
}
#ifdef CONFIG_CLS_U32_MARK
if ((skb->mark & n->mask) != n->val) {
n = rcu_dereference_bh(n->next);
@ -443,13 +448,14 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle)
}
}
static void u32_replace_hw_hnode(struct tcf_proto *tp,
static int u32_replace_hw_hnode(struct tcf_proto *tp,
struct tc_u_hnode *h,
u32 flags)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_u32_offload u32_offload = {0};
struct tc_to_netdev offload;
int err;
offload.type = TC_SETUP_CLSU32;
offload.cls_u32 = &u32_offload;
@ -460,9 +466,13 @@ static void u32_replace_hw_hnode(struct tcf_proto *tp,
offload.cls_u32->hnode.handle = h->handle;
offload.cls_u32->hnode.prio = h->prio;
dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle,
tp->protocol, &offload);
err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle,
tp->protocol, &offload);
if (tc_skip_sw(flags))
return err;
}
return 0;
}
static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h)
@ -485,13 +495,14 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h)
}
}
static void u32_replace_hw_knode(struct tcf_proto *tp,
static int u32_replace_hw_knode(struct tcf_proto *tp,
struct tc_u_knode *n,
u32 flags)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_u32_offload u32_offload = {0};
struct tc_to_netdev offload;
int err;
offload.type = TC_SETUP_CLSU32;
offload.cls_u32 = &u32_offload;
@ -512,9 +523,13 @@ static void u32_replace_hw_knode(struct tcf_proto *tp,
if (n->ht_down)
offload.cls_u32->knode.link_handle = n->ht_down->handle;
dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle,
tp->protocol, &offload);
err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle,
tp->protocol, &offload);
if (tc_skip_sw(flags))
return err;
}
return 0;
}
static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
@ -845,8 +860,11 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
return err;
if (tb[TCA_U32_FLAGS])
if (tb[TCA_U32_FLAGS]) {
flags = nla_get_u32(tb[TCA_U32_FLAGS]);
if (!tc_flags_valid(flags))
return err;
}
n = (struct tc_u_knode *)*arg;
if (n) {
@ -871,10 +889,15 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
return err;
}
err = u32_replace_hw_knode(tp, new, flags);
if (err) {
u32_destroy_key(tp, new, false);
return err;
}
u32_replace_knode(tp, tp_c, new);
tcf_unbind_filter(tp, &n->res);
call_rcu(&n->rcu, u32_delete_key_rcu);
u32_replace_hw_knode(tp, new, flags);
return 0;
}
@ -978,6 +1001,10 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
struct tc_u_knode __rcu **ins;
struct tc_u_knode *pins;
err = u32_replace_hw_knode(tp, n, flags);
if (err)
goto errhw;
ins = &ht->ht[TC_U32_HASH(handle)];
for (pins = rtnl_dereference(*ins); pins;
ins = &pins->next, pins = rtnl_dereference(*ins))
@ -986,11 +1013,11 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
RCU_INIT_POINTER(n->next, pins);
rcu_assign_pointer(*ins, n);
u32_replace_hw_knode(tp, n, flags);
*arg = (unsigned long)n;
return 0;
}
errhw:
#ifdef CONFIG_CLS_U32_MARK
free_percpu(n->pcpu_success);
errout: