xref: /openbmc/linux/drivers/net/ifb.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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