Skip to content

Commit 4c5b9d9

Browse files
redmazdadavem330
authored andcommitted
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: Jamal Hadi Salim <jhs@mojatatu.com> Acked-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: Manish Kurup <manish.kurup@verizon.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent bf068bd commit 4c5b9d9

File tree

2 files changed

+88
-33
lines changed

2 files changed

+88
-33
lines changed

include/net/tc_act/tc_vlan.h

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@
1313
#include <net/act_api.h>
1414
#include <linux/tc_act/tc_vlan.h>
1515

16+
struct tcf_vlan_params {
17+
int tcfv_action;
18+
u16 tcfv_push_vid;
19+
__be16 tcfv_push_proto;
20+
u8 tcfv_push_prio;
21+
struct rcu_head rcu;
22+
};
23+
1624
struct tcf_vlan {
1725
struct tc_action common;
18-
int tcfv_action;
19-
u16 tcfv_push_vid;
20-
__be16 tcfv_push_proto;
21-
u8 tcfv_push_prio;
26+
struct tcf_vlan_params __rcu *vlan_p;
2227
};
2328
#define to_vlan(a) ((struct tcf_vlan *)a)
2429

@@ -33,22 +38,45 @@ static inline bool is_tcf_vlan(const struct tc_action *a)
3338

3439
static inline u32 tcf_vlan_action(const struct tc_action *a)
3540
{
36-
return to_vlan(a)->tcfv_action;
41+
u32 tcfv_action;
42+
43+
rcu_read_lock();
44+
tcfv_action = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_action;
45+
rcu_read_unlock();
46+
47+
return tcfv_action;
3748
}
3849

3950
static inline u16 tcf_vlan_push_vid(const struct tc_action *a)
4051
{
41-
return to_vlan(a)->tcfv_push_vid;
52+
u16 tcfv_push_vid;
53+
54+
rcu_read_lock();
55+
tcfv_push_vid = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_vid;
56+
rcu_read_unlock();
57+
58+
return tcfv_push_vid;
4259
}
4360

4461
static inline __be16 tcf_vlan_push_proto(const struct tc_action *a)
4562
{
46-
return to_vlan(a)->tcfv_push_proto;
63+
__be16 tcfv_push_proto;
64+
65+
rcu_read_lock();
66+
tcfv_push_proto = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_proto;
67+
rcu_read_unlock();
68+
69+
return tcfv_push_proto;
4770
}
4871

4972
static inline u8 tcf_vlan_push_prio(const struct tc_action *a)
5073
{
51-
return to_vlan(a)->tcfv_push_prio;
52-
}
74+
u8 tcfv_push_prio;
5375

76+
rcu_read_lock();
77+
tcfv_push_prio = rcu_dereference(to_vlan(a)->vlan_p)->tcfv_push_prio;
78+
rcu_read_unlock();
79+
80+
return tcfv_push_prio;
81+
}
5482
#endif /* __NET_TC_VLAN_H */

