]>
Commit | Line | Data |
---|---|---|
70ebe4a4 | 1 | /* Copyright 2011-2014 Autronica Fire and Security AS |
f421436a AB |
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 | * | |
8 | * Author(s): | |
70ebe4a4 | 9 | * 2011-2014 Arvid Brodin, arvid.brodin@alten.se |
f421436a AB |
10 | */ |
11 | ||
12 | #include <linux/netdevice.h> | |
13 | #include <linux/rculist.h> | |
14 | #include <linux/timer.h> | |
15 | #include <linux/etherdevice.h> | |
16 | #include "hsr_main.h" | |
17 | #include "hsr_device.h" | |
18 | #include "hsr_netlink.h" | |
19 | #include "hsr_framereg.h" | |
20 | ||
21 | ||
22 | /* List of all registered virtual HSR devices */ | |
23 | static LIST_HEAD(hsr_list); | |
24 | ||
70ebe4a4 | 25 | void register_hsr_master(struct hsr_priv *hsr) |
f421436a | 26 | { |
70ebe4a4 | 27 | list_add_tail_rcu(&hsr->hsr_list, &hsr_list); |
f421436a AB |
28 | } |
29 | ||
70ebe4a4 | 30 | void unregister_hsr_master(struct hsr_priv *hsr) |
f421436a | 31 | { |
70ebe4a4 | 32 | struct hsr_priv *hsr_it; |
f421436a | 33 | |
70ebe4a4 AB |
34 | list_for_each_entry(hsr_it, &hsr_list, hsr_list) |
35 | if (hsr_it == hsr) { | |
36 | list_del_rcu(&hsr_it->hsr_list); | |
f421436a AB |
37 | return; |
38 | } | |
39 | } | |
40 | ||
41 | bool is_hsr_slave(struct net_device *dev) | |
42 | { | |
70ebe4a4 | 43 | struct hsr_priv *hsr_it; |
f421436a | 44 | |
70ebe4a4 AB |
45 | list_for_each_entry_rcu(hsr_it, &hsr_list, hsr_list) { |
46 | if (dev == hsr_it->slave[0]) | |
f421436a | 47 | return true; |
70ebe4a4 | 48 | if (dev == hsr_it->slave[1]) |
f421436a AB |
49 | return true; |
50 | } | |
51 | ||
52 | return false; | |
53 | } | |
54 | ||
f421436a AB |
55 | /* If dev is a HSR slave device, return the virtual master device. Return NULL |
56 | * otherwise. | |
57 | */ | |
81ba6afd | 58 | struct hsr_priv *get_hsr_master(struct net_device *dev) |
f421436a | 59 | { |
70ebe4a4 | 60 | struct hsr_priv *hsr; |
f421436a AB |
61 | |
62 | rcu_read_lock(); | |
70ebe4a4 AB |
63 | list_for_each_entry_rcu(hsr, &hsr_list, hsr_list) |
64 | if ((dev == hsr->slave[0]) || | |
65 | (dev == hsr->slave[1])) { | |
f421436a | 66 | rcu_read_unlock(); |
70ebe4a4 | 67 | return hsr; |
f421436a AB |
68 | } |
69 | ||
70 | rcu_read_unlock(); | |
71 | return NULL; | |
72 | } | |
73 | ||
f421436a AB |
74 | /* If dev is a HSR slave device, return the other slave device. Return NULL |
75 | * otherwise. | |
76 | */ | |
81ba6afd AB |
77 | struct net_device *get_other_slave(struct hsr_priv *hsr, |
78 | struct net_device *dev) | |
f421436a | 79 | { |
70ebe4a4 AB |
80 | if (dev == hsr->slave[0]) |
81 | return hsr->slave[1]; | |
82 | if (dev == hsr->slave[1]) | |
83 | return hsr->slave[0]; | |
f421436a AB |
84 | |
85 | return NULL; | |
86 | } | |
87 | ||
88 | ||
89 | static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, | |
90 | void *ptr) | |
91 | { | |
92 | struct net_device *slave, *other_slave; | |
70ebe4a4 | 93 | struct hsr_priv *hsr; |
f421436a AB |
94 | int old_operstate; |
95 | int mtu_max; | |
96 | int res; | |
97 | struct net_device *dev; | |
98 | ||
99 | dev = netdev_notifier_info_to_dev(ptr); | |
100 | ||
70ebe4a4 AB |
101 | hsr = get_hsr_master(dev); |
102 | if (hsr) { | |
f421436a AB |
103 | /* dev is a slave device */ |
104 | slave = dev; | |
70ebe4a4 | 105 | other_slave = get_other_slave(hsr, slave); |
f421436a AB |
106 | } else { |
107 | if (!is_hsr_master(dev)) | |
108 | return NOTIFY_DONE; | |
70ebe4a4 AB |
109 | hsr = netdev_priv(dev); |
110 | slave = hsr->slave[0]; | |
111 | other_slave = hsr->slave[1]; | |
f421436a AB |
112 | } |
113 | ||
114 | switch (event) { | |
115 | case NETDEV_UP: /* Administrative state DOWN */ | |
116 | case NETDEV_DOWN: /* Administrative state UP */ | |
117 | case NETDEV_CHANGE: /* Link (carrier) state changes */ | |
70ebe4a4 AB |
118 | old_operstate = hsr->dev->operstate; |
119 | hsr_set_carrier(hsr->dev, slave, other_slave); | |
f421436a AB |
120 | /* netif_stacked_transfer_operstate() cannot be used here since |
121 | * it doesn't set IF_OPER_LOWERLAYERDOWN (?) | |
122 | */ | |
70ebe4a4 AB |
123 | hsr_set_operstate(hsr->dev, slave, other_slave); |
124 | hsr_check_announce(hsr->dev, old_operstate); | |
f421436a AB |
125 | break; |
126 | case NETDEV_CHANGEADDR: | |
127 | ||
128 | /* This should not happen since there's no ndo_set_mac_address() | |
129 | * for HSR devices - i.e. not supported. | |
130 | */ | |
70ebe4a4 | 131 | if (dev == hsr->dev) |
f421436a AB |
132 | break; |
133 | ||
70ebe4a4 AB |
134 | if (dev == hsr->slave[0]) |
135 | ether_addr_copy(hsr->dev->dev_addr, | |
136 | hsr->slave[0]->dev_addr); | |
f421436a AB |
137 | |
138 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ | |
70ebe4a4 AB |
139 | res = hsr_create_self_node(&hsr->self_node_db, |
140 | hsr->dev->dev_addr, | |
141 | hsr->slave[1] ? | |
142 | hsr->slave[1]->dev_addr : | |
143 | hsr->dev->dev_addr); | |
f421436a | 144 | if (res) |
70ebe4a4 | 145 | netdev_warn(hsr->dev, |
f421436a AB |
146 | "Could not update HSR node address.\n"); |
147 | ||
70ebe4a4 AB |
148 | if (dev == hsr->slave[0]) |
149 | call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr->dev); | |
f421436a AB |
150 | break; |
151 | case NETDEV_CHANGEMTU: | |
70ebe4a4 | 152 | if (dev == hsr->dev) |
f421436a | 153 | break; /* Handled in ndo_change_mtu() */ |
70ebe4a4 AB |
154 | mtu_max = hsr_get_max_mtu(hsr); |
155 | if (hsr->dev->mtu > mtu_max) | |
156 | dev_set_mtu(hsr->dev, mtu_max); | |
f421436a AB |
157 | break; |
158 | case NETDEV_UNREGISTER: | |
70ebe4a4 AB |
159 | if (dev == hsr->slave[0]) |
160 | hsr->slave[0] = NULL; | |
161 | if (dev == hsr->slave[1]) | |
162 | hsr->slave[1] = NULL; | |
f421436a AB |
163 | |
164 | /* There should really be a way to set a new slave device... */ | |
165 | ||
166 | break; | |
167 | case NETDEV_PRE_TYPE_CHANGE: | |
168 | /* HSR works only on Ethernet devices. Refuse slave to change | |
169 | * its type. | |
170 | */ | |
171 | return NOTIFY_BAD; | |
172 | } | |
173 | ||
174 | return NOTIFY_DONE; | |
175 | } | |
176 | ||
177 | ||
f421436a AB |
178 | static struct notifier_block hsr_nb = { |
179 | .notifier_call = hsr_netdev_notify, /* Slave event notifications */ | |
180 | }; | |
181 | ||
182 | ||
183 | static int __init hsr_init(void) | |
184 | { | |
185 | int res; | |
186 | ||
70ebe4a4 | 187 | BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); |
f421436a | 188 | |
f421436a | 189 | register_netdevice_notifier(&hsr_nb); |
f421436a AB |
190 | res = hsr_netlink_init(); |
191 | ||
192 | return res; | |
193 | } | |
194 | ||
195 | static void __exit hsr_exit(void) | |
196 | { | |
197 | unregister_netdevice_notifier(&hsr_nb); | |
f421436a | 198 | hsr_netlink_exit(); |
f421436a AB |
199 | } |
200 | ||
201 | module_init(hsr_init); | |
202 | module_exit(hsr_exit); | |
203 | MODULE_LICENSE("GPL"); |