Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / drivers / net / ifb.c
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
38 #define TX_TIMEOUT  (2*HZ)
39                                                                                 
40 #define TX_Q_LIMIT    32
41 struct ifb_private {
42         struct net_device_stats stats;
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
59 static int numifbs = 2;
60
61 static void ri_tasklet(unsigned long dev);
62 static int ifb_xmit(struct sk_buff *skb, struct net_device *dev);
63 static struct net_device_stats *ifb_get_stats(struct net_device *dev);
64 static int ifb_open(struct net_device *dev);
65 static int ifb_close(struct net_device *dev);
66
67 static void ri_tasklet(unsigned long dev) 
68 {
69
70         struct net_device *_dev = (struct net_device *)dev;
71         struct ifb_private *dp = netdev_priv(_dev);
72         struct net_device_stats *stats = &dp->stats;
73         struct sk_buff *skb;
74
75         dp->st_task_enter++;
76         if ((skb = skb_peek(&dp->tq)) == NULL) {
77                 dp->st_txq_refl_try++;
78                 if (netif_tx_trylock(_dev)) {
79                         dp->st_rxq_enter++;
80                         while ((skb = skb_dequeue(&dp->rq)) != NULL) {
81                                 skb_queue_tail(&dp->tq, skb);
82                                 dp->st_rx2tx_tran++;
83                         }
84                         netif_tx_unlock(_dev);
85                 } else {
86                         /* reschedule */
87                         dp->st_rxq_notenter++;
88                         goto resched;
89                 }
90         }
91
92         while ((skb = skb_dequeue(&dp->tq)) != NULL) {
93                 u32 from = G_TC_FROM(skb->tc_verd);
94
95                 skb->tc_verd = 0;
96                 skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
97                 stats->tx_packets++;
98                 stats->tx_bytes +=skb->len;
99                 if (from & AT_EGRESS) {
100                         dp->st_rx_frm_egr++;
101                         dev_queue_xmit(skb);
102                 } else if (from & AT_INGRESS) {
103
104                         dp->st_rx_frm_ing++;
105                         netif_rx(skb);
106                 } else {
107                         dev_kfree_skb(skb);
108                         stats->tx_dropped++;
109                 }
110         }
111
112         if (netif_tx_trylock(_dev)) {
113                 dp->st_rxq_check++;
114                 if ((skb = skb_peek(&dp->rq)) == NULL) {
115                         dp->tasklet_pending = 0;
116                         if (netif_queue_stopped(_dev))
117                                 netif_wake_queue(_dev);
118                 } else {
119                         dp->st_rxq_rsch++;
120                         netif_tx_unlock(_dev);
121                         goto resched;
122                 }
123                 netif_tx_unlock(_dev);
124         } else {
125 resched:
126                 dp->tasklet_pending = 1;
127                 tasklet_schedule(&dp->ifb_tasklet);
128         }
129
130 }
131
132 static void __init ifb_setup(struct net_device *dev)
133 {
134         /* Initialize the device structure. */
135         dev->get_stats = ifb_get_stats;
136         dev->hard_start_xmit = ifb_xmit;
137         dev->open = &ifb_open;
138         dev->stop = &ifb_close;
139
140         /* Fill in device structure with ethernet-generic values. */
141         ether_setup(dev);
142         dev->tx_queue_len = TX_Q_LIMIT;
143         dev->change_mtu = NULL;
144         dev->flags |= IFF_NOARP;
145         dev->flags &= ~IFF_MULTICAST;
146         SET_MODULE_OWNER(dev);
147         random_ether_addr(dev->dev_addr);
148 }
149
150 static int ifb_xmit(struct sk_buff *skb, struct net_device *dev)
151 {
152         struct ifb_private *dp = netdev_priv(dev);
153         struct net_device_stats *stats = &dp->stats;
154         int ret = 0;
155         u32 from = G_TC_FROM(skb->tc_verd);
156
157         stats->tx_packets++;
158         stats->tx_bytes+=skb->len;
159
160         if (!from || !skb->input_dev) {
161 dropped:
162                 dev_kfree_skb(skb);
163                 stats->rx_dropped++;
164                 return ret;
165         } else {
166                 /* 
167                  * note we could be going
168                  * ingress -> egress or
169                  * egress -> ingress
170                 */
171                 skb->dev = skb->input_dev;
172                 skb->input_dev = dev;
173                 if (from & AT_INGRESS) {
174                         skb_pull(skb, skb->dev->hard_header_len);
175                 } else {
176                         if (!(from & AT_EGRESS)) {
177                                 goto dropped;
178                         }
179                 }
180         }
181
182         if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
183                 netif_stop_queue(dev);
184         }
185
186         dev->trans_start = jiffies;
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 ret;
194 }
195
196 static struct net_device_stats *ifb_get_stats(struct net_device *dev)
197 {
198         struct ifb_private *dp = netdev_priv(dev);
199         struct net_device_stats *stats = &dp->stats;
200
201         pr_debug("tasklets stats %ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld \n",
202                 dp->st_task_enter, dp->st_txq_refl_try, dp->st_rxq_enter, 
203                 dp->st_rx2tx_tran dp->st_rxq_notenter, dp->st_rx_frm_egr,
204                 dp->st_rx_frm_ing, dp->st_rxq_check, dp->st_rxq_rsch );
205
206         return stats;
207 }
208
209 static struct net_device **ifbs;
210
211 /* Number of ifb devices to be set up by this module. */
212 module_param(numifbs, int, 0);
213 MODULE_PARM_DESC(numifbs, "Number of ifb devices");
214
215 static int ifb_close(struct net_device *dev)
216 {
217         struct ifb_private *dp = netdev_priv(dev);
218
219         tasklet_kill(&dp->ifb_tasklet);
220         netif_stop_queue(dev);
221         skb_queue_purge(&dp->rq);
222         skb_queue_purge(&dp->tq);
223         return 0;
224 }
225
226 static int ifb_open(struct net_device *dev)
227 {
228         struct ifb_private *dp = netdev_priv(dev);
229
230         tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
231         skb_queue_head_init(&dp->rq);
232         skb_queue_head_init(&dp->tq);
233         netif_start_queue(dev);
234
235         return 0;
236 }
237
238 static int __init ifb_init_one(int index)
239 {
240         struct net_device *dev_ifb;
241         int err;
242
243         dev_ifb = alloc_netdev(sizeof(struct ifb_private),
244                                  "ifb%d", ifb_setup);
245
246         if (!dev_ifb)
247                 return -ENOMEM;
248
249         if ((err = register_netdev(dev_ifb))) {
250                 free_netdev(dev_ifb);
251                 dev_ifb = NULL;
252         } else {
253                 ifbs[index] = dev_ifb; 
254         }
255
256         return err;
257 }
258
259 static void ifb_free_one(int index)
260 {
261         unregister_netdev(ifbs[index]);
262         free_netdev(ifbs[index]);
263
264
265 static int __init ifb_init_module(void)
266
267         int i, err = 0;
268         ifbs = kmalloc(numifbs * sizeof(void *), GFP_KERNEL); 
269         if (!ifbs)
270                 return -ENOMEM; 
271         for (i = 0; i < numifbs && !err; i++)
272                 err = ifb_init_one(i); 
273         if (err) { 
274                 i--;
275                 while (--i >= 0)
276                         ifb_free_one(i);
277         }
278
279         return err;
280
281
282 static 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
291 module_init(ifb_init_module);
292 module_exit(ifb_cleanup_module);
293 MODULE_LICENSE("GPL");
294 MODULE_AUTHOR("Jamal Hadi Salim");