blob: ce53f4a23b199f2fceaa0b938a6f70fbce3aac04 [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;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080044 struct sk_buff_head rq;
45 struct sk_buff_head tq;
46};
47
Richard Lucassen35eaa312006-02-23 16:23:51 -080048static int numifbs = 2;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080049
50static void ri_tasklet(unsigned long dev);
Stephen Hemminger424efe92009-08-31 19:50:51 +000051static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -080052static int ifb_open(struct net_device *dev);
53static int ifb_close(struct net_device *dev);
54
Jeff Garzik6aa20a22006-09-13 13:24:59 -040055static void ri_tasklet(unsigned long dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -080056{
57
58 struct net_device *_dev = (struct net_device *)dev;
59 struct ifb_private *dp = netdev_priv(_dev);
Jeff Garzik09f75cd2007-10-03 17:41:50 -070060 struct net_device_stats *stats = &_dev->stats;
David S. Millerc3f26a22008-07-31 16:58:50 -070061 struct netdev_queue *txq;
Jamal Hadi Salim253af422006-01-08 22:34:25 -080062 struct sk_buff *skb;
63
David S. Millerc3f26a22008-07-31 16:58:50 -070064 txq = netdev_get_tx_queue(_dev, 0);
Jamal Hadi Salim253af422006-01-08 22:34:25 -080065 if ((skb = skb_peek(&dp->tq)) == NULL) {
David S. Millerc3f26a22008-07-31 16:58:50 -070066 if (__netif_tx_trylock(txq)) {
Changli Gao957fca92010-12-04 15:01:52 +000067 skb_queue_splice_tail_init(&dp->rq, &dp->tq);
David S. Millerc3f26a22008-07-31 16:58:50 -070068 __netif_tx_unlock(txq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -080069 } else {
70 /* reschedule */
Jamal Hadi Salim253af422006-01-08 22:34:25 -080071 goto resched;
72 }
73 }
74
Eric Dumazet7edc3452010-12-15 23:52:55 +000075 while ((skb = __skb_dequeue(&dp->tq)) != NULL) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -080076 u32 from = G_TC_FROM(skb->tc_verd);
77
78 skb->tc_verd = 0;
79 skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
80 stats->tx_packets++;
81 stats->tx_bytes +=skb->len;
Patrick McHardyc01003c2007-03-29 11:46:52 -070082
Eric Dumazet05e86892009-11-01 19:45:16 +000083 rcu_read_lock();
Eric Dumazet8964be42009-11-20 15:35:04 -080084 skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif);
Patrick McHardyc01003c2007-03-29 11:46:52 -070085 if (!skb->dev) {
Eric Dumazet05e86892009-11-01 19:45:16 +000086 rcu_read_unlock();
Patrick McHardyc01003c2007-03-29 11:46:52 -070087 dev_kfree_skb(skb);
88 stats->tx_dropped++;
Changli Gao75c1c822010-12-04 14:09:08 +000089 if (skb_queue_len(&dp->tq) != 0)
90 goto resched;
Patrick McHardyc01003c2007-03-29 11:46:52 -070091 break;
92 }
Eric Dumazet05e86892009-11-01 19:45:16 +000093 rcu_read_unlock();
Eric Dumazet8964be42009-11-20 15:35:04 -080094 skb->skb_iif = _dev->ifindex;
Patrick McHardyc01003c2007-03-29 11:46:52 -070095
Jamal Hadi Salim253af422006-01-08 22:34:25 -080096 if (from & AT_EGRESS) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -080097 dev_queue_xmit(skb);
98 } else if (from & AT_INGRESS) {
Patrick McHardyc01003c2007-03-29 11:46:52 -070099 skb_pull(skb, skb->dev->hard_header_len);
Eric Dumazet1a759722010-12-14 22:39:58 +0000100 netif_receive_skb(skb);
Patrick McHardyc01003c2007-03-29 11:46:52 -0700101 } else
102 BUG();
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800103 }
104
David S. Millerc3f26a22008-07-31 16:58:50 -0700105 if (__netif_tx_trylock(txq)) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800106 if ((skb = skb_peek(&dp->rq)) == NULL) {
107 dp->tasklet_pending = 0;
108 if (netif_queue_stopped(_dev))
109 netif_wake_queue(_dev);
110 } else {
David S. Millerc3f26a22008-07-31 16:58:50 -0700111 __netif_tx_unlock(txq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800112 goto resched;
113 }
David S. Millerc3f26a22008-07-31 16:58:50 -0700114 __netif_tx_unlock(txq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800115 } else {
116resched:
117 dp->tasklet_pending = 1;
118 tasklet_schedule(&dp->ifb_tasklet);
119 }
120
121}
122
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800123static const struct net_device_ops ifb_netdev_ops = {
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800124 .ndo_open = ifb_open,
125 .ndo_stop = ifb_close,
Stephen Hemminger00829822008-11-20 20:14:53 -0800126 .ndo_start_xmit = ifb_xmit,
127 .ndo_validate_addr = eth_validate_addr,
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800128};
129
Eric Dumazet39980292011-01-03 10:35:22 +0000130#define IFB_FEATURES (NETIF_F_NO_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \
131 NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 | \
132 NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_TX)
133
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700134static void ifb_setup(struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800135{
136 /* Initialize the device structure. */
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700137 dev->destructor = free_netdev;
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800138 dev->netdev_ops = &ifb_netdev_ops;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800139
140 /* Fill in device structure with ethernet-generic values. */
141 ether_setup(dev);
142 dev->tx_queue_len = TX_Q_LIMIT;
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800143
Eric Dumazet39980292011-01-03 10:35:22 +0000144 dev->features |= IFB_FEATURES;
145 dev->vlan_features |= IFB_FEATURES;
146
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800147 dev->flags |= IFF_NOARP;
148 dev->flags &= ~IFF_MULTICAST;
Eric Dumazet93f154b2009-05-18 22:19:19 -0700149 dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800150 random_ether_addr(dev->dev_addr);
151}
152
Stephen Hemminger424efe92009-08-31 19:50:51 +0000153static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800154{
155 struct ifb_private *dp = netdev_priv(dev);
Jeff Garzik09f75cd2007-10-03 17:41:50 -0700156 struct net_device_stats *stats = &dev->stats;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800157 u32 from = G_TC_FROM(skb->tc_verd);
158
dean gaudet3136dcb2007-01-01 19:39:09 -0800159 stats->rx_packets++;
160 stats->rx_bytes+=skb->len;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800161
Eric Dumazet8964be42009-11-20 15:35:04 -0800162 if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800163 dev_kfree_skb(skb);
164 stats->rx_dropped++;
Stephen Hemminger424efe92009-08-31 19:50:51 +0000165 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800166 }
167
168 if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
169 netif_stop_queue(dev);
170 }
171
Changli Gao957fca92010-12-04 15:01:52 +0000172 __skb_queue_tail(&dp->rq, skb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800173 if (!dp->tasklet_pending) {
174 dp->tasklet_pending = 1;
175 tasklet_schedule(&dp->ifb_tasklet);
176 }
177
Stephen Hemminger424efe92009-08-31 19:50:51 +0000178 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800179}
180
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800181static int ifb_close(struct net_device *dev)
182{
183 struct ifb_private *dp = netdev_priv(dev);
184
185 tasklet_kill(&dp->ifb_tasklet);
186 netif_stop_queue(dev);
Changli Gao957fca92010-12-04 15:01:52 +0000187 __skb_queue_purge(&dp->rq);
188 __skb_queue_purge(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800189 return 0;
190}
191
192static int ifb_open(struct net_device *dev)
193{
194 struct ifb_private *dp = netdev_priv(dev);
195
196 tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
Changli Gao957fca92010-12-04 15:01:52 +0000197 __skb_queue_head_init(&dp->rq);
198 __skb_queue_head_init(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800199 netif_start_queue(dev);
200
201 return 0;
202}
203
Patrick McHardy0e068772007-07-11 19:42:31 -0700204static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
205{
206 if (tb[IFLA_ADDRESS]) {
207 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
208 return -EINVAL;
209 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
210 return -EADDRNOTAVAIL;
211 }
212 return 0;
213}
214
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700215static struct rtnl_link_ops ifb_link_ops __read_mostly = {
216 .kind = "ifb",
217 .priv_size = sizeof(struct ifb_private),
218 .setup = ifb_setup,
Patrick McHardy0e068772007-07-11 19:42:31 -0700219 .validate = ifb_validate,
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700220};
221
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700222/* Number of ifb devices to be set up by this module. */
223module_param(numifbs, int, 0);
224MODULE_PARM_DESC(numifbs, "Number of ifb devices");
225
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800226static int __init ifb_init_one(int index)
227{
228 struct net_device *dev_ifb;
229 int err;
230
231 dev_ifb = alloc_netdev(sizeof(struct ifb_private),
232 "ifb%d", ifb_setup);
233
234 if (!dev_ifb)
235 return -ENOMEM;
236
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700237 dev_ifb->rtnl_link_ops = &ifb_link_ops;
238 err = register_netdevice(dev_ifb);
239 if (err < 0)
240 goto err;
Jarek Poplawski94833df2008-03-20 17:05:13 -0700241
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700242 return 0;
243
244err:
245 free_netdev(dev_ifb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800246 return err;
247}
248
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800249static int __init ifb_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400250{
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700251 int i, err;
252
253 rtnl_lock();
254 err = __rtnl_link_register(&ifb_link_ops);
Patrick McHardy62b7ffc2007-06-13 12:04:51 -0700255
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800256 for (i = 0; i < numifbs && !err; i++)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400257 err = ifb_init_one(i);
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700258 if (err)
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700259 __rtnl_link_unregister(&ifb_link_ops);
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700260 rtnl_unlock();
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800261
262 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400263}
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800264
265static void __exit ifb_cleanup_module(void)
266{
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700267 rtnl_link_unregister(&ifb_link_ops);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800268}
269
270module_init(ifb_init_module);
271module_exit(ifb_cleanup_module);
272MODULE_LICENSE("GPL");
273MODULE_AUTHOR("Jamal Hadi Salim");
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700274MODULE_ALIAS_RTNL_LINK("ifb");