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