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(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(addr1: failover_dev->perm_addr, addr2: mac)) { |
29 | *ops = rtnl_dereference(failover->ops); |
30 | spin_unlock(lock: &failover_lock); |
31 | return failover_dev; |
32 | } |
33 | } |
34 | spin_unlock(lock: &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(mac: slave_dev->perm_addr, ops: &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(dev: slave_dev, rx_handler: fops->slave_handle_frame, |
67 | rx_handler_data: failover_dev); |
68 | if (err) { |
69 | netdev_err(dev: slave_dev, format: "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(dev: slave_dev, upper_dev: failover_dev, NULL, |
76 | upper_info: &lag_upper_info, NULL); |
77 | if (err) { |
78 | netdev_err(dev: slave_dev, format: "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(dev: slave_dev, upper_dev: failover_dev); |
90 | slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); |
91 | err_upper_link: |
92 | netdev_rx_handler_unregister(dev: 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(dev: slave_dev)) |
110 | goto done; |
111 | |
112 | ASSERT_RTNL(); |
113 | |
114 | failover_dev = failover_get_bymac(mac: slave_dev->perm_addr, ops: &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(dev: slave_dev); |
123 | netdev_upper_dev_unlink(dev: slave_dev, upper_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(dev: slave_dev)) |
141 | goto done; |
142 | |
143 | ASSERT_RTNL(); |
144 | |
145 | failover_dev = failover_get_bymac(mac: slave_dev->perm_addr, ops: &fops); |
146 | if (!failover_dev) |
147 | goto done; |
148 | |
149 | if (!netif_running(dev: 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(dev: slave_dev)) |
166 | goto done; |
167 | |
168 | ASSERT_RTNL(); |
169 | |
170 | failover_dev = failover_get_bymac(mac: slave_dev->perm_addr, ops: &fops); |
171 | if (!failover_dev) |
172 | goto done; |
173 | |
174 | if (!netif_running(dev: 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(info: ptr); |
189 | |
190 | /* Skip parent events */ |
191 | if (netif_is_failover(dev: event_dev)) |
192 | return NOTIFY_DONE; |
193 | |
194 | switch (event) { |
195 | case NETDEV_REGISTER: |
196 | return failover_slave_register(slave_dev: 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(slave_dev: event_dev); |
203 | case NETDEV_CHANGENAME: |
204 | return failover_slave_name_change(slave_dev: 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(dev: 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(addr1: failover_dev->perm_addr, addr2: dev->perm_addr)) |
225 | failover_slave_register(slave_dev: 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(error: -EINVAL); |
249 | |
250 | failover = kzalloc(size: sizeof(*failover), GFP_KERNEL); |
251 | if (!failover) |
252 | return ERR_PTR(error: -ENOMEM); |
253 | |
254 | rcu_assign_pointer(failover->ops, ops); |
255 | netdev_hold(dev, tracker: &failover->dev_tracker, GFP_KERNEL); |
256 | dev->priv_flags |= IFF_FAILOVER; |
257 | rcu_assign_pointer(failover->failover_dev, dev); |
258 | |
259 | spin_lock(lock: &failover_lock); |
260 | list_add_tail(new: &failover->list, head: &failover_list); |
261 | spin_unlock(lock: &failover_lock); |
262 | |
263 | netdev_info(dev, format: "failover master:%s registered\n" , dev->name); |
264 | |
265 | failover_existing_slave_register(failover_dev: 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(dev: failover_dev, format: "failover master:%s unregistered\n" , |
285 | failover_dev->name); |
286 | |
287 | failover_dev->priv_flags &= ~IFF_FAILOVER; |
288 | netdev_put(dev: failover_dev, tracker: &failover->dev_tracker); |
289 | |
290 | spin_lock(lock: &failover_lock); |
291 | list_del(entry: &failover->list); |
292 | spin_unlock(lock: &failover_lock); |
293 | |
294 | kfree(objp: failover); |
295 | } |
296 | EXPORT_SYMBOL_GPL(failover_unregister); |
297 | |
298 | static __init int |
299 | failover_init(void) |
300 | { |
301 | register_netdevice_notifier(nb: &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(nb: &failover_notifier); |
311 | } |
312 | module_exit(failover_exit); |
313 | |
314 | MODULE_DESCRIPTION("Generic failover infrastructure/interface" ); |
315 | MODULE_LICENSE("GPL v2" ); |
316 | |