net: sched: lock action when translating it to flow_action infra

In order to remove dependency on rtnl lock, take action's tcfa_lock when
constructing its representation as flow_action_entry structure.

Refactor tcf_sample_get_group() to assume that caller holds tcf_lock and
don't take it manually. This callback is only called from flow_action infra
representation translator which now calls it with tcf_lock held, so this
refactoring is necessary to prevent deadlock.

Allocate memory with GFP_ATOMIC flag for ip_tunnel_info copy because
tcf_tunnel_info_copy() is only called from flow_action representation infra
code with tcf_lock spinlock taken.

Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vlad Buslov 2020-02-17 12:12:09 +02:00 committed by David S. Miller
parent 92df9f8a74
commit 7a47281439
3 changed files with 12 additions and 9 deletions

View File

@ -69,7 +69,7 @@ tcf_tunnel_info_copy(const struct tc_action *a)
if (tun) { if (tun) {
size_t tun_size = sizeof(*tun) + tun->options_len; size_t tun_size = sizeof(*tun) + tun->options_len;
struct ip_tunnel_info *tun_copy = kmemdup(tun, tun_size, struct ip_tunnel_info *tun_copy = kmemdup(tun, tun_size,
GFP_KERNEL); GFP_ATOMIC);
return tun_copy; return tun_copy;
} }

View File

@ -267,14 +267,12 @@ tcf_sample_get_group(const struct tc_action *a,
struct tcf_sample *s = to_sample(a); struct tcf_sample *s = to_sample(a);
struct psample_group *group; struct psample_group *group;
spin_lock_bh(&s->tcf_lock);
group = rcu_dereference_protected(s->psample_group, group = rcu_dereference_protected(s->psample_group,
lockdep_is_held(&s->tcf_lock)); lockdep_is_held(&s->tcf_lock));
if (group) { if (group) {
psample_group_take(group); psample_group_take(group);
*destructor = tcf_psample_group_put; *destructor = tcf_psample_group_put;
} }
spin_unlock_bh(&s->tcf_lock);
return group; return group;
} }

View File

@ -3435,7 +3435,7 @@ static void tcf_sample_get_group(struct flow_action_entry *entry,
int tc_setup_flow_action(struct flow_action *flow_action, int tc_setup_flow_action(struct flow_action *flow_action,
const struct tcf_exts *exts, bool rtnl_held) const struct tcf_exts *exts, bool rtnl_held)
{ {
const struct tc_action *act; struct tc_action *act;
int i, j, k, err = 0; int i, j, k, err = 0;
if (!exts) if (!exts)
@ -3449,6 +3449,7 @@ int tc_setup_flow_action(struct flow_action *flow_action,
struct flow_action_entry *entry; struct flow_action_entry *entry;
entry = &flow_action->entries[j]; entry = &flow_action->entries[j];
spin_lock_bh(&act->tcfa_lock);
if (is_tcf_gact_ok(act)) { if (is_tcf_gact_ok(act)) {
entry->id = FLOW_ACTION_ACCEPT; entry->id = FLOW_ACTION_ACCEPT;
} else if (is_tcf_gact_shot(act)) { } else if (is_tcf_gact_shot(act)) {
@ -3489,13 +3490,13 @@ int tc_setup_flow_action(struct flow_action *flow_action,
break; break;
default: default:
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto err_out; goto err_out_locked;
} }
} else if (is_tcf_tunnel_set(act)) { } else if (is_tcf_tunnel_set(act)) {
entry->id = FLOW_ACTION_TUNNEL_ENCAP; entry->id = FLOW_ACTION_TUNNEL_ENCAP;
err = tcf_tunnel_encap_get_tunnel(entry, act); err = tcf_tunnel_encap_get_tunnel(entry, act);
if (err) if (err)
goto err_out; goto err_out_locked;
} else if (is_tcf_tunnel_release(act)) { } else if (is_tcf_tunnel_release(act)) {
entry->id = FLOW_ACTION_TUNNEL_DECAP; entry->id = FLOW_ACTION_TUNNEL_DECAP;
} else if (is_tcf_pedit(act)) { } else if (is_tcf_pedit(act)) {
@ -3509,7 +3510,7 @@ int tc_setup_flow_action(struct flow_action *flow_action,
break; break;
default: default:
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto err_out; goto err_out_locked;
} }
entry->mangle.htype = tcf_pedit_htype(act, k); entry->mangle.htype = tcf_pedit_htype(act, k);
entry->mangle.mask = tcf_pedit_mask(act, k); entry->mangle.mask = tcf_pedit_mask(act, k);
@ -3560,15 +3561,16 @@ int tc_setup_flow_action(struct flow_action *flow_action,
entry->mpls_mangle.ttl = tcf_mpls_ttl(act); entry->mpls_mangle.ttl = tcf_mpls_ttl(act);
break; break;
default: default:
goto err_out; goto err_out_locked;
} }
} else if (is_tcf_skbedit_ptype(act)) { } else if (is_tcf_skbedit_ptype(act)) {
entry->id = FLOW_ACTION_PTYPE; entry->id = FLOW_ACTION_PTYPE;
entry->ptype = tcf_skbedit_ptype(act); entry->ptype = tcf_skbedit_ptype(act);
} else { } else {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto err_out; goto err_out_locked;
} }
spin_unlock_bh(&act->tcfa_lock);
if (!is_tcf_pedit(act)) if (!is_tcf_pedit(act))
j++; j++;
@ -3582,6 +3584,9 @@ err_out:
tc_cleanup_flow_action(flow_action); tc_cleanup_flow_action(flow_action);
return err; return err;
err_out_locked:
spin_unlock_bh(&act->tcfa_lock);
goto err_out;
} }
EXPORT_SYMBOL(tc_setup_flow_action); EXPORT_SYMBOL(tc_setup_flow_action);