diff mbox

[net-next,6/7] net_sched: cls: move allocation in ->init to generic layer

Message ID 1389312845-10304-7-git-send-email-xiyou.wangcong@gmail.com
State Rejected, archived
Delegated to: David Miller
Headers show

Commit Message

Cong Wang Jan. 10, 2014, 12:14 a.m. UTC
Most of the filters need allocation of tp->root in ->init()
and free it in ->destroy(), make this generic.

Also we could reduce the use of tcf_tree_lock a bit.

Cc: Thomas Graf <tgraf@suug.ch>
Cc: David S. Miller <davem@davemloft.net>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
---
 include/net/sch_generic.h |  1 +
 net/sched/cls_api.c       |  7 +++++++
 net/sched/cls_basic.c     |  8 ++------
 net/sched/cls_bpf.c       | 11 ++---------
 net/sched/cls_cgroup.c    | 21 +++++++--------------
 net/sched/cls_flow.c      |  8 ++------
 net/sched/cls_fw.c        | 14 ++++----------
 net/sched/cls_route.c     | 15 ++-------------
 net/sched/cls_rsvp.h      | 10 ++--------
 net/sched/cls_tcindex.c   | 13 ++-----------
 net/sched/cls_u32.c       |  9 ++-------
 net/sched/sch_api.c       |  1 +
 12 files changed, 34 insertions(+), 84 deletions(-)

Comments

Jamal Hadi Salim Jan. 12, 2014, 1:07 p.m. UTC | #1
On 01/09/14 19:14, Cong Wang wrote:
> Most of the filters need allocation of tp->root in ->init()
> and free it in ->destroy(), make this generic.
>
> Also we could reduce the use of tcf_tree_lock a bit.
>
> Cc: Thomas Graf <tgraf@suug.ch>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: Jamal Hadi Salim <jhs@mojatatu.com>
> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>


Hrm. This one worries me a little.
I dont see how just pre-allocing the private head of the classifier
magically allows you to get rid of locks. Have you tested against those
classifiers you changed?
If those locks are useless - then that is a separate patch to kill
them (sorry, dont have time to test myself right now).

cheers,
jamal

