blob: 94570aace2414a5984d989045c599ed141582eb5 [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) {
Herbert Xuf40ae912015-04-17 13:32:09 +0800108 skb_pull(skb, skb->mac_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 {
Eric W. Biederman57a77442014-03-13 21:26:42 -0700139 start = u64_stats_fetch_begin_irq(&dp->rsync);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000140 stats->rx_packets = dp->rx_packets;
141 stats->rx_bytes = dp->rx_bytes;
Eric W. Biederman57a77442014-03-13 21:26:42 -0700142 } while (u64_stats_fetch_retry_irq(&dp->rsync, start));
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000143
144 do {
Eric W. Biederman57a77442014-03-13 21:26:42 -0700145 start = u64_stats_fetch_begin_irq(&dp->tsync);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000146
147 stats->tx_packets = dp->tx_packets;
148 stats->tx_bytes = dp->tx_bytes;
149
Eric W. Biederman57a77442014-03-13 21:26:42 -0700150 } while (u64_stats_fetch_retry_irq(&dp->tsync, start));
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000151
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;
Vlad Yasevich8dd6e142014-03-27 22:14:47 -0400183 dev->vlan_features |= IFB_FEATURES & ~(NETIF_F_HW_VLAN_CTAG_TX |
184 NETIF_F_HW_VLAN_STAG_TX);
Eric Dumazet39980292011-01-03 10:35:22 +0000185
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800186 dev->flags |= IFF_NOARP;
187 dev->flags &= ~IFF_MULTICAST;
Eric Dumazet02875872014-10-05 18:38:35 -0700188 dev->priv_flags &= ~IFF_TX_SKB_SHARING;
189 netif_keep_dst(dev);
Danny Kukawkaf2cedb62012-02-15 06:45:39 +0000190 eth_hw_addr_random(dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800191}
192
Stephen Hemminger424efe92009-08-31 19:50:51 +0000193static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800194{
195 struct ifb_private *dp = netdev_priv(dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800196 u32 from = G_TC_FROM(skb->tc_verd);
197
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000198 u64_stats_update_begin(&dp->rsync);
199 dp->rx_packets++;
200 dp->rx_bytes += skb->len;
201 u64_stats_update_end(&dp->rsync);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800202
Eric Dumazet8964be42009-11-20 15:35:04 -0800203 if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800204 dev_kfree_skb(skb);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000205 dev->stats.rx_dropped++;
Stephen Hemminger424efe92009-08-31 19:50:51 +0000206 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800207 }
208
209 if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
210 netif_stop_queue(dev);
211 }
212
Changli Gao957fca92010-12-04 15:01:52 +0000213 __skb_queue_tail(&dp->rq, skb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800214 if (!dp->tasklet_pending) {
215 dp->tasklet_pending = 1;
216 tasklet_schedule(&dp->ifb_tasklet);
217 }
218
Stephen Hemminger424efe92009-08-31 19:50:51 +0000219 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800220}
221
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800222static int ifb_close(struct net_device *dev)
223{
224 struct ifb_private *dp = netdev_priv(dev);
225
226 tasklet_kill(&dp->ifb_tasklet);
227 netif_stop_queue(dev);
Changli Gao957fca92010-12-04 15:01:52 +0000228 __skb_queue_purge(&dp->rq);
229 __skb_queue_purge(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800230 return 0;
231}
232
233static int ifb_open(struct net_device *dev)
234{
235 struct ifb_private *dp = netdev_priv(dev);
236
237 tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
Changli Gao957fca92010-12-04 15:01:52 +0000238 __skb_queue_head_init(&dp->rq);
239 __skb_queue_head_init(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800240 netif_start_queue(dev);
241
242 return 0;
243}
244
Patrick McHardy0e068772007-07-11 19:42:31 -0700245static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
246{
247 if (tb[IFLA_ADDRESS]) {
248 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
249 return -EINVAL;
250 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
251 return -EADDRNOTAVAIL;
252 }
253 return 0;
254}
255
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700256static struct rtnl_link_ops ifb_link_ops __read_mostly = {
257 .kind = "ifb",
258 .priv_size = sizeof(struct ifb_private),
259 .setup = ifb_setup,
Patrick McHardy0e068772007-07-11 19:42:31 -0700260 .validate = ifb_validate,
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700261};
262
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700263/* Number of ifb devices to be set up by this module. */
264module_param(numifbs, int, 0);
265MODULE_PARM_DESC(numifbs, "Number of ifb devices");
266
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800267static int __init ifb_init_one(int index)
268{
269 struct net_device *dev_ifb;
John Stultz827da442013-10-07 15:51:58 -0700270 struct ifb_private *dp;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800271 int err;
272
Tom Gundersenc835a672014-07-14 16:37:24 +0200273 dev_ifb = alloc_netdev(sizeof(struct ifb_private), "ifb%d",
274 NET_NAME_UNKNOWN, ifb_setup);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800275
276 if (!dev_ifb)
277 return -ENOMEM;
278
John Stultz827da442013-10-07 15:51:58 -0700279 dp = netdev_priv(dev_ifb);
280 u64_stats_init(&dp->rsync);
281 u64_stats_init(&dp->tsync);
282
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700283 dev_ifb->rtnl_link_ops = &ifb_link_ops;
284 err = register_netdevice(dev_ifb);
285 if (err < 0)
286 goto err;
Jarek Poplawski94833df2008-03-20 17:05:13 -0700287
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700288 return 0;
289
290err:
291 free_netdev(dev_ifb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800292 return err;
293}
294
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800295static int __init ifb_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400296{
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700297 int i, err;
298
299 rtnl_lock();
300 err = __rtnl_link_register(&ifb_link_ops);
dingtianhongf2966cd2013-07-11 19:04:06 +0800301 if (err < 0)
302 goto out;
Patrick McHardy62b7ffc2007-06-13 12:04:51 -0700303
dingtianhong440d57b2013-07-10 12:04:02 +0800304 for (i = 0; i < numifbs && !err; i++) {
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400305 err = ifb_init_one(i);
dingtianhong440d57b2013-07-10 12:04:02 +0800306 cond_resched();
307 }
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700308 if (err)
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700309 __rtnl_link_unregister(&ifb_link_ops);
dingtianhongf2966cd2013-07-11 19:04:06 +0800310
311out:
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700312 rtnl_unlock();
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800313
314 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400315}
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800316
317static void __exit ifb_cleanup_module(void)
318{
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700319 rtnl_link_unregister(&ifb_link_ops);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800320}
321
322module_init(ifb_init_module);
323module_exit(ifb_cleanup_module);
324MODULE_LICENSE("GPL");
325MODULE_AUTHOR("Jamal Hadi Salim");
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700326MODULE_ALIAS_RTNL_LINK("ifb");