162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2018, Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* A common module to handle registrations and notifications for paravirtual 562306a36Sopenharmony_ci * drivers to enable accelerated datapath and support VF live migration. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The notifier and event handling code is based on netvsc driver. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/etherdevice.h> 1262306a36Sopenharmony_ci#include <uapi/linux/if_arp.h> 1362306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1462306a36Sopenharmony_ci#include <linux/if_vlan.h> 1562306a36Sopenharmony_ci#include <net/failover.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic LIST_HEAD(failover_list); 1862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(failover_lock); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic struct net_device *failover_get_bymac(u8 *mac, struct failover_ops **ops) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct net_device *failover_dev; 2362306a36Sopenharmony_ci struct failover *failover; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci spin_lock(&failover_lock); 2662306a36Sopenharmony_ci list_for_each_entry(failover, &failover_list, list) { 2762306a36Sopenharmony_ci failover_dev = rtnl_dereference(failover->failover_dev); 2862306a36Sopenharmony_ci if (ether_addr_equal(failover_dev->perm_addr, mac)) { 2962306a36Sopenharmony_ci *ops = rtnl_dereference(failover->ops); 3062306a36Sopenharmony_ci spin_unlock(&failover_lock); 3162306a36Sopenharmony_ci return failover_dev; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci spin_unlock(&failover_lock); 3562306a36Sopenharmony_ci return NULL; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/** 3962306a36Sopenharmony_ci * failover_slave_register - Register a slave netdev 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * @slave_dev: slave netdev that is being registered 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Registers a slave device to a failover instance. Only ethernet devices 4462306a36Sopenharmony_ci * are supported. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic int failover_slave_register(struct net_device *slave_dev) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct netdev_lag_upper_info lag_upper_info; 4962306a36Sopenharmony_ci struct net_device *failover_dev; 5062306a36Sopenharmony_ci struct failover_ops *fops; 5162306a36Sopenharmony_ci int err; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (slave_dev->type != ARPHRD_ETHER) 5462306a36Sopenharmony_ci goto done; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ASSERT_RTNL(); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 5962306a36Sopenharmony_ci if (!failover_dev) 6062306a36Sopenharmony_ci goto done; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (fops && fops->slave_pre_register && 6362306a36Sopenharmony_ci fops->slave_pre_register(slave_dev, failover_dev)) 6462306a36Sopenharmony_ci goto done; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci err = netdev_rx_handler_register(slave_dev, fops->slave_handle_frame, 6762306a36Sopenharmony_ci failover_dev); 6862306a36Sopenharmony_ci if (err) { 6962306a36Sopenharmony_ci netdev_err(slave_dev, "can not register failover rx handler (err = %d)\n", 7062306a36Sopenharmony_ci err); 7162306a36Sopenharmony_ci goto done; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci lag_upper_info.tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP; 7562306a36Sopenharmony_ci err = netdev_master_upper_dev_link(slave_dev, failover_dev, NULL, 7662306a36Sopenharmony_ci &lag_upper_info, NULL); 7762306a36Sopenharmony_ci if (err) { 7862306a36Sopenharmony_ci netdev_err(slave_dev, "can not set failover device %s (err = %d)\n", 7962306a36Sopenharmony_ci failover_dev->name, err); 8062306a36Sopenharmony_ci goto err_upper_link; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (fops && fops->slave_register && 8662306a36Sopenharmony_ci !fops->slave_register(slave_dev, failover_dev)) 8762306a36Sopenharmony_ci return NOTIFY_OK; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci netdev_upper_dev_unlink(slave_dev, failover_dev); 9062306a36Sopenharmony_ci slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); 9162306a36Sopenharmony_cierr_upper_link: 9262306a36Sopenharmony_ci netdev_rx_handler_unregister(slave_dev); 9362306a36Sopenharmony_cidone: 9462306a36Sopenharmony_ci return NOTIFY_DONE; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * failover_slave_unregister - Unregister a slave netdev 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * @slave_dev: slave netdev that is being unregistered 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Unregisters a slave device from a failover instance. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ciint failover_slave_unregister(struct net_device *slave_dev) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct net_device *failover_dev; 10762306a36Sopenharmony_ci struct failover_ops *fops; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!netif_is_failover_slave(slave_dev)) 11062306a36Sopenharmony_ci goto done; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ASSERT_RTNL(); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 11562306a36Sopenharmony_ci if (!failover_dev) 11662306a36Sopenharmony_ci goto done; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (fops && fops->slave_pre_unregister && 11962306a36Sopenharmony_ci fops->slave_pre_unregister(slave_dev, failover_dev)) 12062306a36Sopenharmony_ci goto done; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci netdev_rx_handler_unregister(slave_dev); 12362306a36Sopenharmony_ci netdev_upper_dev_unlink(slave_dev, failover_dev); 12462306a36Sopenharmony_ci slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (fops && fops->slave_unregister && 12762306a36Sopenharmony_ci !fops->slave_unregister(slave_dev, failover_dev)) 12862306a36Sopenharmony_ci return NOTIFY_OK; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cidone: 13162306a36Sopenharmony_ci return NOTIFY_DONE; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(failover_slave_unregister); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int failover_slave_link_change(struct net_device *slave_dev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct net_device *failover_dev; 13862306a36Sopenharmony_ci struct failover_ops *fops; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!netif_is_failover_slave(slave_dev)) 14162306a36Sopenharmony_ci goto done; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ASSERT_RTNL(); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 14662306a36Sopenharmony_ci if (!failover_dev) 14762306a36Sopenharmony_ci goto done; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!netif_running(failover_dev)) 15062306a36Sopenharmony_ci goto done; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (fops && fops->slave_link_change && 15362306a36Sopenharmony_ci !fops->slave_link_change(slave_dev, failover_dev)) 15462306a36Sopenharmony_ci return NOTIFY_OK; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cidone: 15762306a36Sopenharmony_ci return NOTIFY_DONE; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int failover_slave_name_change(struct net_device *slave_dev) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct net_device *failover_dev; 16362306a36Sopenharmony_ci struct failover_ops *fops; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!netif_is_failover_slave(slave_dev)) 16662306a36Sopenharmony_ci goto done; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ASSERT_RTNL(); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 17162306a36Sopenharmony_ci if (!failover_dev) 17262306a36Sopenharmony_ci goto done; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!netif_running(failover_dev)) 17562306a36Sopenharmony_ci goto done; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (fops && fops->slave_name_change && 17862306a36Sopenharmony_ci !fops->slave_name_change(slave_dev, failover_dev)) 17962306a36Sopenharmony_ci return NOTIFY_OK; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cidone: 18262306a36Sopenharmony_ci return NOTIFY_DONE; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int 18662306a36Sopenharmony_cifailover_event(struct notifier_block *this, unsigned long event, void *ptr) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Skip parent events */ 19162306a36Sopenharmony_ci if (netif_is_failover(event_dev)) 19262306a36Sopenharmony_ci return NOTIFY_DONE; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci switch (event) { 19562306a36Sopenharmony_ci case NETDEV_REGISTER: 19662306a36Sopenharmony_ci return failover_slave_register(event_dev); 19762306a36Sopenharmony_ci case NETDEV_UNREGISTER: 19862306a36Sopenharmony_ci return failover_slave_unregister(event_dev); 19962306a36Sopenharmony_ci case NETDEV_UP: 20062306a36Sopenharmony_ci case NETDEV_DOWN: 20162306a36Sopenharmony_ci case NETDEV_CHANGE: 20262306a36Sopenharmony_ci return failover_slave_link_change(event_dev); 20362306a36Sopenharmony_ci case NETDEV_CHANGENAME: 20462306a36Sopenharmony_ci return failover_slave_name_change(event_dev); 20562306a36Sopenharmony_ci default: 20662306a36Sopenharmony_ci return NOTIFY_DONE; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic struct notifier_block failover_notifier = { 21162306a36Sopenharmony_ci .notifier_call = failover_event, 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic void 21562306a36Sopenharmony_cifailover_existing_slave_register(struct net_device *failover_dev) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct net *net = dev_net(failover_dev); 21862306a36Sopenharmony_ci struct net_device *dev; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci rtnl_lock(); 22162306a36Sopenharmony_ci for_each_netdev(net, dev) { 22262306a36Sopenharmony_ci if (netif_is_failover(dev)) 22362306a36Sopenharmony_ci continue; 22462306a36Sopenharmony_ci if (ether_addr_equal(failover_dev->perm_addr, dev->perm_addr)) 22562306a36Sopenharmony_ci failover_slave_register(dev); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci rtnl_unlock(); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/** 23162306a36Sopenharmony_ci * failover_register - Register a failover instance 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * @dev: failover netdev 23462306a36Sopenharmony_ci * @ops: failover ops 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * Allocate and register a failover instance for a failover netdev. ops 23762306a36Sopenharmony_ci * provides handlers for slave device register/unregister/link change/ 23862306a36Sopenharmony_ci * name change events. 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Return: pointer to failover instance 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistruct failover *failover_register(struct net_device *dev, 24362306a36Sopenharmony_ci struct failover_ops *ops) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct failover *failover; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (dev->type != ARPHRD_ETHER) 24862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci failover = kzalloc(sizeof(*failover), GFP_KERNEL); 25162306a36Sopenharmony_ci if (!failover) 25262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci rcu_assign_pointer(failover->ops, ops); 25562306a36Sopenharmony_ci netdev_hold(dev, &failover->dev_tracker, GFP_KERNEL); 25662306a36Sopenharmony_ci dev->priv_flags |= IFF_FAILOVER; 25762306a36Sopenharmony_ci rcu_assign_pointer(failover->failover_dev, dev); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_lock(&failover_lock); 26062306a36Sopenharmony_ci list_add_tail(&failover->list, &failover_list); 26162306a36Sopenharmony_ci spin_unlock(&failover_lock); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci netdev_info(dev, "failover master:%s registered\n", dev->name); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci failover_existing_slave_register(dev); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return failover; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(failover_register); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/** 27262306a36Sopenharmony_ci * failover_unregister - Unregister a failover instance 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * @failover: pointer to failover instance 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * Unregisters and frees a failover instance. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_civoid failover_unregister(struct failover *failover) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct net_device *failover_dev; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci failover_dev = rcu_dereference(failover->failover_dev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci netdev_info(failover_dev, "failover master:%s unregistered\n", 28562306a36Sopenharmony_ci failover_dev->name); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci failover_dev->priv_flags &= ~IFF_FAILOVER; 28862306a36Sopenharmony_ci netdev_put(failover_dev, &failover->dev_tracker); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci spin_lock(&failover_lock); 29162306a36Sopenharmony_ci list_del(&failover->list); 29262306a36Sopenharmony_ci spin_unlock(&failover_lock); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci kfree(failover); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(failover_unregister); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic __init int 29962306a36Sopenharmony_cifailover_init(void) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci register_netdevice_notifier(&failover_notifier); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_cimodule_init(failover_init); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic __exit 30862306a36Sopenharmony_civoid failover_exit(void) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci unregister_netdevice_notifier(&failover_notifier); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_cimodule_exit(failover_exit); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic failover infrastructure/interface"); 31562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 316