> ---
>   include/net/sch_generic.h |  1 +
>   net/sched/cls_api.c       |  7 +++++++
>   net/sched/cls_basic.c     |  8 ++------
>   net/sched/cls_bpf.c       | 11 ++---------
>   net/sched/cls_cgroup.c    | 21 +++++++--------------
>   net/sched/cls_flow.c      |  8 ++------
>   net/sched/cls_fw.c        | 14 ++++----------
>   net/sched/cls_route.c     | 15 ++-------------
>   net/sched/cls_rsvp.h      | 10 ++--------
>   net/sched/cls_tcindex.c   | 13 ++-----------
>   net/sched/cls_u32.c       |  9 ++-------
>   net/sched/sch_api.c       |  1 +
>   12 files changed, 34 insertions(+), 84 deletions(-)
>
> diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
> index d062f81..819dc1d 100644
> --- a/include/net/sch_generic.h
> +++ b/include/net/sch_generic.h
> @@ -208,6 +208,7 @@ struct tcf_proto_ops {
>   					struct sk_buff *skb, struct tcmsg*);
>
>   	struct module		*owner;
> +	size_t			root_size;
>   };
>
>   struct tcf_proto {
> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> index 29a30a1..8460c75 100644
> --- a/net/sched/cls_api.c
> +++ b/net/sched/cls_api.c
> @@ -262,6 +262,13 @@ replay:
>   		tp->q = q;
>   		tp->classify = tp_ops->classify;
>   		tp->classid = parent;
> +		tp->root = kzalloc(tp_ops->root_size, GFP_KERNEL);
> +		if (!tp->root) {
> +			err = -ENOBUFS;
> +			module_put(tp_ops->owner);
> +			kfree(tp);
> +			goto errout;
> +		}
>
>   		err = tp_ops->init(tp);
>   		if (err != 0) {
> diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
> index e98ca99..318f672 100644
> --- a/net/sched/cls_basic.c
> +++ b/net/sched/cls_basic.c
> @@ -75,13 +75,9 @@ static void basic_put(struct tcf_proto *tp, unsigned long f)
>
>   static int basic_init(struct tcf_proto *tp)
>   {
> -	struct basic_head *head;
> +	struct basic_head *head = tp->root;
>
> -	head = kzalloc(sizeof(*head), GFP_KERNEL);
> -	if (head == NULL)
> -		return -ENOBUFS;
>   	INIT_LIST_HEAD(&head->flist);
> -	tp->root = head;
>   	return 0;
>   }
>
> @@ -102,7 +98,6 @@ static void basic_destroy(struct tcf_proto *tp)
>   		list_del(&f->link);
>   		basic_delete_filter(tp, f);
>   	}
> -	kfree(head);
>   }
>
>   static int basic_delete(struct tcf_proto *tp, unsigned long arg)
> @@ -288,6 +283,7 @@ static struct tcf_proto_ops cls_basic_ops __read_mostly = {
>   	.walk		=	basic_walk,
>   	.dump		=	basic_dump,
>   	.owner		=	THIS_MODULE,
> +	.root_size	=	sizeof(struct basic_head),
>   };
>
>   static int __init init_basic(void)
> diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
> index 8e3cf49..eedd296 100644
> --- a/net/sched/cls_bpf.c
> +++ b/net/sched/cls_bpf.c
> @@ -75,15 +75,9 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
>
>   static int cls_bpf_init(struct tcf_proto *tp)
>   {
> -	struct cls_bpf_head *head;
> -
> -	head = kzalloc(sizeof(*head), GFP_KERNEL);
> -	if (head == NULL)
> -		return -ENOBUFS;
> +	struct cls_bpf_head *head = tp->root;
>
>   	INIT_LIST_HEAD(&head->plist);
> -	tp->root = head;
> -
>   	return 0;
>   }
>
> @@ -126,8 +120,6 @@ static void cls_bpf_destroy(struct tcf_proto *tp)
>   		list_del(&prog->link);
>   		cls_bpf_delete_prog(tp, prog);
>   	}
> -
> -	kfree(head);
>   }
>
>   static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
> @@ -366,6 +358,7 @@ static struct tcf_proto_ops cls_bpf_ops __read_mostly = {
>   	.delete		=	cls_bpf_delete,
>   	.walk		=	cls_bpf_walk,
>   	.dump		=	cls_bpf_dump,
> +	.root_size	=	sizeof(struct cls_bpf_head),
>   };
>
>   static int __init cls_bpf_init_mod(void)
> diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
> index 8e2158a..4b7e083 100644
> --- a/net/sched/cls_cgroup.c
> +++ b/net/sched/cls_cgroup.c
> @@ -22,6 +22,7 @@ struct cls_cgroup_head {
>   	u32			handle;
>   	struct tcf_exts		exts;
>   	struct tcf_ematch_tree	ematches;
> +	bool			init;
>   };
>
>   static int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp,
> @@ -73,6 +74,9 @@ static void cls_cgroup_put(struct tcf_proto *tp, unsigned long f)
>
>   static int cls_cgroup_init(struct tcf_proto *tp)
>   {
> +	struct cls_cgroup_head *head = tp->root;
> +
> +	tcf_exts_init(&head->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
>   	return 0;
>   }
>
> @@ -94,20 +98,9 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
>   	if (!tca[TCA_OPTIONS])
>   		return -EINVAL;
>
> -	if (head == NULL) {
> -		if (!handle)
> -			return -EINVAL;
> -
> -		head = kzalloc(sizeof(*head), GFP_KERNEL);
> -		if (head == NULL)
> -			return -ENOBUFS;
> -
> -		tcf_exts_init(&head->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
> +	if (!head->init) {
>   		head->handle = handle;
> -
> -		tcf_tree_lock(tp);
> -		tp->root = head;
> -		tcf_tree_unlock(tp);
> +		head->init = true;
>   	}
>
>   	if (handle != head->handle)
> @@ -140,7 +133,6 @@ static void cls_cgroup_destroy(struct tcf_proto *tp)
>   	if (head) {
>   		tcf_exts_destroy(tp, &head->exts);
>   		tcf_em_tree_destroy(tp, &head->ematches);
> -		kfree(head);
>   	}
>   }
>
> @@ -205,6 +197,7 @@ static struct tcf_proto_ops cls_cgroup_ops __read_mostly = {
>   	.walk		=	cls_cgroup_walk,
>   	.dump		=	cls_cgroup_dump,
>   	.owner		=	THIS_MODULE,
> +	.root_size	=	sizeof(struct cls_cgroup_head),
>   };
>
>   static int __init init_cgroup_cls(void)
> diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
> index 257029c..b39080a 100644
> --- a/net/sched/cls_flow.c
> +++ b/net/sched/cls_flow.c
> @@ -526,13 +526,9 @@ static int flow_delete(struct tcf_proto *tp, unsigned long arg)
>
>   static int flow_init(struct tcf_proto *tp)
>   {
> -	struct flow_head *head;
> +	struct flow_head *head = tp->root;
>
> -	head = kzalloc(sizeof(*head), GFP_KERNEL);
> -	if (head == NULL)
> -		return -ENOBUFS;
>   	INIT_LIST_HEAD(&head->filters);
> -	tp->root = head;
>   	return 0;
>   }
>
> @@ -545,7 +541,6 @@ static void flow_destroy(struct tcf_proto *tp)
>   		list_del(&f->list);
>   		flow_destroy_filter(tp, f);
>   	}
> -	kfree(head);
>   }
>
>   static unsigned long flow_get(struct tcf_proto *tp, u32 handle)
> @@ -653,6 +648,7 @@ static struct tcf_proto_ops cls_flow_ops __read_mostly = {
>   	.dump		= flow_dump,
>   	.walk		= flow_walk,
>   	.owner		= THIS_MODULE,
> +	.root_size	= sizeof(struct flow_head),
>   };
>
>   static int __init cls_flow_init(void)
> diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
> index ed00e8c..73cd277 100644
> --- a/net/sched/cls_fw.c
> +++ b/net/sched/cls_fw.c
> @@ -34,6 +34,7 @@
>   struct fw_head {
>   	struct fw_filter *ht[HTSIZE];
>   	u32 mask;
> +	bool init;
>   };
>
>   struct fw_filter {
> @@ -155,7 +156,6 @@ static void fw_destroy(struct tcf_proto *tp)
>   			fw_delete_filter(tp, f);
>   		}
>   	}
> -	kfree(head);
>   }
>
>   static int fw_delete(struct tcf_proto *tp, unsigned long arg)
> @@ -259,19 +259,12 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
>   	if (!handle)
>   		return -EINVAL;
>
> -	if (head == NULL) {
> +	if (!head->init) {
>   		u32 mask = 0xFFFFFFFF;
>   		if (tb[TCA_FW_MASK])
>   			mask = nla_get_u32(tb[TCA_FW_MASK]);
> -
> -		head = kzalloc(sizeof(struct fw_head), GFP_KERNEL);
> -		if (head == NULL)
> -			return -ENOBUFS;
>   		head->mask = mask;
> -
> -		tcf_tree_lock(tp);
> -		tp->root = head;
> -		tcf_tree_unlock(tp);
> +		head->init = true;
>   	}
>
>   	f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
> @@ -388,6 +381,7 @@ static struct tcf_proto_ops cls_fw_ops __read_mostly = {
>   	.walk		=	fw_walk,
>   	.dump		=	fw_dump,
>   	.owner		=	THIS_MODULE,
> +	.root_size	= 	sizeof(struct fw_head),
>   };
>
>   static int __init init_fw(void)
> diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
> index 1ad3068..038f35f 100644
> --- a/net/sched/cls_route.c
> +++ b/net/sched/cls_route.c
> @@ -279,7 +279,6 @@ static void route4_destroy(struct tcf_proto *tp)
>   			kfree(b);
>   		}
>   	}
> -	kfree(head);
>   }
>
>   static int route4_delete(struct tcf_proto *tp, unsigned long arg)
> @@ -462,20 +461,9 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
>   		goto reinsert;
>   	}
>
> -	err = -ENOBUFS;
> -	if (head == NULL) {
> -		head = kzalloc(sizeof(struct route4_head), GFP_KERNEL);
> -		if (head == NULL)
> -			goto errout;
> -
> -		tcf_tree_lock(tp);
> -		tp->root = head;
> -		tcf_tree_unlock(tp);
> -	}
> -
>   	f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);
>   	if (f == NULL)
> -		goto errout;
> +		return -ENOBUFS;
>
>   	tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
>   	err = route4_set_parms(net, tp, base, f, handle, head, tb,
> @@ -613,6 +601,7 @@ static struct tcf_proto_ops cls_route4_ops __read_mostly = {
>   	.walk		=	route4_walk,
>   	.dump		=	route4_dump,
>   	.owner		=	THIS_MODULE,
> +	.root_size	=	sizeof(struct route4_head),
>   };
>
>   static int __init init_route4(void)
> diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
> index 19f8e5d..47930bc 100644
> --- a/net/sched/cls_rsvp.h
> +++ b/net/sched/cls_rsvp.h
> @@ -242,14 +242,7 @@ static void rsvp_put(struct tcf_proto *tp, unsigned long f)
>
>   static int rsvp_init(struct tcf_proto *tp)
>   {
> -	struct rsvp_head *data;
> -
> -	data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
> -	if (data) {
> -		tp->root = data;
> -		return 0;
> -	}
> -	return -ENOBUFS;
> +	return 0;
>   }
>
>   static void
> @@ -656,6 +649,7 @@ static struct tcf_proto_ops RSVP_OPS __read_mostly = {
>   	.walk		=	rsvp_walk,
>   	.dump		=	rsvp_dump,
>   	.owner		=	THIS_MODULE,
> +	.root_size	=	sizeof(struct rsvp_head),
>   };
>
>   static int __init init_rsvp(void)
> diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
> index eed8404..6454158 100644
> --- a/net/sched/cls_tcindex.c
> +++ b/net/sched/cls_tcindex.c
> @@ -118,18 +118,11 @@ static void tcindex_put(struct tcf_proto *tp, unsigned long f)
>
>   static int tcindex_init(struct tcf_proto *tp)
>   {
> -	struct tcindex_data *p;
> -
> -	pr_debug("tcindex_init(tp %p)\n", tp);
> -	p = kzalloc(sizeof(struct tcindex_data), GFP_KERNEL);
> -	if (!p)
> -		return -ENOMEM;
> +	struct tcindex_data *p = tp->root;
>
>   	p->mask = 0xffff;
>   	p->hash = DEFAULT_HASH_SIZE;
>   	p->fall_through = 1;
> -
> -	tp->root = p;
>   	return 0;
>   }
>
> @@ -407,15 +400,12 @@ static void tcindex_destroy(struct tcf_proto *tp)
>   	struct tcindex_data *p = tp->root;
>   	struct tcf_walker walker;
>
> -	pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p);
>   	walker.count = 0;
>   	walker.skip = 0;
>   	walker.fn = &tcindex_destroy_element;
>   	tcindex_walk(tp, &walker);
>   	kfree(p->perfect);
>   	kfree(p->h);
> -	kfree(p);
> -	tp->root = NULL;
>   }
>
>
> @@ -491,6 +481,7 @@ static struct tcf_proto_ops cls_tcindex_ops __read_mostly = {
>   	.walk		=	tcindex_walk,
>   	.dump		=	tcindex_dump,
>   	.owner		=	THIS_MODULE,
> +	.root_size	=	sizeof(struct tcindex_data),
>   };
>
>   static int __init init_tcindex(void)
> diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
> index 84c28da..678c2d72 100644
> --- a/net/sched/cls_u32.c
> +++ b/net/sched/cls_u32.c
> @@ -300,15 +300,11 @@ static u32 gen_new_htid(struct tc_u_common *tp_c)
>
>   static int u32_init(struct tcf_proto *tp)
>   {
> -	struct tc_u_hnode *root_ht;
> +	struct tc_u_hnode *root_ht = tp->root;
>   	struct tc_u_common *tp_c;
>
>   	tp_c = tp->q->u32_node;
>
> -	root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL);
> -	if (root_ht == NULL)
> -		return -ENOBUFS;
> -
>   	root_ht->divisor = 0;
>   	root_ht->refcnt++;
>   	root_ht->handle = tp_c ? gen_new_htid(tp_c) : 0x80000000;
> @@ -329,7 +325,6 @@ static int u32_init(struct tcf_proto *tp)
>   	tp_c->hlist = root_ht;
>   	root_ht->tp_c = tp_c;
>
> -	tp->root = root_ht;
>   	tp->data = tp_c;
>   	return 0;
>   }
> @@ -394,7 +389,6 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
>   	for (hn = &tp_c->hlist; *hn; hn = &(*hn)->next) {
>   		if (*hn == ht) {
>   			*hn = ht->next;
> -			kfree(ht);
>   			return 0;
>   		}
>   	}
> @@ -801,6 +795,7 @@ static struct tcf_proto_ops cls_u32_ops __read_mostly = {
>   	.walk		=	u32_walk,
>   	.dump		=	u32_dump,
>   	.owner		=	THIS_MODULE,
> +	.root_size	=	sizeof(struct tc_u_hnode),
>   };
>
>   static int __init init_u32(void)
> diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
> index 1313145..5fef7f4 100644
> --- a/net/sched/sch_api.c
> +++ b/net/sched/sch_api.c
> @@ -1829,6 +1829,7 @@ EXPORT_SYMBOL(tc_classify);
>   void tcf_destroy(struct tcf_proto *tp)
>   {
>   	tp->ops->destroy(tp);
> +	kfree(tp->root);
>   	module_put(tp->ops->owner);
>   	kfree(tp);
>   }
>

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller Jan. 13, 2014, 7:49 p.m. UTC | #2
From: Cong Wang <xiyou.wangcong@gmail.com>
Date: Thu,  9 Jan 2014 16:14:04 -0800

