summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlad Buslov <vladbu@mellanox.com>2019-09-13 11:28:40 -0400
committerDavid S. Miller <davem@davemloft.net>2019-09-16 03:18:03 -0400
commit4a5da47d5cb6aba3c26a5cc0dddfb2d577e851e9 (patch)
treec6f58d1ec493b1cd695eb7135af7255f2fc9c20d
parent1158958a218bb55d1c358200d7f82808d11bf929 (diff)
net: sched: take reference to psample group in flow_action infra
With recent patch set that removed rtnl lock dependency from cls hardware offload API rtnl lock is only taken when reading action data and can be released after action-specific data is parsed into intermediate representation. However, sample action psample group is passed by pointer without obtaining reference to it first, which makes it possible to concurrently overwrite the action and deallocate object pointed by psample_group pointer after rtnl lock is released but before driver finished using the pointer. To prevent such race condition, obtain reference to psample group while it is used by flow_action infra. Extend psample API with function psample_group_take() that increments psample group reference counter. Extend struct tc_action_ops with new get_psample_group() API. Implement the API for action sample using psample_group_take() and already existing psample_group_put() as a destructor. Use it in tc_setup_flow_action() to take reference to psample group pointed to by entry->sample.psample_group and release it in tc_cleanup_flow_action(). Disable bh when taking psample_groups_lock. The lock is now taken while holding action tcf_lock that is used by data path and requires bh to be disabled, so doing the same for psample_groups_lock is necessary to preserve SOFTIRQ-irq-safety. Fixes: 918190f50eb6 ("net: sched: flower: don't take rtnl lock for cls hw offloads API") Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/act_api.h5
-rw-r--r--include/net/psample.h1
-rw-r--r--include/net/tc_act/tc_sample.h6
-rw-r--r--net/psample/psample.c20
-rw-r--r--net/sched/act_sample.c27
-rw-r--r--net/sched/cls_api.c13
6 files changed, 58 insertions, 14 deletions
diff --git a/include/net/act_api.h b/include/net/act_api.h
index 3a1a72990fce..4be8b0daedf0 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -78,6 +78,8 @@ static inline void tcf_tm_dump(struct tcf_t *dtm, const struct tcf_t *stm)
78#define ACT_P_CREATED 1 78#define ACT_P_CREATED 1
79#define ACT_P_DELETED 1 79#define ACT_P_DELETED 1
80 80
81typedef void (*tc_action_priv_destructor)(void *priv);
82
81struct tc_action_ops { 83struct tc_action_ops {
82 struct list_head head; 84 struct list_head head;
83 char kind[IFNAMSIZ]; 85 char kind[IFNAMSIZ];
@@ -101,6 +103,9 @@ struct tc_action_ops {
101 size_t (*get_fill_size)(const struct tc_action *act); 103 size_t (*get_fill_size)(const struct tc_action *act);
102 struct net_device *(*get_dev)(const struct tc_action *a); 104 struct net_device *(*get_dev)(const struct tc_action *a);
103 void (*put_dev)(struct net_device *dev); 105 void (*put_dev)(struct net_device *dev);
106 struct psample_group *
107 (*get_psample_group)(const struct tc_action *a,
108 tc_action_priv_destructor *destructor);
104}; 109};
105 110
106struct tc_action_net { 111struct tc_action_net {
diff --git a/include/net/psample.h b/include/net/psample.h
index 6b578ce69cd8..68ae16bb0a4a 100644
--- a/include/net/psample.h
+++ b/include/net/psample.h
@@ -15,6 +15,7 @@ struct psample_group {
15}; 15};
16 16
17struct psample_group *psample_group_get(struct net *net, u32 group_num); 17struct psample_group *psample_group_get(struct net *net, u32 group_num);
18void psample_group_take(struct psample_group *group);
18void psample_group_put(struct psample_group *group); 19void psample_group_put(struct psample_group *group);
19 20
20#if IS_ENABLED(CONFIG_PSAMPLE) 21#if IS_ENABLED(CONFIG_PSAMPLE)
diff --git a/include/net/tc_act/tc_sample.h b/include/net/tc_act/tc_sample.h
index b4fce0fae645..b5d76305e854 100644
--- a/include/net/tc_act/tc_sample.h
+++ b/include/net/tc_act/tc_sample.h
@@ -41,10 +41,4 @@ static inline int tcf_sample_trunc_size(const struct tc_action *a)
41 return to_sample(a)->trunc_size; 41 return to_sample(a)->trunc_size;
42} 42}
43 43
44static inline struct psample_group *
45tcf_sample_psample_group(const struct tc_action *a)
46{
47 return rcu_dereference_rtnl(to_sample(a)->psample_group);
48}
49
50#endif /* __NET_TC_SAMPLE_H */ 44#endif /* __NET_TC_SAMPLE_H */
diff --git a/net/psample/psample.c b/net/psample/psample.c
index 66e4b61a350d..a6ceb0533b5b 100644
--- a/net/psample/psample.c
+++ b/net/psample/psample.c
@@ -73,7 +73,7 @@ static int psample_nl_cmd_get_group_dumpit(struct sk_buff *msg,
73 int idx = 0; 73 int idx = 0;
74 int err; 74 int err;
75 75
76 spin_lock(&psample_groups_lock); 76 spin_lock_bh(&psample_groups_lock);
77 list_for_each_entry(group, &psample_groups_list, list) { 77 list_for_each_entry(group, &psample_groups_list, list) {
78 if (!net_eq(group->net, sock_net(msg->sk))) 78 if (!net_eq(group->net, sock_net(msg->sk)))
79 continue; 79 continue;
@@ -89,7 +89,7 @@ static int psample_nl_cmd_get_group_dumpit(struct sk_buff *msg,
89 idx++; 89 idx++;
90 } 90 }
91 91
92 spin_unlock(&psample_groups_lock); 92 spin_unlock_bh(&psample_groups_lock);
93 cb->args[0] = idx; 93 cb->args[0] = idx;
94 return msg->len; 94 return msg->len;
95} 95}
@@ -172,7 +172,7 @@ struct psample_group *psample_group_get(struct net *net, u32 group_num)
172{ 172{
173 struct psample_group *group; 173 struct psample_group *group;
174 174
175 spin_lock(&psample_groups_lock); 175 spin_lock_bh(&psample_groups_lock);
176 176
177 group = psample_group_lookup(net, group_num); 177 group = psample_group_lookup(net, group_num);
178 if (!group) { 178 if (!group) {
@@ -183,19 +183,27 @@ struct psample_group *psample_group_get(struct net *net, u32 group_num)
183 group->refcount++; 183 group->refcount++;
184 184
185out: 185out:
186 spin_unlock(&psample_groups_lock); 186 spin_unlock_bh(&psample_groups_lock);
187 return group; 187 return group;
188} 188}
189EXPORT_SYMBOL_GPL(psample_group_get); 189EXPORT_SYMBOL_GPL(psample_group_get);
190 190
191void psample_group_take(struct psample_group *group)
192{
193 spin_lock_bh(&psample_groups_lock);
194 group->refcount++;
195 spin_unlock_bh(&psample_groups_lock);
196}
197EXPORT_SYMBOL_GPL(psample_group_take);
198
191void psample_group_put(struct psample_group *group) 199void psample_group_put(struct psample_group *group)
192{ 200{
193 spin_lock(&psample_groups_lock); 201 spin_lock_bh(&psample_groups_lock);
194 202
195 if (--group->refcount == 0) 203 if (--group->refcount == 0)
196 psample_group_destroy(group); 204 psample_group_destroy(group);
197 205
198 spin_unlock(&psample_groups_lock); 206 spin_unlock_bh(&psample_groups_lock);
199} 207}
200EXPORT_SYMBOL_GPL(psample_group_put); 208EXPORT_SYMBOL_GPL(psample_group_put);
201 209
diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c
index 10229124a992..692c4c9040fd 100644
--- a/net/sched/act_sample.c
+++ b/net/sched/act_sample.c
@@ -252,6 +252,32 @@ static int tcf_sample_search(struct net *net, struct tc_action **a, u32 index)
252 return tcf_idr_search(tn, a, index); 252 return tcf_idr_search(tn, a, index);
253} 253}
254 254
255static void tcf_psample_group_put(void *priv)
256{
257 struct psample_group *group = priv;
258
259 psample_group_put(group);
260}
261
262static struct psample_group *
263tcf_sample_get_group(const struct tc_action *a,
264 tc_action_priv_destructor *destructor)
265{
266 struct tcf_sample *s = to_sample(a);
267 struct psample_group *group;
268
269 spin_lock_bh(&s->tcf_lock);
270 group = rcu_dereference_protected(s->psample_group,
271 lockdep_is_held(&s->tcf_lock));
272 if (group) {
273 psample_group_take(group);
274 *destructor = tcf_psample_group_put;
275 }
276 spin_unlock_bh(&s->tcf_lock);
277
278 return group;
279}
280
255static struct tc_action_ops act_sample_ops = { 281static struct tc_action_ops act_sample_ops = {
256 .kind = "sample", 282 .kind = "sample",
257 .id = TCA_ID_SAMPLE, 283 .id = TCA_ID_SAMPLE,
@@ -262,6 +288,7 @@ static struct tc_action_ops act_sample_ops = {
262 .cleanup = tcf_sample_cleanup, 288 .cleanup = tcf_sample_cleanup,
263 .walk = tcf_sample_walker, 289 .walk = tcf_sample_walker,
264 .lookup = tcf_sample_search, 290 .lookup = tcf_sample_search,
291 .get_psample_group = tcf_sample_get_group,
265 .size = sizeof(struct tcf_sample), 292 .size = sizeof(struct tcf_sample),
266}; 293};
267 294
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index c668195379bd..60d44b14750a 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -3324,6 +3324,16 @@ static int tcf_tunnel_encap_get_tunnel(struct flow_action_entry *entry,
3324 return 0; 3324 return 0;
3325} 3325}
3326 3326
3327static void tcf_sample_get_group(struct flow_action_entry *entry,
3328 const struct tc_action *act)
3329{
3330#ifdef CONFIG_NET_CLS_ACT
3331 entry->sample.psample_group =
3332 act->ops->get_psample_group(act, &entry->destructor);
3333 entry->destructor_priv = entry->sample.psample_group;
3334#endif
3335}
3336
3327int tc_setup_flow_action(struct flow_action *flow_action, 3337int tc_setup_flow_action(struct flow_action *flow_action,
3328 const struct tcf_exts *exts, bool rtnl_held) 3338 const struct tcf_exts *exts, bool rtnl_held)
3329{ 3339{
@@ -3417,11 +3427,10 @@ int tc_setup_flow_action(struct flow_action *flow_action,
3417 entry->mark = tcf_skbedit_mark(act); 3427 entry->mark = tcf_skbedit_mark(act);
3418 } else if (is_tcf_sample(act)) { 3428 } else if (is_tcf_sample(act)) {
3419 entry->id = FLOW_ACTION_SAMPLE; 3429 entry->id = FLOW_ACTION_SAMPLE;
3420 entry->sample.psample_group =
3421 tcf_sample_psample_group(act);
3422 entry->sample.trunc_size = tcf_sample_trunc_size(act); 3430 entry->sample.trunc_size = tcf_sample_trunc_size(act);
3423 entry->sample.truncate = tcf_sample_truncate(act); 3431 entry->sample.truncate = tcf_sample_truncate(act);
3424 entry->sample.rate = tcf_sample_rate(act); 3432 entry->sample.rate = tcf_sample_rate(act);
3433 tcf_sample_get_group(entry, act);
3425 } else if (is_tcf_police(act)) { 3434 } else if (is_tcf_police(act)) {
3426 entry->id = FLOW_ACTION_POLICE; 3435 entry->id = FLOW_ACTION_POLICE;
3427 entry->police.burst = tcf_police_tcfp_burst(act); 3436 entry->police.burst = tcf_police_tcfp_burst(act);