2018-05-24 19:55:15 +03:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, Intel Corporation. */
/* This provides a net_failover interface for paravirtual drivers to
* provide an alternate datapath by exporting APIs to create and
* destroy a upper ' net_failover ' netdev . The upper dev manages the
* original paravirtual interface as a ' standby ' netdev and uses the
* generic failover infrastructure to register and manage a direct
* attached VF as a ' primary ' netdev . This enables live migration of
* a VM with direct attached VF by failing over to the paravirtual
* datapath when the VF is unplugged .
*
* Some of the netdev management routines are based on bond / team driver as
* this driver provides active - backup functionality similar to those drivers .
*/
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/netpoll.h>
# include <linux/rtnetlink.h>
# include <linux/if_vlan.h>
# include <linux/pci.h>
# include <net/sch_generic.h>
# include <uapi/linux/if_arp.h>
# include <net/net_failover.h>
static bool net_failover_xmit_ready ( struct net_device * dev )
{
return netif_running ( dev ) & & netif_carrier_ok ( dev ) ;
}
static int net_failover_open ( struct net_device * dev )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * primary_dev , * standby_dev ;
int err ;
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
if ( primary_dev ) {
2018-12-06 20:05:36 +03:00
err = dev_open ( primary_dev , NULL ) ;
2018-05-24 19:55:15 +03:00
if ( err )
goto err_primary_open ;
}
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
if ( standby_dev ) {
2018-12-06 20:05:36 +03:00
err = dev_open ( standby_dev , NULL ) ;
2018-05-24 19:55:15 +03:00
if ( err )
goto err_standby_open ;
}
if ( ( primary_dev & & net_failover_xmit_ready ( primary_dev ) ) | |
( standby_dev & & net_failover_xmit_ready ( standby_dev ) ) ) {
netif_carrier_on ( dev ) ;
netif_tx_wake_all_queues ( dev ) ;
}
return 0 ;
err_standby_open :
dev_close ( primary_dev ) ;
err_primary_open :
netif_tx_disable ( dev ) ;
return err ;
}
static int net_failover_close ( struct net_device * dev )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * slave_dev ;
netif_tx_disable ( dev ) ;
slave_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
if ( slave_dev )
dev_close ( slave_dev ) ;
slave_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev )
dev_close ( slave_dev ) ;
return 0 ;
}
static netdev_tx_t net_failover_drop_xmit ( struct sk_buff * skb ,
struct net_device * dev )
{
atomic_long_inc ( & dev - > tx_dropped ) ;
dev_kfree_skb_any ( skb ) ;
return NETDEV_TX_OK ;
}
static netdev_tx_t net_failover_start_xmit ( struct sk_buff * skb ,
struct net_device * dev )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * xmit_dev ;
/* Try xmit via primary netdev followed by standby netdev */
xmit_dev = rcu_dereference_bh ( nfo_info - > primary_dev ) ;
if ( ! xmit_dev | | ! net_failover_xmit_ready ( xmit_dev ) ) {
xmit_dev = rcu_dereference_bh ( nfo_info - > standby_dev ) ;
if ( ! xmit_dev | | ! net_failover_xmit_ready ( xmit_dev ) )
return net_failover_drop_xmit ( skb , dev ) ;
}
skb - > dev = xmit_dev ;
skb - > queue_mapping = qdisc_skb_cb ( skb ) - > slave_dev_queue_mapping ;
return dev_queue_xmit ( skb ) ;
}
static u16 net_failover_select_queue ( struct net_device * dev ,
2018-07-09 19:19:59 +03:00
struct sk_buff * skb ,
2019-03-20 13:02:06 +03:00
struct net_device * sb_dev )
2018-05-24 19:55:15 +03:00
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * primary_dev ;
u16 txq ;
primary_dev = rcu_dereference ( nfo_info - > primary_dev ) ;
if ( primary_dev ) {
const struct net_device_ops * ops = primary_dev - > netdev_ops ;
if ( ops - > ndo_select_queue )
2019-03-20 13:02:06 +03:00
txq = ops - > ndo_select_queue ( primary_dev , skb , sb_dev ) ;
2018-05-24 19:55:15 +03:00
else
2019-03-20 13:02:06 +03:00
txq = netdev_pick_tx ( primary_dev , skb , NULL ) ;
2018-05-24 19:55:15 +03:00
qdisc_skb_cb ( skb ) - > slave_dev_queue_mapping = skb - > queue_mapping ;
return txq ;
}
txq = skb_rx_queue_recorded ( skb ) ? skb_get_rx_queue ( skb ) : 0 ;
/* Save the original txq to restore before passing to the driver */
qdisc_skb_cb ( skb ) - > slave_dev_queue_mapping = skb - > queue_mapping ;
if ( unlikely ( txq > = dev - > real_num_tx_queues ) ) {
do {
txq - = dev - > real_num_tx_queues ;
} while ( txq > = dev - > real_num_tx_queues ) ;
}
return txq ;
}
/* fold stats, assuming all rtnl_link_stats64 fields are u64, but
* that some drivers can provide 32 bit values only .
*/
static void net_failover_fold_stats ( struct rtnl_link_stats64 * _res ,
const struct rtnl_link_stats64 * _new ,
const struct rtnl_link_stats64 * _old )
{
const u64 * new = ( const u64 * ) _new ;
const u64 * old = ( const u64 * ) _old ;
u64 * res = ( u64 * ) _res ;
int i ;
for ( i = 0 ; i < sizeof ( * _res ) / sizeof ( u64 ) ; i + + ) {
u64 nv = new [ i ] ;
u64 ov = old [ i ] ;
s64 delta = nv - ov ;
/* detects if this particular field is 32bit only */
if ( ( ( nv | ov ) > > 32 ) = = 0 )
delta = ( s64 ) ( s32 ) ( ( u32 ) nv - ( u32 ) ov ) ;
/* filter anomalies, some drivers reset their stats
* at down / up events .
*/
if ( delta > 0 )
res [ i ] + = delta ;
}
}
static void net_failover_get_stats ( struct net_device * dev ,
struct rtnl_link_stats64 * stats )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
const struct rtnl_link_stats64 * new ;
struct rtnl_link_stats64 temp ;
struct net_device * slave_dev ;
spin_lock ( & nfo_info - > stats_lock ) ;
memcpy ( stats , & nfo_info - > failover_stats , sizeof ( * stats ) ) ;
rcu_read_lock ( ) ;
slave_dev = rcu_dereference ( nfo_info - > primary_dev ) ;
if ( slave_dev ) {
new = dev_get_stats ( slave_dev , & temp ) ;
net_failover_fold_stats ( stats , new , & nfo_info - > primary_stats ) ;
memcpy ( & nfo_info - > primary_stats , new , sizeof ( * new ) ) ;
}
slave_dev = rcu_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev ) {
new = dev_get_stats ( slave_dev , & temp ) ;
net_failover_fold_stats ( stats , new , & nfo_info - > standby_stats ) ;
memcpy ( & nfo_info - > standby_stats , new , sizeof ( * new ) ) ;
}
rcu_read_unlock ( ) ;
memcpy ( & nfo_info - > failover_stats , stats , sizeof ( * stats ) ) ;
spin_unlock ( & nfo_info - > stats_lock ) ;
}
static int net_failover_change_mtu ( struct net_device * dev , int new_mtu )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * primary_dev , * standby_dev ;
int ret = 0 ;
2018-07-27 23:43:21 +03:00
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
2018-05-24 19:55:15 +03:00
if ( primary_dev ) {
ret = dev_set_mtu ( primary_dev , new_mtu ) ;
if ( ret )
return ret ;
}
2018-07-27 23:43:21 +03:00
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
2018-05-24 19:55:15 +03:00
if ( standby_dev ) {
ret = dev_set_mtu ( standby_dev , new_mtu ) ;
if ( ret ) {
if ( primary_dev )
dev_set_mtu ( primary_dev , dev - > mtu ) ;
return ret ;
}
}
dev - > mtu = new_mtu ;
return 0 ;
}
static void net_failover_set_rx_mode ( struct net_device * dev )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * slave_dev ;
rcu_read_lock ( ) ;
slave_dev = rcu_dereference ( nfo_info - > primary_dev ) ;
if ( slave_dev ) {
dev_uc_sync_multiple ( slave_dev , dev ) ;
dev_mc_sync_multiple ( slave_dev , dev ) ;
}
slave_dev = rcu_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev ) {
dev_uc_sync_multiple ( slave_dev , dev ) ;
dev_mc_sync_multiple ( slave_dev , dev ) ;
}
rcu_read_unlock ( ) ;
}
static int net_failover_vlan_rx_add_vid ( struct net_device * dev , __be16 proto ,
u16 vid )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * primary_dev , * standby_dev ;
int ret = 0 ;
primary_dev = rcu_dereference ( nfo_info - > primary_dev ) ;
if ( primary_dev ) {
ret = vlan_vid_add ( primary_dev , proto , vid ) ;
if ( ret )
return ret ;
}
standby_dev = rcu_dereference ( nfo_info - > standby_dev ) ;
if ( standby_dev ) {
ret = vlan_vid_add ( standby_dev , proto , vid ) ;
if ( ret )
if ( primary_dev )
vlan_vid_del ( primary_dev , proto , vid ) ;
}
return ret ;
}
static int net_failover_vlan_rx_kill_vid ( struct net_device * dev , __be16 proto ,
u16 vid )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * slave_dev ;
slave_dev = rcu_dereference ( nfo_info - > primary_dev ) ;
if ( slave_dev )
vlan_vid_del ( slave_dev , proto , vid ) ;
slave_dev = rcu_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev )
vlan_vid_del ( slave_dev , proto , vid ) ;
return 0 ;
}
static const struct net_device_ops failover_dev_ops = {
. ndo_open = net_failover_open ,
. ndo_stop = net_failover_close ,
. ndo_start_xmit = net_failover_start_xmit ,
. ndo_select_queue = net_failover_select_queue ,
. ndo_get_stats64 = net_failover_get_stats ,
. ndo_change_mtu = net_failover_change_mtu ,
. ndo_set_rx_mode = net_failover_set_rx_mode ,
. ndo_vlan_rx_add_vid = net_failover_vlan_rx_add_vid ,
. ndo_vlan_rx_kill_vid = net_failover_vlan_rx_kill_vid ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_features_check = passthru_features_check ,
} ;
# define FAILOVER_NAME "net_failover"
# define FAILOVER_VERSION "0.1"
static void nfo_ethtool_get_drvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * drvinfo )
{
strlcpy ( drvinfo - > driver , FAILOVER_NAME , sizeof ( drvinfo - > driver ) ) ;
strlcpy ( drvinfo - > version , FAILOVER_VERSION , sizeof ( drvinfo - > version ) ) ;
}
static int nfo_ethtool_get_link_ksettings ( struct net_device * dev ,
struct ethtool_link_ksettings * cmd )
{
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * slave_dev ;
slave_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
if ( ! slave_dev | | ! net_failover_xmit_ready ( slave_dev ) ) {
slave_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
if ( ! slave_dev | | ! net_failover_xmit_ready ( slave_dev ) ) {
cmd - > base . duplex = DUPLEX_UNKNOWN ;
cmd - > base . port = PORT_OTHER ;
cmd - > base . speed = SPEED_UNKNOWN ;
return 0 ;
}
}
return __ethtool_get_link_ksettings ( slave_dev , cmd ) ;
}
static const struct ethtool_ops failover_ethtool_ops = {
. get_drvinfo = nfo_ethtool_get_drvinfo ,
. get_link = ethtool_op_get_link ,
. get_link_ksettings = nfo_ethtool_get_link_ksettings ,
} ;
/* Called when slave dev is injecting data into network stack.
* Change the associated network device from lower dev to failover dev .
* note : already called with rcu_read_lock
*/
static rx_handler_result_t net_failover_handle_frame ( struct sk_buff * * pskb )
{
struct sk_buff * skb = * pskb ;
struct net_device * dev = rcu_dereference ( skb - > dev - > rx_handler_data ) ;
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * primary_dev , * standby_dev ;
primary_dev = rcu_dereference ( nfo_info - > primary_dev ) ;
standby_dev = rcu_dereference ( nfo_info - > standby_dev ) ;
if ( primary_dev & & skb - > dev = = standby_dev )
return RX_HANDLER_EXACT ;
skb - > dev = dev ;
return RX_HANDLER_ANOTHER ;
}
static void net_failover_compute_features ( struct net_device * dev )
{
2018-06-04 17:43:21 +03:00
netdev_features_t vlan_features = FAILOVER_VLAN_FEATURES &
NETIF_F_ALL_FOR_ALL ;
2018-05-24 19:55:15 +03:00
netdev_features_t enc_features = FAILOVER_ENC_FEATURES ;
unsigned short max_hard_header_len = ETH_HLEN ;
unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
IFF_XMIT_DST_RELEASE_PERM ;
struct net_failover_info * nfo_info = netdev_priv ( dev ) ;
struct net_device * primary_dev , * standby_dev ;
primary_dev = rcu_dereference ( nfo_info - > primary_dev ) ;
if ( primary_dev ) {
vlan_features =
netdev_increment_features ( vlan_features ,
primary_dev - > vlan_features ,
FAILOVER_VLAN_FEATURES ) ;
enc_features =
netdev_increment_features ( enc_features ,
primary_dev - > hw_enc_features ,
FAILOVER_ENC_FEATURES ) ;
dst_release_flag & = primary_dev - > priv_flags ;
if ( primary_dev - > hard_header_len > max_hard_header_len )
max_hard_header_len = primary_dev - > hard_header_len ;
}
standby_dev = rcu_dereference ( nfo_info - > standby_dev ) ;
if ( standby_dev ) {
vlan_features =
netdev_increment_features ( vlan_features ,
standby_dev - > vlan_features ,
FAILOVER_VLAN_FEATURES ) ;
enc_features =
netdev_increment_features ( enc_features ,
standby_dev - > hw_enc_features ,
FAILOVER_ENC_FEATURES ) ;
dst_release_flag & = standby_dev - > priv_flags ;
if ( standby_dev - > hard_header_len > max_hard_header_len )
max_hard_header_len = standby_dev - > hard_header_len ;
}
dev - > vlan_features = vlan_features ;
dev - > hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL ;
dev - > hard_header_len = max_hard_header_len ;
dev - > priv_flags & = ~ IFF_XMIT_DST_RELEASE ;
if ( dst_release_flag = = ( IFF_XMIT_DST_RELEASE |
IFF_XMIT_DST_RELEASE_PERM ) )
dev - > priv_flags | = IFF_XMIT_DST_RELEASE ;
netdev_change_features ( dev ) ;
}
static void net_failover_lower_state_changed ( struct net_device * slave_dev ,
struct net_device * primary_dev ,
struct net_device * standby_dev )
{
struct netdev_lag_lower_state_info info ;
if ( netif_carrier_ok ( slave_dev ) )
info . link_up = true ;
else
info . link_up = false ;
if ( slave_dev = = primary_dev ) {
if ( netif_running ( primary_dev ) )
info . tx_enabled = true ;
else
info . tx_enabled = false ;
} else {
if ( ( primary_dev & & netif_running ( primary_dev ) ) | |
( ! netif_running ( standby_dev ) ) )
info . tx_enabled = false ;
else
info . tx_enabled = true ;
}
netdev_lower_state_changed ( slave_dev , & info ) ;
}
static int net_failover_slave_pre_register ( struct net_device * slave_dev ,
struct net_device * failover_dev )
{
struct net_device * standby_dev , * primary_dev ;
struct net_failover_info * nfo_info ;
bool slave_is_standby ;
nfo_info = netdev_priv ( failover_dev ) ;
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
slave_is_standby = slave_dev - > dev . parent = = failover_dev - > dev . parent ;
if ( slave_is_standby ? standby_dev : primary_dev ) {
netdev_err ( failover_dev , " %s attempting to register as slave dev when %s already present \n " ,
slave_dev - > name ,
slave_is_standby ? " standby " : " primary " ) ;
return - EINVAL ;
}
/* We want to allow only a direct attached VF device as a primary
* netdev . As there is no easy way to check for a VF device , restrict
* this to a pci device .
*/
if ( ! slave_is_standby & & ( ! slave_dev - > dev . parent | |
! dev_is_pci ( slave_dev - > dev . parent ) ) )
return - EINVAL ;
if ( failover_dev - > features & NETIF_F_VLAN_CHALLENGED & &
vlan_uses_dev ( failover_dev ) ) {
netdev_err ( failover_dev , " Device %s is VLAN challenged and failover device has VLAN set up \n " ,
failover_dev - > name ) ;
return - EINVAL ;
}
return 0 ;
}
static int net_failover_slave_register ( struct net_device * slave_dev ,
struct net_device * failover_dev )
{
struct net_device * standby_dev , * primary_dev ;
struct net_failover_info * nfo_info ;
bool slave_is_standby ;
u32 orig_mtu ;
int err ;
/* Align MTU of slave with failover dev */
orig_mtu = slave_dev - > mtu ;
err = dev_set_mtu ( slave_dev , failover_dev - > mtu ) ;
if ( err ) {
netdev_err ( failover_dev , " unable to change mtu of %s to %u register failed \n " ,
slave_dev - > name , failover_dev - > mtu ) ;
goto done ;
}
dev_hold ( slave_dev ) ;
if ( netif_running ( failover_dev ) ) {
2018-12-06 20:05:36 +03:00
err = dev_open ( slave_dev , NULL ) ;
2018-05-24 19:55:15 +03:00
if ( err & & ( err ! = - EBUSY ) ) {
netdev_err ( failover_dev , " Opening slave %s failed err:%d \n " ,
slave_dev - > name , err ) ;
goto err_dev_open ;
}
}
netif_addr_lock_bh ( failover_dev ) ;
dev_uc_sync_multiple ( slave_dev , failover_dev ) ;
2018-06-18 15:04:05 +03:00
dev_mc_sync_multiple ( slave_dev , failover_dev ) ;
2018-05-24 19:55:15 +03:00
netif_addr_unlock_bh ( failover_dev ) ;
err = vlan_vids_add_by_dev ( slave_dev , failover_dev ) ;
if ( err ) {
netdev_err ( failover_dev , " Failed to add vlan ids to device %s err:%d \n " ,
slave_dev - > name , err ) ;
goto err_vlan_add ;
}
nfo_info = netdev_priv ( failover_dev ) ;
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
slave_is_standby = slave_dev - > dev . parent = = failover_dev - > dev . parent ;
if ( slave_is_standby ) {
rcu_assign_pointer ( nfo_info - > standby_dev , slave_dev ) ;
standby_dev = slave_dev ;
dev_get_stats ( standby_dev , & nfo_info - > standby_stats ) ;
} else {
rcu_assign_pointer ( nfo_info - > primary_dev , slave_dev ) ;
primary_dev = slave_dev ;
dev_get_stats ( primary_dev , & nfo_info - > primary_stats ) ;
failover_dev - > min_mtu = slave_dev - > min_mtu ;
failover_dev - > max_mtu = slave_dev - > max_mtu ;
}
net_failover_lower_state_changed ( slave_dev , primary_dev , standby_dev ) ;
net_failover_compute_features ( failover_dev ) ;
call_netdevice_notifiers ( NETDEV_JOIN , slave_dev ) ;
netdev_info ( failover_dev , " failover %s slave:%s registered \n " ,
slave_is_standby ? " standby " : " primary " , slave_dev - > name ) ;
return 0 ;
err_vlan_add :
dev_uc_unsync ( slave_dev , failover_dev ) ;
dev_mc_unsync ( slave_dev , failover_dev ) ;
dev_close ( slave_dev ) ;
err_dev_open :
dev_put ( slave_dev ) ;
dev_set_mtu ( slave_dev , orig_mtu ) ;
done :
return err ;
}
static int net_failover_slave_pre_unregister ( struct net_device * slave_dev ,
struct net_device * failover_dev )
{
struct net_device * standby_dev , * primary_dev ;
struct net_failover_info * nfo_info ;
nfo_info = netdev_priv ( failover_dev ) ;
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev ! = primary_dev & & slave_dev ! = standby_dev )
return - ENODEV ;
return 0 ;
}
static int net_failover_slave_unregister ( struct net_device * slave_dev ,
struct net_device * failover_dev )
{
struct net_device * standby_dev , * primary_dev ;
struct net_failover_info * nfo_info ;
bool slave_is_standby ;
nfo_info = netdev_priv ( failover_dev ) ;
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
2018-09-04 05:56:26 +03:00
if ( WARN_ON_ONCE ( slave_dev ! = primary_dev & & slave_dev ! = standby_dev ) )
return - ENODEV ;
2018-05-24 19:55:15 +03:00
vlan_vids_del_by_dev ( slave_dev , failover_dev ) ;
dev_uc_unsync ( slave_dev , failover_dev ) ;
dev_mc_unsync ( slave_dev , failover_dev ) ;
dev_close ( slave_dev ) ;
nfo_info = netdev_priv ( failover_dev ) ;
dev_get_stats ( failover_dev , & nfo_info - > failover_stats ) ;
slave_is_standby = slave_dev - > dev . parent = = failover_dev - > dev . parent ;
if ( slave_is_standby ) {
RCU_INIT_POINTER ( nfo_info - > standby_dev , NULL ) ;
} else {
RCU_INIT_POINTER ( nfo_info - > primary_dev , NULL ) ;
if ( standby_dev ) {
failover_dev - > min_mtu = standby_dev - > min_mtu ;
failover_dev - > max_mtu = standby_dev - > max_mtu ;
}
}
dev_put ( slave_dev ) ;
net_failover_compute_features ( failover_dev ) ;
netdev_info ( failover_dev , " failover %s slave:%s unregistered \n " ,
slave_is_standby ? " standby " : " primary " , slave_dev - > name ) ;
return 0 ;
}
static int net_failover_slave_link_change ( struct net_device * slave_dev ,
struct net_device * failover_dev )
{
struct net_device * primary_dev , * standby_dev ;
struct net_failover_info * nfo_info ;
nfo_info = netdev_priv ( failover_dev ) ;
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev ! = primary_dev & & slave_dev ! = standby_dev )
return - ENODEV ;
if ( ( primary_dev & & net_failover_xmit_ready ( primary_dev ) ) | |
( standby_dev & & net_failover_xmit_ready ( standby_dev ) ) ) {
netif_carrier_on ( failover_dev ) ;
netif_tx_wake_all_queues ( failover_dev ) ;
} else {
dev_get_stats ( failover_dev , & nfo_info - > failover_stats ) ;
netif_carrier_off ( failover_dev ) ;
netif_tx_stop_all_queues ( failover_dev ) ;
}
net_failover_lower_state_changed ( slave_dev , primary_dev , standby_dev ) ;
return 0 ;
}
static int net_failover_slave_name_change ( struct net_device * slave_dev ,
struct net_device * failover_dev )
{
struct net_device * primary_dev , * standby_dev ;
struct net_failover_info * nfo_info ;
nfo_info = netdev_priv ( failover_dev ) ;
primary_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
standby_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev ! = primary_dev & & slave_dev ! = standby_dev )
return - ENODEV ;
/* We need to bring up the slave after the rename by udev in case
* open failed with EBUSY when it was registered .
*/
2018-12-06 20:05:36 +03:00
dev_open ( slave_dev , NULL ) ;
2018-05-24 19:55:15 +03:00
return 0 ;
}
static struct failover_ops net_failover_ops = {
. slave_pre_register = net_failover_slave_pre_register ,
. slave_register = net_failover_slave_register ,
. slave_pre_unregister = net_failover_slave_pre_unregister ,
. slave_unregister = net_failover_slave_unregister ,
. slave_link_change = net_failover_slave_link_change ,
. slave_name_change = net_failover_slave_name_change ,
. slave_handle_frame = net_failover_handle_frame ,
} ;
/**
* net_failover_create - Create and register a failover instance
*
* @ dev : standby netdev
*
* Creates a failover netdev and registers a failover instance for a standby
* netdev . Used by paravirtual drivers that use 3 - netdev model .
* The failover netdev acts as a master device and controls 2 slave devices -
* the original standby netdev and a VF netdev with the same MAC gets
* registered as primary netdev .
*
* Return : pointer to failover instance
*/
struct failover * net_failover_create ( struct net_device * standby_dev )
{
struct device * dev = standby_dev - > dev . parent ;
struct net_device * failover_dev ;
struct failover * failover ;
int err ;
/* Alloc at least 2 queues, for now we are going with 16 assuming
* that VF devices being enslaved won ' t have too many queues .
*/
failover_dev = alloc_etherdev_mq ( sizeof ( struct net_failover_info ) , 16 ) ;
if ( ! failover_dev ) {
dev_err ( dev , " Unable to allocate failover_netdev! \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
dev_net_set ( failover_dev , dev_net ( standby_dev ) ) ;
SET_NETDEV_DEV ( failover_dev , dev ) ;
failover_dev - > netdev_ops = & failover_dev_ops ;
failover_dev - > ethtool_ops = & failover_ethtool_ops ;
/* Initialize the device options */
failover_dev - > priv_flags | = IFF_UNICAST_FLT | IFF_NO_QUEUE ;
failover_dev - > priv_flags & = ~ ( IFF_XMIT_DST_RELEASE |
IFF_TX_SKB_SHARING ) ;
/* don't acquire failover netdev's netif_tx_lock when transmitting */
failover_dev - > features | = NETIF_F_LLTX ;
/* Don't allow failover devices to change network namespaces. */
failover_dev - > features | = NETIF_F_NETNS_LOCAL ;
failover_dev - > hw_features = FAILOVER_VLAN_FEATURES |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER ;
failover_dev - > hw_features | = NETIF_F_GSO_ENCAP_ALL ;
failover_dev - > features | = failover_dev - > hw_features ;
memcpy ( failover_dev - > dev_addr , standby_dev - > dev_addr ,
failover_dev - > addr_len ) ;
failover_dev - > min_mtu = standby_dev - > min_mtu ;
failover_dev - > max_mtu = standby_dev - > max_mtu ;
err = register_netdev ( failover_dev ) ;
if ( err ) {
dev_err ( dev , " Unable to register failover_dev! \n " ) ;
goto err_register_netdev ;
}
netif_carrier_off ( failover_dev ) ;
failover = failover_register ( failover_dev , & net_failover_ops ) ;
2018-09-06 16:04:12 +03:00
if ( IS_ERR ( failover ) ) {
err = PTR_ERR ( failover ) ;
2018-05-24 19:55:15 +03:00
goto err_failover_register ;
2018-09-06 16:04:12 +03:00
}
2018-05-24 19:55:15 +03:00
return failover ;
err_failover_register :
unregister_netdev ( failover_dev ) ;
err_register_netdev :
free_netdev ( failover_dev ) ;
return ERR_PTR ( err ) ;
}
EXPORT_SYMBOL_GPL ( net_failover_create ) ;
/**
* net_failover_destroy - Destroy a failover instance
*
* @ failover : pointer to failover instance
*
* Unregisters any slave netdevs associated with the failover instance by
* calling failover_slave_unregister ( ) .
* unregisters the failover instance itself and finally frees the failover
* netdev . Used by paravirtual drivers that use 3 - netdev model .
*
*/
void net_failover_destroy ( struct failover * failover )
{
struct net_failover_info * nfo_info ;
struct net_device * failover_dev ;
struct net_device * slave_dev ;
if ( ! failover )
return ;
failover_dev = rcu_dereference ( failover - > failover_dev ) ;
nfo_info = netdev_priv ( failover_dev ) ;
netif_device_detach ( failover_dev ) ;
rtnl_lock ( ) ;
slave_dev = rtnl_dereference ( nfo_info - > primary_dev ) ;
if ( slave_dev )
failover_slave_unregister ( slave_dev ) ;
slave_dev = rtnl_dereference ( nfo_info - > standby_dev ) ;
if ( slave_dev )
failover_slave_unregister ( slave_dev ) ;
failover_unregister ( failover ) ;
unregister_netdevice ( failover_dev ) ;
rtnl_unlock ( ) ;
free_netdev ( failover_dev ) ;
}
EXPORT_SYMBOL_GPL ( net_failover_destroy ) ;
static __init int
net_failover_init ( void )
{
return 0 ;
}
module_init ( net_failover_init ) ;
static __exit
void net_failover_exit ( void )
{
}
module_exit ( net_failover_exit ) ;
MODULE_DESCRIPTION ( " Failover driver for Paravirtual drivers " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;