Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-serial
[linux-2.6] / 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/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
42 struct 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
60 static int numifbs = 2;
61
62 static void ri_tasklet(unsigned long dev);
63 static int ifb_xmit(struct sk_buff *skb, struct net_device *dev);
64 static struct net_device_stats *ifb_get_stats(struct net_device *dev);
65 static int ifb_open(struct net_device *dev);
66 static int ifb_close(struct net_device *dev);
67
68 static 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 (netif_tx_trylock(_dev)) {
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(_dev);
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 (netif_tx_trylock(_dev)) {
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                         netif_tx_unlock(_dev);
122                         goto resched;
123                 }
124                 netif_tx_unlock(_dev);
125         } else {
126 resched:
127                 dp->tasklet_pending = 1;
128                 tasklet_schedule(&dp->ifb_tasklet);
129         }
130
131 }
132
133 static 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
151 static 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) {
162 dropped:
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
197 static 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
210 static struct net_device **ifbs;
211
212 /* Number of ifb devices to be set up by this module. */
213 module_param(numifbs, int, 0);
214 MODULE_PARM_DESC(numifbs, "Number of ifb devices");
215
216 static 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
227 static 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
239 static 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
260 static void ifb_free_one(int index)
261 {
262         unregister_netdev(ifbs[index]);
263         free_netdev(ifbs[index]);
264
265
266 static 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
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");