162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* drivers/net/ifb.c: 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci The purpose of this driver is to provide a device that allows 562306a36Sopenharmony_ci for sharing of resources: 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci 1) qdiscs/policies that are per device as opposed to system wide. 862306a36Sopenharmony_ci ifb allows for a device which can be redirected to thus providing 962306a36Sopenharmony_ci an impression of sharing. 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci 2) Allows for queueing incoming traffic for shaping instead of 1262306a36Sopenharmony_ci dropping. 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci The original concept is based on what is known as the IMQ 1562306a36Sopenharmony_ci driver initially written by Martin Devera, later rewritten 1662306a36Sopenharmony_ci by Patrick McHardy and then maintained by Andre Correa. 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci You need the tc action mirror or redirect to feed this device 1962306a36Sopenharmony_ci packets. 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci Authors: Jamal Hadi Salim (2005) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci*/ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/kernel.h> 2962306a36Sopenharmony_ci#include <linux/netdevice.h> 3062306a36Sopenharmony_ci#include <linux/ethtool.h> 3162306a36Sopenharmony_ci#include <linux/etherdevice.h> 3262306a36Sopenharmony_ci#include <linux/init.h> 3362306a36Sopenharmony_ci#include <linux/interrupt.h> 3462306a36Sopenharmony_ci#include <linux/moduleparam.h> 3562306a36Sopenharmony_ci#include <linux/netfilter_netdev.h> 3662306a36Sopenharmony_ci#include <net/pkt_sched.h> 3762306a36Sopenharmony_ci#include <net/net_namespace.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define TX_Q_LIMIT 32 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct ifb_q_stats { 4262306a36Sopenharmony_ci u64 packets; 4362306a36Sopenharmony_ci u64 bytes; 4462306a36Sopenharmony_ci struct u64_stats_sync sync; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct ifb_q_private { 4862306a36Sopenharmony_ci struct net_device *dev; 4962306a36Sopenharmony_ci struct tasklet_struct ifb_tasklet; 5062306a36Sopenharmony_ci int tasklet_pending; 5162306a36Sopenharmony_ci int txqnum; 5262306a36Sopenharmony_ci struct sk_buff_head rq; 5362306a36Sopenharmony_ci struct sk_buff_head tq; 5462306a36Sopenharmony_ci struct ifb_q_stats rx_stats; 5562306a36Sopenharmony_ci struct ifb_q_stats tx_stats; 5662306a36Sopenharmony_ci} ____cacheline_aligned_in_smp; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct ifb_dev_private { 5962306a36Sopenharmony_ci struct ifb_q_private *tx_private; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* For ethtools stats. */ 6362306a36Sopenharmony_cistruct ifb_q_stats_desc { 6462306a36Sopenharmony_ci char desc[ETH_GSTRING_LEN]; 6562306a36Sopenharmony_ci size_t offset; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define IFB_Q_STAT(m) offsetof(struct ifb_q_stats, m) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const struct ifb_q_stats_desc ifb_q_stats_desc[] = { 7162306a36Sopenharmony_ci { "packets", IFB_Q_STAT(packets) }, 7262306a36Sopenharmony_ci { "bytes", IFB_Q_STAT(bytes) }, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define IFB_Q_STATS_LEN ARRAY_SIZE(ifb_q_stats_desc) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev); 7862306a36Sopenharmony_cistatic int ifb_open(struct net_device *dev); 7962306a36Sopenharmony_cistatic int ifb_close(struct net_device *dev); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void ifb_update_q_stats(struct ifb_q_stats *stats, int len) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u64_stats_update_begin(&stats->sync); 8462306a36Sopenharmony_ci stats->packets++; 8562306a36Sopenharmony_ci stats->bytes += len; 8662306a36Sopenharmony_ci u64_stats_update_end(&stats->sync); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void ifb_ri_tasklet(struct tasklet_struct *t) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct ifb_q_private *txp = from_tasklet(txp, t, ifb_tasklet); 9262306a36Sopenharmony_ci struct netdev_queue *txq; 9362306a36Sopenharmony_ci struct sk_buff *skb; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci txq = netdev_get_tx_queue(txp->dev, txp->txqnum); 9662306a36Sopenharmony_ci skb = skb_peek(&txp->tq); 9762306a36Sopenharmony_ci if (!skb) { 9862306a36Sopenharmony_ci if (!__netif_tx_trylock(txq)) 9962306a36Sopenharmony_ci goto resched; 10062306a36Sopenharmony_ci skb_queue_splice_tail_init(&txp->rq, &txp->tq); 10162306a36Sopenharmony_ci __netif_tx_unlock(txq); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci while ((skb = __skb_dequeue(&txp->tq)) != NULL) { 10562306a36Sopenharmony_ci /* Skip tc and netfilter to prevent redirection loop. */ 10662306a36Sopenharmony_ci skb->redirected = 0; 10762306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 10862306a36Sopenharmony_ci skb->tc_skip_classify = 1; 10962306a36Sopenharmony_ci#endif 11062306a36Sopenharmony_ci nf_skip_egress(skb, true); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ifb_update_q_stats(&txp->tx_stats, skb->len); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci rcu_read_lock(); 11562306a36Sopenharmony_ci skb->dev = dev_get_by_index_rcu(dev_net(txp->dev), skb->skb_iif); 11662306a36Sopenharmony_ci if (!skb->dev) { 11762306a36Sopenharmony_ci rcu_read_unlock(); 11862306a36Sopenharmony_ci dev_kfree_skb(skb); 11962306a36Sopenharmony_ci txp->dev->stats.tx_dropped++; 12062306a36Sopenharmony_ci if (skb_queue_len(&txp->tq) != 0) 12162306a36Sopenharmony_ci goto resched; 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci rcu_read_unlock(); 12562306a36Sopenharmony_ci skb->skb_iif = txp->dev->ifindex; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!skb->from_ingress) { 12862306a36Sopenharmony_ci dev_queue_xmit(skb); 12962306a36Sopenharmony_ci } else { 13062306a36Sopenharmony_ci skb_pull_rcsum(skb, skb->mac_len); 13162306a36Sopenharmony_ci netif_receive_skb(skb); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (__netif_tx_trylock(txq)) { 13662306a36Sopenharmony_ci skb = skb_peek(&txp->rq); 13762306a36Sopenharmony_ci if (!skb) { 13862306a36Sopenharmony_ci txp->tasklet_pending = 0; 13962306a36Sopenharmony_ci if (netif_tx_queue_stopped(txq)) 14062306a36Sopenharmony_ci netif_tx_wake_queue(txq); 14162306a36Sopenharmony_ci } else { 14262306a36Sopenharmony_ci __netif_tx_unlock(txq); 14362306a36Sopenharmony_ci goto resched; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci __netif_tx_unlock(txq); 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ciresched: 14862306a36Sopenharmony_ci txp->tasklet_pending = 1; 14962306a36Sopenharmony_ci tasklet_schedule(&txp->ifb_tasklet); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void ifb_stats64(struct net_device *dev, 15562306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct ifb_dev_private *dp = netdev_priv(dev); 15862306a36Sopenharmony_ci struct ifb_q_private *txp = dp->tx_private; 15962306a36Sopenharmony_ci unsigned int start; 16062306a36Sopenharmony_ci u64 packets, bytes; 16162306a36Sopenharmony_ci int i; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci for (i = 0; i < dev->num_tx_queues; i++,txp++) { 16462306a36Sopenharmony_ci do { 16562306a36Sopenharmony_ci start = u64_stats_fetch_begin(&txp->rx_stats.sync); 16662306a36Sopenharmony_ci packets = txp->rx_stats.packets; 16762306a36Sopenharmony_ci bytes = txp->rx_stats.bytes; 16862306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&txp->rx_stats.sync, start)); 16962306a36Sopenharmony_ci stats->rx_packets += packets; 17062306a36Sopenharmony_ci stats->rx_bytes += bytes; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci do { 17362306a36Sopenharmony_ci start = u64_stats_fetch_begin(&txp->tx_stats.sync); 17462306a36Sopenharmony_ci packets = txp->tx_stats.packets; 17562306a36Sopenharmony_ci bytes = txp->tx_stats.bytes; 17662306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&txp->tx_stats.sync, start)); 17762306a36Sopenharmony_ci stats->tx_packets += packets; 17862306a36Sopenharmony_ci stats->tx_bytes += bytes; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci stats->rx_dropped = dev->stats.rx_dropped; 18162306a36Sopenharmony_ci stats->tx_dropped = dev->stats.tx_dropped; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int ifb_dev_init(struct net_device *dev) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct ifb_dev_private *dp = netdev_priv(dev); 18762306a36Sopenharmony_ci struct ifb_q_private *txp; 18862306a36Sopenharmony_ci int i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci txp = kcalloc(dev->num_tx_queues, sizeof(*txp), GFP_KERNEL); 19162306a36Sopenharmony_ci if (!txp) 19262306a36Sopenharmony_ci return -ENOMEM; 19362306a36Sopenharmony_ci dp->tx_private = txp; 19462306a36Sopenharmony_ci for (i = 0; i < dev->num_tx_queues; i++,txp++) { 19562306a36Sopenharmony_ci txp->txqnum = i; 19662306a36Sopenharmony_ci txp->dev = dev; 19762306a36Sopenharmony_ci __skb_queue_head_init(&txp->rq); 19862306a36Sopenharmony_ci __skb_queue_head_init(&txp->tq); 19962306a36Sopenharmony_ci u64_stats_init(&txp->rx_stats.sync); 20062306a36Sopenharmony_ci u64_stats_init(&txp->tx_stats.sync); 20162306a36Sopenharmony_ci tasklet_setup(&txp->ifb_tasklet, ifb_ri_tasklet); 20262306a36Sopenharmony_ci netif_tx_start_queue(netdev_get_tx_queue(dev, i)); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void ifb_get_strings(struct net_device *dev, u32 stringset, u8 *buf) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u8 *p = buf; 21062306a36Sopenharmony_ci int i, j; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci switch (stringset) { 21362306a36Sopenharmony_ci case ETH_SS_STATS: 21462306a36Sopenharmony_ci for (i = 0; i < dev->real_num_rx_queues; i++) 21562306a36Sopenharmony_ci for (j = 0; j < IFB_Q_STATS_LEN; j++) 21662306a36Sopenharmony_ci ethtool_sprintf(&p, "rx_queue_%u_%.18s", 21762306a36Sopenharmony_ci i, ifb_q_stats_desc[j].desc); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci for (i = 0; i < dev->real_num_tx_queues; i++) 22062306a36Sopenharmony_ci for (j = 0; j < IFB_Q_STATS_LEN; j++) 22162306a36Sopenharmony_ci ethtool_sprintf(&p, "tx_queue_%u_%.18s", 22262306a36Sopenharmony_ci i, ifb_q_stats_desc[j].desc); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int ifb_get_sset_count(struct net_device *dev, int sset) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci switch (sset) { 23162306a36Sopenharmony_ci case ETH_SS_STATS: 23262306a36Sopenharmony_ci return IFB_Q_STATS_LEN * (dev->real_num_rx_queues + 23362306a36Sopenharmony_ci dev->real_num_tx_queues); 23462306a36Sopenharmony_ci default: 23562306a36Sopenharmony_ci return -EOPNOTSUPP; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void ifb_fill_stats_data(u64 **data, 24062306a36Sopenharmony_ci struct ifb_q_stats *q_stats) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci void *stats_base = (void *)q_stats; 24362306a36Sopenharmony_ci unsigned int start; 24462306a36Sopenharmony_ci size_t offset; 24562306a36Sopenharmony_ci int j; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci do { 24862306a36Sopenharmony_ci start = u64_stats_fetch_begin(&q_stats->sync); 24962306a36Sopenharmony_ci for (j = 0; j < IFB_Q_STATS_LEN; j++) { 25062306a36Sopenharmony_ci offset = ifb_q_stats_desc[j].offset; 25162306a36Sopenharmony_ci (*data)[j] = *(u64 *)(stats_base + offset); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&q_stats->sync, start)); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci *data += IFB_Q_STATS_LEN; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void ifb_get_ethtool_stats(struct net_device *dev, 25962306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct ifb_dev_private *dp = netdev_priv(dev); 26262306a36Sopenharmony_ci struct ifb_q_private *txp; 26362306a36Sopenharmony_ci int i; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci for (i = 0; i < dev->real_num_rx_queues; i++) { 26662306a36Sopenharmony_ci txp = dp->tx_private + i; 26762306a36Sopenharmony_ci ifb_fill_stats_data(&data, &txp->rx_stats); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for (i = 0; i < dev->real_num_tx_queues; i++) { 27162306a36Sopenharmony_ci txp = dp->tx_private + i; 27262306a36Sopenharmony_ci ifb_fill_stats_data(&data, &txp->tx_stats); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic const struct net_device_ops ifb_netdev_ops = { 27762306a36Sopenharmony_ci .ndo_open = ifb_open, 27862306a36Sopenharmony_ci .ndo_stop = ifb_close, 27962306a36Sopenharmony_ci .ndo_get_stats64 = ifb_stats64, 28062306a36Sopenharmony_ci .ndo_start_xmit = ifb_xmit, 28162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 28262306a36Sopenharmony_ci .ndo_init = ifb_dev_init, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic const struct ethtool_ops ifb_ethtool_ops = { 28662306a36Sopenharmony_ci .get_strings = ifb_get_strings, 28762306a36Sopenharmony_ci .get_sset_count = ifb_get_sset_count, 28862306a36Sopenharmony_ci .get_ethtool_stats = ifb_get_ethtool_stats, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \ 29262306a36Sopenharmony_ci NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL | \ 29362306a36Sopenharmony_ci NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX | \ 29462306a36Sopenharmony_ci NETIF_F_HW_VLAN_STAG_TX) 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void ifb_dev_free(struct net_device *dev) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct ifb_dev_private *dp = netdev_priv(dev); 29962306a36Sopenharmony_ci struct ifb_q_private *txp = dp->tx_private; 30062306a36Sopenharmony_ci int i; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci for (i = 0; i < dev->num_tx_queues; i++,txp++) { 30362306a36Sopenharmony_ci tasklet_kill(&txp->ifb_tasklet); 30462306a36Sopenharmony_ci __skb_queue_purge(&txp->rq); 30562306a36Sopenharmony_ci __skb_queue_purge(&txp->tq); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci kfree(dp->tx_private); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void ifb_setup(struct net_device *dev) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci /* Initialize the device structure. */ 31362306a36Sopenharmony_ci dev->netdev_ops = &ifb_netdev_ops; 31462306a36Sopenharmony_ci dev->ethtool_ops = &ifb_ethtool_ops; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Fill in device structure with ethernet-generic values. */ 31762306a36Sopenharmony_ci ether_setup(dev); 31862306a36Sopenharmony_ci dev->tx_queue_len = TX_Q_LIMIT; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci dev->features |= IFB_FEATURES; 32162306a36Sopenharmony_ci dev->hw_features |= dev->features; 32262306a36Sopenharmony_ci dev->hw_enc_features |= dev->features; 32362306a36Sopenharmony_ci dev->vlan_features |= IFB_FEATURES & ~(NETIF_F_HW_VLAN_CTAG_TX | 32462306a36Sopenharmony_ci NETIF_F_HW_VLAN_STAG_TX); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci dev->flags |= IFF_NOARP; 32762306a36Sopenharmony_ci dev->flags &= ~IFF_MULTICAST; 32862306a36Sopenharmony_ci dev->priv_flags &= ~IFF_TX_SKB_SHARING; 32962306a36Sopenharmony_ci netif_keep_dst(dev); 33062306a36Sopenharmony_ci eth_hw_addr_random(dev); 33162306a36Sopenharmony_ci dev->needs_free_netdev = true; 33262306a36Sopenharmony_ci dev->priv_destructor = ifb_dev_free; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci dev->min_mtu = 0; 33562306a36Sopenharmony_ci dev->max_mtu = 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct ifb_dev_private *dp = netdev_priv(dev); 34162306a36Sopenharmony_ci struct ifb_q_private *txp = dp->tx_private + skb_get_queue_mapping(skb); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ifb_update_q_stats(&txp->rx_stats, skb->len); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!skb->redirected || !skb->skb_iif) { 34662306a36Sopenharmony_ci dev_kfree_skb(skb); 34762306a36Sopenharmony_ci dev->stats.rx_dropped++; 34862306a36Sopenharmony_ci return NETDEV_TX_OK; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (skb_queue_len(&txp->rq) >= dev->tx_queue_len) 35262306a36Sopenharmony_ci netif_tx_stop_queue(netdev_get_tx_queue(dev, txp->txqnum)); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci __skb_queue_tail(&txp->rq, skb); 35562306a36Sopenharmony_ci if (!txp->tasklet_pending) { 35662306a36Sopenharmony_ci txp->tasklet_pending = 1; 35762306a36Sopenharmony_ci tasklet_schedule(&txp->ifb_tasklet); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return NETDEV_TX_OK; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int ifb_close(struct net_device *dev) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci netif_tx_stop_all_queues(dev); 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int ifb_open(struct net_device *dev) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int ifb_validate(struct nlattr *tb[], struct nlattr *data[], 37662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci if (tb[IFLA_ADDRESS]) { 37962306a36Sopenharmony_ci if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) 38262306a36Sopenharmony_ci return -EADDRNOTAVAIL; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic struct rtnl_link_ops ifb_link_ops __read_mostly = { 38862306a36Sopenharmony_ci .kind = "ifb", 38962306a36Sopenharmony_ci .priv_size = sizeof(struct ifb_dev_private), 39062306a36Sopenharmony_ci .setup = ifb_setup, 39162306a36Sopenharmony_ci .validate = ifb_validate, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* Number of ifb devices to be set up by this module. 39562306a36Sopenharmony_ci * Note that these legacy devices have one queue. 39662306a36Sopenharmony_ci * Prefer something like : ip link add ifb10 numtxqueues 8 type ifb 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_cistatic int numifbs = 2; 39962306a36Sopenharmony_cimodule_param(numifbs, int, 0); 40062306a36Sopenharmony_ciMODULE_PARM_DESC(numifbs, "Number of ifb devices"); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int __init ifb_init_one(int index) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct net_device *dev_ifb; 40562306a36Sopenharmony_ci int err; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci dev_ifb = alloc_netdev(sizeof(struct ifb_dev_private), "ifb%d", 40862306a36Sopenharmony_ci NET_NAME_UNKNOWN, ifb_setup); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (!dev_ifb) 41162306a36Sopenharmony_ci return -ENOMEM; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci dev_ifb->rtnl_link_ops = &ifb_link_ops; 41462306a36Sopenharmony_ci err = register_netdevice(dev_ifb); 41562306a36Sopenharmony_ci if (err < 0) 41662306a36Sopenharmony_ci goto err; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cierr: 42162306a36Sopenharmony_ci free_netdev(dev_ifb); 42262306a36Sopenharmony_ci return err; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int __init ifb_init_module(void) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci int i, err; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci down_write(&pernet_ops_rwsem); 43062306a36Sopenharmony_ci rtnl_lock(); 43162306a36Sopenharmony_ci err = __rtnl_link_register(&ifb_link_ops); 43262306a36Sopenharmony_ci if (err < 0) 43362306a36Sopenharmony_ci goto out; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci for (i = 0; i < numifbs && !err; i++) { 43662306a36Sopenharmony_ci err = ifb_init_one(i); 43762306a36Sopenharmony_ci cond_resched(); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci if (err) 44062306a36Sopenharmony_ci __rtnl_link_unregister(&ifb_link_ops); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ciout: 44362306a36Sopenharmony_ci rtnl_unlock(); 44462306a36Sopenharmony_ci up_write(&pernet_ops_rwsem); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return err; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic void __exit ifb_cleanup_module(void) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci rtnl_link_unregister(&ifb_link_ops); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cimodule_init(ifb_init_module); 45562306a36Sopenharmony_cimodule_exit(ifb_cleanup_module); 45662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 45762306a36Sopenharmony_ciMODULE_AUTHOR("Jamal Hadi Salim"); 45862306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ifb"); 459