xref: /linux/drivers/net/ifb.c (revision 6e8331ac6973435b1e7604c30f2ad394035b46e1)
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");
295