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