pkt_sched: gen_kill_estimator() rcu fixes
gen_kill_estimator() API is incomplete or not well documented, since
caller should make sure an RCU grace period is respected before
freeing stats_lock.
This was partially addressed in commit 5d944c640b
(gen_estimator: deadlock fix), but same problem exist for all
gen_kill_estimator() users, if lock they use is not already RCU
protected.
A code review shows xt_RATEEST.c, act_api.c, act_police.c have this
problem. Other are ok because they use qdisc lock, already RCU
protected.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
cbd6890c59
commit
c7de2cf053
6 changed files with 36 additions and 3 deletions
|
@ -20,6 +20,7 @@ struct tcf_common {
|
||||||
struct gnet_stats_queue tcfc_qstats;
|
struct gnet_stats_queue tcfc_qstats;
|
||||||
struct gnet_stats_rate_est tcfc_rate_est;
|
struct gnet_stats_rate_est tcfc_rate_est;
|
||||||
spinlock_t tcfc_lock;
|
spinlock_t tcfc_lock;
|
||||||
|
struct rcu_head tcfc_rcu;
|
||||||
};
|
};
|
||||||
#define tcf_next common.tcfc_next
|
#define tcf_next common.tcfc_next
|
||||||
#define tcf_index common.tcfc_index
|
#define tcf_index common.tcfc_index
|
||||||
|
@ -32,6 +33,7 @@ struct tcf_common {
|
||||||
#define tcf_qstats common.tcfc_qstats
|
#define tcf_qstats common.tcfc_qstats
|
||||||
#define tcf_rate_est common.tcfc_rate_est
|
#define tcf_rate_est common.tcfc_rate_est
|
||||||
#define tcf_lock common.tcfc_lock
|
#define tcf_lock common.tcfc_lock
|
||||||
|
#define tcf_rcu common.tcfc_rcu
|
||||||
|
|
||||||
struct tcf_police {
|
struct tcf_police {
|
||||||
struct tcf_common common;
|
struct tcf_common common;
|
||||||
|
|
|
@ -9,6 +9,7 @@ struct xt_rateest {
|
||||||
struct gnet_estimator params;
|
struct gnet_estimator params;
|
||||||
struct gnet_stats_rate_est rstats;
|
struct gnet_stats_rate_est rstats;
|
||||||
struct gnet_stats_basic_packed bstats;
|
struct gnet_stats_basic_packed bstats;
|
||||||
|
struct rcu_head rcu;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct xt_rateest *xt_rateest_lookup(const char *name);
|
extern struct xt_rateest *xt_rateest_lookup(const char *name);
|
||||||
|
|
|
@ -263,6 +263,7 @@ static void __gen_kill_estimator(struct rcu_head *head)
|
||||||
*
|
*
|
||||||
* Removes the rate estimator specified by &bstats and &rate_est.
|
* Removes the rate estimator specified by &bstats and &rate_est.
|
||||||
*
|
*
|
||||||
|
* Note : Caller should respect an RCU grace period before freeing stats_lock
|
||||||
*/
|
*/
|
||||||
void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
|
void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
|
||||||
struct gnet_stats_rate_est *rate_est)
|
struct gnet_stats_rate_est *rate_est)
|
||||||
|
|
|
@ -60,13 +60,22 @@ struct xt_rateest *xt_rateest_lookup(const char *name)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(xt_rateest_lookup);
|
EXPORT_SYMBOL_GPL(xt_rateest_lookup);
|
||||||
|
|
||||||
|
static void xt_rateest_free_rcu(struct rcu_head *head)
|
||||||
|
{
|
||||||
|
kfree(container_of(head, struct xt_rateest, rcu));
|
||||||
|
}
|
||||||
|
|
||||||
void xt_rateest_put(struct xt_rateest *est)
|
void xt_rateest_put(struct xt_rateest *est)
|
||||||
{
|
{
|
||||||
mutex_lock(&xt_rateest_mutex);
|
mutex_lock(&xt_rateest_mutex);
|
||||||
if (--est->refcnt == 0) {
|
if (--est->refcnt == 0) {
|
||||||
hlist_del(&est->list);
|
hlist_del(&est->list);
|
||||||
gen_kill_estimator(&est->bstats, &est->rstats);
|
gen_kill_estimator(&est->bstats, &est->rstats);
|
||||||
kfree(est);
|
/*
|
||||||
|
* gen_estimator est_timer() might access est->lock or bstats,
|
||||||
|
* wait a RCU grace period before freeing 'est'
|
||||||
|
*/
|
||||||
|
call_rcu(&est->rcu, xt_rateest_free_rcu);
|
||||||
}
|
}
|
||||||
mutex_unlock(&xt_rateest_mutex);
|
mutex_unlock(&xt_rateest_mutex);
|
||||||
}
|
}
|
||||||
|
@ -179,6 +188,7 @@ static int __init xt_rateest_tg_init(void)
|
||||||
static void __exit xt_rateest_tg_fini(void)
|
static void __exit xt_rateest_tg_fini(void)
|
||||||
{
|
{
|
||||||
xt_unregister_target(&xt_rateest_tg_reg);
|
xt_unregister_target(&xt_rateest_tg_reg);
|
||||||
|
rcu_barrier(); /* Wait for completion of call_rcu()'s (xt_rateest_free_rcu) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,11 @@
|
||||||
#include <net/act_api.h>
|
#include <net/act_api.h>
|
||||||
#include <net/netlink.h>
|
#include <net/netlink.h>
|
||||||
|
|
||||||
|
static void tcf_common_free_rcu(struct rcu_head *head)
|
||||||
|
{
|
||||||
|
kfree(container_of(head, struct tcf_common, tcfc_rcu));
|
||||||
|
}
|
||||||
|
|
||||||
void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
|
void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
|
||||||
{
|
{
|
||||||
unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
|
unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
|
||||||
|
@ -38,7 +43,11 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
|
||||||
write_unlock_bh(hinfo->lock);
|
write_unlock_bh(hinfo->lock);
|
||||||
gen_kill_estimator(&p->tcfc_bstats,
|
gen_kill_estimator(&p->tcfc_bstats,
|
||||||
&p->tcfc_rate_est);
|
&p->tcfc_rate_est);
|
||||||
kfree(p);
|
/*
|
||||||
|
* gen_estimator est_timer() might access p->tcfc_lock
|
||||||
|
* or bstats, wait a RCU grace period before freeing p
|
||||||
|
*/
|
||||||
|
call_rcu(&p->tcfc_rcu, tcf_common_free_rcu);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,11 @@ static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *c
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tcf_police_free_rcu(struct rcu_head *head)
|
||||||
|
{
|
||||||
|
kfree(container_of(head, struct tcf_police, tcf_rcu));
|
||||||
|
}
|
||||||
|
|
||||||
static void tcf_police_destroy(struct tcf_police *p)
|
static void tcf_police_destroy(struct tcf_police *p)
|
||||||
{
|
{
|
||||||
unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK);
|
unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK);
|
||||||
|
@ -113,7 +118,11 @@ static void tcf_police_destroy(struct tcf_police *p)
|
||||||
qdisc_put_rtab(p->tcfp_R_tab);
|
qdisc_put_rtab(p->tcfp_R_tab);
|
||||||
if (p->tcfp_P_tab)
|
if (p->tcfp_P_tab)
|
||||||
qdisc_put_rtab(p->tcfp_P_tab);
|
qdisc_put_rtab(p->tcfp_P_tab);
|
||||||
kfree(p);
|
/*
|
||||||
|
* gen_estimator est_timer() might access p->tcf_lock
|
||||||
|
* or bstats, wait a RCU grace period before freeing p
|
||||||
|
*/
|
||||||
|
call_rcu(&p->tcf_rcu, tcf_police_free_rcu);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,6 +406,7 @@ static void __exit
|
||||||
police_cleanup_module(void)
|
police_cleanup_module(void)
|
||||||
{
|
{
|
||||||
tcf_unregister_action(&act_police_ops);
|
tcf_unregister_action(&act_police_ops);
|
||||||
|
rcu_barrier(); /* Wait for completion of call_rcu()'s (tcf_police_free_rcu) */
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(police_init_module);
|
module_init(police_init_module);
|
||||||
|
|
Loading…
Reference in a new issue