Blame view

net/sched/sch_teql.c 12 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
  /* net/sched/sch_teql.c	"True" (or "trivial") link equalizer.
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   */
  
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
  #include <linux/types.h>
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/errno.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
17
  #include <linux/if_arp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/netdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
  #include <linux/skbuff.h>
  #include <linux/moduleparam.h>
0ba480538   Patrick McHardy   [NET_SCHED]: Remo...
22
23
  #include <net/dst.h>
  #include <net/neighbour.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  #include <net/pkt_sched.h>
  
  /*
     How to setup it.
     ----------------
  
     After loading this module you will find a new device teqlN
     and new qdisc with the same name. To join a slave to the equalizer
     you should just set this qdisc on a device f.e.
  
     # tc qdisc add dev eth0 root teql0
     # tc qdisc add dev eth1 root teql0
  
     That's all. Full PnP 8)
  
     Applicability.
     --------------
  
     1. Slave devices MUST be active devices, i.e., they must raise the tbusy
        signal and generate EOI events. If you want to equalize virtual devices
        like tunnels, use a normal eql device.
     2. This device puts no limitations on physical slave characteristics
        f.e. it will equalize 9600baud line and 100Mb ethernet perfectly :-)
        Certainly, large difference in link speeds will make the resulting
        eqalized link unusable, because of huge packet reordering.
        I estimate an upper useful difference as ~10 times.
     3. If the slave requires address resolution, only protocols using
        neighbour cache (IPv4/IPv6) will work over the equalized link.
        Other protocols are still allowed to use the slave device directly,
        which will not break load balancing, though native slave
        traffic will have the highest priority.  */