> +	if (!head->init) {
>  		head->handle = handle;
> -
> -		tcf_tree_lock(tp);
> -		tp->root = head;
> -		tcf_tree_unlock(tp);
> +		head->init = true;
 ...
>  		head->mask = mask;
> -
> -		tcf_tree_lock(tp);
> -		tp->root = head;
> -		tcf_tree_unlock(tp);
> +		head->init = true;

Like Jamal, I don't think these transformations are valid.

You can't make the root visible in the hierarchy until the
->handle and ->mask (respectively) members are initialized
in these two classifiers.

What I'm going to do for now is apply patches 1-5 and 7.

Thanks.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Cong Wang Jan. 14, 2014, 6:56 p.m. UTC | #3
On Sun, Jan 12, 2014 at 5:07 AM, Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> On 01/09/14 19:14, Cong Wang wrote:
>>
>> Most of the filters need allocation of tp->root in ->init()
>> and free it in ->destroy(), make this generic.
>>
>> Also we could reduce the use of tcf_tree_lock a bit.
>>
>> Cc: Thomas Graf <tgraf@suug.ch>
>> Cc: David S. Miller <davem@davemloft.net>
>> Cc: Jamal Hadi Salim <jhs@mojatatu.com>
>> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
>
>
>
> Hrm. This one worries me a little.
> I dont see how just pre-allocing the private head of the classifier
> magically allows you to get rid of locks. Have you tested against those
> classifiers you changed?
> If those locks are useless - then that is a separate patch to kill
> them (sorry, dont have time to test myself right now).


Yeah, I am not sure either. I will re-think about this locking stuff.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Cong Wang Jan. 14, 2014, 6:57 p.m. UTC | #4
On Mon, Jan 13, 2014 at 11:49 AM, David Miller <davem@davemloft.net> wrote:
>
> What I'm going to do for now is apply patches 1-5 and 7.
>

Sounds good to me. I will re-work on it.
Thanks!
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index d062f81..819dc1d 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -208,6 +208,7 @@  struct tcf_proto_ops {
 					struct sk_buff *skb, struct tcmsg*);
 
 	struct module		*owner;
+	size_t			root_size;
 };
 
 struct tcf_proto {
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 29a30a1..8460c75 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -262,6 +262,13 @@  replay:
 		tp->q = q;
 		tp->classify = tp_ops->classify;
 		tp->classid = parent;
+		tp->root = kzalloc(tp_ops->root_size, GFP_KERNEL);
+		if (!tp->root) {
+			err = -ENOBUFS;
+			module_put(tp_ops->owner);
+			kfree(tp);
+			goto errout;
+		}
 
 		err = tp_ops->init(tp);
 		if (err != 0) {
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index e98ca99..318f672 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -75,13 +75,9 @@  static void basic_put(struct tcf_proto *tp, unsigned long f)
 
 static int basic_init(struct tcf_proto *tp)
 {
-	struct basic_head *head;
+	struct basic_head *head = tp->root;
 
-	head = kzalloc(sizeof(*head), GFP_KERNEL);
-	if (head == NULL)
-		return -ENOBUFS;
 	INIT_LIST_HEAD(&head->flist);
-	tp->root = head;
 	return 0;
 }
 
@@ -102,7 +98,6 @@  static void basic_destroy(struct tcf_proto *tp)
 		list_del(&f->link);
 		basic_delete_filter(tp, f);
 	}
-	kfree(head);
 }
 
 static int basic_delete(struct tcf_proto *tp, unsigned long arg)
