1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2018, Intel Corporation. */ 3 4 /* A common module to handle registrations and notifications for paravirtual 5 * drivers to enable accelerated datapath and support VF live migration. 6 * 7 * The notifier and event handling code is based on netvsc driver. 8 */ 9 10 #include <linux/module.h> 11 #include <linux/etherdevice.h> 12 #include <uapi/linux/if_arp.h> 13 #include <linux/rtnetlink.h> 14 #include <linux/if_vlan.h> 15 #include <net/failover.h> 16 17 static LIST_HEAD(failover_list); 18 static DEFINE_SPINLOCK(failover_lock); 19 20 static struct net_device *failover_get_bymac(u8 *mac, struct failover_ops **ops) 21 { 22 struct net_device *failover_dev; 23 struct failover *failover; 24 25 spin_lock(&failover_lock); 26 list_for_each_entry(failover, &failover_list, list) { 27 failover_dev = rtnl_dereference(failover->failover_dev); 28 if (ether_addr_equal(failover_dev->perm_addr, mac)) { 29 *ops = rtnl_dereference(failover->ops); 30 spin_unlock(&failover_lock); 31 return failover_dev; 32 } 33 } 34 spin_unlock(&failover_lock); 35 return NULL; 36 } 37 38 /** 39 * failover_slave_register - Register a slave netdev 40 * 41 * @slave_dev: slave netdev that is being registered 42 * 43 * Registers a slave device to a failover instance. Only ethernet devices 44 * are supported. 45 */ 46 static int failover_slave_register(struct net_device *slave_dev) 47 { 48 struct netdev_lag_upper_info lag_upper_info; 49 struct net_device *failover_dev; 50 struct failover_ops *fops; 51 int err; 52 53 if (slave_dev->type != ARPHRD_ETHER) 54 goto done; 55 56 ASSERT_RTNL(); 57 58 failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 59 if (!failover_dev) 60 goto done; 61 62 if (fops && fops->slave_pre_register && 63 fops->slave_pre_register(slave_dev, failover_dev)) 64 goto done; 65 66 err = netdev_rx_handler_register(slave_dev, fops->slave_handle_frame, 67 failover_dev); 68 if (err) { 69 netdev_err(slave_dev, "can not register failover rx handler (err = %d)\n", 70 err); 71 goto done; 72 } 73 74 lag_upper_info.tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP; 75 err = netdev_master_upper_dev_link(slave_dev, failover_dev, NULL, 76 &lag_upper_info, NULL); 77 if (err) { 78 netdev_err(slave_dev, "can not set failover device %s (err = %d)\n", 79 failover_dev->name, err); 80 goto err_upper_link; 81 } 82 83 slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); 84 85 if (fops && fops->slave_register && 86 !fops->slave_register(slave_dev, failover_dev)) 87 return NOTIFY_OK; 88 89 netdev_upper_dev_unlink(slave_dev, failover_dev); 90 slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); 91 err_upper_link: 92 netdev_rx_handler_unregister(slave_dev); 93 done: 94 return NOTIFY_DONE; 95 } 96 97 /** 98 * failover_slave_unregister - Unregister a slave netdev 99 * 100 * @slave_dev: slave netdev that is being unregistered 101 * 102 * Unregisters a slave device from a failover instance. 103 */ 104 int failover_slave_unregister(struct net_device *slave_dev) 105 { 106 struct net_device *failover_dev; 107 struct failover_ops *fops; 108 109 if (!netif_is_failover_slave(slave_dev)) 110 goto done; 111 112 ASSERT_RTNL(); 113 114 failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 115 if (!failover_dev) 116 goto done; 117 118 if (fops && fops->slave_pre_unregister && 119 fops->slave_pre_unregister(slave_dev, failover_dev)) 120 goto done; 121 122 netdev_rx_handler_unregister(slave_dev); 123 netdev_upper_dev_unlink(slave_dev, failover_dev); 124 slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); 125 126 if (fops && fops->slave_unregister && 127 !fops->slave_unregister(slave_dev, failover_dev)) 128 return NOTIFY_OK; 129 130 done: 131 return NOTIFY_DONE; 132 } 133 EXPORT_SYMBOL_GPL(failover_slave_unregister); 134 135 static int failover_slave_link_change(struct net_device *slave_dev) 136 { 137 struct net_device *failover_dev; 138 struct failover_ops *fops; 139 140 if (!netif_is_failover_slave(slave_dev)) 141 goto done; 142 143 ASSERT_RTNL(); 144 145 failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 146 if (!failover_dev) 147 goto done; 148 149 if (!netif_running(failover_dev)) 150 goto done; 151 152 if (fops && fops->slave_link_change && 153 !fops->slave_link_change(slave_dev, failover_dev)) 154 return NOTIFY_OK; 155 156 done: 157 return NOTIFY_DONE; 158 } 159 160 static int failover_slave_name_change(struct net_device *slave_dev) 161 { 162 struct net_device *failover_dev; 163 struct failover_ops *fops; 164 165 if (!netif_is_failover_slave(slave_dev)) 166 goto done; 167 168 ASSERT_RTNL(); 169 170 failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops); 171 if (!failover_dev) 172 goto done; 173 174 if (!netif_running(failover_dev)) 175 goto done; 176 177 if (fops && fops->slave_name_change && 178 !fops->slave_name_change(slave_dev, failover_dev)) 179 return NOTIFY_OK; 180 181 done: 182 return NOTIFY_DONE; 183 } 184 185 static int 186 failover_event(struct notifier_block *this, unsigned long event, void *ptr) 187 { 188 struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); 189 190 /* Skip parent events */ 191 if (netif_is_failover(event_dev)) 192 return NOTIFY_DONE; 193 194 switch (event) { 195 case NETDEV_REGISTER: 196 return failover_slave_register(event_dev); 197 case NETDEV_UNREGISTER: 198 return failover_slave_unregister(event_dev); 199 case NETDEV_UP: 200 case NETDEV_DOWN: 201 case NETDEV_CHANGE: 202 return failover_slave_link_change(event_dev); 203 case NETDEV_CHANGENAME: 204 return failover_slave_name_change(event_dev); 205 default: 206 return NOTIFY_DONE; 207 } 208 } 209 210 static struct notifier_block failover_notifier = { 211 .notifier_call = failover_event, 212 }; 213 214 static void 215 failover_existing_slave_register(struct net_device *failover_dev) 216 { 217 struct net *net = dev_net(failover_dev); 218 struct net_device *dev; 219 220 rtnl_lock(); 221 for_each_netdev(net, dev) { 222 if (netif_is_failover(dev)) 223 continue; 224 if (ether_addr_equal(failover_dev->perm_addr, dev->perm_addr)) 225 failover_slave_register(dev); 226 } 227 rtnl_unlock(); 228 } 229 230 /** 231 * failover_register - Register a failover instance 232 * 233 * @dev: failover netdev 234 * @ops: failover ops 235 * 236 * Allocate and register a failover instance for a failover netdev. ops 237 * provides handlers for slave device register/unregister/link change/ 238 * name change events. 239 * 240 * Return: pointer to failover instance 241 */ 242 struct failover *failover_register(struct net_device *dev, 243 struct failover_ops *ops) 244 { 245 struct failover *failover; 246 247 if (dev->type != ARPHRD_ETHER) 248 return ERR_PTR(-EINVAL); 249 250 failover = kzalloc(sizeof(*failover), GFP_KERNEL); 251 if (!failover) 252 return ERR_PTR(-ENOMEM); 253 254 rcu_assign_pointer(failover->ops, ops); 255 netdev_hold(dev, &failover->dev_tracker, GFP_KERNEL); 256 dev->priv_flags |= IFF_FAILOVER; 257 rcu_assign_pointer(failover->failover_dev, dev); 258 259 spin_lock(&failover_lock); 260 list_add_tail(&failover->list, &failover_list); 261 spin_unlock(&failover_lock); 262 263 netdev_info(dev, "failover master:%s registered\n", dev->name); 264 265 failover_existing_slave_register(dev); 266 267 return failover; 268 } 269 EXPORT_SYMBOL_GPL(failover_register); 270 271 /** 272 * failover_unregister - Unregister a failover instance 273 * 274 * @failover: pointer to failover instance 275 * 276 * Unregisters and frees a failover instance. 277 */ 278 void failover_unregister(struct failover *failover) 279 { 280 struct net_device *failover_dev; 281 282 failover_dev = rcu_dereference(failover->failover_dev); 283 284 netdev_info(failover_dev, "failover master:%s unregistered\n", 285 failover_dev->name); 286 287 failover_dev->priv_flags &= ~IFF_FAILOVER; 288 netdev_put(failover_dev, &failover->dev_tracker); 289 290 spin_lock(&failover_lock); 291 list_del(&failover->list); 292 spin_unlock(&failover_lock); 293 294 kfree(failover); 295 } 296 EXPORT_SYMBOL_GPL(failover_unregister); 297 298 static __init int 299 failover_init(void) 300 { 301 register_netdevice_notifier(&failover_notifier); 302 303 return 0; 304 } 305 module_init(failover_init); 306 307 static __exit 308 void failover_exit(void) 309 { 310 unregister_netdevice_notifier(&failover_notifier); 311 } 312 module_exit(failover_exit); 313 314 MODULE_DESCRIPTION("Generic failover infrastructure/interface"); 315 MODULE_LICENSE("GPL v2"); 316
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.