]>
Commit | Line | Data |
---|---|---|
9d12e591 | 1 | diff -uNrbB v24-org/include/linux/pkt_sched.h v24-new/include/linux/pkt_sched.h |
2 | --- v24-org/include/linux/pkt_sched.h Tue Apr 28 20:10:10 1998 | |
3 | +++ v24-new/include/linux/pkt_sched.h Sun Sep 9 14:34:14 2001 | |
4 | @@ -274,4 +274,116 @@ | |
5 | ||
6 | #define TCA_CBQ_MAX TCA_CBQ_POLICE | |
7 | ||
8 | +/* WRR section */ | |
9 | + | |
10 | +/* Other includes */ | |
11 | +#include <linux/if_ether.h> | |
12 | + | |
13 | +// A sub weight and of a class | |
14 | +// All numbers are represented as parts of (2^64-1). | |
15 | +struct tc_wrr_class_weight { | |
16 | + __u64 val; // Current value (0 is not valid) | |
17 | + __u64 decr; // Value pr bytes (2^64-1 is not valid) | |
18 | + __u64 incr; // Value pr seconds (2^64-1 is not valid) | |
19 | + __u64 min; // Minimal value (0 is not valid) | |
20 | + __u64 max; // Minimal value (0 is not valid) | |
21 | + | |
22 | + // The time where the above information was correct: | |
23 | + time_t tim; | |
24 | +}; | |
25 | + | |
26 | +// Pakcet send when modifying a class: | |
27 | +struct tc_wrr_class_modf { | |
28 | + // Not-valid values are ignored. | |
29 | + struct tc_wrr_class_weight weight1; | |
30 | + struct tc_wrr_class_weight weight2; | |
31 | +}; | |
32 | + | |
33 | +// Packet returned when quering a class: | |
34 | +struct tc_wrr_class_stats { | |
35 | + char used; // If this is false the information below is invalid | |
36 | + | |
37 | + struct tc_wrr_class_modf class_modf; | |
38 | + | |
39 | + unsigned char addr[ETH_ALEN]; | |
40 | + char usemac; // True if addr is a MAC address, else it is an IP address | |
41 | + // (this value is only for convience, it is always the same | |
42 | + // value as in the qdisc) | |
43 | + int heappos; // Current heap position or 0 if not in heap | |
44 | + __u64 penal_ls; // Penalty value in heap (ls) | |
45 | + __u64 penal_ms; // Penalty value in heap (ms) | |
46 | +}; | |
47 | + | |
48 | +// Qdisc-wide penalty information (boolean values - 2 not valid) | |
49 | +struct tc_wrr_qdisc_weight { | |
50 | + char weight_mode; // 0=No automatic change to weight | |
51 | + // 1=Decrease normally | |
52 | + // 2=Also multiply with number of machines | |
53 | + // 3=Instead multiply with priority divided | |
54 | + // with priority of the other. | |
55 | + // -1=no change | |
56 | +}; | |
57 | + | |
58 | +// Packet send when modifing a qdisc: | |
59 | +struct tc_wrr_qdisc_modf { | |
60 | + // Not-valid values are ignored: | |
61 | + struct tc_wrr_qdisc_weight weight1; | |
62 | + struct tc_wrr_qdisc_weight weight2; | |
63 | +}; | |
64 | + | |
65 | +// Packet send when creating a qdisc: | |
66 | +struct tc_wrr_qdisc_crt { | |
67 | + struct tc_wrr_qdisc_modf qdisc_modf; | |
68 | + | |
69 | + char srcaddr; // 1=lookup source, 0=lookup destination | |
70 | + char usemac; // 1=Classify on MAC addresses, 0=classify on IP | |
71 | + char usemasq; // 1=Classify based on masqgrading - only valid | |
72 | + // if usemac is zero | |
73 | + int bands_max; // Maximal number of bands (i.e.: classes) | |
74 | + int proxy_maxconn; // If differnt from 0 then we support proxy remapping | |
75 | + // of packets. And this is the number of maximal | |
76 | + // concurrent proxy connections. | |
77 | +}; | |
78 | + | |
79 | +// Packet returned when quering a qdisc: | |
80 | +struct tc_wrr_qdisc_stats { | |
81 | + struct tc_wrr_qdisc_crt qdisc_crt; | |
82 | + int proxy_curconn; | |
83 | + int nodes_in_heap; // Current number of bands wanting to send something | |
84 | + int bands_cur; // Current number of bands used (i.e.: MAC/IP addresses seen) | |
85 | + int bands_reused; // Number of times this band has been reused. | |
86 | + int packets_requed; // Number of times packets have been requeued. | |
87 | + __u64 priosum; // Sum of priorities in heap where 1 is 2^32 | |
88 | +}; | |
89 | + | |
90 | +struct tc_wrr_qdisc_modf_std { | |
91 | + // This indicates which of the tc_wrr_qdisc_modf structers this is: | |
92 | + char proxy; // 0=This struct | |
93 | + | |
94 | + // Should we also change a class? | |
95 | + char change_class; | |
96 | + | |
97 | + // Only valid if change_class is false | |
98 | + struct tc_wrr_qdisc_modf qdisc_modf; | |
99 | + | |
100 | + // Only valid if change_class is true: | |
101 | + unsigned char addr[ETH_ALEN]; // Class to change (non-used bytes should be 0) | |
102 | + struct tc_wrr_class_modf class_modf; // The change | |
103 | +}; | |
104 | + | |
105 | +// Used for proxyrempping: | |
106 | +struct tc_wrr_qdisc_modf_proxy { | |
107 | + // This indicates which of the tc_wrr_qdisc_modf structers this is: | |
108 | + char proxy; // 1=This struct | |
109 | + | |
110 | + // This is 1 if the proxyremap information should be reset | |
111 | + char reset; | |
112 | + | |
113 | + // changec is the number of elements in changes. | |
114 | + int changec; | |
115 | + | |
116 | + // This is an array of type ProxyRemapBlock: | |
117 | + long changes[0]; | |
118 | +}; | |
119 | + | |
120 | #endif | |
121 | diff -uNrbB v24-org/net/sched/Config.in v24-new/net/sched/Config.in | |
122 | --- v24-org/net/sched/Config.in Sat Jan 15 04:18:53 2000 | |
123 | +++ v24-new/net/sched/Config.in Wed Jan 2 09:39:30 2002 | |
124 | @@ -11,6 +11,7 @@ | |
125 | bool ' ATM pseudo-scheduler' CONFIG_NET_SCH_ATM | |
126 | fi | |
127 | tristate ' The simplest PRIO pseudoscheduler' CONFIG_NET_SCH_PRIO | |
128 | +tristate ' WRR packet scheduler' CONFIG_NET_SCH_WRR | |
129 | tristate ' RED queue' CONFIG_NET_SCH_RED | |
130 | tristate ' SFQ queue' CONFIG_NET_SCH_SFQ | |
131 | tristate ' TEQL queue' CONFIG_NET_SCH_TEQL | |
132 | diff -uNrbB v24-org/net/sched/Makefile v24-new/net/sched/Makefile | |
133 | --- v24-org/net/sched/Makefile Wed Mar 7 07:44:15 2001 | |
134 | +++ v24-new/net/sched/Makefile Wed May 30 13:28:41 2001 | |
135 | @@ -13,6 +13,7 @@ | |
136 | obj-$(CONFIG_NET_CLS_POLICE) += police.o | |
137 | obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o | |
138 | obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o | |
139 | +obj-$(CONFIG_NET_SCH_WRR) += sch_wrr.o | |
140 | obj-$(CONFIG_NET_SCH_CSZ) += sch_csz.o | |
141 | obj-$(CONFIG_NET_SCH_HPFQ) += sch_hpfq.o | |
142 | obj-$(CONFIG_NET_SCH_HFSC) += sch_hfsc.o | |
143 | diff -uNrbB v24-org/net/sched/proxydict.c v24-new/net/sched/proxydict.c | |
144 | --- v24-org/net/sched/proxydict.c Thu Jan 1 01:00:00 1970 | |
145 | +++ v24-new/net/sched/proxydict.c Thu Mar 1 20:33:02 2001 | |
146 | @@ -0,0 +1,153 @@ | |
147 | +#ifndef __KERNEL__ | |
148 | +#include <string.h> | |
149 | +#include <netinet/in.h> | |
150 | +#endif | |
151 | + | |
152 | +#include "proxyremap.h" | |
153 | +#include "proxydict.h" | |
154 | + | |
155 | + | |
156 | +/*-------------------------------------------------------------------------- | |
157 | +Implementation. | |
158 | +*/ | |
159 | + | |
160 | +// Hash function | |
161 | +#define hash_fnc(m,server,port,proto) \ | |
162 | + (((proto)*7+(server)*13+(port)*5)%m->hash_size) | |
163 | + | |
164 | +// Size of hash table given maximal number of connections: | |
165 | +#define hash_size_max_con(max_con) (2*(max_con)) | |
166 | + | |
167 | +// The memory area we maintain: | |
168 | +typedef struct { | |
169 | + int hash_size; | |
170 | + int max_con; | |
171 | + int cur_con; | |
172 | + | |
173 | + int free_first; | |
174 | + | |
175 | + // Then we have: | |
176 | + // int hash_table[hash_size]; | |
177 | + // int next[max_con]; | |
178 | + // ProxyRemapBlock info[max_con]; | |
179 | + // | |
180 | + // The idea is the following: | |
181 | + // Given a connection we map it by hash_fnc into hash_table. This gives an | |
182 | + // index in next which contains a -1 terminated linked list of connections | |
183 | + // mapping to that hash value. | |
184 | + // | |
185 | + // The entries in next not allocated is also in linked list where | |
186 | + // the first free index is free_first. | |
187 | +} memory; | |
188 | + | |
189 | +#define Memory(m) ((memory*)m) | |
190 | +#define Hash_table(m) ((int*)(((char*)m)+sizeof(memory))) | |
191 | +#define Next(m) ((int*)(((char*)m)+sizeof(memory)+ \ | |
192 | + sizeof(int)*((memory*)m)->hash_size)) | |
193 | +#define Info(m) ((ProxyRemapBlock*)(((char*)m)+ \ | |
194 | + sizeof(memory)+ \ | |
195 | + sizeof(int)*((memory*)m)->hash_size+\ | |
196 | + sizeof(int)*((memory*)m)->max_con \ | |
197 | + )) | |
198 | + | |
199 | +int proxyGetMemSize(int max_con) { | |
200 | + return sizeof(memory)+ | |
201 | + sizeof(int)*hash_size_max_con(max_con)+ | |
202 | + sizeof(int)*max_con+ | |
203 | + sizeof(ProxyRemapBlock)*max_con; | |
204 | +} | |
205 | + | |
206 | +void proxyInitMem(void* data, int max_con) { | |
207 | + // Init m: | |
208 | + memory* m=Memory(data); | |
209 | + m->max_con=max_con; | |
210 | + m->cur_con=0; | |
211 | + m->hash_size=hash_size_max_con(max_con); | |
212 | + | |
213 | + { | |
214 | + // Get pointers: | |
215 | + int* hash_table=Hash_table(data); | |
216 | + int* next=Next(data); | |
217 | + int i; | |
218 | + | |
219 | + // Init the hash table: | |
220 | + for(i=0; i<m->hash_size; i++) hash_table[i]=-1; | |
221 | + | |
222 | + // Init the free-list | |
223 | + for(i=0; i<m->max_con; i++) next[i]=i+1; | |
224 | + m->free_first=0; | |
225 | + } | |
226 | +} | |
227 | + | |
228 | +int proxyGetCurConn(void* data) { | |
229 | + return Memory(data)->cur_con; | |
230 | +} | |
231 | + | |
232 | +int proxyGetMaxConn(void* data) { | |
233 | + return Memory(data)->max_con; | |
234 | +} | |
235 | + | |
236 | +ProxyRemapBlock* proxyLookup(void* data, unsigned ipaddr, unsigned short port, char proto) { | |
237 | + memory* m=Memory(data); | |
238 | + int* hash_table=Hash_table(m); | |
239 | + int* next=Next(m); | |
240 | + ProxyRemapBlock* info=Info(m); | |
241 | + int i; | |
242 | + | |
243 | + for(i=hash_table[hash_fnc(m,ipaddr,port,proto)]; i!=-1; i=next[i]) { | |
244 | + if(info[i].proto==proto && | |
245 | + info[i].sport==port && | |
246 | + info[i].saddr==ipaddr) return &info[i]; | |
247 | + } | |
248 | + | |
249 | + return 0; | |
250 | +} | |
251 | + | |
252 | +int proxyConsumeBlock(void* data, ProxyRemapBlock* blk) { | |
253 | + memory* m=Memory(data); | |
254 | + int* hash_table=Hash_table(m); | |
255 | + int* next=Next(m); | |
256 | + ProxyRemapBlock* info=Info(m); | |
257 | + int hash=hash_fnc(m,blk->saddr,blk->sport,blk->proto); | |
258 | + int foo; | |
259 | + | |
260 | + if(blk->open) { | |
261 | + if(m->cur_con == m->max_con) return -1; | |
262 | + | |
263 | + // Insert the block at a free entry: | |
264 | + info[m->free_first]=*blk; | |
265 | + m->cur_con++; | |
266 | + | |
267 | + foo=next[m->free_first]; | |
268 | + | |
269 | + // And insert it in the hash tabel: | |
270 | + next[m->free_first]=hash_table[hash]; | |
271 | + hash_table[hash]=m->free_first; | |
272 | + m->free_first=foo; | |
273 | + } else { | |
274 | + int* toupdate; | |
275 | + | |
276 | + // Find the block | |
277 | + for(toupdate=&hash_table[hash]; | |
278 | + *toupdate!=-1; | |
279 | + toupdate=&next[*toupdate]) { | |
280 | + if(info[*toupdate].proto==blk->proto && | |
281 | + info[*toupdate].sport==blk->sport && | |
282 | + info[*toupdate].saddr==blk->saddr) break; | |
283 | + } | |
284 | + if(*toupdate==-1) return -1; | |
285 | + | |
286 | + foo=*toupdate; | |
287 | + | |
288 | + // Delete it from the hashing list: | |
289 | + *toupdate=next[*toupdate]; | |
290 | + | |
291 | + // And put it on the free list: | |
292 | + next[foo]=m->free_first; | |
293 | + m->free_first=foo; | |
294 | + | |
295 | + m->cur_con--; | |
296 | + } | |
297 | + | |
298 | + return 0; | |
299 | +} | |
300 | diff -uNrbB v24-org/net/sched/proxydict.h v24-new/net/sched/proxydict.h | |
301 | --- v24-org/net/sched/proxydict.h Thu Jan 1 01:00:00 1970 | |
302 | +++ v24-new/net/sched/proxydict.h Tue Feb 13 22:47:00 2001 | |
303 | @@ -0,0 +1,32 @@ | |
304 | +#ifdef __cplusplus | |
305 | +extern "C" { | |
306 | +#endif | |
307 | + | |
308 | +/*-------------------------------------------------------------------------- | |
309 | +This is common code for for handling the tabels containing information about | |
310 | +which proxyserver connections are associated with which machines.. | |
311 | +*/ | |
312 | + | |
313 | +// Returns the number of bytes that should be available in the area | |
314 | +// maintained by this module given the maximal number of concurrent | |
315 | +// connections. | |
316 | +int proxyGetMemSize(int max_connections); | |
317 | + | |
318 | +// Initializes a memory area to use. There must be as many bytes | |
319 | +// available as returned by getMemSize. | |
320 | +void proxyInitMem(void* data, int max_connections); | |
321 | + | |
322 | +// Queries: | |
323 | +int proxyGetCurConn(void* data); // Returns current number of connections | |
324 | +int proxyMaxCurConn(void* data); // Returns maximal number of connections | |
325 | + | |
326 | +// This is called to open and close conenctions. Returns -1 if | |
327 | +// a protocol error occores (i.e.: If it is discovered) | |
328 | +int proxyConsumeBlock(void* data, ProxyRemapBlock*); | |
329 | + | |
330 | +// Returns the RemapBlock associated with this connection or 0: | |
331 | +ProxyRemapBlock* proxyLookup(void* data, unsigned ipaddr, unsigned short port, char proto); | |
332 | + | |
333 | +#ifdef __cplusplus | |
334 | +} | |
335 | +#endif | |
336 | diff -uNrbB v24-org/net/sched/proxyremap.h v24-new/net/sched/proxyremap.h | |
337 | --- v24-org/net/sched/proxyremap.h Thu Jan 1 01:00:00 1970 | |
338 | +++ v24-new/net/sched/proxyremap.h Thu May 17 10:54:11 2001 | |
339 | @@ -0,0 +1,33 @@ | |
340 | +#ifndef PROXYREMAP_H | |
341 | +#define PROXYREMAP_H | |
342 | + | |
343 | +// This describes the information that is written in proxyremap.log and which | |
344 | +// are used in the communication between proxyremapserver and proxyremapclient. | |
345 | +// Everything is in network order. | |
346 | + | |
347 | +// First this header is send: | |
348 | +#define PROXY_WELCOME_LINE "ProxyRemap 1.02. This is a binary protocol.\r\n" | |
349 | + | |
350 | +// Then this block is send every time a connection is opened or closed. | |
351 | +// Note how it is alligned to use small space usage - arrays of this | |
352 | +// structure are saved in many places. | |
353 | +typedef struct { | |
354 | + // Server endpoint of connection: | |
355 | + unsigned saddr; | |
356 | + unsigned short sport; | |
357 | + | |
358 | + // IP protocol for this connection (typically udp or tcp): | |
359 | + unsigned char proto; | |
360 | + | |
361 | + // Is the connection opened or closed? | |
362 | + unsigned char open; | |
363 | + | |
364 | + // Client the packets should be accounted to: | |
365 | + unsigned caddr; | |
366 | + unsigned char macaddr[6]; // Might be 0. | |
367 | + | |
368 | + // An informal two-charecter code from the proxyserver. Used for debugging. | |
369 | + char proxyinfo[2]; | |
370 | +} ProxyRemapBlock; | |
371 | + | |
372 | +#endif | |
373 | diff -uNrbB v24-org/net/sched/sch_wrr.c v24-new/net/sched/sch_wrr.c | |
374 | --- v24-org/net/sched/sch_wrr.c Thu Jan 1 01:00:00 1970 | |
375 | +++ v24-new/net/sched/sch_wrr.c Mon Apr 1 17:07:51 2002 | |
376 | @@ -0,0 +1,1357 @@ | |
377 | +/*----------------------------------------------------------------------------- | |
378 | +Weighted Round Robin scheduler. | |
379 | + | |
380 | +Written by Christian Worm Mortensen, cworm@it-c.dk. | |
381 | + | |
382 | +Introduction | |
383 | +============ | |
384 | +This module implements a weighted round robin queue with build-in classifier. | |
385 | +The classifier currently map each MAC or IP address (configurable either MAC | |
386 | +or IP and either source or destination) to different classes. Each such class | |
387 | +is called a band. Whan using MAC addresses only bridged packets can be | |
388 | +classified other packets go to a default MAC address. | |
389 | + | |
390 | +Each band has a weight value, where 0<weight<=1. The bandwidth each band | |
391 | +get is proportional to the weight as can be deduced from the next section. | |
392 | + | |
393 | + | |
394 | +The queue | |
395 | +========= | |
396 | +Each band has a penalty value. Bands having something to sent are kept in | |
397 | +a heap according to this value. The band with the lowest penalty value | |
398 | +is in the root of the heap. The penalty value is a 128 bit number. Initially | |
399 | +no bands are in the heap. | |
400 | + | |
401 | +Two global 64 bit values counter_low_penal and couter_high_penal are initialized | |
402 | +to 0 and to 2^63 respectively. | |
403 | + | |
404 | +Enqueing: | |
405 | + The packet is inserted in the queue for the band it belongs to. If the band | |
406 | + is not in the heap it is inserted into it. In this case, the upper 64 bits | |
407 | + of its penalty value is set to the same as for the root-band of the heap. | |
408 | + If the heap is empty 0 is used. The lower 64 bit is set to couter_low_penal | |
409 | + and couter_low_penal is incremented by 1. | |
410 | + | |
411 | +Dequing: | |
412 | + If the heap is empty we have nothing to send. | |
413 | + | |
414 | + If the root band has a non-empty queue a packet is dequeued from that. | |
415 | + The upper 64 bit of the penalty value of the band is incremented by the | |
416 | + packet size divided with the weight of the band. The lower 64 bit is set to | |
417 | + couter_high_penal and couter_high_penal is incremented by 1. | |
418 | + | |
419 | + If the root element for some reason has an empty queue it is removed from | |
420 | + the heap and we try to dequeue again. | |
421 | + | |
422 | +The effect of the heap and the upper 64 bit of the penalty values is to | |
423 | +implement a weighted round robin queue. The effect of counter_low_penal, | |
424 | +counter_high_penal and the lower 64 bit of the penalty value is primarily to | |
425 | +stabilize the queue and to give better quality of service to machines only | |
426 | +sending a packet now and then. For example machines which have a single | |
427 | +interactive connection such as telnet or simple text chatting. | |
428 | + | |
429 | + | |
430 | +Setting weight | |
431 | +============== | |
432 | +The weight value can be changed dynamically by the queue itself. The weight | |
433 | +value and how it is changed is described by the two members weight1 and | |
434 | +weight2 which has type tc_wrr_class_weight and which are in each class. And | |
435 | +by the two integer value members of the qdisc called penalfact1 and penalfact2. | |
436 | +The structure is defined as: | |
437 | + | |
438 | + struct tc_wrr_class_weight { | |
439 | + // All are represented as parts of (2^64-1). | |
440 | + __u64 val; // Current value (0 is not valid) | |
441 | + __u64 decr; // Value pr bytes (2^64-1 is not valid) | |
442 | + __u64 incr; // Value pr seconds (2^64-1 is not valid) | |
443 | + __u64 min; // Minimal value (0 is not valid) | |
444 | + __u64 max; // Minimal value (0 is not valid) | |
445 | + | |
446 | + // The time where the above information was correct: | |
447 | + time_t tim; | |
448 | + }; | |
449 | + | |
450 | +The weight value used by the dequeue operations is calculated as | |
451 | +weight1.val*weight2.val. weight1 and weight2 and handled independently and in the | |
452 | +same way as will be described now. | |
453 | + | |
454 | +Every second, the val parameter is incremented by incr. | |
455 | + | |
456 | +Every time a packet is transmitted the value is increment by decr times | |
457 | +the packet size. Depending on the value of the weight_mode parameter it | |
458 | +is also mulitplied with other numbers. This makes it possible to give | |
459 | +penalty to machines transferring much data. | |
460 | + | |
461 | +-----------------------------------------------------------------------------*/ | |
462 | + | |
463 | +#include <linux/config.h> | |
464 | +#include <linux/module.h> | |
465 | +#include <asm/uaccess.h> | |
466 | +#include <asm/system.h> | |
467 | +#include <asm/bitops.h> | |
468 | +#include <linux/types.h> | |
469 | +#include <linux/kernel.h> | |
470 | +#include <linux/sched.h> | |
471 | +#include <linux/string.h> | |
472 | +#include <linux/mm.h> | |
473 | +#include <linux/socket.h> | |
474 | +#include <linux/sockios.h> | |
475 | +#include <linux/in.h> | |
476 | +#include <linux/errno.h> | |
477 | +#include <linux/interrupt.h> | |
478 | +#include <linux/if_ether.h> | |
479 | +#include <linux/inet.h> | |
480 | +#include <linux/netdevice.h> | |
481 | +#include <linux/etherdevice.h> | |
482 | +#include <linux/notifier.h> | |
483 | +#include <net/ip.h> | |
484 | +#include <net/route.h> | |
485 | +#include <linux/skbuff.h> | |
486 | +#include <net/sock.h> | |
487 | +#include <net/pkt_sched.h> | |
488 | + | |
489 | +#include <linux/if_arp.h> | |
490 | +#include <linux/version.h> | |
491 | + | |
492 | +// Kernel depend stuff: | |
493 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) | |
494 | + #define KERNEL22 | |
495 | +#endif | |
496 | + | |
497 | +#ifdef KERNEL22 | |
498 | + #define LOCK_START start_bh_atomic(); | |
499 | + #define LOCK_END end_bh_atomic(); | |
500 | + #define ENQUEUE_SUCCESS 1 | |
501 | + #define ENQUEUE_FAIL 0 | |
502 | + #ifdef CONFIG_IP_MASQUERADE | |
503 | + #include <net/ip_masq.h> | |
504 | + #define MASQ_SUPPORT | |
505 | + #endif | |
506 | +#else | |
507 | + #define LOCK_START sch_tree_lock(sch); | |
508 | + #define LOCK_END sch_tree_unlock(sch); | |
509 | + #define ENQUEUE_SUCCESS 0 | |
510 | + #define ENQUEUE_FAIL NET_XMIT_DROP | |
511 | + #ifdef CONFIG_NETFILTER | |
512 | + #include <linux/netfilter_ipv4/ip_conntrack.h> | |
513 | + #define MASQ_SUPPORT | |
514 | + #endif | |
515 | +#endif | |
516 | + | |
517 | +#include "proxydict.c" | |
518 | + | |
519 | +// The penalty (priority) type: | |
520 | +typedef u64 penalty_base_t; | |
521 | +#define penalty_base_t_max ((penalty_base_t)-1) | |
522 | +typedef struct penalty_t { | |
523 | + penalty_base_t ms; | |
524 | + penalty_base_t ls; | |
525 | +} penalty_t; | |
526 | +#define penalty_leq(a,b) (a.ms<b.ms || (a.ms==b.ms && a.ls<=b.ls)) | |
527 | +#define penalty_le(a,b) (a.ms<b.ms || (a.ms==b.ms && a.ls<b.ls)) | |
528 | +static penalty_t penalty_max={penalty_base_t_max,penalty_base_t_max}; | |
529 | + | |
530 | +//----------------------------------------------------------------------------- | |
531 | +// A generel heap. | |
532 | + | |
533 | +struct heap; | |
534 | +struct heap_element; | |
535 | + | |
536 | +// Initializes an empty heap: | |
537 | +// he: A pointer to an unintialized heap structure identifying the heap | |
538 | +// size: Maximal number of elements the heap can contain | |
539 | +// poll: An array of size "size" used by the heap. | |
540 | +static void heap_init(struct heap* he,int size, struct heap_element* poll); | |
541 | + | |
542 | +// Each element in the heap is identified by a user-assigned id which | |
543 | +// should be a non negative integer less than the size argument | |
544 | +// given to heap_init. | |
545 | +static void heap_insert(struct heap*, int id, penalty_t); | |
546 | +static void heap_remove(struct heap*, int id); | |
547 | +static void heap_set_penalty(struct heap*, int id, penalty_t); | |
548 | + | |
549 | +// Retreviewing information: | |
550 | +static char heap_empty(struct heap*); // Heap empty? | |
551 | +static char heap_contains(struct heap*, int id); // Does heap contain | |
552 | + // the given id? | |
553 | +static int heap_root(struct heap*); // Returns the id of the root | |
554 | +static penalty_t heap_get_penalty(struct heap*, int id); // Returns penaly | |
555 | + // of root node | |
556 | + | |
557 | +//-------------------- | |
558 | +// Heap implementation | |
559 | + | |
560 | +struct heap_element { | |
561 | + penalty_t penalty; | |
562 | + int id; // The user-assigned id of this element | |
563 | + int id2idx; // Maps from user-assigned ids to indices in root_1 | |
564 | +}; | |
565 | + | |
566 | +struct heap { | |
567 | + struct heap_element* root_1; | |
568 | + int elements; | |
569 | +}; | |
570 | + | |
571 | +// Heap implementation: | |
572 | +static void heap_init(struct heap* h, int size, struct heap_element* poll) { | |
573 | + int i; | |
574 | + | |
575 | + h->elements=0; | |
576 | + h->root_1=poll-1; | |
577 | + | |
578 | + for(i=0; i<size; i++) poll[i].id2idx=0; | |
579 | +}; | |
580 | + | |
581 | +static char heap_empty(struct heap* h) { | |
582 | + return h->elements==0; | |
583 | +} | |
584 | + | |
585 | +static char heap_contains(struct heap* h, int id) { | |
586 | + return h->root_1[id+1].id2idx!=0; | |
587 | +} | |
588 | + | |
589 | +static int heap_root(struct heap* h) { | |
590 | + return h->root_1[1].id; | |
591 | +} | |
592 | + | |
593 | +static penalty_t heap_get_penalty(struct heap* h, int id) { | |
594 | + return h->root_1[ h->root_1[id+1].id2idx ].penalty; | |
595 | +} | |
596 | + | |
597 | +static void heap_penalty_changed_internal(struct heap* h,int idx); | |
598 | + | |
599 | +static void heap_set_penalty(struct heap* h, int id, penalty_t p) { | |
600 | + int idx=h->root_1[id+1].id2idx; | |
601 | + h->root_1[idx].penalty=p; | |
602 | + heap_penalty_changed_internal(h,idx); | |
603 | +} | |
604 | + | |
605 | +static void heap_insert(struct heap* h, int id, penalty_t p) { | |
606 | + // Insert at the end of the heap: | |
607 | + h->elements++; | |
608 | + h->root_1[h->elements].id=id; | |
609 | + h->root_1[h->elements].penalty=p; | |
610 | + h->root_1[id+1].id2idx=h->elements; | |
611 | + | |
612 | + // And put it in the right position: | |
613 | + heap_penalty_changed_internal(h,h->elements); | |
614 | +} | |
615 | + | |
616 | +static void heap_remove(struct heap* h, int id) { | |
617 | + int idx=h->root_1[id+1].id2idx; | |
618 | + int mvid; | |
619 | + h->root_1[id+1].id2idx=0; | |
620 | + | |
621 | + if(h->elements==idx) { h->elements--; return; } | |
622 | + | |
623 | + mvid=h->root_1[h->elements].id; | |
624 | + h->root_1[idx].id=mvid; | |
625 | + h->root_1[idx].penalty=h->root_1[h->elements].penalty; | |
626 | + h->root_1[mvid+1].id2idx=idx; | |
627 | + | |
628 | + h->elements--; | |
629 | + heap_penalty_changed_internal(h,idx); | |
630 | +} | |
631 | + | |
632 | +static void heap_swap(struct heap* h, int idx0, int idx1) { | |
633 | + penalty_t tmp_p; | |
634 | + int tmp_id; | |
635 | + int id0,id1; | |
636 | + | |
637 | + // Simple content: | |
638 | + tmp_p=h->root_1[idx0].penalty; | |
639 | + tmp_id=h->root_1[idx0].id; | |
640 | + h->root_1[idx0].penalty=h->root_1[idx1].penalty; | |
641 | + h->root_1[idx0].id=h->root_1[idx1].id; | |
642 | + h->root_1[idx1].penalty=tmp_p; | |
643 | + h->root_1[idx1].id=tmp_id; | |
644 | + | |
645 | + // Update reverse pointers: | |
646 | + id0=h->root_1[idx0].id; | |
647 | + id1=h->root_1[idx1].id; | |
648 | + h->root_1[id0+1].id2idx=idx0; | |
649 | + h->root_1[id1+1].id2idx=idx1; | |
650 | +} | |
651 | + | |
652 | +static void heap_penalty_changed_internal(struct heap* h,int cur) { | |
653 | + if(cur==1 || penalty_leq(h->root_1[cur>>1].penalty,h->root_1[cur].penalty)) { | |
654 | + // We are in heap order upwards - so we should move the element down | |
655 | + for(;;) { | |
656 | + int nxt0=cur<<1; | |
657 | + int nxt1=nxt0+1; | |
658 | + penalty_t pen_c=h->root_1[cur].penalty; | |
659 | + penalty_t pen_0=nxt0<=h->elements ? h->root_1[nxt0].penalty : penalty_max; | |
660 | + penalty_t pen_1=nxt1<=h->elements ? h->root_1[nxt1].penalty : penalty_max; | |
661 | + | |
662 | + if(penalty_le(pen_0,pen_c) && penalty_leq(pen_0,pen_1)) { | |
663 | + // Swap with child 0: | |
664 | + heap_swap(h,cur,nxt0); | |
665 | + cur=nxt0; | |
666 | + } else if(penalty_le(pen_1,pen_c)) { | |
667 | + // Swap with child 1: | |
668 | + heap_swap(h,cur,nxt1); | |
669 | + cur=nxt1; | |
670 | + } else { | |
671 | + // Heap in heap order: | |
672 | + return; | |
673 | + } | |
674 | + } | |
675 | + } else { | |
676 | + // We are not in heap order upwards (and thus we must be it downwards). | |
677 | + // We move up: | |
678 | + while(cur!=1) { // While not root | |
679 | + int nxt=cur>>1; | |
680 | + if(penalty_leq(h->root_1[nxt].penalty,h->root_1[cur].penalty)) return; | |
681 | + heap_swap(h,cur,nxt); | |
682 | + cur=nxt; | |
683 | + } | |
684 | + } | |
685 | +}; | |
686 | + | |
687 | +//----------------------------------------------------------------------------- | |
688 | +// Classification based on MAC or IP adresses. Note that of historical reason | |
689 | +// these are prefixed with mac_ since originally only MAC bases classification | |
690 | +// was supported. | |
691 | +// | |
692 | +// This code should be in a separate filter module - but it isn't. | |
693 | + | |
694 | +// Interface: | |
695 | + | |
696 | +struct mac_head; | |
697 | + | |
698 | +// Initialices/destroys the structure we maintain. | |
699 | +// Returns -1 on error | |
700 | +static int mac_init(struct mac_head*, int max_macs, char srcaddr, | |
701 | + char usemac, char usemasq, void* proxyremap); | |
702 | +static void mac_done(struct mac_head*); | |
703 | +static void mac_reset(struct mac_head*); | |
704 | + | |
705 | +// Classify a packet. Returns a number n where 0<=n<max_macs. Or -1 if | |
706 | +// the packet should be dropped. | |
707 | +static int mac_classify(struct mac_head*, struct sk_buff *skb); | |
708 | + | |
709 | +//------------- | |
710 | +// Implementation: | |
711 | + | |
712 | +struct mac_addr { | |
713 | + unsigned char addr[ETH_ALEN]; // Address of this band (last two are 0 on IP) | |
714 | + unsigned long lastused; // Last time a packet was encountered | |
715 | + int class; // Classid of this band (0<=classid<max_macs) | |
716 | +}; | |
717 | + | |
718 | +static int mac_compare(const void* a, const void* b) { | |
719 | + return memcmp(a,b,ETH_ALEN); | |
720 | +} | |
721 | + | |
722 | +struct mac_head { | |
723 | + int mac_max; // Maximal number of MAC addresses/classes allowed | |
724 | + int mac_cur; // Current number of MAC addresses/classes | |
725 | + int mac_reused; // Number of times we have reused a class with a new | |
726 | + // address. | |
727 | + u64 incr_time; | |
728 | + char srcaddr; // True if we classify on the source address of packets, | |
729 | + // else we use destination address. | |
730 | + char usemac; // If true we use mac, else we use IP | |
731 | + char usemasq; // If true we try to demasqgrade | |
732 | + struct mac_addr* macs; // Allocated mac_max elements, used max_cur | |
733 | + char* cls2mac; // Mapping from classnumbers to addresses - | |
734 | + // there is 6 bytes in each entry | |
735 | + | |
736 | + void* proxyremap; // Information on proxy remapping of data or 0 | |
737 | +}; | |
738 | + | |
739 | +// This is as the standard C library function with the same name: | |
740 | +static const void* bsearch(const void* key, const void* base, int nmemb, | |
741 | + size_t size, | |
742 | + int (*compare)(const void*, const void*)) { | |
743 | + int m_idx; | |
744 | + const void* m_ptr; | |
745 | + int i; | |
746 | + | |
747 | + if(nmemb<=0) return 0; | |
748 | + | |
749 | + m_idx=nmemb>>1; | |
750 | + m_ptr=((const char*)base)+m_idx*size; | |
751 | + | |
752 | + i=compare(key,m_ptr); | |
753 | + if(i<0) // key is less | |
754 | + return bsearch(key,base,m_idx,size,compare); | |
755 | + else if(i>0) | |
756 | + return bsearch(key,((const char*)m_ptr)+size,nmemb-m_idx-1,size,compare); | |
757 | + | |
758 | + return m_ptr; | |
759 | +} | |
760 | + | |
761 | +static int mac_init(struct mac_head* h, int max_macs, char srcaddr, | |
762 | + char usemac, char usemasq,void* proxyremap) { | |
763 | + h->mac_cur=0; | |
764 | + h->mac_reused=0; | |
765 | + h->incr_time=0; | |
766 | + h->srcaddr=srcaddr; | |
767 | + h->usemac=usemac; | |
768 | + h->usemasq=usemasq; | |
769 | + h->mac_max=max_macs; | |
770 | + h->proxyremap=proxyremap; | |
771 | + | |
772 | + h->macs=(struct mac_addr*) | |
773 | + kmalloc( sizeof(struct mac_addr)*max_macs, GFP_KERNEL); | |
774 | + h->cls2mac=(char*)kmalloc( 6*max_macs, GFP_KERNEL); | |
775 | + if(!h->macs || !h->cls2mac) { | |
776 | + if(h->macs) kfree(h->macs); | |
777 | + if(h->cls2mac) kfree(h->cls2mac); | |
778 | + return -1; | |
779 | + } | |
780 | + return 0; | |
781 | +} | |
782 | + | |
783 | +static void mac_done(struct mac_head* h) { | |
784 | + kfree(h->macs); | |
785 | + kfree(h->cls2mac); | |
786 | +} | |
787 | + | |
788 | +static void mac_reset(struct mac_head* h) { | |
789 | + h->mac_cur=0; | |
790 | + h->mac_reused=0; | |
791 | + h->incr_time=0; | |
792 | +} | |
793 | + | |
794 | +static int lookup_mac(struct mac_head* h, unsigned char* addr) { | |
795 | + int i; | |
796 | + int class; | |
797 | + | |
798 | + // First try to find the address in the table: | |
799 | + struct mac_addr* m=(struct mac_addr*) | |
800 | + bsearch(addr,h->macs,h->mac_cur,sizeof(struct mac_addr),mac_compare); | |
801 | + if(m) { | |
802 | + // Found: | |
803 | + m->lastused=h->incr_time++; | |
804 | + return m->class; | |
805 | + } | |
806 | + | |
807 | + // Okay - the MAC adress was not in table | |
808 | + if(h->mac_cur==h->mac_max) { | |
809 | + // And the table is full - delete the oldest entry: | |
810 | + | |
811 | + // Find the oldest entry: | |
812 | + int lowidx=0; | |
813 | + int i; | |
814 | + for(i=1; i<h->mac_cur; i++) | |
815 | + if(h->macs[i].lastused < h->macs[lowidx].lastused) lowidx=i; | |
816 | + | |
817 | + class=h->macs[lowidx].class; | |
818 | + | |
819 | + // And delete it: | |
820 | + memmove(&h->macs[lowidx],&h->macs[lowidx+1], | |
821 | + (h->mac_cur-lowidx-1)*sizeof(struct mac_addr)); | |
822 | + h->mac_reused++; | |
823 | + h->mac_cur--; | |
824 | + } else { | |
825 | + class=h->mac_cur; | |
826 | + } | |
827 | + | |
828 | + // The table is now not full - find the position we should put the address in: | |
829 | + for(i=0; i<h->mac_cur; i++) if(mac_compare(addr,&h->macs[i])<0) break; | |
830 | + | |
831 | + // We should insert at position i: | |
832 | + memmove(&h->macs[i+1],&h->macs[i],(h->mac_cur-i)*sizeof(struct mac_addr)); | |
833 | + m=&h->macs[i]; | |
834 | + memcpy(m->addr,addr,ETH_ALEN); | |
835 | + m->lastused=h->incr_time++; | |
836 | + m->class=class; | |
837 | + h->mac_cur++; | |
838 | + | |
839 | + // Finally update the cls2mac variabel: | |
840 | + memcpy(h->cls2mac+ETH_ALEN*class,addr,ETH_ALEN); | |
841 | + | |
842 | + return m->class; | |
843 | +} | |
844 | + | |
845 | +int valid_ip_checksum(struct iphdr* ip, int size) { | |
846 | + __u16 header_len=ip->ihl<<2; | |
847 | + __u16 c=0; | |
848 | + __u16* ipu=(u16*)ip; | |
849 | + int a; | |
850 | + | |
851 | + // We require 4 bytes in the packet since we access the port numbers: | |
852 | + if((size<header_len) || size<sizeof(struct iphdr)+4) return 0; | |
853 | + | |
854 | + for(a=0; a<(header_len>>1); a++, ipu++) { | |
855 | + if(a!=5) { // If not the checksum field | |
856 | + __u16 oldc=c; | |
857 | + c+=(*ipu); | |
858 | + if(c<oldc) c++; | |
859 | + } | |
860 | + } | |
861 | + | |
862 | + return ip->check==(__u16)~c; | |
863 | +} | |
864 | + | |
865 | +static int mac_classify(struct mac_head* head, struct sk_buff *skb) | |
866 | +{ | |
867 | + // We set this to the address we map to. In case we map to an IP | |
868 | + // address the last two entries are set to 0. | |
869 | + unsigned char addr[ETH_ALEN]; | |
870 | + | |
871 | + | |
872 | + // This is the size of the network part of the packet, I think: | |
873 | + int size=((char*)skb->data+skb->len)-((char*)skb->nh.iph); | |
874 | + | |
875 | + // Set a default value for the address: | |
876 | + memset(addr,0,ETH_ALEN); | |
877 | + | |
878 | + // Accept IP-ARP traffic with big-enough packets: | |
879 | + if(ntohs(skb->protocol)==ETH_P_ARP && | |
880 | + ntohs(skb->nh.arph->ar_pro)==ETH_P_IP) { | |
881 | + // Map all ARP trafic to a default adress to make sure | |
882 | + // it goes through | |
883 | + } else if ((ntohs(skb->protocol)==ETH_P_IP) && | |
884 | + valid_ip_checksum(skb->nh.iph,size)) { | |
885 | + // Accept IP packets which have correct checksum. | |
886 | + | |
887 | + // This is the IP header: | |
888 | + struct iphdr* iph=skb->nh.iph; | |
889 | + | |
890 | + // And this is the port numbers: | |
891 | + const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]); | |
892 | + __u16 sport=portp[0]; | |
893 | + __u16 dport=portp[1]; | |
894 | + | |
895 | + // We will set this to the IP address of the packet that should be | |
896 | + // accounted to: | |
897 | + unsigned ipaddr; | |
898 | + | |
899 | + // Used below: | |
900 | + ProxyRemapBlock* prm; | |
901 | + | |
902 | + // Set ipaddr: | |
903 | + if(head->srcaddr) | |
904 | + ipaddr=iph->saddr; | |
905 | + else | |
906 | + ipaddr=iph->daddr; | |
907 | + | |
908 | +#ifdef MASQ_SUPPORT | |
909 | + // Update ipaddr if packet is masqgraded: | |
910 | + if(head->usemasq) { | |
911 | + #ifdef KERNEL22 | |
912 | + struct ip_masq* src; | |
913 | + | |
914 | + // HACK!: | |
915 | + // ip_masq_in_get must be called for packets comming from the outside | |
916 | + // to the firewall. We have a a packet which is comming from the | |
917 | + // firewall to the outside - so we switch the parameters: | |
918 | + if((src=ip_masq_in_get( | |
919 | + iph->protocol, | |
920 | + iph->daddr,dport, | |
921 | + iph->saddr,sport))) { | |
922 | + // Use masqgraded address: | |
923 | + ipaddr=src->saddr; | |
924 | + | |
925 | + // It seems like we must put it back: | |
926 | + ip_masq_put(src); | |
927 | + } | |
928 | + #else | |
929 | + // Thanks to Rusty Russell for help with the following code: | |
930 | + enum ip_conntrack_info ctinfo; | |
931 | + struct ip_conntrack *ct; | |
932 | + ct = ip_conntrack_get(skb, &ctinfo); | |
933 | + if (ct) { | |
934 | + if(head->srcaddr) | |
935 | + ipaddr=ct->tuplehash[CTINFO2DIR(ctinfo)].tuple.src.ip; | |
936 | + else | |
937 | + ipaddr=ct->tuplehash[CTINFO2DIR(ctinfo)].tuple.dst.ip; | |
938 | + } | |
939 | + #endif | |
940 | + } | |
941 | +#endif | |
942 | + | |
943 | + // Set prm based on ipaddr: | |
944 | + prm=0; | |
945 | + if(head->proxyremap) { | |
946 | + if(head->srcaddr) { | |
947 | + prm=proxyLookup(head->proxyremap,ipaddr,sport,skb->nh.iph->protocol); | |
948 | + } else { | |
949 | + prm=proxyLookup(head->proxyremap,ipaddr,dport,skb->nh.iph->protocol); | |
950 | + } | |
951 | + } | |
952 | + | |
953 | + // And finally set addr to the address: | |
954 | + memset(addr,0,ETH_ALEN); | |
955 | + if(prm) { | |
956 | + // This package should be remapped: | |
957 | + if(head->usemac) | |
958 | + memcpy(addr,prm->macaddr,ETH_ALEN); | |
959 | + else { | |
960 | + memcpy(addr,&prm->caddr,sizeof(unsigned)); | |
961 | + } | |
962 | + } else { | |
963 | + // This packet should not be remapped: | |
964 | + if(head->usemac) { | |
965 | + // We should find MAC address of packet. | |
966 | + // Unfortunatly, this is not always available. | |
967 | + // On bridged packets it always is, however.. | |
968 | + #ifdef KERNEL22 | |
969 | + if(skb->pkt_bridged) { | |
970 | + if(head->srcaddr) { | |
971 | + memcpy(addr,skb->mac.ethernet->h_source,ETH_ALEN); | |
972 | + } else { | |
973 | + memcpy(addr,skb->mac.ethernet->h_dest,ETH_ALEN); | |
974 | + } | |
975 | + } | |
976 | + #endif | |
977 | + } else { | |
978 | + memcpy(addr,&ipaddr,4); | |
979 | + } | |
980 | + } | |
981 | + } else { | |
982 | + // All other traffic is dropped - this ensures that packets | |
983 | + // we consider probably have valid addresses so we don't | |
984 | + // get to many strange addresses into our table. And that we | |
985 | + // don't use bandwidth on strange packets.. | |
986 | + return -1; | |
987 | + } | |
988 | + | |
989 | + return lookup_mac(head,addr); | |
990 | +} | |
991 | + | |
992 | +//----------------------------------------------------------------------------- | |
993 | +// The qdisc itself | |
994 | + | |
995 | +// Pr-class information. | |
996 | +struct wrrc_sched_data { | |
997 | + struct Qdisc* que; // The queue for this class | |
998 | + struct tc_wrr_class_modf class_modf; // Information about the class. | |
999 | + | |
1000 | + // For classes in the heap this is the priority value priosum | |
1001 | + // was updated with for this class: | |
1002 | + u64 priosum_val; | |
1003 | +}; | |
1004 | + | |
1005 | +// Pr-qdisc information: | |
1006 | +struct wrr_sched_data | |
1007 | +{ | |
1008 | + // A heap containing all the bands that will send something | |
1009 | + struct heap h; | |
1010 | + struct heap_element* poll; // bandc elements | |
1011 | + | |
1012 | + // The sum of the prioities of the elements in the heap where | |
1013 | + // a priority of 1 is saved as 2^32 | |
1014 | + u64 priosum; | |
1015 | + | |
1016 | + // A class for each band | |
1017 | + struct wrrc_sched_data* bands; // bandc elements | |
1018 | + | |
1019 | + // Information maintained by the proxydict module of 0 if we | |
1020 | + // have no proxy remapping | |
1021 | + void* proxydict; | |
1022 | + | |
1023 | + // Always incrementning counters, we always have that any value of | |
1024 | + // counter_low_penal < any value of counter_high_penal. | |
1025 | + penalty_base_t counter_low_penal; | |
1026 | + penalty_base_t counter_high_penal; | |
1027 | + | |
1028 | + // Penalty updating: | |
1029 | + struct tc_wrr_qdisc_modf qdisc_modf; | |
1030 | + | |
1031 | + // Statistics: | |
1032 | + int packets_requed; | |
1033 | + | |
1034 | + // The filter: | |
1035 | + struct mac_head filter; | |
1036 | + int bandc; // Number of bands | |
1037 | +}; | |
1038 | + | |
1039 | +// Priority handling. | |
1040 | +// weight is in interval [0..2^32] | |
1041 | +// priosum has whole numbers in the upper and fragments in the lower 32 bits. | |
1042 | +static void weight_transmit(struct tc_wrr_class_weight* p, | |
1043 | + struct tc_wrr_qdisc_weight q, | |
1044 | + unsigned heapsize, | |
1045 | + u64 priosum, u64 weight, | |
1046 | + unsigned size) { | |
1047 | + | |
1048 | + unsigned long now=jiffies/HZ; | |
1049 | + | |
1050 | + // Penalty for transmitting: | |
1051 | + u64 change,old; | |
1052 | + u32 divisor; | |
1053 | + | |
1054 | + change=0; | |
1055 | + switch(q.weight_mode) { | |
1056 | + case 1: change=p->decr*size; break; | |
1057 | + case 2: change=p->decr*size*heapsize; break; | |
1058 | + case 3: // Note: 64 bit division is not always available.. | |
1059 | + divisor=(u32)(weight>>16); | |
1060 | + if(divisor<=0) divisor=1; | |
1061 | + change=p->decr*size*(((u32)(priosum>>16))/divisor); break; | |
1062 | + } | |
1063 | + old=p->val; | |
1064 | + p->val-=change; | |
1065 | + if(p->val>old || p->val<p->min) p->val=p->min; | |
1066 | + | |
1067 | + // Credit for time went: | |
1068 | + change=(now-p->tim)*p->incr; | |
1069 | + p->tim=now; | |
1070 | + old=p->val; | |
1071 | + p->val+=change; | |
1072 | + if(p->val<old || p->val>p->max) p->val=p->max; | |
1073 | +} | |
1074 | + | |
1075 | +static void weight_setdefault(struct tc_wrr_class_weight* p) { | |
1076 | + p->val=(u64)-1; | |
1077 | + p->decr=0; | |
1078 | + p->incr=0; | |
1079 | + p->min=(u64)-1; | |
1080 | + p->max=(u64)-1; | |
1081 | + p->tim=jiffies/HZ; | |
1082 | +} | |
1083 | + | |
1084 | +static void weight_setvalue(struct tc_wrr_class_weight* dst, | |
1085 | + struct tc_wrr_class_weight* src) { | |
1086 | + if(src->val!=0) { | |
1087 | + dst->val=src->val; | |
1088 | + dst->tim=jiffies/HZ; | |
1089 | + } | |
1090 | + if(src->min!=0) dst->min=src->min; | |
1091 | + if(src->max!=0) dst->max=src->max; | |
1092 | + if(src->decr!=((u64)-1)) dst->decr=src->decr; | |
1093 | + if(src->incr!=((u64)-1)) dst->incr=src->incr; | |
1094 | + if(dst->val<dst->min) dst->val=dst->min; | |
1095 | + if(dst->val>dst->max) dst->val=dst->max; | |
1096 | +} | |
1097 | + | |
1098 | +static void wrr_destroy(struct Qdisc *sch) | |
1099 | +{ | |
1100 | + struct wrr_sched_data *q=(struct wrr_sched_data *)sch->data; | |
1101 | + int i; | |
1102 | + | |
1103 | + // Destroy our filter: | |
1104 | + mac_done(&q->filter); | |
1105 | + | |
1106 | + // Destroy all our childre ques: | |
1107 | + for(i=0; i<q->bandc; i++) | |
1108 | + qdisc_destroy(q->bands[i].que); | |
1109 | + | |
1110 | + // And free memory: | |
1111 | + kfree(q->bands); | |
1112 | + kfree(q->poll); | |
1113 | + if(q->proxydict) kfree(q->proxydict); | |
1114 | + | |
1115 | + MOD_DEC_USE_COUNT; | |
1116 | +} | |
1117 | + | |
1118 | +static int wrr_init(struct Qdisc *sch, struct rtattr *opt) | |
1119 | +{ | |
1120 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1121 | + int i,maciniterr; | |
1122 | + char crterr; | |
1123 | + struct tc_wrr_qdisc_crt *qopt; | |
1124 | + | |
1125 | + // Parse options: | |
1126 | + if (!opt) return -EINVAL; // Options must be specified | |
1127 | + if (opt->rta_len < RTA_LENGTH(sizeof(*qopt))) return -EINVAL; | |
1128 | + qopt = RTA_DATA(opt); | |
1129 | + | |
1130 | + if(qopt->bands_max>2048|| qopt->bands_max<2) { | |
1131 | + // More than 2048 queues or less than 2? That cannot be true - it must be | |
1132 | + // an error... | |
1133 | + return -EINVAL; | |
1134 | + } | |
1135 | + | |
1136 | + if(qopt->proxy_maxconn<0 || qopt->proxy_maxconn>20000) { | |
1137 | + // More than this number of maximal concurrent connections is unrealistic | |
1138 | + return -EINVAL; | |
1139 | + } | |
1140 | + | |
1141 | +#ifndef MASQ_SUPPORT | |
1142 | + if(qopt->usemasq) { | |
1143 | + return -ENOSYS; | |
1144 | + } | |
1145 | +#endif | |
1146 | + | |
1147 | +#ifndef KERNEL22 | |
1148 | + if(qopt->usemac) { // Not supported - please fix this! | |
1149 | + return -ENOSYS; | |
1150 | + } | |
1151 | +#endif | |
1152 | + | |
1153 | + q->bandc=qopt->bands_max; | |
1154 | + q->qdisc_modf=qopt->qdisc_modf; | |
1155 | + | |
1156 | + // Create structures: | |
1157 | + q->poll=(struct heap_element*) | |
1158 | + kmalloc( sizeof(struct heap_element)*q->bandc, GFP_KERNEL); | |
1159 | + q->bands=(struct wrrc_sched_data*) | |
1160 | + kmalloc( sizeof(struct wrrc_sched_data)*q->bandc, GFP_KERNEL); | |
1161 | + | |
1162 | + if(qopt->proxy_maxconn>0) { | |
1163 | + q->proxydict=kmalloc(proxyGetMemSize(qopt->proxy_maxconn),GFP_KERNEL); | |
1164 | + } else { | |
1165 | + q->proxydict=0; | |
1166 | + } | |
1167 | + | |
1168 | + // Init mac module: | |
1169 | + maciniterr=mac_init(&q->filter,qopt->bands_max,qopt->srcaddr, | |
1170 | + qopt->usemac,qopt->usemasq,q->proxydict); | |
1171 | + | |
1172 | + // See if we got the memory we wanted: | |
1173 | + if(!q->poll || !q->bands || | |
1174 | + (qopt->proxy_maxconn>0 && !q->proxydict) || maciniterr<0) { | |
1175 | + if(q->poll) kfree(q->poll); | |
1176 | + if(q->bands) kfree(q->bands); | |
1177 | + if(q->proxydict) kfree(q->proxydict); | |
1178 | + if(maciniterr>=0) mac_done(&q->filter); | |
1179 | + return -ENOSPC; | |
1180 | + } | |
1181 | + | |
1182 | + // Initialize proxy: | |
1183 | + if(q->proxydict) { | |
1184 | + proxyInitMem(q->proxydict,qopt->proxy_maxconn); | |
1185 | + } | |
1186 | + | |
1187 | + // Initialize values: | |
1188 | + q->counter_low_penal=0; | |
1189 | + q->counter_high_penal=penalty_base_t_max>>1; | |
1190 | + q->packets_requed=0; | |
1191 | + | |
1192 | + // Initialize empty heap: | |
1193 | + heap_init(&q->h,q->bandc,q->poll); | |
1194 | + q->priosum=0; | |
1195 | + | |
1196 | + // Initialize each band: | |
1197 | + crterr=0; | |
1198 | + for (i=0; i<q->bandc; i++) { | |
1199 | + weight_setdefault(&q->bands[i].class_modf.weight1); | |
1200 | + weight_setdefault(&q->bands[i].class_modf.weight2); | |
1201 | + if(!crterr) { | |
1202 | + struct Qdisc *child=qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); | |
1203 | + if(child) | |
1204 | + q->bands[i].que = child; | |
1205 | + else { | |
1206 | + // Queue couldn't be created :-( | |
1207 | + crterr=1; | |
1208 | + } | |
1209 | + } | |
1210 | + if(crterr) q->bands[i].que = &noop_qdisc; | |
1211 | + } | |
1212 | + | |
1213 | + MOD_INC_USE_COUNT; | |
1214 | + | |
1215 | + if(crterr) { | |
1216 | + // Destroy again: | |
1217 | + wrr_destroy(sch); | |
1218 | + return -ENOMEM; | |
1219 | + } | |
1220 | + | |
1221 | + return 0; | |
1222 | +} | |
1223 | + | |
1224 | +static void wrr_reset(struct Qdisc* sch) | |
1225 | +{ | |
1226 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1227 | + int i; | |
1228 | + | |
1229 | + // Reset own values: | |
1230 | + q->counter_low_penal=0; | |
1231 | + q->counter_high_penal=penalty_base_t_max>>1; | |
1232 | + q->packets_requed=0; | |
1233 | + | |
1234 | + // Reset filter: | |
1235 | + mac_reset(&q->filter); | |
1236 | + | |
1237 | + // Reinitialize heap: | |
1238 | + heap_init(&q->h,q->bandc,q->poll); | |
1239 | + q->priosum=0; | |
1240 | + | |
1241 | + // Reset all bands: | |
1242 | + for (i=0; i<q->bandc; i++) { | |
1243 | + weight_setdefault(&q->bands[i].class_modf.weight1); | |
1244 | + weight_setdefault(&q->bands[i].class_modf.weight2); | |
1245 | + qdisc_reset(q->bands[i].que); | |
1246 | + } | |
1247 | + | |
1248 | + // Reset proxy remapping information: | |
1249 | + if(q->proxydict) | |
1250 | + proxyInitMem(q->proxydict,proxyGetMaxConn(q->proxydict)); | |
1251 | +} | |
1252 | + | |
1253 | +static int wrr_enqueue(struct sk_buff *skb, struct Qdisc* sch) | |
1254 | +{ | |
1255 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1256 | + int retvalue=ENQUEUE_FAIL; | |
1257 | + | |
1258 | + // The packet is in skb. | |
1259 | + int band=mac_classify(&q->filter,skb); | |
1260 | + | |
1261 | + if(band>=0) { | |
1262 | + // Enque packet for this band: | |
1263 | + struct Qdisc* qdisc = q->bands[band].que; | |
1264 | + | |
1265 | + if ((retvalue=qdisc->enqueue(skb, qdisc)) == ENQUEUE_SUCCESS) { | |
1266 | + // Successfull | |
1267 | + sch->stats.bytes += skb->len; | |
1268 | + sch->stats.packets++; | |
1269 | + sch->q.qlen++; | |
1270 | + | |
1271 | + // Insert band into heap if not already there: | |
1272 | + if(!heap_contains(&q->h,band)) { | |
1273 | + penalty_t p; | |
1274 | + if(!heap_empty(&q->h)) | |
1275 | + p.ms=heap_get_penalty(&q->h,heap_root(&q->h)).ms; | |
1276 | + else | |
1277 | + p.ms=0; | |
1278 | + p.ls=q->counter_low_penal++; | |
1279 | + heap_insert(&q->h,band,p); | |
1280 | + q->bands[band].priosum_val= | |
1281 | + ((q->bands[band].class_modf.weight1.val>>48)+1)* | |
1282 | + ((q->bands[band].class_modf.weight2.val>>48)+1); | |
1283 | + q->priosum+=q->bands[band].priosum_val; | |
1284 | + } | |
1285 | + } | |
1286 | + } else { | |
1287 | + // If we decide not to enque it seems like we also need to free the packet: | |
1288 | + kfree_skb(skb); | |
1289 | + } | |
1290 | + | |
1291 | + if(retvalue!=ENQUEUE_SUCCESS) { | |
1292 | + // Packet not enqued: | |
1293 | + sch->stats.drops++; | |
1294 | + } | |
1295 | + | |
1296 | + return retvalue; | |
1297 | +} | |
1298 | + | |
1299 | +static struct sk_buff *wrr_dequeue(struct Qdisc* sch) | |
1300 | +{ | |
1301 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1302 | + struct sk_buff* skb; | |
1303 | + int band; | |
1304 | + u64 weight,priosum; | |
1305 | + struct wrrc_sched_data* b; | |
1306 | + | |
1307 | + // Return if heap is empty: | |
1308 | + if(heap_empty(&q->h)) return 0; | |
1309 | + | |
1310 | + // Find root element: | |
1311 | + band=heap_root(&q->h); | |
1312 | + | |
1313 | + // Find priority of this element in interval [1;2^32] | |
1314 | + b=&q->bands[band]; | |
1315 | + weight=((b->class_modf.weight1.val>>48)+1)* | |
1316 | + ((b->class_modf.weight2.val>>48)+1); //weight is in interval [1;2^32] | |
1317 | + priosum=q->priosum; | |
1318 | + q->priosum-=q->bands[band].priosum_val; | |
1319 | + | |
1320 | + // Deque the packet from the root: | |
1321 | + skb=q->bands[band].que->dequeue(q->bands[band].que); | |
1322 | + | |
1323 | + if(skb) { | |
1324 | + // There was a packet in this que. | |
1325 | + unsigned adjlen; | |
1326 | + penalty_t p; | |
1327 | + | |
1328 | + // Find length of packet adjusted with priority: | |
1329 | + adjlen=(u32)(weight>>(32-16)); | |
1330 | + if(adjlen==0) adjlen=1; | |
1331 | + adjlen=(skb->len<<16)/adjlen; | |
1332 | + | |
1333 | + // Update penalty information for this class: | |
1334 | + weight_transmit(&b->class_modf.weight1,q->qdisc_modf.weight1,q->h.elements,priosum,weight,skb->len); | |
1335 | + weight_transmit(&b->class_modf.weight2,q->qdisc_modf.weight2,q->h.elements,priosum,weight,skb->len); | |
1336 | + q->bands[band].priosum_val=((b->class_modf.weight1.val>>48)+1)* | |
1337 | + ((b->class_modf.weight2.val>>48)+1); | |
1338 | + q->priosum+=q->bands[band].priosum_val; | |
1339 | + | |
1340 | + // And update the class in the heap | |
1341 | + p=heap_get_penalty(&q->h,band); | |
1342 | + p.ms+=adjlen; | |
1343 | + p.ls=q->counter_high_penal++; | |
1344 | + heap_set_penalty(&q->h,band,p); | |
1345 | + | |
1346 | + // Return packet: | |
1347 | + sch->q.qlen--; | |
1348 | + return skb; | |
1349 | + } | |
1350 | + | |
1351 | + // No packet - so machine should be removed from heap: | |
1352 | + heap_remove(&q->h,band); | |
1353 | + | |
1354 | + // And try again: | |
1355 | + return wrr_dequeue(sch); | |
1356 | +} | |
1357 | + | |
1358 | +static int wrr_requeue(struct sk_buff *skb, struct Qdisc* sch) | |
1359 | +{ | |
1360 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1361 | + struct Qdisc* qdisc; | |
1362 | + int ret; | |
1363 | + | |
1364 | + // Find band we took it from: | |
1365 | + int band=mac_classify(&q->filter,skb); | |
1366 | + if(band<0) { | |
1367 | + // Who should now free the pakcet? | |
1368 | + printk(KERN_DEBUG "sch_wrr: Oops - packet requed could never have been queued.\n"); | |
1369 | + sch->stats.drops++; | |
1370 | + return ENQUEUE_FAIL; | |
1371 | + } | |
1372 | + | |
1373 | + q->packets_requed++; | |
1374 | + | |
1375 | + // Try to requeue it on that machine: | |
1376 | + qdisc=q->bands[band].que; | |
1377 | + | |
1378 | + if((ret=qdisc->ops->requeue(skb,qdisc))==ENQUEUE_SUCCESS) { | |
1379 | + // On success: | |
1380 | + sch->q.qlen++; | |
1381 | + | |
1382 | + // We should restore priority information - but we don't | |
1383 | + // | |
1384 | + // p=heap_get_penalty(&q->h,band); | |
1385 | + // ... | |
1386 | + // heap_set_penalty(&q->h,band,p); | |
1387 | + | |
1388 | + return ENQUEUE_SUCCESS; | |
1389 | + } else { | |
1390 | + sch->stats.drops++; | |
1391 | + return ret; | |
1392 | + } | |
1393 | +} | |
1394 | + | |
1395 | +static int wrr_drop(struct Qdisc* sch) | |
1396 | +{ | |
1397 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1398 | + | |
1399 | + // Ugly... Drop button up in heap: | |
1400 | + int i; | |
1401 | + | |
1402 | + for(i=q->h.elements; i>=1; i--) { | |
1403 | + int band=q->h.root_1[i].id; | |
1404 | + if(q->bands[band].que->ops->drop(q->bands[band].que)) { | |
1405 | + // On success | |
1406 | + sch->q.qlen--; | |
1407 | + return 1; | |
1408 | + } | |
1409 | + } | |
1410 | + | |
1411 | + return 0; | |
1412 | +} | |
1413 | + | |
1414 | +#ifdef CONFIG_RTNETLINK | |
1415 | +static int wrr_dump(struct Qdisc *sch, struct sk_buff *skb) | |
1416 | +{ | |
1417 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1418 | + unsigned char *b = skb->tail; | |
1419 | + struct tc_wrr_qdisc_stats opt; | |
1420 | + | |
1421 | + opt.qdisc_crt.qdisc_modf=q->qdisc_modf; | |
1422 | + opt.qdisc_crt.srcaddr=q->filter.srcaddr; | |
1423 | + opt.qdisc_crt.usemac=q->filter.usemac; | |
1424 | + opt.qdisc_crt.usemasq=q->filter.usemasq; | |
1425 | + opt.qdisc_crt.bands_max=q->filter.mac_max; | |
1426 | + opt.nodes_in_heap=q->h.elements; | |
1427 | + opt.bands_cur=q->filter.mac_cur; | |
1428 | + opt.bands_reused=q->filter.mac_reused; | |
1429 | + opt.packets_requed=q->packets_requed; | |
1430 | + opt.priosum=q->priosum; | |
1431 | + | |
1432 | + if(q->proxydict) { | |
1433 | + opt.qdisc_crt.proxy_maxconn=proxyGetMaxConn(q->proxydict); | |
1434 | + opt.proxy_curconn=proxyGetCurConn(q->proxydict); | |
1435 | + } else { | |
1436 | + opt.qdisc_crt.proxy_maxconn=0; | |
1437 | + opt.proxy_curconn=0; | |
1438 | + } | |
1439 | + | |
1440 | + RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); | |
1441 | + return skb->len; | |
1442 | + | |
1443 | +rtattr_failure: // seems like RTA_PUT jump to this label.. | |
1444 | + skb_trim(skb, b - skb->data); | |
1445 | + return -1; | |
1446 | +} | |
1447 | +#endif | |
1448 | + | |
1449 | +static int wrr_tune_std(struct Qdisc *sch, struct rtattr *opt) | |
1450 | +{ | |
1451 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1452 | + struct tc_wrr_qdisc_modf_std *qopt = RTA_DATA(opt); | |
1453 | + | |
1454 | + if(opt->rta_len < RTA_LENGTH(sizeof(*qopt))) return -EINVAL; | |
1455 | + | |
1456 | + LOCK_START | |
1457 | + | |
1458 | + if(qopt->change_class) { | |
1459 | + int idx=lookup_mac(&q->filter,qopt->addr); | |
1460 | + weight_setvalue | |
1461 | + (&q->bands[idx].class_modf.weight1,&qopt->class_modf.weight1); | |
1462 | + weight_setvalue | |
1463 | + (&q->bands[idx].class_modf.weight2,&qopt->class_modf.weight2); | |
1464 | + } else { | |
1465 | + if(qopt->qdisc_modf.weight1.weight_mode!=-1) | |
1466 | + q->qdisc_modf.weight1.weight_mode=qopt->qdisc_modf.weight1.weight_mode; | |
1467 | + if(qopt->qdisc_modf.weight2.weight_mode!=-1) | |
1468 | + q->qdisc_modf.weight2.weight_mode=qopt->qdisc_modf.weight2.weight_mode; | |
1469 | + } | |
1470 | + | |
1471 | + LOCK_END | |
1472 | + | |
1473 | + return 0; | |
1474 | +} | |
1475 | + | |
1476 | +static int wrr_tune_proxy(struct Qdisc *sch, struct rtattr *opt) | |
1477 | +{ | |
1478 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1479 | + struct tc_wrr_qdisc_modf_proxy *qopt = RTA_DATA(opt); | |
1480 | + int i; | |
1481 | + | |
1482 | + // Return if we are not configured with proxy support: | |
1483 | + if(!q->proxydict) return -ENOSYS; | |
1484 | + | |
1485 | + // Return if not enough data given: | |
1486 | + if(opt->rta_len<RTA_LENGTH(sizeof(*qopt)) || | |
1487 | + opt->rta_len< | |
1488 | + RTA_LENGTH(sizeof(*qopt)+sizeof(ProxyRemapBlock)*qopt->changec)) | |
1489 | + return -EINVAL; | |
1490 | + | |
1491 | + LOCK_START; | |
1492 | + | |
1493 | + if(qopt->reset) { | |
1494 | + proxyInitMem(q->proxydict,proxyGetMaxConn(q->proxydict)); | |
1495 | + } | |
1496 | + | |
1497 | + // Do all the changes: | |
1498 | + for(i=0; i<qopt->changec; i++) { | |
1499 | + proxyConsumeBlock(q->proxydict,&((ProxyRemapBlock*)&qopt->changes)[i]); | |
1500 | + } | |
1501 | + | |
1502 | + LOCK_END; | |
1503 | + | |
1504 | + return 0; | |
1505 | +} | |
1506 | + | |
1507 | +static int wrr_tune(struct Qdisc *sch, struct rtattr *opt) { | |
1508 | + if(((struct tc_wrr_qdisc_modf_std*)RTA_DATA(opt))->proxy) { | |
1509 | + return wrr_tune_proxy(sch,opt); | |
1510 | + } else { | |
1511 | + return wrr_tune_std(sch,opt); | |
1512 | + } | |
1513 | +} | |
1514 | + | |
1515 | +//----------------------------------------------------------------------------- | |
1516 | +// Classes. | |
1517 | +// External and internal IDs are equal. They are the band number plus 1. | |
1518 | + | |
1519 | +// Replace a class with another: | |
1520 | +static int wrr_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, | |
1521 | + struct Qdisc **old) | |
1522 | +{ | |
1523 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1524 | + if(arg>q->bandc || arg==0) return -EINVAL; | |
1525 | + arg--; | |
1526 | + | |
1527 | + if (new == NULL) | |
1528 | + new = &noop_qdisc; | |
1529 | + | |
1530 | +#ifdef KERNEL22 | |
1531 | + *old = xchg(&q->bands[arg].que, new); | |
1532 | +#else | |
1533 | + LOCK_START | |
1534 | + *old = q->bands[arg].que; | |
1535 | + q->bands[arg].que = new; | |
1536 | + qdisc_reset(*old); | |
1537 | + LOCK_END | |
1538 | +#endif | |
1539 | + | |
1540 | + return 0; | |
1541 | +} | |
1542 | + | |
1543 | +// Returns the qdisc for a class: | |
1544 | +static struct Qdisc * wrr_leaf(struct Qdisc *sch, unsigned long arg) | |
1545 | +{ | |
1546 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1547 | + if(arg>q->bandc || arg==0) return NULL; | |
1548 | + arg--; | |
1549 | + return q->bands[arg].que; | |
1550 | +} | |
1551 | + | |
1552 | +static unsigned long wrr_get(struct Qdisc *sch, u32 classid) | |
1553 | +{ | |
1554 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1555 | + unsigned long band = TC_H_MIN(classid); | |
1556 | + if(band>q->bandc || band==0) return 0; | |
1557 | + return band; | |
1558 | +} | |
1559 | + | |
1560 | +static void wrr_put(struct Qdisc *q, unsigned long cl) | |
1561 | +{ | |
1562 | + return; | |
1563 | +} | |
1564 | + | |
1565 | +static int wrr_delete(struct Qdisc *sch, unsigned long cl) | |
1566 | +{ | |
1567 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1568 | + if(cl==0 || cl>q->bandc) return -ENOENT; | |
1569 | + cl--; | |
1570 | + return 0; | |
1571 | +} | |
1572 | + | |
1573 | + | |
1574 | +#ifdef CONFIG_RTNETLINK | |
1575 | +static int wrr_dump_class(struct Qdisc *sch, unsigned long cl, | |
1576 | + struct sk_buff *skb, struct tcmsg *tcm) | |
1577 | +{ | |
1578 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1579 | + unsigned char *b = skb->tail; | |
1580 | + struct tc_wrr_class_stats opt; | |
1581 | + | |
1582 | + // Handle of this class: | |
1583 | + tcm->tcm_handle = sch->handle|cl; | |
1584 | + | |
1585 | + if(cl==0 || cl>q->bandc) | |
1586 | + goto rtattr_failure; | |
1587 | + cl--; | |
1588 | + | |
1589 | + if(cl>=q->filter.mac_cur) { | |
1590 | + // Band is unused: | |
1591 | + memset(&opt,0,sizeof(opt)); | |
1592 | + opt.used=0; | |
1593 | + } else { | |
1594 | + opt.used=1; | |
1595 | + opt.class_modf.weight1=q->bands[cl].class_modf.weight1; | |
1596 | + opt.class_modf.weight2=q->bands[cl].class_modf.weight2; | |
1597 | + weight_transmit(&opt.class_modf.weight1,q->qdisc_modf.weight1,0,0,0,0); | |
1598 | + weight_transmit(&opt.class_modf.weight2,q->qdisc_modf.weight2,0,0,0,0); | |
1599 | + memcpy(opt.addr,q->filter.cls2mac+cl*ETH_ALEN,ETH_ALEN); | |
1600 | + opt.usemac=q->filter.usemac; | |
1601 | + opt.heappos=q->h.root_1[cl+1].id2idx; | |
1602 | + if(opt.heappos!=0) { // Is in heap | |
1603 | + opt.penal_ls=heap_get_penalty(&q->h,cl).ls; | |
1604 | + opt.penal_ms=heap_get_penalty(&q->h,cl).ms; | |
1605 | + } else { | |
1606 | + opt.penal_ls=0; | |
1607 | + opt.penal_ms=0; | |
1608 | + } | |
1609 | + } | |
1610 | + | |
1611 | + // Put quing information: | |
1612 | + RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); | |
1613 | + return skb->len; | |
1614 | + | |
1615 | +rtattr_failure: | |
1616 | + skb_trim(skb, b - skb->data); | |
1617 | + return -1; | |
1618 | +} | |
1619 | +#endif | |
1620 | + | |
1621 | +static int wrr_change(struct Qdisc *sch, u32 handle, u32 parent, | |
1622 | + struct rtattr **tca, unsigned long *arg) | |
1623 | +{ | |
1624 | + unsigned long cl = *arg; | |
1625 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1626 | + struct rtattr *opt = tca[TCA_OPTIONS-1]; | |
1627 | + struct tc_wrr_class_modf *copt = RTA_DATA(opt); | |
1628 | + | |
1629 | + if(cl==0 || cl>q->bandc) return -EINVAL; | |
1630 | + cl--; | |
1631 | + | |
1632 | + if (opt->rta_len < RTA_LENGTH(sizeof(*copt))) return -EINVAL; | |
1633 | + | |
1634 | + LOCK_START; | |
1635 | + | |
1636 | + weight_setvalue(&q->bands[cl].class_modf.weight1,&copt->weight1); | |
1637 | + weight_setvalue(&q->bands[cl].class_modf.weight2,&copt->weight2); | |
1638 | + | |
1639 | + LOCK_END; | |
1640 | + | |
1641 | + return 0; | |
1642 | +} | |
1643 | + | |
1644 | +static void wrr_walk(struct Qdisc *sch, struct qdisc_walker *arg) | |
1645 | +{ | |
1646 | + struct wrr_sched_data *q = (struct wrr_sched_data *)sch->data; | |
1647 | + int prio; | |
1648 | + | |
1649 | + if (arg->stop) return; | |
1650 | + | |
1651 | + for (prio = 1; prio <= q->bandc; prio++) { | |
1652 | + if (arg->count < arg->skip) { | |
1653 | + arg->count++; | |
1654 | + continue; | |
1655 | + } | |
1656 | + if (arg->fn(sch, prio, arg) < 0) { | |
1657 | + arg->stop = 1; | |
1658 | + break; | |
1659 | + } | |
1660 | + arg->count++; | |
1661 | + } | |
1662 | +} | |
1663 | + | |
1664 | +static struct tcf_proto ** wrr_find_tcf(struct Qdisc *sch, unsigned long cl) | |
1665 | +{ | |
1666 | + return NULL; | |
1667 | +} | |
1668 | + | |
1669 | +static unsigned long wrr_bind(struct Qdisc *sch, | |
1670 | + unsigned long parent, u32 classid) | |
1671 | +{ | |
1672 | + return wrr_get(sch, classid); | |
1673 | +} | |
1674 | + | |
1675 | +//----------------------------------------------------------------------------- | |
1676 | +// Generel | |
1677 | + | |
1678 | +static struct Qdisc_class_ops wrr_class_ops = | |
1679 | +{ | |
1680 | + wrr_graft, | |
1681 | + wrr_leaf, | |
1682 | + | |
1683 | + wrr_get, | |
1684 | + wrr_put, | |
1685 | + wrr_change, | |
1686 | + wrr_delete, | |
1687 | + wrr_walk, | |
1688 | + | |
1689 | + wrr_find_tcf, | |
1690 | + wrr_bind, | |
1691 | + wrr_put, | |
1692 | + | |
1693 | +#ifdef CONFIG_RTNETLINK | |
1694 | + wrr_dump_class, | |
1695 | +#endif | |
1696 | +}; | |
1697 | + | |
1698 | +struct Qdisc_ops wrr_qdisc_ops = | |
1699 | +{ | |
1700 | + NULL, | |
1701 | + &wrr_class_ops, | |
1702 | + "wrr", | |
1703 | + sizeof(struct wrr_sched_data), | |
1704 | + | |
1705 | + wrr_enqueue, | |
1706 | + wrr_dequeue, | |
1707 | + wrr_requeue, | |
1708 | + wrr_drop, | |
1709 | + | |
1710 | + wrr_init, | |
1711 | + wrr_reset, | |
1712 | + wrr_destroy, | |
1713 | + wrr_tune, | |
1714 | + | |
1715 | +#ifdef CONFIG_RTNETLINK | |
1716 | + wrr_dump, | |
1717 | +#endif | |
1718 | +}; | |
1719 | + | |
1720 | +#ifdef MODULE | |
1721 | + | |
1722 | +int init_module(void) | |
1723 | +{ | |
1724 | + return register_qdisc(&wrr_qdisc_ops); | |
1725 | +} | |
1726 | + | |
1727 | +void cleanup_module(void) | |
1728 | +{ | |
1729 | + unregister_qdisc(&wrr_qdisc_ops); | |
1730 | +} | |
1731 | + | |
1732 | +#endif | |
1733 | + |