@@ -288,6 +283,7 @@  static struct tcf_proto_ops cls_basic_ops __read_mostly = {
 	.walk		=	basic_walk,
 	.dump		=	basic_dump,
 	.owner		=	THIS_MODULE,
+	.root_size	=	sizeof(struct basic_head),
 };
 
 static int __init init_basic(void)
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 8e3cf49..eedd296 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -75,15 +75,9 @@  static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 
 static int cls_bpf_init(struct tcf_proto *tp)
 {
-	struct cls_bpf_head *head;
-
-	head = kzalloc(sizeof(*head), GFP_KERNEL);
-	if (head == NULL)
-		return -ENOBUFS;
+	struct cls_bpf_head *head = tp->root;
 
 	INIT_LIST_HEAD(&head->plist);
-	tp->root = head;
-
 	return 0;
 }
 
@@ -126,8 +120,6 @@  static void cls_bpf_destroy(struct tcf_proto *tp)
 		list_del(&prog->link);
 		cls_bpf_delete_prog(tp, prog);
 	}
-
-	kfree(head);
 }
 
 static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
@@ -366,6 +358,7 @@  static struct tcf_proto_ops cls_bpf_ops __read_mostly = {
 	.delete		=	cls_bpf_delete,
 	.walk		=	cls_bpf_walk,
 	.dump		=	cls_bpf_dump,
+	.root_size	=	sizeof(struct cls_bpf_head),
 };
 
 static int __init cls_bpf_init_mod(void)
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index 8e2158a..4b7e083 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -22,6 +22,7 @@  struct cls_cgroup_head {
 	u32			handle;
 	struct tcf_exts		exts;
 	struct tcf_ematch_tree	ematches;
+	bool			init;
 };
 
 static int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp,
