blob: 46a7790be004a7653d391ce96d51ae48975baa8c [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 {
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;
Neil Horman550fd082011-07-26 06:05:38 +0000188 dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
Danny Kukawkaf2cedb62012-02-15 06:45:39 +0000189 eth_hw_addr_random(dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800190}
191
Stephen Hemminger424efe92009-08-31 19:50:51 +0000192static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800193{
194 struct ifb_private *dp = netdev_priv(dev);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800195 u32 from = G_TC_FROM(skb->tc_verd);
196
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000197 u64_stats_update_begin(&dp->rsync);
198 dp->rx_packets++;
199 dp->rx_bytes += skb->len;
200 u64_stats_update_end(&dp->rsync);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800201
Eric Dumazet8964be42009-11-20 15:35:04 -0800202 if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800203 dev_kfree_skb(skb);
stephen hemminger3b0c9cb2011-06-20 11:42:30 +0000204 dev->stats.rx_dropped++;
Stephen Hemminger424efe92009-08-31 19:50:51 +0000205 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800206 }
207
208 if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
209 netif_stop_queue(dev);
210 }
211
Changli Gao957fca92010-12-04 15:01:52 +0000212 __skb_queue_tail(&dp->rq, skb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800213 if (!dp->tasklet_pending) {
214 dp->tasklet_pending = 1;
215 tasklet_schedule(&dp->ifb_tasklet);
216 }
217
Stephen Hemminger424efe92009-08-31 19:50:51 +0000218 return NETDEV_TX_OK;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800219}
220
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800221static int ifb_close(struct net_device *dev)
222{
223 struct ifb_private *dp = netdev_priv(dev);
224
225 tasklet_kill(&dp->ifb_tasklet);
226 netif_stop_queue(dev);
Changli Gao957fca92010-12-04 15:01:52 +0000227 __skb_queue_purge(&dp->rq);
228 __skb_queue_purge(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800229 return 0;
230}
231
232static int ifb_open(struct net_device *dev)
233{
234 struct ifb_private *dp = netdev_priv(dev);
235
236 tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
Changli Gao957fca92010-12-04 15:01:52 +0000237 __skb_queue_head_init(&dp->rq);
238 __skb_queue_head_init(&dp->tq);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800239 netif_start_queue(dev);
240
241 return 0;
242}
243
Patrick McHardy0e068772007-07-11 19:42:31 -0700244static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
245{
246 if (tb[IFLA_ADDRESS]) {
247 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
248 return -EINVAL;
249 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
250 return -EADDRNOTAVAIL;
251 }
252 return 0;
253}
254
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700255static struct rtnl_link_ops ifb_link_ops __read_mostly = {
256 .kind = "ifb",
257 .priv_size = sizeof(struct ifb_private),
258 .setup = ifb_setup,
Patrick McHardy0e068772007-07-11 19:42:31 -0700259 .validate = ifb_validate,
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700260};
261
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700262/* Number of ifb devices to be set up by this module. */
263module_param(numifbs, int, 0);
264MODULE_PARM_DESC(numifbs, "Number of ifb devices");
265
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800266static int __init ifb_init_one(int index)
267{
268 struct net_device *dev_ifb;
John Stultz827da442013-10-07 15:51:58 -0700269 struct ifb_private *dp;
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800270 int err;
271
272 dev_ifb = alloc_netdev(sizeof(struct ifb_private),
273 "ifb%d", ifb_setup);
274
275 if (!dev_ifb)
276 return -ENOMEM;
277
John Stultz827da442013-10-07 15:51:58 -0700278 dp = netdev_priv(dev_ifb);
279 u64_stats_init(&dp->rsync);
280 u64_stats_init(&dp->tsync);
281
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700282 dev_ifb->rtnl_link_ops = &ifb_link_ops;
283 err = register_netdevice(dev_ifb);
284 if (err < 0)
285 goto err;
Jarek Poplawski94833df2008-03-20 17:05:13 -0700286
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700287 return 0;
288
289err:
290 free_netdev(dev_ifb);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800291 return err;
292}
293
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800294static int __init ifb_init_module(void)
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400295{
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700296 int i, err;
297
298 rtnl_lock();
299 err = __rtnl_link_register(&ifb_link_ops);
dingtianhongf2966cd2013-07-11 19:04:06 +0800300 if (err < 0)
301 goto out;
Patrick McHardy62b7ffc2007-06-13 12:04:51 -0700302
dingtianhong440d57b2013-07-10 12:04:02 +0800303 for (i = 0; i < numifbs && !err; i++) {
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400304 err = ifb_init_one(i);
dingtianhong440d57b2013-07-10 12:04:02 +0800305 cond_resched();
306 }
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700307 if (err)
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700308 __rtnl_link_unregister(&ifb_link_ops);
dingtianhongf2966cd2013-07-11 19:04:06 +0800309
310out:
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700311 rtnl_unlock();
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800312
313 return err;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400314}
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800315
316static void __exit ifb_cleanup_module(void)
317{
Patrick McHardy2d85cba2007-07-11 19:42:13 -0700318 rtnl_link_unregister(&ifb_link_ops);
Jamal Hadi Salim253af422006-01-08 22:34:25 -0800319}
320
321module_init(ifb_init_module);
322module_exit(ifb_cleanup_module);
323MODULE_LICENSE("GPL");
324MODULE_AUTHOR("Jamal Hadi Salim");
Patrick McHardy9ba2cd62007-06-13 12:05:06 -0700325MODULE_ALIAS_RTNL_LINK("ifb");