blob: a3bed28197d29bf9722b6c586f311827d357b501 [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 Dumazet73bf0d02013-01-13 07:46:34 +000093 skb->dev = dev_get_by_index_rcu(dev_net(_dev), 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
Michał Mirosław34324dc2011-11-15 15:29:55 +0000167#define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \
Eric Dumazet39980292011-01-03 10:35:22 +0000168 NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 | \
Patrick McHardy28d2b132013-04-19 02:04:32 +0000169 NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX | \
170 NETIF_F_HW_VLAN_STAG_TX)
Eric Dumazet39980292011-01-03 10:35:22 +0000171
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700172static void ifb_setup(struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800173{
174 /* Initialize the device structure. */
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700175 dev->destructor = free_netdev;
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800176 dev->netdev_ops = &ifb_netdev_ops;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800177
178 /* Fill in device structure with ethernet-generic values. */
179 ether_setup(dev);
180 dev->tx_queue_len = TX_Q_LIMIT;
Stephen Hemminger8dfcdf32008-11-19 21:47:07 -0800181
Eric Dumazet39980292011-01-03 10:35:22 +0000182 dev->features |= IFB_FEATURES;
183 dev->vlan_features |= IFB_FEATURES;
184
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800185 dev->flags |= IFF_NOARP;
186 dev->flags &= ~IFF_MULTICAST;
Neil Horman550fd082011-07-26 06:05:38 +0000187 dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
Danny Kukawkaf2cedb62012-02-15 06:45:39 +0000188 eth_hw_addr_random(dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800189}
190
Stephen Hemminger424efe92009-08-31 19:50:51 +0000191static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800192{
193 struct ifb_private *dp = netdev_priv(dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800194 u32 from = G_TC_FROM(skb->tc_verd);
195
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000196 u64_stats_update_begin(&dp->rsync);
197 dp->rx_packets++;
198 dp->rx_bytes += skb->len;
199 u64_stats_update_end(&dp->rsync);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800200
Eric Dumazet8964be42009-11-20 15:35:04 -0800201 if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800202 dev_kfree_skb(skb);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000203 dev->stats.rx_dropped++;
Stephen Hemminger424efe92009-08-31 19:50:51 +0000204 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800205 }
206
207 if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
208 netif_stop_queue(dev);
209 }
210
Changli Gao957fca92010-12-04 15:01:52 +0000211 __skb_queue_tail(&dp->rq, skb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800212 if (!dp->tasklet_pending) {
213 dp->tasklet_pending = 1;
214 tasklet_schedule(&dp->ifb_tasklet);
215 }
216
Stephen Hemminger424efe92009-08-31 19:50:51 +0000217 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800218}
219
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800220static int ifb_close(struct net_device *dev)
221{
222 struct ifb_private *dp = netdev_priv(dev);
223
224 tasklet_kill(&dp->ifb_tasklet);
225 netif_stop_queue(dev);
Changli Gao957fca92010-12-04 15:01:52 +0000226 __skb_queue_purge(&dp->rq);
227 __skb_queue_purge(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800228 return 0;
229}
230
231static int ifb_open(struct net_device *dev)
232{
233 struct ifb_private *dp = netdev_priv(dev);
234
235 tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
Changli Gao957fca92010-12-04 15:01:52 +0000236 __skb_queue_head_init(&dp->rq);
237 __skb_queue_head_init(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800238 netif_start_queue(dev);
239
240 return 0;
241}
242
Patrick McHardy0e068772007-07-11 19:42:31 -0700243static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
244{
245 if (tb[IFLA_ADDRESS]) {
246 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
247 return -EINVAL;
248 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
249 return -EADDRNOTAVAIL;
250 }
251 return 0;
252}
253
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700254static struct rtnl_link_ops ifb_link_ops __read_mostly = {
255 .kind = "ifb",
256 .priv_size = sizeof(struct ifb_private),
257 .setup = ifb_setup,
Patrick McHardy0e068772007-07-11 19:42:31 -0700258 .validate = ifb_validate,
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700259};
260
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700261/* Number of ifb devices to be set up by this module. */
262module_param(numifbs, int, 0);
263MODULE_PARM_DESC(numifbs, "Number of ifb devices");
264
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800265static int __init ifb_init_one(int index)
266{
267 struct net_device *dev_ifb;
268 int err;
269
270 dev_ifb = alloc_netdev(sizeof(struct ifb_private),
271 "ifb%d", ifb_setup);
272
273 if (!dev_ifb)
274 return -ENOMEM;
275
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700276 dev_ifb->rtnl_link_ops = &ifb_link_ops;
277 err = register_netdevice(dev_ifb);
278 if (err < 0)
279 goto err;
Jarek Poplawski94833df2008-03-20 17:05:13 -0700280
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700281 return 0;
282
283err:
284 free_netdev(dev_ifb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800285 return err;
286}
287
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800288static int __init ifb_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400289{
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700290 int i, err;
291
292 rtnl_lock();
293 err = __rtnl_link_register(&ifb_link_ops);
dingtianhongf2966cd2013-07-11 19:04:06 +0800294 if (err < 0)
295 goto out;
Patrick McHardy62b7ffc2007-06-13 12:04:51 -0700296
dingtianhong440d57b2013-07-10 12:04:02 +0800297 for (i = 0; i < numifbs && !err; i++) {
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400298 err = ifb_init_one(i);
dingtianhong440d57b2013-07-10 12:04:02 +0800299 cond_resched();
300 }
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700301 if (err)
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700302 __rtnl_link_unregister(&ifb_link_ops);
dingtianhongf2966cd2013-07-11 19:04:06 +0800303
304out:
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700305 rtnl_unlock();
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800306
307 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400308}
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800309
310static void __exit ifb_cleanup_module(void)
311{
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700312 rtnl_link_unregister(&ifb_link_ops);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800313}
314
315module_init(ifb_init_module);
316module_exit(ifb_cleanup_module);
317MODULE_LICENSE("GPL");
318MODULE_AUTHOR("Jamal Hadi Salim");
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700319MODULE_ALIAS_RTNL_LINK("ifb");