Commit 1ca46734 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 860d7ef6 d34e3e18
...@@ -392,9 +392,6 @@ struct tc_cls_u32_offload { ...@@ -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) static inline bool tc_should_offload(struct net_device *dev, u32 flags)
{ {
if (!(dev->features & NETIF_F_HW_TC)) if (!(dev->features & NETIF_F_HW_TC))
...@@ -409,6 +406,23 @@ static inline bool tc_should_offload(struct net_device *dev, u32 flags) ...@@ -409,6 +406,23 @@ static inline bool tc_should_offload(struct net_device *dev, u32 flags)
return true; 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 { enum tc_fl_command {
TC_CLSFLOWER_REPLACE, TC_CLSFLOWER_REPLACE,
TC_CLSFLOWER_DESTROY, TC_CLSFLOWER_DESTROY,
......
...@@ -151,6 +151,10 @@ enum { ...@@ -151,6 +151,10 @@ enum {
#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) #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 */ /* U32 filters */
#define TC_U32_HTID(h) ((h)&0xFFF00000) #define TC_U32_HTID(h) ((h)&0xFFF00000)
......
...@@ -134,6 +134,11 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct ...@@ -134,6 +134,11 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct
j = 0; j = 0;
#endif #endif
if (tc_skip_sw(n->flags)) {
n = rcu_dereference_bh(n->next);
goto next_knode;
}
#ifdef CONFIG_CLS_U32_MARK #ifdef CONFIG_CLS_U32_MARK
if ((skb->mark & n->mask) != n->val) { if ((skb->mark & n->mask) != n->val) {
n = rcu_dereference_bh(n->next); n = rcu_dereference_bh(n->next);
...@@ -443,13 +448,14 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) ...@@ -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, struct tc_u_hnode *h,
u32 flags) u32 flags)
{ {
struct net_device *dev = tp->q->dev_queue->dev; struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_u32_offload u32_offload = {0}; struct tc_cls_u32_offload u32_offload = {0};
struct tc_to_netdev offload; struct tc_to_netdev offload;
int err;
offload.type = TC_SETUP_CLSU32; offload.type = TC_SETUP_CLSU32;
offload.cls_u32 = &u32_offload; offload.cls_u32 = &u32_offload;
...@@ -460,9 +466,13 @@ static void u32_replace_hw_hnode(struct tcf_proto *tp, ...@@ -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.handle = h->handle;
offload.cls_u32->hnode.prio = h->prio; offload.cls_u32->hnode.prio = h->prio;
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 (tc_skip_sw(flags))
return err;
} }
return 0;
} }
static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) 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) ...@@ -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, struct tc_u_knode *n,
u32 flags) u32 flags)
{ {
struct net_device *dev = tp->q->dev_queue->dev; struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_u32_offload u32_offload = {0}; struct tc_cls_u32_offload u32_offload = {0};
struct tc_to_netdev offload; struct tc_to_netdev offload;
int err;
offload.type = TC_SETUP_CLSU32; offload.type = TC_SETUP_CLSU32;
offload.cls_u32 = &u32_offload; offload.cls_u32 = &u32_offload;
...@@ -512,9 +523,13 @@ static void u32_replace_hw_knode(struct tcf_proto *tp, ...@@ -512,9 +523,13 @@ static void u32_replace_hw_knode(struct tcf_proto *tp,
if (n->ht_down) if (n->ht_down)
offload.cls_u32->knode.link_handle = n->ht_down->handle; offload.cls_u32->knode.link_handle = n->ht_down->handle;
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 (tc_skip_sw(flags))
return err;
} }
return 0;
} }
static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) 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, ...@@ -845,8 +860,11 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
if (err < 0) if (err < 0)
return err; return err;
if (tb[TCA_U32_FLAGS]) if (tb[TCA_U32_FLAGS]) {
flags = nla_get_u32(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; n = (struct tc_u_knode *)*arg;
if (n) { if (n) {
...@@ -871,10 +889,15 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -871,10 +889,15 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
return err; 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); 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);
u32_replace_hw_knode(tp, new, flags);
return 0; return 0;
} }
...@@ -978,6 +1001,10 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -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 __rcu **ins;
struct tc_u_knode *pins; struct tc_u_knode *pins;
err = u32_replace_hw_knode(tp, n, flags);
if (err)
goto errhw;
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))
...@@ -986,11 +1013,11 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -986,11 +1013,11 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
RCU_INIT_POINTER(n->next, pins); RCU_INIT_POINTER(n->next, pins);
rcu_assign_pointer(*ins, n); rcu_assign_pointer(*ins, n);
u32_replace_hw_knode(tp, n, flags);
*arg = (unsigned long)n; *arg = (unsigned long)n;
return 0; return 0;
} }
errhw:
#ifdef CONFIG_CLS_U32_MARK #ifdef CONFIG_CLS_U32_MARK
free_percpu(n->pcpu_success); free_percpu(n->pcpu_success);
errout: errout:
......
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