Commit 4c5b9d96 authored by Manish Kurup's avatar Manish Kurup Committed by David S. Miller

act_vlan: VLAN action rewrite to use RCU lock/unlock and update

Using a spinlock in the VLAN action causes performance issues when the VLAN
action is used on multiple cores. Rewrote the VLAN action to use RCU read
locking for reads and updates instead.
All functions now use an RCU dereferenced pointer to access the VLAN action
context. Modified helper functions used by other modules, to use the RCU as
opposed to directly accessing the structure.
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Acked-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarManish Kurup <manish.kurup@verizon.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bf068bdd
...@@ -13,12 +13,17 @@ ...@@ -13,12 +13,17 @@
#include <net/act_api.h> #include <net/act_api.h>
#include <linux/tc_act/tc_vlan.h> #include <linux/tc_act/tc_vlan.h>
struct tcf_vlan { struct tcf_vlan_params {
struct tc_action common;
int tcfv_action; int tcfv_action;
u16 tcfv_push_vid; u16 tcfv_push_vid;
__be16 tcfv_push_proto; __be16 tcfv_push_proto;
u8 tcfv_push_prio; u8 tcfv_push_prio;
struct rcu_head rcu;
};
struct tcf_vlan {
struct tc_action common;
struct tcf_vlan_params __rcu *vlan_p;
}; };
#define to_vlan(a) ((struct tcf_vlan *)a) #define to_vlan(a) ((struct tcf_vlan *)a)
...@@ -33,22 +38,45 @@ static inline bool is_tcf_vlan(const struct tc_action *a) ...@@ -33,22 +38,45 @@ static inline bool is_tcf_vlan(const struct tc_action *a)
static inline u32 tcf_vlan_action(const struct tc_action *a) static inline u32 tcf_vlan_action(const struct tc_action *a)
{ {
return to_vlan(a)->tcfv_action; u32 tcfv_action;
rcu_read_lock();
tcfv_action = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_action;
rcu_read_unlock();
return tcfv_action;
} }
static inline u16 tcf_vlan_push_vid(const struct tc_action *a) static inline u16 tcf_vlan_push_vid(const struct tc_action *a)
{ {
return to_vlan(a)->tcfv_push_vid; u16 tcfv_push_vid;
rcu_read_lock();
tcfv_push_vid = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_vid;
rcu_read_unlock();
return tcfv_push_vid;
} }
static inline __be16 tcf_vlan_push_proto(const struct tc_action *a) static inline __be16 tcf_vlan_push_proto(const struct tc_action *a)
{ {
return to_vlan(a)->tcfv_push_proto; __be16 tcfv_push_proto;
rcu_read_lock();
tcfv_push_proto = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_proto;
rcu_read_unlock();
return tcfv_push_proto;
} }
static inline u8 tcf_vlan_push_prio(const struct tc_action *a) static inline u8 tcf_vlan_push_prio(const struct tc_action *a)
{ {
return to_vlan(a)->tcfv_push_prio; u8 tcfv_push_prio;
}
rcu_read_lock();
tcfv_push_prio = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_prio;
rcu_read_unlock();
return tcfv_push_prio;
}
#endif /* __NET_TC_VLAN_H */ #endif /* __NET_TC_VLAN_H */
...@@ -26,6 +26,7 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, ...@@ -26,6 +26,7 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res) struct tcf_result *res)
{ {
struct tcf_vlan *v = to_vlan(a); struct tcf_vlan *v = to_vlan(a);
struct tcf_vlan_params *p;
int action; int action;
int err; int err;
u16 tci; u16 tci;
...@@ -33,24 +34,27 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, ...@@ -33,24 +34,27 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
tcf_lastuse_update(&v->tcf_tm); tcf_lastuse_update(&v->tcf_tm);
bstats_cpu_update(this_cpu_ptr(v->common.cpu_bstats), skb); bstats_cpu_update(this_cpu_ptr(v->common.cpu_bstats), skb);
spin_lock(&v->tcf_lock);
action = v->tcf_action;
/* Ensure 'data' points at mac_header prior calling vlan manipulating /* Ensure 'data' points at mac_header prior calling vlan manipulating
* functions. * functions.
*/ */
if (skb_at_tc_ingress(skb)) if (skb_at_tc_ingress(skb))
skb_push_rcsum(skb, skb->mac_len); skb_push_rcsum(skb, skb->mac_len);
switch (v->tcfv_action) { rcu_read_lock();
action = READ_ONCE(v->tcf_action);
p = rcu_dereference(v->vlan_p);
switch (p->tcfv_action) {
case TCA_VLAN_ACT_POP: case TCA_VLAN_ACT_POP:
err = skb_vlan_pop(skb); err = skb_vlan_pop(skb);
if (err) if (err)
goto drop; goto drop;
break; break;
case TCA_VLAN_ACT_PUSH: case TCA_VLAN_ACT_PUSH:
err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid | err = skb_vlan_push(skb, p->tcfv_push_proto, p->tcfv_push_vid |
(v->tcfv_push_prio << VLAN_PRIO_SHIFT)); (p->tcfv_push_prio << VLAN_PRIO_SHIFT));
if (err) if (err)
goto drop; goto drop;
break; break;
...@@ -69,14 +73,14 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, ...@@ -69,14 +73,14 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
goto drop; goto drop;
} }
/* replace the vid */ /* replace the vid */
tci = (tci & ~VLAN_VID_MASK) | v->tcfv_push_vid; tci = (tci & ~VLAN_VID_MASK) | p->tcfv_push_vid;
/* replace prio bits, if tcfv_push_prio specified */ /* replace prio bits, if tcfv_push_prio specified */
if (v->tcfv_push_prio) { if (p->tcfv_push_prio) {
tci &= ~VLAN_PRIO_MASK; tci &= ~VLAN_PRIO_MASK;
tci |= v->tcfv_push_prio << VLAN_PRIO_SHIFT; tci |= p->tcfv_push_prio << VLAN_PRIO_SHIFT;
} }
/* put updated tci as hwaccel tag */ /* put updated tci as hwaccel tag */
__vlan_hwaccel_put_tag(skb, v->tcfv_push_proto, tci); __vlan_hwaccel_put_tag(skb, p->tcfv_push_proto, tci);
break; break;
default: default:
BUG(); BUG();
...@@ -89,10 +93,10 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, ...@@ -89,10 +93,10 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
qstats_drop_inc(this_cpu_ptr(v->common.cpu_qstats)); qstats_drop_inc(this_cpu_ptr(v->common.cpu_qstats));
unlock: unlock:
rcu_read_unlock();
if (skb_at_tc_ingress(skb)) if (skb_at_tc_ingress(skb))
skb_pull_rcsum(skb, skb->mac_len); skb_pull_rcsum(skb, skb->mac_len);
spin_unlock(&v->tcf_lock);
return action; return action;
} }
...@@ -109,6 +113,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, ...@@ -109,6 +113,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
{ {
struct tc_action_net *tn = net_generic(net, vlan_net_id); struct tc_action_net *tn = net_generic(net, vlan_net_id);
struct nlattr *tb[TCA_VLAN_MAX + 1]; struct nlattr *tb[TCA_VLAN_MAX + 1];
struct tcf_vlan_params *p, *p_old;
struct tc_vlan *parm; struct tc_vlan *parm;
struct tcf_vlan *v; struct tcf_vlan *v;
int action; int action;
...@@ -187,46 +192,67 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, ...@@ -187,46 +192,67 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
v = to_vlan(*a); v = to_vlan(*a);
spin_lock_bh(&v->tcf_lock); ASSERT_RTNL();
p = kzalloc(sizeof(*p), GFP_KERNEL);
v->tcfv_action = action; if (!p) {
v->tcfv_push_vid = push_vid; if (ovr)
v->tcfv_push_prio = push_prio; tcf_idr_release(*a, bind);
v->tcfv_push_proto = push_proto; return -ENOMEM;
}
v->tcf_action = parm->action; v->tcf_action = parm->action;
spin_unlock_bh(&v->tcf_lock); p_old = rtnl_dereference(v->vlan_p);
p->tcfv_action = action;
p->tcfv_push_vid = push_vid;
p->tcfv_push_prio = push_prio;
p->tcfv_push_proto = push_proto;
rcu_assign_pointer(v->vlan_p, p);
if (p_old)
kfree_rcu(p_old, rcu);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
return ret; return ret;
} }
static void tcf_vlan_cleanup(struct tc_action *a, int bind)
{
struct tcf_vlan *v = to_vlan(a);
struct tcf_vlan_params *p;
p = rcu_dereference_protected(v->vlan_p, 1);
kfree_rcu(p, rcu);
}
static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a, static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a,
int bind, int ref) int bind, int ref)
{ {
unsigned char *b = skb_tail_pointer(skb); unsigned char *b = skb_tail_pointer(skb);
struct tcf_vlan *v = to_vlan(a); struct tcf_vlan *v = to_vlan(a);
struct tcf_vlan_params *p = rtnl_dereference(v->vlan_p);
struct tc_vlan opt = { struct tc_vlan opt = {
.index = v->tcf_index, .index = v->tcf_index,
.refcnt = v->tcf_refcnt - ref, .refcnt = v->tcf_refcnt - ref,
.bindcnt = v->tcf_bindcnt - bind, .bindcnt = v->tcf_bindcnt - bind,
.action = v->tcf_action, .action = v->tcf_action,
.v_action = v->tcfv_action, .v_action = p->tcfv_action,
}; };
struct tcf_t t; struct tcf_t t;
if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt)) if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt))
goto nla_put_failure; goto nla_put_failure;
if ((v->tcfv_action == TCA_VLAN_ACT_PUSH || if ((p->tcfv_action == TCA_VLAN_ACT_PUSH ||
v->tcfv_action == TCA_VLAN_ACT_MODIFY) && p->tcfv_action == TCA_VLAN_ACT_MODIFY) &&
(nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) || (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, p->tcfv_push_vid) ||
nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL, nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL,
v->tcfv_push_proto) || p->tcfv_push_proto) ||
(nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY, (nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY,
v->tcfv_push_prio)))) p->tcfv_push_prio))))
goto nla_put_failure; goto nla_put_failure;
tcf_tm_dump(&t, &v->tcf_tm); tcf_tm_dump(&t, &v->tcf_tm);
...@@ -262,6 +288,7 @@ static struct tc_action_ops act_vlan_ops = { ...@@ -262,6 +288,7 @@ static struct tc_action_ops act_vlan_ops = {
.act = tcf_vlan, .act = tcf_vlan,
.dump = tcf_vlan_dump, .dump = tcf_vlan_dump,
.init = tcf_vlan_init, .init = tcf_vlan_init,
.cleanup = tcf_vlan_cleanup,
.walk = tcf_vlan_walker, .walk = tcf_vlan_walker,
.lookup = tcf_vlan_search, .lookup = tcf_vlan_search,
.size = sizeof(struct tcf_vlan), .size = sizeof(struct tcf_vlan),
......
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