diff options
Diffstat (limited to 'net/sched')
| -rw-r--r-- | net/sched/act_meta_mark.c | 1 | ||||
| -rw-r--r-- | net/sched/act_meta_skbtcindex.c | 1 | ||||
| -rw-r--r-- | net/sched/act_sample.c | 14 | ||||
| -rw-r--r-- | net/sched/cls_api.c | 22 | ||||
| -rw-r--r-- | net/sched/cls_bpf.c | 116 | ||||
| -rw-r--r-- | net/sched/cls_u32.c | 1 | ||||
| -rw-r--r-- | net/sched/sch_api.c | 2 | ||||
| -rw-r--r-- | net/sched/sch_cbq.c | 9 | ||||
| -rw-r--r-- | net/sched/sch_choke.c | 3 | ||||
| -rw-r--r-- | net/sched/sch_generic.c | 3 | ||||
| -rw-r--r-- | net/sched/sch_gred.c | 3 | ||||
| -rw-r--r-- | net/sched/sch_ingress.c | 15 | ||||
| -rw-r--r-- | net/sched/sch_red.c | 33 | ||||
| -rw-r--r-- | net/sched/sch_sfq.c | 4 | 
14 files changed, 114 insertions, 113 deletions
| diff --git a/net/sched/act_meta_mark.c b/net/sched/act_meta_mark.c index 1e3f10e5da99..6445184b2759 100644 --- a/net/sched/act_meta_mark.c +++ b/net/sched/act_meta_mark.c @@ -22,7 +22,6 @@  #include <net/pkt_sched.h>  #include <uapi/linux/tc_act/tc_ife.h>  #include <net/tc_act/tc_ife.h> -#include <linux/rtnetlink.h>  static int skbmark_encode(struct sk_buff *skb, void *skbdata,  			  struct tcf_meta_info *e) diff --git a/net/sched/act_meta_skbtcindex.c b/net/sched/act_meta_skbtcindex.c index 2ea1f26c9e96..7221437ca3a6 100644 --- a/net/sched/act_meta_skbtcindex.c +++ b/net/sched/act_meta_skbtcindex.c @@ -22,7 +22,6 @@  #include <net/pkt_sched.h>  #include <uapi/linux/tc_act/tc_ife.h>  #include <net/tc_act/tc_ife.h> -#include <linux/rtnetlink.h>  static int skbtcindex_encode(struct sk_buff *skb, void *skbdata,  			     struct tcf_meta_info *e) diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c index 8b5abcd2f32f..9438969290a6 100644 --- a/net/sched/act_sample.c +++ b/net/sched/act_sample.c @@ -96,23 +96,16 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,  	return ret;  } -static void tcf_sample_cleanup_rcu(struct rcu_head *rcu) +static void tcf_sample_cleanup(struct tc_action *a, int bind)  { -	struct tcf_sample *s = container_of(rcu, struct tcf_sample, rcu); +	struct tcf_sample *s = to_sample(a);  	struct psample_group *psample_group; -	psample_group = rcu_dereference_protected(s->psample_group, 1); +	psample_group = rtnl_dereference(s->psample_group);  	RCU_INIT_POINTER(s->psample_group, NULL);  	psample_group_put(psample_group);  } -static void tcf_sample_cleanup(struct tc_action *a, int bind) -{ -	struct tcf_sample *s = to_sample(a); - -	call_rcu(&s->rcu, tcf_sample_cleanup_rcu); -} -  static bool tcf_sample_dev_ok_push(struct net_device *dev)  {  	switch (dev->type) { @@ -264,7 +257,6 @@ static int __init sample_init_module(void)  static void __exit sample_cleanup_module(void)  { -	rcu_barrier();  	tcf_unregister_action(&act_sample_ops, &sample_net_ops);  } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 7d97f612c9b9..b91ea03e3afa 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -23,7 +23,6 @@  #include <linux/skbuff.h>  #include <linux/init.h>  #include <linux/kmod.h> -#include <linux/err.h>  #include <linux/slab.h>  #include <net/net_namespace.h>  #include <net/sock.h> @@ -336,7 +335,8 @@ static void tcf_block_put_final(struct work_struct *work)  	struct tcf_chain *chain, *tmp;  	rtnl_lock(); -	/* Only chain 0 should be still here. */ + +	/* At this point, all the chains should have refcnt == 1. */  	list_for_each_entry_safe(chain, tmp, &block->chain_list, list)  		tcf_chain_put(chain);  	rtnl_unlock(); @@ -344,15 +344,23 @@ static void tcf_block_put_final(struct work_struct *work)  }  /* XXX: Standalone actions are not allowed to jump to any chain, and bound - * actions should be all removed after flushing. However, filters are now - * destroyed in tc filter workqueue with RTNL lock, they can not race here. + * actions should be all removed after flushing.   */  void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,  		       struct tcf_block_ext_info *ei)  { -	struct tcf_chain *chain, *tmp; +	struct tcf_chain *chain; -	list_for_each_entry_safe(chain, tmp, &block->chain_list, list) +	if (!block) +		return; +	/* Hold a refcnt for all chains, except 0, so that they don't disappear +	 * while we are iterating. +	 */ +	list_for_each_entry(chain, &block->chain_list, list) +		if (chain->index) +			tcf_chain_hold(chain); + +	list_for_each_entry(chain, &block->chain_list, list)  		tcf_chain_flush(chain);  	tcf_block_offload_unbind(block, q, ei); @@ -371,8 +379,6 @@ void tcf_block_put(struct tcf_block *block)  {  	struct tcf_block_ext_info ei = {0, }; -	if (!block) -		return;  	tcf_block_put_ext(block, block->q, &ei);  } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index a9f3e317055c..8d78e7f4ecc3 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -42,7 +42,6 @@ struct cls_bpf_prog {  	struct list_head link;  	struct tcf_result res;  	bool exts_integrated; -	bool offloaded;  	u32 gen_flags;  	struct tcf_exts exts;  	u32 handle; @@ -148,33 +147,37 @@ static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog)  }  static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, -			       enum tc_clsbpf_command cmd) +			       struct cls_bpf_prog *oldprog)  { -	bool addorrep = cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE;  	struct tcf_block *block = tp->chain->block; -	bool skip_sw = tc_skip_sw(prog->gen_flags);  	struct tc_cls_bpf_offload cls_bpf = {}; +	struct cls_bpf_prog *obj; +	bool skip_sw;  	int err; +	skip_sw = prog && tc_skip_sw(prog->gen_flags); +	obj = prog ?: oldprog; +  	tc_cls_common_offload_init(&cls_bpf.common, tp); -	cls_bpf.command = cmd; -	cls_bpf.exts = &prog->exts; -	cls_bpf.prog = prog->filter; -	cls_bpf.name = prog->bpf_name; -	cls_bpf.exts_integrated = prog->exts_integrated; -	cls_bpf.gen_flags = prog->gen_flags; +	cls_bpf.command = TC_CLSBPF_OFFLOAD; +	cls_bpf.exts = &obj->exts; +	cls_bpf.prog = prog ? prog->filter : NULL; +	cls_bpf.oldprog = oldprog ? oldprog->filter : NULL; +	cls_bpf.name = obj->bpf_name; +	cls_bpf.exts_integrated = obj->exts_integrated; +	cls_bpf.gen_flags = obj->gen_flags;  	err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, skip_sw); -	if (addorrep) { +	if (prog) {  		if (err < 0) { -			cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_DESTROY); +			cls_bpf_offload_cmd(tp, oldprog, prog);  			return err;  		} else if (err > 0) {  			prog->gen_flags |= TCA_CLS_FLAGS_IN_HW;  		}  	} -	if (addorrep && skip_sw && !(prog->gen_flags & TCA_CLS_FLAGS_IN_HW)) +	if (prog && skip_sw && !(prog->gen_flags & TCA_CLS_FLAGS_IN_HW))  		return -EINVAL;  	return 0; @@ -183,38 +186,17 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,  static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog,  			   struct cls_bpf_prog *oldprog)  { -	struct cls_bpf_prog *obj = prog; -	enum tc_clsbpf_command cmd; -	bool skip_sw; -	int ret; - -	skip_sw = tc_skip_sw(prog->gen_flags) || -		(oldprog && tc_skip_sw(oldprog->gen_flags)); - -	if (oldprog && oldprog->offloaded) { -		if (!tc_skip_hw(prog->gen_flags)) { -			cmd = TC_CLSBPF_REPLACE; -		} else if (!tc_skip_sw(prog->gen_flags)) { -			obj = oldprog; -			cmd = TC_CLSBPF_DESTROY; -		} else { -			return -EINVAL; -		} -	} else { -		if (tc_skip_hw(prog->gen_flags)) -			return skip_sw ? -EINVAL : 0; -		cmd = TC_CLSBPF_ADD; -	} - -	ret = cls_bpf_offload_cmd(tp, obj, cmd); -	if (ret) -		return ret; +	if (prog && oldprog && prog->gen_flags != oldprog->gen_flags) +		return -EINVAL; -	obj->offloaded = true; -	if (oldprog) -		oldprog->offloaded = false; +	if (prog && tc_skip_hw(prog->gen_flags)) +		prog = NULL; +	if (oldprog && tc_skip_hw(oldprog->gen_flags)) +		oldprog = NULL; +	if (!prog && !oldprog) +		return 0; -	return 0; +	return cls_bpf_offload_cmd(tp, prog, oldprog);  }  static void cls_bpf_stop_offload(struct tcf_proto *tp, @@ -222,25 +204,26 @@ static void cls_bpf_stop_offload(struct tcf_proto *tp,  {  	int err; -	if (!prog->offloaded) -		return; - -	err = cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_DESTROY); -	if (err) { +	err = cls_bpf_offload_cmd(tp, NULL, prog); +	if (err)  		pr_err("Stopping hardware offload failed: %d\n", err); -		return; -	} - -	prog->offloaded = false;  }  static void cls_bpf_offload_update_stats(struct tcf_proto *tp,  					 struct cls_bpf_prog *prog)  { -	if (!prog->offloaded) -		return; +	struct tcf_block *block = tp->chain->block; +	struct tc_cls_bpf_offload cls_bpf = {}; + +	tc_cls_common_offload_init(&cls_bpf.common, tp); +	cls_bpf.command = TC_CLSBPF_STATS; +	cls_bpf.exts = &prog->exts; +	cls_bpf.prog = prog->filter; +	cls_bpf.name = prog->bpf_name; +	cls_bpf.exts_integrated = prog->exts_integrated; +	cls_bpf.gen_flags = prog->gen_flags; -	cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_STATS); +	tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, false);  }  static int cls_bpf_init(struct tcf_proto *tp) @@ -258,11 +241,8 @@ static int cls_bpf_init(struct tcf_proto *tp)  	return 0;  } -static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog) +static void cls_bpf_free_parms(struct cls_bpf_prog *prog)  { -	tcf_exts_destroy(&prog->exts); -	tcf_exts_put_net(&prog->exts); -  	if (cls_bpf_is_ebpf(prog))  		bpf_prog_put(prog->filter);  	else @@ -270,6 +250,14 @@ static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog)  	kfree(prog->bpf_name);  	kfree(prog->bpf_ops); +} + +static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog) +{ +	tcf_exts_destroy(&prog->exts); +	tcf_exts_put_net(&prog->exts); + +	cls_bpf_free_parms(prog);  	kfree(prog);  } @@ -514,12 +502,8 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,  		goto errout_idr;  	ret = cls_bpf_offload(tp, prog, oldprog); -	if (ret) { -		if (!oldprog) -			idr_remove_ext(&head->handle_idr, prog->handle); -		__cls_bpf_delete_prog(prog); -		return ret; -	} +	if (ret) +		goto errout_parms;  	if (!tc_in_hw(prog->gen_flags))  		prog->gen_flags |= TCA_CLS_FLAGS_NOT_IN_HW; @@ -537,6 +521,8 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,  	*arg = prog;  	return 0; +errout_parms: +	cls_bpf_free_parms(prog);  errout_idr:  	if (!oldprog)  		idr_remove_ext(&head->handle_idr, prog->handle); diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index ac152b4f4247..507859cdd1cb 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -45,7 +45,6 @@  #include <net/netlink.h>  #include <net/act_api.h>  #include <net/pkt_cls.h> -#include <linux/netdevice.h>  #include <linux/idr.h>  struct tc_u_knode { diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index b6c4f536876b..0f1eab99ff4e 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -795,6 +795,8 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,  	tcm->tcm_info = refcount_read(&q->refcnt);  	if (nla_put_string(skb, TCA_KIND, q->ops->id))  		goto nla_put_failure; +	if (nla_put_u8(skb, TCA_HW_OFFLOAD, !!(q->flags & TCQ_F_OFFLOADED))) +		goto nla_put_failure;  	if (q->ops->dump && q->ops->dump(q, skb) < 0)  		goto nla_put_failure;  	qlen = q->q.qlen; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 6361be7881f1..525eb3a6d625 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1158,9 +1158,13 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)  	if ((q->link.R_tab = qdisc_get_rtab(r, tb[TCA_CBQ_RTAB])) == NULL)  		return -EINVAL; +	err = tcf_block_get(&q->link.block, &q->link.filter_list, sch); +	if (err) +		goto put_rtab; +  	err = qdisc_class_hash_init(&q->clhash);  	if (err < 0) -		goto put_rtab; +		goto put_block;  	q->link.sibling = &q->link;  	q->link.common.classid = sch->handle; @@ -1194,6 +1198,9 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)  	cbq_addprio(q, &q->link);  	return 0; +put_block: +	tcf_block_put(q->link.block); +  put_rtab:  	qdisc_put_rtab(q->link.R_tab);  	return err; diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index b30a2c70bd48..531250fceb9e 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -369,6 +369,9 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)  	ctl = nla_data(tb[TCA_CHOKE_PARMS]); +	if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog)) +		return -EINVAL; +  	if (ctl->limit > CHOKE_MAX_QUEUE)  		return -EINVAL; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 3839cbbdc32b..cd1b200acae7 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -26,6 +26,7 @@  #include <linux/list.h>  #include <linux/slab.h>  #include <linux/if_vlan.h> +#include <linux/if_macvlan.h>  #include <net/sch_generic.h>  #include <net/pkt_sched.h>  #include <net/dst.h> @@ -277,6 +278,8 @@ unsigned long dev_trans_start(struct net_device *dev)  	if (is_vlan_dev(dev))  		dev = vlan_dev_real_dev(dev); +	else if (netif_is_macvlan(dev)) +		dev = macvlan_dev_real_dev(dev);  	res = netdev_get_tx_queue(dev, 0)->trans_start;  	for (i = 1; i < dev->num_tx_queues; i++) {  		val = netdev_get_tx_queue(dev, i)->trans_start; diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index 17c7130454bd..bc30f9186ac6 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -356,6 +356,9 @@ static inline int gred_change_vq(struct Qdisc *sch, int dp,  	struct gred_sched *table = qdisc_priv(sch);  	struct gred_sched_data *q = table->tab[dp]; +	if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog)) +		return -EINVAL; +  	if (!q) {  		table->tab[dp] = q = *prealloc;  		*prealloc = NULL; diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 5ecc38f35d47..fc1286f499c1 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -68,6 +68,8 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt)  	struct net_device *dev = qdisc_dev(sch);  	int err; +	net_inc_ingress_queue(); +  	mini_qdisc_pair_init(&q->miniqp, sch, &dev->miniq_ingress);  	q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; @@ -78,7 +80,6 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt)  	if (err)  		return err; -	net_inc_ingress_queue();  	sch->flags |= TCQ_F_CPUSTATS;  	return 0; @@ -172,6 +173,9 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt)  	struct net_device *dev = qdisc_dev(sch);  	int err; +	net_inc_ingress_queue(); +	net_inc_egress_queue(); +  	mini_qdisc_pair_init(&q->miniqp_ingress, sch, &dev->miniq_ingress);  	q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; @@ -190,18 +194,11 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt)  	err = tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info);  	if (err) -		goto err_egress_block_get; - -	net_inc_ingress_queue(); -	net_inc_egress_queue(); +		return err;  	sch->flags |= TCQ_F_CPUSTATS;  	return 0; - -err_egress_block_get: -	tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info); -	return err;  }  static void clsact_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 7f8ea9e297c3..f0747eb87dc4 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -157,6 +157,7 @@ static int red_offload(struct Qdisc *sch, bool enable)  		.handle = sch->handle,  		.parent = sch->parent,  	}; +	int err;  	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)  		return -EOPNOTSUPP; @@ -171,7 +172,14 @@ static int red_offload(struct Qdisc *sch, bool enable)  		opt.command = TC_RED_DESTROY;  	} -	return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, &opt); +	err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, &opt); + +	if (!err && enable) +		sch->flags |= TCQ_F_OFFLOADED; +	else +		sch->flags &= ~TCQ_F_OFFLOADED; + +	return err;  }  static void red_destroy(struct Qdisc *sch) @@ -212,6 +220,8 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt)  	max_P = tb[TCA_RED_MAX_P] ? nla_get_u32(tb[TCA_RED_MAX_P]) : 0;  	ctl = nla_data(tb[TCA_RED_PARMS]); +	if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog)) +		return -EINVAL;  	if (ctl->limit > 0) {  		child = fifo_create_dflt(sch, &bfifo_qdisc_ops, ctl->limit); @@ -272,7 +282,7 @@ static int red_init(struct Qdisc *sch, struct nlattr *opt)  	return red_change(sch, opt);  } -static int red_dump_offload(struct Qdisc *sch, struct tc_red_qopt *opt) +static int red_dump_offload_stats(struct Qdisc *sch, struct tc_red_qopt *opt)  {  	struct net_device *dev = qdisc_dev(sch);  	struct tc_red_qopt_offload hw_stats = { @@ -284,21 +294,12 @@ static int red_dump_offload(struct Qdisc *sch, struct tc_red_qopt *opt)  			.stats.qstats = &sch->qstats,  		},  	}; -	int err; -	opt->flags &= ~TC_RED_OFFLOADED; -	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) -		return 0; - -	err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, -					    &hw_stats); -	if (err == -EOPNOTSUPP) +	if (!(sch->flags & TCQ_F_OFFLOADED))  		return 0; -	if (!err) -		opt->flags |= TC_RED_OFFLOADED; - -	return err; +	return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, +					     &hw_stats);  }  static int red_dump(struct Qdisc *sch, struct sk_buff *skb) @@ -317,7 +318,7 @@ static int red_dump(struct Qdisc *sch, struct sk_buff *skb)  	int err;  	sch->qstats.backlog = q->qdisc->qstats.backlog; -	err = red_dump_offload(sch, &opt); +	err = red_dump_offload_stats(sch, &opt);  	if (err)  		goto nla_put_failure; @@ -345,7 +346,7 @@ static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d)  		.marked	= q->stats.prob_mark + q->stats.forced_mark,  	}; -	if (tc_can_offload(dev) &&  dev->netdev_ops->ndo_setup_tc) { +	if (sch->flags & TCQ_F_OFFLOADED) {  		struct red_stats hw_stats = {0};  		struct tc_red_qopt_offload hw_stats_request = {  			.command = TC_RED_XSTATS, diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 890f4a4564e7..930e5bd26d3d 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -639,6 +639,9 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)  	if (ctl->divisor &&  	    (!is_power_of_2(ctl->divisor) || ctl->divisor > 65536))  		return -EINVAL; +	if (ctl_v1 && !red_check_params(ctl_v1->qth_min, ctl_v1->qth_max, +					ctl_v1->Wlog)) +		return -EINVAL;  	if (ctl_v1 && ctl_v1->qth_min) {  		p = kmalloc(sizeof(*p), GFP_KERNEL);  		if (!p) @@ -724,6 +727,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)  	int i;  	int err; +	q->sch = sch;  	timer_setup(&q->perturb_timer, sfq_perturbation, TIMER_DEFERRABLE);  	err = tcf_block_get(&q->block, &q->filter_list, sch); |