net/sched/act_vlan.c

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,35 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
2626
struct tcf_result *res)
2727
{
2828
struct tcf_vlan *v = to_vlan(a);
29+
struct tcf_vlan_params *p;
2930
int action;
3031
int err;
3132
u16 tci;
3233

3334
tcf_lastuse_update(&v->tcf_tm);
3435
bstats_cpu_update(this_cpu_ptr(v->common.cpu_bstats), skb);
3536

36-
spin_lock(&v->tcf_lock);
37-
action = v->tcf_action;
38-
3937
/* Ensure 'data' points at mac_header prior calling vlan manipulating
4038
* functions.
4139
*/
4240
if (skb_at_tc_ingress(skb))
4341
skb_push_rcsum(skb, skb->mac_len);
4442

45-
switch (v->tcfv_action) {
43+
rcu_read_lock();
44+
45+
action = READ_ONCE(v->tcf_action);
46+
47+
p = rcu_dereference(v->vlan_p);
48+
49+
switch (p->tcfv_action) {
4650
case TCA_VLAN_ACT_POP:
4751
err = skb_vlan_pop(skb);
4852
if (err)
4953
goto drop;
5054
break;
5155
case TCA_VLAN_ACT_PUSH:
52-
err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid |
53-
(v->tcfv_push_prio << VLAN_PRIO_SHIFT));
56+
err = skb_vlan_push(skb, p->tcfv_push_proto, p->tcfv_push_vid |
57+
(p->tcfv_push_prio << VLAN_PRIO_SHIFT));
5458
if (err)
5559
goto drop;
5660
break;
@@ -69,14 +73,14 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
6973
goto drop;
7074
}
7175
/* replace the vid */
72-
tci = (tci & ~VLAN_VID_MASK) | v->tcfv_push_vid;
76+
tci = (tci & ~VLAN_VID_MASK) | p->tcfv_push_vid;
7377
/* replace prio bits, if tcfv_push_prio specified */
74-
if (v->tcfv_push_prio) {
78+
if (p->tcfv_push_prio) {
7579
tci &= ~VLAN_PRIO_MASK;
76-
tci |= v->tcfv_push_prio << VLAN_PRIO_SHIFT;
80+
tci |= p->tcfv_push_prio << VLAN_PRIO_SHIFT;
7781
}
7882
/* put updated tci as hwaccel tag */
79-
__vlan_hwaccel_put_tag(skb, v->tcfv_push_proto, tci);
83+
__vlan_hwaccel_put_tag(skb, p->tcfv_push_proto, tci);
8084
break;
8185
default:
8286
BUG();
@@ -89,10 +93,10 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
8993
qstats_drop_inc(this_cpu_ptr(v->common.cpu_qstats));
9094

9195
unlock:
96+
rcu_read_unlock();
9297
if (skb_at_tc_ingress(skb))
9398
skb_pull_rcsum(skb, skb->mac_len);
9499

95-
spin_unlock(&v->tcf_lock);
96100
return action;
97101
}
98102

@@ -109,6 +113,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
109113
{
110114
struct tc_action_net *tn = net_generic(net, vlan_net_id);
111115
struct nlattr *tb[TCA_VLAN_MAX + 1];
116+
struct tcf_vlan_params *p, *p_old;
112117
struct tc_vlan *parm;
113118
struct tcf_vlan *v;
114119
int action;
@@ -187,46 +192,67 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
187192

188193
v = to_vlan(*a);
189194

190-
spin_lock_bh(&v->tcf_lock);
191-
192-
v->tcfv_action = action;
193-
v->tcfv_push_vid = push_vid;
194-
v->tcfv_push_prio = push_prio;
195-
v->tcfv_push_proto = push_proto;
195+
ASSERT_RTNL();
196+
p = kzalloc(sizeof(*p), GFP_KERNEL);
197+
if (!p) {
198+
if (ovr)
199+
tcf_idr_release(*a, bind);
200+
return -ENOMEM;
201+
}
196202

197203
v->tcf_action = parm->action;
198204

199-
spin_unlock_bh(&v->tcf_lock);
205+
p_old = rtnl_dereference(v->vlan_p);
206+
207+
p->tcfv_action = action;
208+
p->tcfv_push_vid = push_vid;
209+
p->tcfv_push_prio = push_prio;
210+
p->tcfv_push_proto = push_proto;
211+
212+
rcu_assign_pointer(v->vlan_p, p);
213+
214+
if (p_old)
215+
kfree_rcu(p_old, rcu);
200216

201217
if (ret == ACT_P_CREATED)
202218
tcf_idr_insert(tn, *a);
203219
return ret;
204220
}
205221

222+
static void tcf_vlan_cleanup(struct tc_action *a, int bind)
223+
{
224+
struct tcf_vlan *v = to_vlan(a);
225+
struct tcf_vlan_params *p;
226+
227+
p = rcu_dereference_protected(v->vlan_p, 1);
228+
kfree_rcu(p, rcu);
229+
}
230+
206231
static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a,
207232
int bind, int ref)
208233
{
209234
unsigned char *b = skb_tail_pointer(skb);
210235
struct tcf_vlan *v = to_vlan(a);
236+
struct tcf_vlan_params *p = rtnl_dereference(v->vlan_p);
211237
struct tc_vlan opt = {
212238
.index = v->tcf_index,
213239
.refcnt = v->tcf_refcnt - ref,
214240
.bindcnt = v->tcf_bindcnt - bind,
215241
.action = v->tcf_action,
216-
.v_action = v->tcfv_action,
242+
.v_action = p->tcfv_action,
217243
};
218244
struct tcf_t t;
219245

220246
if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt))
221247
goto nla_put_failure;
222248

223-
if ((v->tcfv_action == TCA_VLAN_ACT_PUSH ||
224-
v->tcfv_action == TCA_VLAN_ACT_MODIFY) &&
225-
(nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) ||
249+
if ((p->tcfv_action == TCA_VLAN_ACT_PUSH ||
250+
p->tcfv_action == TCA_VLAN_ACT_MODIFY) &&
251+
(nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, p->tcfv_push_vid) ||
226252
nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL,
227-
v->tcfv_push_proto) ||
253+
p->tcfv_push_proto) ||
228254
(nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY,
229-
v->tcfv_push_prio))))
255+
p->tcfv_push_prio))))
230256
goto nla_put_failure;
231257

232258
tcf_tm_dump(&t, &v->tcf_tm);
@@ -262,6 +288,7 @@ static struct tc_action_ops act_vlan_ops = {
262288
.act = tcf_vlan,
263289
.dump = tcf_vlan_dump,
264290
.init = tcf_vlan_init,
291+
.cleanup = tcf_vlan_cleanup,
265292
.walk = tcf_vlan_walker,
266293
.lookup = tcf_vlan_search,
267294
.size = sizeof(struct tcf_vlan),

0 commit comments

Comments
 (0)