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