aboutsummaryrefslogtreecommitdiffstats
path: root/net/switchdev/switchdev.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2015-04-19 21:32:26 -0400
committerDave Airlie <airlied@redhat.com>2015-04-19 23:05:20 -0400
commit2c33ce009ca2389dbf0535d0672214d09738e35e (patch)
tree6186a6458c3c160385d794a23eaf07c786a9e61b /net/switchdev/switchdev.c
parentcec32a47010647e8b0603726ebb75b990a4057a4 (diff)
parent09d51602cf84a1264946711dd4ea0dddbac599a1 (diff)
Merge Linus master into drm-next
The merge is clean, but the arm build fails afterwards, due to API changes in the regulator tree. I've included the patch into the merge to fix the build. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r--net/switchdev/switchdev.c217
1 files changed, 197 insertions, 20 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 8c1e558db118..46568b85c333 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -1,6 +1,7 @@
1/* 1/*
2 * net/switchdev/switchdev.c - Switch device API 2 * net/switchdev/switchdev.c - Switch device API
3 * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> 3 * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
4 * Copyright (c) 2014-2015 Scott Feldman <sfeldma@gmail.com>
4 * 5 *
5 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by 7 * it under the terms of the GNU General Public License as published by
@@ -14,6 +15,7 @@
14#include <linux/mutex.h> 15#include <linux/mutex.h>
15#include <linux/notifier.h> 16#include <linux/notifier.h>
16#include <linux/netdevice.h> 17#include <linux/netdevice.h>
18#include <net/ip_fib.h>
17#include <net/switchdev.h> 19#include <net/switchdev.h>
18 20
19/** 21/**
@@ -26,13 +28,13 @@
26int netdev_switch_parent_id_get(struct net_device *dev, 28int netdev_switch_parent_id_get(struct net_device *dev,
27 struct netdev_phys_item_id *psid) 29 struct netdev_phys_item_id *psid)
28{ 30{
29 const struct net_device_ops *ops = dev->netdev_ops; 31 const struct swdev_ops *ops = dev->swdev_ops;
30 32
31 if (!ops->ndo_switch_parent_id_get) 33 if (!ops || !ops->swdev_parent_id_get)
32 return -EOPNOTSUPP; 34 return -EOPNOTSUPP;
33 return ops->ndo_switch_parent_id_get(dev, psid); 35 return ops->swdev_parent_id_get(dev, psid);
34} 36}
35EXPORT_SYMBOL(netdev_switch_parent_id_get); 37EXPORT_SYMBOL_GPL(netdev_switch_parent_id_get);
36 38
37/** 39/**
38 * netdev_switch_port_stp_update - Notify switch device port of STP 40 * netdev_switch_port_stp_update - Notify switch device port of STP
@@ -44,20 +46,29 @@ EXPORT_SYMBOL(netdev_switch_parent_id_get);
44 */ 46 */
45int netdev_switch_port_stp_update(struct net_device *dev, u8 state) 47int netdev_switch_port_stp_update(struct net_device *dev, u8 state)
46{ 48{
47 const struct net_device_ops *ops = dev->netdev_ops; 49 const struct swdev_ops *ops = dev->swdev_ops;
50 struct net_device *lower_dev;
51 struct list_head *iter;
52 int err = -EOPNOTSUPP;
48 53
49 if (!ops->ndo_switch_port_stp_update) 54 if (ops && ops->swdev_port_stp_update)
50 return -EOPNOTSUPP; 55 return ops->swdev_port_stp_update(dev, state);
51 WARN_ON(!ops->ndo_switch_parent_id_get); 56
52 return ops->ndo_switch_port_stp_update(dev, state); 57 netdev_for_each_lower_dev(dev, lower_dev, iter) {
58 err = netdev_switch_port_stp_update(lower_dev, state);
59 if (err && err != -EOPNOTSUPP)
60 return err;
61 }
62
63 return err;
53} 64}
54EXPORT_SYMBOL(netdev_switch_port_stp_update); 65EXPORT_SYMBOL_GPL(netdev_switch_port_stp_update);
55 66
56static DEFINE_MUTEX(netdev_switch_mutex); 67static DEFINE_MUTEX(netdev_switch_mutex);
57static RAW_NOTIFIER_HEAD(netdev_switch_notif_chain); 68static RAW_NOTIFIER_HEAD(netdev_switch_notif_chain);
58 69
59/** 70/**
60 * register_netdev_switch_notifier - Register nofifier 71 * register_netdev_switch_notifier - Register notifier
61 * @nb: notifier_block 72 * @nb: notifier_block
62 * 73 *
63 * Register switch device notifier. This should be used by code 74 * Register switch device notifier. This should be used by code
@@ -73,10 +84,10 @@ int register_netdev_switch_notifier(struct notifier_block *nb)
73 mutex_unlock(&netdev_switch_mutex); 84 mutex_unlock(&netdev_switch_mutex);
74 return err; 85 return err;
75} 86}
76EXPORT_SYMBOL(register_netdev_switch_notifier); 87EXPORT_SYMBOL_GPL(register_netdev_switch_notifier);
77 88
78/** 89/**
79 * unregister_netdev_switch_notifier - Unregister nofifier 90 * unregister_netdev_switch_notifier - Unregister notifier
80 * @nb: notifier_block 91 * @nb: notifier_block
81 * 92 *
82 * Unregister switch device notifier. 93 * Unregister switch device notifier.
@@ -91,10 +102,10 @@ int unregister_netdev_switch_notifier(struct notifier_block *nb)
91 mutex_unlock(&netdev_switch_mutex); 102 mutex_unlock(&netdev_switch_mutex);
92 return err; 103 return err;
93} 104}
94EXPORT_SYMBOL(unregister_netdev_switch_notifier); 105EXPORT_SYMBOL_GPL(unregister_netdev_switch_notifier);
95 106
96/** 107/**
97 * call_netdev_switch_notifiers - Call nofifiers 108 * call_netdev_switch_notifiers - Call notifiers
98 * @val: value passed unmodified to notifier function 109 * @val: value passed unmodified to notifier function
99 * @dev: port device 110 * @dev: port device
100 * @info: notifier information data 111 * @info: notifier information data
@@ -114,7 +125,7 @@ int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
114 mutex_unlock(&netdev_switch_mutex); 125 mutex_unlock(&netdev_switch_mutex);
115 return err; 126 return err;
116} 127}
117EXPORT_SYMBOL(call_netdev_switch_notifiers); 128EXPORT_SYMBOL_GPL(call_netdev_switch_notifiers);
118 129
119/** 130/**
120 * netdev_switch_port_bridge_setlink - Notify switch device port of bridge 131 * netdev_switch_port_bridge_setlink - Notify switch device port of bridge
@@ -139,7 +150,7 @@ int netdev_switch_port_bridge_setlink(struct net_device *dev,
139 150
140 return ops->ndo_bridge_setlink(dev, nlh, flags); 151 return ops->ndo_bridge_setlink(dev, nlh, flags);
141} 152}
142EXPORT_SYMBOL(netdev_switch_port_bridge_setlink); 153EXPORT_SYMBOL_GPL(netdev_switch_port_bridge_setlink);
143 154
144/** 155/**
145 * netdev_switch_port_bridge_dellink - Notify switch device port of bridge 156 * netdev_switch_port_bridge_dellink - Notify switch device port of bridge
@@ -164,7 +175,7 @@ int netdev_switch_port_bridge_dellink(struct net_device *dev,
164 175
165 return ops->ndo_bridge_dellink(dev, nlh, flags); 176 return ops->ndo_bridge_dellink(dev, nlh, flags);
166} 177}
167EXPORT_SYMBOL(netdev_switch_port_bridge_dellink); 178EXPORT_SYMBOL_GPL(netdev_switch_port_bridge_dellink);
168 179
169/** 180/**
170 * ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink 181 * ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink
@@ -194,7 +205,7 @@ int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
194 205
195 return ret; 206 return ret;
196} 207}
197EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_setlink); 208EXPORT_SYMBOL_GPL(ndo_dflt_netdev_switch_port_bridge_setlink);
198 209
199/** 210/**
200 * ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink 211 * ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink
@@ -224,4 +235,170 @@ int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
224 235
225 return ret; 236 return ret;
226} 237}
227EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink); 238EXPORT_SYMBOL_GPL(ndo_dflt_netdev_switch_port_bridge_dellink);
239
240static struct net_device *netdev_switch_get_lowest_dev(struct net_device *dev)
241{
242 const struct swdev_ops *ops = dev->swdev_ops;
243 struct net_device *lower_dev;
244 struct net_device *port_dev;
245 struct list_head *iter;
246
247 /* Recusively search down until we find a sw port dev.
248 * (A sw port dev supports swdev_parent_id_get).
249 */
250
251 if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD &&
252 ops && ops->swdev_parent_id_get)
253 return dev;
254
255 netdev_for_each_lower_dev(dev, lower_dev, iter) {
256 port_dev = netdev_switch_get_lowest_dev(lower_dev);
257 if (port_dev)
258 return port_dev;
259 }
260
261 return NULL;
262}
263
264static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi)
265{
266 struct netdev_phys_item_id psid;
267 struct netdev_phys_item_id prev_psid;
268 struct net_device *dev = NULL;
269 int nhsel;
270
271 /* For this route, all nexthop devs must be on the same switch. */
272
273 for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
274 const struct fib_nh *nh = &fi->fib_nh[nhsel];
275
276 if (!nh->nh_dev)
277 return NULL;
278
279 dev = netdev_switch_get_lowest_dev(nh->nh_dev);
280 if (!dev)
281 return NULL;
282
283 if (netdev_switch_parent_id_get(dev, &psid))
284 return NULL;
285
286 if (nhsel > 0) {
287 if (prev_psid.id_len != psid.id_len)
288 return NULL;
289 if (memcmp(prev_psid.id, psid.id, psid.id_len))
290 return NULL;
291 }
292
293 prev_psid = psid;
294 }
295
296 return dev;
297}
298
299/**
300 * netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch
301 *
302 * @dst: route's IPv4 destination address
303 * @dst_len: destination address length (prefix length)
304 * @fi: route FIB info structure
305 * @tos: route TOS
306 * @type: route type
307 * @nlflags: netlink flags passed in (NLM_F_*)
308 * @tb_id: route table ID
309 *
310 * Add IPv4 route entry to switch device.
311 */
312int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
313 u8 tos, u8 type, u32 nlflags, u32 tb_id)
314{
315 struct net_device *dev;
316 const struct swdev_ops *ops;
317 int err = 0;
318
319 /* Don't offload route if using custom ip rules or if
320 * IPv4 FIB offloading has been disabled completely.
321 */
322
323#ifdef CONFIG_IP_MULTIPLE_TABLES
324 if (fi->fib_net->ipv4.fib_has_custom_rules)
325 return 0;
326#endif
327
328 if (fi->fib_net->ipv4.fib_offload_disabled)
329 return 0;
330
331 dev = netdev_switch_get_dev_by_nhs(fi);
332 if (!dev)
333 return 0;
334 ops = dev->swdev_ops;
335
336 if (ops->swdev_fib_ipv4_add) {
337 err = ops->swdev_fib_ipv4_add(dev, htonl(dst), dst_len,
338 fi, tos, type, nlflags,
339 tb_id);
340 if (!err)
341 fi->fib_flags |= RTNH_F_EXTERNAL;
342 }
343
344 return err;
345}
346EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_add);
347
348/**
349 * netdev_switch_fib_ipv4_del - Delete IPv4 route entry from switch
350 *
351 * @dst: route's IPv4 destination address
352 * @dst_len: destination address length (prefix length)
353 * @fi: route FIB info structure
354 * @tos: route TOS
355 * @type: route type
356 * @tb_id: route table ID
357 *
358 * Delete IPv4 route entry from switch device.
359 */
360int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
361 u8 tos, u8 type, u32 tb_id)
362{
363 struct net_device *dev;
364 const struct swdev_ops *ops;
365 int err = 0;
366
367 if (!(fi->fib_flags & RTNH_F_EXTERNAL))
368 return 0;
369
370 dev = netdev_switch_get_dev_by_nhs(fi);
371 if (!dev)
372 return 0;
373 ops = dev->swdev_ops;
374
375 if (ops->swdev_fib_ipv4_del) {
376 err = ops->swdev_fib_ipv4_del(dev, htonl(dst), dst_len,
377 fi, tos, type, tb_id);
378 if (!err)
379 fi->fib_flags &= ~RTNH_F_EXTERNAL;
380 }
381
382 return err;
383}
384EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_del);
385
386/**
387 * netdev_switch_fib_ipv4_abort - Abort an IPv4 FIB operation
388 *
389 * @fi: route FIB info structure
390 */
391void netdev_switch_fib_ipv4_abort(struct fib_info *fi)
392{
393 /* There was a problem installing this route to the offload
394 * device. For now, until we come up with more refined
395 * policy handling, abruptly end IPv4 fib offloading for
396 * for entire net by flushing offload device(s) of all
397 * IPv4 routes, and mark IPv4 fib offloading broken from
398 * this point forward.
399 */
400
401 fib_flush_external(fi->fib_net);
402 fi->fib_net->ipv4.fib_offload_disabled = true;
403}
404EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_abort);