Blame view
drivers/net/ifb.c
7.15 KB
6aa20a223 drivers/net: Trim... |
1 |
/* drivers/net/ifb.c: |
253af4235 [NET]: Add IFB (I... |
2 3 4 5 6 7 8 9 10 |
The purpose of this driver is to provide a device that allows for sharing of resources: 1) qdiscs/policies that are per device as opposed to system wide. ifb allows for a device which can be redirected to thus providing an impression of sharing. 2) Allows for queueing incoming traffic for shaping instead of |
6aa20a223 drivers/net: Trim... |
11 |
dropping. |
253af4235 [NET]: Add IFB (I... |
12 13 14 15 16 17 18 19 20 21 22 |
The original concept is based on what is known as the IMQ driver initially written by Martin Devera, later rewritten by Patrick McHardy and then maintained by Andre Correa. You need the tc action mirror or redirect to feed this device packets. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
6aa20a223 drivers/net: Trim... |
23 |
|
253af4235 [NET]: Add IFB (I... |
24 |
Authors: Jamal Hadi Salim (2005) |
6aa20a223 drivers/net: Trim... |
25 |
|
253af4235 [NET]: Add IFB (I... |
26 |
*/ |
253af4235 [NET]: Add IFB (I... |
27 28 29 30 31 |
#include <linux/module.h> #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/init.h> |
a6b7a4078 net: remove inter... |
32 |
#include <linux/interrupt.h> |
253af4235 [NET]: Add IFB (I... |
33 |
#include <linux/moduleparam.h> |
6aa20a223 drivers/net: Trim... |
34 |
#include <net/pkt_sched.h> |
881d966b4 [NET]: Make the d... |
35 |
#include <net/net_namespace.h> |
253af4235 [NET]: Add IFB (I... |
36 |
|
253af4235 [NET]: Add IFB (I... |
37 38 |
#define TX_Q_LIMIT 32 struct ifb_private { |
253af4235 [NET]: Add IFB (I... |
39 40 |
struct tasklet_struct ifb_tasklet; int tasklet_pending; |
3b0c9cbb6 ifb: convert to 6... |
41 42 |
struct u64_stats_sync rsync; |
253af4235 [NET]: Add IFB (I... |
43 |
struct sk_buff_head rq; |
3b0c9cbb6 ifb: convert to 6... |
44 45 46 47 |
u64 rx_packets; u64 rx_bytes; struct u64_stats_sync tsync; |
253af4235 [NET]: Add IFB (I... |
48 |
struct sk_buff_head tq; |
3b0c9cbb6 ifb: convert to 6... |
49 50 |
u64 tx_packets; u64 tx_bytes; |
253af4235 [NET]: Add IFB (I... |
51 |
}; |
35eaa31e5 [NET]: Increase d... |
52 |
static int numifbs = 2; |
253af4235 [NET]: Add IFB (I... |
53 54 |
static void ri_tasklet(unsigned long dev); |
424efe9ca netdev: convert p... |
55 |
static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev); |
253af4235 [NET]: Add IFB (I... |
56 57 |
static int ifb_open(struct net_device *dev); static int ifb_close(struct net_device *dev); |
6aa20a223 drivers/net: Trim... |
58 |
static void ri_tasklet(unsigned long dev) |
253af4235 [NET]: Add IFB (I... |
59 |
{ |
253af4235 [NET]: Add IFB (I... |
60 61 |
struct net_device *_dev = (struct net_device *)dev; struct ifb_private *dp = netdev_priv(_dev); |
c3f26a269 netdev: Fix lockd... |
62 |
struct netdev_queue *txq; |
253af4235 [NET]: Add IFB (I... |
63 |
struct sk_buff *skb; |
c3f26a269 netdev: Fix lockd... |
64 |
txq = netdev_get_tx_queue(_dev, 0); |
253af4235 [NET]: Add IFB (I... |
65 |
if ((skb = skb_peek(&dp->tq)) == NULL) { |
c3f26a269 netdev: Fix lockd... |
66 |
if (__netif_tx_trylock(txq)) { |
957fca95e ifb: use the lock... |
67 |
skb_queue_splice_tail_init(&dp->rq, &dp->tq); |
c3f26a269 netdev: Fix lockd... |
68 |
__netif_tx_unlock(txq); |
253af4235 [NET]: Add IFB (I... |
69 70 |
} else { /* reschedule */ |
253af4235 [NET]: Add IFB (I... |
71 72 73 |
goto resched; } } |
7edc3453e ifb: fix a lockde... |
74 |
while ((skb = __skb_dequeue(&dp->tq)) != NULL) { |
253af4235 [NET]: Add IFB (I... |
75 76 77 78 |
u32 from = G_TC_FROM(skb->tc_verd); skb->tc_verd = 0; skb->tc_verd = SET_TC_NCLS(skb->tc_verd); |
3b0c9cbb6 ifb: convert to 6... |
79 80 81 82 83 |
u64_stats_update_begin(&dp->tsync); dp->tx_packets++; dp->tx_bytes += skb->len; u64_stats_update_end(&dp->tsync); |
c01003c20 [IFB]: Fix crash ... |
84 |
|
05e8689c9 ifb: RCU locking ... |
85 |
rcu_read_lock(); |
8964be4a9 net: rename skb->... |
86 |
skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif); |
c01003c20 [IFB]: Fix crash ... |
87 |
if (!skb->dev) { |
05e8689c9 ifb: RCU locking ... |
88 |
rcu_read_unlock(); |
c01003c20 [IFB]: Fix crash ... |
89 |
dev_kfree_skb(skb); |
3b0c9cbb6 ifb: convert to 6... |
90 |
_dev->stats.tx_dropped++; |
75c1c8256 ifb: goto resched... |
91 92 |
if (skb_queue_len(&dp->tq) != 0) goto resched; |
c01003c20 [IFB]: Fix crash ... |
93 94 |
break; } |
05e8689c9 ifb: RCU locking ... |
95 |
rcu_read_unlock(); |
8964be4a9 net: rename skb->... |
96 |
skb->skb_iif = _dev->ifindex; |
c01003c20 [IFB]: Fix crash ... |
97 |
|
253af4235 [NET]: Add IFB (I... |
98 |
if (from & AT_EGRESS) { |
253af4235 [NET]: Add IFB (I... |
99 100 |
dev_queue_xmit(skb); } else if (from & AT_INGRESS) { |
c01003c20 [IFB]: Fix crash ... |
101 |
skb_pull(skb, skb->dev->hard_header_len); |
1a75972c6 ifb: use netif_re... |
102 |
netif_receive_skb(skb); |
c01003c20 [IFB]: Fix crash ... |
103 104 |
} else BUG(); |
253af4235 [NET]: Add IFB (I... |
105 |
} |
c3f26a269 netdev: Fix lockd... |
106 |
if (__netif_tx_trylock(txq)) { |
253af4235 [NET]: Add IFB (I... |
107 108 109 110 111 |
if ((skb = skb_peek(&dp->rq)) == NULL) { dp->tasklet_pending = 0; if (netif_queue_stopped(_dev)) netif_wake_queue(_dev); } else { |
c3f26a269 netdev: Fix lockd... |
112 |
__netif_tx_unlock(txq); |
253af4235 [NET]: Add IFB (I... |
113 114 |
goto resched; } |
c3f26a269 netdev: Fix lockd... |
115 |
__netif_tx_unlock(txq); |
253af4235 [NET]: Add IFB (I... |
116 117 118 119 120 121 122 |
} else { resched: dp->tasklet_pending = 1; tasklet_schedule(&dp->ifb_tasklet); } } |
3b0c9cbb6 ifb: convert to 6... |
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
static struct rtnl_link_stats64 *ifb_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct ifb_private *dp = netdev_priv(dev); unsigned int start; do { start = u64_stats_fetch_begin_bh(&dp->rsync); stats->rx_packets = dp->rx_packets; stats->rx_bytes = dp->rx_bytes; } while (u64_stats_fetch_retry_bh(&dp->rsync, start)); do { start = u64_stats_fetch_begin_bh(&dp->tsync); stats->tx_packets = dp->tx_packets; stats->tx_bytes = dp->tx_bytes; } while (u64_stats_fetch_retry_bh(&dp->tsync, start)); stats->rx_dropped = dev->stats.rx_dropped; stats->tx_dropped = dev->stats.tx_dropped; return stats; } |
8dfcdf342 ifb: convert to n... |
148 |
static const struct net_device_ops ifb_netdev_ops = { |
8dfcdf342 ifb: convert to n... |
149 150 |
.ndo_open = ifb_open, .ndo_stop = ifb_close, |
3b0c9cbb6 ifb: convert to 6... |
151 |
.ndo_get_stats64 = ifb_stats64, |
008298231 netdev: add more ... |
152 153 |
.ndo_start_xmit = ifb_xmit, .ndo_validate_addr = eth_validate_addr, |
8dfcdf342 ifb: convert to n... |
154 |
}; |
34324dc2b net: remove NETIF... |
155 |
#define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \ |
39980292f ifb: add performa... |
156 157 |
NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 | \ NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_TX) |
9ba2cd656 [IFB]: Use rtnl_l... |
158 |
static void ifb_setup(struct net_device *dev) |
253af4235 [NET]: Add IFB (I... |
159 160 |
{ /* Initialize the device structure. */ |
9ba2cd656 [IFB]: Use rtnl_l... |
161 |
dev->destructor = free_netdev; |
8dfcdf342 ifb: convert to n... |
162 |
dev->netdev_ops = &ifb_netdev_ops; |
253af4235 [NET]: Add IFB (I... |
163 164 165 166 |
/* Fill in device structure with ethernet-generic values. */ ether_setup(dev); dev->tx_queue_len = TX_Q_LIMIT; |
8dfcdf342 ifb: convert to n... |
167 |
|
39980292f ifb: add performa... |
168 169 |
dev->features |= IFB_FEATURES; dev->vlan_features |= IFB_FEATURES; |
253af4235 [NET]: Add IFB (I... |
170 171 |
dev->flags |= IFF_NOARP; dev->flags &= ~IFF_MULTICAST; |
550fd08c2 net: Audit driver... |
172 |
dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); |
253af4235 [NET]: Add IFB (I... |
173 174 |
random_ether_addr(dev->dev_addr); } |
424efe9ca netdev: convert p... |
175 |
static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev) |
253af4235 [NET]: Add IFB (I... |
176 177 |
{ struct ifb_private *dp = netdev_priv(dev); |
253af4235 [NET]: Add IFB (I... |
178 |
u32 from = G_TC_FROM(skb->tc_verd); |
3b0c9cbb6 ifb: convert to 6... |
179 180 181 182 |
u64_stats_update_begin(&dp->rsync); dp->rx_packets++; dp->rx_bytes += skb->len; u64_stats_update_end(&dp->rsync); |
253af4235 [NET]: Add IFB (I... |
183 |
|
8964be4a9 net: rename skb->... |
184 |
if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) { |
253af4235 [NET]: Add IFB (I... |
185 |
dev_kfree_skb(skb); |
3b0c9cbb6 ifb: convert to 6... |
186 |
dev->stats.rx_dropped++; |
424efe9ca netdev: convert p... |
187 |
return NETDEV_TX_OK; |
253af4235 [NET]: Add IFB (I... |
188 189 190 191 192 |
} if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) { netif_stop_queue(dev); } |
957fca95e ifb: use the lock... |
193 |
__skb_queue_tail(&dp->rq, skb); |
253af4235 [NET]: Add IFB (I... |
194 195 196 197 |
if (!dp->tasklet_pending) { dp->tasklet_pending = 1; tasklet_schedule(&dp->ifb_tasklet); } |
424efe9ca netdev: convert p... |
198 |
return NETDEV_TX_OK; |
253af4235 [NET]: Add IFB (I... |
199 |
} |
253af4235 [NET]: Add IFB (I... |
200 201 202 203 204 205 |
static int ifb_close(struct net_device *dev) { struct ifb_private *dp = netdev_priv(dev); tasklet_kill(&dp->ifb_tasklet); netif_stop_queue(dev); |
957fca95e ifb: use the lock... |
206 207 |
__skb_queue_purge(&dp->rq); __skb_queue_purge(&dp->tq); |
253af4235 [NET]: Add IFB (I... |
208 209 210 211 212 213 214 215 |
return 0; } static int ifb_open(struct net_device *dev) { struct ifb_private *dp = netdev_priv(dev); tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev); |
957fca95e ifb: use the lock... |
216 217 |
__skb_queue_head_init(&dp->rq); __skb_queue_head_init(&dp->tq); |
253af4235 [NET]: Add IFB (I... |
218 219 220 221 |
netif_start_queue(dev); return 0; } |
0e06877c6 [RTNETLINK]: rtnl... |
222 223 224 225 226 227 228 229 230 231 |
static int ifb_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) return -EINVAL; if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) return -EADDRNOTAVAIL; } return 0; } |
9ba2cd656 [IFB]: Use rtnl_l... |
232 233 234 235 |
static struct rtnl_link_ops ifb_link_ops __read_mostly = { .kind = "ifb", .priv_size = sizeof(struct ifb_private), .setup = ifb_setup, |
0e06877c6 [RTNETLINK]: rtnl... |
236 |
.validate = ifb_validate, |
9ba2cd656 [IFB]: Use rtnl_l... |
237 |
}; |
2d85cba2b [RTNETLINK]: rtnl... |
238 239 240 |
/* Number of ifb devices to be set up by this module. */ module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); |
253af4235 [NET]: Add IFB (I... |
241 242 243 244 245 246 247 248 249 250 |
static int __init ifb_init_one(int index) { struct net_device *dev_ifb; int err; dev_ifb = alloc_netdev(sizeof(struct ifb_private), "ifb%d", ifb_setup); if (!dev_ifb) return -ENOMEM; |
9ba2cd656 [IFB]: Use rtnl_l... |
251 252 253 254 |
dev_ifb->rtnl_link_ops = &ifb_link_ops; err = register_netdevice(dev_ifb); if (err < 0) goto err; |
94833dfb8 [NET] ifb: set se... |
255 |
|
9ba2cd656 [IFB]: Use rtnl_l... |
256 |
return 0; |
62b7ffcaa [IFB]: Keep ifb d... |
257 |
|
9ba2cd656 [IFB]: Use rtnl_l... |
258 259 260 |
err: free_netdev(dev_ifb); return err; |
6aa20a223 drivers/net: Trim... |
261 |
} |
253af4235 [NET]: Add IFB (I... |
262 263 |
static int __init ifb_init_module(void) |
6aa20a223 drivers/net: Trim... |
264 |
{ |
9ba2cd656 [IFB]: Use rtnl_l... |
265 266 267 268 |
int i, err; rtnl_lock(); err = __rtnl_link_register(&ifb_link_ops); |
62b7ffcaa [IFB]: Keep ifb d... |
269 |
|
253af4235 [NET]: Add IFB (I... |
270 |
for (i = 0; i < numifbs && !err; i++) |
6aa20a223 drivers/net: Trim... |
271 |
err = ifb_init_one(i); |
2d85cba2b [RTNETLINK]: rtnl... |
272 |
if (err) |
9ba2cd656 [IFB]: Use rtnl_l... |
273 |
__rtnl_link_unregister(&ifb_link_ops); |
9ba2cd656 [IFB]: Use rtnl_l... |
274 |
rtnl_unlock(); |
253af4235 [NET]: Add IFB (I... |
275 276 |
return err; |
6aa20a223 drivers/net: Trim... |
277 |
} |
253af4235 [NET]: Add IFB (I... |
278 279 280 |
static void __exit ifb_cleanup_module(void) { |
2d85cba2b [RTNETLINK]: rtnl... |
281 |
rtnl_link_unregister(&ifb_link_ops); |
253af4235 [NET]: Add IFB (I... |
282 283 284 285 286 287 |
} module_init(ifb_init_module); module_exit(ifb_cleanup_module); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jamal Hadi Salim"); |
9ba2cd656 [IFB]: Use rtnl_l... |
288 |
MODULE_ALIAS_RTNL_LINK("ifb"); |