18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2018, Intel Corporation. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* A common module to handle registrations and notifications for paravirtual 58c2ecf20Sopenharmony_ci * drivers to enable accelerated datapath and support VF live migration. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The notifier and event handling code is based on netvsc driver. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <uapi/linux/if_arp.h> 138c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 148c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 158c2ecf20Sopenharmony_ci#include <net/failover.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic LIST_HEAD(failover_list); 188c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(failover_lock); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct net_device *failover_get_bymac(u8 *mac, struct failover_ops **ops) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct net_device *failover_dev; 238c2ecf20Sopenharmony_ci struct failover *failover; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci spin_lock(&failover_lock); 268c2ecf20Sopenharmony_ci list_for_each_entry(failover, &failover_list, list) { 278c2ecf20Sopenharmony_ci failover_dev = rtnl_dereference(failover->failover_dev); 288c2ecf20Sopenharmony_ci if (ether_addr_equal(failover_dev->perm_addr, mac)) { 298c2ecf20Sopenharmony_ci *ops = rtnl_dereference(failover->ops); 308c2ecf20Sopenharmony_ci spin_unlock(&failover_lock); 318c2ecf20Sopenharmony_ci return failover_dev; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci spin_unlock(&failover_lock); 358c2ecf20Sopenharmony_ci return NULL; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/** 398c2ecf20Sopenharmony_ci * failover_slave_register - Register a slave netdev 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * @slave_dev: slave netdev that is being registered 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Registers a slave device to a failover instance. Only ethernet devices 448c2ecf20Sopenharmony_ci * are supported. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic int failover_slave_register(struct net_device *slave_dev) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct netdev_lag_upper_info lag_upper_info; 498c2ecf20Sopenharmony_ci struct net_device *failover_dev; 508c2ecf20Sopenharmony_ci struct failover_ops *fops; 518c2ecf20Sopenharmony_ci int err; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (slave_dev->type != ARPHRD_ETHER) 548c2ecf20Sopenharmony_ci goto done; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci ASSERT_RTNL(); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 598c2ecf20Sopenharmony_ci if (!failover_dev) 608c2ecf20Sopenharmony_ci goto done; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (fops && fops->slave_pre_register && 638c2ecf20Sopenharmony_ci fops->slave_pre_register(slave_dev, failover_dev)) 648c2ecf20Sopenharmony_ci goto done; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci err = netdev_rx_handler_register(slave_dev, fops->slave_handle_frame, 678c2ecf20Sopenharmony_ci failover_dev); 688c2ecf20Sopenharmony_ci if (err) { 698c2ecf20Sopenharmony_ci netdev_err(slave_dev, "can not register failover rx handler (err = %d)\n", 708c2ecf20Sopenharmony_ci err); 718c2ecf20Sopenharmony_ci goto done; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci lag_upper_info.tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP; 758c2ecf20Sopenharmony_ci err = netdev_master_upper_dev_link(slave_dev, failover_dev, NULL, 768c2ecf20Sopenharmony_ci &lag_upper_info, NULL); 778c2ecf20Sopenharmony_ci if (err) { 788c2ecf20Sopenharmony_ci netdev_err(slave_dev, "can not set failover device %s (err = %d)\n", 798c2ecf20Sopenharmony_ci failover_dev->name, err); 808c2ecf20Sopenharmony_ci goto err_upper_link; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (fops && fops->slave_register && 868c2ecf20Sopenharmony_ci !fops->slave_register(slave_dev, failover_dev)) 878c2ecf20Sopenharmony_ci return NOTIFY_OK; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci netdev_upper_dev_unlink(slave_dev, failover_dev); 908c2ecf20Sopenharmony_ci slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); 918c2ecf20Sopenharmony_cierr_upper_link: 928c2ecf20Sopenharmony_ci netdev_rx_handler_unregister(slave_dev); 938c2ecf20Sopenharmony_cidone: 948c2ecf20Sopenharmony_ci return NOTIFY_DONE; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/** 988c2ecf20Sopenharmony_ci * failover_slave_unregister - Unregister a slave netdev 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * @slave_dev: slave netdev that is being unregistered 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Unregisters a slave device from a failover instance. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ciint failover_slave_unregister(struct net_device *slave_dev) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct net_device *failover_dev; 1078c2ecf20Sopenharmony_ci struct failover_ops *fops; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!netif_is_failover_slave(slave_dev)) 1108c2ecf20Sopenharmony_ci goto done; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 1158c2ecf20Sopenharmony_ci if (!failover_dev) 1168c2ecf20Sopenharmony_ci goto done; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (fops && fops->slave_pre_unregister && 1198c2ecf20Sopenharmony_ci fops->slave_pre_unregister(slave_dev, failover_dev)) 1208c2ecf20Sopenharmony_ci goto done; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci netdev_rx_handler_unregister(slave_dev); 1238c2ecf20Sopenharmony_ci netdev_upper_dev_unlink(slave_dev, failover_dev); 1248c2ecf20Sopenharmony_ci slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (fops && fops->slave_unregister && 1278c2ecf20Sopenharmony_ci !fops->slave_unregister(slave_dev, failover_dev)) 1288c2ecf20Sopenharmony_ci return NOTIFY_OK; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cidone: 1318c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(failover_slave_unregister); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int failover_slave_link_change(struct net_device *slave_dev) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct net_device *failover_dev; 1388c2ecf20Sopenharmony_ci struct failover_ops *fops; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!netif_is_failover_slave(slave_dev)) 1418c2ecf20Sopenharmony_ci goto done; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 1468c2ecf20Sopenharmony_ci if (!failover_dev) 1478c2ecf20Sopenharmony_ci goto done; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!netif_running(failover_dev)) 1508c2ecf20Sopenharmony_ci goto done; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (fops && fops->slave_link_change && 1538c2ecf20Sopenharmony_ci !fops->slave_link_change(slave_dev, failover_dev)) 1548c2ecf20Sopenharmony_ci return NOTIFY_OK; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cidone: 1578c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int failover_slave_name_change(struct net_device *slave_dev) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct net_device *failover_dev; 1638c2ecf20Sopenharmony_ci struct failover_ops *fops; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (!netif_is_failover_slave(slave_dev)) 1668c2ecf20Sopenharmony_ci goto done; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 1718c2ecf20Sopenharmony_ci if (!failover_dev) 1728c2ecf20Sopenharmony_ci goto done; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!netif_running(failover_dev)) 1758c2ecf20Sopenharmony_ci goto done; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (fops && fops->slave_name_change && 1788c2ecf20Sopenharmony_ci !fops->slave_name_change(slave_dev, failover_dev)) 1798c2ecf20Sopenharmony_ci return NOTIFY_OK; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cidone: 1828c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int 1868c2ecf20Sopenharmony_cifailover_event(struct notifier_block *this, unsigned long event, void *ptr) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Skip parent events */ 1918c2ecf20Sopenharmony_ci if (netif_is_failover(event_dev)) 1928c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci switch (event) { 1958c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 1968c2ecf20Sopenharmony_ci return failover_slave_register(event_dev); 1978c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 1988c2ecf20Sopenharmony_ci return failover_slave_unregister(event_dev); 1998c2ecf20Sopenharmony_ci case NETDEV_UP: 2008c2ecf20Sopenharmony_ci case NETDEV_DOWN: 2018c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 2028c2ecf20Sopenharmony_ci return failover_slave_link_change(event_dev); 2038c2ecf20Sopenharmony_ci case NETDEV_CHANGENAME: 2048c2ecf20Sopenharmony_ci return failover_slave_name_change(event_dev); 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic struct notifier_block failover_notifier = { 2118c2ecf20Sopenharmony_ci .notifier_call = failover_event, 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void 2158c2ecf20Sopenharmony_cifailover_existing_slave_register(struct net_device *failover_dev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct net *net = dev_net(failover_dev); 2188c2ecf20Sopenharmony_ci struct net_device *dev; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci rtnl_lock(); 2218c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 2228c2ecf20Sopenharmony_ci if (netif_is_failover(dev)) 2238c2ecf20Sopenharmony_ci continue; 2248c2ecf20Sopenharmony_ci if (ether_addr_equal(failover_dev->perm_addr, dev->perm_addr)) 2258c2ecf20Sopenharmony_ci failover_slave_register(dev); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci rtnl_unlock(); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/** 2318c2ecf20Sopenharmony_ci * failover_register - Register a failover instance 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * @dev: failover netdev 2348c2ecf20Sopenharmony_ci * @ops: failover ops 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Allocate and register a failover instance for a failover netdev. ops 2378c2ecf20Sopenharmony_ci * provides handlers for slave device register/unregister/link change/ 2388c2ecf20Sopenharmony_ci * name change events. 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * Return: pointer to failover instance 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistruct failover *failover_register(struct net_device *dev, 2438c2ecf20Sopenharmony_ci struct failover_ops *ops) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct failover *failover; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (dev->type != ARPHRD_ETHER) 2488c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci failover = kzalloc(sizeof(*failover), GFP_KERNEL); 2518c2ecf20Sopenharmony_ci if (!failover) 2528c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci rcu_assign_pointer(failover->ops, ops); 2558c2ecf20Sopenharmony_ci dev_hold(dev); 2568c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_FAILOVER; 2578c2ecf20Sopenharmony_ci rcu_assign_pointer(failover->failover_dev, dev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_lock(&failover_lock); 2608c2ecf20Sopenharmony_ci list_add_tail(&failover->list, &failover_list); 2618c2ecf20Sopenharmony_ci spin_unlock(&failover_lock); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci netdev_info(dev, "failover master:%s registered\n", dev->name); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci failover_existing_slave_register(dev); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return failover; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(failover_register); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/** 2728c2ecf20Sopenharmony_ci * failover_unregister - Unregister a failover instance 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * @failover: pointer to failover instance 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * Unregisters and frees a failover instance. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_civoid failover_unregister(struct failover *failover) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct net_device *failover_dev; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci failover_dev = rcu_dereference(failover->failover_dev); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci netdev_info(failover_dev, "failover master:%s unregistered\n", 2858c2ecf20Sopenharmony_ci failover_dev->name); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci failover_dev->priv_flags &= ~IFF_FAILOVER; 2888c2ecf20Sopenharmony_ci dev_put(failover_dev); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci spin_lock(&failover_lock); 2918c2ecf20Sopenharmony_ci list_del(&failover->list); 2928c2ecf20Sopenharmony_ci spin_unlock(&failover_lock); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci kfree(failover); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(failover_unregister); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic __init int 2998c2ecf20Sopenharmony_cifailover_init(void) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci register_netdevice_notifier(&failover_notifier); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_cimodule_init(failover_init); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic __exit 3088c2ecf20Sopenharmony_civoid failover_exit(void) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&failover_notifier); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_cimodule_exit(failover_exit); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic failover infrastructure/interface"); 3158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 316