Instead of holding netns refcnt in tc actions, we can minimize the holding time by saving it in struct tcf_exts instead. This means we can just hold netns refcnt right before call_rcu() and release it after tcf_exts_destroy() is done. However, because on netns cleanup path we call tcf_proto_destroy() too, obviously we can not hold netns for a zero refcnt, in this case we have to do cleanup synchronously. It is fine for RCU too, the caller cleanup_net() already waits for a grace period. For other cases, refcnt is non-zero and we can safely grab it as normal and release it after we are done. This patch provides two new API for each filter to use: tcf_exts_get_net() and tcf_exts_put_net(). And all filters now can use the following pattern: void __destroy_filter() { tcf_exts_destroy(); tcf_exts_put_net(); // <== release netns refcnt kfree(); } void some_work() { rtnl_lock(); __destroy_filter(); rtnl_unlock(); } void some_rcu_callback() { tcf_queue_work(some_work); } if (tcf_exts_get_net()) // <== hold netns refcnt call_rcu(some_rcu_callback); else __destroy_filter();
Showing the changes to include (1 of 2 files). Show all
Unified · Split
| @@ -94,6 +94,7 @@ struct tcf_exts { · 93 unmodified lines | ||
| 94 | 94 | __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ |
| 95 | 95 | int nr_actions; |
| 96 | 96 | struct tc_action **actions; |
| 97 | + struct net *net; | |
| 97 | 98 | #endif |
| 98 | 99 | /* Map to export classifier specific extension TLV types to the |
| 99 | 100 | * generic extensions API. Unsupported extensions must be set to 0. |
| @@ -107,6 +108,7 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police) · 7 unmodified lines | ||
| 107 | 108 | #ifdef CONFIG_NET_CLS_ACT |
| 108 | 109 | exts->type = 0; |
| 109 | 110 | exts->nr_actions = 0; |
| 111 | + exts->net = NULL; | |
| 110 | 112 | exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *), |
| 111 | 113 | GFP_KERNEL); |
| 112 | 114 | if (!exts->actions) |
| @@ -117,6 +119,28 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police) · 4 unmodified lines | ||
| 117 | 119 | return 0; |
| 118 | 120 | } |
| 119 | 121 | |
| 122 | +/* Return false if the netns is being destroyed in cleanup_net(). Callers | |
| 123 | + * need to do cleanup synchronously in this case, otherwise may race with | |
| 124 | + * tc_action_net_exit(). Return true for other cases. | |
| 125 | + */ | |
| 126 | +static inline bool tcf_exts_get_net(struct tcf_exts *exts) | |
| 127 | +{ | |
| 128 | +#ifdef CONFIG_NET_CLS_ACT | |
| 129 | + exts->net = maybe_get_net(exts->net); | |
| 130 | + return exts->net != NULL; | |
| 131 | +#else | |
| 132 | + return true; | |
| 133 | +#endif | |
| 134 | +} | |
| 135 | + | |
| 136 | +static inline void tcf_exts_put_net(struct tcf_exts *exts) | |
| 137 | +{ | |
| 138 | +#ifdef CONFIG_NET_CLS_ACT | |
| 139 | + if (exts->net) | |
| 140 | + put_net(exts->net); | |
| 141 | +#endif | |
| 142 | +} | |
| 143 | + | |
| 120 | 144 | static inline void tcf_exts_to_list(const struct tcf_exts *exts, |
| 121 | 145 | struct list_head *actions) |
| 122 | 146 | { |