12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2253af423SJamal Hadi Salim /* drivers/net/ifb.c:
3253af423SJamal Hadi Salim
4253af423SJamal Hadi Salim The purpose of this driver is to provide a device that allows
5253af423SJamal Hadi Salim for sharing of resources:
6253af423SJamal Hadi Salim
7253af423SJamal Hadi Salim 1) qdiscs/policies that are per device as opposed to system wide.
8253af423SJamal Hadi Salim ifb allows for a device which can be redirected to thus providing
9253af423SJamal Hadi Salim an impression of sharing.
10253af423SJamal Hadi Salim
11253af423SJamal Hadi Salim 2) Allows for queueing incoming traffic for shaping instead of
12253af423SJamal Hadi Salim dropping.
13253af423SJamal Hadi Salim
14253af423SJamal Hadi Salim The original concept is based on what is known as the IMQ
15253af423SJamal Hadi Salim driver initially written by Martin Devera, later rewritten
16253af423SJamal Hadi Salim by Patrick McHardy and then maintained by Andre Correa.
17253af423SJamal Hadi Salim
18253af423SJamal Hadi Salim You need the tc action mirror or redirect to feed this device
19253af423SJamal Hadi Salim packets.
20253af423SJamal Hadi Salim
21253af423SJamal Hadi Salim
22253af423SJamal Hadi Salim Authors: Jamal Hadi Salim (2005)
23253af423SJamal Hadi Salim
24253af423SJamal Hadi Salim */
25253af423SJamal Hadi Salim
26253af423SJamal Hadi Salim
27253af423SJamal Hadi Salim #include <linux/module.h>
28253af423SJamal Hadi Salim #include <linux/kernel.h>
29253af423SJamal Hadi Salim #include <linux/netdevice.h>
30a21ee5b2STonghao Zhang #include <linux/ethtool.h>
31253af423SJamal Hadi Salim #include <linux/etherdevice.h>
32253af423SJamal Hadi Salim #include <linux/init.h>
33a6b7a407SAlexey Dobriyan #include <linux/interrupt.h>
34253af423SJamal Hadi Salim #include <linux/moduleparam.h>
3542df6e1dSLukas Wunner #include <linux/netfilter_netdev.h>
36253af423SJamal Hadi Salim #include <net/pkt_sched.h>
37881d966bSEric W. Biederman #include <net/net_namespace.h>
38253af423SJamal Hadi Salim
39253af423SJamal Hadi Salim #define TX_Q_LIMIT 32
40a21ee5b2STonghao Zhang
41a21ee5b2STonghao Zhang struct ifb_q_stats {
42a21ee5b2STonghao Zhang u64 packets;
43a21ee5b2STonghao Zhang u64 bytes;
44a21ee5b2STonghao Zhang struct u64_stats_sync sync;
45a21ee5b2STonghao Zhang };
46a21ee5b2STonghao Zhang
479e29e21aSEric Dumazet struct ifb_q_private {
489e29e21aSEric Dumazet struct net_device *dev;
49253af423SJamal Hadi Salim struct tasklet_struct ifb_tasklet;
50253af423SJamal Hadi Salim int tasklet_pending;
519e29e21aSEric Dumazet int txqnum;
52253af423SJamal Hadi Salim struct sk_buff_head rq;
539e29e21aSEric Dumazet struct sk_buff_head tq;
54a21ee5b2STonghao Zhang struct ifb_q_stats rx_stats;
55a21ee5b2STonghao Zhang struct ifb_q_stats tx_stats;
569e29e21aSEric Dumazet } ____cacheline_aligned_in_smp;
579e29e21aSEric Dumazet
589e29e21aSEric Dumazet struct ifb_dev_private {
599e29e21aSEric Dumazet struct ifb_q_private *tx_private;
60253af423SJamal Hadi Salim };
61253af423SJamal Hadi Salim
62a21ee5b2STonghao Zhang /* For ethtools stats. */
63a21ee5b2STonghao Zhang struct ifb_q_stats_desc {
64a21ee5b2STonghao Zhang char desc[ETH_GSTRING_LEN];
65a21ee5b2STonghao Zhang size_t offset;
66a21ee5b2STonghao Zhang };
67a21ee5b2STonghao Zhang
68a21ee5b2STonghao Zhang #define IFB_Q_STAT(m) offsetof(struct ifb_q_stats, m)
69a21ee5b2STonghao Zhang
70a21ee5b2STonghao Zhang static const struct ifb_q_stats_desc ifb_q_stats_desc[] = {
71a21ee5b2STonghao Zhang { "packets", IFB_Q_STAT(packets) },
72a21ee5b2STonghao Zhang { "bytes", IFB_Q_STAT(bytes) },
73a21ee5b2STonghao Zhang };
74a21ee5b2STonghao Zhang
75a21ee5b2STonghao Zhang #define IFB_Q_STATS_LEN ARRAY_SIZE(ifb_q_stats_desc)
76a21ee5b2STonghao Zhang
77424efe9cSStephen Hemminger static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev);
78253af423SJamal Hadi Salim static int ifb_open(struct net_device *dev);
79253af423SJamal Hadi Salim static int ifb_close(struct net_device *dev);
80253af423SJamal Hadi Salim
ifb_update_q_stats(struct ifb_q_stats * stats,int len)81a21ee5b2STonghao Zhang static void ifb_update_q_stats(struct ifb_q_stats *stats, int len)
82a21ee5b2STonghao Zhang {
83a21ee5b2STonghao Zhang u64_stats_update_begin(&stats->sync);
84a21ee5b2STonghao Zhang stats->packets++;
85a21ee5b2STonghao Zhang stats->bytes += len;
86a21ee5b2STonghao Zhang u64_stats_update_end(&stats->sync);
87a21ee5b2STonghao Zhang }
88a21ee5b2STonghao Zhang
ifb_ri_tasklet(struct tasklet_struct * t)8908267523SEmil Renner Berthing static void ifb_ri_tasklet(struct tasklet_struct *t)
90253af423SJamal Hadi Salim {
9108267523SEmil Renner Berthing struct ifb_q_private *txp = from_tasklet(txp, t, ifb_tasklet);
92c3f26a26SDavid S. Miller struct netdev_queue *txq;
93253af423SJamal Hadi Salim struct sk_buff *skb;
94253af423SJamal Hadi Salim
959e29e21aSEric Dumazet txq = netdev_get_tx_queue(txp->dev, txp->txqnum);
969e29e21aSEric Dumazet skb = skb_peek(&txp->tq);
979e29e21aSEric Dumazet if (!skb) {
989e29e21aSEric Dumazet if (!__netif_tx_trylock(txq))
99253af423SJamal Hadi Salim goto resched;
1009e29e21aSEric Dumazet skb_queue_splice_tail_init(&txp->rq, &txp->tq);
1019e29e21aSEric Dumazet __netif_tx_unlock(txq);
102253af423SJamal Hadi Salim }
103253af423SJamal Hadi Salim
1049e29e21aSEric Dumazet while ((skb = __skb_dequeue(&txp->tq)) != NULL) {
10542df6e1dSLukas Wunner /* Skip tc and netfilter to prevent redirection loop. */
1062c64605bSPablo Neira Ayuso skb->redirected = 0;
1077444d706SArnd Bergmann #ifdef CONFIG_NET_CLS_ACT
108e7246e12SWillem de Bruijn skb->tc_skip_classify = 1;
1097444d706SArnd Bergmann #endif
11042df6e1dSLukas Wunner nf_skip_egress(skb, true);
1113b0c9cbbSstephen hemminger
112a21ee5b2STonghao Zhang ifb_update_q_stats(&txp->tx_stats, skb->len);
113c01003c2SPatrick McHardy
11405e8689cSEric Dumazet rcu_read_lock();
1159e29e21aSEric Dumazet skb->dev = dev_get_by_index_rcu(dev_net(txp->dev), skb->skb_iif);
116c01003c2SPatrick McHardy if (!skb->dev) {
11705e8689cSEric Dumazet rcu_read_unlock();
118c01003c2SPatrick McHardy dev_kfree_skb(skb);
1199e29e21aSEric Dumazet txp->dev->stats.tx_dropped++;
1209e29e21aSEric Dumazet if (skb_queue_len(&txp->tq) != 0)
12175c1c825SChangli Gao goto resched;
122c01003c2SPatrick McHardy break;
123c01003c2SPatrick McHardy }
12405e8689cSEric Dumazet rcu_read_unlock();
1259e29e21aSEric Dumazet skb->skb_iif = txp->dev->ifindex;
126c01003c2SPatrick McHardy
1272c64605bSPablo Neira Ayuso if (!skb->from_ingress) {
128253af423SJamal Hadi Salim dev_queue_xmit(skb);
129bc31c905SWillem de Bruijn } else {
130b1d2e4e0SJon Maxwell skb_pull_rcsum(skb, skb->mac_len);
1311a75972cSEric Dumazet netif_receive_skb(skb);
132bc31c905SWillem de Bruijn }
133253af423SJamal Hadi Salim }
134253af423SJamal Hadi Salim
135c3f26a26SDavid S. Miller if (__netif_tx_trylock(txq)) {
1369e29e21aSEric Dumazet skb = skb_peek(&txp->rq);
1379e29e21aSEric Dumazet if (!skb) {
1389e29e21aSEric Dumazet txp->tasklet_pending = 0;
1399e29e21aSEric Dumazet if (netif_tx_queue_stopped(txq))
1409e29e21aSEric Dumazet netif_tx_wake_queue(txq);
141253af423SJamal Hadi Salim } else {
142c3f26a26SDavid S. Miller __netif_tx_unlock(txq);
143253af423SJamal Hadi Salim goto resched;
144253af423SJamal Hadi Salim }
145c3f26a26SDavid S. Miller __netif_tx_unlock(txq);
146253af423SJamal Hadi Salim } else {
147253af423SJamal Hadi Salim resched:
1489e29e21aSEric Dumazet txp->tasklet_pending = 1;
1499e29e21aSEric Dumazet tasklet_schedule(&txp->ifb_tasklet);
150253af423SJamal Hadi Salim }
151253af423SJamal Hadi Salim
152253af423SJamal Hadi Salim }
153253af423SJamal Hadi Salim
ifb_stats64(struct net_device * dev,struct rtnl_link_stats64 * stats)154bc1f4470Sstephen hemminger static void ifb_stats64(struct net_device *dev,
1553b0c9cbbSstephen hemminger struct rtnl_link_stats64 *stats)
1563b0c9cbbSstephen hemminger {
1579e29e21aSEric Dumazet struct ifb_dev_private *dp = netdev_priv(dev);
1589e29e21aSEric Dumazet struct ifb_q_private *txp = dp->tx_private;
1593b0c9cbbSstephen hemminger unsigned int start;
1609e29e21aSEric Dumazet u64 packets, bytes;
1619e29e21aSEric Dumazet int i;
1629e29e21aSEric Dumazet
1639e29e21aSEric Dumazet for (i = 0; i < dev->num_tx_queues; i++,txp++) {
1649e29e21aSEric Dumazet do {
165*068c38adSThomas Gleixner start = u64_stats_fetch_begin(&txp->rx_stats.sync);
166a21ee5b2STonghao Zhang packets = txp->rx_stats.packets;
167a21ee5b2STonghao Zhang bytes = txp->rx_stats.bytes;
168*068c38adSThomas Gleixner } while (u64_stats_fetch_retry(&txp->rx_stats.sync, start));
1699e29e21aSEric Dumazet stats->rx_packets += packets;
1709e29e21aSEric Dumazet stats->rx_bytes += bytes;
1713b0c9cbbSstephen hemminger
1723b0c9cbbSstephen hemminger do {
173*068c38adSThomas Gleixner start = u64_stats_fetch_begin(&txp->tx_stats.sync);
174a21ee5b2STonghao Zhang packets = txp->tx_stats.packets;
175a21ee5b2STonghao Zhang bytes = txp->tx_stats.bytes;
176*068c38adSThomas Gleixner } while (u64_stats_fetch_retry(&txp->tx_stats.sync, start));
1779e29e21aSEric Dumazet stats->tx_packets += packets;
1789e29e21aSEric Dumazet stats->tx_bytes += bytes;
1799e29e21aSEric Dumazet }
1803b0c9cbbSstephen hemminger stats->rx_dropped = dev->stats.rx_dropped;
1813b0c9cbbSstephen hemminger stats->tx_dropped = dev->stats.tx_dropped;
1823b0c9cbbSstephen hemminger }
1833b0c9cbbSstephen hemminger
ifb_dev_init(struct net_device * dev)1849e29e21aSEric Dumazet static int ifb_dev_init(struct net_device *dev)
1859e29e21aSEric Dumazet {
1869e29e21aSEric Dumazet struct ifb_dev_private *dp = netdev_priv(dev);
1879e29e21aSEric Dumazet struct ifb_q_private *txp;
1889e29e21aSEric Dumazet int i;
1899e29e21aSEric Dumazet
1909e29e21aSEric Dumazet txp = kcalloc(dev->num_tx_queues, sizeof(*txp), GFP_KERNEL);
1919e29e21aSEric Dumazet if (!txp)
1929e29e21aSEric Dumazet return -ENOMEM;
1939e29e21aSEric Dumazet dp->tx_private = txp;
1949e29e21aSEric Dumazet for (i = 0; i < dev->num_tx_queues; i++,txp++) {
1959e29e21aSEric Dumazet txp->txqnum = i;
1969e29e21aSEric Dumazet txp->dev = dev;
1979e29e21aSEric Dumazet __skb_queue_head_init(&txp->rq);
1989e29e21aSEric Dumazet __skb_queue_head_init(&txp->tq);
199a21ee5b2STonghao Zhang u64_stats_init(&txp->rx_stats.sync);
200a21ee5b2STonghao Zhang u64_stats_init(&txp->tx_stats.sync);
20108267523SEmil Renner Berthing tasklet_setup(&txp->ifb_tasklet, ifb_ri_tasklet);
2029e29e21aSEric Dumazet netif_tx_start_queue(netdev_get_tx_queue(dev, i));
2039e29e21aSEric Dumazet }
2049e29e21aSEric Dumazet return 0;
2059e29e21aSEric Dumazet }
2063b0c9cbbSstephen hemminger
ifb_get_strings(struct net_device * dev,u32 stringset,u8 * buf)207a21ee5b2STonghao Zhang static void ifb_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
208a21ee5b2STonghao Zhang {
209a21ee5b2STonghao Zhang u8 *p = buf;
210a21ee5b2STonghao Zhang int i, j;
211a21ee5b2STonghao Zhang
212a21ee5b2STonghao Zhang switch (stringset) {
213a21ee5b2STonghao Zhang case ETH_SS_STATS:
214a21ee5b2STonghao Zhang for (i = 0; i < dev->real_num_rx_queues; i++)
215a21ee5b2STonghao Zhang for (j = 0; j < IFB_Q_STATS_LEN; j++)
216a21ee5b2STonghao Zhang ethtool_sprintf(&p, "rx_queue_%u_%.18s",
217a21ee5b2STonghao Zhang i, ifb_q_stats_desc[j].desc);
218a21ee5b2STonghao Zhang
219a21ee5b2STonghao Zhang for (i = 0; i < dev->real_num_tx_queues; i++)
220a21ee5b2STonghao Zhang for (j = 0; j < IFB_Q_STATS_LEN; j++)
221a21ee5b2STonghao Zhang ethtool_sprintf(&p, "tx_queue_%u_%.18s",
222a21ee5b2STonghao Zhang i, ifb_q_stats_desc[j].desc);
223a21ee5b2STonghao Zhang
224a21ee5b2STonghao Zhang break;
225a21ee5b2STonghao Zhang }
226a21ee5b2STonghao Zhang }
227a21ee5b2STonghao Zhang
ifb_get_sset_count(struct net_device * dev,int sset)228a21ee5b2STonghao Zhang static int ifb_get_sset_count(struct net_device *dev, int sset)
229a21ee5b2STonghao Zhang {
230a21ee5b2STonghao Zhang switch (sset) {
231a21ee5b2STonghao Zhang case ETH_SS_STATS:
232a21ee5b2STonghao Zhang return IFB_Q_STATS_LEN * (dev->real_num_rx_queues +
233a21ee5b2STonghao Zhang dev->real_num_tx_queues);
234a21ee5b2STonghao Zhang default:
235a21ee5b2STonghao Zhang return -EOPNOTSUPP;
236a21ee5b2STonghao Zhang }
237a21ee5b2STonghao Zhang }
238a21ee5b2STonghao Zhang
ifb_fill_stats_data(u64 ** data,struct ifb_q_stats * q_stats)239a21ee5b2STonghao Zhang static void ifb_fill_stats_data(u64 **data,
240a21ee5b2STonghao Zhang struct ifb_q_stats *q_stats)
241a21ee5b2STonghao Zhang {
242a21ee5b2STonghao Zhang void *stats_base = (void *)q_stats;
243a21ee5b2STonghao Zhang unsigned int start;
244a21ee5b2STonghao Zhang size_t offset;
245a21ee5b2STonghao Zhang int j;
246a21ee5b2STonghao Zhang
247a21ee5b2STonghao Zhang do {
248*068c38adSThomas Gleixner start = u64_stats_fetch_begin(&q_stats->sync);
249a21ee5b2STonghao Zhang for (j = 0; j < IFB_Q_STATS_LEN; j++) {
250a21ee5b2STonghao Zhang offset = ifb_q_stats_desc[j].offset;
251a21ee5b2STonghao Zhang (*data)[j] = *(u64 *)(stats_base + offset);
252a21ee5b2STonghao Zhang }
253*068c38adSThomas Gleixner } while (u64_stats_fetch_retry(&q_stats->sync, start));
254a21ee5b2STonghao Zhang
255a21ee5b2STonghao Zhang *data += IFB_Q_STATS_LEN;
256a21ee5b2STonghao Zhang }
257a21ee5b2STonghao Zhang
ifb_get_ethtool_stats(struct net_device * dev,struct ethtool_stats * stats,u64 * data)258a21ee5b2STonghao Zhang static void ifb_get_ethtool_stats(struct net_device *dev,
259a21ee5b2STonghao Zhang struct ethtool_stats *stats, u64 *data)
260a21ee5b2STonghao Zhang {
261a21ee5b2STonghao Zhang struct ifb_dev_private *dp = netdev_priv(dev);
262a21ee5b2STonghao Zhang struct ifb_q_private *txp;
263a21ee5b2STonghao Zhang int i;
264a21ee5b2STonghao Zhang
265a21ee5b2STonghao Zhang for (i = 0; i < dev->real_num_rx_queues; i++) {
266a21ee5b2STonghao Zhang txp = dp->tx_private + i;
267a21ee5b2STonghao Zhang ifb_fill_stats_data(&data, &txp->rx_stats);
268a21ee5b2STonghao Zhang }
269a21ee5b2STonghao Zhang
270a21ee5b2STonghao Zhang for (i = 0; i < dev->real_num_tx_queues; i++) {
271a21ee5b2STonghao Zhang txp = dp->tx_private + i;
272a21ee5b2STonghao Zhang ifb_fill_stats_data(&data, &txp->tx_stats);
273a21ee5b2STonghao Zhang }
274a21ee5b2STonghao Zhang }
275a21ee5b2STonghao Zhang
2768dfcdf34SStephen Hemminger static const struct net_device_ops ifb_netdev_ops = {
2778dfcdf34SStephen Hemminger .ndo_open = ifb_open,
2788dfcdf34SStephen Hemminger .ndo_stop = ifb_close,
2793b0c9cbbSstephen hemminger .ndo_get_stats64 = ifb_stats64,
28000829823SStephen Hemminger .ndo_start_xmit = ifb_xmit,
28100829823SStephen Hemminger .ndo_validate_addr = eth_validate_addr,
2829e29e21aSEric Dumazet .ndo_init = ifb_dev_init,
2838dfcdf34SStephen Hemminger };
2848dfcdf34SStephen Hemminger
285a21ee5b2STonghao Zhang static const struct ethtool_ops ifb_ethtool_ops = {
286a21ee5b2STonghao Zhang .get_strings = ifb_get_strings,
287a21ee5b2STonghao Zhang .get_sset_count = ifb_get_sset_count,
288a21ee5b2STonghao Zhang .get_ethtool_stats = ifb_get_ethtool_stats,
289a21ee5b2STonghao Zhang };
290a21ee5b2STonghao Zhang
29134324dc2SMichał Mirosław #define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \
292ecb8fed4SAlexander Lobakin NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL | \
29328d2b136SPatrick McHardy NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX | \
29428d2b136SPatrick McHardy NETIF_F_HW_VLAN_STAG_TX)
29539980292SEric Dumazet
ifb_dev_free(struct net_device * dev)2969e29e21aSEric Dumazet static void ifb_dev_free(struct net_device *dev)
2979e29e21aSEric Dumazet {
2989e29e21aSEric Dumazet struct ifb_dev_private *dp = netdev_priv(dev);
2999e29e21aSEric Dumazet struct ifb_q_private *txp = dp->tx_private;
3009e29e21aSEric Dumazet int i;
3019e29e21aSEric Dumazet
3029e29e21aSEric Dumazet for (i = 0; i < dev->num_tx_queues; i++,txp++) {
3039e29e21aSEric Dumazet tasklet_kill(&txp->ifb_tasklet);
3049e29e21aSEric Dumazet __skb_queue_purge(&txp->rq);
3059e29e21aSEric Dumazet __skb_queue_purge(&txp->tq);
3069e29e21aSEric Dumazet }
3079e29e21aSEric Dumazet kfree(dp->tx_private);
3089e29e21aSEric Dumazet }
3099e29e21aSEric Dumazet
ifb_setup(struct net_device * dev)3109ba2cd65SPatrick McHardy static void ifb_setup(struct net_device *dev)
311253af423SJamal Hadi Salim {
312253af423SJamal Hadi Salim /* Initialize the device structure. */
3138dfcdf34SStephen Hemminger dev->netdev_ops = &ifb_netdev_ops;
314a21ee5b2STonghao Zhang dev->ethtool_ops = &ifb_ethtool_ops;
315253af423SJamal Hadi Salim
316253af423SJamal Hadi Salim /* Fill in device structure with ethernet-generic values. */
317253af423SJamal Hadi Salim ether_setup(dev);
318253af423SJamal Hadi Salim dev->tx_queue_len = TX_Q_LIMIT;
3198dfcdf34SStephen Hemminger
32039980292SEric Dumazet dev->features |= IFB_FEATURES;
3217d945796SEric Dumazet dev->hw_features |= dev->features;
3227d945796SEric Dumazet dev->hw_enc_features |= dev->features;
3238dd6e147SVlad Yasevich dev->vlan_features |= IFB_FEATURES & ~(NETIF_F_HW_VLAN_CTAG_TX |
3248dd6e147SVlad Yasevich NETIF_F_HW_VLAN_STAG_TX);
32539980292SEric Dumazet
326253af423SJamal Hadi Salim dev->flags |= IFF_NOARP;
327253af423SJamal Hadi Salim dev->flags &= ~IFF_MULTICAST;
32802875878SEric Dumazet dev->priv_flags &= ~IFF_TX_SKB_SHARING;
32902875878SEric Dumazet netif_keep_dst(dev);
330f2cedb63SDanny Kukawka eth_hw_addr_random(dev);
331cf124db5SDavid S. Miller dev->needs_free_netdev = true;
332cf124db5SDavid S. Miller dev->priv_destructor = ifb_dev_free;
333e94cd811SZhang Shengju
334e94cd811SZhang Shengju dev->min_mtu = 0;
335e94cd811SZhang Shengju dev->max_mtu = 0;
336253af423SJamal Hadi Salim }
337253af423SJamal Hadi Salim
ifb_xmit(struct sk_buff * skb,struct net_device * dev)338424efe9cSStephen Hemminger static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
339253af423SJamal Hadi Salim {
3409e29e21aSEric Dumazet struct ifb_dev_private *dp = netdev_priv(dev);
3419e29e21aSEric Dumazet struct ifb_q_private *txp = dp->tx_private + skb_get_queue_mapping(skb);
342253af423SJamal Hadi Salim
343a21ee5b2STonghao Zhang ifb_update_q_stats(&txp->rx_stats, skb->len);
344253af423SJamal Hadi Salim
3452c64605bSPablo Neira Ayuso if (!skb->redirected || !skb->skb_iif) {
346253af423SJamal Hadi Salim dev_kfree_skb(skb);
3473b0c9cbbSstephen hemminger dev->stats.rx_dropped++;
348424efe9cSStephen Hemminger return NETDEV_TX_OK;
349253af423SJamal Hadi Salim }
350253af423SJamal Hadi Salim
3519e29e21aSEric Dumazet if (skb_queue_len(&txp->rq) >= dev->tx_queue_len)
3529e29e21aSEric Dumazet netif_tx_stop_queue(netdev_get_tx_queue(dev, txp->txqnum));
353253af423SJamal Hadi Salim
3549e29e21aSEric Dumazet __skb_queue_tail(&txp->rq, skb);
3559e29e21aSEric Dumazet if (!txp->tasklet_pending) {
3569e29e21aSEric Dumazet txp->tasklet_pending = 1;
3579e29e21aSEric Dumazet tasklet_schedule(&txp->ifb_tasklet);
358253af423SJamal Hadi Salim }
359253af423SJamal Hadi Salim
360424efe9cSStephen Hemminger return NETDEV_TX_OK;
361253af423SJamal Hadi Salim }
362253af423SJamal Hadi Salim
ifb_close(struct net_device * dev)363253af423SJamal Hadi Salim static int ifb_close(struct net_device *dev)
364253af423SJamal Hadi Salim {
3659e29e21aSEric Dumazet netif_tx_stop_all_queues(dev);
366253af423SJamal Hadi Salim return 0;
367253af423SJamal Hadi Salim }
368253af423SJamal Hadi Salim
ifb_open(struct net_device * dev)369253af423SJamal Hadi Salim static int ifb_open(struct net_device *dev)
370253af423SJamal Hadi Salim {
3719e29e21aSEric Dumazet netif_tx_start_all_queues(dev);
372253af423SJamal Hadi Salim return 0;
373253af423SJamal Hadi Salim }
374253af423SJamal Hadi Salim
ifb_validate(struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)375a8b8a889SMatthias Schiffer static int ifb_validate(struct nlattr *tb[], struct nlattr *data[],
376a8b8a889SMatthias Schiffer struct netlink_ext_ack *extack)
3770e06877cSPatrick McHardy {
3780e06877cSPatrick McHardy if (tb[IFLA_ADDRESS]) {
3790e06877cSPatrick McHardy if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
3800e06877cSPatrick McHardy return -EINVAL;
3810e06877cSPatrick McHardy if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
3820e06877cSPatrick McHardy return -EADDRNOTAVAIL;
3830e06877cSPatrick McHardy }
3840e06877cSPatrick McHardy return 0;
3850e06877cSPatrick McHardy }
3860e06877cSPatrick McHardy
3879ba2cd65SPatrick McHardy static struct rtnl_link_ops ifb_link_ops __read_mostly = {
3889ba2cd65SPatrick McHardy .kind = "ifb",
3899e29e21aSEric Dumazet .priv_size = sizeof(struct ifb_dev_private),
3909ba2cd65SPatrick McHardy .setup = ifb_setup,
3910e06877cSPatrick McHardy .validate = ifb_validate,
3929ba2cd65SPatrick McHardy };
3939ba2cd65SPatrick McHardy
3949e29e21aSEric Dumazet /* Number of ifb devices to be set up by this module.
3959e29e21aSEric Dumazet * Note that these legacy devices have one queue.
3969e29e21aSEric Dumazet * Prefer something like : ip link add ifb10 numtxqueues 8 type ifb
3979e29e21aSEric Dumazet */
3989e29e21aSEric Dumazet static int numifbs = 2;
3992d85cba2SPatrick McHardy module_param(numifbs, int, 0);
4002d85cba2SPatrick McHardy MODULE_PARM_DESC(numifbs, "Number of ifb devices");
4012d85cba2SPatrick McHardy
ifb_init_one(int index)402253af423SJamal Hadi Salim static int __init ifb_init_one(int index)
403253af423SJamal Hadi Salim {
404253af423SJamal Hadi Salim struct net_device *dev_ifb;
405253af423SJamal Hadi Salim int err;
406253af423SJamal Hadi Salim
4079e29e21aSEric Dumazet dev_ifb = alloc_netdev(sizeof(struct ifb_dev_private), "ifb%d",
408c835a677STom Gundersen NET_NAME_UNKNOWN, ifb_setup);
409253af423SJamal Hadi Salim
410253af423SJamal Hadi Salim if (!dev_ifb)
411253af423SJamal Hadi Salim return -ENOMEM;
412253af423SJamal Hadi Salim
4139ba2cd65SPatrick McHardy dev_ifb->rtnl_link_ops = &ifb_link_ops;
4149ba2cd65SPatrick McHardy err = register_netdevice(dev_ifb);
4159ba2cd65SPatrick McHardy if (err < 0)
4169ba2cd65SPatrick McHardy goto err;
41794833dfbSJarek Poplawski
4189ba2cd65SPatrick McHardy return 0;
419253af423SJamal Hadi Salim
4209ba2cd65SPatrick McHardy err:
4219ba2cd65SPatrick McHardy free_netdev(dev_ifb);
422253af423SJamal Hadi Salim return err;
423253af423SJamal Hadi Salim }
424253af423SJamal Hadi Salim
ifb_init_module(void)425253af423SJamal Hadi Salim static int __init ifb_init_module(void)
426253af423SJamal Hadi Salim {
4279ba2cd65SPatrick McHardy int i, err;
4289ba2cd65SPatrick McHardy
429554873e5SKirill Tkhai down_write(&pernet_ops_rwsem);
4309ba2cd65SPatrick McHardy rtnl_lock();
4319ba2cd65SPatrick McHardy err = __rtnl_link_register(&ifb_link_ops);
432f2966cd5Sdingtianhong if (err < 0)
433f2966cd5Sdingtianhong goto out;
43462b7ffcaSPatrick McHardy
435440d57bcSdingtianhong for (i = 0; i < numifbs && !err; i++) {
436253af423SJamal Hadi Salim err = ifb_init_one(i);
437440d57bcSdingtianhong cond_resched();
438440d57bcSdingtianhong }
4392d85cba2SPatrick McHardy if (err)
4409ba2cd65SPatrick McHardy __rtnl_link_unregister(&ifb_link_ops);
441f2966cd5Sdingtianhong
442f2966cd5Sdingtianhong out:
4439ba2cd65SPatrick McHardy rtnl_unlock();
444554873e5SKirill Tkhai up_write(&pernet_ops_rwsem);
445253af423SJamal Hadi Salim
446253af423SJamal Hadi Salim return err;
447253af423SJamal Hadi Salim }
448253af423SJamal Hadi Salim
ifb_cleanup_module(void)449253af423SJamal Hadi Salim static void __exit ifb_cleanup_module(void)
450253af423SJamal Hadi Salim {
4512d85cba2SPatrick McHardy rtnl_link_unregister(&ifb_link_ops);
452253af423SJamal Hadi Salim }
453253af423SJamal Hadi Salim
454253af423SJamal Hadi Salim module_init(ifb_init_module);
455253af423SJamal Hadi Salim module_exit(ifb_cleanup_module);
456253af423SJamal Hadi Salim MODULE_LICENSE("GPL");
457253af423SJamal Hadi Salim MODULE_AUTHOR("Jamal Hadi Salim");
4589ba2cd65SPatrick McHardy MODULE_ALIAS_RTNL_LINK("ifb");
459