18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright 2011-2014 Autronica Fire and Security AS 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Author(s): 58c2ecf20Sopenharmony_ci * 2011-2014 Arvid Brodin, arvid.brodin@alten.se 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Event handling for HSR and PRP devices. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 128c2ecf20Sopenharmony_ci#include <linux/rculist.h> 138c2ecf20Sopenharmony_ci#include <linux/timer.h> 148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 158c2ecf20Sopenharmony_ci#include "hsr_main.h" 168c2ecf20Sopenharmony_ci#include "hsr_device.h" 178c2ecf20Sopenharmony_ci#include "hsr_netlink.h" 188c2ecf20Sopenharmony_ci#include "hsr_framereg.h" 198c2ecf20Sopenharmony_ci#include "hsr_slave.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic bool hsr_slave_empty(struct hsr_priv *hsr) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct hsr_port *port; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci hsr_for_each_port(hsr, port) 268c2ecf20Sopenharmony_ci if (port->type != HSR_PT_MASTER) 278c2ecf20Sopenharmony_ci return false; 288c2ecf20Sopenharmony_ci return true; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, 328c2ecf20Sopenharmony_ci void *ptr) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct hsr_port *port, *master; 358c2ecf20Sopenharmony_ci struct net_device *dev; 368c2ecf20Sopenharmony_ci struct hsr_priv *hsr; 378c2ecf20Sopenharmony_ci LIST_HEAD(list_kill); 388c2ecf20Sopenharmony_ci int mtu_max; 398c2ecf20Sopenharmony_ci int res; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci dev = netdev_notifier_info_to_dev(ptr); 428c2ecf20Sopenharmony_ci port = hsr_port_get_rtnl(dev); 438c2ecf20Sopenharmony_ci if (!port) { 448c2ecf20Sopenharmony_ci if (!is_hsr_master(dev)) 458c2ecf20Sopenharmony_ci return NOTIFY_DONE; /* Not an HSR device */ 468c2ecf20Sopenharmony_ci hsr = netdev_priv(dev); 478c2ecf20Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 488c2ecf20Sopenharmony_ci if (!port) { 498c2ecf20Sopenharmony_ci /* Resend of notification concerning removed device? */ 508c2ecf20Sopenharmony_ci return NOTIFY_DONE; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci } else { 538c2ecf20Sopenharmony_ci hsr = port->hsr; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci switch (event) { 578c2ecf20Sopenharmony_ci case NETDEV_UP: /* Administrative state DOWN */ 588c2ecf20Sopenharmony_ci case NETDEV_DOWN: /* Administrative state UP */ 598c2ecf20Sopenharmony_ci case NETDEV_CHANGE: /* Link (carrier) state changes */ 608c2ecf20Sopenharmony_ci hsr_check_carrier_and_operstate(hsr); 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci case NETDEV_CHANGENAME: 638c2ecf20Sopenharmony_ci if (is_hsr_master(dev)) 648c2ecf20Sopenharmony_ci hsr_debugfs_rename(dev); 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci case NETDEV_CHANGEADDR: 678c2ecf20Sopenharmony_ci if (port->type == HSR_PT_MASTER) { 688c2ecf20Sopenharmony_ci /* This should not happen since there's no 698c2ecf20Sopenharmony_ci * ndo_set_mac_address() for HSR devices - i.e. not 708c2ecf20Sopenharmony_ci * supported. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (port->type == HSR_PT_SLAVE_A) { 788c2ecf20Sopenharmony_ci ether_addr_copy(master->dev->dev_addr, dev->dev_addr); 798c2ecf20Sopenharmony_ci call_netdevice_notifiers(NETDEV_CHANGEADDR, 808c2ecf20Sopenharmony_ci master->dev); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Make sure we recognize frames from ourselves in hsr_rcv() */ 848c2ecf20Sopenharmony_ci port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 858c2ecf20Sopenharmony_ci res = hsr_create_self_node(hsr, 868c2ecf20Sopenharmony_ci master->dev->dev_addr, 878c2ecf20Sopenharmony_ci port ? 888c2ecf20Sopenharmony_ci port->dev->dev_addr : 898c2ecf20Sopenharmony_ci master->dev->dev_addr); 908c2ecf20Sopenharmony_ci if (res) 918c2ecf20Sopenharmony_ci netdev_warn(master->dev, 928c2ecf20Sopenharmony_ci "Could not update HSR node address.\n"); 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci case NETDEV_CHANGEMTU: 958c2ecf20Sopenharmony_ci if (port->type == HSR_PT_MASTER) 968c2ecf20Sopenharmony_ci break; /* Handled in ndo_change_mtu() */ 978c2ecf20Sopenharmony_ci mtu_max = hsr_get_max_mtu(port->hsr); 988c2ecf20Sopenharmony_ci master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER); 998c2ecf20Sopenharmony_ci master->dev->mtu = mtu_max; 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 1028c2ecf20Sopenharmony_ci if (!is_hsr_master(dev)) { 1038c2ecf20Sopenharmony_ci master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER); 1048c2ecf20Sopenharmony_ci hsr_del_port(port); 1058c2ecf20Sopenharmony_ci if (hsr_slave_empty(master->hsr)) { 1068c2ecf20Sopenharmony_ci const struct rtnl_link_ops *ops; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ops = master->dev->rtnl_link_ops; 1098c2ecf20Sopenharmony_ci ops->dellink(master->dev, &list_kill); 1108c2ecf20Sopenharmony_ci unregister_netdevice_many(&list_kill); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case NETDEV_PRE_TYPE_CHANGE: 1158c2ecf20Sopenharmony_ci /* HSR works only on Ethernet devices. Refuse slave to change 1168c2ecf20Sopenharmony_ci * its type. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci return NOTIFY_BAD; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct hsr_port *port; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci hsr_for_each_port(hsr, port) 1298c2ecf20Sopenharmony_ci if (port->type == pt) 1308c2ecf20Sopenharmony_ci return port; 1318c2ecf20Sopenharmony_ci return NULL; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic struct notifier_block hsr_nb = { 1358c2ecf20Sopenharmony_ci .notifier_call = hsr_netdev_notify, /* Slave event notifications */ 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int __init hsr_init(void) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int res; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci register_netdevice_notifier(&hsr_nb); 1458c2ecf20Sopenharmony_ci res = hsr_netlink_init(); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return res; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void __exit hsr_exit(void) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci hsr_netlink_exit(); 1538c2ecf20Sopenharmony_ci hsr_debugfs_remove_root(); 1548c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&hsr_nb); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cimodule_init(hsr_init); 1588c2ecf20Sopenharmony_cimodule_exit(hsr_exit); 1598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 160