Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7de2cf0 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

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 5d944c64
(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: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cbd6890c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ struct tcf_common {
	struct gnet_stats_queue		tcfc_qstats;
	struct gnet_stats_rate_est	tcfc_rate_est;
	spinlock_t			tcfc_lock;
	struct rcu_head			tcfc_rcu;
};
#define tcf_next	common.tcfc_next
#define tcf_index	common.tcfc_index
@@ -32,6 +33,7 @@ struct tcf_common {
#define tcf_qstats	common.tcfc_qstats
#define tcf_rate_est	common.tcfc_rate_est
#define tcf_lock	common.tcfc_lock
#define tcf_rcu		common.tcfc_rcu

struct tcf_police {
	struct tcf_common	common;
+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ struct xt_rateest {
	struct gnet_estimator		params;
	struct gnet_stats_rate_est	rstats;
	struct gnet_stats_basic_packed	bstats;
	struct rcu_head			rcu;
};

extern struct xt_rateest *xt_rateest_lookup(const char *name);
+1 −0
Original line number Diff line number Diff line
@@ -263,6 +263,7 @@ static void __gen_kill_estimator(struct rcu_head *head)
 *
 * 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,
			struct gnet_stats_rate_est *rate_est)
+11 −1
Original line number Diff line number Diff line
@@ -60,13 +60,22 @@ struct xt_rateest *xt_rateest_lookup(const char *name)
}
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)
{
	mutex_lock(&xt_rateest_mutex);
	if (--est->refcnt == 0) {
		hlist_del(&est->list);
		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);
}
@@ -179,6 +188,7 @@ static int __init xt_rateest_tg_init(void)
static void __exit xt_rateest_tg_fini(void)
{
	xt_unregister_target(&xt_rateest_tg_reg);
	rcu_barrier(); /* Wait for completion of call_rcu()'s (xt_rateest_free_rcu) */
}


+10 −1
Original line number Diff line number Diff line
@@ -26,6 +26,11 @@
#include <net/act_api.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)
{
	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);
			gen_kill_estimator(&p->tcfc_bstats,
					   &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;
		}
	}
Loading