• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/net/
1/* drivers/net/ifb.c:
2
3	The purpose of this driver is to provide a device that allows
4	for sharing of resources:
5
6	1) qdiscs/policies that are per device as opposed to system wide.
7	ifb allows for a device which can be redirected to thus providing
8	an impression of sharing.
9
10	2) Allows for queueing incoming traffic for shaping instead of
11	dropping.
12
13	The original concept is based on what is known as the IMQ
14	driver initially written by Martin Devera, later rewritten
15	by Patrick McHardy and then maintained by Andre Correa.
16
17	You need the tc action  mirror or redirect to feed this device
18       	packets.
19
20	This program is free software; you can redistribute it and/or
21	modify it under the terms of the GNU General Public License
22	as published by the Free Software Foundation; either version
23	2 of the License, or (at your option) any later version.
24
25  	Authors:	Jamal Hadi Salim (2005)
26
27*/
28
29
30#include <linux/module.h>
31#include <linux/kernel.h>
32#include <linux/netdevice.h>
33#include <linux/etherdevice.h>
34#include <linux/init.h>
35#include <linux/moduleparam.h>
36#include <net/pkt_sched.h>
37#include <net/net_namespace.h>
38
39#define TX_TIMEOUT  (2*HZ)
40
41#define TX_Q_LIMIT    32
42struct ifb_private {
43	struct tasklet_struct   ifb_tasklet;
44	int     tasklet_pending;
45	/* mostly debug stats leave in for now */
46	unsigned long   st_task_enter; /* tasklet entered */
47	unsigned long   st_txq_refl_try; /* transmit queue refill attempt */
48	unsigned long   st_rxq_enter; /* receive queue entered */
49	unsigned long   st_rx2tx_tran; /* receive to trasmit transfers */
50	unsigned long   st_rxq_notenter; /*receiveQ not entered, resched */
51	unsigned long   st_rx_frm_egr; /* received from egress path */
52	unsigned long   st_rx_frm_ing; /* received from ingress path */
53	unsigned long   st_rxq_check;
54	unsigned long   st_rxq_rsch;
55	struct sk_buff_head     rq;
56	struct sk_buff_head     tq;
57};
58
59static int numifbs = 2;
60
61static void ri_tasklet(unsigned long dev);
62static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev);
63static int ifb_open(struct net_device *dev);
64static int ifb_close(struct net_device *dev);
65
66static void ri_tasklet(unsigned long dev)
67{
68
69	struct net_device *_dev = (struct net_device *)dev;
70	struct ifb_private *dp = netdev_priv(_dev);
71	struct net_device_stats *stats = &_dev->stats;
72	struct netdev_queue *txq;
73	struct sk_buff *skb;
74
75	txq = netdev_get_tx_queue(_dev, 0);
76	dp->st_task_enter++;
77	if ((skb = skb_peek(&dp->tq)) == NULL) {
78		dp->st_txq_refl_try++;
79		if (__netif_tx_trylock(txq)) {
80			dp->st_rxq_enter++;
81			while ((skb = skb_dequeue(&dp->rq)) != NULL) {
82				skb_queue_tail(&dp->tq, skb);
83				dp->st_rx2tx_tran++;
84			}
85			__netif_tx_unlock(txq);
86		} else {
87			/* reschedule */
88			dp->st_rxq_notenter++;
89			goto resched;
90		}
91	}
92
93	while ((skb = skb_dequeue(&dp->tq)) != NULL) {
94		u32 from = G_TC_FROM(skb->tc_verd);
95
96		skb->tc_verd = 0;
97		skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
98		stats->tx_packets++;
99		stats->tx_bytes +=skb->len;
100
101		rcu_read_lock();
102		skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif);
103		if (!skb->dev) {
104			rcu_read_unlock();
105			dev_kfree_skb(skb);
106			stats->tx_dropped++;
107			if (skb_queue_len(&dp->tq) != 0)
108				goto resched;
109			break;
110		}
111		rcu_read_unlock();
112		skb->skb_iif = _dev->ifindex;
113
114		if (from & AT_EGRESS) {
115			dp->st_rx_frm_egr++;
116			dev_queue_xmit(skb);
117		} else if (from & AT_INGRESS) {
118			dp->st_rx_frm_ing++;
119			skb_pull(skb, skb->dev->hard_header_len);
120			netif_rx(skb);
121		} else
122			BUG();
123	}
124
125	if (__netif_tx_trylock(txq)) {
126		dp->st_rxq_check++;
127		if ((skb = skb_peek(&dp->rq)) == NULL) {
128			dp->tasklet_pending = 0;
129			if (netif_queue_stopped(_dev))
130				netif_wake_queue(_dev);
131		} else {
132			dp->st_rxq_rsch++;
133			__netif_tx_unlock(txq);
134			goto resched;
135		}
136		__netif_tx_unlock(txq);
137	} else {
138resched:
139		dp->tasklet_pending = 1;
140		tasklet_schedule(&dp->ifb_tasklet);
141	}
142
143}
144
145static const struct net_device_ops ifb_netdev_ops = {
146	.ndo_open	= ifb_open,
147	.ndo_stop	= ifb_close,
148	.ndo_start_xmit	= ifb_xmit,
149	.ndo_validate_addr = eth_validate_addr,
150};
151
152static void ifb_setup(struct net_device *dev)
153{
154	/* Initialize the device structure. */
155	dev->destructor = free_netdev;
156	dev->netdev_ops = &ifb_netdev_ops;
157
158	/* Fill in device structure with ethernet-generic values. */
159	ether_setup(dev);
160	dev->tx_queue_len = TX_Q_LIMIT;
161
162	dev->flags |= IFF_NOARP;
163	dev->flags &= ~IFF_MULTICAST;
164	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
165	random_ether_addr(dev->dev_addr);
166}
167
168static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
169{
170	struct ifb_private *dp = netdev_priv(dev);
171	struct net_device_stats *stats = &dev->stats;
172	u32 from = G_TC_FROM(skb->tc_verd);
173
174	stats->rx_packets++;
175	stats->rx_bytes+=skb->len;
176
177	if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
178		dev_kfree_skb(skb);
179		stats->rx_dropped++;
180		return NETDEV_TX_OK;
181	}
182
183	if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
184		netif_stop_queue(dev);
185	}
186
187	skb_queue_tail(&dp->rq, skb);
188	if (!dp->tasklet_pending) {
189		dp->tasklet_pending = 1;
190		tasklet_schedule(&dp->ifb_tasklet);
191	}
192
193	return NETDEV_TX_OK;
194}
195
196static int ifb_close(struct net_device *dev)
197{
198	struct ifb_private *dp = netdev_priv(dev);
199
200	tasklet_kill(&dp->ifb_tasklet);
201	netif_stop_queue(dev);
202	skb_queue_purge(&dp->rq);
203	skb_queue_purge(&dp->tq);
204	return 0;
205}
206
207static int ifb_open(struct net_device *dev)
208{
209	struct ifb_private *dp = netdev_priv(dev);
210
211	tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
212	skb_queue_head_init(&dp->rq);
213	skb_queue_head_init(&dp->tq);
214	netif_start_queue(dev);
215
216	return 0;
217}
218
219static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
220{
221	if (tb[IFLA_ADDRESS]) {
222		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
223			return -EINVAL;
224		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
225			return -EADDRNOTAVAIL;
226	}
227	return 0;
228}
229
230static struct rtnl_link_ops ifb_link_ops __read_mostly = {
231	.kind		= "ifb",
232	.priv_size	= sizeof(struct ifb_private),
233	.setup		= ifb_setup,
234	.validate	= ifb_validate,
235};
236
237/* Number of ifb devices to be set up by this module. */
238module_param(numifbs, int, 0);
239MODULE_PARM_DESC(numifbs, "Number of ifb devices");
240
241static int __init ifb_init_one(int index)
242{
243	struct net_device *dev_ifb;
244	int err;
245
246	dev_ifb = alloc_netdev(sizeof(struct ifb_private),
247				 "ifb%d", ifb_setup);
248
249	if (!dev_ifb)
250		return -ENOMEM;
251
252	err = dev_alloc_name(dev_ifb, dev_ifb->name);
253	if (err < 0)
254		goto err;
255
256	dev_ifb->rtnl_link_ops = &ifb_link_ops;
257	err = register_netdevice(dev_ifb);
258	if (err < 0)
259		goto err;
260
261	return 0;
262
263err:
264	free_netdev(dev_ifb);
265	return err;
266}
267
268static int __init ifb_init_module(void)
269{
270	int i, err;
271
272	rtnl_lock();
273	err = __rtnl_link_register(&ifb_link_ops);
274
275	for (i = 0; i < numifbs && !err; i++)
276		err = ifb_init_one(i);
277	if (err)
278		__rtnl_link_unregister(&ifb_link_ops);
279	rtnl_unlock();
280
281	return err;
282}
283
284static void __exit ifb_cleanup_module(void)
285{
286	rtnl_link_unregister(&ifb_link_ops);
287}
288
289module_init(ifb_init_module);
290module_exit(ifb_cleanup_module);
291MODULE_LICENSE("GPL");
292MODULE_AUTHOR("Jamal Hadi Salim");
293MODULE_ALIAS_RTNL_LINK("ifb");
294