| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
|---|
| 1 | 2 | /* Copyright 2011-2014 Autronica Fire and Security AS |
|---|
| 2 | | - * |
|---|
| 3 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 4 | | - * under the terms of the GNU General Public License as published by the Free |
|---|
| 5 | | - * Software Foundation; either version 2 of the License, or (at your option) |
|---|
| 6 | | - * any later version. |
|---|
| 7 | 3 | * |
|---|
| 8 | 4 | * Author(s): |
|---|
| 9 | 5 | * 2011-2014 Arvid Brodin, arvid.brodin@alten.se |
|---|
| 6 | + * |
|---|
| 7 | + * Event handling for HSR and PRP devices. |
|---|
| 10 | 8 | */ |
|---|
| 11 | 9 | |
|---|
| 12 | 10 | #include <linux/netdevice.h> |
|---|
| 11 | +#include <net/rtnetlink.h> |
|---|
| 13 | 12 | #include <linux/rculist.h> |
|---|
| 14 | 13 | #include <linux/timer.h> |
|---|
| 15 | 14 | #include <linux/etherdevice.h> |
|---|
| .. | .. |
|---|
| 19 | 18 | #include "hsr_framereg.h" |
|---|
| 20 | 19 | #include "hsr_slave.h" |
|---|
| 21 | 20 | |
|---|
| 21 | +static bool hsr_slave_empty(struct hsr_priv *hsr) |
|---|
| 22 | +{ |
|---|
| 23 | + struct hsr_port *port; |
|---|
| 24 | + |
|---|
| 25 | + hsr_for_each_port(hsr, port) |
|---|
| 26 | + if (port->type != HSR_PT_MASTER) |
|---|
| 27 | + return false; |
|---|
| 28 | + return true; |
|---|
| 29 | +} |
|---|
| 22 | 30 | |
|---|
| 23 | 31 | static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, |
|---|
| 24 | 32 | void *ptr) |
|---|
| 25 | 33 | { |
|---|
| 26 | | - struct net_device *dev; |
|---|
| 27 | 34 | struct hsr_port *port, *master; |
|---|
| 35 | + struct net_device *dev; |
|---|
| 28 | 36 | struct hsr_priv *hsr; |
|---|
| 37 | + LIST_HEAD(list_kill); |
|---|
| 29 | 38 | int mtu_max; |
|---|
| 30 | 39 | int res; |
|---|
| 31 | 40 | |
|---|
| 32 | 41 | dev = netdev_notifier_info_to_dev(ptr); |
|---|
| 33 | 42 | port = hsr_port_get_rtnl(dev); |
|---|
| 34 | | - if (port == NULL) { |
|---|
| 43 | + if (!port) { |
|---|
| 35 | 44 | if (!is_hsr_master(dev)) |
|---|
| 36 | 45 | return NOTIFY_DONE; /* Not an HSR device */ |
|---|
| 37 | 46 | hsr = netdev_priv(dev); |
|---|
| 38 | 47 | port = hsr_port_get_hsr(hsr, HSR_PT_MASTER); |
|---|
| 39 | | - if (port == NULL) { |
|---|
| 48 | + if (!port) { |
|---|
| 40 | 49 | /* Resend of notification concerning removed device? */ |
|---|
| 41 | 50 | return NOTIFY_DONE; |
|---|
| 42 | 51 | } |
|---|
| .. | .. |
|---|
| 49 | 58 | case NETDEV_DOWN: /* Administrative state UP */ |
|---|
| 50 | 59 | case NETDEV_CHANGE: /* Link (carrier) state changes */ |
|---|
| 51 | 60 | hsr_check_carrier_and_operstate(hsr); |
|---|
| 61 | + break; |
|---|
| 62 | + case NETDEV_CHANGENAME: |
|---|
| 63 | + if (is_hsr_master(dev)) |
|---|
| 64 | + hsr_debugfs_rename(dev); |
|---|
| 52 | 65 | break; |
|---|
| 53 | 66 | case NETDEV_CHANGEADDR: |
|---|
| 54 | 67 | if (port->type == HSR_PT_MASTER) { |
|---|
| .. | .. |
|---|
| 63 | 76 | |
|---|
| 64 | 77 | if (port->type == HSR_PT_SLAVE_A) { |
|---|
| 65 | 78 | ether_addr_copy(master->dev->dev_addr, dev->dev_addr); |
|---|
| 66 | | - call_netdevice_notifiers(NETDEV_CHANGEADDR, master->dev); |
|---|
| 79 | + call_netdevice_notifiers(NETDEV_CHANGEADDR, |
|---|
| 80 | + master->dev); |
|---|
| 67 | 81 | } |
|---|
| 68 | 82 | |
|---|
| 69 | 83 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ |
|---|
| 70 | 84 | port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); |
|---|
| 71 | | - res = hsr_create_self_node(&hsr->self_node_db, |
|---|
| 85 | + res = hsr_create_self_node(hsr, |
|---|
| 72 | 86 | master->dev->dev_addr, |
|---|
| 73 | 87 | port ? |
|---|
| 74 | 88 | port->dev->dev_addr : |
|---|
| .. | .. |
|---|
| 85 | 99 | master->dev->mtu = mtu_max; |
|---|
| 86 | 100 | break; |
|---|
| 87 | 101 | case NETDEV_UNREGISTER: |
|---|
| 88 | | - hsr_del_port(port); |
|---|
| 102 | + if (!is_hsr_master(dev)) { |
|---|
| 103 | + master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER); |
|---|
| 104 | + hsr_del_port(port); |
|---|
| 105 | + if (hsr_slave_empty(master->hsr)) { |
|---|
| 106 | + const struct rtnl_link_ops *ops; |
|---|
| 107 | + |
|---|
| 108 | + ops = master->dev->rtnl_link_ops; |
|---|
| 109 | + ops->dellink(master->dev, &list_kill); |
|---|
| 110 | + unregister_netdevice_many(&list_kill); |
|---|
| 111 | + } |
|---|
| 112 | + } |
|---|
| 89 | 113 | break; |
|---|
| 90 | 114 | case NETDEV_PRE_TYPE_CHANGE: |
|---|
| 91 | 115 | /* HSR works only on Ethernet devices. Refuse slave to change |
|---|
| .. | .. |
|---|
| 96 | 120 | |
|---|
| 97 | 121 | return NOTIFY_DONE; |
|---|
| 98 | 122 | } |
|---|
| 99 | | - |
|---|
| 100 | 123 | |
|---|
| 101 | 124 | struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt) |
|---|
| 102 | 125 | { |
|---|
| .. | .. |
|---|
| 112 | 135 | .notifier_call = hsr_netdev_notify, /* Slave event notifications */ |
|---|
| 113 | 136 | }; |
|---|
| 114 | 137 | |
|---|
| 115 | | - |
|---|
| 116 | 138 | static int __init hsr_init(void) |
|---|
| 117 | 139 | { |
|---|
| 118 | 140 | int res; |
|---|
| .. | .. |
|---|
| 127 | 149 | |
|---|
| 128 | 150 | static void __exit hsr_exit(void) |
|---|
| 129 | 151 | { |
|---|
| 130 | | - unregister_netdevice_notifier(&hsr_nb); |
|---|
| 131 | 152 | hsr_netlink_exit(); |
|---|
| 153 | + hsr_debugfs_remove_root(); |
|---|
| 154 | + unregister_netdevice_notifier(&hsr_nb); |
|---|
| 132 | 155 | } |
|---|
| 133 | 156 | |
|---|
| 134 | 157 | module_init(hsr_init); |
|---|