@@ -73,6 +74,9 @@  static void cls_cgroup_put(struct tcf_proto *tp, unsigned long f)
 
 static int cls_cgroup_init(struct tcf_proto *tp)
 {
+	struct cls_cgroup_head *head = tp->root;
+
+	tcf_exts_init(&head->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
 	return 0;
 }
 
@@ -94,20 +98,9 @@  static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
 	if (!tca[TCA_OPTIONS])
 		return -EINVAL;
 
-	if (head == NULL) {
-		if (!handle)
-			return -EINVAL;
-
-		head = kzalloc(sizeof(*head), GFP_KERNEL);
-		if (head == NULL)
-			return -ENOBUFS;
-
-		tcf_exts_init(&head->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
+	if (!head->init) {
 		head->handle = handle;
-
-		tcf_tree_lock(tp);
-		tp->root = head;
-		tcf_tree_unlock(tp);
+		head->init = true;
 	}
 
 	if (handle != head->handle)
@@ -140,7 +133,6 @@  static void cls_cgroup_destroy(struct tcf_proto *tp)
 	if (head) {
 		tcf_exts_destroy(tp, &head->exts);
 		tcf_em_tree_destroy(tp, &head->ematches);
-		kfree(head);
 	}
 }
 
@@ -205,6 +197,7 @@  static struct tcf_proto_ops cls_cgroup_ops __read_mostly = {
 	.walk		=	cls_cgroup_walk,
 	.dump		=	cls_cgroup_dump,
 	.owner		=	THIS_MODULE,
+	.root_size	=	sizeof(struct cls_cgroup_head),
 };
 
 static int __init init_cgroup_cls(void)
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 257029c..b39080a 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -526,13 +526,9 @@  static int flow_delete(struct tcf_proto *tp, unsigned long arg)
 
 static int flow_init(struct tcf_proto *tp)
 {
-	struct flow_head *head;
+	struct flow_head *head = tp->root;
 
-	head = kzalloc(sizeof(*head), GFP_KERNEL);
-	if (head == NULL)
-		return -ENOBUFS;
 	INIT_LIST_HEAD(&head->filters);
-	tp->root = head;
 	return 0;
 }
 
@@ -545,7 +541,6 @@  static void flow_destroy(struct tcf_proto *tp)
 		list_del(&f->list);
 		flow_destroy_filter(tp, f);
 	}
-	kfree(head);
 }
 
 static unsigned long flow_get(struct tcf_proto *tp, u32 handle)
