Files
linux-stable-mirror/net/core/failover.c
Linus Torvalds bf4afc53b7 Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
This was done entirely with mindless brute force, using

    git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
        xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'

to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.

Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.

For the same reason the 'flex' versions will be done as a separate
conversion.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-02-21 17:09:51 -08:00

316 lines
7.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, Intel Corporation. */
/* A common module to handle registrations and notifications for paravirtual
* drivers to enable accelerated datapath and support VF live migration.
*
* The notifier and event handling code is based on netvsc driver.
*/
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <uapi/linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/if_vlan.h>
#include <net/failover.h>
static LIST_HEAD(failover_list);
static DEFINE_SPINLOCK(failover_lock);
static struct net_device *failover_get_bymac(u8 *mac, struct failover_ops **ops)
{
struct net_device *failover_dev;
struct failover *failover;
spin_lock(&failover_lock);
list_for_each_entry(failover, &failover_list, list) {
failover_dev = rtnl_dereference(failover->failover_dev);
if (ether_addr_equal(failover_dev->perm_addr, mac)) {
*ops = rtnl_dereference(failover->ops);
spin_unlock(&failover_lock);
return failover_dev;
}
}
spin_unlock(&failover_lock);
return NULL;
}
/**
* failover_slave_register - Register a slave netdev
*
* @slave_dev: slave netdev that is being registered
*
* Registers a slave device to a failover instance. Only ethernet devices
* are supported.
*/
static int failover_slave_register(struct net_device *slave_dev)
{
struct netdev_lag_upper_info lag_upper_info;
struct net_device *failover_dev;
struct failover_ops *fops;
int err;
if (slave_dev->type != ARPHRD_ETHER)
goto done;
ASSERT_RTNL();
failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
if (!failover_dev)
goto done;
if (fops && fops->slave_pre_register &&
fops->slave_pre_register(slave_dev, failover_dev))
goto done;
err = netdev_rx_handler_register(slave_dev, fops->slave_handle_frame,
failover_dev);
if (err) {
netdev_err(slave_dev, "can not register failover rx handler (err = %d)\n",
err);
goto done;
}
lag_upper_info.tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP;
err = netdev_master_upper_dev_link(slave_dev, failover_dev, NULL,
&lag_upper_info, NULL);
if (err) {
netdev_err(slave_dev, "can not set failover device %s (err = %d)\n",
failover_dev->name, err);
goto err_upper_link;
}
slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF);
if (fops && fops->slave_register &&
!fops->slave_register(slave_dev, failover_dev))
return NOTIFY_OK;
netdev_upper_dev_unlink(slave_dev, failover_dev);
slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF);
err_upper_link:
netdev_rx_handler_unregister(slave_dev);
done:
return NOTIFY_DONE;
}
/**
* failover_slave_unregister - Unregister a slave netdev
*
* @slave_dev: slave netdev that is being unregistered
*
* Unregisters a slave device from a failover instance.
*/
int failover_slave_unregister(struct net_device *slave_dev)
{
struct net_device *failover_dev;
struct failover_ops *fops;
if (!netif_is_failover_slave(slave_dev))
goto done;
ASSERT_RTNL();
failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
if (!failover_dev)
goto done;
if (fops && fops->slave_pre_unregister &&
fops->slave_pre_unregister(slave_dev, failover_dev))
goto done;
netdev_rx_handler_unregister(slave_dev);
netdev_upper_dev_unlink(slave_dev, failover_dev);
slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF);
if (fops && fops->slave_unregister &&
!fops->slave_unregister(slave_dev, failover_dev))
return NOTIFY_OK;
done:
return NOTIFY_DONE;
}
EXPORT_SYMBOL_GPL(failover_slave_unregister);
static int failover_slave_link_change(struct net_device *slave_dev)
{
struct net_device *failover_dev;
struct failover_ops *fops;
if (!netif_is_failover_slave(slave_dev))
goto done;
ASSERT_RTNL();
failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
if (!failover_dev)
goto done;
if (!netif_running(failover_dev))
goto done;
if (fops && fops->slave_link_change &&
!fops->slave_link_change(slave_dev, failover_dev))
return NOTIFY_OK;
done:
return NOTIFY_DONE;
}
static int failover_slave_name_change(struct net_device *slave_dev)
{
struct net_device *failover_dev;
struct failover_ops *fops;
if (!netif_is_failover_slave(slave_dev))
goto done;
ASSERT_RTNL();
failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
if (!failover_dev)
goto done;
if (!netif_running(failover_dev))
goto done;
if (fops && fops->slave_name_change &&
!fops->slave_name_change(slave_dev, failover_dev))
return NOTIFY_OK;
done:
return NOTIFY_DONE;
}
static int
failover_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
/* Skip parent events */
if (netif_is_failover(event_dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_REGISTER:
return failover_slave_register(event_dev);
case NETDEV_UNREGISTER:
return failover_slave_unregister(event_dev);
case NETDEV_UP:
case NETDEV_DOWN:
case NETDEV_CHANGE:
return failover_slave_link_change(event_dev);
case NETDEV_CHANGENAME:
return failover_slave_name_change(event_dev);
default:
return NOTIFY_DONE;
}
}
static struct notifier_block failover_notifier = {
.notifier_call = failover_event,
};
static void
failover_existing_slave_register(struct net_device *failover_dev)
{
struct net *net = dev_net(failover_dev);
struct net_device *dev;
rtnl_lock();
for_each_netdev(net, dev) {
if (netif_is_failover(dev))
continue;
if (ether_addr_equal(failover_dev->perm_addr, dev->perm_addr))
failover_slave_register(dev);
}
rtnl_unlock();
}
/**
* failover_register - Register a failover instance
*
* @dev: failover netdev
* @ops: failover ops
*
* Allocate and register a failover instance for a failover netdev. ops
* provides handlers for slave device register/unregister/link change/
* name change events.
*
* Return: pointer to failover instance
*/
struct failover *failover_register(struct net_device *dev,
struct failover_ops *ops)
{
struct failover *failover;
if (dev->type != ARPHRD_ETHER)
return ERR_PTR(-EINVAL);
failover = kzalloc_obj(*failover);
if (!failover)
return ERR_PTR(-ENOMEM);
rcu_assign_pointer(failover->ops, ops);
netdev_hold(dev, &failover->dev_tracker, GFP_KERNEL);
dev->priv_flags |= IFF_FAILOVER;
rcu_assign_pointer(failover->failover_dev, dev);
spin_lock(&failover_lock);
list_add_tail(&failover->list, &failover_list);
spin_unlock(&failover_lock);
netdev_info(dev, "failover master:%s registered\n", dev->name);
failover_existing_slave_register(dev);
return failover;
}
EXPORT_SYMBOL_GPL(failover_register);
/**
* failover_unregister - Unregister a failover instance
*
* @failover: pointer to failover instance
*
* Unregisters and frees a failover instance.
*/
void failover_unregister(struct failover *failover)
{
struct net_device *failover_dev;
failover_dev = rcu_dereference(failover->failover_dev);
netdev_info(failover_dev, "failover master:%s unregistered\n",
failover_dev->name);
failover_dev->priv_flags &= ~IFF_FAILOVER;
netdev_put(failover_dev, &failover->dev_tracker);
spin_lock(&failover_lock);
list_del(&failover->list);
spin_unlock(&failover_lock);
kfree(failover);
}
EXPORT_SYMBOL_GPL(failover_unregister);
static __init int
failover_init(void)
{
register_netdevice_notifier(&failover_notifier);
return 0;
}
module_init(failover_init);
static __exit
void failover_exit(void)
{
unregister_netdevice_notifier(&failover_notifier);
}
module_exit(failover_exit);
MODULE_DESCRIPTION("Generic failover infrastructure/interface");
MODULE_LICENSE("GPL v2");