From: Vlad Buslov Date: Mon, 26 Aug 2019 13:45:05 +0000 (+0300) Subject: net: sched: copy tunnel info when setting flow_action entry->tunnel X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=1444c175a37443d3f6d3db825df050741452c3c3;p=uclinux-h8%2Flinux.git net: sched: copy tunnel info when setting flow_action entry->tunnel In order to remove dependency on rtnl lock, modify tc_setup_flow_action() to copy tunnel info, instead of just saving pointer to tunnel_key action tunnel info. This is necessary to prevent concurrent action overwrite from releasing tunnel info while it is being used by rtnl-unlocked driver. Implement helper tcf_tunnel_info_copy() that is used to copy tunnel info with all its options to dynamically allocated memory block. Modify tc_cleanup_flow_action() to free dynamically allocated tunnel info. Signed-off-by: Vlad Buslov Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- diff --git a/include/net/tc_act/tc_tunnel_key.h b/include/net/tc_act/tc_tunnel_key.h index 7c3f777c168c..0689d9bcdf84 100644 --- a/include/net/tc_act/tc_tunnel_key.h +++ b/include/net/tc_act/tc_tunnel_key.h @@ -59,4 +59,21 @@ static inline struct ip_tunnel_info *tcf_tunnel_info(const struct tc_action *a) return NULL; #endif } + +static inline struct ip_tunnel_info * +tcf_tunnel_info_copy(const struct tc_action *a) +{ +#ifdef CONFIG_NET_CLS_ACT + struct ip_tunnel_info *tun = tcf_tunnel_info(a); + + if (tun) { + size_t tun_size = sizeof(*tun) + tun->options_len; + struct ip_tunnel_info *tun_copy = kmemdup(tun, tun_size, + GFP_KERNEL); + + return tun_copy; + } +#endif + return NULL; +} #endif /* __NET_TC_TUNNEL_KEY_H */ diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index d988737693e4..671ca905dbb5 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3279,6 +3279,9 @@ void tc_cleanup_flow_action(struct flow_action *flow_action) if (entry->dev) dev_put(entry->dev); break; + case FLOW_ACTION_TUNNEL_ENCAP: + kfree(entry->tunnel); + break; default: break; } @@ -3355,7 +3358,11 @@ int tc_setup_flow_action(struct flow_action *flow_action, } } else if (is_tcf_tunnel_set(act)) { entry->id = FLOW_ACTION_TUNNEL_ENCAP; - entry->tunnel = tcf_tunnel_info(act); + entry->tunnel = tcf_tunnel_info_copy(act); + if (!entry->tunnel) { + err = -ENOMEM; + goto err_out; + } } else if (is_tcf_tunnel_release(act)) { entry->id = FLOW_ACTION_TUNNEL_DECAP; } else if (is_tcf_pedit(act)) {