blob: 46b5f5fd686bab98b98aff86fbf3e43b0702ddb8 [file] [log] [blame]
Jeff Garzik6aa20a22006-09-13 13:24:59 -04001/* drivers/net/ifb.c:
Jamal Hadi Salim253af422006-01-08 22:34:25 -08002
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
Jeff Garzik6aa20a22006-09-13 13:24:59 -040011 dropping.
12
Jamal Hadi Salim253af422006-01-08 22:34:25 -080013 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.
Jeff Garzik6aa20a22006-09-13 13:24:59 -040024
Jamal Hadi Salim253af422006-01-08 22:34:25 -080025 Authors: Jamal Hadi Salim (2005)
Jeff Garzik6aa20a22006-09-13 13:24:59 -040026
Jamal Hadi Salim253af422006-01-08 22:34:25 -080027*/
28
29
Jamal Hadi Salim253af422006-01-08 22:34:25 -080030#include <linux/module.h>
31#include <linux/kernel.h>
32#include <linux/netdevice.h>
33#include <linux/etherdevice.h>
34#include <linux/init.h>
Alexey Dobriyana6b7a402011-06-06 10:43:46 +000035#include <linux/interrupt.h>
Jamal Hadi Salim253af422006-01-08 22:34:25 -080036#include <linux/moduleparam.h>
Jeff Garzik6aa20a22006-09-13 13:24:59 -040037#include <net/pkt_sched.h>
Eric W. Biederman881d9662007-09-17 11:56:21 -070038#include <net/net_namespace.h>
Jamal Hadi Salim253af422006-01-08 22:34:25 -080039
Jamal Hadi Salim253af422006-01-08 22:34:25 -080040#define TX_Q_LIMIT 32
41struct ifb_private {
Jamal Hadi Salim253af422006-01-08 22:34:25 -080042 struct tasklet_struct ifb_tasklet;
43 int tasklet_pending;
stephen hemminger3b0c9cb2011-06-20 11:42:30 +000044
45 struct u64_stats_sync rsync;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080046 struct sk_buff_head rq;
stephen hemminger3b0c9cb2011-06-20 11:42:30 +000047 u64 rx_packets;
48 u64 rx_bytes;
49
50 struct u64_stats_sync tsync;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080051 struct sk_buff_head tq;
stephen hemminger3b0c9cb2011-06-20 11:42:30 +000052 u64 tx_packets;
53 u64 tx_bytes;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080054};
55
Richard Lucassen35eaa312006-02-23 16:23:51 -080056static int numifbs = 2;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080057
58static void ri_tasklet(unsigned long dev);
Stephen Hemminger424efe92009-08-31 19:50:51 +000059static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -080060static int ifb_open(struct net_device *dev);
61static int ifb_close(struct net_device *dev);
62
Jeff Garzik6aa20a22006-09-13 13:24:59 -040063static void ri_tasklet(unsigned long dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -080064{
Jamal Hadi Salim253af422006-01-08 22:34:25 -080065 struct net_device *_dev = (struct net_device *)dev;
66 struct ifb_private *dp = netdev_priv(_dev);
David S. Millerc3f26a22008-07-31 16:58:50 -070067 struct netdev_queue *txq;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080068 struct sk_buff *skb;
69
David S. Millerc3f26a22008-07-31 16:58:50 -070070 txq = netdev_get_tx_queue(_dev, 0);
Jamal Hadi Salim253af422006-01-08 22:34:25 -080071 if ((skb = skb_peek(&dp->tq)) == NULL) {
David S. Millerc3f26a22008-07-31 16:58:50 -070072 if (__netif_tx_trylock(txq)) {
Changli Gao957fca92010-12-04 15:01:52 +000073 skb_queue_splice_tail_init(&dp->rq, &dp->tq);
David S. Millerc3f26a22008-07-31 16:58:50 -070074 __netif_tx_unlock(txq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -080075 } else {
76 /* reschedule */
Jamal Hadi Salim253af422006-01-08 22:34:25 -080077 goto resched;
78 }
79 }
80
Eric Dumazet7edc3452010-12-15 23:52:55 +000081 while ((skb = __skb_dequeue(&dp->tq)) != NULL) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -080082 u32 from = G_TC_FROM(skb->tc_verd);
83
84 skb->tc_verd = 0;
85 skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +000086
87 u64_stats_update_begin(&dp->tsync);
88 dp->tx_packets++;
89 dp->tx_bytes += skb->len;
90 u64_stats_update_end(&dp->tsync);
Patrick McHardyc01003c2007-03-29 11:46:52 -070091
Eric Dumazet05e86892009-11-01 19:45:16 +000092 rcu_read_lock();
Eric Dumazet8964be42009-11-20 15:35:04 -080093 skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif);
Patrick McHardyc01003c2007-03-29 11:46:52 -070094 if (!skb->dev) {
Eric Dumazet05e86892009-11-01 19:45:16 +000095 rcu_read_unlock();
Patrick McHardyc01003c2007-03-29 11:46:52 -070096 dev_kfree_skb(skb);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +000097 _dev->stats.tx_dropped++;
Changli Gao75c1c822010-12-04 14:09:08 +000098 if (skb_queue_len(&dp->tq) != 0)
99 goto resched;
Patrick McHardyc01003c2007-03-29 11:46:52 -0700100 break;
101 }
Eric Dumazet05e86892009-11-01 19:45:16 +0000102 rcu_read_unlock();
Eric Dumazet8964be42009-11-20 15:35:04 -0800103 skb->skb_iif = _dev->ifindex;
Patrick McHardyc01003c2007-03-29 11:46:52 -0700104
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800105 if (from & AT_EGRESS) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800106 dev_queue_xmit(skb);
107 } else if (from & AT_INGRESS) {
Patrick McHardyc01003c2007-03-29 11:46:52 -0700108 skb_pull(skb, skb->dev->hard_header_len);
Eric Dumazet1a759722010-12-14 22:39:58 +0000109 netif_receive_skb(skb);
Patrick McHardyc01003c2007-03-29 11:46:52 -0700110 } else
111 BUG();
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800112 }
113
David S. Millerc3f26a22008-07-31 16:58:50 -0700114 if (__netif_tx_trylock(txq)) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800115 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 {
David S. Millerc3f26a22008-07-31 16:58:50 -0700120 __netif_tx_unlock(txq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800121 goto resched;
122 }
David S. Millerc3f26a22008-07-31 16:58:50 -0700123 __netif_tx_unlock(txq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800124 } else {
125resched:
126 dp->tasklet_pending = 1;
127 tasklet_schedule(&dp->ifb_tasklet);
128 }
129
130}
131
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000132static struct rtnl_link_stats64 *ifb_stats64(struct net_device *dev,
133 struct rtnl_link_stats64 *stats)
134{
135 struct ifb_private *dp = netdev_priv(dev);
136 unsigned int start;
137
138 do {
139 start = u64_stats_fetch_begin_bh(&dp->rsync);
140 stats->rx_packets = dp->rx_packets;
141 stats->rx_bytes = dp->rx_bytes;
142 } while (u64_stats_fetch_retry_bh(&dp->rsync, start));
143
144 do {
145 start = u64_stats_fetch_begin_bh(&dp->tsync);
146
147 stats->tx_packets = dp->tx_packets;
148 stats->tx_bytes = dp->tx_bytes;
149
150 } while (u64_stats_fetch_retry_bh(&dp->tsync, start));
151
152 stats->rx_dropped = dev->stats.rx_dropped;
153 stats->tx_dropped = dev->stats.tx_dropped;
154
155 return stats;
156}
157
158
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800159static const struct net_device_ops ifb_netdev_ops = {
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800160 .ndo_open = ifb_open,
161 .ndo_stop = ifb_close,
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000162 .ndo_get_stats64 = ifb_stats64,
Stephen Hemminger00829822008-11-20 20:14:53 -0800163 .ndo_start_xmit = ifb_xmit,
164 .ndo_validate_addr = eth_validate_addr,
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800165};
166
Eric Dumazet39980292011-01-03 10:35:22 +0000167#define IFB_FEATURES (NETIF_F_NO_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \
168 NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 | \
169 NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_TX)
170
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700171static void ifb_setup(struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800172{
173 /* Initialize the device structure. */
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700174 dev->destructor = free_netdev;
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800175 dev->netdev_ops = &ifb_netdev_ops;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800176
177 /* Fill in device structure with ethernet-generic values. */
178 ether_setup(dev);
179 dev->tx_queue_len = TX_Q_LIMIT;
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800180
Eric Dumazet39980292011-01-03 10:35:22 +0000181 dev->features |= IFB_FEATURES;
182 dev->vlan_features |= IFB_FEATURES;
183
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800184 dev->flags |= IFF_NOARP;
185 dev->flags &= ~IFF_MULTICAST;
Neil Horman550fd082011-07-26 06:05:38 +0000186 dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800187 random_ether_addr(dev->dev_addr);
188}
189
Stephen Hemminger424efe92009-08-31 19:50:51 +0000190static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800191{
192 struct ifb_private *dp = netdev_priv(dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800193 u32 from = G_TC_FROM(skb->tc_verd);
194
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000195 u64_stats_update_begin(&dp->rsync);
196 dp->rx_packets++;
197 dp->rx_bytes += skb->len;
198 u64_stats_update_end(&dp->rsync);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800199
Eric Dumazet8964be42009-11-20 15:35:04 -0800200 if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800201 dev_kfree_skb(skb);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000202 dev->stats.rx_dropped++;
Stephen Hemminger424efe92009-08-31 19:50:51 +0000203 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800204 }
205
206 if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
207 netif_stop_queue(dev);
208 }
209
Changli Gao957fca92010-12-04 15:01:52 +0000210 __skb_queue_tail(&dp->rq, skb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800211 if (!dp->tasklet_pending) {
212 dp->tasklet_pending = 1;
213 tasklet_schedule(&dp->ifb_tasklet);
214 }
215
Stephen Hemminger424efe92009-08-31 19:50:51 +0000216 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800217}
218
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800219static int ifb_close(struct net_device *dev)
220{
221 struct ifb_private *dp = netdev_priv(dev);
222
223 tasklet_kill(&dp->ifb_tasklet);
224 netif_stop_queue(dev);
Changli Gao957fca92010-12-04 15:01:52 +0000225 __skb_queue_purge(&dp->rq);
226 __skb_queue_purge(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800227 return 0;
228}
229
230static int ifb_open(struct net_device *dev)
231{
232 struct ifb_private *dp = netdev_priv(dev);
233
234 tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
Changli Gao957fca92010-12-04 15:01:52 +0000235 __skb_queue_head_init(&dp->rq);
236 __skb_queue_head_init(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800237 netif_start_queue(dev);
238
239 return 0;
240}
241
Patrick McHardy0e068772007-07-11 19:42:31 -0700242static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
243{
244 if (tb[IFLA_ADDRESS]) {
245 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
246 return -EINVAL;
247 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
248 return -EADDRNOTAVAIL;
249 }
250 return 0;
251}
252
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700253static struct rtnl_link_ops ifb_link_ops __read_mostly = {
254 .kind = "ifb",
255 .priv_size = sizeof(struct ifb_private),
256 .setup = ifb_setup,
Patrick McHardy0e068772007-07-11 19:42:31 -0700257 .validate = ifb_validate,
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700258};
259
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700260/* Number of ifb devices to be set up by this module. */
261module_param(numifbs, int, 0);
262MODULE_PARM_DESC(numifbs, "Number of ifb devices");
263
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800264static int __init ifb_init_one(int index)
265{
266 struct net_device *dev_ifb;
267 int err;
268
269 dev_ifb = alloc_netdev(sizeof(struct ifb_private),
270 "ifb%d", ifb_setup);
271
272 if (!dev_ifb)
273 return -ENOMEM;
274
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700275 dev_ifb->rtnl_link_ops = &ifb_link_ops;
276 err = register_netdevice(dev_ifb);
277 if (err < 0)
278 goto err;
Jarek Poplawski94833df2008-03-20 17:05:13 -0700279
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700280 return 0;
281
282err:
283 free_netdev(dev_ifb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800284 return err;
285}
286
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800287static int __init ifb_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400288{
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700289 int i, err;
290
291 rtnl_lock();
292 err = __rtnl_link_register(&ifb_link_ops);
Patrick McHardy62b7ffc2007-06-13 12:04:51 -0700293
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800294 for (i = 0; i < numifbs && !err; i++)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400295 err = ifb_init_one(i);
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700296 if (err)
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700297 __rtnl_link_unregister(&ifb_link_ops);
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700298 rtnl_unlock();
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800299
300 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400301}
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800302
303static void __exit ifb_cleanup_module(void)
304{
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700305 rtnl_link_unregister(&ifb_link_ops);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800306}
307
308module_init(ifb_init_module);
309module_exit(ifb_cleanup_module);
310MODULE_LICENSE("GPL");
311MODULE_AUTHOR("Jamal Hadi Salim");
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700312MODULE_ALIAS_RTNL_LINK("ifb");