ifb.c revision 8dfcdf342d9e8294a3292005f9158022289dfd67
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 int 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		skb->dev = __dev_get_by_index(&init_net, skb->iif);
102		if (!skb->dev) {
103			dev_kfree_skb(skb);
104			stats->tx_dropped++;
105			break;
106		}
107		skb->iif = _dev->ifindex;
108
109		if (from & AT_EGRESS) {
110			dp->st_rx_frm_egr++;
111			dev_queue_xmit(skb);
112		} else if (from & AT_INGRESS) {
113			dp->st_rx_frm_ing++;
114			skb_pull(skb, skb->dev->hard_header_len);
115			netif_rx(skb);
116		} else
117			BUG();
118	}
119
120	if (__netif_tx_trylock(txq)) {
121		dp->st_rxq_check++;
122		if ((skb = skb_peek(&dp->rq)) == NULL) {
123			dp->tasklet_pending = 0;
124			if (netif_queue_stopped(_dev))
125				netif_wake_queue(_dev);
126		} else {
127			dp->st_rxq_rsch++;
128			__netif_tx_unlock(txq);
129			goto resched;
130		}
131		__netif_tx_unlock(txq);
132	} else {
133resched:
134		dp->tasklet_pending = 1;
135		tasklet_schedule(&dp->ifb_tasklet);
136	}
137
138}
139
140static const struct net_device_ops ifb_netdev_ops = {
141	.ndo_validate_addr = eth_validate_addr,
142	.ndo_open	= ifb_open,
143	.ndo_stop	= ifb_close,
144};
145
146static void ifb_setup(struct net_device *dev)
147{
148	/* Initialize the device structure. */
149	dev->hard_start_xmit = ifb_xmit;
150	dev->destructor = free_netdev;
151	dev->netdev_ops = &ifb_netdev_ops;
152
153	/* Fill in device structure with ethernet-generic values. */
154	ether_setup(dev);
155	dev->tx_queue_len = TX_Q_LIMIT;
156
157	dev->flags |= IFF_NOARP;
158	dev->flags &= ~IFF_MULTICAST;
159	random_ether_addr(dev->dev_addr);
160}
161
162static int ifb_xmit(struct sk_buff *skb, struct net_device *dev)
163{
164	struct ifb_private *dp = netdev_priv(dev);
165	struct net_device_stats *stats = &dev->stats;
166	int ret = 0;
167	u32 from = G_TC_FROM(skb->tc_verd);
168
169	stats->rx_packets++;
170	stats->rx_bytes+=skb->len;
171
172	if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) {
173		dev_kfree_skb(skb);
174		stats->rx_dropped++;
175		return ret;
176	}
177
178	if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
179		netif_stop_queue(dev);
180	}
181
182	dev->trans_start = jiffies;
183	skb_queue_tail(&dp->rq, skb);
184	if (!dp->tasklet_pending) {
185		dp->tasklet_pending = 1;
186		tasklet_schedule(&dp->ifb_tasklet);
187	}
188
189	return ret;
190}
191
192static int ifb_close(struct net_device *dev)
193{
194	struct ifb_private *dp = netdev_priv(dev);
195
196	tasklet_kill(&dp->ifb_tasklet);
197	netif_stop_queue(dev);
198	skb_queue_purge(&dp->rq);
199	skb_queue_purge(&dp->tq);
200	return 0;
201}
202
203static int ifb_open(struct net_device *dev)
204{
205	struct ifb_private *dp = netdev_priv(dev);
206
207	tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
208	skb_queue_head_init(&dp->rq);
209	skb_queue_head_init(&dp->tq);
210	netif_start_queue(dev);
211
212	return 0;
213}
214
215static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
216{
217	if (tb[IFLA_ADDRESS]) {
218		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
219			return -EINVAL;
220		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
221			return -EADDRNOTAVAIL;
222	}
223	return 0;
224}
225
226static struct rtnl_link_ops ifb_link_ops __read_mostly = {
227	.kind		= "ifb",
228	.priv_size	= sizeof(struct ifb_private),
229	.setup		= ifb_setup,
230	.validate	= ifb_validate,
231};
232
233/* Number of ifb devices to be set up by this module. */
234module_param(numifbs, int, 0);
235MODULE_PARM_DESC(numifbs, "Number of ifb devices");
236
237static int __init ifb_init_one(int index)
238{
239	struct net_device *dev_ifb;
240	int err;
241
242	dev_ifb = alloc_netdev(sizeof(struct ifb_private),
243				 "ifb%d", ifb_setup);
244
245	if (!dev_ifb)
246		return -ENOMEM;
247
248	err = dev_alloc_name(dev_ifb, dev_ifb->name);
249	if (err < 0)
250		goto err;
251
252	dev_ifb->rtnl_link_ops = &ifb_link_ops;
253	err = register_netdevice(dev_ifb);
254	if (err < 0)
255		goto err;
256
257	return 0;
258
259err:
260	free_netdev(dev_ifb);
261	return err;
262}
263
264static int __init ifb_init_module(void)
265{
266	int i, err;
267
268	rtnl_lock();
269	err = __rtnl_link_register(&ifb_link_ops);
270
271	for (i = 0; i < numifbs && !err; i++)
272		err = ifb_init_one(i);
273	if (err)
274		__rtnl_link_unregister(&ifb_link_ops);
275	rtnl_unlock();
276
277	return err;
278}
279
280static void __exit ifb_cleanup_module(void)
281{
282	rtnl_link_unregister(&ifb_link_ops);
283}
284
285module_init(ifb_init_module);
286module_exit(ifb_cleanup_module);
287MODULE_LICENSE("GPL");
288MODULE_AUTHOR("Jamal Hadi Salim");
289MODULE_ALIAS_RTNL_LINK("ifb");
290