162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2018, Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* This provides a net_failover interface for paravirtual drivers to 562306a36Sopenharmony_ci * provide an alternate datapath by exporting APIs to create and 662306a36Sopenharmony_ci * destroy a upper 'net_failover' netdev. The upper dev manages the 762306a36Sopenharmony_ci * original paravirtual interface as a 'standby' netdev and uses the 862306a36Sopenharmony_ci * generic failover infrastructure to register and manage a direct 962306a36Sopenharmony_ci * attached VF as a 'primary' netdev. This enables live migration of 1062306a36Sopenharmony_ci * a VM with direct attached VF by failing over to the paravirtual 1162306a36Sopenharmony_ci * datapath when the VF is unplugged. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Some of the netdev management routines are based on bond/team driver as 1462306a36Sopenharmony_ci * this driver provides active-backup functionality similar to those drivers. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/netdevice.h> 1862306a36Sopenharmony_ci#include <linux/etherdevice.h> 1962306a36Sopenharmony_ci#include <linux/ethtool.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/netpoll.h> 2362306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2462306a36Sopenharmony_ci#include <linux/if_vlan.h> 2562306a36Sopenharmony_ci#include <linux/pci.h> 2662306a36Sopenharmony_ci#include <net/sch_generic.h> 2762306a36Sopenharmony_ci#include <uapi/linux/if_arp.h> 2862306a36Sopenharmony_ci#include <net/net_failover.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic bool net_failover_xmit_ready(struct net_device *dev) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return netif_running(dev) && netif_carrier_ok(dev); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int net_failover_open(struct net_device *dev) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 3862306a36Sopenharmony_ci struct net_device *primary_dev, *standby_dev; 3962306a36Sopenharmony_ci int err; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 4262306a36Sopenharmony_ci if (primary_dev) { 4362306a36Sopenharmony_ci err = dev_open(primary_dev, NULL); 4462306a36Sopenharmony_ci if (err) 4562306a36Sopenharmony_ci goto err_primary_open; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 4962306a36Sopenharmony_ci if (standby_dev) { 5062306a36Sopenharmony_ci err = dev_open(standby_dev, NULL); 5162306a36Sopenharmony_ci if (err) 5262306a36Sopenharmony_ci goto err_standby_open; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if ((primary_dev && net_failover_xmit_ready(primary_dev)) || 5662306a36Sopenharmony_ci (standby_dev && net_failover_xmit_ready(standby_dev))) { 5762306a36Sopenharmony_ci netif_carrier_on(dev); 5862306a36Sopenharmony_ci netif_tx_wake_all_queues(dev); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cierr_standby_open: 6462306a36Sopenharmony_ci if (primary_dev) 6562306a36Sopenharmony_ci dev_close(primary_dev); 6662306a36Sopenharmony_cierr_primary_open: 6762306a36Sopenharmony_ci netif_tx_disable(dev); 6862306a36Sopenharmony_ci return err; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int net_failover_close(struct net_device *dev) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 7462306a36Sopenharmony_ci struct net_device *slave_dev; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci netif_tx_disable(dev); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci slave_dev = rtnl_dereference(nfo_info->primary_dev); 7962306a36Sopenharmony_ci if (slave_dev) 8062306a36Sopenharmony_ci dev_close(slave_dev); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci slave_dev = rtnl_dereference(nfo_info->standby_dev); 8362306a36Sopenharmony_ci if (slave_dev) 8462306a36Sopenharmony_ci dev_close(slave_dev); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic netdev_tx_t net_failover_drop_xmit(struct sk_buff *skb, 9062306a36Sopenharmony_ci struct net_device *dev) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci dev_core_stats_tx_dropped_inc(dev); 9362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 9462306a36Sopenharmony_ci return NETDEV_TX_OK; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic netdev_tx_t net_failover_start_xmit(struct sk_buff *skb, 9862306a36Sopenharmony_ci struct net_device *dev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 10162306a36Sopenharmony_ci struct net_device *xmit_dev; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Try xmit via primary netdev followed by standby netdev */ 10462306a36Sopenharmony_ci xmit_dev = rcu_dereference_bh(nfo_info->primary_dev); 10562306a36Sopenharmony_ci if (!xmit_dev || !net_failover_xmit_ready(xmit_dev)) { 10662306a36Sopenharmony_ci xmit_dev = rcu_dereference_bh(nfo_info->standby_dev); 10762306a36Sopenharmony_ci if (!xmit_dev || !net_failover_xmit_ready(xmit_dev)) 10862306a36Sopenharmony_ci return net_failover_drop_xmit(skb, dev); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci skb->dev = xmit_dev; 11262306a36Sopenharmony_ci skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return dev_queue_xmit(skb); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic u16 net_failover_select_queue(struct net_device *dev, 11862306a36Sopenharmony_ci struct sk_buff *skb, 11962306a36Sopenharmony_ci struct net_device *sb_dev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 12262306a36Sopenharmony_ci struct net_device *primary_dev; 12362306a36Sopenharmony_ci u16 txq; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci primary_dev = rcu_dereference(nfo_info->primary_dev); 12662306a36Sopenharmony_ci if (primary_dev) { 12762306a36Sopenharmony_ci const struct net_device_ops *ops = primary_dev->netdev_ops; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (ops->ndo_select_queue) 13062306a36Sopenharmony_ci txq = ops->ndo_select_queue(primary_dev, skb, sb_dev); 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci txq = netdev_pick_tx(primary_dev, skb, NULL); 13362306a36Sopenharmony_ci } else { 13462306a36Sopenharmony_ci txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Save the original txq to restore before passing to the driver */ 13862306a36Sopenharmony_ci qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (unlikely(txq >= dev->real_num_tx_queues)) { 14162306a36Sopenharmony_ci do { 14262306a36Sopenharmony_ci txq -= dev->real_num_tx_queues; 14362306a36Sopenharmony_ci } while (txq >= dev->real_num_tx_queues); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return txq; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* fold stats, assuming all rtnl_link_stats64 fields are u64, but 15062306a36Sopenharmony_ci * that some drivers can provide 32bit values only. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic void net_failover_fold_stats(struct rtnl_link_stats64 *_res, 15362306a36Sopenharmony_ci const struct rtnl_link_stats64 *_new, 15462306a36Sopenharmony_ci const struct rtnl_link_stats64 *_old) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci const u64 *new = (const u64 *)_new; 15762306a36Sopenharmony_ci const u64 *old = (const u64 *)_old; 15862306a36Sopenharmony_ci u64 *res = (u64 *)_res; 15962306a36Sopenharmony_ci int i; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = 0; i < sizeof(*_res) / sizeof(u64); i++) { 16262306a36Sopenharmony_ci u64 nv = new[i]; 16362306a36Sopenharmony_ci u64 ov = old[i]; 16462306a36Sopenharmony_ci s64 delta = nv - ov; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* detects if this particular field is 32bit only */ 16762306a36Sopenharmony_ci if (((nv | ov) >> 32) == 0) 16862306a36Sopenharmony_ci delta = (s64)(s32)((u32)nv - (u32)ov); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* filter anomalies, some drivers reset their stats 17162306a36Sopenharmony_ci * at down/up events. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci if (delta > 0) 17462306a36Sopenharmony_ci res[i] += delta; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void net_failover_get_stats(struct net_device *dev, 17962306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 18262306a36Sopenharmony_ci const struct rtnl_link_stats64 *new; 18362306a36Sopenharmony_ci struct rtnl_link_stats64 temp; 18462306a36Sopenharmony_ci struct net_device *slave_dev; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci spin_lock(&nfo_info->stats_lock); 18762306a36Sopenharmony_ci memcpy(stats, &nfo_info->failover_stats, sizeof(*stats)); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci rcu_read_lock(); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci slave_dev = rcu_dereference(nfo_info->primary_dev); 19262306a36Sopenharmony_ci if (slave_dev) { 19362306a36Sopenharmony_ci new = dev_get_stats(slave_dev, &temp); 19462306a36Sopenharmony_ci net_failover_fold_stats(stats, new, &nfo_info->primary_stats); 19562306a36Sopenharmony_ci memcpy(&nfo_info->primary_stats, new, sizeof(*new)); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci slave_dev = rcu_dereference(nfo_info->standby_dev); 19962306a36Sopenharmony_ci if (slave_dev) { 20062306a36Sopenharmony_ci new = dev_get_stats(slave_dev, &temp); 20162306a36Sopenharmony_ci net_failover_fold_stats(stats, new, &nfo_info->standby_stats); 20262306a36Sopenharmony_ci memcpy(&nfo_info->standby_stats, new, sizeof(*new)); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci rcu_read_unlock(); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci memcpy(&nfo_info->failover_stats, stats, sizeof(*stats)); 20862306a36Sopenharmony_ci spin_unlock(&nfo_info->stats_lock); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int net_failover_change_mtu(struct net_device *dev, int new_mtu) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 21462306a36Sopenharmony_ci struct net_device *primary_dev, *standby_dev; 21562306a36Sopenharmony_ci int ret = 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 21862306a36Sopenharmony_ci if (primary_dev) { 21962306a36Sopenharmony_ci ret = dev_set_mtu(primary_dev, new_mtu); 22062306a36Sopenharmony_ci if (ret) 22162306a36Sopenharmony_ci return ret; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 22562306a36Sopenharmony_ci if (standby_dev) { 22662306a36Sopenharmony_ci ret = dev_set_mtu(standby_dev, new_mtu); 22762306a36Sopenharmony_ci if (ret) { 22862306a36Sopenharmony_ci if (primary_dev) 22962306a36Sopenharmony_ci dev_set_mtu(primary_dev, dev->mtu); 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci dev->mtu = new_mtu; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void net_failover_set_rx_mode(struct net_device *dev) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 24262306a36Sopenharmony_ci struct net_device *slave_dev; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci rcu_read_lock(); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci slave_dev = rcu_dereference(nfo_info->primary_dev); 24762306a36Sopenharmony_ci if (slave_dev) { 24862306a36Sopenharmony_ci dev_uc_sync_multiple(slave_dev, dev); 24962306a36Sopenharmony_ci dev_mc_sync_multiple(slave_dev, dev); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci slave_dev = rcu_dereference(nfo_info->standby_dev); 25362306a36Sopenharmony_ci if (slave_dev) { 25462306a36Sopenharmony_ci dev_uc_sync_multiple(slave_dev, dev); 25562306a36Sopenharmony_ci dev_mc_sync_multiple(slave_dev, dev); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci rcu_read_unlock(); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int net_failover_vlan_rx_add_vid(struct net_device *dev, __be16 proto, 26262306a36Sopenharmony_ci u16 vid) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 26562306a36Sopenharmony_ci struct net_device *primary_dev, *standby_dev; 26662306a36Sopenharmony_ci int ret = 0; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci primary_dev = rcu_dereference(nfo_info->primary_dev); 26962306a36Sopenharmony_ci if (primary_dev) { 27062306a36Sopenharmony_ci ret = vlan_vid_add(primary_dev, proto, vid); 27162306a36Sopenharmony_ci if (ret) 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci standby_dev = rcu_dereference(nfo_info->standby_dev); 27662306a36Sopenharmony_ci if (standby_dev) { 27762306a36Sopenharmony_ci ret = vlan_vid_add(standby_dev, proto, vid); 27862306a36Sopenharmony_ci if (ret) 27962306a36Sopenharmony_ci if (primary_dev) 28062306a36Sopenharmony_ci vlan_vid_del(primary_dev, proto, vid); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int net_failover_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, 28762306a36Sopenharmony_ci u16 vid) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 29062306a36Sopenharmony_ci struct net_device *slave_dev; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci slave_dev = rcu_dereference(nfo_info->primary_dev); 29362306a36Sopenharmony_ci if (slave_dev) 29462306a36Sopenharmony_ci vlan_vid_del(slave_dev, proto, vid); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci slave_dev = rcu_dereference(nfo_info->standby_dev); 29762306a36Sopenharmony_ci if (slave_dev) 29862306a36Sopenharmony_ci vlan_vid_del(slave_dev, proto, vid); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic const struct net_device_ops failover_dev_ops = { 30462306a36Sopenharmony_ci .ndo_open = net_failover_open, 30562306a36Sopenharmony_ci .ndo_stop = net_failover_close, 30662306a36Sopenharmony_ci .ndo_start_xmit = net_failover_start_xmit, 30762306a36Sopenharmony_ci .ndo_select_queue = net_failover_select_queue, 30862306a36Sopenharmony_ci .ndo_get_stats64 = net_failover_get_stats, 30962306a36Sopenharmony_ci .ndo_change_mtu = net_failover_change_mtu, 31062306a36Sopenharmony_ci .ndo_set_rx_mode = net_failover_set_rx_mode, 31162306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = net_failover_vlan_rx_add_vid, 31262306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = net_failover_vlan_rx_kill_vid, 31362306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 31462306a36Sopenharmony_ci .ndo_features_check = passthru_features_check, 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci#define FAILOVER_NAME "net_failover" 31862306a36Sopenharmony_ci#define FAILOVER_VERSION "0.1" 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void nfo_ethtool_get_drvinfo(struct net_device *dev, 32162306a36Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci strscpy(drvinfo->driver, FAILOVER_NAME, sizeof(drvinfo->driver)); 32462306a36Sopenharmony_ci strscpy(drvinfo->version, FAILOVER_VERSION, sizeof(drvinfo->version)); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int nfo_ethtool_get_link_ksettings(struct net_device *dev, 32862306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 33162306a36Sopenharmony_ci struct net_device *slave_dev; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci slave_dev = rtnl_dereference(nfo_info->primary_dev); 33462306a36Sopenharmony_ci if (!slave_dev || !net_failover_xmit_ready(slave_dev)) { 33562306a36Sopenharmony_ci slave_dev = rtnl_dereference(nfo_info->standby_dev); 33662306a36Sopenharmony_ci if (!slave_dev || !net_failover_xmit_ready(slave_dev)) { 33762306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 33862306a36Sopenharmony_ci cmd->base.port = PORT_OTHER; 33962306a36Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return __ethtool_get_link_ksettings(slave_dev, cmd); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic const struct ethtool_ops failover_ethtool_ops = { 34962306a36Sopenharmony_ci .get_drvinfo = nfo_ethtool_get_drvinfo, 35062306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 35162306a36Sopenharmony_ci .get_link_ksettings = nfo_ethtool_get_link_ksettings, 35262306a36Sopenharmony_ci}; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/* Called when slave dev is injecting data into network stack. 35562306a36Sopenharmony_ci * Change the associated network device from lower dev to failover dev. 35662306a36Sopenharmony_ci * note: already called with rcu_read_lock 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistatic rx_handler_result_t net_failover_handle_frame(struct sk_buff **pskb) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct sk_buff *skb = *pskb; 36162306a36Sopenharmony_ci struct net_device *dev = rcu_dereference(skb->dev->rx_handler_data); 36262306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 36362306a36Sopenharmony_ci struct net_device *primary_dev, *standby_dev; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci primary_dev = rcu_dereference(nfo_info->primary_dev); 36662306a36Sopenharmony_ci standby_dev = rcu_dereference(nfo_info->standby_dev); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (primary_dev && skb->dev == standby_dev) 36962306a36Sopenharmony_ci return RX_HANDLER_EXACT; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci skb->dev = dev; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return RX_HANDLER_ANOTHER; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void net_failover_compute_features(struct net_device *dev) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci netdev_features_t vlan_features = FAILOVER_VLAN_FEATURES & 37962306a36Sopenharmony_ci NETIF_F_ALL_FOR_ALL; 38062306a36Sopenharmony_ci netdev_features_t enc_features = FAILOVER_ENC_FEATURES; 38162306a36Sopenharmony_ci unsigned short max_hard_header_len = ETH_HLEN; 38262306a36Sopenharmony_ci unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | 38362306a36Sopenharmony_ci IFF_XMIT_DST_RELEASE_PERM; 38462306a36Sopenharmony_ci struct net_failover_info *nfo_info = netdev_priv(dev); 38562306a36Sopenharmony_ci struct net_device *primary_dev, *standby_dev; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci primary_dev = rcu_dereference(nfo_info->primary_dev); 38862306a36Sopenharmony_ci if (primary_dev) { 38962306a36Sopenharmony_ci vlan_features = 39062306a36Sopenharmony_ci netdev_increment_features(vlan_features, 39162306a36Sopenharmony_ci primary_dev->vlan_features, 39262306a36Sopenharmony_ci FAILOVER_VLAN_FEATURES); 39362306a36Sopenharmony_ci enc_features = 39462306a36Sopenharmony_ci netdev_increment_features(enc_features, 39562306a36Sopenharmony_ci primary_dev->hw_enc_features, 39662306a36Sopenharmony_ci FAILOVER_ENC_FEATURES); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci dst_release_flag &= primary_dev->priv_flags; 39962306a36Sopenharmony_ci if (primary_dev->hard_header_len > max_hard_header_len) 40062306a36Sopenharmony_ci max_hard_header_len = primary_dev->hard_header_len; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci standby_dev = rcu_dereference(nfo_info->standby_dev); 40462306a36Sopenharmony_ci if (standby_dev) { 40562306a36Sopenharmony_ci vlan_features = 40662306a36Sopenharmony_ci netdev_increment_features(vlan_features, 40762306a36Sopenharmony_ci standby_dev->vlan_features, 40862306a36Sopenharmony_ci FAILOVER_VLAN_FEATURES); 40962306a36Sopenharmony_ci enc_features = 41062306a36Sopenharmony_ci netdev_increment_features(enc_features, 41162306a36Sopenharmony_ci standby_dev->hw_enc_features, 41262306a36Sopenharmony_ci FAILOVER_ENC_FEATURES); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci dst_release_flag &= standby_dev->priv_flags; 41562306a36Sopenharmony_ci if (standby_dev->hard_header_len > max_hard_header_len) 41662306a36Sopenharmony_ci max_hard_header_len = standby_dev->hard_header_len; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci dev->vlan_features = vlan_features; 42062306a36Sopenharmony_ci dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL; 42162306a36Sopenharmony_ci dev->hard_header_len = max_hard_header_len; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; 42462306a36Sopenharmony_ci if (dst_release_flag == (IFF_XMIT_DST_RELEASE | 42562306a36Sopenharmony_ci IFF_XMIT_DST_RELEASE_PERM)) 42662306a36Sopenharmony_ci dev->priv_flags |= IFF_XMIT_DST_RELEASE; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci netdev_change_features(dev); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void net_failover_lower_state_changed(struct net_device *slave_dev, 43262306a36Sopenharmony_ci struct net_device *primary_dev, 43362306a36Sopenharmony_ci struct net_device *standby_dev) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct netdev_lag_lower_state_info info; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (netif_carrier_ok(slave_dev)) 43862306a36Sopenharmony_ci info.link_up = true; 43962306a36Sopenharmony_ci else 44062306a36Sopenharmony_ci info.link_up = false; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (slave_dev == primary_dev) { 44362306a36Sopenharmony_ci if (netif_running(primary_dev)) 44462306a36Sopenharmony_ci info.tx_enabled = true; 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci info.tx_enabled = false; 44762306a36Sopenharmony_ci } else { 44862306a36Sopenharmony_ci if ((primary_dev && netif_running(primary_dev)) || 44962306a36Sopenharmony_ci (!netif_running(standby_dev))) 45062306a36Sopenharmony_ci info.tx_enabled = false; 45162306a36Sopenharmony_ci else 45262306a36Sopenharmony_ci info.tx_enabled = true; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci netdev_lower_state_changed(slave_dev, &info); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int net_failover_slave_pre_register(struct net_device *slave_dev, 45962306a36Sopenharmony_ci struct net_device *failover_dev) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct net_device *standby_dev, *primary_dev; 46262306a36Sopenharmony_ci struct net_failover_info *nfo_info; 46362306a36Sopenharmony_ci bool slave_is_standby; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 46662306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 46762306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 46862306a36Sopenharmony_ci slave_is_standby = slave_dev->dev.parent == failover_dev->dev.parent; 46962306a36Sopenharmony_ci if (slave_is_standby ? standby_dev : primary_dev) { 47062306a36Sopenharmony_ci netdev_err(failover_dev, "%s attempting to register as slave dev when %s already present\n", 47162306a36Sopenharmony_ci slave_dev->name, 47262306a36Sopenharmony_ci slave_is_standby ? "standby" : "primary"); 47362306a36Sopenharmony_ci return -EINVAL; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* We want to allow only a direct attached VF device as a primary 47762306a36Sopenharmony_ci * netdev. As there is no easy way to check for a VF device, restrict 47862306a36Sopenharmony_ci * this to a pci device. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci if (!slave_is_standby && (!slave_dev->dev.parent || 48162306a36Sopenharmony_ci !dev_is_pci(slave_dev->dev.parent))) 48262306a36Sopenharmony_ci return -EINVAL; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (failover_dev->features & NETIF_F_VLAN_CHALLENGED && 48562306a36Sopenharmony_ci vlan_uses_dev(failover_dev)) { 48662306a36Sopenharmony_ci netdev_err(failover_dev, "Device %s is VLAN challenged and failover device has VLAN set up\n", 48762306a36Sopenharmony_ci failover_dev->name); 48862306a36Sopenharmony_ci return -EINVAL; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int net_failover_slave_register(struct net_device *slave_dev, 49562306a36Sopenharmony_ci struct net_device *failover_dev) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct net_device *standby_dev, *primary_dev; 49862306a36Sopenharmony_ci struct net_failover_info *nfo_info; 49962306a36Sopenharmony_ci bool slave_is_standby; 50062306a36Sopenharmony_ci u32 orig_mtu; 50162306a36Sopenharmony_ci int err; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* Align MTU of slave with failover dev */ 50462306a36Sopenharmony_ci orig_mtu = slave_dev->mtu; 50562306a36Sopenharmony_ci err = dev_set_mtu(slave_dev, failover_dev->mtu); 50662306a36Sopenharmony_ci if (err) { 50762306a36Sopenharmony_ci netdev_err(failover_dev, "unable to change mtu of %s to %u register failed\n", 50862306a36Sopenharmony_ci slave_dev->name, failover_dev->mtu); 50962306a36Sopenharmony_ci goto done; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci dev_hold(slave_dev); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (netif_running(failover_dev)) { 51562306a36Sopenharmony_ci err = dev_open(slave_dev, NULL); 51662306a36Sopenharmony_ci if (err && (err != -EBUSY)) { 51762306a36Sopenharmony_ci netdev_err(failover_dev, "Opening slave %s failed err:%d\n", 51862306a36Sopenharmony_ci slave_dev->name, err); 51962306a36Sopenharmony_ci goto err_dev_open; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci netif_addr_lock_bh(failover_dev); 52462306a36Sopenharmony_ci dev_uc_sync_multiple(slave_dev, failover_dev); 52562306a36Sopenharmony_ci dev_mc_sync_multiple(slave_dev, failover_dev); 52662306a36Sopenharmony_ci netif_addr_unlock_bh(failover_dev); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci err = vlan_vids_add_by_dev(slave_dev, failover_dev); 52962306a36Sopenharmony_ci if (err) { 53062306a36Sopenharmony_ci netdev_err(failover_dev, "Failed to add vlan ids to device %s err:%d\n", 53162306a36Sopenharmony_ci slave_dev->name, err); 53262306a36Sopenharmony_ci goto err_vlan_add; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 53662306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 53762306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 53862306a36Sopenharmony_ci slave_is_standby = slave_dev->dev.parent == failover_dev->dev.parent; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (slave_is_standby) { 54162306a36Sopenharmony_ci rcu_assign_pointer(nfo_info->standby_dev, slave_dev); 54262306a36Sopenharmony_ci standby_dev = slave_dev; 54362306a36Sopenharmony_ci dev_get_stats(standby_dev, &nfo_info->standby_stats); 54462306a36Sopenharmony_ci } else { 54562306a36Sopenharmony_ci rcu_assign_pointer(nfo_info->primary_dev, slave_dev); 54662306a36Sopenharmony_ci primary_dev = slave_dev; 54762306a36Sopenharmony_ci dev_get_stats(primary_dev, &nfo_info->primary_stats); 54862306a36Sopenharmony_ci failover_dev->min_mtu = slave_dev->min_mtu; 54962306a36Sopenharmony_ci failover_dev->max_mtu = slave_dev->max_mtu; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci net_failover_lower_state_changed(slave_dev, primary_dev, standby_dev); 55362306a36Sopenharmony_ci net_failover_compute_features(failover_dev); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci call_netdevice_notifiers(NETDEV_JOIN, slave_dev); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci netdev_info(failover_dev, "failover %s slave:%s registered\n", 55862306a36Sopenharmony_ci slave_is_standby ? "standby" : "primary", slave_dev->name); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cierr_vlan_add: 56362306a36Sopenharmony_ci dev_uc_unsync(slave_dev, failover_dev); 56462306a36Sopenharmony_ci dev_mc_unsync(slave_dev, failover_dev); 56562306a36Sopenharmony_ci dev_close(slave_dev); 56662306a36Sopenharmony_cierr_dev_open: 56762306a36Sopenharmony_ci dev_put(slave_dev); 56862306a36Sopenharmony_ci dev_set_mtu(slave_dev, orig_mtu); 56962306a36Sopenharmony_cidone: 57062306a36Sopenharmony_ci return err; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int net_failover_slave_pre_unregister(struct net_device *slave_dev, 57462306a36Sopenharmony_ci struct net_device *failover_dev) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct net_device *standby_dev, *primary_dev; 57762306a36Sopenharmony_ci struct net_failover_info *nfo_info; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 58062306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 58162306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (slave_dev != primary_dev && slave_dev != standby_dev) 58462306a36Sopenharmony_ci return -ENODEV; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic int net_failover_slave_unregister(struct net_device *slave_dev, 59062306a36Sopenharmony_ci struct net_device *failover_dev) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct net_device *standby_dev, *primary_dev; 59362306a36Sopenharmony_ci struct net_failover_info *nfo_info; 59462306a36Sopenharmony_ci bool slave_is_standby; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 59762306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 59862306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (WARN_ON_ONCE(slave_dev != primary_dev && slave_dev != standby_dev)) 60162306a36Sopenharmony_ci return -ENODEV; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci vlan_vids_del_by_dev(slave_dev, failover_dev); 60462306a36Sopenharmony_ci dev_uc_unsync(slave_dev, failover_dev); 60562306a36Sopenharmony_ci dev_mc_unsync(slave_dev, failover_dev); 60662306a36Sopenharmony_ci dev_close(slave_dev); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 60962306a36Sopenharmony_ci dev_get_stats(failover_dev, &nfo_info->failover_stats); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci slave_is_standby = slave_dev->dev.parent == failover_dev->dev.parent; 61262306a36Sopenharmony_ci if (slave_is_standby) { 61362306a36Sopenharmony_ci RCU_INIT_POINTER(nfo_info->standby_dev, NULL); 61462306a36Sopenharmony_ci } else { 61562306a36Sopenharmony_ci RCU_INIT_POINTER(nfo_info->primary_dev, NULL); 61662306a36Sopenharmony_ci if (standby_dev) { 61762306a36Sopenharmony_ci failover_dev->min_mtu = standby_dev->min_mtu; 61862306a36Sopenharmony_ci failover_dev->max_mtu = standby_dev->max_mtu; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci dev_put(slave_dev); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci net_failover_compute_features(failover_dev); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci netdev_info(failover_dev, "failover %s slave:%s unregistered\n", 62762306a36Sopenharmony_ci slave_is_standby ? "standby" : "primary", slave_dev->name); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int net_failover_slave_link_change(struct net_device *slave_dev, 63362306a36Sopenharmony_ci struct net_device *failover_dev) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct net_device *primary_dev, *standby_dev; 63662306a36Sopenharmony_ci struct net_failover_info *nfo_info; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 64162306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (slave_dev != primary_dev && slave_dev != standby_dev) 64462306a36Sopenharmony_ci return -ENODEV; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if ((primary_dev && net_failover_xmit_ready(primary_dev)) || 64762306a36Sopenharmony_ci (standby_dev && net_failover_xmit_ready(standby_dev))) { 64862306a36Sopenharmony_ci netif_carrier_on(failover_dev); 64962306a36Sopenharmony_ci netif_tx_wake_all_queues(failover_dev); 65062306a36Sopenharmony_ci } else { 65162306a36Sopenharmony_ci dev_get_stats(failover_dev, &nfo_info->failover_stats); 65262306a36Sopenharmony_ci netif_carrier_off(failover_dev); 65362306a36Sopenharmony_ci netif_tx_stop_all_queues(failover_dev); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci net_failover_lower_state_changed(slave_dev, primary_dev, standby_dev); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int net_failover_slave_name_change(struct net_device *slave_dev, 66262306a36Sopenharmony_ci struct net_device *failover_dev) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct net_device *primary_dev, *standby_dev; 66562306a36Sopenharmony_ci struct net_failover_info *nfo_info; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci primary_dev = rtnl_dereference(nfo_info->primary_dev); 67062306a36Sopenharmony_ci standby_dev = rtnl_dereference(nfo_info->standby_dev); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (slave_dev != primary_dev && slave_dev != standby_dev) 67362306a36Sopenharmony_ci return -ENODEV; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* We need to bring up the slave after the rename by udev in case 67662306a36Sopenharmony_ci * open failed with EBUSY when it was registered. 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci dev_open(slave_dev, NULL); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic struct failover_ops net_failover_ops = { 68462306a36Sopenharmony_ci .slave_pre_register = net_failover_slave_pre_register, 68562306a36Sopenharmony_ci .slave_register = net_failover_slave_register, 68662306a36Sopenharmony_ci .slave_pre_unregister = net_failover_slave_pre_unregister, 68762306a36Sopenharmony_ci .slave_unregister = net_failover_slave_unregister, 68862306a36Sopenharmony_ci .slave_link_change = net_failover_slave_link_change, 68962306a36Sopenharmony_ci .slave_name_change = net_failover_slave_name_change, 69062306a36Sopenharmony_ci .slave_handle_frame = net_failover_handle_frame, 69162306a36Sopenharmony_ci}; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/** 69462306a36Sopenharmony_ci * net_failover_create - Create and register a failover instance 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci * @standby_dev: standby netdev 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * Creates a failover netdev and registers a failover instance for a standby 69962306a36Sopenharmony_ci * netdev. Used by paravirtual drivers that use 3-netdev model. 70062306a36Sopenharmony_ci * The failover netdev acts as a master device and controls 2 slave devices - 70162306a36Sopenharmony_ci * the original standby netdev and a VF netdev with the same MAC gets 70262306a36Sopenharmony_ci * registered as primary netdev. 70362306a36Sopenharmony_ci * 70462306a36Sopenharmony_ci * Return: pointer to failover instance 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_cistruct failover *net_failover_create(struct net_device *standby_dev) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct device *dev = standby_dev->dev.parent; 70962306a36Sopenharmony_ci struct net_device *failover_dev; 71062306a36Sopenharmony_ci struct failover *failover; 71162306a36Sopenharmony_ci int err; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Alloc at least 2 queues, for now we are going with 16 assuming 71462306a36Sopenharmony_ci * that VF devices being enslaved won't have too many queues. 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci failover_dev = alloc_etherdev_mq(sizeof(struct net_failover_info), 16); 71762306a36Sopenharmony_ci if (!failover_dev) { 71862306a36Sopenharmony_ci dev_err(dev, "Unable to allocate failover_netdev!\n"); 71962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci dev_net_set(failover_dev, dev_net(standby_dev)); 72362306a36Sopenharmony_ci SET_NETDEV_DEV(failover_dev, dev); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci failover_dev->netdev_ops = &failover_dev_ops; 72662306a36Sopenharmony_ci failover_dev->ethtool_ops = &failover_ethtool_ops; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Initialize the device options */ 72962306a36Sopenharmony_ci failover_dev->priv_flags |= IFF_UNICAST_FLT | IFF_NO_QUEUE; 73062306a36Sopenharmony_ci failover_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | 73162306a36Sopenharmony_ci IFF_TX_SKB_SHARING); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* don't acquire failover netdev's netif_tx_lock when transmitting */ 73462306a36Sopenharmony_ci failover_dev->features |= NETIF_F_LLTX; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* Don't allow failover devices to change network namespaces. */ 73762306a36Sopenharmony_ci failover_dev->features |= NETIF_F_NETNS_LOCAL; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci failover_dev->hw_features = FAILOVER_VLAN_FEATURES | 74062306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX | 74162306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | 74262306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_FILTER; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci failover_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL; 74562306a36Sopenharmony_ci failover_dev->features |= failover_dev->hw_features; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci dev_addr_set(failover_dev, standby_dev->dev_addr); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci failover_dev->min_mtu = standby_dev->min_mtu; 75062306a36Sopenharmony_ci failover_dev->max_mtu = standby_dev->max_mtu; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci err = register_netdev(failover_dev); 75362306a36Sopenharmony_ci if (err) { 75462306a36Sopenharmony_ci dev_err(dev, "Unable to register failover_dev!\n"); 75562306a36Sopenharmony_ci goto err_register_netdev; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci netif_carrier_off(failover_dev); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci failover = failover_register(failover_dev, &net_failover_ops); 76162306a36Sopenharmony_ci if (IS_ERR(failover)) { 76262306a36Sopenharmony_ci err = PTR_ERR(failover); 76362306a36Sopenharmony_ci goto err_failover_register; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return failover; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cierr_failover_register: 76962306a36Sopenharmony_ci unregister_netdev(failover_dev); 77062306a36Sopenharmony_cierr_register_netdev: 77162306a36Sopenharmony_ci free_netdev(failover_dev); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return ERR_PTR(err); 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(net_failover_create); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/** 77862306a36Sopenharmony_ci * net_failover_destroy - Destroy a failover instance 77962306a36Sopenharmony_ci * 78062306a36Sopenharmony_ci * @failover: pointer to failover instance 78162306a36Sopenharmony_ci * 78262306a36Sopenharmony_ci * Unregisters any slave netdevs associated with the failover instance by 78362306a36Sopenharmony_ci * calling failover_slave_unregister(). 78462306a36Sopenharmony_ci * unregisters the failover instance itself and finally frees the failover 78562306a36Sopenharmony_ci * netdev. Used by paravirtual drivers that use 3-netdev model. 78662306a36Sopenharmony_ci * 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_civoid net_failover_destroy(struct failover *failover) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct net_failover_info *nfo_info; 79162306a36Sopenharmony_ci struct net_device *failover_dev; 79262306a36Sopenharmony_ci struct net_device *slave_dev; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (!failover) 79562306a36Sopenharmony_ci return; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci failover_dev = rcu_dereference(failover->failover_dev); 79862306a36Sopenharmony_ci nfo_info = netdev_priv(failover_dev); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci netif_device_detach(failover_dev); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci rtnl_lock(); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci slave_dev = rtnl_dereference(nfo_info->primary_dev); 80562306a36Sopenharmony_ci if (slave_dev) 80662306a36Sopenharmony_ci failover_slave_unregister(slave_dev); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci slave_dev = rtnl_dereference(nfo_info->standby_dev); 80962306a36Sopenharmony_ci if (slave_dev) 81062306a36Sopenharmony_ci failover_slave_unregister(slave_dev); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci failover_unregister(failover); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci unregister_netdevice(failover_dev); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci rtnl_unlock(); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci free_netdev(failover_dev); 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(net_failover_destroy); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic __init int 82362306a36Sopenharmony_cinet_failover_init(void) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_cimodule_init(net_failover_init); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic __exit 83062306a36Sopenharmony_civoid net_failover_exit(void) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_cimodule_exit(net_failover_exit); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ciMODULE_DESCRIPTION("Failover driver for Paravirtual drivers"); 83662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 837