cc7ec456f   Eric Dumazet   net_sched: cleanups
55
  struct teql_master {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
  	struct Qdisc_ops qops;
  	struct net_device *dev;
  	struct Qdisc *slaves;
  	struct list_head master_list;
1ac9ad139   Eric Dumazet   net: remove dev_t...
60
61
62
63
  	unsigned long	tx_bytes;
  	unsigned long	tx_packets;
  	unsigned long	tx_errors;
  	unsigned long	tx_dropped;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
65
  struct teql_sched_data {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  	struct Qdisc *next;
  	struct teql_master *m;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
  	struct sk_buff_head q;
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
70
  #define NEXT_SLAVE(q) (((struct teql_sched_data *)qdisc_priv(q))->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71

cc7ec456f   Eric Dumazet   net_sched: cleanups
72
  #define FMASK (IFF_BROADCAST | IFF_POINTOPOINT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
  
  /* "teql*" qdisc routines */
  
  static int
520ac30f4   Eric Dumazet   net_sched: drop p...
77
  teql_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
  {
5ce2d488f   David S. Miller   pkt_sched: Remove...
79
  	struct net_device *dev = qdisc_dev(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  	struct teql_sched_data *q = qdisc_priv(sch);
4cd8c9e87   Krishna Kumar   [NET_SCHED]: teql...
81
82
  	if (q->q.qlen < dev->tx_queue_len) {
  		__skb_queue_tail(&q->q, skb);
9871e50ed   Ben Greear   net: Use NET_XMIT...
83
  		return NET_XMIT_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  	}
520ac30f4   Eric Dumazet   net_sched: drop p...
85
  	return qdisc_drop(skb, sch, to_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  static struct sk_buff *
cc7ec456f   Eric Dumazet   net_sched: cleanups
88
  teql_dequeue(struct Qdisc *sch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
  {
  	struct teql_sched_data *dat = qdisc_priv(sch);
b0e1e6462   David S. Miller   netdev: Move rest...
91
  	struct netdev_queue *dat_queue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  	struct sk_buff *skb;
46e5da40a   John Fastabend   net: qdisc: use r...
93
  	struct Qdisc *q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
  
  	skb = __skb_dequeue(&dat->q);
e8a0464cc   David S. Miller   netdev: Allocate ...
96
  	dat_queue = netdev_get_tx_queue(dat->m->dev, 0);
46e5da40a   John Fastabend   net: qdisc: use r...
97
  	q = rcu_dereference_bh(dat_queue->qdisc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  	if (skb == NULL) {
46e5da40a   John Fastabend   net: qdisc: use r...
99
  		struct net_device *m = qdisc_dev(q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
  		if (m) {
  			dat->m->slaves = sch;
  			netif_wake_queue(m);
  		}
9190b3b32   Eric Dumazet   net_sched: accura...
104
105
  	} else {
  		qdisc_bstats_update(sch, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  	}
46e5da40a   John Fastabend   net: qdisc: use r...
107
  	sch->q.qlen = dat->q.qlen + q->q.qlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  	return skb;
  }
8e3af9789   Jarek Poplawski   pkt_sched: Add qd...
110
  static struct sk_buff *
cc7ec456f   Eric Dumazet   net_sched: cleanups
111
  teql_peek(struct Qdisc *sch)
8e3af9789   Jarek Poplawski   pkt_sched: Add qd...
112
113
114
115
  {
  	/* teql is meant to be used as root qdisc */
  	return NULL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  static void
cc7ec456f   Eric Dumazet   net_sched: cleanups
117
  teql_reset(struct Qdisc *sch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
  {
  	struct teql_sched_data *dat = qdisc_priv(sch);
  
  	skb_queue_purge(&dat->q);
  	sch->q.qlen = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
  }
  
  static void
cc7ec456f   Eric Dumazet   net_sched: cleanups
126
  teql_destroy(struct Qdisc *sch)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
129
130
  {
  	struct Qdisc *q, *prev;
  	struct teql_sched_data *dat = qdisc_priv(sch);
  	struct teql_master *master = dat->m;
cc7ec456f   Eric Dumazet   net_sched: cleanups
131
132
  	prev = master->slaves;
  	if (prev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
138
139
  		do {
  			q = NEXT_SLAVE(prev);
  			if (q == sch) {
  				NEXT_SLAVE(prev) = NEXT_SLAVE(q);
  				if (q == master->slaves) {
  					master->slaves = NEXT_SLAVE(q);
  					if (q == master->slaves) {
e8a0464cc   David S. Miller   netdev: Allocate ...
140
  						struct netdev_queue *txq;
838740009   David S. Miller   pkt_sched: Kill n...
141
  						spinlock_t *root_lock;
e8a0464cc   David S. Miller   netdev: Allocate ...
142
143
  
  						txq = netdev_get_tx_queue(master->dev, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  						master->slaves = NULL;
838740009   David S. Miller   pkt_sched: Kill n...
145

46e5da40a   John Fastabend   net: qdisc: use r...
146
  						root_lock = qdisc_root_sleeping_lock(rtnl_dereference(txq->qdisc));
838740009   David S. Miller   pkt_sched: Kill n...
147
  						spin_lock_bh(root_lock);
46e5da40a   John Fastabend   net: qdisc: use r...
148
  						qdisc_reset(rtnl_dereference(txq->qdisc));
838740009   David S. Miller   pkt_sched: Kill n...
149
  						spin_unlock_bh(root_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
  					}
  				}
  				skb_queue_purge(&dat->q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
  				break;
  			}
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
155

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
  		} while ((prev = q) != master->slaves);
  	}
  }
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
159
  static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  {
5ce2d488f   David S. Miller   pkt_sched: Remove...
161
  	struct net_device *dev = qdisc_dev(sch);
cc7ec456f   Eric Dumazet   net_sched: cleanups
162
  	struct teql_master *m = (struct teql_master *)sch->ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  	struct teql_sched_data *q = qdisc_priv(sch);
  
  	if (dev->hard_header_len > m->dev->hard_header_len)
  		return -EINVAL;
  
  	if (m->dev == dev)
  		return -ELOOP;
  
  	q->m = m;
  
  	skb_queue_head_init(&q->q);
  
  	if (m->slaves) {
  		if (m->dev->flags & IFF_UP) {
f64f9e719   Joe Perches   net: Move && and ...
177
178
179
180
181
182
183
  			if ((m->dev->flags & IFF_POINTOPOINT &&
  			     !(dev->flags & IFF_POINTOPOINT)) ||
  			    (m->dev->flags & IFF_BROADCAST &&
  			     !(dev->flags & IFF_BROADCAST)) ||
  			    (m->dev->flags & IFF_MULTICAST &&
  			     !(dev->flags & IFF_MULTICAST)) ||
  			    dev->mtu < m->dev->mtu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  				return -EINVAL;
  		} else {
  			if (!(dev->flags&IFF_POINTOPOINT))
  				m->dev->flags &= ~IFF_POINTOPOINT;
  			if (!(dev->flags&IFF_BROADCAST))
  				m->dev->flags &= ~IFF_BROADCAST;
  			if (!(dev->flags&IFF_MULTICAST))
  				m->dev->flags &= ~IFF_MULTICAST;
  			if (dev->mtu < m->dev->mtu)
  				m->dev->mtu = dev->mtu;
  		}
  		q->next = NEXT_SLAVE(m->slaves);
  		NEXT_SLAVE(m->slaves) = sch;
  	} else {
  		q->next = sch;
  		m->slaves = sch;
  		m->dev->mtu = dev->mtu;
  		m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK);
  	}
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
  
  static int
f7e57044e   Eric Dumazet   sch_teql: fix loc...
207
208
  __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
  	       struct net_device *dev, struct netdev_queue *txq,
dbedbe6d5   David S. Miller   sch_teql: Convert...
209
  	       struct dst_entry *dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  {
dbedbe6d5   David S. Miller   sch_teql: Convert...
211
212
  	struct neighbour *n;
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213

dbedbe6d5   David S. Miller   sch_teql: Convert...
214
215
216
217
218
219
220
221
222
223
224
225
  	n = dst_neigh_lookup_skb(dst, skb);
  	if (!n)
  		return -ENOENT;
  
  	if (dst->dev != dev) {
  		struct neighbour *mn;
  
  		mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
  		neigh_release(n);
  		if (IS_ERR(mn))
  			return PTR_ERR(mn);
  		n = mn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  	}
dbedbe6d5   David S. Miller   sch_teql: Convert...
227

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
  	if (neigh_event_send(n, skb_res) == 0) {
  		int err;
0ed8ddf40   Eric Dumazet   neigh: Protect ne...
230
  		char haddr[MAX_ADDR_LEN];
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
231

0ed8ddf40   Eric Dumazet   neigh: Protect ne...
232
  		neigh_ha_snapshot(haddr, n, dev);
d8b9605d2   Jiri Pirko   net: sched: fix s...
233
234
  		err = dev_hard_header(skb, dev, ntohs(tc_skb_protocol(skb)),
  				      haddr, NULL, skb->len);
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
235

dbedbe6d5   David S. Miller   sch_teql: Convert...
236
237
238
239
  		if (err < 0)
  			err = -EINVAL;
  	} else {
  		err = (skb_res == NULL) ? -EAGAIN : 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
  	}
  	neigh_release(n);
dbedbe6d5   David S. Miller   sch_teql: Convert...
242
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
  }
3b04ddde0   Stephen Hemminger   [NET]: Move hardw...
244
  static inline int teql_resolve(struct sk_buff *skb,
f7e57044e   Eric Dumazet   sch_teql: fix loc...
245
246
247
  			       struct sk_buff *skb_res,
  			       struct net_device *dev,
  			       struct netdev_queue *txq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  {
f7e57044e   Eric Dumazet   sch_teql: fix loc...
249
  	struct dst_entry *dst = skb_dst(skb);
f7e57044e   Eric Dumazet   sch_teql: fix loc...
250
  	int res;
46e5da40a   John Fastabend   net: qdisc: use r...
251
  	if (rcu_access_pointer(txq->qdisc) == &noop_qdisc)
4f9f8311a   Evgeniy Polyakov   [PKT_SCHED]: Fix ...
252
  		return -ENODEV;
f7e57044e   Eric Dumazet   sch_teql: fix loc...
253
  	if (!dev->header_ops || !dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  		return 0;
f7e57044e   Eric Dumazet   sch_teql: fix loc...
255
256
  
  	rcu_read_lock();
dbedbe6d5   David S. Miller   sch_teql: Convert...
257
  	res = __teql_resolve(skb, skb_res, dev, txq, dst);
f7e57044e   Eric Dumazet   sch_teql: fix loc...
258
259
260
  	rcu_read_unlock();
  
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  }
6fef4c0c8   Stephen Hemminger   netdev: convert p...
262
  static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
  {
2941a4863   Patrick McHardy   [NET]: Convert ne...
264
  	struct teql_master *master = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
  	struct Qdisc *start, *q;
  	int busy;
  	int nores;
4e3ab47a5   Pavel Emelyanov   [NET]: Make and u...
268
  	int subq = skb_get_queue_mapping(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
  	struct sk_buff *skb_res = NULL;
  
  	start = master->slaves;
  
  restart:
  	nores = 0;
  	busy = 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
276
277
  	q = start;
  	if (!q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
  		goto drop;
  
  	do {
5ce2d488f   David S. Miller   pkt_sched: Remove...
281
  		struct net_device *slave = qdisc_dev(q);
61294e2e2   Stephen Hemminger   sch_teql: convert...
282
  		struct netdev_queue *slave_txq = netdev_get_tx_queue(slave, 0);
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
283

e8a0464cc   David S. Miller   netdev: Allocate ...
284
  		if (slave_txq->qdisc_sleeping != q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  			continue;
734664982   Tom Herbert   net: Add queue st...
286
  		if (netif_xmit_stopped(netdev_get_tx_queue(slave, subq)) ||
f25f4e448   Peter P Waskiewicz Jr   [CORE] Stack chan...
287
  		    !netif_running(slave)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
  			busy = 1;
  			continue;
  		}
f7e57044e   Eric Dumazet   sch_teql: fix loc...
291
  		switch (teql_resolve(skb, skb_res, slave, slave_txq)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  		case 0:
c3f26a269   David S. Miller   netdev: Fix lockd...
293
  			if (__netif_tx_trylock(slave_txq)) {
c0f84d0d4   Eric Dumazet   sch_teql: should ...
294
  				unsigned int length = qdisc_pkt_len(skb);
734664982   Tom Herbert   net: Add queue st...
295
  				if (!netif_xmit_frozen_or_stopped(slave_txq) &&
fa2dbdc25   David S. Miller   net: Pass a "more...
296
297
  				    netdev_start_xmit(skb, slave, slave_txq, false) ==
  				    NETDEV_TX_OK) {
c3f26a269   David S. Miller   netdev: Fix lockd...
298
  					__netif_tx_unlock(slave_txq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
  					master->slaves = NEXT_SLAVE(q);
  					netif_wake_queue(dev);
1ac9ad139   Eric Dumazet   net: remove dev_t...
301
302
  					master->tx_packets++;
  					master->tx_bytes += length;
6ed106549   Patrick McHardy   net: use NETDEV_T...
303
  					return NETDEV_TX_OK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  				}
c3f26a269   David S. Miller   netdev: Fix lockd...
305
  				__netif_tx_unlock(slave_txq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  			}
734664982   Tom Herbert   net: Add queue st...
307
  			if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
  				busy = 1;
  			break;
  		case 1:
  			master->slaves = NEXT_SLAVE(q);
6ed106549   Patrick McHardy   net: use NETDEV_T...
312
  			return NETDEV_TX_OK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
  		default:
  			nores = 1;
  			break;
  		}
bbe735e42   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
317
  		__skb_pull(skb, skb_network_offset(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
324
325
326
  	} while ((q = NEXT_SLAVE(q)) != start);
  
  	if (nores && skb_res == NULL) {
  		skb_res = skb;
  		goto restart;
  	}
  
  	if (busy) {
  		netif_stop_queue(dev);
5b5481402   Patrick McHardy   net: use symbolic...
327
  		return NETDEV_TX_BUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  	}
1ac9ad139   Eric Dumazet   net: remove dev_t...
329
  	master->tx_errors++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  
  drop:
1ac9ad139   Eric Dumazet   net: remove dev_t...
332
  	master->tx_dropped++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
  	dev_kfree_skb(skb);
6ed106549   Patrick McHardy   net: use NETDEV_T...
334
  	return NETDEV_TX_OK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
  }
  
  static int teql_master_open(struct net_device *dev)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
339
  	struct Qdisc *q;
2941a4863   Patrick McHardy   [NET]: Convert ne...
340
  	struct teql_master *m = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
  	int mtu = 0xFFFE;
cc7ec456f   Eric Dumazet   net_sched: cleanups
342
  	unsigned int flags = IFF_NOARP | IFF_MULTICAST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
349
350
  
  	if (m->slaves == NULL)
  		return -EUNATCH;
  
  	flags = FMASK;
  
  	q = m->slaves;
  	do {
5ce2d488f   David S. Miller   pkt_sched: Remove...
351
  		struct net_device *slave = qdisc_dev(q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
  
  		if (slave == NULL)
  			return -EUNATCH;
  
  		if (slave->mtu < mtu)
  			mtu = slave->mtu;
  		if (slave->hard_header_len > LL_MAX_HEADER)
  			return -EINVAL;
  
  		/* If all the slaves are BROADCAST, master is BROADCAST
  		   If all the slaves are PtP, master is PtP
  		   Otherwise, master is NBMA.
  		 */
  		if (!(slave->flags&IFF_POINTOPOINT))
  			flags &= ~IFF_POINTOPOINT;
  		if (!(slave->flags&IFF_BROADCAST))
  			flags &= ~IFF_BROADCAST;
  		if (!(slave->flags&IFF_MULTICAST))
  			flags &= ~IFF_MULTICAST;
  	} while ((q = NEXT_SLAVE(q)) != m->slaves);
  
  	m->dev->mtu = mtu;
  	m->dev->flags = (m->dev->flags&~FMASK) | flags;
  	netif_start_queue(m->dev);
  	return 0;
  }
  
  static int teql_master_close(struct net_device *dev)
  {
  	netif_stop_queue(dev);
  	return 0;
  }
bc1f44709   stephen hemminger   net: make ndo_get...
384
385
  static void teql_master_stats64(struct net_device *dev,
  				struct rtnl_link_stats64 *stats)
1ac9ad139   Eric Dumazet   net: remove dev_t...
386
387
388
389
390
391
392
  {
  	struct teql_master *m = netdev_priv(dev);
  
  	stats->tx_packets	= m->tx_packets;
  	stats->tx_bytes		= m->tx_bytes;
  	stats->tx_errors	= m->tx_errors;
  	stats->tx_dropped	= m->tx_dropped;
1ac9ad139   Eric Dumazet   net: remove dev_t...
393
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
  static int teql_master_mtu(struct net_device *dev, int new_mtu)
  {
2941a4863   Patrick McHardy   [NET]: Convert ne...
396
  	struct teql_master *m = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
  	struct Qdisc *q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
  	q = m->slaves;
  	if (q) {
  		do {
5ce2d488f   David S. Miller   pkt_sched: Remove...
401
  			if (new_mtu > qdisc_dev(q)->mtu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
  				return -EINVAL;
cc7ec456f   Eric Dumazet   net_sched: cleanups
403
  		} while ((q = NEXT_SLAVE(q)) != m->slaves);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
406
407
408
  	}
  
  	dev->mtu = new_mtu;
  	return 0;
  }
61294e2e2   Stephen Hemminger   sch_teql: convert...
409
410
411
412
  static const struct net_device_ops teql_netdev_ops = {
  	.ndo_open	= teql_master_open,
  	.ndo_stop	= teql_master_close,
  	.ndo_start_xmit	= teql_master_xmit,
1ac9ad139   Eric Dumazet   net: remove dev_t...
413
  	.ndo_get_stats64 = teql_master_stats64,
61294e2e2   Stephen Hemminger   sch_teql: convert...
414
415
  	.ndo_change_mtu	= teql_master_mtu,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
  static __init void teql_master_setup(struct net_device *dev)
  {
2941a4863   Patrick McHardy   [NET]: Convert ne...
418
  	struct teql_master *master = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
422
  	struct Qdisc_ops *ops = &master->qops;
  
  	master->dev	= dev;
  	ops->priv_size  = sizeof(struct teql_sched_data);
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
423

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
  	ops->enqueue	=	teql_enqueue;
  	ops->dequeue	=	teql_dequeue;
8e3af9789   Jarek Poplawski   pkt_sched: Add qd...
426
  	ops->peek	=	teql_peek;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
  	ops->init	=	teql_qdisc_init;
  	ops->reset	=	teql_reset;
  	ops->destroy	=	teql_destroy;
  	ops->owner	=	THIS_MODULE;
61294e2e2   Stephen Hemminger   sch_teql: convert...
431
  	dev->netdev_ops =       &teql_netdev_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
  	dev->type		= ARPHRD_VOID;
  	dev->mtu		= 1500;
91572088e   Jarod Wilson   net: use core MTU...
434
435
  	dev->min_mtu		= 68;
  	dev->max_mtu		= 65535;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
437
438
  	dev->tx_queue_len	= 100;
  	dev->flags		= IFF_NOARP;
  	dev->hard_header_len	= LL_MAX_HEADER;
028758788   Eric Dumazet   net: better IFF_X...
439
  	netif_keep_dst(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
  }
  
  static LIST_HEAD(master_dev_list);
  static int max_equalizers = 1;
  module_param(max_equalizers, int, 0);
  MODULE_PARM_DESC(max_equalizers, "Max number of link equalizers");
  
  static int __init teql_init(void)
  {
  	int i;
  	int err = -ENODEV;
  
  	for (i = 0; i < max_equalizers; i++) {
  		struct net_device *dev;
  		struct teql_master *master;
c835a6773   Tom Gundersen   net: set name_ass...
455
456
  		dev = alloc_netdev(sizeof(struct teql_master), "teql%d",
  				   NET_NAME_UNKNOWN, teql_master_setup);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
461
462
463
464
465
  		if (!dev) {
  			err = -ENOMEM;
  			break;
  		}
  
  		if ((err = register_netdev(dev))) {
  			free_netdev(dev);
  			break;
  		}
2941a4863   Patrick McHardy   [NET]: Convert ne...
466
  		master = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  
  		strlcpy(master->qops.id, dev->name, IFNAMSIZ);
  		err = register_qdisc(&master->qops);
  
  		if (err) {
  			unregister_netdev(dev);
  			free_netdev(dev);
  			break;
  		}
  
  		list_add_tail(&master->master_list, &master_dev_list);
  	}
  	return i ? 0 : err;
  }
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
481
  static void __exit teql_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  {
  	struct teql_master *master, *nxt;
  
  	list_for_each_entry_safe(master, nxt, &master_dev_list, master_list) {
  
  		list_del(&master->master_list);
  
  		unregister_qdisc(&master->qops);
  		unregister_netdev(master->dev);
  		free_netdev(master->dev);
  	}
  }
  
  module_init(teql_init);
  module_exit(teql_exit);
  
  MODULE_LICENSE("GPL");