diff options
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/gen_estimator.c | 9 |
1 files changed, 5 insertions, 4 deletions
diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index 57abe8266be1..a89f32fa94f6 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c | |||
@@ -99,7 +99,7 @@ struct gen_estimator_head | |||
99 | 99 | ||
100 | static struct gen_estimator_head elist[EST_MAX_INTERVAL+1]; | 100 | static struct gen_estimator_head elist[EST_MAX_INTERVAL+1]; |
101 | 101 | ||
102 | /* Protects against NULL dereference */ | 102 | /* Protects against NULL dereference and RCU write-side */ |
103 | static DEFINE_RWLOCK(est_lock); | 103 | static DEFINE_RWLOCK(est_lock); |
104 | 104 | ||
105 | static void est_timer(unsigned long arg) | 105 | static void est_timer(unsigned long arg) |
@@ -185,6 +185,7 @@ int gen_new_estimator(struct gnet_stats_basic *bstats, | |||
185 | est->last_packets = bstats->packets; | 185 | est->last_packets = bstats->packets; |
186 | est->avpps = rate_est->pps<<10; | 186 | est->avpps = rate_est->pps<<10; |
187 | 187 | ||
188 | write_lock_bh(&est_lock); | ||
188 | if (!elist[idx].timer.function) { | 189 | if (!elist[idx].timer.function) { |
189 | INIT_LIST_HEAD(&elist[idx].list); | 190 | INIT_LIST_HEAD(&elist[idx].list); |
190 | setup_timer(&elist[idx].timer, est_timer, idx); | 191 | setup_timer(&elist[idx].timer, est_timer, idx); |
@@ -194,6 +195,7 @@ int gen_new_estimator(struct gnet_stats_basic *bstats, | |||
194 | mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx)); | 195 | mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx)); |
195 | 196 | ||
196 | list_add_rcu(&est->list, &elist[idx].list); | 197 | list_add_rcu(&est->list, &elist[idx].list); |
198 | write_unlock_bh(&est_lock); | ||
197 | return 0; | 199 | return 0; |
198 | } | 200 | } |
199 | 201 | ||
@@ -212,7 +214,6 @@ static void __gen_kill_estimator(struct rcu_head *head) | |||
212 | * Removes the rate estimator specified by &bstats and &rate_est | 214 | * Removes the rate estimator specified by &bstats and &rate_est |
213 | * and deletes the timer. | 215 | * and deletes the timer. |
214 | * | 216 | * |
215 | * NOTE: Called under rtnl_mutex | ||
216 | */ | 217 | */ |
217 | void gen_kill_estimator(struct gnet_stats_basic *bstats, | 218 | void gen_kill_estimator(struct gnet_stats_basic *bstats, |
218 | struct gnet_stats_rate_est *rate_est) | 219 | struct gnet_stats_rate_est *rate_est) |
@@ -226,17 +227,17 @@ void gen_kill_estimator(struct gnet_stats_basic *bstats, | |||
226 | if (!elist[idx].timer.function) | 227 | if (!elist[idx].timer.function) |
227 | continue; | 228 | continue; |
228 | 229 | ||
230 | write_lock_bh(&est_lock); | ||
229 | list_for_each_entry_safe(e, n, &elist[idx].list, list) { | 231 | list_for_each_entry_safe(e, n, &elist[idx].list, list) { |
230 | if (e->rate_est != rate_est || e->bstats != bstats) | 232 | if (e->rate_est != rate_est || e->bstats != bstats) |
231 | continue; | 233 | continue; |
232 | 234 | ||
233 | write_lock_bh(&est_lock); | ||
234 | e->bstats = NULL; | 235 | e->bstats = NULL; |
235 | write_unlock_bh(&est_lock); | ||
236 | 236 | ||
237 | list_del_rcu(&e->list); | 237 | list_del_rcu(&e->list); |
238 | call_rcu(&e->e_rcu, __gen_kill_estimator); | 238 | call_rcu(&e->e_rcu, __gen_kill_estimator); |
239 | } | 239 | } |
240 | write_unlock_bh(&est_lock); | ||
240 | } | 241 | } |
241 | } | 242 | } |
242 | 243 | ||