@@ -653,6 +648,7 @@  static struct tcf_proto_ops cls_flow_ops __read_mostly = {
 	.dump		= flow_dump,
 	.walk		= flow_walk,
 	.owner		= THIS_MODULE,
+	.root_size	= sizeof(struct flow_head),
 };
 
 static int __init cls_flow_init(void)
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index ed00e8c..73cd277 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -34,6 +34,7 @@ 
 struct fw_head {
 	struct fw_filter *ht[HTSIZE];
 	u32 mask;
+	bool init;
 };
 
 struct fw_filter {
@@ -155,7 +156,6 @@  static void fw_destroy(struct tcf_proto *tp)
 			fw_delete_filter(tp, f);
 		}
 	}
-	kfree(head);
 }
 
 static int fw_delete(struct tcf_proto *tp, unsigned long arg)
@@ -259,19 +259,12 @@  static int fw_change(struct net *net, struct sk_buff *in_skb,
 	if (!handle)
 		return -EINVAL;
 
-	if (head == NULL) {
+	if (!head->init) {
 		u32 mask = 0xFFFFFFFF;
 		if (tb[TCA_FW_MASK])
 			mask = nla_get_u32(tb[TCA_FW_MASK]);
-
-		head = kzalloc(sizeof(struct fw_head), GFP_KERNEL);
-		if (head == NULL)
-			return -ENOBUFS;
 		head->mask = mask;
-
-		tcf_tree_lock(tp);
-		tp->root = head;
-		tcf_tree_unlock(tp);
+		head->init = true;
 	}
 
 	f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
@@ -388,6 +381,7 @@  static struct tcf_proto_ops cls_fw_ops __read_mostly = {
 	.walk		=	fw_walk,
 	.dump		=	fw_dump,
 	.owner		=	THIS_MODULE,
+	.root_size	= 	sizeof(struct fw_head),
 };
 
 static int __init init_fw(void)
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index 1ad3068..038f35f 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -279,7 +279,6 @@  static void route4_destroy(struct tcf_proto *tp)
 			kfree(b);
 		}
 	}
