ifb.c revision 253af4235d24ddfcd9f5403485e9273b33d8fa5e
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/config.h>
31#include <linux/module.h>
32#include <linux/kernel.h>
33#include <linux/netdevice.h>
34#include <linux/etherdevice.h>
35#include <linux/init.h>
36#include <linux/moduleparam.h>
37#include <net/pkt_sched.h>
38
39#define TX_TIMEOUT  (2*HZ)
40
41#define TX_Q_LIMIT    32
42struct ifb_private {
43	struct net_device_stats stats;
44	struct tasklet_struct   ifb_tasklet;
45	int     tasklet_pending;
46	/* mostly debug stats leave in for now */
47	unsigned long   st_task_enter; /* tasklet entered */
48	unsigned long   st_txq_refl_try; /* transmit queue refill attempt */
49	unsigned long   st_rxq_enter; /* receive queue entered */
50	unsigned long   st_rx2tx_tran; /* receive to trasmit transfers */
51	unsigned long   st_rxq_notenter; /*receiveQ not entered, resched */
52	unsigned long   st_rx_frm_egr; /* received from egress path */
53	unsigned long   st_rx_frm_ing; /* received from ingress path */
54	unsigned long   st_rxq_check;
55	unsigned long   st_rxq_rsch;
56	struct sk_buff_head     rq;
57	struct sk_buff_head     tq;
58};
59
60static int numifbs = 1;
61
62static void ri_tasklet(unsigned long dev);
63static int ifb_xmit(struct sk_buff *skb, struct net_device *dev);
64static struct net_device_stats *ifb_get_stats(struct net_device *dev);
65static int ifb_open(struct net_device *dev);
66static int ifb_close(struct net_device *dev);
67
68static void ri_tasklet(unsigned long dev)
69{
70
71	struct net_device *_dev = (struct net_device *)dev;
72	struct ifb_private *dp = netdev_priv(_dev);
73	struct net_device_stats *stats = &dp->stats;
74	struct sk_buff *skb;
75
76	dp->st_task_enter++;
77	if ((skb = skb_peek(&dp->tq)) == NULL) {
78		dp->st_txq_refl_try++;
79		if (spin_trylock(&_dev->xmit_lock)) {
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			spin_unlock(&_dev->xmit_lock);
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		if (from & AT_EGRESS) {
101			dp->st_rx_frm_egr++;
102			dev_queue_xmit(skb);
103		} else if (from & AT_INGRESS) {
104
105			dp->st_rx_frm_ing++;
106			netif_rx(skb);
107		} else {
108			dev_kfree_skb(skb);
109			stats->tx_dropped++;
110		}
111	}
112
113	if (spin_trylock(&_dev->xmit_lock)) {
114		dp->st_rxq_check++;
115		if ((skb = skb_peek(&dp->rq)) == NULL) {
116			dp->tasklet_pending = 0;
117			if (netif_queue_stopped(_dev))
118				netif_wake_queue(_dev);
119		} else {
120			dp->st_rxq_rsch++;
121			spin_unlock(&_dev->xmit_lock);
122			goto resched;
123		}
124		spin_unlock(&_dev->xmit_lock);
125	} else {
126resched:
127		dp->tasklet_pending = 1;
128		tasklet_schedule(&dp->ifb_tasklet);
129	}
130
131}
132
133static void __init ifb_setup(struct net_device *dev)
134{
135	/* Initialize the device structure. */
136	dev->get_stats = ifb_get_stats;
137	dev->hard_start_xmit = ifb_xmit;
138	dev->open = &ifb_open;
139	dev->stop = &ifb_close;
140
141	/* Fill in device structure with ethernet-generic values. */
142	ether_setup(dev);
143	dev->tx_queue_len = TX_Q_LIMIT;
144	dev->change_mtu = NULL;
145	dev->flags |= IFF_NOARP;
146	dev->flags &= ~IFF_MULTICAST;
147	SET_MODULE_OWNER(dev);
148	random_ether_addr(dev->dev_addr);
149}
150
151static int ifb_xmit(struct sk_buff *skb, struct net_device *dev)
152{
153	struct ifb_private *dp = netdev_priv(dev);
154	struct net_device_stats *stats = &dp->stats;
155	int ret = 0;
156	u32 from = G_TC_FROM(skb->tc_verd);
157
158	stats->tx_packets++;
159	stats->tx_bytes+=skb->len;
160
161	if (!from || !skb->input_dev) {
162dropped:
163		dev_kfree_skb(skb);
164		stats->rx_dropped++;
165		return ret;
166	} else {
167		/*
168		 * note we could be going
169		 * ingress -> egress or
170		 * egress -> ingress
171		*/
172		skb->dev = skb->input_dev;
173		skb->input_dev = dev;
174		if (from & AT_INGRESS) {
175			skb_pull(skb, skb->dev->hard_header_len);
176		} else {
177			if (!(from & AT_EGRESS)) {
178				goto dropped;
179			}
180		}
181	}
182
183	if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
184		netif_stop_queue(dev);
185	}
186
187	dev->trans_start = jiffies;
188	skb_queue_tail(&dp->rq, skb);
189	if (!dp->tasklet_pending) {
190		dp->tasklet_pending = 1;
191		tasklet_schedule(&dp->ifb_tasklet);
192	}
193
194	return ret;
195}
196
197static struct net_device_stats *ifb_get_stats(struct net_device *dev)
198{
199	struct ifb_private *dp = netdev_priv(dev);
200	struct net_device_stats *stats = &dp->stats;
201
202	pr_debug("tasklets stats %ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld \n",
203		dp->st_task_enter, dp->st_txq_refl_try, dp->st_rxq_enter,
204		dp->st_rx2tx_tran dp->st_rxq_notenter, dp->st_rx_frm_egr,
205		dp->st_rx_frm_ing, dp->st_rxq_check, dp->st_rxq_rsch );
206
207	return stats;
208}
209
210static struct net_device **ifbs;
211
212/* Number of ifb devices to be set up by this module. */
213module_param(numifbs, int, 0);
214MODULE_PARM_DESC(numifbs, "Number of ifb devices");
215
216static int ifb_close(struct net_device *dev)
217{
218	struct ifb_private *dp = netdev_priv(dev);
219
220	tasklet_kill(&dp->ifb_tasklet);
221	netif_stop_queue(dev);
222	skb_queue_purge(&dp->rq);
223	skb_queue_purge(&dp->tq);
224	return 0;
225}
226
227static int ifb_open(struct net_device *dev)
228{
229	struct ifb_private *dp = netdev_priv(dev);
230
231	tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
232	skb_queue_head_init(&dp->rq);
233	skb_queue_head_init(&dp->tq);
234	netif_start_queue(dev);
235
236	return 0;
237}
238
239static int __init ifb_init_one(int index)
240{
241	struct net_device *dev_ifb;
242	int err;
243
244	dev_ifb = alloc_netdev(sizeof(struct ifb_private),
245				 "ifb%d", ifb_setup);
246
247	if (!dev_ifb)
248		return -ENOMEM;
249
250	if ((err = register_netdev(dev_ifb))) {
251		free_netdev(dev_ifb);
252		dev_ifb = NULL;
253	} else {
254		ifbs[index] = dev_ifb;
255	}
256
257	return err;
258}
259
260static void ifb_free_one(int index)
261{
262	unregister_netdev(ifbs[index]);
263	free_netdev(ifbs[index]);
264}
265
266static int __init ifb_init_module(void)
267{
268	int i, err = 0;
269	ifbs = kmalloc(numifbs * sizeof(void *), GFP_KERNEL);
270	if (!ifbs)
271		return -ENOMEM;
272	for (i = 0; i < numifbs && !err; i++)
273		err = ifb_init_one(i);
274	if (err) {
275		while (--i >= 0)
276			ifb_free_one(i);
277	}
278
279	return err;
280}
281
282static void __exit ifb_cleanup_module(void)
283{
284	int i;
285
286	for (i = 0; i < numifbs; i++)
287		ifb_free_one(i);
288	kfree(ifbs);
289}
290
291module_init(ifb_init_module);
292module_exit(ifb_cleanup_module);
293MODULE_LICENSE("GPL");
294MODULE_AUTHOR("Jamal Hadi Salim");
295