diff options
author | Arjan van de Ven <arjan@linux.intel.com> | 2008-10-17 12:20:26 -0400 |
---|---|---|
committer | Arjan van de Ven <arjan@linux.intel.com> | 2008-10-17 12:20:26 -0400 |
commit | 651dab4264e4ba0e563f5ff56f748127246e9065 (patch) | |
tree | 016630974bdcb00fe529b673f96d389e0fd6dc94 /net/sched | |
parent | 40b8606253552109815786e5d4b0de98782d31f5 (diff) | |
parent | 2e532d68a2b3e2aa6b19731501222069735c741c (diff) |
Merge commit 'linus/master' into merge-linus
Conflicts:
arch/x86/kvm/i8254.c
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/Kconfig | 20 | ||||
-rw-r--r-- | net/sched/Makefile | 2 | ||||
-rw-r--r-- | net/sched/act_ipt.c | 46 | ||||
-rw-r--r-- | net/sched/act_skbedit.c | 203 | ||||
-rw-r--r-- | net/sched/cls_flow.c | 28 | ||||
-rw-r--r-- | net/sched/em_cmp.c | 9 | ||||
-rw-r--r-- | net/sched/sch_dsmark.c | 8 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 32 | ||||
-rw-r--r-- | net/sched/sch_multiq.c | 477 | ||||
-rw-r--r-- | net/sched/sch_netem.c | 18 | ||||
-rw-r--r-- | net/sched/sch_prio.c | 6 | ||||
-rw-r--r-- | net/sched/sch_sfq.c | 4 |
12 files changed, 789 insertions, 64 deletions
diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 9437b27ff84d..6767e54155db 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig | |||
@@ -106,6 +106,15 @@ config NET_SCH_PRIO | |||
106 | To compile this code as a module, choose M here: the | 106 | To compile this code as a module, choose M here: the |
107 | module will be called sch_prio. | 107 | module will be called sch_prio. |
108 | 108 | ||
109 | config NET_SCH_MULTIQ | ||
110 | tristate "Hardware Multiqueue-aware Multi Band Queuing (MULTIQ)" | ||
111 | ---help--- | ||
112 | Say Y here if you want to use an n-band queue packet scheduler | ||
113 | to support devices that have multiple hardware transmit queues. | ||
114 | |||
115 | To compile this code as a module, choose M here: the | ||
116 | module will be called sch_multiq. | ||
117 | |||
109 | config NET_SCH_RED | 118 | config NET_SCH_RED |
110 | tristate "Random Early Detection (RED)" | 119 | tristate "Random Early Detection (RED)" |
111 | ---help--- | 120 | ---help--- |
@@ -476,6 +485,17 @@ config NET_ACT_SIMP | |||
476 | To compile this code as a module, choose M here: the | 485 | To compile this code as a module, choose M here: the |
477 | module will be called simple. | 486 | module will be called simple. |
478 | 487 | ||
488 | config NET_ACT_SKBEDIT | ||
489 | tristate "SKB Editing" | ||
490 | depends on NET_CLS_ACT | ||
491 | ---help--- | ||
492 | Say Y here to change skb priority or queue_mapping settings. | ||
493 | |||
494 | If unsure, say N. | ||
495 | |||
496 | To compile this code as a module, choose M here: the | ||
497 | module will be called skbedit. | ||
498 | |||
479 | config NET_CLS_IND | 499 | config NET_CLS_IND |
480 | bool "Incoming device classification" | 500 | bool "Incoming device classification" |
481 | depends on NET_CLS_U32 || NET_CLS_FW | 501 | depends on NET_CLS_U32 || NET_CLS_FW |
diff --git a/net/sched/Makefile b/net/sched/Makefile index 1d2b0f7df848..e60c9925b269 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_NET_ACT_IPT) += act_ipt.o | |||
14 | obj-$(CONFIG_NET_ACT_NAT) += act_nat.o | 14 | obj-$(CONFIG_NET_ACT_NAT) += act_nat.o |
15 | obj-$(CONFIG_NET_ACT_PEDIT) += act_pedit.o | 15 | obj-$(CONFIG_NET_ACT_PEDIT) += act_pedit.o |
16 | obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o | 16 | obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o |
17 | obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o | ||
17 | obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o | 18 | obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o |
18 | obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o | 19 | obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o |
19 | obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o | 20 | obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o |
@@ -26,6 +27,7 @@ obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o | |||
26 | obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o | 27 | obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o |
27 | obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o | 28 | obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o |
28 | obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o | 29 | obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o |
30 | obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o | ||
29 | obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o | 31 | obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o |
30 | obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o | 32 | obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o |
31 | obj-$(CONFIG_NET_CLS_U32) += cls_u32.o | 33 | obj-$(CONFIG_NET_CLS_U32) += cls_u32.o |
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index d1263b3c96c3..0453d79ebf57 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c | |||
@@ -40,6 +40,7 @@ static struct tcf_hashinfo ipt_hash_info = { | |||
40 | 40 | ||
41 | static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook) | 41 | static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook) |
42 | { | 42 | { |
43 | struct xt_tgchk_param par; | ||
43 | struct xt_target *target; | 44 | struct xt_target *target; |
44 | int ret = 0; | 45 | int ret = 0; |
45 | 46 | ||
@@ -49,29 +50,30 @@ static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int | |||
49 | return -ENOENT; | 50 | return -ENOENT; |
50 | 51 | ||
51 | t->u.kernel.target = target; | 52 | t->u.kernel.target = target; |
52 | 53 | par.table = table; | |
53 | ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), | 54 | par.entryinfo = NULL; |
54 | table, hook, 0, 0); | 55 | par.target = target; |
55 | if (ret) { | 56 | par.targinfo = t->data; |
57 | par.hook_mask = hook; | ||
58 | par.family = NFPROTO_IPV4; | ||
59 | |||
60 | ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); | ||
61 | if (ret < 0) { | ||
56 | module_put(t->u.kernel.target->me); | 62 | module_put(t->u.kernel.target->me); |
57 | return ret; | 63 | return ret; |
58 | } | 64 | } |
59 | if (t->u.kernel.target->checkentry | 65 | return 0; |
60 | && !t->u.kernel.target->checkentry(table, NULL, | ||
61 | t->u.kernel.target, t->data, | ||
62 | hook)) { | ||
63 | module_put(t->u.kernel.target->me); | ||
64 | ret = -EINVAL; | ||
65 | } | ||
66 | |||
67 | return ret; | ||
68 | } | 66 | } |
69 | 67 | ||
70 | static void ipt_destroy_target(struct ipt_entry_target *t) | 68 | static void ipt_destroy_target(struct ipt_entry_target *t) |
71 | { | 69 | { |
72 | if (t->u.kernel.target->destroy) | 70 | struct xt_tgdtor_param par = { |
73 | t->u.kernel.target->destroy(t->u.kernel.target, t->data); | 71 | .target = t->u.kernel.target, |
74 | module_put(t->u.kernel.target->me); | 72 | .targinfo = t->data, |
73 | }; | ||
74 | if (par.target->destroy != NULL) | ||
75 | par.target->destroy(&par); | ||
76 | module_put(par.target->me); | ||
75 | } | 77 | } |
76 | 78 | ||
77 | static int tcf_ipt_release(struct tcf_ipt *ipt, int bind) | 79 | static int tcf_ipt_release(struct tcf_ipt *ipt, int bind) |
@@ -196,6 +198,7 @@ static int tcf_ipt(struct sk_buff *skb, struct tc_action *a, | |||
196 | { | 198 | { |
197 | int ret = 0, result = 0; | 199 | int ret = 0, result = 0; |
198 | struct tcf_ipt *ipt = a->priv; | 200 | struct tcf_ipt *ipt = a->priv; |
201 | struct xt_target_param par; | ||
199 | 202 | ||
200 | if (skb_cloned(skb)) { | 203 | if (skb_cloned(skb)) { |
201 | if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) | 204 | if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) |
@@ -211,10 +214,13 @@ static int tcf_ipt(struct sk_buff *skb, struct tc_action *a, | |||
211 | /* yes, we have to worry about both in and out dev | 214 | /* yes, we have to worry about both in and out dev |
212 | worry later - danger - this API seems to have changed | 215 | worry later - danger - this API seems to have changed |
213 | from earlier kernels */ | 216 | from earlier kernels */ |
214 | ret = ipt->tcfi_t->u.kernel.target->target(skb, skb->dev, NULL, | 217 | par.in = skb->dev; |
215 | ipt->tcfi_hook, | 218 | par.out = NULL; |
216 | ipt->tcfi_t->u.kernel.target, | 219 | par.hooknum = ipt->tcfi_hook; |
217 | ipt->tcfi_t->data); | 220 | par.target = ipt->tcfi_t->u.kernel.target; |
221 | par.targinfo = ipt->tcfi_t->data; | ||
222 | ret = par.target->target(skb, &par); | ||
223 | |||
218 | switch (ret) { | 224 | switch (ret) { |
219 | case NF_ACCEPT: | 225 | case NF_ACCEPT: |
220 | result = TC_ACT_OK; | 226 | result = TC_ACT_OK; |
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c new file mode 100644 index 000000000000..fe9777e77f35 --- /dev/null +++ b/net/sched/act_skbedit.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008, Intel Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
16 | * | ||
17 | * Author: Alexander Duyck <alexander.h.duyck@intel.com> | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/skbuff.h> | ||
24 | #include <linux/rtnetlink.h> | ||
25 | #include <net/netlink.h> | ||
26 | #include <net/pkt_sched.h> | ||
27 | |||
28 | #include <linux/tc_act/tc_skbedit.h> | ||
29 | #include <net/tc_act/tc_skbedit.h> | ||
30 | |||
31 | #define SKBEDIT_TAB_MASK 15 | ||
32 | static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1]; | ||
33 | static u32 skbedit_idx_gen; | ||
34 | static DEFINE_RWLOCK(skbedit_lock); | ||
35 | |||
36 | static struct tcf_hashinfo skbedit_hash_info = { | ||
37 | .htab = tcf_skbedit_ht, | ||
38 | .hmask = SKBEDIT_TAB_MASK, | ||
39 | .lock = &skbedit_lock, | ||
40 | }; | ||
41 | |||
42 | static int tcf_skbedit(struct sk_buff *skb, struct tc_action *a, | ||
43 | struct tcf_result *res) | ||
44 | { | ||
45 | struct tcf_skbedit *d = a->priv; | ||
46 | |||
47 | spin_lock(&d->tcf_lock); | ||
48 | d->tcf_tm.lastuse = jiffies; | ||
49 | d->tcf_bstats.bytes += qdisc_pkt_len(skb); | ||
50 | d->tcf_bstats.packets++; | ||
51 | |||
52 | if (d->flags & SKBEDIT_F_PRIORITY) | ||
53 | skb->priority = d->priority; | ||
54 | if (d->flags & SKBEDIT_F_QUEUE_MAPPING && | ||
55 | skb->dev->real_num_tx_queues > d->queue_mapping) | ||
56 | skb_set_queue_mapping(skb, d->queue_mapping); | ||
57 | |||
58 | spin_unlock(&d->tcf_lock); | ||
59 | return d->tcf_action; | ||
60 | } | ||
61 | |||
62 | static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { | ||
63 | [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, | ||
64 | [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, | ||
65 | [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, | ||
66 | }; | ||
67 | |||
68 | static int tcf_skbedit_init(struct nlattr *nla, struct nlattr *est, | ||
69 | struct tc_action *a, int ovr, int bind) | ||
70 | { | ||
71 | struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; | ||
72 | struct tc_skbedit *parm; | ||
73 | struct tcf_skbedit *d; | ||
74 | struct tcf_common *pc; | ||
75 | u32 flags = 0, *priority = NULL; | ||
76 | u16 *queue_mapping = NULL; | ||
77 | int ret = 0, err; | ||
78 | |||
79 | if (nla == NULL) | ||
80 | return -EINVAL; | ||
81 | |||
82 | err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy); | ||
83 | if (err < 0) | ||
84 | return err; | ||
85 | |||
86 | if (tb[TCA_SKBEDIT_PARMS] == NULL) | ||
87 | return -EINVAL; | ||
88 | |||
89 | if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { | ||
90 | flags |= SKBEDIT_F_PRIORITY; | ||
91 | priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); | ||
92 | } | ||
93 | |||
94 | if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { | ||
95 | flags |= SKBEDIT_F_QUEUE_MAPPING; | ||
96 | queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); | ||
97 | } | ||
98 | if (!flags) | ||
99 | return -EINVAL; | ||
100 | |||
101 | parm = nla_data(tb[TCA_SKBEDIT_PARMS]); | ||
102 | |||
103 | pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info); | ||
104 | if (!pc) { | ||
105 | pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, | ||
106 | &skbedit_idx_gen, &skbedit_hash_info); | ||
107 | if (unlikely(!pc)) | ||
108 | return -ENOMEM; | ||
109 | |||
110 | d = to_skbedit(pc); | ||
111 | ret = ACT_P_CREATED; | ||
112 | } else { | ||
113 | d = to_skbedit(pc); | ||
114 | if (!ovr) { | ||
115 | tcf_hash_release(pc, bind, &skbedit_hash_info); | ||
116 | return -EEXIST; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | spin_lock_bh(&d->tcf_lock); | ||
121 | |||
122 | d->flags = flags; | ||
123 | if (flags & SKBEDIT_F_PRIORITY) | ||
124 | d->priority = *priority; | ||
125 | if (flags & SKBEDIT_F_QUEUE_MAPPING) | ||
126 | d->queue_mapping = *queue_mapping; | ||
127 | d->tcf_action = parm->action; | ||
128 | |||
129 | spin_unlock_bh(&d->tcf_lock); | ||
130 | |||
131 | if (ret == ACT_P_CREATED) | ||
132 | tcf_hash_insert(pc, &skbedit_hash_info); | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | static inline int tcf_skbedit_cleanup(struct tc_action *a, int bind) | ||
137 | { | ||
138 | struct tcf_skbedit *d = a->priv; | ||
139 | |||
140 | if (d) | ||
141 | return tcf_hash_release(&d->common, bind, &skbedit_hash_info); | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static inline int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, | ||
146 | int bind, int ref) | ||
147 | { | ||
148 | unsigned char *b = skb_tail_pointer(skb); | ||
149 | struct tcf_skbedit *d = a->priv; | ||
150 | struct tc_skbedit opt; | ||
151 | struct tcf_t t; | ||
152 | |||
153 | opt.index = d->tcf_index; | ||
154 | opt.refcnt = d->tcf_refcnt - ref; | ||
155 | opt.bindcnt = d->tcf_bindcnt - bind; | ||
156 | opt.action = d->tcf_action; | ||
157 | NLA_PUT(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt); | ||
158 | if (d->flags & SKBEDIT_F_PRIORITY) | ||
159 | NLA_PUT(skb, TCA_SKBEDIT_PRIORITY, sizeof(d->priority), | ||
160 | &d->priority); | ||
161 | if (d->flags & SKBEDIT_F_QUEUE_MAPPING) | ||
162 | NLA_PUT(skb, TCA_SKBEDIT_QUEUE_MAPPING, | ||
163 | sizeof(d->queue_mapping), &d->queue_mapping); | ||
164 | t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); | ||
165 | t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); | ||
166 | t.expires = jiffies_to_clock_t(d->tcf_tm.expires); | ||
167 | NLA_PUT(skb, TCA_SKBEDIT_TM, sizeof(t), &t); | ||
168 | return skb->len; | ||
169 | |||
170 | nla_put_failure: | ||
171 | nlmsg_trim(skb, b); | ||
172 | return -1; | ||
173 | } | ||
174 | |||
175 | static struct tc_action_ops act_skbedit_ops = { | ||
176 | .kind = "skbedit", | ||
177 | .hinfo = &skbedit_hash_info, | ||
178 | .type = TCA_ACT_SKBEDIT, | ||
179 | .capab = TCA_CAP_NONE, | ||
180 | .owner = THIS_MODULE, | ||
181 | .act = tcf_skbedit, | ||
182 | .dump = tcf_skbedit_dump, | ||
183 | .cleanup = tcf_skbedit_cleanup, | ||
184 | .init = tcf_skbedit_init, | ||
185 | .walk = tcf_generic_walker, | ||
186 | }; | ||
187 | |||
188 | MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); | ||
189 | MODULE_DESCRIPTION("SKB Editing"); | ||
190 | MODULE_LICENSE("GPL"); | ||
191 | |||
192 | static int __init skbedit_init_module(void) | ||
193 | { | ||
194 | return tcf_register_action(&act_skbedit_ops); | ||
195 | } | ||
196 | |||
197 | static void __exit skbedit_cleanup_module(void) | ||
198 | { | ||
199 | tcf_unregister_action(&act_skbedit_ops); | ||
200 | } | ||
201 | |||
202 | module_init(skbedit_init_module); | ||
203 | module_exit(skbedit_cleanup_module); | ||
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 8f63a1a94014..0ebaff637e31 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c | |||
@@ -67,9 +67,9 @@ static inline u32 addr_fold(void *addr) | |||
67 | static u32 flow_get_src(const struct sk_buff *skb) | 67 | static u32 flow_get_src(const struct sk_buff *skb) |
68 | { | 68 | { |
69 | switch (skb->protocol) { | 69 | switch (skb->protocol) { |
70 | case __constant_htons(ETH_P_IP): | 70 | case htons(ETH_P_IP): |
71 | return ntohl(ip_hdr(skb)->saddr); | 71 | return ntohl(ip_hdr(skb)->saddr); |
72 | case __constant_htons(ETH_P_IPV6): | 72 | case htons(ETH_P_IPV6): |
73 | return ntohl(ipv6_hdr(skb)->saddr.s6_addr32[3]); | 73 | return ntohl(ipv6_hdr(skb)->saddr.s6_addr32[3]); |
74 | default: | 74 | default: |
75 | return addr_fold(skb->sk); | 75 | return addr_fold(skb->sk); |
@@ -79,9 +79,9 @@ static u32 flow_get_src(const struct sk_buff *skb) | |||
79 | static u32 flow_get_dst(const struct sk_buff *skb) | 79 | static u32 flow_get_dst(const struct sk_buff *skb) |
80 | { | 80 | { |
81 | switch (skb->protocol) { | 81 | switch (skb->protocol) { |
82 | case __constant_htons(ETH_P_IP): | 82 | case htons(ETH_P_IP): |
83 | return ntohl(ip_hdr(skb)->daddr); | 83 | return ntohl(ip_hdr(skb)->daddr); |
84 | case __constant_htons(ETH_P_IPV6): | 84 | case htons(ETH_P_IPV6): |
85 | return ntohl(ipv6_hdr(skb)->daddr.s6_addr32[3]); | 85 | return ntohl(ipv6_hdr(skb)->daddr.s6_addr32[3]); |
86 | default: | 86 | default: |
87 | return addr_fold(skb->dst) ^ (__force u16)skb->protocol; | 87 | return addr_fold(skb->dst) ^ (__force u16)skb->protocol; |
@@ -91,9 +91,9 @@ static u32 flow_get_dst(const struct sk_buff *skb) | |||
91 | static u32 flow_get_proto(const struct sk_buff *skb) | 91 | static u32 flow_get_proto(const struct sk_buff *skb) |
92 | { | 92 | { |
93 | switch (skb->protocol) { | 93 | switch (skb->protocol) { |
94 | case __constant_htons(ETH_P_IP): | 94 | case htons(ETH_P_IP): |
95 | return ip_hdr(skb)->protocol; | 95 | return ip_hdr(skb)->protocol; |
96 | case __constant_htons(ETH_P_IPV6): | 96 | case htons(ETH_P_IPV6): |
97 | return ipv6_hdr(skb)->nexthdr; | 97 | return ipv6_hdr(skb)->nexthdr; |
98 | default: | 98 | default: |
99 | return 0; | 99 | return 0; |
@@ -120,7 +120,7 @@ static u32 flow_get_proto_src(const struct sk_buff *skb) | |||
120 | u32 res = 0; | 120 | u32 res = 0; |
121 | 121 | ||
122 | switch (skb->protocol) { | 122 | switch (skb->protocol) { |
123 | case __constant_htons(ETH_P_IP): { | 123 | case htons(ETH_P_IP): { |
124 | struct iphdr *iph = ip_hdr(skb); | 124 | struct iphdr *iph = ip_hdr(skb); |
125 | 125 | ||
126 | if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && | 126 | if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && |
@@ -128,7 +128,7 @@ static u32 flow_get_proto_src(const struct sk_buff *skb) | |||
128 | res = ntohs(*(__be16 *)((void *)iph + iph->ihl * 4)); | 128 | res = ntohs(*(__be16 *)((void *)iph + iph->ihl * 4)); |
129 | break; | 129 | break; |
130 | } | 130 | } |
131 | case __constant_htons(ETH_P_IPV6): { | 131 | case htons(ETH_P_IPV6): { |
132 | struct ipv6hdr *iph = ipv6_hdr(skb); | 132 | struct ipv6hdr *iph = ipv6_hdr(skb); |
133 | 133 | ||
134 | if (has_ports(iph->nexthdr)) | 134 | if (has_ports(iph->nexthdr)) |
@@ -147,7 +147,7 @@ static u32 flow_get_proto_dst(const struct sk_buff *skb) | |||
147 | u32 res = 0; | 147 | u32 res = 0; |
148 | 148 | ||
149 | switch (skb->protocol) { | 149 | switch (skb->protocol) { |
150 | case __constant_htons(ETH_P_IP): { | 150 | case htons(ETH_P_IP): { |
151 | struct iphdr *iph = ip_hdr(skb); | 151 | struct iphdr *iph = ip_hdr(skb); |
152 | 152 | ||
153 | if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && | 153 | if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && |
@@ -155,7 +155,7 @@ static u32 flow_get_proto_dst(const struct sk_buff *skb) | |||
155 | res = ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 + 2)); | 155 | res = ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 + 2)); |
156 | break; | 156 | break; |
157 | } | 157 | } |
158 | case __constant_htons(ETH_P_IPV6): { | 158 | case htons(ETH_P_IPV6): { |
159 | struct ipv6hdr *iph = ipv6_hdr(skb); | 159 | struct ipv6hdr *iph = ipv6_hdr(skb); |
160 | 160 | ||
161 | if (has_ports(iph->nexthdr)) | 161 | if (has_ports(iph->nexthdr)) |
@@ -213,9 +213,9 @@ static u32 flow_get_nfct(const struct sk_buff *skb) | |||
213 | static u32 flow_get_nfct_src(const struct sk_buff *skb) | 213 | static u32 flow_get_nfct_src(const struct sk_buff *skb) |
214 | { | 214 | { |
215 | switch (skb->protocol) { | 215 | switch (skb->protocol) { |
216 | case __constant_htons(ETH_P_IP): | 216 | case htons(ETH_P_IP): |
217 | return ntohl(CTTUPLE(skb, src.u3.ip)); | 217 | return ntohl(CTTUPLE(skb, src.u3.ip)); |
218 | case __constant_htons(ETH_P_IPV6): | 218 | case htons(ETH_P_IPV6): |
219 | return ntohl(CTTUPLE(skb, src.u3.ip6[3])); | 219 | return ntohl(CTTUPLE(skb, src.u3.ip6[3])); |
220 | } | 220 | } |
221 | fallback: | 221 | fallback: |
@@ -225,9 +225,9 @@ fallback: | |||
225 | static u32 flow_get_nfct_dst(const struct sk_buff *skb) | 225 | static u32 flow_get_nfct_dst(const struct sk_buff *skb) |
226 | { | 226 | { |
227 | switch (skb->protocol) { | 227 | switch (skb->protocol) { |
228 | case __constant_htons(ETH_P_IP): | 228 | case htons(ETH_P_IP): |
229 | return ntohl(CTTUPLE(skb, dst.u3.ip)); | 229 | return ntohl(CTTUPLE(skb, dst.u3.ip)); |
230 | case __constant_htons(ETH_P_IPV6): | 230 | case htons(ETH_P_IPV6): |
231 | return ntohl(CTTUPLE(skb, dst.u3.ip6[3])); | 231 | return ntohl(CTTUPLE(skb, dst.u3.ip6[3])); |
232 | } | 232 | } |
233 | fallback: | 233 | fallback: |
diff --git a/net/sched/em_cmp.c b/net/sched/em_cmp.c index cc49c932641d..bc450397487a 100644 --- a/net/sched/em_cmp.c +++ b/net/sched/em_cmp.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/skbuff.h> | 15 | #include <linux/skbuff.h> |
16 | #include <linux/tc_ematch/tc_em_cmp.h> | 16 | #include <linux/tc_ematch/tc_em_cmp.h> |
17 | #include <asm/unaligned.h> | ||
17 | #include <net/pkt_cls.h> | 18 | #include <net/pkt_cls.h> |
18 | 19 | ||
19 | static inline int cmp_needs_transformation(struct tcf_em_cmp *cmp) | 20 | static inline int cmp_needs_transformation(struct tcf_em_cmp *cmp) |
@@ -37,8 +38,7 @@ static int em_cmp_match(struct sk_buff *skb, struct tcf_ematch *em, | |||
37 | break; | 38 | break; |
38 | 39 | ||
39 | case TCF_EM_ALIGN_U16: | 40 | case TCF_EM_ALIGN_U16: |
40 | val = *ptr << 8; | 41 | val = get_unaligned_be16(ptr); |
41 | val |= *(ptr+1); | ||
42 | 42 | ||
43 | if (cmp_needs_transformation(cmp)) | 43 | if (cmp_needs_transformation(cmp)) |
44 | val = be16_to_cpu(val); | 44 | val = be16_to_cpu(val); |
@@ -47,10 +47,7 @@ static int em_cmp_match(struct sk_buff *skb, struct tcf_ematch *em, | |||
47 | case TCF_EM_ALIGN_U32: | 47 | case TCF_EM_ALIGN_U32: |
48 | /* Worth checking boundries? The branching seems | 48 | /* Worth checking boundries? The branching seems |
49 | * to get worse. Visit again. */ | 49 | * to get worse. Visit again. */ |
50 | val = *ptr << 24; | 50 | val = get_unaligned_be32(ptr); |
51 | val |= *(ptr+1) << 16; | ||
52 | val |= *(ptr+2) << 8; | ||
53 | val |= *(ptr+3); | ||
54 | 51 | ||
55 | if (cmp_needs_transformation(cmp)) | 52 | if (cmp_needs_transformation(cmp)) |
56 | val = be32_to_cpu(val); | 53 | val = be32_to_cpu(val); |
diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index edd1298f85f6..ba43aab3a851 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c | |||
@@ -202,7 +202,7 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
202 | 202 | ||
203 | if (p->set_tc_index) { | 203 | if (p->set_tc_index) { |
204 | switch (skb->protocol) { | 204 | switch (skb->protocol) { |
205 | case __constant_htons(ETH_P_IP): | 205 | case htons(ETH_P_IP): |
206 | if (skb_cow_head(skb, sizeof(struct iphdr))) | 206 | if (skb_cow_head(skb, sizeof(struct iphdr))) |
207 | goto drop; | 207 | goto drop; |
208 | 208 | ||
@@ -210,7 +210,7 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
210 | & ~INET_ECN_MASK; | 210 | & ~INET_ECN_MASK; |
211 | break; | 211 | break; |
212 | 212 | ||
213 | case __constant_htons(ETH_P_IPV6): | 213 | case htons(ETH_P_IPV6): |
214 | if (skb_cow_head(skb, sizeof(struct ipv6hdr))) | 214 | if (skb_cow_head(skb, sizeof(struct ipv6hdr))) |
215 | goto drop; | 215 | goto drop; |
216 | 216 | ||
@@ -289,11 +289,11 @@ static struct sk_buff *dsmark_dequeue(struct Qdisc *sch) | |||
289 | pr_debug("index %d->%d\n", skb->tc_index, index); | 289 | pr_debug("index %d->%d\n", skb->tc_index, index); |
290 | 290 | ||
291 | switch (skb->protocol) { | 291 | switch (skb->protocol) { |
292 | case __constant_htons(ETH_P_IP): | 292 | case htons(ETH_P_IP): |
293 | ipv4_change_dsfield(ip_hdr(skb), p->mask[index], | 293 | ipv4_change_dsfield(ip_hdr(skb), p->mask[index], |
294 | p->value[index]); | 294 | p->value[index]); |
295 | break; | 295 | break; |
296 | case __constant_htons(ETH_P_IPV6): | 296 | case htons(ETH_P_IPV6): |
297 | ipv6_change_dsfield(ipv6_hdr(skb), p->mask[index], | 297 | ipv6_change_dsfield(ipv6_hdr(skb), p->mask[index], |
298 | p->value[index]); | 298 | p->value[index]); |
299 | break; | 299 | break; |
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 9634091ee2f0..7b5572d6beb5 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c | |||
@@ -44,23 +44,30 @@ static inline int qdisc_qlen(struct Qdisc *q) | |||
44 | 44 | ||
45 | static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) | 45 | static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) |
46 | { | 46 | { |
47 | if (unlikely(skb->next)) | 47 | q->gso_skb = skb; |
48 | q->gso_skb = skb; | 48 | q->qstats.requeues++; |
49 | else | ||
50 | q->ops->requeue(skb, q); | ||
51 | |||
52 | __netif_schedule(q); | 49 | __netif_schedule(q); |
50 | |||
53 | return 0; | 51 | return 0; |
54 | } | 52 | } |
55 | 53 | ||
56 | static inline struct sk_buff *dequeue_skb(struct Qdisc *q) | 54 | static inline struct sk_buff *dequeue_skb(struct Qdisc *q) |
57 | { | 55 | { |
58 | struct sk_buff *skb; | 56 | struct sk_buff *skb = q->gso_skb; |
59 | 57 | ||
60 | if ((skb = q->gso_skb)) | 58 | if (unlikely(skb)) { |
61 | q->gso_skb = NULL; | 59 | struct net_device *dev = qdisc_dev(q); |
62 | else | 60 | struct netdev_queue *txq; |
61 | |||
62 | /* check the reason of requeuing without tx lock first */ | ||
63 | txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); | ||
64 | if (!netif_tx_queue_stopped(txq) && !netif_tx_queue_frozen(txq)) | ||
65 | q->gso_skb = NULL; | ||
66 | else | ||
67 | skb = NULL; | ||
68 | } else { | ||
63 | skb = q->dequeue(q); | 69 | skb = q->dequeue(q); |
70 | } | ||
64 | 71 | ||
65 | return skb; | 72 | return skb; |
66 | } | 73 | } |
@@ -215,10 +222,9 @@ static void dev_watchdog(unsigned long arg) | |||
215 | time_after(jiffies, (dev->trans_start + | 222 | time_after(jiffies, (dev->trans_start + |
216 | dev->watchdog_timeo))) { | 223 | dev->watchdog_timeo))) { |
217 | char drivername[64]; | 224 | char drivername[64]; |
218 | printk(KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit timed out\n", | 225 | WARN_ONCE(1, KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit timed out\n", |
219 | dev->name, netdev_drivername(dev, drivername, 64)); | 226 | dev->name, netdev_drivername(dev, drivername, 64)); |
220 | dev->tx_timeout(dev); | 227 | dev->tx_timeout(dev); |
221 | WARN_ON_ONCE(1); | ||
222 | } | 228 | } |
223 | if (!mod_timer(&dev->watchdog_timer, | 229 | if (!mod_timer(&dev->watchdog_timer, |
224 | round_jiffies(jiffies + | 230 | round_jiffies(jiffies + |
@@ -328,6 +334,7 @@ struct Qdisc noop_qdisc = { | |||
328 | .flags = TCQ_F_BUILTIN, | 334 | .flags = TCQ_F_BUILTIN, |
329 | .ops = &noop_qdisc_ops, | 335 | .ops = &noop_qdisc_ops, |
330 | .list = LIST_HEAD_INIT(noop_qdisc.list), | 336 | .list = LIST_HEAD_INIT(noop_qdisc.list), |
337 | .requeue.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), | ||
331 | .q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), | 338 | .q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), |
332 | .dev_queue = &noop_netdev_queue, | 339 | .dev_queue = &noop_netdev_queue, |
333 | }; | 340 | }; |
@@ -353,6 +360,7 @@ static struct Qdisc noqueue_qdisc = { | |||
353 | .flags = TCQ_F_BUILTIN, | 360 | .flags = TCQ_F_BUILTIN, |
354 | .ops = &noqueue_qdisc_ops, | 361 | .ops = &noqueue_qdisc_ops, |
355 | .list = LIST_HEAD_INIT(noqueue_qdisc.list), | 362 | .list = LIST_HEAD_INIT(noqueue_qdisc.list), |
363 | .requeue.lock = __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock), | ||
356 | .q.lock = __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock), | 364 | .q.lock = __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock), |
357 | .dev_queue = &noqueue_netdev_queue, | 365 | .dev_queue = &noqueue_netdev_queue, |
358 | }; | 366 | }; |
@@ -473,6 +481,7 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, | |||
473 | sch->padded = (char *) sch - (char *) p; | 481 | sch->padded = (char *) sch - (char *) p; |
474 | 482 | ||
475 | INIT_LIST_HEAD(&sch->list); | 483 | INIT_LIST_HEAD(&sch->list); |
484 | skb_queue_head_init(&sch->requeue); | ||
476 | skb_queue_head_init(&sch->q); | 485 | skb_queue_head_init(&sch->q); |
477 | sch->ops = ops; | 486 | sch->ops = ops; |
478 | sch->enqueue = ops->enqueue; | 487 | sch->enqueue = ops->enqueue; |
@@ -541,6 +550,7 @@ void qdisc_destroy(struct Qdisc *qdisc) | |||
541 | dev_put(qdisc_dev(qdisc)); | 550 | dev_put(qdisc_dev(qdisc)); |
542 | 551 | ||
543 | kfree_skb(qdisc->gso_skb); | 552 | kfree_skb(qdisc->gso_skb); |
553 | __skb_queue_purge(&qdisc->requeue); | ||
544 | 554 | ||
545 | kfree((char *) qdisc - qdisc->padded); | 555 | kfree((char *) qdisc - qdisc->padded); |
546 | } | 556 | } |
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c new file mode 100644 index 000000000000..915f3149dde2 --- /dev/null +++ b/net/sched/sch_multiq.c | |||
@@ -0,0 +1,477 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008, Intel Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
16 | * | ||
17 | * Author: Alexander Duyck <alexander.h.duyck@intel.com> | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/string.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/skbuff.h> | ||
26 | #include <net/netlink.h> | ||
27 | #include <net/pkt_sched.h> | ||
28 | |||
29 | |||
30 | struct multiq_sched_data { | ||
31 | u16 bands; | ||
32 | u16 max_bands; | ||
33 | u16 curband; | ||
34 | struct tcf_proto *filter_list; | ||
35 | struct Qdisc **queues; | ||
36 | }; | ||
37 | |||
38 | |||
39 | static struct Qdisc * | ||
40 | multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) | ||
41 | { | ||
42 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
43 | u32 band; | ||
44 | struct tcf_result res; | ||
45 | int err; | ||
46 | |||
47 | *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; | ||
48 | err = tc_classify(skb, q->filter_list, &res); | ||
49 | #ifdef CONFIG_NET_CLS_ACT | ||
50 | switch (err) { | ||
51 | case TC_ACT_STOLEN: | ||
52 | case TC_ACT_QUEUED: | ||
53 | *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; | ||
54 | case TC_ACT_SHOT: | ||
55 | return NULL; | ||
56 | } | ||
57 | #endif | ||
58 | band = skb_get_queue_mapping(skb); | ||
59 | |||
60 | if (band >= q->bands) | ||
61 | return q->queues[0]; | ||
62 | |||
63 | return q->queues[band]; | ||
64 | } | ||
65 | |||
66 | static int | ||
67 | multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch) | ||
68 | { | ||
69 | struct Qdisc *qdisc; | ||
70 | int ret; | ||
71 | |||
72 | qdisc = multiq_classify(skb, sch, &ret); | ||
73 | #ifdef CONFIG_NET_CLS_ACT | ||
74 | if (qdisc == NULL) { | ||
75 | |||
76 | if (ret & __NET_XMIT_BYPASS) | ||
77 | sch->qstats.drops++; | ||
78 | kfree_skb(skb); | ||
79 | return ret; | ||
80 | } | ||
81 | #endif | ||
82 | |||
83 | ret = qdisc_enqueue(skb, qdisc); | ||
84 | if (ret == NET_XMIT_SUCCESS) { | ||
85 | sch->bstats.bytes += qdisc_pkt_len(skb); | ||
86 | sch->bstats.packets++; | ||
87 | sch->q.qlen++; | ||
88 | return NET_XMIT_SUCCESS; | ||
89 | } | ||
90 | if (net_xmit_drop_count(ret)) | ||
91 | sch->qstats.drops++; | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | |||
96 | static int | ||
97 | multiq_requeue(struct sk_buff *skb, struct Qdisc *sch) | ||
98 | { | ||
99 | struct Qdisc *qdisc; | ||
100 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
101 | int ret; | ||
102 | |||
103 | qdisc = multiq_classify(skb, sch, &ret); | ||
104 | #ifdef CONFIG_NET_CLS_ACT | ||
105 | if (qdisc == NULL) { | ||
106 | if (ret & __NET_XMIT_BYPASS) | ||
107 | sch->qstats.drops++; | ||
108 | kfree_skb(skb); | ||
109 | return ret; | ||
110 | } | ||
111 | #endif | ||
112 | |||
113 | ret = qdisc->ops->requeue(skb, qdisc); | ||
114 | if (ret == NET_XMIT_SUCCESS) { | ||
115 | sch->q.qlen++; | ||
116 | sch->qstats.requeues++; | ||
117 | if (q->curband) | ||
118 | q->curband--; | ||
119 | else | ||
120 | q->curband = q->bands - 1; | ||
121 | return NET_XMIT_SUCCESS; | ||
122 | } | ||
123 | if (net_xmit_drop_count(ret)) | ||
124 | sch->qstats.drops++; | ||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | |||
129 | static struct sk_buff *multiq_dequeue(struct Qdisc *sch) | ||
130 | { | ||
131 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
132 | struct Qdisc *qdisc; | ||
133 | struct sk_buff *skb; | ||
134 | int band; | ||
135 | |||
136 | for (band = 0; band < q->bands; band++) { | ||
137 | /* cycle through bands to ensure fairness */ | ||
138 | q->curband++; | ||
139 | if (q->curband >= q->bands) | ||
140 | q->curband = 0; | ||
141 | |||
142 | /* Check that target subqueue is available before | ||
143 | * pulling an skb to avoid excessive requeues | ||
144 | */ | ||
145 | if (!__netif_subqueue_stopped(qdisc_dev(sch), q->curband)) { | ||
146 | qdisc = q->queues[q->curband]; | ||
147 | skb = qdisc->dequeue(qdisc); | ||
148 | if (skb) { | ||
149 | sch->q.qlen--; | ||
150 | return skb; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | return NULL; | ||
155 | |||
156 | } | ||
157 | |||
158 | static unsigned int multiq_drop(struct Qdisc *sch) | ||
159 | { | ||
160 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
161 | int band; | ||
162 | unsigned int len; | ||
163 | struct Qdisc *qdisc; | ||
164 | |||
165 | for (band = q->bands-1; band >= 0; band--) { | ||
166 | qdisc = q->queues[band]; | ||
167 | if (qdisc->ops->drop) { | ||
168 | len = qdisc->ops->drop(qdisc); | ||
169 | if (len != 0) { | ||
170 | sch->q.qlen--; | ||
171 | return len; | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | |||
179 | static void | ||
180 | multiq_reset(struct Qdisc *sch) | ||
181 | { | ||
182 | u16 band; | ||
183 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
184 | |||
185 | for (band = 0; band < q->bands; band++) | ||
186 | qdisc_reset(q->queues[band]); | ||
187 | sch->q.qlen = 0; | ||
188 | q->curband = 0; | ||
189 | } | ||
190 | |||
191 | static void | ||
192 | multiq_destroy(struct Qdisc *sch) | ||
193 | { | ||
194 | int band; | ||
195 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
196 | |||
197 | tcf_destroy_chain(&q->filter_list); | ||
198 | for (band = 0; band < q->bands; band++) | ||
199 | qdisc_destroy(q->queues[band]); | ||
200 | |||
201 | kfree(q->queues); | ||
202 | } | ||
203 | |||
204 | static int multiq_tune(struct Qdisc *sch, struct nlattr *opt) | ||
205 | { | ||
206 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
207 | struct tc_multiq_qopt *qopt; | ||
208 | int i; | ||
209 | |||
210 | if (!netif_is_multiqueue(qdisc_dev(sch))) | ||
211 | return -EINVAL; | ||
212 | if (nla_len(opt) < sizeof(*qopt)) | ||
213 | return -EINVAL; | ||
214 | |||
215 | qopt = nla_data(opt); | ||
216 | |||
217 | qopt->bands = qdisc_dev(sch)->real_num_tx_queues; | ||
218 | |||
219 | sch_tree_lock(sch); | ||
220 | q->bands = qopt->bands; | ||
221 | for (i = q->bands; i < q->max_bands; i++) { | ||
222 | if (q->queues[i] != &noop_qdisc) { | ||
223 | struct Qdisc *child = xchg(&q->queues[i], &noop_qdisc); | ||
224 | qdisc_tree_decrease_qlen(child, child->q.qlen); | ||
225 | qdisc_destroy(child); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | sch_tree_unlock(sch); | ||
230 | |||
231 | for (i = 0; i < q->bands; i++) { | ||
232 | if (q->queues[i] == &noop_qdisc) { | ||
233 | struct Qdisc *child; | ||
234 | child = qdisc_create_dflt(qdisc_dev(sch), | ||
235 | sch->dev_queue, | ||
236 | &pfifo_qdisc_ops, | ||
237 | TC_H_MAKE(sch->handle, | ||
238 | i + 1)); | ||
239 | if (child) { | ||
240 | sch_tree_lock(sch); | ||
241 | child = xchg(&q->queues[i], child); | ||
242 | |||
243 | if (child != &noop_qdisc) { | ||
244 | qdisc_tree_decrease_qlen(child, | ||
245 | child->q.qlen); | ||
246 | qdisc_destroy(child); | ||
247 | } | ||
248 | sch_tree_unlock(sch); | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int multiq_init(struct Qdisc *sch, struct nlattr *opt) | ||
256 | { | ||
257 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
258 | int i, err; | ||
259 | |||
260 | q->queues = NULL; | ||
261 | |||
262 | if (opt == NULL) | ||
263 | return -EINVAL; | ||
264 | |||
265 | q->max_bands = qdisc_dev(sch)->num_tx_queues; | ||
266 | |||
267 | q->queues = kcalloc(q->max_bands, sizeof(struct Qdisc *), GFP_KERNEL); | ||
268 | if (!q->queues) | ||
269 | return -ENOBUFS; | ||
270 | for (i = 0; i < q->max_bands; i++) | ||
271 | q->queues[i] = &noop_qdisc; | ||
272 | |||
273 | err = multiq_tune(sch,opt); | ||
274 | |||
275 | if (err) | ||
276 | kfree(q->queues); | ||
277 | |||
278 | return err; | ||
279 | } | ||
280 | |||
281 | static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb) | ||
282 | { | ||
283 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
284 | unsigned char *b = skb_tail_pointer(skb); | ||
285 | struct tc_multiq_qopt opt; | ||
286 | |||
287 | opt.bands = q->bands; | ||
288 | opt.max_bands = q->max_bands; | ||
289 | |||
290 | NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); | ||
291 | |||
292 | return skb->len; | ||
293 | |||
294 | nla_put_failure: | ||
295 | nlmsg_trim(skb, b); | ||
296 | return -1; | ||
297 | } | ||
298 | |||
299 | static int multiq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, | ||
300 | struct Qdisc **old) | ||
301 | { | ||
302 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
303 | unsigned long band = arg - 1; | ||
304 | |||
305 | if (band >= q->bands) | ||
306 | return -EINVAL; | ||
307 | |||
308 | if (new == NULL) | ||
309 | new = &noop_qdisc; | ||
310 | |||
311 | sch_tree_lock(sch); | ||
312 | *old = q->queues[band]; | ||
313 | q->queues[band] = new; | ||
314 | qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); | ||
315 | qdisc_reset(*old); | ||
316 | sch_tree_unlock(sch); | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static struct Qdisc * | ||
322 | multiq_leaf(struct Qdisc *sch, unsigned long arg) | ||
323 | { | ||
324 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
325 | unsigned long band = arg - 1; | ||
326 | |||
327 | if (band >= q->bands) | ||
328 | return NULL; | ||
329 | |||
330 | return q->queues[band]; | ||
331 | } | ||
332 | |||
333 | static unsigned long multiq_get(struct Qdisc *sch, u32 classid) | ||
334 | { | ||
335 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
336 | unsigned long band = TC_H_MIN(classid); | ||
337 | |||
338 | if (band - 1 >= q->bands) | ||
339 | return 0; | ||
340 | return band; | ||
341 | } | ||
342 | |||
343 | static unsigned long multiq_bind(struct Qdisc *sch, unsigned long parent, | ||
344 | u32 classid) | ||
345 | { | ||
346 | return multiq_get(sch, classid); | ||
347 | } | ||
348 | |||
349 | |||
350 | static void multiq_put(struct Qdisc *q, unsigned long cl) | ||
351 | { | ||
352 | return; | ||
353 | } | ||
354 | |||
355 | static int multiq_change(struct Qdisc *sch, u32 handle, u32 parent, | ||
356 | struct nlattr **tca, unsigned long *arg) | ||
357 | { | ||
358 | unsigned long cl = *arg; | ||
359 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
360 | |||
361 | if (cl - 1 > q->bands) | ||
362 | return -ENOENT; | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int multiq_delete(struct Qdisc *sch, unsigned long cl) | ||
367 | { | ||
368 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
369 | if (cl - 1 > q->bands) | ||
370 | return -ENOENT; | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | |||
375 | static int multiq_dump_class(struct Qdisc *sch, unsigned long cl, | ||
376 | struct sk_buff *skb, struct tcmsg *tcm) | ||
377 | { | ||
378 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
379 | |||
380 | if (cl - 1 > q->bands) | ||
381 | return -ENOENT; | ||
382 | tcm->tcm_handle |= TC_H_MIN(cl); | ||
383 | if (q->queues[cl-1]) | ||
384 | tcm->tcm_info = q->queues[cl-1]->handle; | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl, | ||
389 | struct gnet_dump *d) | ||
390 | { | ||
391 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
392 | struct Qdisc *cl_q; | ||
393 | |||
394 | cl_q = q->queues[cl - 1]; | ||
395 | if (gnet_stats_copy_basic(d, &cl_q->bstats) < 0 || | ||
396 | gnet_stats_copy_queue(d, &cl_q->qstats) < 0) | ||
397 | return -1; | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static void multiq_walk(struct Qdisc *sch, struct qdisc_walker *arg) | ||
403 | { | ||
404 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
405 | int band; | ||
406 | |||
407 | if (arg->stop) | ||
408 | return; | ||
409 | |||
410 | for (band = 0; band < q->bands; band++) { | ||
411 | if (arg->count < arg->skip) { | ||
412 | arg->count++; | ||
413 | continue; | ||
414 | } | ||
415 | if (arg->fn(sch, band+1, arg) < 0) { | ||
416 | arg->stop = 1; | ||
417 | break; | ||
418 | } | ||
419 | arg->count++; | ||
420 | } | ||
421 | } | ||
422 | |||
423 | static struct tcf_proto **multiq_find_tcf(struct Qdisc *sch, unsigned long cl) | ||
424 | { | ||
425 | struct multiq_sched_data *q = qdisc_priv(sch); | ||
426 | |||
427 | if (cl) | ||
428 | return NULL; | ||
429 | return &q->filter_list; | ||
430 | } | ||
431 | |||
432 | static const struct Qdisc_class_ops multiq_class_ops = { | ||
433 | .graft = multiq_graft, | ||
434 | .leaf = multiq_leaf, | ||
435 | .get = multiq_get, | ||
436 | .put = multiq_put, | ||
437 | .change = multiq_change, | ||
438 | .delete = multiq_delete, | ||
439 | .walk = multiq_walk, | ||
440 | .tcf_chain = multiq_find_tcf, | ||
441 | .bind_tcf = multiq_bind, | ||
442 | .unbind_tcf = multiq_put, | ||
443 | .dump = multiq_dump_class, | ||
444 | .dump_stats = multiq_dump_class_stats, | ||
445 | }; | ||
446 | |||
447 | static struct Qdisc_ops multiq_qdisc_ops __read_mostly = { | ||
448 | .next = NULL, | ||
449 | .cl_ops = &multiq_class_ops, | ||
450 | .id = "multiq", | ||
451 | .priv_size = sizeof(struct multiq_sched_data), | ||
452 | .enqueue = multiq_enqueue, | ||
453 | .dequeue = multiq_dequeue, | ||
454 | .requeue = multiq_requeue, | ||
455 | .drop = multiq_drop, | ||
456 | .init = multiq_init, | ||
457 | .reset = multiq_reset, | ||
458 | .destroy = multiq_destroy, | ||
459 | .change = multiq_tune, | ||
460 | .dump = multiq_dump, | ||
461 | .owner = THIS_MODULE, | ||
462 | }; | ||
463 | |||
464 | static int __init multiq_module_init(void) | ||
465 | { | ||
466 | return register_qdisc(&multiq_qdisc_ops); | ||
467 | } | ||
468 | |||
469 | static void __exit multiq_module_exit(void) | ||
470 | { | ||
471 | unregister_qdisc(&multiq_qdisc_ops); | ||
472 | } | ||
473 | |||
474 | module_init(multiq_module_init) | ||
475 | module_exit(multiq_module_exit) | ||
476 | |||
477 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 3781e55046d0..a11959908d9a 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c | |||
@@ -388,6 +388,20 @@ static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = { | |||
388 | [TCA_NETEM_CORRUPT] = { .len = sizeof(struct tc_netem_corrupt) }, | 388 | [TCA_NETEM_CORRUPT] = { .len = sizeof(struct tc_netem_corrupt) }, |
389 | }; | 389 | }; |
390 | 390 | ||
391 | static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, | ||
392 | const struct nla_policy *policy, int len) | ||
393 | { | ||
394 | int nested_len = nla_len(nla) - NLA_ALIGN(len); | ||
395 | |||
396 | if (nested_len < 0) | ||
397 | return -EINVAL; | ||
398 | if (nested_len >= nla_attr_size(0)) | ||
399 | return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), | ||
400 | nested_len, policy); | ||
401 | memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
391 | /* Parse netlink message to set options */ | 405 | /* Parse netlink message to set options */ |
392 | static int netem_change(struct Qdisc *sch, struct nlattr *opt) | 406 | static int netem_change(struct Qdisc *sch, struct nlattr *opt) |
393 | { | 407 | { |
@@ -399,8 +413,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt) | |||
399 | if (opt == NULL) | 413 | if (opt == NULL) |
400 | return -EINVAL; | 414 | return -EINVAL; |
401 | 415 | ||
402 | ret = nla_parse_nested_compat(tb, TCA_NETEM_MAX, opt, netem_policy, | 416 | qopt = nla_data(opt); |
403 | qopt, sizeof(*qopt)); | 417 | ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt)); |
404 | if (ret < 0) | 418 | if (ret < 0) |
405 | return ret; | 419 | return ret; |
406 | 420 | ||
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index a6697c686c7f..504a78cdb718 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c | |||
@@ -254,16 +254,12 @@ static int prio_dump(struct Qdisc *sch, struct sk_buff *skb) | |||
254 | { | 254 | { |
255 | struct prio_sched_data *q = qdisc_priv(sch); | 255 | struct prio_sched_data *q = qdisc_priv(sch); |
256 | unsigned char *b = skb_tail_pointer(skb); | 256 | unsigned char *b = skb_tail_pointer(skb); |
257 | struct nlattr *nest; | ||
258 | struct tc_prio_qopt opt; | 257 | struct tc_prio_qopt opt; |
259 | 258 | ||
260 | opt.bands = q->bands; | 259 | opt.bands = q->bands; |
261 | memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX+1); | 260 | memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX+1); |
262 | 261 | ||
263 | nest = nla_nest_compat_start(skb, TCA_OPTIONS, sizeof(opt), &opt); | 262 | NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); |
264 | if (nest == NULL) | ||
265 | goto nla_put_failure; | ||
266 | nla_nest_compat_end(skb, nest); | ||
267 | 263 | ||
268 | return skb->len; | 264 | return skb->len; |
269 | 265 | ||
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 6e041d10dbdb..fe1508ef0d3d 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c | |||
@@ -119,7 +119,7 @@ static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb) | |||
119 | u32 h, h2; | 119 | u32 h, h2; |
120 | 120 | ||
121 | switch (skb->protocol) { | 121 | switch (skb->protocol) { |
122 | case __constant_htons(ETH_P_IP): | 122 | case htons(ETH_P_IP): |
123 | { | 123 | { |
124 | const struct iphdr *iph = ip_hdr(skb); | 124 | const struct iphdr *iph = ip_hdr(skb); |
125 | h = iph->daddr; | 125 | h = iph->daddr; |
@@ -134,7 +134,7 @@ static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb) | |||
134 | h2 ^= *(((u32*)iph) + iph->ihl); | 134 | h2 ^= *(((u32*)iph) + iph->ihl); |
135 | break; | 135 | break; |
136 | } | 136 | } |
137 | case __constant_htons(ETH_P_IPV6): | 137 | case htons(ETH_P_IPV6): |
138 | { | 138 | { |
139 | struct ipv6hdr *iph = ipv6_hdr(skb); | 139 | struct ipv6hdr *iph = ipv6_hdr(skb); |
140 | h = iph->daddr.s6_addr32[3]; | 140 | h = iph->daddr.s6_addr32[3]; |