-	kfree(head);
 }
 
 static int route4_delete(struct tcf_proto *tp, unsigned long arg)
@@ -462,20 +461,9 @@  static int route4_change(struct net *net, struct sk_buff *in_skb,
 		goto reinsert;
 	}
 
-	err = -ENOBUFS;
-	if (head == NULL) {
-		head = kzalloc(sizeof(struct route4_head), GFP_KERNEL);
-		if (head == NULL)
-			goto errout;
-
-		tcf_tree_lock(tp);
-		tp->root = head;
-		tcf_tree_unlock(tp);
-	}
-
 	f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);
 	if (f == NULL)
-		goto errout;
+		return -ENOBUFS;
 
 	tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
 	err = route4_set_parms(net, tp, base, f, handle, head, tb,
@@ -613,6 +601,7 @@  static struct tcf_proto_ops cls_route4_ops __read_mostly = {
 	.walk		=	route4_walk,
 	.dump		=	route4_dump,
 	.owner		=	THIS_MODULE,
+	.root_size	=	sizeof(struct route4_head),
 };
 
 static int __init init_route4(void)
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
index 19f8e5d..47930bc 100644
--- a/net/sched/cls_rsvp.h
+++ b/net/sched/cls_rsvp.h
@@ -242,14 +242,7 @@  static void rsvp_put(struct tcf_proto *tp, unsigned long f)
 
 static int rsvp_init(struct tcf_proto *tp)
 {
-	struct rsvp_head *data;
-
-	data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
-	if (data) {
-		tp->root = data;
-		return 0;
-	}
-	return -ENOBUFS;
+	return 0;
 }
 
 static void
@@ -656,6 +649,7 @@  static struct tcf_proto_ops RSVP_OPS __read_mostly = {
 	.walk		=	rsvp_walk,
 	.dump		=	rsvp_dump,
 	.owner		=	THIS_MODULE,
+	.root_size	=	sizeof(struct rsvp_head),
 };
 
 static int __init init_rsvp(void)
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index eed8404..6454158 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -118,18 +118,11 @@  static void tcindex_put(struct tcf_proto *tp, unsigned long f)
 
 static int tcindex_init(struct tcf_proto *tp)
 {
-	struct tcindex_data *p;
-
-	pr_debug("tcindex_init(tp %p)\n", tp);
-	p = kzalloc(sizeof(struct tcindex_data), GFP_KERNEL);
-	if (!p)
-		return -ENOMEM;
+	struct tcindex_data *p = tp->root;
 
 	p->mask = 0xffff;
 	p->hash = DEFAULT_HASH_SIZE;
 	p->fall_through = 1;
-
-	tp->root = p;
 	return 0;
 }
 
@@ -407,15 +400,12 @@  static void tcindex_destroy(struct tcf_proto *tp)
 	struct tcindex_data *p = tp->root;
 	struct tcf_walker walker;
 
-	pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p);
 	walker.count = 0;
 	walker.skip = 0;
 	walker.fn = &tcindex_destroy_element;
 	tcindex_walk(tp, &walker);
 	kfree(p->perfect);
 	kfree(p->h);
-	kfree(p);
-	tp->root = NULL;
 }
 
 
@@ -491,6 +481,7 @@  static struct tcf_proto_ops cls_tcindex_ops __read_mostly = {
 	.walk		=	tcindex_walk,
 	.dump		=	tcindex_dump,
 	.owner		=	THIS_MODULE,
+	.root_size	=	sizeof(struct tcindex_data),
 };
 
 static int __init init_tcindex(void)
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 84c28da..678c2d72 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -300,15 +300,11 @@  static u32 gen_new_htid(struct tc_u_common *tp_c)
 
 static int u32_init(struct tcf_proto *tp)
 {
-	struct tc_u_hnode *root_ht;
+	struct tc_u_hnode *root_ht = tp->root;
 	struct tc_u_common *tp_c;
 
 	tp_c = tp->q->u32_node;
 
-	root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL);
-	if (root_ht == NULL)
-		return -ENOBUFS;
-
 	root_ht->divisor = 0;
 	root_ht->refcnt++;
 	root_ht->handle = tp_c ? gen_new_htid(tp_c) : 0x80000000;
@@ -329,7 +325,6 @@  static int u32_init(struct tcf_proto *tp)
 	tp_c->hlist = root_ht;
 	root_ht->tp_c = tp_c;
 
-	tp->root = root_ht;
 	tp->data = tp_c;
 	return 0;
 }
@@ -394,7 +389,6 @@  static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
 	for (hn = &tp_c->hlist; *hn; hn = &(*hn)->next) {
 		if (*hn == ht) {
 			*hn = ht->next;
-			kfree(ht);
 			return 0;
 		}
 	}
@@ -801,6 +795,7 @@  static struct tcf_proto_ops cls_u32_ops __read_mostly = {
 	.walk		=	u32_walk,
 	.dump		=	u32_dump,
 	.owner		=	THIS_MODULE,
+	.root_size	=	sizeof(struct tc_u_hnode),
 };
 
 static int __init init_u32(void)
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 1313145..5fef7f4 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1829,6 +1829,7 @@  EXPORT_SYMBOL(tc_classify);
 void tcf_destroy(struct tcf_proto *tp)
 {
 	tp->ops->destroy(tp);
+	kfree(tp->root);
 	module_put(tp->ops->owner);
 	kfree(tp);
 }